1313use Lkrms \Console \ConsoleFormatter as Formatter ;
1414use Lkrms \Contract \IFacade ;
1515use Lkrms \Contract \ReceivesFacade ;
16+ use Lkrms \Contract \Unloadable ;
1617use Lkrms \Exception \Contract \ExceptionInterface ;
1718use Lkrms \Exception \Contract \MultipleErrorExceptionInterface ;
1819use Lkrms \Exception \InvalidEnvironmentException ;
3435 * {@see Console} facade. If a {@see ConsoleWriter} instance is required, call
3536 * {@see Console::getInstance()}.
3637 */
37- final class ConsoleWriter implements ReceivesFacade
38+ final class ConsoleWriter implements ReceivesFacade, Unloadable
3839{
3940 /**
4041 * @var array<Level::*,TargetStream[]>
@@ -56,6 +57,11 @@ final class ConsoleWriter implements ReceivesFacade
5657 */
5758 private array $ Targets = [];
5859
60+ /**
61+ * @var array<int,Target>
62+ */
63+ private array $ DeregisteredTargets = [];
64+
5965 /**
6066 * @var array<int,int-mask-of<TargetTypeFlag::*>>
6167 */
@@ -95,6 +101,14 @@ public function setFacade(string $name)
95101 return $ this ;
96102 }
97103
104+ /**
105+ * @inheritDoc
106+ */
107+ public function unload (): void
108+ {
109+ $ this ->deregisterAllTargets ();
110+ }
111+
98112 /**
99113 * Register a log file to receive console output
100114 *
@@ -196,7 +210,8 @@ public function registerStdioTargets(bool $replace = false)
196210 return $ this
197211 ->clearStdioTargets ()
198212 ->registerTarget ($ stderr , $ stderrLevels )
199- ->registerTarget ($ stdout , $ stdoutLevels );
213+ ->registerTarget ($ stdout , $ stdoutLevels )
214+ ->closeDeregisteredTargets ();
200215 }
201216
202217 /**
@@ -243,7 +258,8 @@ private function registerStdioTarget(
243258
244259 return $ this
245260 ->clearStdioTargets ()
246- ->registerTarget ($ target , $ levels );
261+ ->registerTarget ($ target , $ levels )
262+ ->closeDeregisteredTargets ();
247263 }
248264
249265 /**
@@ -256,11 +272,24 @@ private function clearStdioTargets()
256272 }
257273 $ targets = $ this ->reduceTargets ($ this ->StdioTargetsByLevel );
258274 foreach ($ targets as $ target ) {
259- $ this ->deregisterTarget ($ target );
275+ $ this ->onlyDeregisterTarget ($ target );
260276 }
261277 return $ this ;
262278 }
263279
280+ /**
281+ * Close and deregister all registered targets
282+ *
283+ * @return $this
284+ */
285+ public function deregisterAllTargets ()
286+ {
287+ foreach ($ this ->Targets as $ target ) {
288+ $ this ->onlyDeregisterTarget ($ target );
289+ }
290+ return $ this ->closeDeregisteredTargets ();
291+ }
292+
264293 /**
265294 * Register a target to receive console output
266295 *
@@ -313,11 +342,19 @@ public function registerTarget(
313342 }
314343
315344 /**
316- * Deregister a previously registered target
345+ * Close and deregister a previously registered target
317346 *
318347 * @return $this
319348 */
320349 public function deregisterTarget (Target $ target )
350+ {
351+ return $ this ->onlyDeregisterTarget ($ target )->closeDeregisteredTargets ();
352+ }
353+
354+ /**
355+ * @return $this
356+ */
357+ private function onlyDeregisterTarget (Target $ target )
321358 {
322359 $ targetId = spl_object_id ($ target );
323360
@@ -344,6 +381,8 @@ public function deregisterTarget(Target $target)
344381 $ this ->StdoutTarget = null ;
345382 }
346383
384+ $ this ->DeregisteredTargets [$ targetId ] = $ target ;
385+
347386 // Reinstate previous STDOUT and STDERR targets if possible
348387 if (
349388 $ this ->Targets &&
@@ -372,7 +411,24 @@ public function deregisterTarget(Target $target)
372411 }
373412
374413 /**
375- * Get a list of registered output targets
414+ * @return $this
415+ */
416+ private function closeDeregisteredTargets ()
417+ {
418+ // Reduce `$this->DeregisteredTargets` to targets not subsequently
419+ // re-registered
420+ $ this ->DeregisteredTargets = array_diff_key (
421+ $ this ->DeregisteredTargets , $ this ->Targets
422+ );
423+ foreach ($ this ->DeregisteredTargets as $ i => $ target ) {
424+ $ target ->close ();
425+ unset($ this ->DeregisteredTargets [$ i ]);
426+ }
427+ return $ this ;
428+ }
429+
430+ /**
431+ * Get a list of registered targets
376432 *
377433 * @return Target[]
378434 */
@@ -468,7 +524,7 @@ private function maybeGetTtyTarget($level): TargetStream
468524 public function getStdoutTarget (): TargetStream
469525 {
470526 if (!$ this ->StdoutTarget ) {
471- return $ this ->StdoutTarget = new StreamTarget (\STDOUT );
527+ return $ this ->StdoutTarget = StreamTarget:: fromStream (\STDOUT );
472528 }
473529 return $ this ->StdoutTarget ;
474530 }
@@ -479,7 +535,7 @@ public function getStdoutTarget(): TargetStream
479535 public function getStderrTarget (): TargetStream
480536 {
481537 if (!$ this ->StderrTarget ) {
482- return $ this ->StderrTarget = new StreamTarget (\STDERR );
538+ return $ this ->StderrTarget = StreamTarget:: fromStream (\STDERR );
483539 }
484540 return $ this ->StderrTarget ;
485541 }
0 commit comments