Просмотр исходного кода

Add Record/Level/LevelName type aliases and improve phpstan type coverage to level 6

Jordi Boggiano 4 лет назад
Родитель
Сommit
01d104aa78
88 измененных файлов с 791 добавлено и 180 удалено
  1. 6 1
      phpstan.neon.dist
  2. 60 25
      src/Monolog/ErrorHandler.php
  3. 2 0
      src/Monolog/Formatter/ChromePHPFormatter.php
  4. 4 2
      src/Monolog/Formatter/ElasticaFormatter.php
  5. 4 4
      src/Monolog/Formatter/ElasticsearchFormatter.php
  6. 4 0
      src/Monolog/Formatter/FlowdockFormatter.php
  7. 6 0
      src/Monolog/Formatter/FormatterInterface.php
  8. 2 0
      src/Monolog/Formatter/GelfMessageFormatter.php
  9. 5 2
      src/Monolog/Formatter/HtmlFormatter.php
  10. 17 3
      src/Monolog/Formatter/JsonFormatter.php
  11. 13 3
      src/Monolog/Formatter/LineFormatter.php
  12. 34 18
      src/Monolog/Formatter/MongoDBFormatter.php
  13. 7 4
      src/Monolog/Formatter/NormalizerFormatter.php
  14. 2 0
      src/Monolog/Formatter/ScalarFormatter.php
  15. 10 1
      src/Monolog/Formatter/WildfireFormatter.php
  16. 12 0
      src/Monolog/Handler/AbstractHandler.php
  17. 9 0
      src/Monolog/Handler/AbstractProcessingHandler.php
  18. 6 0
      src/Monolog/Handler/AbstractSyslogHandler.php
  19. 5 0
      src/Monolog/Handler/AmqpHandler.php
  20. 20 0
      src/Monolog/Handler/BrowserConsoleHandler.php
  21. 7 0
      src/Monolog/Handler/BufferHandler.php
  22. 3 0
      src/Monolog/Handler/ChromePHPHandler.php
  23. 4 0
      src/Monolog/Handler/CouchDBHandler.php
  24. 9 3
      src/Monolog/Handler/CubeHandler.php
  25. 1 0
      src/Monolog/Handler/Curl/Util.php
  26. 8 0
      src/Monolog/Handler/DeduplicationHandler.php
  27. 1 0
      src/Monolog/Handler/DoctrineCouchDBHandler.php
  28. 4 0
      src/Monolog/Handler/DynamoDbHandler.php
  29. 9 2
      src/Monolog/Handler/ElasticaHandler.php
  30. 6 6
      src/Monolog/Handler/ElasticsearchHandler.php
  31. 3 1
      src/Monolog/Handler/ErrorLogHandler.php
  32. 18 2
      src/Monolog/Handler/FilterHandler.php
  33. 4 0
      src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php
  34. 11 3
      src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php
  35. 22 3
      src/Monolog/Handler/FingersCrossedHandler.php
  36. 13 4
      src/Monolog/Handler/FirePHPHandler.php
  37. 4 0
      src/Monolog/Handler/FleepHookHandler.php
  38. 4 2
      src/Monolog/Handler/FlowdockHandler.php
  39. 1 0
      src/Monolog/Handler/GroupHandler.php
  40. 9 0
      src/Monolog/Handler/HandlerInterface.php
  41. 2 0
      src/Monolog/Handler/IFTTTHandler.php
  42. 2 0
      src/Monolog/Handler/LogglyHandler.php
  43. 8 0
      src/Monolog/Handler/MailHandler.php
  44. 3 0
      src/Monolog/Handler/MongoDBHandler.php
  45. 6 6
      src/Monolog/Handler/NativeMailerHandler.php
  46. 6 2
      src/Monolog/Handler/NewRelicHandler.php
  47. 1 4
      src/Monolog/Handler/OverflowHandler.php
  48. 27 4
      src/Monolog/Handler/PHPConsoleHandler.php
  49. 2 2
      src/Monolog/Handler/ProcessHandler.php
  50. 5 3
      src/Monolog/Handler/ProcessableHandlerInterface.php
  51. 7 0
      src/Monolog/Handler/ProcessableHandlerTrait.php
  52. 25 3
      src/Monolog/Handler/PushoverHandler.php
  53. 7 0
      src/Monolog/Handler/RedisHandler.php
  54. 2 0
      src/Monolog/Handler/RedisPubSubHandler.php
  55. 2 0
      src/Monolog/Handler/RollbarHandler.php
  56. 6 0
      src/Monolog/Handler/RotatingFileHandler.php
  57. 8 2
      src/Monolog/Handler/SamplingHandler.php
  58. 8 8
      src/Monolog/Handler/SendGridHandler.php
  59. 22 2
      src/Monolog/Handler/Slack/SlackRecord.php
  60. 12 1
      src/Monolog/Handler/SlackHandler.php
  61. 1 3
      src/Monolog/Handler/SlackWebhookHandler.php
  62. 32 9
      src/Monolog/Handler/SocketHandler.php
  63. 1 3
      src/Monolog/Handler/SqsHandler.php
  64. 13 5
      src/Monolog/Handler/StreamHandler.php
  65. 7 1
      src/Monolog/Handler/SwiftMailerHandler.php
  66. 2 0
      src/Monolog/Handler/SyslogHandler.php
  67. 5 0
      src/Monolog/Handler/SyslogUdp/UdpSocket.php
  68. 10 0
      src/Monolog/Handler/SyslogUdpHandler.php
  69. 4 4
      src/Monolog/Handler/TelegramBotHandler.php
  70. 35 2
      src/Monolog/Handler/TestHandler.php
  71. 8 1
      src/Monolog/Handler/ZendMonitorHandler.php
  72. 29 5
      src/Monolog/Logger.php
  73. 9 1
      src/Monolog/Processor/GitProcessor.php
  74. 4 0
      src/Monolog/Processor/HostnameProcessor.php
  75. 13 5
      src/Monolog/Processor/IntrospectionProcessor.php
  76. 3 0
      src/Monolog/Processor/MemoryPeakUsageProcessor.php
  77. 3 0
      src/Monolog/Processor/MemoryUsageProcessor.php
  78. 9 1
      src/Monolog/Processor/MercurialProcessor.php
  79. 3 0
      src/Monolog/Processor/ProcessIdProcessor.php
  80. 5 0
      src/Monolog/Processor/ProcessorInterface.php
  81. 1 2
      src/Monolog/Processor/PsrLogMessageProcessor.php
  82. 13 0
      src/Monolog/Processor/TagProcessor.php
  83. 4 0
      src/Monolog/Processor/UidProcessor.php
  84. 11 4
      src/Monolog/Processor/WebProcessor.php
  85. 2 1
      src/Monolog/Registry.php
  86. 19 3
      src/Monolog/SignalHandler.php
  87. 13 2
      src/Monolog/Test/TestCase.php
  88. 2 2
      tests/Monolog/ErrorHandlerTest.php

+ 6 - 1
phpstan.neon.dist

@@ -1,5 +1,5 @@
 parameters:
-    level: 5
+    level: 6
 
     treatPhpDocTypesAsCertain: false
     reportUnmatchedIgnoredErrors: false
@@ -18,3 +18,8 @@ parameters:
         - message: '#Method Monolog\\Handler\\LogglyHandler::loadCurlHandle\(\) never returns resource so it can be removed from the return typehint.#'
           paths:
             - src/Monolog/Handler/LogglyHandler.php
+
+        # blocked by https://github.com/phpstan/phpstan/issues/5091
+        - '#has unknown class Monolog\\Handler\\Record#'
+        - '#::processRecord#'
+        - '#is incompatible with native type array.#'

+ 60 - 25
src/Monolog/ErrorHandler.php

@@ -25,19 +25,30 @@ use Psr\Log\LogLevel;
  */
 class ErrorHandler
 {
+    /** @var LoggerInterface */
     private $logger;
 
-    private $previousExceptionHandler;
-    private $uncaughtExceptionLevelMap;
-
-    private $previousErrorHandler;
-    private $errorLevelMap;
-    private $handleOnlyReportedErrors;
-
-    private $hasFatalErrorHandler;
-    private $fatalLevel;
-    private $reservedMemory;
+    /** @var ?callable */
+    private $previousExceptionHandler = null;
+    /** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
+    private $uncaughtExceptionLevelMap = [];
+
+    /** @var callable|true|null */
+    private $previousErrorHandler = null;
+    /** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
+    private $errorLevelMap = [];
+    /** @var bool */
+    private $handleOnlyReportedErrors = true;
+
+    /** @var bool */
+    private $hasFatalErrorHandler = false;
+    /** @var LogLevel::* */
+    private $fatalLevel = LogLevel::ALERT;
+    /** @var ?string */
+    private $reservedMemory = null;
+    /** @var ?mixed */
     private $lastFatalTrace;
+    /** @var int[] */
     private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
 
     public function __construct(LoggerInterface $logger)
@@ -50,10 +61,10 @@ class ErrorHandler
      *
      * By default it will handle errors, exceptions and fatal errors
      *
-     * @param  LoggerInterface   $logger
-     * @param  array|false       $errorLevelMap     an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
-     * @param  array|false       $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
-     * @param  string|null|false $fatalLevel        a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
+     * @param  LoggerInterface                        $logger
+     * @param  array<int, LogLevel::*>|false          $errorLevelMap     an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
+     * @param  array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
+     * @param  LogLevel::*|null|false                 $fatalLevel        a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
      * @return ErrorHandler
      */
     public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self
@@ -73,7 +84,11 @@ class ErrorHandler
         return $handler;
     }
 
