From 0dac87975ce1e3dbcf8773623ff62a8452ecb8ca Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 21 Apr 2022 21:58:19 +0200 Subject: [PATCH] Add property types to all properties where possible --- doc/04-extending.md | 6 +-- src/Monolog/ErrorHandler.php | 21 +++++--- src/Monolog/Formatter/ElasticaFormatter.php | 4 +- src/Monolog/Handler/AmqpHandler.php | 8 ++- src/Monolog/Handler/FilterHandler.php | 23 ++++---- src/Monolog/Handler/FingersCrossedHandler.php | 40 ++++++++------ .../Handler/FormattableHandlerTrait.php | 7 +-- src/Monolog/Handler/NewRelicHandler.php | 46 +++++++--------- src/Monolog/Handler/PushoverHandler.php | 10 +++- src/Monolog/Handler/RedisHandler.php | 23 ++++---- src/Monolog/Handler/RedisPubSubHandler.php | 15 +++--- src/Monolog/Handler/SamplingHandler.php | 25 +++++---- src/Monolog/Handler/Slack/SlackRecord.php | 11 ++-- src/Monolog/Handler/SocketHandler.php | 15 ++---- src/Monolog/Handler/StreamHandler.php | 14 ++--- src/Monolog/Handler/SyslogUdpHandler.php | 2 +- src/Monolog/Handler/TelegramBotHandler.php | 9 ++-- src/Monolog/Logger.php | 24 ++++----- src/Monolog/Processor/WebProcessor.php | 15 +++--- tests/Monolog/Formatter/LineFormatterTest.php | 2 +- .../Monolog/Formatter/ScalarFormatterTest.php | 2 +- tests/Monolog/Handler/BufferHandlerTest.php | 2 +- .../Monolog/Handler/ChromePHPHandlerTest.php | 2 +- tests/Monolog/Handler/DynamoDbHandlerTest.php | 22 +++----- tests/Monolog/Handler/FirePHPHandlerTest.php | 2 +- tests/Monolog/Handler/HandlerWrapperTest.php | 5 +- .../Monolog/Handler/PHPConsoleHandlerTest.php | 9 ++-- tests/Monolog/Handler/PushoverHandlerTest.php | 6 ++- tests/Monolog/Handler/RedisHandlerTest.php | 7 --- .../Handler/RedisPubSubHandlerTest.php | 7 --- .../Handler/RotatingFileHandlerTest.php | 2 +- tests/Monolog/Handler/SocketHandlerTest.php | 53 +++++++++---------- .../Handler/ZendMonitorHandlerTest.php | 2 - tests/Monolog/Processor/WebProcessorTest.php | 2 +- tests/Monolog/PsrLogCompatTest.php | 2 +- tests/Monolog/SignalHandlerTest.php | 6 +-- 36 files changed, 201 insertions(+), 250 deletions(-) diff --git a/doc/04-extending.md b/doc/04-extending.md index 49cded91..f5d292ad 100644 --- a/doc/04-extending.md +++ b/doc/04-extending.md @@ -26,9 +26,9 @@ use Monolog\Handler\AbstractProcessingHandler; class PDOHandler extends AbstractProcessingHandler { - private $initialized = false; - private $pdo; - private $statement; + private bool $initialized = false; + private PDO $pdo; + private PDOStatement $statement; public function __construct(PDO $pdo, $level = Level::Debug, bool $bubble = true) { diff --git a/src/Monolog/ErrorHandler.php b/src/Monolog/ErrorHandler.php index 50e999c3..74d86995 100644 --- a/src/Monolog/ErrorHandler.php +++ b/src/Monolog/ErrorHandler.php @@ -26,28 +26,33 @@ use Psr\Log\LogLevel; */ class ErrorHandler { - private LoggerInterface $logger; - private Closure|null $previousExceptionHandler = null; + /** @var array an array of class name to LogLevel::* constant mapping */ private array $uncaughtExceptionLevelMap = []; - /** @var callable|true|null */ - private $previousErrorHandler = null; + /** @var Closure|true|null */ + private Closure|bool|null $previousErrorHandler = null; + /** @var array an array of E_* constant to LogLevel::* constant mapping */ private array $errorLevelMap = []; + private bool $handleOnlyReportedErrors = true; private bool $hasFatalErrorHandler = false; + private string $fatalLevel = LogLevel::ALERT; + private string|null $reservedMemory = null; + /** @var mixed|null */ private $lastFatalTrace = null; + private const FATAL_ERRORS = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; - public function __construct(LoggerInterface $logger) - { - $this->logger = $logger; + public function __construct( + private LoggerInterface $logger + ) { } /** @@ -108,7 +113,7 @@ class ErrorHandler $prev = set_error_handler([$this, 'handleError'], $errorTypes); $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); if ($callPrevious) { - $this->previousErrorHandler = $prev ?: true; + $this->previousErrorHandler = $prev !== null ? $prev(...) : true; } else { $this->previousErrorHandler = null; } diff --git a/src/Monolog/Formatter/ElasticaFormatter.php b/src/Monolog/Formatter/ElasticaFormatter.php index 750a6645..160510ad 100644 --- a/src/Monolog/Formatter/ElasticaFormatter.php +++ b/src/Monolog/Formatter/ElasticaFormatter.php @@ -27,9 +27,9 @@ class ElasticaFormatter extends NormalizerFormatter protected string $index; /** - * @var ?string Elastic search document type + * @var string|null Elastic search document type */ - protected $type; + protected string|null $type; /** * @param string $index Elastic Search index name diff --git a/src/Monolog/Handler/AmqpHandler.php b/src/Monolog/Handler/AmqpHandler.php index 8fe564b9..2c83a207 100644 --- a/src/Monolog/Handler/AmqpHandler.php +++ b/src/Monolog/Handler/AmqpHandler.php @@ -14,6 +14,7 @@ namespace Monolog\Handler; use Monolog\Level; use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\JsonFormatter; +use Monolog\LevelName; use PhpAmqpLib\Message\AMQPMessage; use PhpAmqpLib\Channel\AMQPChannel; use AMQPExchange; @@ -21,10 +22,7 @@ use Monolog\LogRecord; class AmqpHandler extends AbstractProcessingHandler { - /** - * @var AMQPExchange|AMQPChannel $exchange - */ - protected $exchange; + protected AMQPExchange|AMQPChannel $exchange; protected string $exchangeName; @@ -32,7 +30,7 @@ class AmqpHandler extends AbstractProcessingHandler * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use * @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only */ - public function __construct($exchange, ?string $exchangeName = null, $level = Level::Debug, bool $bubble = true) + public function __construct(AMQPExchange|AMQPChannel $exchange, ?string $exchangeName = null, int|string|Level|LevelName $level = Level::Debug, bool $bubble = true) { if ($exchange instanceof AMQPChannel) { $this->exchangeName = (string) $exchangeName; diff --git a/src/Monolog/Handler/FilterHandler.php b/src/Monolog/Handler/FilterHandler.php index 63199619..c697e72f 100644 --- a/src/Monolog/Handler/FilterHandler.php +++ b/src/Monolog/Handler/FilterHandler.php @@ -11,6 +11,7 @@ namespace Monolog\Handler; +use Closure; use Monolog\Level; use Monolog\LevelName; use Monolog\Logger; @@ -32,12 +33,11 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese use ProcessableHandlerTrait; /** - * Handler or factory callable($record, $this) + * Handler or factory Closure($record, $this) * - * @var callable|HandlerInterface - * @phpstan-var (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface + * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface */ - protected $handler; + protected Closure|HandlerInterface $handler; /** * Minimum level for logs that are passed to handler @@ -53,9 +53,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese protected bool $bubble; /** - * @phpstan-param (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler + * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * - * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). + * @param Closure|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). * @param int|string|Level|LevelName|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int|string|Level|LevelName|LogLevel::* $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param bool $bubble Whether the messages that are handled can bubble up the stack or not @@ -63,15 +63,11 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese * @phpstan-param value-of|value-of|Level|LevelName|LogLevel::*|array|value-of|Level|LevelName|LogLevel::*> $minLevelOrList * @phpstan-param value-of|value-of|Level|LevelName|LogLevel::* $maxLevel */ - public function __construct($handler, int|string|Level|LevelName|array $minLevelOrList = Level::Debug, int|string|Level|LevelName $maxLevel = Level::Emergency, bool $bubble = true) + public function __construct(Closure|HandlerInterface $handler, int|string|Level|LevelName|array $minLevelOrList = Level::Debug, int|string|Level|LevelName $maxLevel = Level::Emergency, bool $bubble = true) { $this->handler = $handler; $this->bubble = $bubble; $this->setAcceptedLevels($minLevelOrList, $maxLevel); - - if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { - throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); - } } /** @@ -157,10 +153,11 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese public function getHandler(LogRecord $record = null): HandlerInterface { if (!$this->handler instanceof HandlerInterface) { - $this->handler = ($this->handler)($record, $this); - if (!$this->handler instanceof HandlerInterface) { + $handler = ($this->handler)($record, $this); + if (!$handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } + $this->handler = $handler; } return $this->handler; diff --git a/src/Monolog/Handler/FingersCrossedHandler.php b/src/Monolog/Handler/FingersCrossedHandler.php index c500d5b4..633233e2 100644 --- a/src/Monolog/Handler/FingersCrossedHandler.php +++ b/src/Monolog/Handler/FingersCrossedHandler.php @@ -11,6 +11,7 @@ namespace Monolog\Handler; +use Closure; use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Level; @@ -42,33 +43,41 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa use ProcessableHandlerTrait; /** - * @var callable|HandlerInterface - * @phpstan-var (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface + * Handler or factory callable($record, $this) + * + * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface */ - protected $handler; + protected Closure|HandlerInterface $handler; + protected ActivationStrategyInterface $activationStrategy; + protected bool $buffering = true; + protected int $bufferSize; + /** @var LogRecord[] */ protected array $buffer = []; + protected bool $stopBuffering; + protected Level|null $passthruLevel = null; + protected bool $bubble; /** - * @phpstan-param (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler + * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * - * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). - * @param int|string|Level|LevelName|LogLevel::* $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated - * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. - * @param bool $bubble Whether the messages that are handled can bubble up the stack or not - * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) - * @param int|string|Level|LevelName|LogLevel::* $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + * @param Closure|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). + * @param int|string|Level|LevelName|LogLevel::* $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int|string|Level|LevelName|LogLevel::*|null $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered * * @phpstan-param value-of|value-of|Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy * @phpstan-param value-of|value-of|Level|LevelName|LogLevel::* $passthruLevel */ - public function __construct($handler, int|string|Level|LevelName|ActivationStrategyInterface $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, int|string|Level|LevelName $passthruLevel = null) + public function __construct(Closure|HandlerInterface $handler, int|string|Level|LevelName|ActivationStrategyInterface $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, int|string|Level|LevelName|null $passthruLevel = null) { if (null === $activationStrategy) { $activationStrategy = new ErrorLevelActivationStrategy(Level::Warning); @@ -88,10 +97,6 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa if ($passthruLevel !== null) { $this->passthruLevel = Logger::toMonologLevel($passthruLevel); } - - if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { - throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); - } } /** @@ -198,10 +203,11 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa public function getHandler(LogRecord $record = null): HandlerInterface { if (!$this->handler instanceof HandlerInterface) { - $this->handler = ($this->handler)($record, $this); - if (!$this->handler instanceof HandlerInterface) { + $handler = ($this->handler)($record, $this); + if (!$handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } + $this->handler = $handler; } return $this->handler; diff --git a/src/Monolog/Handler/FormattableHandlerTrait.php b/src/Monolog/Handler/FormattableHandlerTrait.php index 2bf924d2..c044e078 100644 --- a/src/Monolog/Handler/FormattableHandlerTrait.php +++ b/src/Monolog/Handler/FormattableHandlerTrait.php @@ -21,10 +21,7 @@ use Monolog\Formatter\LineFormatter; */ trait FormattableHandlerTrait { - /** - * @var ?FormatterInterface - */ - protected $formatter; + protected FormatterInterface|null $formatter = null; /** * @inheritDoc @@ -41,7 +38,7 @@ trait FormattableHandlerTrait */ public function getFormatter(): FormatterInterface { - if (!$this->formatter) { + if (null === $this->formatter) { $this->formatter = $this->getDefaultFormatter(); } diff --git a/src/Monolog/Handler/NewRelicHandler.php b/src/Monolog/Handler/NewRelicHandler.php index b040311a..78108285 100644 --- a/src/Monolog/Handler/NewRelicHandler.php +++ b/src/Monolog/Handler/NewRelicHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Level; +use Monolog\LevelName; use Monolog\Utils; use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\FormatterInterface; @@ -28,41 +29,30 @@ use Monolog\LogRecord; */ class NewRelicHandler extends AbstractProcessingHandler { - /** - * Name of the New Relic application that will receive logs from this handler. - * - * @var ?string - */ - protected $appName; - - /** - * Name of the current transaction - * - * @var ?string - */ - protected $transactionName; - - /** - * Some context and extra data is passed into the handler as arrays of values. Do we send them as is - * (useful if we are using the API), or explode them for display on the NewRelic RPM website? - */ - protected bool $explodeArrays; - /** * @inheritDoc */ public function __construct( - $level = Level::Error, + int|string|Level|LevelName $level = Level::Error, bool $bubble = true, - ?string $appName = null, - bool $explodeArrays = false, - ?string $transactionName = null + + /** + * Name of the New Relic application that will receive logs from this handler. + */ + protected string|null $appName = null, + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + */ + protected bool $explodeArrays = false, + + /** + * Name of the current transaction + */ + protected string|null $transactionName = null ) { parent::__construct($level, $bubble); - - $this->appName = $appName; - $this->explodeArrays = $explodeArrays; - $this->transactionName = $transactionName; } /** diff --git a/src/Monolog/Handler/PushoverHandler.php b/src/Monolog/Handler/PushoverHandler.php index cf52dc34..d745697c 100644 --- a/src/Monolog/Handler/PushoverHandler.php +++ b/src/Monolog/Handler/PushoverHandler.php @@ -27,16 +27,22 @@ use Monolog\LogRecord; class PushoverHandler extends SocketHandler { private string $token; + /** @var array */ private array $users; + private string $title; - /** @var string|int|null */ - private $user = null; + + private string|int|null $user = null; + private int $retry; + private int $expire; private Level $highPriorityLevel; + private Level $emergencyLevel; + private bool $useFormattedMessage = false; /** diff --git a/src/Monolog/Handler/RedisHandler.php b/src/Monolog/Handler/RedisHandler.php index fa1f06e4..9edfa181 100644 --- a/src/Monolog/Handler/RedisHandler.php +++ b/src/Monolog/Handler/RedisHandler.php @@ -14,7 +14,10 @@ namespace Monolog\Handler; use Monolog\Formatter\LineFormatter; use Monolog\Formatter\FormatterInterface; use Monolog\Level; +use Monolog\LevelName; use Monolog\LogRecord; +use Predis\Client as Predis; +use Redis; /** * Logs to a Redis key using rpush @@ -29,22 +32,18 @@ use Monolog\LogRecord; */ class RedisHandler extends AbstractProcessingHandler { - /** @var \Predis\Client<\Predis\Client>|\Redis */ - private $redisClient; + /** @var Predis|Redis */ + private Predis|Redis $redisClient; private string $redisKey; protected int $capSize; /** - * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance - * @param string $key The key name to push records to - * @param int $capSize Number of entries to limit list size to, 0 = unlimited + * @param Predis|Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $capSize Number of entries to limit list size to, 0 = unlimited */ - public function __construct($redis, string $key, $level = Level::Debug, bool $bubble = true, int $capSize = 0) + public function __construct(Predis|Redis $redis, string $key, int|string|Level|LevelName $level = Level::Debug, bool $bubble = true, int $capSize = 0) { - if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { - throw new \InvalidArgumentException('Predis\Client or Redis instance required'); - } - $this->redisClient = $redis; $this->redisKey = $key; $this->capSize = $capSize; @@ -70,8 +69,8 @@ class RedisHandler extends AbstractProcessingHandler */ protected function writeCapped(LogRecord $record): void { - if ($this->redisClient instanceof \Redis) { - $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; + if ($this->redisClient instanceof Redis) { + $mode = defined('Redis::MULTI') ? Redis::MULTI : 1; $this->redisClient->multi($mode) ->rpush($this->redisKey, $record->formatted) ->ltrim($this->redisKey, -$this->capSize, -1) diff --git a/src/Monolog/Handler/RedisPubSubHandler.php b/src/Monolog/Handler/RedisPubSubHandler.php index c9ef5cd8..6c903515 100644 --- a/src/Monolog/Handler/RedisPubSubHandler.php +++ b/src/Monolog/Handler/RedisPubSubHandler.php @@ -14,7 +14,10 @@ namespace Monolog\Handler; use Monolog\Formatter\LineFormatter; use Monolog\Formatter\FormatterInterface; use Monolog\Level; +use Monolog\LevelName; use Monolog\LogRecord; +use Predis\Client as Predis; +use Redis; /** * Sends the message to a Redis Pub/Sub channel using PUBLISH @@ -29,20 +32,16 @@ use Monolog\LogRecord; */ class RedisPubSubHandler extends AbstractProcessingHandler { - /** @var \Predis\Client<\Predis\Client>|\Redis */ + /** @var Predis|Redis */ private $redisClient; private string $channelKey; /** - * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance - * @param string $key The channel key to publish records to + * @param Predis|Redis $redis The redis instance + * @param string $key The channel key to publish records to */ - public function __construct($redis, string $key, $level = Level::Debug, bool $bubble = true) + public function __construct(Predis|Redis $redis, string $key, int|string|Level|LevelName $level = Level::Debug, bool $bubble = true) { - if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { - throw new \InvalidArgumentException('Predis\Client or Redis instance required'); - } - $this->redisClient = $redis; $this->channelKey = $key; diff --git a/src/Monolog/Handler/SamplingHandler.php b/src/Monolog/Handler/SamplingHandler.php index 753190a9..917413ef 100644 --- a/src/Monolog/Handler/SamplingHandler.php +++ b/src/Monolog/Handler/SamplingHandler.php @@ -11,6 +11,7 @@ namespace Monolog\Handler; +use Closure; use Monolog\Formatter\FormatterInterface; use Monolog\LogRecord; @@ -33,28 +34,25 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter use ProcessableHandlerTrait; /** - * @var HandlerInterface|callable - * @phpstan-var (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface + * Handler or factory callable($record, $this) + * + * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface */ - protected $handler; + protected Closure|HandlerInterface $handler; protected int $factor; /** - * @phpstan-param (callable(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler + * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler * - * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). - * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) + * @param Closure|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). + * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) */ - public function __construct($handler, int $factor) + public function __construct(Closure|HandlerInterface $handler, int $factor) { parent::__construct(); $this->handler = $handler; $this->factor = $factor; - - if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { - throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); - } } public function isHandling(LogRecord $record): bool @@ -83,10 +81,11 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter public function getHandler(LogRecord $record = null): HandlerInterface { if (!$this->handler instanceof HandlerInterface) { - $this->handler = ($this->handler)($record, $this); - if (!$this->handler instanceof HandlerInterface) { + $handler = ($this->handler)($record, $this); + if (!$handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } + $this->handler = $handler; } return $this->handler; diff --git a/src/Monolog/Handler/Slack/SlackRecord.php b/src/Monolog/Handler/Slack/SlackRecord.php index 4ef5f454..f530e3e5 100644 --- a/src/Monolog/Handler/Slack/SlackRecord.php +++ b/src/Monolog/Handler/Slack/SlackRecord.php @@ -38,17 +38,17 @@ class SlackRecord /** * Slack channel (encoded ID or name) */ - private ?string $channel; + private string|null $channel; /** * Name of a bot */ - private ?string $username; + private string|null $username; /** * User icon e.g. 'ghost', 'http://example.com/user.png' */ - private ?string $userIcon; + private string|null $userIcon; /** * Whether the message should be added to Slack as attachment (plain text otherwise) @@ -71,10 +71,7 @@ class SlackRecord */ private array $excludeFields; - /** - * @var ?FormatterInterface - */ - private $formatter; + private FormatterInterface|null $formatter; private NormalizerFormatter $normalizerFormatter; diff --git a/src/Monolog/Handler/SocketHandler.php b/src/Monolog/Handler/SocketHandler.php index 8bcecb2d..81115971 100644 --- a/src/Monolog/Handler/SocketHandler.php +++ b/src/Monolog/Handler/SocketHandler.php @@ -28,17 +28,12 @@ class SocketHandler extends AbstractProcessingHandler private $resource; private float $timeout; private float $writingTimeout; - /** @var ?int */ - private $lastSentBytes = null; - /** @var ?int */ - private $chunkSize; + private int|null $lastSentBytes = null; + private int|null $chunkSize; private bool $persistent; - /** @var ?int */ - private $errno = null; - /** @var ?string */ - private $errstr = null; - /** @var ?float */ - private $lastWritingAt = null; + private int|null $errno = null; + private string|null $errstr = null; + private float|null $lastWritingAt = null; /** * @param string $connectionString Socket connection string diff --git a/src/Monolog/Handler/StreamHandler.php b/src/Monolog/Handler/StreamHandler.php index dd60b71e..504b547f 100644 --- a/src/Monolog/Handler/StreamHandler.php +++ b/src/Monolog/Handler/StreamHandler.php @@ -24,22 +24,18 @@ use Monolog\LogRecord; */ class StreamHandler extends AbstractProcessingHandler { - /** @const int */ protected const MAX_CHUNK_SIZE = 2147483647; - /** @const int 10MB */ + /** 10MB */ protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; protected int $streamChunkSize; /** @var resource|null */ protected $stream; - /** @var ?string */ - protected $url = null; - /** @var ?string */ - private $errorMessage = null; - /** @var ?int */ - protected $filePermission; + protected string|null $url = null; + private string|null $errorMessage = null; + protected int|null $filePermission; protected bool $useLocking; /** @var true|null */ - private ?bool $dirCreated = null; + private bool|null $dirCreated = null; /** * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write diff --git a/src/Monolog/Handler/SyslogUdpHandler.php b/src/Monolog/Handler/SyslogUdpHandler.php index a00a1b7c..0370cf6e 100644 --- a/src/Monolog/Handler/SyslogUdpHandler.php +++ b/src/Monolog/Handler/SyslogUdpHandler.php @@ -39,7 +39,7 @@ class SyslogUdpHandler extends AbstractSyslogHandler protected UdpSocket $socket; protected string $ident; /** @var self::RFC* */ - protected $rfc; + protected int $rfc; /** * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) diff --git a/src/Monolog/Handler/TelegramBotHandler.php b/src/Monolog/Handler/TelegramBotHandler.php index 3ff49f08..7729d84c 100644 --- a/src/Monolog/Handler/TelegramBotHandler.php +++ b/src/Monolog/Handler/TelegramBotHandler.php @@ -64,21 +64,18 @@ class TelegramBotHandler extends AbstractProcessingHandler * The kind of formatting that is used for the message. * See available options at https://core.telegram.org/bots/api#formatting-options * or in AVAILABLE_PARSE_MODES - * @var ?string */ - private $parseMode; + private string|null $parseMode; /** * Disables link previews for links in the message. - * @var ?bool */ - private $disableWebPagePreview; + private bool|null $disableWebPagePreview; /** * Sends the message silently. Users will receive a notification with no sound. - * @var ?bool */ - private $disableNotification; + private bool|null $disableNotification; /** * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. diff --git a/src/Monolog/Logger.php b/src/Monolog/Logger.php index 405acec7..fa38923a 100644 --- a/src/Monolog/Logger.php +++ b/src/Monolog/Logger.php @@ -11,6 +11,7 @@ namespace Monolog; +use Closure; use DateTimeZone; use Monolog\Handler\HandlerInterface; use Monolog\Processor\ProcessorInterface; @@ -101,8 +102,6 @@ class Logger implements LoggerInterface, ResettableInterface * * This is only bumped when API breaks are done and should * follow the major version of the library - * - * @var int */ public const API = 3; @@ -111,7 +110,7 @@ class Logger implements LoggerInterface, ResettableInterface /** * The handler stack * - * @var HandlerInterface[] + * @var list */ protected array $handlers; @@ -122,16 +121,13 @@ class Logger implements LoggerInterface, ResettableInterface * * @var array<(callable(LogRecord): LogRecord)|ProcessorInterface> */ - protected $processors; + protected array $processors; protected bool $microsecondTimestamps = true; protected DateTimeZone $timezone; - /** - * @var callable|null - */ - protected $exceptionHandler; + protected Closure|null $exceptionHandler = null; /** * @param string $name The logging channel, a simple descriptive name that is attached to all log records @@ -182,7 +178,7 @@ class Logger implements LoggerInterface, ResettableInterface */ public function popHandler(): HandlerInterface { - if (!$this->handlers) { + if (0 === \count($this->handlers)) { throw new \LogicException('You tried to pop from an empty handler stack.'); } @@ -194,7 +190,7 @@ class Logger implements LoggerInterface, ResettableInterface * * If a map is passed, keys will be ignored. * - * @param HandlerInterface[] $handlers + * @param list $handlers */ public function setHandlers(array $handlers): self { @@ -207,7 +203,7 @@ class Logger implements LoggerInterface, ResettableInterface } /** - * @return HandlerInterface[] + * @return list */ public function getHandlers(): array { @@ -231,7 +227,7 @@ class Logger implements LoggerInterface, ResettableInterface */ public function popProcessor(): callable { - if (!$this->processors) { + if (0 === \count($this->processors)) { throw new \LogicException('You tried to pop from an empty processor stack.'); } @@ -439,14 +435,14 @@ class Logger implements LoggerInterface, ResettableInterface * * The callable will receive an exception object and the record that failed to be logged */ - public function setExceptionHandler(?callable $callback): self + public function setExceptionHandler(Closure|null $callback): self { $this->exceptionHandler = $callback; return $this; } - public function getExceptionHandler(): ?callable + public function getExceptionHandler(): Closure|null { return $this->exceptionHandler; } diff --git a/src/Monolog/Processor/WebProcessor.php b/src/Monolog/Processor/WebProcessor.php index e24dc629..947de362 100644 --- a/src/Monolog/Processor/WebProcessor.php +++ b/src/Monolog/Processor/WebProcessor.php @@ -11,6 +11,7 @@ namespace Monolog\Processor; +use ArrayAccess; use Monolog\LogRecord; /** @@ -21,9 +22,9 @@ use Monolog\LogRecord; class WebProcessor implements ProcessorInterface { /** - * @var array|\ArrayAccess + * @var array|ArrayAccess */ - protected $serverData; + protected array|ArrayAccess $serverData; /** * Default fields @@ -42,17 +43,15 @@ class WebProcessor implements ProcessorInterface ]; /** - * @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data - * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data + * @param array|ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data */ - public function __construct($serverData = null, array $extraFields = null) + public function __construct(array|ArrayAccess|null $serverData = null, array|null $extraFields = null) { if (null === $serverData) { $this->serverData = &$_SERVER; - } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { - $this->serverData = $serverData; } else { - throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + $this->serverData = $serverData; } $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; diff --git a/tests/Monolog/Formatter/LineFormatterTest.php b/tests/Monolog/Formatter/LineFormatterTest.php index 0cc5d246..9e9364ca 100644 --- a/tests/Monolog/Formatter/LineFormatterTest.php +++ b/tests/Monolog/Formatter/LineFormatterTest.php @@ -224,7 +224,7 @@ class LineFormatterTest extends TestCase class TestFoo { - public $foo = 'fooValue'; + public string $foo = 'fooValue'; } class TestBar diff --git a/tests/Monolog/Formatter/ScalarFormatterTest.php b/tests/Monolog/Formatter/ScalarFormatterTest.php index e75dd5d2..e12bbc03 100644 --- a/tests/Monolog/Formatter/ScalarFormatterTest.php +++ b/tests/Monolog/Formatter/ScalarFormatterTest.php @@ -16,7 +16,7 @@ use Monolog\Test\TestCase; class ScalarFormatterTest extends TestCase { - private $formatter; + private ScalarFormatter $formatter; public function setUp(): void { diff --git a/tests/Monolog/Handler/BufferHandlerTest.php b/tests/Monolog/Handler/BufferHandlerTest.php index 8e709612..d36a2713 100644 --- a/tests/Monolog/Handler/BufferHandlerTest.php +++ b/tests/Monolog/Handler/BufferHandlerTest.php @@ -16,7 +16,7 @@ use Monolog\Level; class BufferHandlerTest extends TestCase { - private $shutdownCheckHandler; + private TestHandler $shutdownCheckHandler; /** * @covers Monolog\Handler\BufferHandler::__construct diff --git a/tests/Monolog/Handler/ChromePHPHandlerTest.php b/tests/Monolog/Handler/ChromePHPHandlerTest.php index 722e0350..2dcb5200 100644 --- a/tests/Monolog/Handler/ChromePHPHandlerTest.php +++ b/tests/Monolog/Handler/ChromePHPHandlerTest.php @@ -134,7 +134,7 @@ class ChromePHPHandlerTest extends TestCase class TestChromePHPHandler extends ChromePHPHandler { - protected $headers = []; + protected array $headers = []; public static function resetStatic(): void { diff --git a/tests/Monolog/Handler/DynamoDbHandlerTest.php b/tests/Monolog/Handler/DynamoDbHandlerTest.php index b206ef84..d8ace447 100644 --- a/tests/Monolog/Handler/DynamoDbHandlerTest.php +++ b/tests/Monolog/Handler/DynamoDbHandlerTest.php @@ -11,17 +11,19 @@ namespace Monolog\Handler; +use Aws\DynamoDb\DynamoDbClient; use Monolog\Test\TestCase; +use PHPUnit\Framework\MockObject\MockObject; class DynamoDbHandlerTest extends TestCase { - private $client; + private DynamoDbClient&MockObject $client; - private $isV3; + private bool $isV3; public function setUp(): void { - if (!class_exists('Aws\DynamoDb\DynamoDbClient')) { + if (!class_exists(DynamoDbClient::class)) { $this->markTestSkipped('aws/aws-sdk-php not installed'); } @@ -29,13 +31,13 @@ class DynamoDbHandlerTest extends TestCase $implementedMethods = ['__call']; $absentMethods = []; - if (method_exists('Aws\DynamoDb\DynamoDbClient', 'formatAttributes')) { + if (method_exists(DynamoDbClient::class, 'formatAttributes')) { $implementedMethods[] = 'formatAttributes'; } else { $absentMethods[] = 'formatAttributes'; } - $clientMockBuilder = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient') + $clientMockBuilder = $this->getMockBuilder(DynamoDbClient::class) ->onlyMethods($implementedMethods) ->disableOriginalConstructor(); if ($absentMethods) { @@ -45,16 +47,6 @@ class DynamoDbHandlerTest extends TestCase $this->client = $clientMockBuilder->getMock(); } - public function testConstruct() - { - $this->assertInstanceOf('Monolog\Handler\DynamoDbHandler', new DynamoDbHandler($this->client, 'foo')); - } - - public function testInterface() - { - $this->assertInstanceOf('Monolog\Handler\HandlerInterface', new DynamoDbHandler($this->client, 'foo')); - } - public function testGetFormatter() { $handler = new DynamoDbHandler($this->client, 'foo'); diff --git a/tests/Monolog/Handler/FirePHPHandlerTest.php b/tests/Monolog/Handler/FirePHPHandlerTest.php index fd28b95a..40fb6782 100644 --- a/tests/Monolog/Handler/FirePHPHandlerTest.php +++ b/tests/Monolog/Handler/FirePHPHandlerTest.php @@ -75,7 +75,7 @@ class FirePHPHandlerTest extends TestCase class TestFirePHPHandler extends FirePHPHandler { - protected $headers = []; + protected array $headers = []; public static function resetStatic(): void { diff --git a/tests/Monolog/Handler/HandlerWrapperTest.php b/tests/Monolog/Handler/HandlerWrapperTest.php index 44c4fa79..51e80b31 100644 --- a/tests/Monolog/Handler/HandlerWrapperTest.php +++ b/tests/Monolog/Handler/HandlerWrapperTest.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Test\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * @author Alexey Karapetov @@ -20,12 +21,12 @@ class HandlerWrapperTest extends TestCase { private HandlerWrapper $wrapper; - private $handler; + private HandlerInterface&MockObject $handler; public function setUp(): void { parent::setUp(); - $this->handler = $this->createMock('Monolog\\Handler\\HandlerInterface'); + $this->handler = $this->createMock(HandlerInterface::class); $this->wrapper = new HandlerWrapper($this->handler); } diff --git a/tests/Monolog/Handler/PHPConsoleHandlerTest.php b/tests/Monolog/Handler/PHPConsoleHandlerTest.php index 84dc911a..79a2502d 100644 --- a/tests/Monolog/Handler/PHPConsoleHandlerTest.php +++ b/tests/Monolog/Handler/PHPConsoleHandlerTest.php @@ -28,12 +28,9 @@ use PHPUnit\Framework\MockObject\MockObject; */ class PHPConsoleHandlerTest extends TestCase { - /** @var Connector|MockObject */ - protected $connector; - /** @var DebugDispatcher|MockObject */ - protected $debugDispatcher; - /** @var ErrorDispatcher|MockObject */ - protected $errorDispatcher; + protected Connector&MockObject $connector; + protected DebugDispatcher&MockObject $debugDispatcher; + protected ErrorDispatcher&MockObject $errorDispatcher; protected function setUp(): void { diff --git a/tests/Monolog/Handler/PushoverHandlerTest.php b/tests/Monolog/Handler/PushoverHandlerTest.php index 7db7098e..11f3cd24 100644 --- a/tests/Monolog/Handler/PushoverHandlerTest.php +++ b/tests/Monolog/Handler/PushoverHandlerTest.php @@ -13,6 +13,7 @@ namespace Monolog\Handler; use Monolog\Test\TestCase; use Monolog\Level; +use PHPUnit\Framework\MockObject\MockObject; /** * Almost all examples (expected header, titles, messages) taken from @@ -22,8 +23,9 @@ use Monolog\Level; */ class PushoverHandlerTest extends TestCase { + /** @var resource */ private $res; - private $handler; + private PushoverHandler&MockObject $handler; public function testWriteHeader() { @@ -116,7 +118,7 @@ class PushoverHandlerTest extends TestCase { $constructorArgs = [$token, $user, $title]; $this->res = fopen('php://memory', 'a'); - $this->handler = $this->getMockBuilder('Monolog\Handler\PushoverHandler') + $this->handler = $this->getMockBuilder(PushoverHandler::class) ->setConstructorArgs($constructorArgs) ->onlyMethods(['fsockopen', 'streamSetTimeout', 'closeSocket']) ->getMock(); diff --git a/tests/Monolog/Handler/RedisHandlerTest.php b/tests/Monolog/Handler/RedisHandlerTest.php index c65885c7..479262cf 100644 --- a/tests/Monolog/Handler/RedisHandlerTest.php +++ b/tests/Monolog/Handler/RedisHandlerTest.php @@ -17,13 +17,6 @@ use Monolog\Formatter\LineFormatter; class RedisHandlerTest extends TestCase { - public function testConstructorShouldThrowExceptionForInvalidRedis() - { - $this->expectException(\InvalidArgumentException::class); - - new RedisHandler(new \stdClass(), 'key'); - } - public function testConstructorShouldWorkWithPredis() { $redis = $this->createMock('Predis\Client'); diff --git a/tests/Monolog/Handler/RedisPubSubHandlerTest.php b/tests/Monolog/Handler/RedisPubSubHandlerTest.php index f3e9f6e6..38ece0d2 100644 --- a/tests/Monolog/Handler/RedisPubSubHandlerTest.php +++ b/tests/Monolog/Handler/RedisPubSubHandlerTest.php @@ -19,13 +19,6 @@ use Monolog\Formatter\LineFormatter; class RedisPubSubHandlerTest extends TestCase { - public function testConstructorShouldThrowExceptionForInvalidRedis() - { - $this->expectException(\InvalidArgumentException::class); - - new RedisPubSubHandler(new \stdClass(), 'key'); - } - public function testConstructorShouldWorkWithPredis() { $redis = $this->createMock('Predis\Client'); diff --git a/tests/Monolog/Handler/RotatingFileHandlerTest.php b/tests/Monolog/Handler/RotatingFileHandlerTest.php index 8e131cd6..02286bbb 100644 --- a/tests/Monolog/Handler/RotatingFileHandlerTest.php +++ b/tests/Monolog/Handler/RotatingFileHandlerTest.php @@ -19,7 +19,7 @@ use Monolog\Test\TestCase; */ class RotatingFileHandlerTest extends TestCase { - private $lastError; + private array|null $lastError = null; public function setUp(): void { diff --git a/tests/Monolog/Handler/SocketHandlerTest.php b/tests/Monolog/Handler/SocketHandlerTest.php index 192cfbe0..02d469c9 100644 --- a/tests/Monolog/Handler/SocketHandlerTest.php +++ b/tests/Monolog/Handler/SocketHandlerTest.php @@ -20,10 +20,7 @@ use PHPUnit\Framework\MockObject\MockObject; */ class SocketHandlerTest extends TestCase { - /** - * @var \Monolog\Handler\SocketHandler|MockObject - */ - private $handler; + private SocketHandler&MockObject $handler; /** * @var resource @@ -34,58 +31,58 @@ class SocketHandlerTest extends TestCase { $this->expectException(\UnexpectedValueException::class); - $this->createHandler('garbage://here'); - $this->writeRecord('data'); + $handler = $this->createHandler('garbage://here'); + $handler->handle($this->getRecord(Level::Warning, 'data')); } public function testBadConnectionTimeout() { $this->expectException(\InvalidArgumentException::class); - $this->createHandler('localhost:1234'); - $this->handler->setConnectionTimeout(-1); + $handler = $this->createHandler('localhost:1234'); + $handler->setConnectionTimeout(-1); } public function testSetConnectionTimeout() { - $this->createHandler('localhost:1234'); - $this->handler->setConnectionTimeout(10.1); - $this->assertEquals(10.1, $this->handler->getConnectionTimeout()); + $handler = $this->createHandler('localhost:1234'); + $handler->setConnectionTimeout(10.1); + $this->assertEquals(10.1, $handler->getConnectionTimeout()); } public function testBadTimeout() { $this->expectException(\InvalidArgumentException::class); - $this->createHandler('localhost:1234'); - $this->handler->setTimeout(-1); + $handler = $this->createHandler('localhost:1234'); + $handler->setTimeout(-1); } public function testSetTimeout() { - $this->createHandler('localhost:1234'); - $this->handler->setTimeout(10.25); - $this->assertEquals(10.25, $this->handler->getTimeout()); + $handler = $this->createHandler('localhost:1234'); + $handler->setTimeout(10.25); + $this->assertEquals(10.25, $handler->getTimeout()); } public function testSetWritingTimeout() { - $this->createHandler('localhost:1234'); - $this->handler->setWritingTimeout(10.25); - $this->assertEquals(10.25, $this->handler->getWritingTimeout()); + $handler = $this->createHandler('localhost:1234'); + $handler->setWritingTimeout(10.25); + $this->assertEquals(10.25, $handler->getWritingTimeout()); } public function testSetChunkSize() { - $this->createHandler('localhost:1234'); - $this->handler->setChunkSize(1025); - $this->assertEquals(1025, $this->handler->getChunkSize()); + $handler = $this->createHandler('localhost:1234'); + $handler->setChunkSize(1025); + $this->assertEquals(1025, $handler->getChunkSize()); } public function testSetConnectionString() { - $this->createHandler('tcp://localhost:9090'); - $this->assertEquals('tcp://localhost:9090', $this->handler->getConnectionString()); + $handler = $this->createHandler('tcp://localhost:9090'); + $this->assertEquals('tcp://localhost:9090', $handler->getConnectionString()); } public function testExceptionIsThrownOnFsockopenError() @@ -277,10 +274,12 @@ class SocketHandlerTest extends TestCase $this->writeRecord('Hello world'); } - private function createHandler($connectionString) + private function createHandler(string $connectionString): SocketHandler { - $this->handler = new SocketHandler($connectionString); - $this->handler->setFormatter($this->getIdentityFormatter()); + $handler = new SocketHandler($connectionString); + $handler->setFormatter($this->getIdentityFormatter()); + + return $handler; } private function writeRecord($string) diff --git a/tests/Monolog/Handler/ZendMonitorHandlerTest.php b/tests/Monolog/Handler/ZendMonitorHandlerTest.php index 446bedb5..51362887 100644 --- a/tests/Monolog/Handler/ZendMonitorHandlerTest.php +++ b/tests/Monolog/Handler/ZendMonitorHandlerTest.php @@ -15,8 +15,6 @@ use Monolog\Test\TestCase; class ZendMonitorHandlerTest extends TestCase { - protected $zendMonitorHandler; - public function setUp(): void { if (!function_exists('zend_monitor_custom_event')) { diff --git a/tests/Monolog/Processor/WebProcessorTest.php b/tests/Monolog/Processor/WebProcessorTest.php index 59cc52e2..7c50127a 100644 --- a/tests/Monolog/Processor/WebProcessorTest.php +++ b/tests/Monolog/Processor/WebProcessorTest.php @@ -118,7 +118,7 @@ class WebProcessorTest extends TestCase public function testInvalidData() { - $this->expectException(\UnexpectedValueException::class); + $this->expectException(\TypeError::class); new WebProcessor(new \stdClass); } diff --git a/tests/Monolog/PsrLogCompatTest.php b/tests/Monolog/PsrLogCompatTest.php index 35cf76ea..eb5dafbc 100644 --- a/tests/Monolog/PsrLogCompatTest.php +++ b/tests/Monolog/PsrLogCompatTest.php @@ -24,7 +24,7 @@ use Stringable; class PsrLogCompatTest extends TestCase { - private $handler; + private TestHandler $handler; public function getLogger(): LoggerInterface { diff --git a/tests/Monolog/SignalHandlerTest.php b/tests/Monolog/SignalHandlerTest.php index 0c406bb8..1ca47724 100644 --- a/tests/Monolog/SignalHandlerTest.php +++ b/tests/Monolog/SignalHandlerTest.php @@ -22,9 +22,9 @@ use Monolog\Test\TestCase; */ class SignalHandlerTest extends TestCase { - private $asyncSignalHandling; - private $blockedSignals; - private $signalHandlers; + private bool $asyncSignalHandling; + private array $blockedSignals = []; + private array $signalHandlers = []; protected function setUp(): void {