diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..cc808e7a --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,62 @@ +parameters: + ignoreErrors: + - + message: "#^Property Monolog\\\\ErrorHandler\\:\\:\\$reservedMemory is never read, only written\\.$#" + count: 1 + path: src/Monolog/ErrorHandler.php + + - + message: "#^Cannot access offset 'table' on array\\\\|bool\\|float\\|int\\|object\\|string\\|null\\.$#" + count: 1 + path: src/Monolog/Formatter/WildfireFormatter.php + + - + message: "#^Call to method setBody\\(\\) on an unknown class Swift_Message\\.$#" + count: 1 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Call to method setDate\\(\\) on an unknown class Swift_Message\\.$#" + count: 1 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Class Swift_Message not found\\.$#" + count: 2 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Cloning object of an unknown class Swift_Message\\.$#" + count: 1 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Parameter \\$message of method Monolog\\\\Handler\\\\MandrillHandler\\:\\:__construct\\(\\) has invalid type Swift_Message\\.$#" + count: 2 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Property Monolog\\\\Handler\\\\MandrillHandler\\:\\:\\$message has unknown class Swift_Message as its type\\.$#" + count: 1 + path: src/Monolog/Handler/MandrillHandler.php + + - + message: "#^Comparison operation \"\\<\" between int\\<1, 32\\> and 1 is always false\\.$#" + count: 1 + path: src/Monolog/Processor/UidProcessor.php + + - + message: "#^Comparison operation \"\\>\" between int\\<1, 32\\> and 32 is always false\\.$#" + count: 1 + path: src/Monolog/Processor/UidProcessor.php + + - + message: "#^Method Monolog\\\\Processor\\\\UidProcessor\\:\\:generateUid\\(\\) should return non\\-empty\\-string but returns string\\.$#" + count: 1 + path: src/Monolog/Processor/UidProcessor.php + + - + message: "#^Parameter \\#1 \\$length of function random_bytes expects int\\<1, max\\>, int given\\.$#" + count: 1 + path: src/Monolog/Processor/UidProcessor.php + diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 961f7ce4..1ac28524 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,7 +2,7 @@ parameters: level: 8 treatPhpDocTypesAsCertain: false - reportUnmatchedIgnoredErrors: false + reportUnmatchedIgnoredErrors: true paths: - src/ @@ -19,19 +19,5 @@ parameters: paths: - src/Monolog/Formatter/LineFormatter.php - # blocked until we only support php8+ - - '#Parameter \#1 \$socket of function (socket_close|socket_sendto|socket_send) expects Socket, resource\|Socket(\|null)? given\.#' - - '#Parameter \#1 \$handle of function (curl_exec|curl_close|curl_error|curl_errno|curl_setopt) expects CurlHandle, CurlHandle\|resource(\|null)? given\.#' - - 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\(\) should return array#' - - '#::processRecord\(\) has invalid type#' - - '#::processRecord\(\) return type has no value type#' - - '#::processRecord\(\) has parameter \$record with no value type#' - - '#::popProcessor\(\) should return callable#' - - '#Parameter \#1 \$ of callable \(callable\(Monolog\\Handler\\Record\): Monolog\\Handler\\Record\)#' - - '#is incompatible with native type array.#' +includes: + - phpstan-baseline.neon diff --git a/src/Monolog/Formatter/ElasticaFormatter.php b/src/Monolog/Formatter/ElasticaFormatter.php index e60fc6f9..1d7cdca7 100644 --- a/src/Monolog/Formatter/ElasticaFormatter.php +++ b/src/Monolog/Formatter/ElasticaFormatter.php @@ -70,6 +70,8 @@ class ElasticaFormatter extends NormalizerFormatter /** * Convert a log message into an Elastica Document + * + * @param mixed[] $record */ protected function getDocument(array $record): Document { diff --git a/src/Monolog/Formatter/JsonFormatter.php b/src/Monolog/Formatter/JsonFormatter.php index aee6b5df..d5618591 100644 --- a/src/Monolog/Formatter/JsonFormatter.php +++ b/src/Monolog/Formatter/JsonFormatter.php @@ -136,16 +136,12 @@ class JsonFormatter extends NormalizerFormatter */ protected function formatBatchNewlines(array $records): string { - $instance = $this; - $oldNewline = $this->appendNewline; $this->appendNewline = false; - array_walk($records, function (&$value, $key) use ($instance) { - $value = $instance->format($value); - }); + $formatted = array_map(fn (LogRecord $record) => $this->format($record), $records); $this->appendNewline = $oldNewline; - return implode("\n", $records); + return implode("\n", $formatted); } /** diff --git a/src/Monolog/Formatter/NormalizerFormatter.php b/src/Monolog/Formatter/NormalizerFormatter.php index b2deffaa..cc45fe50 100644 --- a/src/Monolog/Formatter/NormalizerFormatter.php +++ b/src/Monolog/Formatter/NormalizerFormatter.php @@ -48,14 +48,17 @@ class NormalizerFormatter implements FormatterInterface /** * {@inheritDoc} - * - * @param mixed[] $record */ public function format(LogRecord $record) { return $this->normalizeRecord($record); } + /** + * Normalize an arbitrary value to a scalar|array|null + * + * @return null|scalar|array + */ public function normalizeValue(mixed $data): mixed { return $this->normalize($data); @@ -134,15 +137,20 @@ class NormalizerFormatter implements FormatterInterface * * Because normalize is called with sub-values of context data etc, normalizeRecord can be * extended when data needs to be appended on the record array but not to other normalized data. + * + * @return array */ protected function normalizeRecord(LogRecord $record): array { - return $this->normalize($record->toArray()); + /** @var array $normalized */ + $normalized = $this->normalize($record->toArray()); + + return $normalized; } /** * @param mixed $data - * @return null|scalar|array + * @return null|scalar|array */ protected function normalize(mixed $data, int $depth = 0): mixed { @@ -189,14 +197,14 @@ class NormalizerFormatter implements FormatterInterface } if ($data instanceof \JsonSerializable) { - /** @var null|scalar|array $value */ + /** @var null|scalar|array $value */ $value = $data->jsonSerialize(); } elseif (method_exists($data, '__toString')) { /** @var string $value */ $value = $data->__toString(); } else { // the rest is normalized by json encoding and decoding it - /** @var null|scalar|array $value */ + /** @var null|scalar|array $value */ $value = json_decode($this->toJson($data, true), true); } @@ -246,7 +254,7 @@ class NormalizerFormatter implements FormatterInterface $trace = $e->getTrace(); foreach ($trace as $frame) { - if (isset($frame['file'])) { + if (isset($frame['file'], $frame['line'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } } diff --git a/src/Monolog/Formatter/WildfireFormatter.php b/src/Monolog/Formatter/WildfireFormatter.php index 7170b020..0d7f23f0 100644 --- a/src/Monolog/Formatter/WildfireFormatter.php +++ b/src/Monolog/Formatter/WildfireFormatter.php @@ -70,26 +70,24 @@ class WildfireFormatter extends NormalizerFormatter unset($record->extra['line']); } - /** @var mixed[] $record */ - $record = $this->normalize($record); $message = ['message' => $record->message]; $handleError = false; - if ($record->context) { - $message['context'] = $record->context; + if (count($record->context) > 0) { + $message['context'] = $this->normalize($record->context); $handleError = true; } - if ($record->extra) { - $message['extra'] = $record->extra; + if (count($record->extra) > 0) { + $message['extra'] = $this->normalize($record->extra); $handleError = true; } if (count($message) === 1) { $message = reset($message); } - if (isset($record->context['table'])) { + if (is_array($message) && isset($message['context']['table'])) { $type = 'TABLE'; $label = $record->channel .': '. $record->message; - $message = $record->context['table']; + $message = $message['context']['table']; } else { $type = $this->logLevels[$record->level]; $label = $record->channel; @@ -127,7 +125,7 @@ class WildfireFormatter extends NormalizerFormatter /** * {@inheritDoc} * - * @return null|scalar|array|object + * @return null|scalar|array|object */ protected function normalize(mixed $data, int $depth = 0): mixed { diff --git a/src/Monolog/Handler/BrowserConsoleHandler.php b/src/Monolog/Handler/BrowserConsoleHandler.php index fa66da4b..7c560de8 100644 --- a/src/Monolog/Handler/BrowserConsoleHandler.php +++ b/src/Monolog/Handler/BrowserConsoleHandler.php @@ -81,9 +81,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler if (count(static::$records)) { if ($format === self::FORMAT_HTML) { - static::writeOutput(''); + static::writeOutput(''); } elseif ($format === self::FORMAT_JS) { - static::writeOutput(static::generateScript()); + static::writeOutput(self::generateScript()); } static::resetStatic(); } @@ -202,7 +202,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler foreach (array_reverse($matches) as $match) { $args[] = '"font-weight: normal"'; - $args[] = self::quote(static::handleCustomStyles($match[2][0], $match[1][0])); + $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); $pos = $match[0][1]; $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0])); diff --git a/src/Monolog/Handler/CubeHandler.php b/src/Monolog/Handler/CubeHandler.php index b86d50af..40a16f5c 100644 --- a/src/Monolog/Handler/CubeHandler.php +++ b/src/Monolog/Handler/CubeHandler.php @@ -23,9 +23,9 @@ use Monolog\LogRecord; */ class CubeHandler extends AbstractProcessingHandler { - /** @var resource|\Socket|null */ + /** @var \Socket|null */ private $udpConnection = null; - /** @var resource|\CurlHandle|null */ + /** @var \CurlHandle|null */ private $httpConnection = null; /** @var string */ private $scheme; @@ -143,6 +143,10 @@ class CubeHandler extends AbstractProcessingHandler $this->connectUdp(); } + if (null === $this->udpConnection) { + throw new \LogicException('No UDP socket could be opened'); + } + socket_send($this->udpConnection, $data, strlen($data), 0); } diff --git a/src/Monolog/Handler/Slack/SlackRecord.php b/src/Monolog/Handler/Slack/SlackRecord.php index fba86225..7e9e3233 100644 --- a/src/Monolog/Handler/Slack/SlackRecord.php +++ b/src/Monolog/Handler/Slack/SlackRecord.php @@ -222,6 +222,7 @@ class SlackRecord */ public function stringify(array $fields): string { + /** @var array $normalized */ $normalized = $this->normalizerFormatter->normalizeValue($fields); $hasSecondDimension = count(array_filter($normalized, 'is_array')); @@ -342,6 +343,7 @@ class SlackRecord */ private function generateAttachmentFields(array $data): array { + /** @var array $normalized */ $normalized = $this->normalizerFormatter->normalizeValue($data); $fields = array(); diff --git a/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/src/Monolog/Handler/SyslogUdp/UdpSocket.php index 30b5186b..05928679 100644 --- a/src/Monolog/Handler/SyslogUdp/UdpSocket.php +++ b/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -22,7 +22,7 @@ class UdpSocket protected $ip; /** @var int */ protected $port; - /** @var resource|Socket|null */ + /** @var Socket|null */ protected $socket; public function __construct(string $ip, int $port = 514) @@ -51,7 +51,7 @@ class UdpSocket public function close(): void { - if (is_resource($this->socket) || $this->socket instanceof Socket) { + if ($this->socket instanceof Socket) { socket_close($this->socket); $this->socket = null; } @@ -59,7 +59,7 @@ class UdpSocket protected function send(string $chunk): void { - if (!is_resource($this->socket) && !$this->socket instanceof Socket) { + if (!$this->socket instanceof Socket) { throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore'); } socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); diff --git a/src/Monolog/Processor/UidProcessor.php b/src/Monolog/Processor/UidProcessor.php index 85d56c3a..973b317e 100644 --- a/src/Monolog/Processor/UidProcessor.php +++ b/src/Monolog/Processor/UidProcessor.php @@ -21,9 +21,12 @@ use Monolog\LogRecord; */ class UidProcessor implements ProcessorInterface, ResettableInterface { - /** @var string */ - private $uid; + /** @var non-empty-string */ + private string $uid; + /** + * @param int<1, 32> $length + */ public function __construct(int $length = 7) { if ($length > 32 || $length < 1) { @@ -53,6 +56,10 @@ class UidProcessor implements ProcessorInterface, ResettableInterface $this->uid = $this->generateUid(strlen($this->uid)); } + /** + * @param positive-int $length + * @return non-empty-string + */ private function generateUid(int $length): string { return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length);