-    public function registerExceptionHandler($levelMap = [], $callPrevious = true): self
+    /**
+     * @param  array<class-string, LogLevel::*> $levelMap an array of class name to LogLevel::* constant mapping
+     * @return $this
+     */
+    public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self
     {
         $prev = set_exception_handler(function (\Throwable $e): void {
             $this->handleException($e);
@@ -91,12 +106,18 @@ class ErrorHandler
         return $this;
     }
 
-    public function registerErrorHandler(array $levelMap = [], $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true): self
+    /**
+     * @param  array<int, LogLevel::*> $levelMap an array of E_* constant to LogLevel::* constant mapping
+     * @return $this
+     */
+    public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
     {
         $prev = set_error_handler([$this, 'handleError'], $errorTypes);
         $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
         if ($callPrevious) {
             $this->previousErrorHandler = $prev ?: true;
+        } else {
+            $this->previousErrorHandler = null;
         }
 
         $this->handleOnlyReportedErrors = $handleOnlyReportedErrors;
@@ -105,20 +126,23 @@ class ErrorHandler
     }
 
     /**
-     * @param string|null $level              a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
-     * @param int         $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
+     * @param LogLevel::*|null $level              a LogLevel::* constant, null to use the default LogLevel::ALERT
+     * @param int              $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
      */
     public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
     {
         register_shutdown_function([$this, 'handleFatalError']);
 
         $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
-        $this->fatalLevel = $level;
+        $this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
         $this->hasFatalErrorHandler = true;
 
         return $this;
     }
 
+    /**
+     * @return array<class-string, LogLevel::*>
+     */
     protected function defaultExceptionLevelMap(): array
     {
         return [
@@ -127,6 +151,9 @@ class ErrorHandler
         ];
     }
 
+    /**
+     * @return array<int, LogLevel::*>
+     */
     protected function defaultErrorLevelMap(): array
     {
         return [
@@ -148,7 +175,10 @@ class ErrorHandler
         ];
     }
 
-    private function handleException(\Throwable $e)
+    /**
+     * @phpstan-return never
+     */
+    private function handleException(\Throwable $e): void
     {
         $level = LogLevel::ERROR;
         foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
@@ -177,11 +207,13 @@ class ErrorHandler
 
     /**
      * @private
+     *
+     * @param mixed[] $context
      */
-    public function handleError($code, $message, $file = '', $line = 0, $context = [])
+    public function handleError(int $code, string $message, string $file = '', int $line = 0, array $context = []): bool
     {
         if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
-            return;
+            return false;
         }
 
         // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
@@ -197,7 +229,7 @@ class ErrorHandler
         if ($this->previousErrorHandler === true) {
             return false;
         } elseif ($this->previousErrorHandler) {
-            return ($this->previousErrorHandler)($code, $message, $file, $line, $context);
+            return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
         }
 
         return true;
@@ -206,14 +238,14 @@ class ErrorHandler
     /**
      * @private
      */
-    public function handleFatalError()
+    public function handleFatalError(): void
     {
         $this->reservedMemory = '';
 
         $lastError = error_get_last();
         if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
             $this->logger->log(
-                $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
+                $this->fatalLevel,
                 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
                 ['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace]
             );
@@ -226,6 +258,9 @@ class ErrorHandler
         }
     }
 
+    /**
+     * @param int $code
+     */
     private static function codeToString($code): string
     {
         switch ($code) {

+ 2 - 0
src/Monolog/Formatter/ChromePHPFormatter.php

@@ -22,6 +22,8 @@ class ChromePHPFormatter implements FormatterInterface
 {
     /**
      * Translates Monolog log levels to Wildfire levels.
+     *
+     * @var array<int, 'log'|'info'|'warn'|'error'>
      */
     private $logLevels = [
         Logger::DEBUG     => 'log',

+ 4 - 2
src/Monolog/Formatter/ElasticaFormatter.php

@@ -17,6 +17,8 @@ use Elastica\Document;
  * Format a log message into an Elastica Document
  *
  * @author Jelle Vink <jelle.vink@gmail.com>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class ElasticaFormatter extends NormalizerFormatter
 {
@@ -68,8 +70,8 @@ class ElasticaFormatter extends NormalizerFormatter
 
     /**
      * Convert a log message into an Elastica Document
-     * @param  array    $record
-     * @return Document
+     *
+     * @phpstan-param Record $record
      */
     protected function getDocument(array $record): Document
     {

+ 4 - 4
src/Monolog/Formatter/ElasticsearchFormatter.php

@@ -11,7 +11,7 @@
 
 namespace Monolog\Formatter;
 
-use DateTime;
+use DateTimeInterface;
 
 /**
  * Format a log message into an Elasticsearch record
@@ -37,7 +37,7 @@ class ElasticsearchFormatter extends NormalizerFormatter
     public function __construct(string $index, string $type)
     {
         // Elasticsearch requires an ISO 8601 format date with optional millisecond precision.
-        parent::__construct(DateTime::ISO8601);
+        parent::__construct(DateTimeInterface::ISO8601);
 
         $this->index = $index;
         $this->type = $type;
@@ -76,8 +76,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
     /**
      * Convert a log message into an Elasticsearch record
      *
-     * @param  array $record Log message
-     * @return array
+     * @param  mixed[] $record Log message
+     * @return mixed[]
      */
     protected function getDocument(array $record): array
     {

+ 4 - 0
src/Monolog/Formatter/FlowdockFormatter.php

@@ -36,6 +36,8 @@ class FlowdockFormatter implements FormatterInterface
 
     /**
      * {@inheritdoc}
+     *
+     * @return mixed[]
      */
     public function format(array $record): array
     {
@@ -70,6 +72,8 @@ class FlowdockFormatter implements FormatterInterface
 
     /**
      * {@inheritdoc}
+     *
+     * @return mixed[][]
      */
     public function formatBatch(array $records): array
     {

+ 6 - 0
src/Monolog/Formatter/FormatterInterface.php

@@ -15,6 +15,8 @@ namespace Monolog\Formatter;
  * Interface for formatters
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 interface FormatterInterface
 {
@@ -23,6 +25,8 @@ interface FormatterInterface
      *
      * @param  array $record A record to format
      * @return mixed The formatted record
+     *
+     * @phpstan-param Record $record
      */
     public function format(array $record);
 
@@ -31,6 +35,8 @@ interface FormatterInterface
      *
      * @param  array $records A set of records to format
      * @return mixed The formatted set of records
+     *
+     * @phpstan-param Record[] $records
      */
     public function formatBatch(array $records);
 }

+ 2 - 0
src/Monolog/Formatter/GelfMessageFormatter.php

@@ -47,6 +47,8 @@ class GelfMessageFormatter extends NormalizerFormatter
 
     /**
      * Translates Monolog log levels to Graylog2 log priorities.
+     *
+     * @var array<int, int>
      */
     private $logLevels = [
         Logger::DEBUG     => 7,

+ 5 - 2
src/Monolog/Formatter/HtmlFormatter.php

@@ -25,6 +25,8 @@ class HtmlFormatter extends NormalizerFormatter
 {
     /**
      * Translates Monolog log levels to html color priorities.
+     *
+     * @var array<int, string>
      */
     protected $logLevels = [
         Logger::DEBUG     => '#CCCCCC',
@@ -79,7 +81,6 @@ class HtmlFormatter extends NormalizerFormatter
     /**
      * Formats a log record.
      *
-     * @param  array  $record A record to format
      * @return string The formatted record
      */
     public function format(array $record): string
@@ -113,7 +114,6 @@ class HtmlFormatter extends NormalizerFormatter
     /**
      * Formats a set of log records.
      *
-     * @param  array  $records A set of records to format
      * @return string The formatted set of records
      */
     public function formatBatch(array $records): string
@@ -126,6 +126,9 @@ class HtmlFormatter extends NormalizerFormatter
         return $message;
     }
 
+    /**
+     * @param mixed $data
+     */
     protected function convertToString($data): string
     {
         if (null === $data || is_scalar($data)) {

+ 17 - 3
src/Monolog/Formatter/JsonFormatter.php

@@ -19,21 +19,26 @@ use Throwable;
  * This can be useful to log to databases or remote APIs
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class JsonFormatter extends NormalizerFormatter
 {
     public const BATCH_MODE_JSON = 1;
     public const BATCH_MODE_NEWLINES = 2;
 
+    /** @var self::BATCH_MODE_* */
     protected $batchMode;
+    /** @var bool */
     protected $appendNewline;
+    /** @var bool */
     protected $ignoreEmptyContextAndExtra;
+    /** @var bool */
+    protected $includeStacktraces = false;
 
     /**
-     * @var bool
+     * @param self::BATCH_MODE_* $batchMode
      */
-    protected $includeStacktraces = false;
-
     public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false)
     {
         $this->batchMode = $batchMode;
@@ -101,6 +106,9 @@ class JsonFormatter extends NormalizerFormatter
         }
     }
 
+    /**
+     * @return void
+     */
     public function includeStacktraces(bool $include = true)
     {
         $this->includeStacktraces = $include;
@@ -108,6 +116,8 @@ class JsonFormatter extends NormalizerFormatter
 
     /**
      * Return a JSON-encoded array of records.
+     *
+     * @phpstan-param Record[] $records
      */
     protected function formatBatchJson(array $records): string
     {
@@ -117,6 +127,8 @@ class JsonFormatter extends NormalizerFormatter
     /**
      * Use new lines to separate records instead of a
      * JSON-encoded array.
+     *
+     * @phpstan-param Record[] $records
      */
     protected function formatBatchNewlines(array $records): string
     {
@@ -175,6 +187,8 @@ class JsonFormatter extends NormalizerFormatter
     /**
      * Normalizes given exception with or without its own stack trace based on
      * `includeStacktraces` property.
+     *
+     * {@inheritDoc}
      */
     protected function normalizeException(Throwable $e, int $depth = 0): array
     {

+ 13 - 3
src/Monolog/Formatter/LineFormatter.php

@@ -25,9 +25,13 @@ class LineFormatter extends NormalizerFormatter
 {
     public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
 
+    /** @var string */
     protected $format;
+    /** @var bool */
     protected $allowInlineLineBreaks;
+    /** @var bool */
     protected $ignoreEmptyContextAndExtra;
+    /** @var bool */
     protected $includeStacktraces;
 
     /**
@@ -44,7 +48,7 @@ class LineFormatter extends NormalizerFormatter
         parent::__construct($dateFormat);
     }
 
-    public function includeStacktraces(bool $include = true)
+    public function includeStacktraces(bool $include = true): void
     {
         $this->includeStacktraces = $include;
         if ($this->includeStacktraces) {
@@ -52,12 +56,12 @@ class LineFormatter extends NormalizerFormatter
         }
     }
 
-    public function allowInlineLineBreaks(bool $allow = true)
+    public function allowInlineLineBreaks(bool $allow = true): void
     {
         $this->allowInlineLineBreaks = $allow;
     }
 
-    public function ignoreEmptyContextAndExtra(bool $ignore = true)
+    public function ignoreEmptyContextAndExtra(bool $ignore = true): void
     {
         $this->ignoreEmptyContextAndExtra = $ignore;
     }
@@ -121,6 +125,9 @@ class LineFormatter extends NormalizerFormatter
         return $message;
     }
 
+    /**
+     * @param mixed $value
+     */
     public function stringify($value): string
     {
         return $this->replaceNewlines($this->convertToString($value));
@@ -139,6 +146,9 @@ class LineFormatter extends NormalizerFormatter
         return $str;
     }
 
+    /**
+     * @param mixed $data
+     */
     protected function convertToString($data): string
     {
         if (null === $data || is_bool($data)) {

+ 34 - 18
src/Monolog/Formatter/MongoDBFormatter.php

@@ -21,8 +21,11 @@ use Monolog\Utils;
  */
 class MongoDBFormatter implements FormatterInterface
 {
+    /** @var bool */
     private $exceptionTraceAsString;
+    /** @var int */
     private $maxNestingLevel;
+    /** @var bool */
     private $isLegacyMongoExt;
 
     /**
@@ -39,6 +42,8 @@ class MongoDBFormatter implements FormatterInterface
 
     /**
      * {@inheritDoc}
+     *
+     * @return scalar[]
      */
     public function format(array $record): array
     {
@@ -47,40 +52,48 @@ class MongoDBFormatter implements FormatterInterface
 
     /**
      * {@inheritDoc}
+     *
+     * @return array<scalar[]>
      */
     public function formatBatch(array $records): array
     {
+        $formatted = [];
         foreach ($records as $key => $record) {
-            $records[$key] = $this->format($record);
+            $formatted[$key] = $this->format($record);
         }
 
-        return $records;
+        return $formatted;
     }
 
     /**
-     * @return array|string Array except when max nesting level is reached then a string "[...]"
+     * @param mixed[] $array
+     * @return mixed[]|string Array except when max nesting level is reached then a string "[...]"
      */
-    protected function formatArray(array $record, int $nestingLevel = 0)
+    protected function formatArray(array $array, int $nestingLevel = 0)
     {
-        if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
-            foreach ($record as $name => $value) {
-                if ($value instanceof \DateTimeInterface) {
-                    $record[$name] = $this->formatDate($value, $nestingLevel + 1);
-                } elseif ($value instanceof \Throwable) {
-                    $record[$name] = $this->formatException($value, $nestingLevel + 1);
-                } elseif (is_array($value)) {
-                    $record[$name] = $this->formatArray($value, $nestingLevel + 1);
-                } elseif (is_object($value)) {
-                    $record[$name] = $this->formatObject($value, $nestingLevel + 1);
-                }
+        if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) {
+            return '[...]';
+        }
+
+        foreach ($array as $name => $value) {
+            if ($value instanceof \DateTimeInterface) {
+                $array[$name] = $this->formatDate($value, $nestingLevel + 1);
+            } elseif ($value instanceof \Throwable) {
+                $array[$name] = $this->formatException($value, $nestingLevel + 1);
+            } elseif (is_array($value)) {
+                $array[$name] = $this->formatArray($value, $nestingLevel + 1);
+            } elseif (is_object($value)) {
+                $array[$name] = $this->formatObject($value, $nestingLevel + 1);
             }
-        } else {
-            $record = '[...]';
         }
 
-        return $record;
+        return $array;
     }
 
+    /**
+     * @param mixed $value
+     * @return mixed[]|string
+     */
     protected function formatObject($value, int $nestingLevel)
     {
         $objectVars = get_object_vars($value);
@@ -89,6 +102,9 @@ class MongoDBFormatter implements FormatterInterface
         return $this->formatArray($objectVars, $nestingLevel);
     }
 
+    /**
+     * @return mixed[]|string
+     */
     protected function formatException(\Throwable $exception, int $nestingLevel)
     {
         $formattedException = [

+ 7 - 4
src/Monolog/Formatter/NormalizerFormatter.php

@@ -123,7 +123,7 @@ class NormalizerFormatter implements FormatterInterface
 
     /**
      * @param  mixed                      $data
-     * @return int|bool|string|null|array
+     * @return scalar|array<scalar>
      */
     protected function normalize($data, int $depth = 0)
     {
@@ -189,7 +189,7 @@ class NormalizerFormatter implements FormatterInterface
     }
 
     /**
-     * @return array
+     * @return mixed[]
      */
     protected function normalizeException(Throwable $e, int $depth = 0)
     {
@@ -248,6 +248,9 @@ class NormalizerFormatter implements FormatterInterface
         return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
     }
 
+    /**
+     * @return string
+     */
     protected function formatDate(\DateTimeInterface $date)
     {
         // in case the date format isn't custom then we defer to the custom DateTimeImmutable
@@ -259,12 +262,12 @@ class NormalizerFormatter implements FormatterInterface
         return $date->format($this->dateFormat);
     }
 
-    public function addJsonEncodeOption(int $option)
+    public function addJsonEncodeOption(int $option): void
     {
         $this->jsonEncodeOptions |= $option;
     }
 
-    public function removeJsonEncodeOption(int $option)
+    public function removeJsonEncodeOption(int $option): void
     {
         $this->jsonEncodeOptions &= ~$option;
     }

+ 2 - 0
src/Monolog/Formatter/ScalarFormatter.php

@@ -21,6 +21,8 @@ class ScalarFormatter extends NormalizerFormatter
 {
     /**
      * {@inheritdoc}
+     *
+     * @phpstan-return scalar[] $record
      */
     public function format(array $record): array
     {

+ 10 - 1
src/Monolog/Formatter/WildfireFormatter.php

@@ -19,11 +19,15 @@ use Monolog\Logger;
  * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
  * @author Christophe Coevoet <stof@notk.org>
  * @author Kirill chEbba Chebunin <iam@chebba.org>
+ *
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 class WildfireFormatter extends NormalizerFormatter
 {
     /**
      * Translates Monolog log levels to Wildfire levels.
+     *
+     * @var array<Level, string>
      */
     private $logLevels = [
         Logger::DEBUG     => 'LOG',
@@ -49,6 +53,8 @@ class WildfireFormatter extends NormalizerFormatter
 
     /**
      * {@inheritdoc}
+     *
+     * @return string
      */
     public function format(array $record): string
     {
@@ -108,6 +114,8 @@ class WildfireFormatter extends NormalizerFormatter
 
     /**
      * {@inheritdoc}
+     *
+     * @phpstan-return never
      */
     public function formatBatch(array $records)
     {
@@ -116,7 +124,8 @@ class WildfireFormatter extends NormalizerFormatter
 
     /**
      * {@inheritdoc}
-     * @return int|bool|string|null|array|object
+     *
+     * @return scalar|array<scalar>|object
      */
     protected function normalize($data, int $depth = 0)
     {

+ 12 - 0
src/Monolog/Handler/AbstractHandler.php

@@ -18,10 +18,17 @@ use Monolog\ResettableInterface;
  * Base Handler class providing basic level/bubble support
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 abstract class AbstractHandler extends Handler implements ResettableInterface
 {
+    /**
+     * @var int
+     * @phpstan-var Level
+     */
     protected $level = Logger::DEBUG;
+    /** @var bool */
     protected $bubble = true;
 
     /**
@@ -59,6 +66,8 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
      * Gets minimum logging level at which this handler will be triggered.
      *
      * @return int
+     *
+     * @phpstan-return Level
      */
     public function getLevel(): int
     {
@@ -90,6 +99,9 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
         return $this->bubble;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function reset()
     {
     }

+ 9 - 0
src/Monolog/Handler/AbstractProcessingHandler.php

@@ -18,6 +18,10 @@ namespace Monolog\Handler;
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
  * @author Christophe Coevoet <stof@notk.org>
+ *
+ * @phpstan-import-type LevelName from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
+ * @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed}
  */
 abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
 {
@@ -46,9 +50,14 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
 
     /**
      * Writes the record down to the log of the implementing handler
+     *
+     * @phpstan-param FormattedRecord $record
      */
     abstract protected function write(array $record): void;
 
+    /**
+     * @return void
+     */
     public function reset()
     {
         parent::reset();

+ 6 - 0
src/Monolog/Handler/AbstractSyslogHandler.php

@@ -17,13 +17,18 @@ use Monolog\Formatter\LineFormatter;
 
 /**
  * Common syslog functionality
+ *
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 abstract class AbstractSyslogHandler extends AbstractProcessingHandler
 {
+    /** @var int */
     protected $facility;
 
     /**
      * Translates Monolog log levels to syslog log priorities.
+     * @var array
+     * @phpstan-var array<Level, int>
      */
     protected $logLevels = [
         Logger::DEBUG     => LOG_DEBUG,
@@ -38,6 +43,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
 
     /**
      * List of valid log facility names.
+     * @var array<string, int>
      */
     protected $facilities = [
         'auth'     => LOG_AUTH,

+ 5 - 0
src/Monolog/Handler/AmqpHandler.php

@@ -18,6 +18,9 @@ use PhpAmqpLib\Message\AMQPMessage;
 use PhpAmqpLib\Channel\AMQPChannel;
 use AMQPExchange;
 
+/**
+ * @phpstan-import-type Record from \Monolog\Logger
+ */
 class AmqpHandler extends AbstractProcessingHandler
 {
     /**
@@ -108,6 +111,8 @@ class AmqpHandler extends AbstractProcessingHandler
 
     /**
      * Gets the routing key for the AMQP exchange
+     *
+     * @phpstan-param Record $record
      */
     protected function getRoutingKey(array $record): string
     {

+ 20 - 0
src/Monolog/Handler/BrowserConsoleHandler.php

@@ -19,10 +19,14 @@ use Monolog\Utils;
  * Handler sending logs to browser's javascript console with no browser extension required
  *
  * @author Olivier Poitrey <rs@dailymotion.com>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class BrowserConsoleHandler extends AbstractProcessingHandler
 {
+    /** @var bool */
     protected static $initialized = false;
+    /** @var FormattedRecord[] */
     protected static $records = [];
 
     /**
@@ -165,6 +169,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
         return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
     }
 
+    /**
+     * @return string[]
+     */
     private static function handleStyles(string $formatted): array
     {
         $args = [];
@@ -205,6 +212,10 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
         }, $style);
     }
 
+    /**
+     * @param mixed[] $dict
+     * @return mixed[]
+     */
     private static function dump(string $title, array $dict): array
     {
         $script = [];
@@ -229,13 +240,22 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
         return '"' . addcslashes($arg, "\"\n\\") . '"';
     }
 
+    /**
+     * @param mixed $args
+     */
     private static function call(...$args): string
     {
         $method = array_shift($args);
+        if (!is_string($method)) {
+            throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
+        }
 
         return static::call_array($method, $args);
     }
 
+    /**
+     * @param mixed[] $args
+     */
     private static function call_array(string $method, array $args): string
     {
         return 'c.' . $method . '(' . implode(', ', $args) . ');';

+ 7 - 0
src/Monolog/Handler/BufferHandler.php

@@ -22,6 +22,8 @@ use Monolog\Formatter\FormatterInterface;
  * sending one per log message.
  *
  * @author Christophe Coevoet <stof@notk.org>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
 {
@@ -29,10 +31,15 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
 
     /** @var HandlerInterface */
     protected $handler;
+    /** @var int */
     protected $bufferSize = 0;
+    /** @var int */
     protected $bufferLimit;
+    /** @var bool */
     protected $flushOnOverflow;
+    /** @var Record[] */
     protected $buffer = [];
+    /** @var bool */
     protected $initialized = false;
 
     /**

+ 3 - 0
src/Monolog/Handler/ChromePHPHandler.php

@@ -42,6 +42,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
      */
     protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
 
+    /** @var bool */
     protected static $initialized = false;
 
     /**
@@ -53,12 +54,14 @@ class ChromePHPHandler extends AbstractProcessingHandler
      */
     protected static $overflowed = false;
 
+    /** @var mixed[] */
     protected static $json = [
         'version' => self::VERSION,
         'columns' => ['label', 'log', 'backtrace', 'type'],
         'rows' => [],
     ];
 
+    /** @var bool */
     protected static $sendHeaders = true;
 
     /**

+ 4 - 0
src/Monolog/Handler/CouchDBHandler.php

@@ -22,8 +22,12 @@ use Monolog\Logger;
  */
 class CouchDBHandler extends AbstractProcessingHandler
 {
+    /** @var mixed[] */
     private $options;
 
+    /**
+     * @param mixed[] $options
+     */
     public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true)
     {
         $this->options = array_merge([

+ 9 - 3
src/Monolog/Handler/CubeHandler.php

@@ -22,11 +22,17 @@ use Monolog\Utils;
  */
 class CubeHandler extends AbstractProcessingHandler
 {
-    private $udpConnection;
-    private $httpConnection;
+    /** @var resource|\Socket|null */
+    private $udpConnection = null;
+    /** @var resource|\CurlHandle|null */
+    private $httpConnection = null;
+    /** @var string */
     private $scheme;
+    /** @var string */
     private $host;
+    /** @var int */
     private $port;
+    /** @var string[] */
     private $acceptedSchemes = ['http', 'udp'];
 
     /**
@@ -53,7 +59,7 @@ class CubeHandler extends AbstractProcessingHandler
 
         $this->scheme = $urlInfo['scheme'];
         $this->host = $urlInfo['host'];
-        $this->port = $urlInfo['port'];
+        $this->port = (int) $urlInfo['port'];
 
         parent::__construct($level, $bubble);
     }

+ 1 - 0
src/Monolog/Handler/Curl/Util.php

@@ -20,6 +20,7 @@ use CurlHandle;
  */
 final class Util
 {
+    /** @var array<int> */
     private static $retriableErrorCodes = [
         CURLE_COULDNT_RESOLVE_HOST,
         CURLE_COULDNT_CONNECT,

+ 8 - 0
src/Monolog/Handler/DeduplicationHandler.php

@@ -32,6 +32,8 @@ use Monolog\Logger;
  * same way.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class DeduplicationHandler extends BufferHandler
 {
@@ -100,6 +102,9 @@ class DeduplicationHandler extends BufferHandler
         }
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     private function isDuplicate(array $record): bool
     {
         if (!file_exists($this->deduplicationStore)) {
@@ -166,6 +171,9 @@ class DeduplicationHandler extends BufferHandler
         $this->gc = false;
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     private function appendRecord(array $record): void
     {
         file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);

+ 1 - 0
src/Monolog/Handler/DoctrineCouchDBHandler.php

@@ -23,6 +23,7 @@ use Doctrine\CouchDB\CouchDBClient;
  */
 class DoctrineCouchDBHandler extends AbstractProcessingHandler
 {
+    /** @var CouchDBClient */
     private $client;
 
     public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true)

+ 4 - 0
src/Monolog/Handler/DynamoDbHandler.php

@@ -86,6 +86,10 @@ class DynamoDbHandler extends AbstractProcessingHandler
         ]);
     }
 
+    /**
+     * @param mixed[] $record
+     * @return mixed[]
+     */
     protected function filterEmptyFields(array $record): array
     {
         return array_filter($record, function ($value) {

+ 9 - 2
src/Monolog/Handler/ElasticaHandler.php

@@ -11,6 +11,7 @@
 
 namespace Monolog\Handler;
 
+use Elastica\Document;
 use Monolog\Formatter\FormatterInterface;
 use Monolog\Formatter\ElasticaFormatter;
 use Monolog\Logger;
@@ -41,13 +42,13 @@ class ElasticaHandler extends AbstractProcessingHandler
     protected $client;
 
     /**
-     * @var array Handler config options
+     * @var mixed[] Handler config options
      */
     protected $options = [];
 
     /**
      * @param Client     $client  Elastica Client object
-     * @param array      $options Handler configuration
+     * @param mixed[]    $options Handler configuration
      * @param int|string $level   The minimum logging level at which this handler will be triggered
      * @param bool       $bubble  Whether the messages that are handled can bubble up the stack or not
      */
@@ -85,6 +86,9 @@ class ElasticaHandler extends AbstractProcessingHandler
         throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter');
     }
 
+    /**
+     * @return mixed[]
+     */
     public function getOptions(): array
     {
         return $this->options;
@@ -109,6 +113,9 @@ class ElasticaHandler extends AbstractProcessingHandler
 
     /**
      * Use Elasticsearch bulk API to send list of documents
+     *
+     * @param Document[] $documents
+     *
      * @throws \RuntimeException
      */
     protected function bulkSend(array $documents): void

+ 6 - 6
src/Monolog/Handler/ElasticsearchHandler.php

@@ -49,13 +49,13 @@ class ElasticsearchHandler extends AbstractProcessingHandler
     protected $client;
 
     /**
-     * @var array Handler config options
+     * @var mixed[] Handler config options
      */
     protected $options = [];
 
     /**
      * @param Client     $client  Elasticsearch Client object
-     * @param array      $options Handler configuration
+     * @param mixed[]    $options Handler configuration
      * @param string|int $level   The minimum logging level at which this handler will be triggered
      * @param bool       $bubble  Whether the messages that are handled can bubble up the stack or not
      */
@@ -96,7 +96,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
     /**
      * Getter options
      *
-     * @return array
+     * @return mixed[]
      */
     public function getOptions(): array
     {
@@ -123,7 +123,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
     /**
      * Use Elasticsearch bulk API to send list of documents
      *
-     * @param  array             $records
+     * @param  array[]           $records Records + _index/_type keys
      * @throws \RuntimeException
      */
     protected function bulkSend(array $records): void
@@ -162,7 +162,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
      *
      * Only the first error is converted into an exception.
      *
-     * @param array $responses returned by $this->client->bulk()
+     * @param mixed[] $responses returned by $this->client->bulk()
      */
     protected function createExceptionFromResponses(array $responses): ElasticsearchRuntimeException
     {
@@ -178,7 +178,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
     /**
      * Creates elasticsearch exception from error array
      *
-     * @param array $error
+     * @param mixed[] $error
      */
     protected function createExceptionFromError(array $error): ElasticsearchRuntimeException
     {

+ 3 - 1
src/Monolog/Handler/ErrorLogHandler.php

@@ -25,7 +25,9 @@ class ErrorLogHandler extends AbstractProcessingHandler
     public const OPERATING_SYSTEM = 0;
     public const SAPI = 4;
 
+    /** @var int */
     protected $messageType;
+    /** @var bool */
     protected $expandNewlines;
 
     /**
@@ -49,7 +51,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
     }
 
     /**
-     * @return array With all available types
+     * @return int[] With all available types
      */
     public static function getAvailableTypes(): array
     {

+ 18 - 2
src/Monolog/Handler/FilterHandler.php

@@ -14,6 +14,7 @@ namespace Monolog\Handler;
 use Monolog\Logger;
 use Monolog\ResettableInterface;
 use Monolog\Formatter\FormatterInterface;
+use Psr\Log\LogLevel;
 
 /**
  * Simple handler wrapper that filters records based on a list of levels
@@ -22,6 +23,10 @@ use Monolog\Formatter\FormatterInterface;
  *
  * @author Hennadiy Verkh
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
+ * @phpstan-import-type LevelName from \Monolog\Logger
  */
 class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
 {
@@ -30,7 +35,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
     /**
      * Handler or factory callable($record, $this)
      *
-     * @var callable|\Monolog\Handler\HandlerInterface
+     * @var callable|HandlerInterface
+     * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
      */
     protected $handler;
 
@@ -38,6 +44,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
      * Minimum level for logs that are passed to handler
      *
      * @var int[]
+     * @phpstan-var Level[]
      */
     protected $acceptedLevels;
 
@@ -49,12 +56,14 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
     protected $bubble;
 
     /**
-     * @psalm-param HandlerInterface|callable(?array, HandlerInterface): HandlerInterface $handler
+     * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
      *
      * @param callable|HandlerInterface $handler        Handler or factory callable($record|null, $filterHandler).
      * @param int|array                 $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
      * @param int|string                $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
+     *
+     * @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
      */
     public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
     {
@@ -67,6 +76,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
         }
     }
 
+    /**
+     * @phpstan-return Level[]
+     */
     public function getAcceptedLevels(): array
     {
         return array_flip($this->acceptedLevels);
@@ -75,6 +87,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
     /**
      * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
      * @param int|string       $maxLevel       Maximum level or level name to accept, only used if $minLevelOrList is not an array
+     *
+     * @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
      */
     public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
     {
@@ -141,6 +155,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
      * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
      *
      * @return HandlerInterface
+     *
+     * @phpstan-param Record $record
      */
     public function getHandler(array $record = null)
     {

+ 4 - 0
src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php

@@ -15,11 +15,15 @@ namespace Monolog\Handler\FingersCrossed;
  * Interface for activation strategies for the FingersCrossedHandler.
  *
  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 interface ActivationStrategyInterface
 {
     /**
      * Returns whether the given record activates the handler.
+     *
+     * @phpstan-param Record $record
      */
     public function isHandlerActivated(array $record): bool;
 }

+ 11 - 3
src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php

@@ -32,6 +32,9 @@ use Monolog\Logger;
  * </code>
  *
  * @author Mike Meessen <netmikey@gmail.com>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 class ChannelLevelActivationStrategy implements ActivationStrategyInterface
 {
@@ -41,13 +44,15 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
     private $defaultActionLevel;
 
     /**
-     * @var array
+     * @var array<string, Level>
      */
     private $channelToActionLevel;
 
     /**
-     * @param int|string $defaultActionLevel   The default action level to be used if the record's category doesn't match any
-     * @param array      $channelToActionLevel An array that maps channel names to action levels.
+     * @param int|string         $defaultActionLevel   The default action level to be used if the record's category doesn't match any
+     * @param array<string, int> $channelToActionLevel An array that maps channel names to action levels.
+     *
+     * @phpstan-param array<string, Level> $channelToActionLevel
      */
     public function __construct($defaultActionLevel, array $channelToActionLevel = [])
     {
@@ -55,6 +60,9 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
         $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     public function isHandlerActivated(array $record): bool
     {
         if (isset($this->channelToActionLevel[$record['channel']])) {

+ 22 - 3
src/Monolog/Handler/FingersCrossedHandler.php

@@ -32,23 +32,40 @@ use Monolog\Formatter\FormatterInterface;
  * Monolog\Handler\FingersCrossed\ namespace.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
+ * @phpstan-import-type LevelName from \Monolog\Logger
  */
 class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
 {
     use ProcessableHandlerTrait;
 
-    /** @var HandlerInterface */
+    /**
+     * @var callable|HandlerInterface
+     * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
+     */
     protected $handler;
+    /** @var ActivationStrategyInterface */
     protected $activationStrategy;
+    /** @var bool */
     protected $buffering = true;
+    /** @var int */
     protected $bufferSize;
+    /** @var Record[] */
     protected $buffer = [];
+    /** @var bool */
     protected $stopBuffering;
+    /**
+     * @var ?int
+     * @phpstan-var ?Level
+     */
     protected $passthruLevel;
+    /** @var bool */
     protected $bubble;
 
     /**
-     * @psalm-param HandlerInterface|callable(?array, FingersCrossedHandler): HandlerInterface $handler
+     * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
      *
      * @param callable|HandlerInterface              $handler            Handler or factory callable($record|null, $fingersCrossedHandler).
      * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
@@ -171,7 +188,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
                 return $record['level'] >= $level;
             });
             if (count($this->buffer) > 0) {
-                $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
+                $this->getHandler(end($this->buffer))->handleBatch($this->buffer);
             }
         }
 
@@ -185,6 +202,8 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
      * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
      *
      * @return HandlerInterface
+     *
+     * @phpstan-param Record $record
      */
     public function getHandler(array $record = null)
     {

+ 13 - 4
src/Monolog/Handler/FirePHPHandler.php

@@ -18,6 +18,8 @@ use Monolog\Formatter\FormatterInterface;
  * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
  *
  * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class FirePHPHandler extends AbstractProcessingHandler
 {
@@ -45,6 +47,7 @@ class FirePHPHandler extends AbstractProcessingHandler
 
     /**
      * Whether or not Wildfire vendor-specific headers have been generated & sent yet
+     * @var bool
      */
     protected static $initialized = false;
 
@@ -54,14 +57,15 @@ class FirePHPHandler extends AbstractProcessingHandler
      */
     protected static $messageIndex = 1;
 
+    /** @var bool */
     protected static $sendHeaders = true;
 
     /**
      * Base header creation function used by init headers & record headers
      *
-     * @param  array  $meta    Wildfire Plugin, Protocol & Structure Indexes
-     * @param  string $message Log message
-     * @return array  Complete header string ready for the client as key and message as value
+     * @param  array<int|string>     $meta    Wildfire Plugin, Protocol & Structure Indexes
+     * @param  string                $message Log message
+     * @return array<string, string> Complete header string ready for the client as key and message as value
      */
     protected function createHeader(array $meta, string $message): array
     {
@@ -73,7 +77,11 @@ class FirePHPHandler extends AbstractProcessingHandler
     /**
      * Creates message header from record
      *
+     * @return array<string, string>
+     *
      * @see createHeader()
+     *
+     * @phpstan-param FormattedRecord $record
      */
     protected function createRecordHeader(array $record): array
     {
@@ -98,6 +106,8 @@ class FirePHPHandler extends AbstractProcessingHandler
      *
      * @see createHeader()
      * @see sendHeader()
+     *
+     * @return array<string, string>
      */
     protected function getInitHeaders(): array
     {
@@ -124,7 +134,6 @@ class FirePHPHandler extends AbstractProcessingHandler
      *
      * @see sendHeader()
      * @see sendInitHeaders()
-     * @param array $record
      */
     protected function write(array $record): void
     {

+ 4 - 0
src/Monolog/Handler/FleepHookHandler.php

@@ -22,6 +22,8 @@ use Monolog\Logger;
  *
  * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
  * @author Ando Roots <ando@sqroot.eu>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class FleepHookHandler extends SocketHandler
 {
@@ -104,6 +106,8 @@ class FleepHookHandler extends SocketHandler
 
     /**
      * Builds the body of API call
+     *
+     * @phpstan-param FormattedRecord $record
      */
     private function buildContent(array $record): string
     {

+ 4 - 2
src/Monolog/Handler/FlowdockHandler.php

@@ -26,6 +26,8 @@ use Monolog\Formatter\FormatterInterface;
  *
  * @author Dominik Liebler <liebler.dominik@gmail.com>
  * @see https://www.flowdock.com/api/push
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class FlowdockHandler extends SocketHandler
 {
@@ -72,8 +74,6 @@ class FlowdockHandler extends SocketHandler
 
     /**
      * {@inheritdoc}
-     *
-     * @param array $record
      */
     protected function write(array $record): void
     {
@@ -94,6 +94,8 @@ class FlowdockHandler extends SocketHandler
 
     /**
      * Builds the body of API call
+     *
+     * @phpstan-param FormattedRecord $record
      */
     private function buildContent(array $record): string
     {

+ 1 - 0
src/Monolog/Handler/GroupHandler.php

@@ -25,6 +25,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
 
     /** @var HandlerInterface[] */
     protected $handlers;
+    /** @var bool */
     protected $bubble;
 
     /**

+ 9 - 0
src/Monolog/Handler/HandlerInterface.php

@@ -15,6 +15,9 @@ namespace Monolog\Handler;
  * Interface that all Monolog Handlers must implement
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 interface HandlerInterface
 {
@@ -30,6 +33,8 @@ interface HandlerInterface
      * @param array $record Partial log record containing only a level key
      *
      * @return bool
+     *
+     * @phpstan-param array{level: Level} $record
      */
     public function isHandling(array $record): bool;
 
@@ -46,6 +51,8 @@ interface HandlerInterface
      * @param  array $record The record to handle
      * @return bool  true means that this handler handled the record, and that bubbling is not permitted.
      *                      false means the record was either not processed or that this handler allows bubbling.
+     *
+     * @phpstan-param Record $record
      */
     public function handle(array $record): bool;
 
@@ -53,6 +60,8 @@ interface HandlerInterface
      * Handles a set of records at once.
      *
      * @param array $records The records to handle (an array of record arrays)
+     *
+     * @phpstan-param Record[] $records
      */
     public function handleBatch(array $records): void;
 

+ 2 - 0
src/Monolog/Handler/IFTTTHandler.php

@@ -27,7 +27,9 @@ use Monolog\Utils;
  */
 class IFTTTHandler extends AbstractProcessingHandler
 {
+    /** @var string */
     private $eventName;
+    /** @var string */
     private $secretKey;
 
     /**

+ 2 - 0
src/Monolog/Handler/LogglyHandler.php

@@ -37,8 +37,10 @@ class LogglyHandler extends AbstractProcessingHandler
      */
     protected $curlHandlers = [];
 
+    /** @var string */
     protected $token;
 
+    /** @var string[] */
     protected $tag = [];
 
     /**

+ 8 - 0
src/Monolog/Handler/MailHandler.php

@@ -18,6 +18,8 @@ use Monolog\Formatter\HtmlFormatter;
  * Base class for all mail handlers
  *
  * @author Gyula Sallai
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 abstract class MailHandler extends AbstractProcessingHandler
 {
@@ -45,6 +47,8 @@ abstract class MailHandler extends AbstractProcessingHandler
      *
      * @param string $content formatted email body to be sent
      * @param array  $records the array of log records that formed this content
+     *
+     * @phpstan-param Record[] $records
      */
     abstract protected function send(string $content, array $records): void;
 
@@ -56,6 +60,10 @@ abstract class MailHandler extends AbstractProcessingHandler
         $this->send((string) $record['formatted'], [$record]);
     }
 
+    /**
+     * @phpstan-param Record[] $records
+     * @phpstan-return Record
+     */
     protected function getHighestRecord(array $records): array
     {
         $highestRecord = null;

+ 3 - 0
src/Monolog/Handler/MongoDBHandler.php

@@ -33,8 +33,11 @@ use Monolog\Formatter\MongoDBFormatter;
  */
 class MongoDBHandler extends AbstractProcessingHandler
 {
+    /** @var \MongoDB\Collection */
     private $collection;
+    /** @var Client|Manager */
     private $manager;
+    /** @var string */
     private $namespace;
 
     /**

+ 6 - 6
src/Monolog/Handler/NativeMailerHandler.php

@@ -24,7 +24,7 @@ class NativeMailerHandler extends MailHandler
 {
     /**
      * The email addresses to which the message will be sent
-     * @var array
+     * @var string[]
      */
     protected $to;
 
@@ -36,13 +36,13 @@ class NativeMailerHandler extends MailHandler
 
     /**
      * Optional headers for the message
-     * @var array
+     * @var string[]
      */
     protected $headers = [];
 
     /**
      * Optional parameters for the message
-     * @var array
+     * @var string[]
      */
     protected $parameters = [];
 
@@ -65,7 +65,7 @@ class NativeMailerHandler extends MailHandler
     protected $encoding = 'utf-8';
 
     /**
-     * @param string|array $to             The receiver of the mail
+     * @param string|string[] $to             The receiver of the mail
      * @param string       $subject        The subject of the mail
      * @param string       $from           The sender of the mail
      * @param string|int   $level          The minimum logging level at which this handler will be triggered
@@ -84,7 +84,7 @@ class NativeMailerHandler extends MailHandler
     /**
      * Add headers to the message
      *
-     * @param string|array $headers Custom added headers
+     * @param string|string[] $headers Custom added headers
      */
     public function addHeader($headers): self
     {
@@ -101,7 +101,7 @@ class NativeMailerHandler extends MailHandler
     /**
      * Add parameters to the message
      *
-     * @param string|array $parameters Custom added parameters
+     * @param string|string[] $parameters Custom added parameters
      */
     public function addParameter($parameters): self
     {

+ 6 - 2
src/Monolog/Handler/NewRelicHandler.php

@@ -30,14 +30,14 @@ class NewRelicHandler extends AbstractProcessingHandler
     /**
      * Name of the New Relic application that will receive logs from this handler.
      *
-     * @var string|null
+     * @var ?string
      */
     protected $appName;
 
     /**
      * Name of the current transaction
      *
-     * @var string|null
+     * @var ?string
      */
     protected $transactionName;
 
@@ -135,6 +135,8 @@ class NewRelicHandler extends AbstractProcessingHandler
     /**
      * Returns the appname where this log should be sent. Each log can override the default appname, set in this
      * handler's constructor, by providing the appname in it's context.
+     *
+     * @param mixed[] $context
      */
     protected function getAppName(array $context): ?string
     {
@@ -148,6 +150,8 @@ class NewRelicHandler extends AbstractProcessingHandler
     /**
      * Returns the name of the current transaction. Each log can override the default transaction name, set in this
      * handler's constructor, by providing the transaction_name in it's context
+     *
+     * @param mixed[] $context
      */
     protected function getTransactionName(array $context): ?string
     {

+ 1 - 4
src/Monolog/Handler/OverflowHandler.php

@@ -87,10 +87,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
      * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
      * calling further handlers in the stack with a given log record.
      *
-     * @param array $record The record to handle
-     *
-     * @return Boolean true means that this handler handled the record, and that bubbling is not permitted.
-     *                 false means the record was either not processed or that this handler allows bubbling.
+     * {@inheritdoc}
      */
     public function handle(array $record): bool
     {

+ 27 - 4
src/Monolog/Handler/PHPConsoleHandler.php

@@ -37,9 +37,12 @@ use PhpConsole\Helper;
  *      PC::debug($_SERVER); // PHP Console debugger for any type of vars
  *
  * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class PHPConsoleHandler extends AbstractProcessingHandler
 {
+    /** @var array<string, mixed> */
     private $options = [
         'enabled' => true, // bool Is PHP Console server enabled
         'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
@@ -67,10 +70,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
     private $connector;
 
     /**
-     * @param  array             $options   See \Monolog\Handler\PHPConsoleHandler::$options for more details
-     * @param  Connector|null    $connector Instance of \PhpConsole\Connector class (optional)
-     * @param  string|int        $level     The minimum logging level at which this handler will be triggered.
-     * @param  bool              $bubble    Whether the messages that are handled can bubble up the stack or not.
+     * @param  array<string, mixed> $options   See \Monolog\Handler\PHPConsoleHandler::$options for more details
+     * @param  Connector|null       $connector Instance of \PhpConsole\Connector class (optional)
+     * @param  string|int           $level     The minimum logging level at which this handler will be triggered.
+     * @param  bool                 $bubble    Whether the messages that are handled can bubble up the stack or not.
      * @throws \RuntimeException
      */
     public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
@@ -83,6 +86,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
         $this->connector = $this->initConnector($connector);
     }
 
+    /**
+     * @param array<string, mixed> $options
+     * @return array<string, mixed>
+     */
     private function initOptions(array $options): array
     {
         $wrongOptions = array_diff(array_keys($options), array_keys($this->options));
@@ -153,6 +160,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
         return $this->connector;
     }
 
+    /**
+     * @return array<string, mixed>
+     */
     public function getOptions(): array
     {
         return $this->options;
@@ -181,6 +191,9 @@ class PHPConsoleHandler extends AbstractProcessingHandler
         }
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     private function handleDebugRecord(array $record): void
     {
         $tags = $this->getRecordTags($record);
@@ -191,11 +204,17 @@ class PHPConsoleHandler extends AbstractProcessingHandler
         $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     private function handleExceptionRecord(array $record): void
     {
         $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
     }
 
+    /**
+     * @phpstan-param Record $record
+     */
     private function handleErrorRecord(array $record): void
     {
         $context = $record['context'];
@@ -209,6 +228,10 @@ class PHPConsoleHandler extends AbstractProcessingHandler
         );
     }
 
+    /**
+     * @phpstan-param Record $record
+     * @return string
+     */
     private function getRecordTags(array &$record)
     {
         $tags = null;

+ 2 - 2
src/Monolog/Handler/ProcessHandler.php

@@ -44,12 +44,12 @@ class ProcessHandler extends AbstractProcessingHandler
     private $cwd;
 
     /**
-     * @var array
+     * @var resource[]
      */
     private $pipes = [];
 
     /**
-     * @var array
+     * @var array<int, string[]>
      */
     protected const DESCRIPTOR_SPEC = [
         0 => ['pipe', 'r'],  // STDIN is a pipe that the child will read from

+ 5 - 3
src/Monolog/Handler/ProcessableHandlerInterface.php

@@ -17,13 +17,15 @@ use Monolog\Processor\ProcessorInterface;
  * Interface to describe loggers that have processors
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 interface ProcessableHandlerInterface
 {
     /**
      * Adds a processor in the stack.
      *
-     * @psalm-param ProcessorInterface|callable(array): array $callback
+     * @psalm-param ProcessorInterface|callable(Record): Record $callback
      *
      * @param  ProcessorInterface|callable $callback
      * @return HandlerInterface            self
@@ -33,10 +35,10 @@ interface ProcessableHandlerInterface
     /**
      * Removes the processor on top of the stack and returns it.
      *
-     * @psalm-return callable(array): array
+     * @psalm-return ProcessorInterface|callable(Record): Record $callback
      *
      * @throws \LogicException In case the processor stack is empty
-     * @return callable
+     * @return callable|ProcessorInterface
      */
     public function popProcessor(): callable;
 }

+ 7 - 0
src/Monolog/Handler/ProcessableHandlerTrait.php

@@ -12,16 +12,20 @@
 namespace Monolog\Handler;
 
 use Monolog\ResettableInterface;
+use Monolog\Processor\ProcessorInterface;
 
 /**
  * Helper trait for implementing ProcessableInterface
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 trait ProcessableHandlerTrait
 {
     /**
      * @var callable[]
+     * @phpstan-var array<ProcessorInterface|callable(Record): Record>
      */
     protected $processors = [];
 
@@ -49,6 +53,9 @@ trait ProcessableHandlerTrait
 
     /**
      * Processes a record.
+     *
+     * @phpstan-param  Record $record
+     * @phpstan-return Record
      */
     protected function processRecord(array $record): array
     {

+ 25 - 3
src/Monolog/Handler/PushoverHandler.php

@@ -19,24 +19,35 @@ use Monolog\Utils;
  *
  * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
  * @see    https://www.pushover.net/api
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class PushoverHandler extends SocketHandler
 {
+    /** @var string */
     private $token;
+    /** @var array<int|string> */
     private $users;
+    /** @var ?string */
     private $title;
-    private $user;
+    /** @var string|int|null */
+    private $user = null;
+    /** @var int */
     private $retry;
+    /** @var int */
     private $expire;
 
+    /** @var int */
     private $highPriorityLevel;
+    /** @var int */
     private $emergencyLevel;
+    /** @var bool */
     private $useFormattedMessage = false;
 
     /**
      * All parameters that can be sent to Pushover
      * @see https://pushover.net/api
-     * @var array
+     * @var array<string, bool>
      */
     private $parameterNames = [
         'token' => true,
@@ -57,7 +68,7 @@ class PushoverHandler extends SocketHandler
     /**
      * Sounds the api supports by default
      * @see https://pushover.net/api#sounds
-     * @var array
+     * @var string[]
      */
     private $sounds = [
         'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
@@ -81,6 +92,8 @@ class PushoverHandler extends SocketHandler
      *                                        send the same notification to the user.
      * @param int          $expire            The expire parameter specifies how many seconds your notification will continue
      *                                        to be retried for (every retry seconds).
+     *
+     * @phpstan-param string|array<int|string> $users
      */
     public function __construct(
         string $token,
@@ -113,6 +126,9 @@ class PushoverHandler extends SocketHandler
         return $this->buildHeader($content) . $content;
     }
 
+    /**
+     * @phpstan-param FormattedRecord $record
+     */
     private function buildContent(array $record): string
     {
         // Pushover has a limit of 512 characters on title and message combined.
@@ -177,6 +193,9 @@ class PushoverHandler extends SocketHandler
         $this->user = null;
     }
 
+    /**
+     * @param int|string $value
+     */
     public function setHighPriorityLevel($value): self
     {
         $this->highPriorityLevel = Logger::toMonologLevel($value);
@@ -184,6 +203,9 @@ class PushoverHandler extends SocketHandler
         return $this;
     }
 
+    /**
+     * @param int|string $value
+     */
     public function setEmergencyLevel($value): self
     {
         $this->emergencyLevel = Logger::toMonologLevel($value);

+ 7 - 0
src/Monolog/Handler/RedisHandler.php

@@ -25,11 +25,16 @@ use Monolog\Logger;
  *   $log->pushHandler($redis);
  *
  * @author Thomas Tourlourat <thomas@tourlourat.com>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class RedisHandler extends AbstractProcessingHandler
 {
+    /** @var \Predis\Client|\Redis */
     private $redisClient;
+    /** @var string */
     private $redisKey;
+    /** @var int */
     protected $capSize;
 
     /**
@@ -67,6 +72,8 @@ class RedisHandler extends AbstractProcessingHandler
     /**
      * Write and cap the collection
      * Writes the record to the redis list and caps its
+     *
+     * @phpstan-param FormattedRecord $record
      */
     protected function writeCapped(array $record): void
     {

+ 2 - 0
src/Monolog/Handler/RedisPubSubHandler.php

@@ -28,7 +28,9 @@ use Monolog\Logger;
  */
 class RedisPubSubHandler extends AbstractProcessingHandler
 {
+    /** @var \Predis\Client|\Redis */
     private $redisClient;
+    /** @var string */
     private $channelKey;
 
     /**

+ 2 - 0
src/Monolog/Handler/RollbarHandler.php

@@ -38,6 +38,7 @@ class RollbarHandler extends AbstractProcessingHandler
      */
     protected $rollbarLogger;
 
+    /** @var string[] */
     protected $levelMap = [
         Logger::DEBUG     => 'debug',
         Logger::INFO      => 'info',
@@ -56,6 +57,7 @@ class RollbarHandler extends AbstractProcessingHandler
      */
     private $hasRecords = false;
 
+    /** @var bool */
     protected $initialized = false;
 
     /**

+ 6 - 0
src/Monolog/Handler/RotatingFileHandler.php

@@ -30,11 +30,17 @@ class RotatingFileHandler extends StreamHandler
     public const FILE_PER_MONTH = 'Y-m';
     public const FILE_PER_YEAR = 'Y';
 
+    /** @var string */
     protected $filename;
+    /** @var int */
     protected $maxFiles;
+    /** @var bool */
     protected $mustRotate;
+    /** @var \DateTimeImmutable */
     protected $nextRotation;
+    /** @var string */
     protected $filenameFormat;
+    /** @var string */
     protected $dateFormat;
 
     /**

+ 8 - 2
src/Monolog/Handler/SamplingHandler.php

@@ -26,13 +26,17 @@ use Monolog\Formatter\FormatterInterface;
  *
  * @author Bryan Davis <bd808@wikimedia.org>
  * @author Kunal Mehta <legoktm@gmail.com>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
 {
     use ProcessableHandlerTrait;
 
     /**
-     * @var callable|HandlerInterface $handler
+     * @var HandlerInterface|callable
+     * @phpstan-var HandlerInterface|callable(Record, HandlerInterface): HandlerInterface
      */
     protected $handler;
 
@@ -42,7 +46,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
     protected $factor;
 
     /**
-     * @psalm-param HandlerInterface|callable(array, HandlerInterface): HandlerInterface $handler
+     * @psalm-param HandlerInterface|callable(Record, 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)
@@ -81,6 +85,8 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
      *
      * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
      *
+     * @phpstan-param Record|array{level: Level}|null $record
+     *
      * @return HandlerInterface
      */
     public function getHandler(array $record = null)

+ 8 - 8
src/Monolog/Handler/SendGridHandler.php

@@ -40,7 +40,7 @@ class SendGridHandler extends MailHandler
 
     /**
      * The email addresses to which the message will be sent
-     * @var array
+     * @var string[]
      */
     protected $to;
 
@@ -51,13 +51,13 @@ class SendGridHandler extends MailHandler
     protected $subject;
 
     /**
-     * @param string       $apiUser The SendGrid API User
-     * @param string       $apiKey  The SendGrid API Key
-     * @param string       $from    The sender of the email
-     * @param string|array $to      The recipients of the email
-     * @param string       $subject The subject of the mail
-     * @param int|string   $level   The minimum logging level at which this handler will be triggered
-     * @param bool         $bubble  Whether the messages that are handled can bubble up the stack or not
+     * @param string          $apiUser The SendGrid API User
+     * @param string          $apiKey  The SendGrid API Key
+     * @param string          $from    The sender of the email
+     * @param string|string[] $to      The recipients of the email
+     * @param string          $subject The subject of the mail
+     * @param int|string      $level   The minimum logging level at which this handler will be triggered
+     * @param bool            $bubble  Whether the messages that are handled can bubble up the stack or not
      */
     public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true)
     {

+ 22 - 2
src/Monolog/Handler/Slack/SlackRecord.php

@@ -23,6 +23,8 @@ use Monolog\Formatter\FormatterInterface;
  * @author Haralan Dobrev <hkdobrev@gmail.com>
  * @see    https://api.slack.com/incoming-webhooks
  * @see    https://api.slack.com/docs/message-attachments
+ *
+ * @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler
  */
 class SlackRecord
 {
@@ -72,7 +74,7 @@ class SlackRecord
 
     /**
      * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
-     * @var array
+     * @var string[]
      */
     private $excludeFields;
 
@@ -86,6 +88,9 @@ class SlackRecord
      */
     private $normalizerFormatter;
 
+    /**
+     * @param string[] $excludeFields
+     */
     public function __construct(
         ?string $channel = null,
         ?string $username = null,
@@ -114,6 +119,9 @@ class SlackRecord
     /**
      * Returns required data in format that Slack
      * is expecting.
+     *
+     * @phpstan-param FormattedRecord $record
+     * @phpstan-return string[]
      */
     public function getSlackData(array $record): array
     {
@@ -208,6 +216,8 @@ class SlackRecord
 
     /**
      * Stringifies an array of key/value pairs to be used in attachment fields
+     *
+     * @param mixed[] $fields
      */
     public function stringify(array $fields): string
     {
@@ -285,6 +295,9 @@ class SlackRecord
         return $this;
     }
 
+    /**
+     * @param string[] $excludeFields
+     */
     public function excludeFields(array $excludeFields = []): self
     {
         $this->excludeFields = $excludeFields;
@@ -302,7 +315,8 @@ class SlackRecord
     /**
      * Generates attachment field
      *
-     * @param string|array $value
+     * @param string|mixed[] $value
+     * @return array{title: string, value: string, short: false}
      */
     private function generateAttachmentField(string $title, $value): array
     {
@@ -319,6 +333,9 @@ class SlackRecord
 
     /**
      * Generates a collection of attachment fields from array
+     *
+     * @param mixed[] $data
+     * @return array<array{title: string, value: string, short: false}>
      */
     private function generateAttachmentFields(array $data): array
     {
@@ -332,6 +349,9 @@ class SlackRecord
 
     /**
      * Get a copy of record with fields excluded according to $this->excludeFields
+     *
+     * @phpstan-param FormattedRecord $record
+     * @return mixed[]
      */
     private function removeExcludedFields(array $record): array
     {

+ 12 - 1
src/Monolog/Handler/SlackHandler.php

@@ -21,6 +21,8 @@ use Monolog\Handler\Slack\SlackRecord;
  *
  * @author Greg Kedzierski <greg@gregkedzierski.com>
  * @see    https://api.slack.com/
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class SlackHandler extends SocketHandler
 {
@@ -46,7 +48,7 @@ class SlackHandler extends SocketHandler
      * @param  bool                      $bubble                 Whether the messages that are handled can bubble up the stack or not
      * @param  bool                      $useShortAttachment     Whether the context/extra messages added to Slack as attachments are in a short style
      * @param  bool                      $includeContextAndExtra Whether the attachment should include context and extra data
-     * @param  array                     $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
+     * @param  string[]                  $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
      * @throws MissingExtensionException If no OpenSSL PHP extension configured
      */
     public function __construct(
@@ -102,6 +104,8 @@ class SlackHandler extends SocketHandler
 
     /**
      * Builds the body of API call
+     *
+     * @phpstan-param FormattedRecord $record
      */
     private function buildContent(array $record): string
     {
@@ -110,6 +114,10 @@ class SlackHandler extends SocketHandler
         return http_build_query($dataArray);
     }
 
+    /**
+     * @phpstan-param FormattedRecord $record
+     * @return string[]
+     */
     protected function prepareContentData(array $record): array
     {
         $dataArray = $this->slackRecord->getSlackData($record);
@@ -224,6 +232,9 @@ class SlackHandler extends SocketHandler
         return $this;
     }
 
+    /**
+     * @param string[] $excludeFields
+     */
     public function excludeFields(array $excludeFields): self
     {
         $this->slackRecord->excludeFields($excludeFields);

+ 1 - 3
src/Monolog/Handler/SlackWebhookHandler.php

@@ -46,7 +46,7 @@ class SlackWebhookHandler extends AbstractProcessingHandler
      * @param bool        $includeContextAndExtra Whether the attachment should include context and extra data
      * @param string|int  $level                  The minimum logging level at which this handler will be triggered
      * @param bool        $bubble                 Whether the messages that are handled can bubble up the stack or not
-     * @param array       $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
+     * @param string[]    $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
      */
     public function __construct(
         string $webhookUrl,
@@ -87,8 +87,6 @@ class SlackWebhookHandler extends AbstractProcessingHandler
 
     /**
      * {@inheritdoc}
-     *
-     * @param array $record
      */
     protected function write(array $record): void
     {

+ 32 - 9
src/Monolog/Handler/SocketHandler.php

@@ -18,10 +18,15 @@ use Monolog\Logger;
  *
  * @author Pablo de Leon Belloc <pablolb@gmail.com>
  * @see    http://php.net/manual/en/function.fsockopen.php
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class SocketHandler extends AbstractProcessingHandler
 {
+    /** @var string */
     private $connectionString;
+    /** @var float */
     private $connectionTimeout;
     /** @var resource|null */
     private $resource;
@@ -29,14 +34,18 @@ class SocketHandler extends AbstractProcessingHandler
     private $timeout = 0.0;
     /** @var float */
     private $writingTimeout = 10.0;
+    /** @var ?int */
     private $lastSentBytes = null;
     /** @var int */
     private $chunkSize = null;
+    /** @var bool */
     private $persistent = false;
-    private $errno;
-    private $errstr;
+    /** @var ?int */
+    private $errno = null;
+    /** @var ?string */
+    private $errstr = null;
     /** @var ?float */
-    private $lastWritingAt;
+    private $lastWritingAt = null;
 
     /**
      * @param string     $connectionString Socket connection string
@@ -53,7 +62,7 @@ class SocketHandler extends AbstractProcessingHandler
     /**
      * Connect (if necessary) and write to the socket
      *
-     * @param array $record
+     * {@inheritDoc}
      *
      * @throws \UnexpectedValueException
      * @throws \RuntimeException
@@ -208,6 +217,8 @@ class SocketHandler extends AbstractProcessingHandler
 
     /**
      * Wrapper to allow mocking
+     *
+     * @return resource|bool
      */
     protected function pfsockopen()
     {
@@ -216,6 +227,8 @@ class SocketHandler extends AbstractProcessingHandler
 
     /**
      * Wrapper to allow mocking
+     *
+     * @return resource|bool
      */
     protected function fsockopen()
     {
@@ -226,6 +239,8 @@ class SocketHandler extends AbstractProcessingHandler
      * Wrapper to allow mocking
      *
      * @see http://php.net/manual/en/function.stream-set-timeout.php
+     *
+     * @return bool
      */
     protected function streamSetTimeout()
     {
@@ -239,6 +254,8 @@ class SocketHandler extends AbstractProcessingHandler
      * Wrapper to allow mocking
      *
      * @see http://php.net/manual/en/function.stream-set-chunk-size.php
+     *
+     * @return int|bool
      */
     protected function streamSetChunkSize()
     {
@@ -247,29 +264,32 @@ class SocketHandler extends AbstractProcessingHandler
 
     /**
      * Wrapper to allow mocking
+     *
+     * @return int|bool
      */
-    protected function fwrite($data)
+    protected function fwrite(string $data)
     {
         return @fwrite($this->resource, $data);
     }
 
     /**
      * Wrapper to allow mocking
+     *
+     * @return mixed[]|bool
      */
     protected function streamGetMetadata()
     {
         return stream_get_meta_data($this->resource);
     }
 
-    private function validateTimeout($value)
+    private function validateTimeout(float $value): void
     {
-        $ok = filter_var($value, FILTER_VALIDATE_FLOAT);
-        if ($ok === false || $value < 0) {
+        if ($value < 0) {
             throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
         }
     }
 
-    private function connectIfNotConnected()
+    private function connectIfNotConnected(): void
     {
         if ($this->isConnected()) {
             return;
@@ -277,6 +297,9 @@ class SocketHandler extends AbstractProcessingHandler
         $this->connect();
     }
 
+    /**
+     * @phpstan-param FormattedRecord $record
+     */
     protected function generateDataStream(array $record): string
     {
         return (string) $record['formatted'];

+ 1 - 3
src/Monolog/Handler/SqsHandler.php

@@ -41,9 +41,7 @@ class SqsHandler extends AbstractProcessingHandler
     }
 
     /**
-     * Writes the record down to the log of the implementing handler.
-     *
-     * @param array $record
+     * {@inheritdoc}
      */
     protected function write(array $record): void
     {

+ 13 - 5
src/Monolog/Handler/StreamHandler.php

@@ -20,6 +20,8 @@ use Monolog\Utils;
  * Can be used to store into php://stderr, remote and local files, etc.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class StreamHandler extends AbstractProcessingHandler
 {
@@ -27,12 +29,16 @@ class StreamHandler extends AbstractProcessingHandler
 
     /** @var resource|null */
     protected $stream;
-    protected $url;
-    /** @var string|null */
-    private $errorMessage;
+    /** @var ?string */
+    protected $url = null;
+    /** @var ?string */
+    private $errorMessage = null;
+    /** @var ?int */
     protected $filePermission;
+    /** @var bool */
     protected $useLocking;
-    private $dirCreated;
+    /** @var true|null */
+    private $dirCreated = null;
 
     /**
      * @param resource|string $stream         If a missing path can't be created, an UnexpectedValueException will be thrown on first write
@@ -132,13 +138,15 @@ class StreamHandler extends AbstractProcessingHandler
      * Write to stream
      * @param resource $stream
      * @param array    $record
+     *
+     * @phpstan-param FormattedRecord $record
      */
     protected function streamWrite($stream, array $record): void
     {
         fwrite($stream, (string) $record['formatted']);
     }
 
-    private function customErrorHandler($code, $msg): bool
+    private function customErrorHandler(int $code, string $msg): bool
     {
         $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
 

+ 7 - 1
src/Monolog/Handler/SwiftMailerHandler.php

@@ -21,14 +21,18 @@ use Swift;
  * SwiftMailerHandler uses Swift_Mailer to send the emails
  *
  * @author Gyula Sallai
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 class SwiftMailerHandler extends MailHandler
 {
+    /** @var \Swift_Mailer */
     protected $mailer;
+    /** @var Swift_Message|callable(string, Record[]): Swift_Message */
     private $messageTemplate;
 
     /**
-     * @psalm-param Swift_Message|callable(string, array): Swift_Message $message
+     * @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message
      *
      * @param \Swift_Mailer          $mailer  The mailer to use
      * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
@@ -67,6 +71,8 @@ class SwiftMailerHandler extends MailHandler
      * @param  string        $content formatted email body to be sent
      * @param  array         $records Log records that formed the content
      * @return Swift_Message
+     *
+     * @phpstan-param Record[] $records
      */
     protected function buildMessage(string $content, array $records): Swift_Message
     {

+ 2 - 0
src/Monolog/Handler/SyslogHandler.php

@@ -28,7 +28,9 @@ use Monolog\Logger;
  */
 class SyslogHandler extends AbstractSyslogHandler
 {
+    /** @var string */
     protected $ident;
+    /** @var int */
     protected $logopts;
 
     /**

+ 5 - 0
src/Monolog/Handler/SyslogUdp/UdpSocket.php

@@ -39,6 +39,11 @@ class UdpSocket
         $this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null;
     }
 
+    /**
+     * @param  string $line
+     * @param  string $header
+     * @return void
+     */
     public function write($line, $header = "")
     {
         $this->send($this->assembleMessage($line, $header));

+ 10 - 0
src/Monolog/Handler/SyslogUdpHandler.php

@@ -27,14 +27,18 @@ class SyslogUdpHandler extends AbstractSyslogHandler
     const RFC5424 = 1;
     const RFC5424e = 2;
 
+    /** @var array<self::RFC*, string> */
     private $dateFormats = array(
         self::RFC3164 => 'M d H:i:s',
         self::RFC5424 => \DateTime::RFC3339,
         self::RFC5424e => \DateTime::RFC3339_EXTENDED,
     );
 
+    /** @var UdpSocket */
     protected $socket;
+    /** @var string */
     protected $ident;
+    /** @var self::RFC* */
     protected $rfc;
 
     /**
@@ -45,6 +49,8 @@ class SyslogUdpHandler extends AbstractSyslogHandler
      * @param bool       $bubble   Whether the messages that are handled can bubble up the stack or not
      * @param string     $ident    Program name or tag for each log message.
      * @param int        $rfc      RFC to format the message for.
+     *
+     * @phpstan-param self::RFC* $rfc
      */
     public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424)
     {
@@ -72,6 +78,10 @@ class SyslogUdpHandler extends AbstractSyslogHandler
         $this->socket->close();
     }
 
+    /**
+     * @param string|string[] $message
+     * @return string[]
+     */
     private function splitMessageIntoLines($message): array
     {
         if (is_array($message)) {

+ 4 - 4
src/Monolog/Handler/TelegramBotHandler.php

@@ -33,7 +33,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
     private const BOT_API = 'https://api.telegram.org/bot';
 
     /**
-     * @var array AVAILABLE_PARSE_MODES The available values of parseMode according to the Telegram api documentation
+     * The available values of parseMode according to the Telegram api documentation
      */
     private const AVAILABLE_PARSE_MODES = [
         'HTML',
@@ -59,19 +59,19 @@ 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|null
+     * @var ?string
      */
     private $parseMode;
 
     /**
      * Disables link previews for links in the message.
-     * @var bool|null
+     * @var ?bool
      */
     private $disableWebPagePreview;
 
     /**
      * Sends the message silently. Users will receive a notification with no sound.
-     * @var bool|null
+     * @var ?bool
      */
     private $disableNotification;
 

+ 35 - 2
src/Monolog/Handler/TestHandler.php

@@ -12,6 +12,7 @@
 namespace Monolog\Handler;
 
 use Monolog\Logger;
+use Psr\Log\LogLevel;
 
 /**
  * Used for testing purposes.
@@ -64,24 +65,42 @@ use Monolog\Logger;
  * @method bool hasNoticeThatPasses($message)
  * @method bool hasInfoThatPasses($message)
  * @method bool hasDebugThatPasses($message)
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
+ * @phpstan-import-type LevelName from \Monolog\Logger
  */
 class TestHandler extends AbstractProcessingHandler
 {
+    /** @var Record[] */
     protected $records = [];
+    /** @var array<Level, Record[]> */
     protected $recordsByLevel = [];
+    /** @var bool */
     private $skipReset = false;
 
+    /**
+     * @return array
+     *
+     * @phpstan-return Record[]
+     */
     public function getRecords()
     {
         return $this->records;
     }
 
+    /**
+     * @return void
+     */
     public function clear()
     {
         $this->records = [];
         $this->recordsByLevel = [];
     }
 
+    /**
+     * @return void
+     */
     public function reset()
     {
         if (!$this->skipReset) {
@@ -89,6 +108,9 @@ class TestHandler extends AbstractProcessingHandler
         }
     }
 
+    /**
+     * @return void
+     */
     public function setSkipReset(bool $skipReset)
     {
         $this->skipReset = $skipReset;
@@ -105,6 +127,9 @@ class TestHandler extends AbstractProcessingHandler
     /**
      * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records
      * @param string|int   $level  Logging level value or name
+     *
+     * @phpstan-param array{message: string, context?: mixed[]}|string $record
+     * @phpstan-param Level|LevelName|LogLevel::*                      $level
      */
     public function hasRecord($record, $level): bool
     {
@@ -136,6 +161,8 @@ class TestHandler extends AbstractProcessingHandler
 
     /**
      * @param string|int $level Logging level value or name
+     *
+     * @phpstan-param Level|LevelName|LogLevel::* $level
      */
     public function hasRecordThatMatches(string $regex, $level): bool
     {
@@ -145,10 +172,11 @@ class TestHandler extends AbstractProcessingHandler
     }
 
     /**
-     * @psalm-param callable(array, int): mixed $predicate
-     *
      * @param string|int $level Logging level value or name
      * @return bool
+     *
+     * @psalm-param callable(Record, int): mixed $predicate
+     * @phpstan-param Level|LevelName|LogLevel::* $level
      */
     public function hasRecordThatPasses(callable $predicate, $level)
     {
@@ -176,6 +204,11 @@ class TestHandler extends AbstractProcessingHandler
         $this->records[] = $record;
     }
 
+    /**
+     * @param string $method
+     * @param mixed[] $args
+     * @return bool
+     */
     public function __call($method, $args)
     {
         if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {

+ 8 - 1
src/Monolog/Handler/ZendMonitorHandler.php

@@ -20,13 +20,15 @@ use Monolog\Logger;
  *
  * @author  Christian Bergau <cbergau86@gmail.com>
  * @author  Jason Davis <happydude@jasondavis.net>
+ *
+ * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
  */
 class ZendMonitorHandler extends AbstractProcessingHandler
 {
     /**
      * Monolog level / ZendMonitor Custom Event priority map
      *
-     * @var array
+     * @var array<int, int>
      */
     protected $levelMap = [];
 
@@ -75,6 +77,8 @@ class ZendMonitorHandler extends AbstractProcessingHandler
      * @param string $message   Text displayed in "Error String"
      * @param array  $formatted Displayed in Custom Variables tab
      * @param int    $severity  Set the event severity level (-1,0,1)
+     *
+     * @phpstan-param FormattedRecord $formatted
      */
     protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void
     {
@@ -89,6 +93,9 @@ class ZendMonitorHandler extends AbstractProcessingHandler
         return new NormalizerFormatter();
     }
 
+    /**
+     * @return array<int, int>
+     */
     public function getLevelMap(): array
     {
         return $this->levelMap;

+ 29 - 5
src/Monolog/Logger.php

@@ -15,6 +15,7 @@ use DateTimeZone;
 use Monolog\Handler\HandlerInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\InvalidArgumentException;
+use Psr\Log\LogLevel;
 use Throwable;
 
 /**
@@ -24,6 +25,10 @@ use Throwable;
  * and uses them to store records that are added to it.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY
+ * @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY'
+ * @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]}
  */
 class Logger implements LoggerInterface, ResettableInterface
 {
@@ -91,6 +96,8 @@ class Logger implements LoggerInterface, ResettableInterface
      * This is a static variable and not a constant to serve as an extension point for custom levels
      *
      * @var array<int, string> $levels Logging levels with the levels as key
+     *
+     * @phpstan-var array<Level, LevelName> $levels Logging levels with the levels as key
      */
     protected static $levels = [
         self::DEBUG     => 'DEBUG',
@@ -276,6 +283,8 @@ class Logger implements LoggerInterface, ResettableInterface
      * @param  string  $message The log message
      * @param  mixed[] $context The log context
      * @return bool    Whether the record has been processed
+     *
+     * @phpstan-param Level $level
      */
     public function addRecord(int $level, string $message, array $context = []): bool
     {
@@ -383,6 +392,9 @@ class Logger implements LoggerInterface, ResettableInterface
      * Gets the name of the logging level.
      *
      * @throws \Psr\Log\InvalidArgumentException If level is not defined
+     *
+     * @phpstan-param  Level     $level
+     * @phpstan-return LevelName
      */
     public static function getLevelName(int $level): string
     {
@@ -398,11 +410,15 @@ class Logger implements LoggerInterface, ResettableInterface
      *
      * @param  string|int                        $level Level number (monolog) or name (PSR-3)
      * @throws \Psr\Log\InvalidArgumentException If level is not defined
+     *
+     * @phpstan-param  Level|LevelName|LogLevel::* $level
+     * @phpstan-return Level
      */
     public static function toMonologLevel($level): int
     {
         if (is_string($level)) {
             if (is_numeric($level)) {
+                /** @phpstan-ignore-next-line */
                 return intval($level);
             }
 
@@ -413,18 +429,21 @@ class Logger implements LoggerInterface, ResettableInterface
                 return constant(__CLASS__ . '::' . $upper);
             }
 
-            throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
+            throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels));
         }
 
         if (!is_int($level)) {
-            throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
+            throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels));
         }
 
+        /** @phpstan-ignore-next-line */
         return $level;
     }
 
     /**
      * Checks whether the Logger has a handler that listens on the given level
+     *
+     * @phpstan-param Level $level
      */
     public function isHandling(int $level): bool
     {
@@ -463,9 +482,11 @@ class Logger implements LoggerInterface, ResettableInterface
      *
      * This method allows for compatibility with common interfaces.
      *
-     * @param mixed   $level   The log level
-     * @param string  $message The log message
-     * @param mixed[] $context The log context
+     * @param int|string $level   The log level
+     * @param string     $message The log message
+     * @param mixed[]    $context The log context
+     *
+     * @phpstan-param Level|LevelName|LogLevel::* $level
      */
     public function log($level, $message, array $context = []): void
     {
@@ -599,6 +620,9 @@ class Logger implements LoggerInterface, ResettableInterface
     /**
      * Delegates exception management to the custom exception handler,
      * or throws the exception if no custom handler is set.
+     *
+     * @param array $record
+     * @phpstan-param Record $record
      */
     protected function handleException(Throwable $e, array $record): void
     {

+ 9 - 1
src/Monolog/Processor/GitProcessor.php

@@ -21,8 +21,10 @@ use Monolog\Logger;
  */
 class GitProcessor implements ProcessorInterface
 {
+    /** @var int */
     private $level;
-    private static $cache;
+    /** @var array{branch: string, commit: string}|array<never>|null */
+    private static $cache = null;
 
     /**
      * @param string|int $level The minimum logging level at which this Processor will be triggered
@@ -32,6 +34,9 @@ class GitProcessor implements ProcessorInterface
         $this->level = Logger::toMonologLevel($level);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         // return if the level is not high enough
@@ -44,6 +49,9 @@ class GitProcessor implements ProcessorInterface
         return $record;
     }
 
+    /**
+     * @return array{branch: string, commit: string}|array<never>
+     */
     private static function getGitInfo(): array
     {
         if (self::$cache) {

+ 4 - 0
src/Monolog/Processor/HostnameProcessor.php

@@ -16,6 +16,7 @@ namespace Monolog\Processor;
  */
 class HostnameProcessor implements ProcessorInterface
 {
+    /** @var string */
     private static $host;
 
     public function __construct()
@@ -23,6 +24,9 @@ class HostnameProcessor implements ProcessorInterface
         self::$host = (string) gethostname();
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $record['extra']['hostname'] = self::$host;

+ 13 - 5
src/Monolog/Processor/IntrospectionProcessor.php

@@ -26,19 +26,21 @@ use Monolog\Logger;
  */
 class IntrospectionProcessor implements ProcessorInterface
 {
+    /** @var int */
     private $level;
-
+    /** @var string[] */
     private $skipClassesPartials;
-
+    /** @var int */
     private $skipStackFramesCount;
-
+    /** @var string[] */
     private $skipFunctions = [
         'call_user_func',
         'call_user_func_array',
     ];
 
     /**
-     * @param string|int $level The minimum logging level at which this Processor will be triggered
+     * @param string|int $level               The minimum logging level at which this Processor will be triggered
+     * @param string[]   $skipClassesPartials
      */
     public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
     {
@@ -47,6 +49,9 @@ class IntrospectionProcessor implements ProcessorInterface
         $this->skipStackFramesCount = $skipStackFramesCount;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         // return if the level is not high enough
@@ -97,7 +102,10 @@ class IntrospectionProcessor implements ProcessorInterface
         return $record;
     }
 
-    private function isTraceClassOrSkippedFunction(array $trace, int $index)
+    /**
+     * @param array[] $trace
+     */
+    private function isTraceClassOrSkippedFunction(array $trace, int $index): bool
     {
         if (!isset($trace[$index])) {
             return false;

+ 3 - 0
src/Monolog/Processor/MemoryPeakUsageProcessor.php

@@ -19,6 +19,9 @@ namespace Monolog\Processor;
  */
 class MemoryPeakUsageProcessor extends MemoryProcessor
 {
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $usage = memory_get_peak_usage($this->realUsage);

+ 3 - 0
src/Monolog/Processor/MemoryUsageProcessor.php

@@ -19,6 +19,9 @@ namespace Monolog\Processor;
  */
 class MemoryUsageProcessor extends MemoryProcessor
 {
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $usage = memory_get_usage($this->realUsage);

+ 9 - 1
src/Monolog/Processor/MercurialProcessor.php

@@ -20,8 +20,10 @@ use Monolog\Logger;
  */
 class MercurialProcessor implements ProcessorInterface
 {
+    /** @var int */
     private $level;
-    private static $cache;
+    /** @var array{branch: string, revision: string}|array<never>|null */
+    private static $cache = null;
 
     /**
      * @param string|int $level The minimum logging level at which this Processor will be triggered
@@ -31,6 +33,9 @@ class MercurialProcessor implements ProcessorInterface
         $this->level = Logger::toMonologLevel($level);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         // return if the level is not high enough
@@ -43,6 +48,9 @@ class MercurialProcessor implements ProcessorInterface
         return $record;
     }
 
+    /**
+     * @return array{branch: string, revision: string}|array<never>
+     */
     private static function getMercurialInfo(): array
     {
         if (self::$cache) {

+ 3 - 0
src/Monolog/Processor/ProcessIdProcessor.php

@@ -18,6 +18,9 @@ namespace Monolog\Processor;
  */
 class ProcessIdProcessor implements ProcessorInterface
 {
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $record['extra']['process_id'] = getmypid();

+ 5 - 0
src/Monolog/Processor/ProcessorInterface.php

@@ -15,11 +15,16 @@ namespace Monolog\Processor;
  * An optional interface to allow labelling Monolog processors.
  *
  * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
  */
 interface ProcessorInterface
 {
     /**
      * @return array The processed record
+     *
+     * @phpstan-param  Record $record
+     * @phpstan-return Record
      */
     public function __invoke(array $record);
 }

+ 1 - 2
src/Monolog/Processor/PsrLogMessageProcessor.php

@@ -41,8 +41,7 @@ class PsrLogMessageProcessor implements ProcessorInterface
     }
 
     /**
-     * @param  array $record
-     * @return array
+     * {@inheritDoc}
      */
     public function __invoke(array $record): array
     {

+ 13 - 0
src/Monolog/Processor/TagProcessor.php

@@ -18,13 +18,20 @@ namespace Monolog\Processor;
  */
 class TagProcessor implements ProcessorInterface
 {
+    /** @var string[] */
     private $tags;
 
+    /**
+     * @param string[] $tags
+     */
     public function __construct(array $tags = [])
     {
         $this->setTags($tags);
     }
 
+    /**
+     * @param string[] $tags
+     */
     public function addTags(array $tags = []): self
     {
         $this->tags = array_merge($this->tags, $tags);
@@ -32,6 +39,9 @@ class TagProcessor implements ProcessorInterface
         return $this;
     }
 
+    /**
+     * @param string[] $tags
+     */
     public function setTags(array $tags = []): self
     {
         $this->tags = $tags;
@@ -39,6 +49,9 @@ class TagProcessor implements ProcessorInterface
         return $this;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $record['extra']['tags'] = $this->tags;

+ 4 - 0
src/Monolog/Processor/UidProcessor.php

@@ -20,6 +20,7 @@ use Monolog\ResettableInterface;
  */
 class UidProcessor implements ProcessorInterface, ResettableInterface
 {
+    /** @var string */
     private $uid;
 
     public function __construct(int $length = 7)
@@ -31,6 +32,9 @@ class UidProcessor implements ProcessorInterface, ResettableInterface
         $this->uid = $this->generateUid($length);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         $record['extra']['uid'] = $this->uid;

+ 11 - 4
src/Monolog/Processor/WebProcessor.php

@@ -19,7 +19,7 @@ namespace Monolog\Processor;
 class WebProcessor implements ProcessorInterface
 {
     /**
-     * @var array|\ArrayAccess
+     * @var array<string, mixed>|\ArrayAccess<string, mixed>
      */
     protected $serverData;
 
@@ -28,7 +28,7 @@ class WebProcessor implements ProcessorInterface
      *
      * Array is structured as [key in record.extra => key in $serverData]
      *
-     * @var array
+     * @var array<string, string>
      */
     protected $extraFields = [
         'url'         => 'REQUEST_URI',
@@ -39,8 +39,8 @@ class WebProcessor implements ProcessorInterface
     ];
 
     /**
-     * @param array|\ArrayAccess|null $serverData  Array or object w/ ArrayAccess that provides access to the $_SERVER data
-     * @param array|null              $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer
+     * @param array<string, mixed>|\ArrayAccess<string, mixed>|null $serverData  Array or object w/ ArrayAccess that provides access to the $_SERVER data
+     * @param array<string, string>|null                            $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer
      */
     public function __construct($serverData = null, array $extraFields = null)
     {
@@ -69,6 +69,9 @@ class WebProcessor implements ProcessorInterface
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public function __invoke(array $record): array
     {
         // skip processing if for some reason request data
@@ -89,6 +92,10 @@ class WebProcessor implements ProcessorInterface
         return $this;
     }
 
+    /**
+     * @param mixed[] $extra
+     * @return mixed[]
+     */
     private function appendExtraFields(array $extra): array
     {
         foreach ($this->extraFields as $extraName => $serverName) {

+ 2 - 1
src/Monolog/Registry.php

@@ -51,6 +51,7 @@ class Registry
      * @param  string|null               $name      Name of the logging channel ($logger->getName() by default)
      * @param  bool                      $overwrite Overwrite instance in the registry if the given name already exists?
      * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists
+     * @return void
      */
     public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false)
     {
@@ -122,7 +123,7 @@ class Registry
      * Gets Logger instance from the registry via static method call
      *
      * @param  string                    $name      Name of the requested Logger instance
-     * @param  array                     $arguments Arguments passed to static method call
+     * @param  mixed[]                   $arguments Arguments passed to static method call
      * @throws \InvalidArgumentException If named Logger instance is not in the registry
      * @return Logger                    Requested instance of Logger
      */

+ 19 - 3
src/Monolog/SignalHandler.php

@@ -22,10 +22,14 @@ use ReflectionExtension;
  */
 class SignalHandler
 {
+    /** @var LoggerInterface */
     private $logger;
 
+    /** @var array<int, callable|int> SIG_DFL, SIG_IGN or previous callable */
     private $previousSignalHandler = [];
+    /** @var array<int, int> */
     private $signalLevelMap = [];
+    /** @var array<int, bool> */
     private $signalRestartSyscalls = [];
 
     public function __construct(LoggerInterface $logger)
@@ -33,12 +37,21 @@ class SignalHandler
         $this->logger = $logger;
     }
 
-    public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self
+    /**
+     * @param int|string $level Level or level name
+     * @param bool      $callPrevious
+     * @param bool      $restartSyscalls
+     * @param bool|null $async
+     * @return $this
+     */
+    public function registerSignalHandler(int $signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self
     {
         if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
             return $this;
         }
 
+        $level = Logger::toMonologLevel($level);
+
         if ($callPrevious) {
             $handler = pcntl_signal_get_handler($signo);
             $this->previousSignalHandler[$signo] = $handler;
@@ -57,7 +70,10 @@ class SignalHandler
         return $this;
     }
 
-    public function handleSignal($signo, array $siginfo = null): void
+    /**
+     * @param mixed $siginfo
+     */
+    public function handleSignal(int $signo, $siginfo = null): void
     {
         static $signals = [];
 
@@ -80,7 +96,7 @@ class SignalHandler
             return;
         }
 
-        if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) {
+        if ($this->previousSignalHandler[$signo] === SIG_DFL) {
             if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch')
                 && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')
             ) {

+ 13 - 2
src/Monolog/Test/TestCase.php

@@ -17,15 +17,23 @@ use Monolog\Formatter\FormatterInterface;
 
 /**
  * Lets you easily generate log records and a dummy formatter for testing purposes
- * *
+ *
  * @author Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * @phpstan-import-type Record from \Monolog\Logger
+ * @phpstan-import-type Level from \Monolog\Logger
  */
 class TestCase extends \PHPUnit\Framework\TestCase
 {
     /**
+     * @param mixed[] $context
+     *
      * @return array Record
+     *
+     * @phpstan-param  Level $level
+     * @phpstan-return Record
      */
-    protected function getRecord($level = Logger::WARNING, $message = 'test', array $context = []): array
+    protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []): array
     {
         return [
             'message' => (string) $message,
@@ -38,6 +46,9 @@ class TestCase extends \PHPUnit\Framework\TestCase
         ];
     }
 
+    /**
+     * @phpstan-return Record[]
+     */
     protected function getMultipleRecords(): array
     {
         return [

+ 2 - 2
tests/Monolog/ErrorHandlerTest.php

@@ -54,8 +54,8 @@ class ErrorHandlerTest extends \PHPUnit\Framework\TestCase
     public function fatalHandlerProvider()
     {
         return [
-            [null, 10, str_repeat(' ', 1024 * 10), null],
-            [E_ALL, 15, str_repeat(' ', 1024 * 15), E_ALL],
+            [null, 10, str_repeat(' ', 1024 * 10), LogLevel::ALERT],
+            [LogLevel::DEBUG, 15, str_repeat(' ', 1024 * 15), LogLevel::DEBUG],
         ];
     }