diff --git a/CHANGELOG.md b/CHANGELOG.md index 049eca31..e80fb1d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 2.0.1 under development -- no changes in this release. +- Bug #84: Change the type of the `$level` parameter in the `Message` constructor to `string` (dood-) ## 2.0.0 May 22, 2022 diff --git a/psalm.xml b/psalm.xml index 4fa75818..deb39dc1 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ targets; } + /** + * @param string $level + * @param string|Stringable $message + * @param array $context + * @psalm-param LogMessageContext $context + * @psalm-suppress MoreSpecificImplementedParamType + * + * @return void + */ public function log(mixed $level, string|Stringable $message, array $context = []): void { $context['time'] ??= microtime(true); @@ -205,7 +217,7 @@ public function setTraceLevel(int $traceLevel): self /** * Sets an array of paths to exclude from tracing when tracing is enabled with {@see Logger::setTraceLevel()}. * - * @param array $excludedTracePaths The paths to exclude from tracing. + * @param string[] $excludedTracePaths The paths to exclude from tracing. * * @throws InvalidArgumentException for non-string values. * @@ -216,6 +228,7 @@ public function setTraceLevel(int $traceLevel): self public function setExcludedTracePaths(array $excludedTracePaths): self { foreach ($excludedTracePaths as $excludedTracePath) { + /** @psalm-suppress DocblockTypeContradiction */ if (!is_string($excludedTracePath)) { throw new InvalidArgumentException(sprintf( 'The trace path must be a string, %s received.', @@ -231,7 +244,7 @@ public function setExcludedTracePaths(array $excludedTracePaths): self /** * Sets a target to {@see Logger::$targets}. * - * @param array $targets The log targets. Each array element represents a single {@see \Yiisoft\Log\Target} + * @param Target[] $targets The log targets. Each array element represents a single {@see \Yiisoft\Log\Target} * instance or the configuration for creating the log target instance. * * @throws InvalidArgumentException for non-instance Target. @@ -281,8 +294,10 @@ private function dispatch(array $messages, bool $final): void * Collects a trace when tracing is enabled with {@see Logger::setTraceLevel()}. * * @param array $backtrace The list of call stack information. + * @psalm-param Backtrace|list $backtrace * * @return array Collected a list of call stack information. + * @psalm-return Backtrace */ private function collectTrace(array $backtrace): array { diff --git a/src/Message.php b/src/Message.php index 19212699..d1e49bd3 100644 --- a/src/Message.php +++ b/src/Message.php @@ -13,6 +13,20 @@ /** * Message is a data object that stores log message data. + * + * @psalm-type Backtrace = list + * @psalm-type LogMessageContext = array{ + * category?:string, + * memory?:int, + * time?:float, + * trace?:Backtrace, + * }&array */ final class Message { @@ -29,7 +43,8 @@ final class Message private string $message; /** - * @var array Log message context. + * @var array Log message context. + * @psalm-var LogMessageContext * * Message context has a following keys: * @@ -41,16 +56,17 @@ final class Message private array $context; /** - * @param mixed $level Log message level. + * @param string $level Log message level. * @param string|Stringable $message Log message. - * @param array $context Log message context. + * @param array $context Log message context. + * @psalm-param LogMessageContext $context * * @throws InvalidArgumentException for invalid log message level. * * @see LoggerTrait::log() * @see LogLevel */ - public function __construct(mixed $level, string|Stringable $message, array $context = []) + public function __construct(string $level, string|Stringable $message, array $context = []) { $this->level = Logger::validateLevel($level); $this->message = $this->parse($message, $context); @@ -86,6 +102,7 @@ public function message(): string * @param mixed $default If the context parameter does not exist, the `$default` will be returned. * * @return mixed The context parameter value. + * @psalm-return LogMessageContext|mixed */ public function context(string $name = null, mixed $default = null): mixed { @@ -101,7 +118,8 @@ public function context(string $name = null, mixed $default = null): mixed * where foo will be replaced by the context data in key "foo". * * @param string|Stringable $message Raw log message. - * @param array $context Message context. + * @param array $context Message context. + * @psalm-param LogMessageContext $context * * @return string Parsed message. */ @@ -113,6 +131,7 @@ private function parse(string|Stringable $message, array $context): string $placeholderName = $matches[1]; if (isset($context[$placeholderName])) { + /** @psalm-suppress PossiblyInvalidCast */ return (string) $context[$placeholderName]; } diff --git a/src/Message/Formatter.php b/src/Message/Formatter.php index fb34f27c..bc7d337d 100644 --- a/src/Message/Formatter.php +++ b/src/Message/Formatter.php @@ -21,6 +21,8 @@ * Formatter formats log messages. * * @internal + * + * @psalm-import-type Backtrace from Message */ final class Formatter { @@ -125,6 +127,7 @@ private function defaultFormat(Message $message, array $commonContext): string $time = $this->getTime($message); $prefix = $this->getPrefix($message, $commonContext); $context = $this->getContext($message, $commonContext); + /** @var string $category */ $category = $message->context('category', CategoryFilter::DEFAULT); return "{$time} {$prefix}[{$message->level()}][{$category}] {$message->message()}{$context}"; @@ -139,6 +142,7 @@ private function defaultFormat(Message $message, array $commonContext): string */ private function getTime(Message $message): string { + /** @psalm-suppress PossiblyInvalidCast */ $timestamp = (string) $message->context('time', microtime(true)); $format = match (true) { @@ -199,12 +203,19 @@ private function getContext(Message $message, array $commonContext): string $context[] = $trace; } + /** + * @var array-key $name + * @var mixed $value + */ foreach ($message->context() as $name => $value) { if ($name !== 'trace') { $context[] = "{$name}: " . $this->convertToString($value); } } + /** + * @var mixed $value + */ foreach ($commonContext as $name => $value) { $common[] = "{$name}: " . $this->convertToString($value); } @@ -222,7 +233,8 @@ private function getContext(Message $message, array $commonContext): string */ private function getTrace(Message $message): string { - $traces = (array) $message->context('trace', []); + /** @psalm-var Backtrace $traces */ + $traces = $message->context('trace', []); foreach ($traces as $key => $trace) { if (isset($trace['file'], $trace['line'])) { @@ -230,6 +242,7 @@ private function getTrace(Message $message): string } } + /** @var string[] $traces */ return empty($traces) ? '' : "trace:\n " . implode("\n ", $traces); } @@ -243,7 +256,7 @@ private function getTrace(Message $message): string private function convertToString(mixed $value): string { if (is_object($value) && method_exists($value, '__toString')) { - return $value->__toString(); + return (string) $value; } return VarDumper::create($value)->asString(); diff --git a/src/PsrTarget.php b/src/PsrTarget.php index 7bc07843..58dcc36f 100644 --- a/src/PsrTarget.php +++ b/src/PsrTarget.php @@ -38,7 +38,9 @@ public function getLogger(): LoggerInterface protected function export(): void { foreach ($this->getMessages() as $message) { - $this->logger->log($message->level(), $message->message(), $message->context()); + /** @var array $context */ + $context = $message->context(); + $this->logger->log($message->level(), $message->message(), $context); } } } diff --git a/src/Target.php b/src/Target.php index 1be188b3..b64ff265 100644 --- a/src/Target.php +++ b/src/Target.php @@ -115,7 +115,7 @@ public function collect(array $messages, bool $final): void /** * Sets a list of log message categories that this target is interested in. * - * @param array $categories The list of log message categories. + * @param string[] $categories The list of log message categories. * * @throws InvalidArgumentException for invalid log message categories structure. * @@ -132,7 +132,7 @@ public function setCategories(array $categories): self /** * Sets a list of log message categories that this target is NOT interested in. * - * @param array $except The list of log message categories. + * @param string[] $except The list of log message categories. * * @throws InvalidArgumentException for invalid log message categories structure. * @@ -149,7 +149,7 @@ public function setExcept(array $except): self /** * Sets a list of log message levels that current target is interested in. * - * @param array $levels The list of log message levels. + * @param string[] $levels The list of log message levels. * * @throws InvalidArgumentException for invalid log message level. * @@ -383,7 +383,10 @@ private function filterMessages(array $messages): void continue; } - if ($this->categories->isExcluded($message->context('category', ''))) { + /** @var string $category */ + $category = $message->context('category', ''); + + if ($this->categories->isExcluded($category)) { unset($messages[$i]); continue; }