Skip to content

Commit 960cc80

Browse files
committed
Merge branch 'close-console-targets'
2 parents e19d5ff + d36f95d commit 960cc80

File tree

17 files changed

+331
-76
lines changed

17 files changed

+331
-76
lines changed

src/Concept/Builder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ protected static function getTerminators(): array
5151
private array $Data = [];
5252

5353
/**
54-
* Creates a new builder
54+
* Creates a new Builder object
5555
*/
5656
final public function __construct(?IContainer $container = null)
5757
{

src/Concept/Facade.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lkrms\Container\Container;
77
use Lkrms\Contract\IFacade;
88
use Lkrms\Contract\ReceivesFacade;
9+
use Lkrms\Contract\Unloadable;
910
use Lkrms\Facade\Event;
1011
use Lkrms\Support\EventDispatcher;
1112
use LogicException;
@@ -42,7 +43,8 @@ private static function _load()
4243
{
4344
$service = static::getServiceName();
4445

45-
if ($container = Container::maybeGetGlobalContainer()) {
46+
$container = Container::maybeGetGlobalContainer();
47+
if ($container) {
4648
$instance = $container
4749
->singletonIf($service)
4850
->get($service, func_get_args());
@@ -96,22 +98,34 @@ final public static function load()
9698
*/
9799
final public static function unload(): void
98100
{
99-
if ($id = self::$ListenerIds[static::class] ?? null) {
101+
$id = self::$ListenerIds[static::class] ?? null;
102+
if ($id !== null) {
100103
Event::removeListener($id);
101104
unset(self::$ListenerIds[static::class]);
102105
}
103-
if ($container = Container::maybeGetGlobalContainer()) {
106+
107+
$container = Container::maybeGetGlobalContainer();
108+
if ($container) {
104109
$container->unbind(static::getServiceName());
105110
}
106-
unset(self::$Instances[static::class]);
111+
112+
$instance = self::$Instances[static::class] ?? null;
113+
if ($instance) {
114+
if ($instance instanceof Unloadable) {
115+
$instance->unload();
116+
}
117+
unset(self::$Instances[static::class]);
118+
}
107119
}
108120

109121
/**
110122
* Clear the underlying instances of all facades
111123
*/
112124
final public static function unloadAll(): void
113125
{
114-
self::$Instances = [];
126+
foreach (array_keys(self::$Instances) as $class) {
127+
$class::unload();
128+
}
115129
}
116130

117131
/**

src/Concept/Provider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ abstract class Provider implements IProvider
2020
private DateFormatterInterface $DateFormatter;
2121

2222
/**
23-
* Creates a new provider object
23+
* Creates a new Provider object
2424
*/
2525
public function __construct(IContainer $app)
2626
{

src/Console/Concept/ConsolePrefixTarget.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ abstract protected function writeToTarget($level, string $message, array $contex
2727
*/
2828
final public function write($level, string $message, array $context = []): void
2929
{
30+
$this->assertIsValid();
31+
3032
if ($this->Prefix === null) {
3133
$this->writeToTarget($level, $message, $context);
3234
return;
@@ -51,6 +53,8 @@ final public function setPrefix(?string $prefix)
5153
return $this;
5254
}
5355

56+
$this->assertIsValid();
57+
5458
$this->PrefixLength = strlen($prefix);
5559
$this->Prefix = $this->getFormatter()->getTagFormat(Tag::LOW_PRIORITY)->apply($prefix);
5660

@@ -62,6 +66,8 @@ final public function setPrefix(?string $prefix)
6266
*/
6367
final public function getPrefix(): ?string
6468
{
69+
$this->assertIsValid();
70+
6571
return $this->Prefix;
6672
}
6773

@@ -70,6 +76,8 @@ final public function getPrefix(): ?string
7076
*/
7177
public function getWidth(): ?int
7278
{
79+
$this->assertIsValid();
80+
7381
return 80 - $this->PrefixLength;
7482
}
7583
}

src/Console/Concept/ConsoleTarget.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ abstract class ConsoleTarget implements ConsoleTargetInterface
1919
*/
2020
public function getFormatter(): Formatter
2121
{
22+
$this->assertIsValid();
23+
2224
return $this->Formatter ??= new Formatter(
2325
$this->createTagFormats(),
2426
$this->createMessageFormats(),
@@ -31,9 +33,18 @@ public function getFormatter(): Formatter
3133
*/
3234
public function getWidth(): ?int
3335
{
36+
$this->assertIsValid();
37+
3438
return null;
3539
}
3640

41+
/**
42+
* @inheritDoc
43+
*/
44+
public function close(): void {}
45+
46+
protected function assertIsValid(): void {}
47+
3748
protected function createTagFormats(): TagFormats
3849
{
3950
return new TagFormats();

src/Console/ConsoleWriter.php

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Lkrms\Console\ConsoleFormatter as Formatter;
1414
use Lkrms\Contract\IFacade;
1515
use Lkrms\Contract\ReceivesFacade;
16+
use Lkrms\Contract\Unloadable;
1617
use Lkrms\Exception\Contract\ExceptionInterface;
1718
use Lkrms\Exception\Contract\MultipleErrorExceptionInterface;
1819
use Lkrms\Exception\InvalidEnvironmentException;
@@ -34,7 +35,7 @@
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
}

src/Console/Contract/ConsoleTargetInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ public function getWidth(): ?int;
3737
* @param array<string,mixed> $context
3838
*/
3939
public function write($level, string $message, array $context = []): void;
40+
41+
/**
42+
* Close the target and any underlying resources
43+
*/
44+
public function close(): void;
4045
}

src/Console/Contract/ConsoleTargetStreamInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ public function isStderr(): bool;
2323
* True if the target writes to a TTY
2424
*/
2525
public function isTty(): bool;
26+
27+
/**
28+
* Close and reopen the underlying PHP stream if possible
29+
*/
30+
public function reopen(): void;
2631
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Lkrms\Console\Exception;
4+
5+
use Lkrms\Exception\Exception;
6+
7+
/**
8+
* Thrown when a console output target receives a message after closing its
9+
* underlying resources
10+
*/
11+
class ConsoleInvalidTargetException extends Exception {}

0 commit comments

Comments
 (0)