1
0
mirror of https://github.com/Seldaek/monolog.git synced 2025-07-31 02:10:22 +02:00

Bump phpstan to level 8

This commit is contained in:
Jordi Boggiano
2021-07-04 14:03:55 +02:00
parent 4ef5da80ad
commit 8b5278d8e1
47 changed files with 262 additions and 101 deletions

View File

@@ -1,5 +1,5 @@
parameters:
level: 6
level: 8
treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false
@@ -19,7 +19,16 @@ parameters:
paths:
- src/Monolog/Handler/LogglyHandler.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\.#'
# blocked by https://github.com/phpstan/phpstan/issues/5091
- '#has unknown class Monolog\\Handler\\Record#'
- '#::processRecord#'
- '#::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.#'

View File

@@ -65,6 +65,7 @@ class ElasticaFormatter extends NormalizerFormatter
*/
public function getType(): string
{
/** @phpstan-ignore-next-line */
return $this->type;
}
@@ -78,6 +79,7 @@ class ElasticaFormatter extends NormalizerFormatter
$document = new Document();
$document->setData($record);
if (method_exists($document, 'setType')) {
/** @phpstan-ignore-next-line */
$document->setType($this->type);
}
$document->setIndex($this->index);

View File

@@ -20,6 +20,8 @@ use Monolog\Utils;
* @see http://docs.graylog.org/en/latest/pages/gelf.html
*
* @author Matt Lehner <mlehner@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
*/
class GelfMessageFormatter extends NormalizerFormatter
{
@@ -49,6 +51,8 @@ class GelfMessageFormatter extends NormalizerFormatter
* Translates Monolog log levels to Graylog2 log priorities.
*
* @var array<int, int>
*
* @phpstan-var array<Level, int>
*/
private $logLevels = [
Logger::DEBUG => 7,
@@ -65,7 +69,7 @@ class GelfMessageFormatter extends NormalizerFormatter
{
parent::__construct('U.u');
$this->systemName = (is_null($systemName) || $systemName === '') ? gethostname() : $systemName;
$this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName;
$this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix;
$this->contextPrefix = $contextPrefix;
@@ -73,15 +77,18 @@ class GelfMessageFormatter extends NormalizerFormatter
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function format(array $record): Message
{
$context = $extra = [];
if (isset($record['context'])) {
$record['context'] = parent::format($record['context']);
/** @var mixed[] $context */
$context = parent::normalize($record['context']);
}
if (isset($record['extra'])) {
$record['extra'] = parent::format($record['extra']);
/** @var mixed[] $extra */
$extra = parent::normalize($record['extra']);
}
if (!isset($record['datetime'], $record['message'], $record['level'])) {
@@ -105,31 +112,31 @@ class GelfMessageFormatter extends NormalizerFormatter
if (isset($record['channel'])) {
$message->setFacility($record['channel']);
}
if (isset($record['extra']['line'])) {
$message->setLine($record['extra']['line']);
unset($record['extra']['line']);
if (isset($extra['line'])) {
$message->setLine($extra['line']);
unset($extra['line']);
}
if (isset($record['extra']['file'])) {
$message->setFile($record['extra']['file']);
unset($record['extra']['file']);
if (isset($extra['file'])) {
$message->setFile($extra['file']);
unset($extra['file']);
}
foreach ($record['extra'] as $key => $val) {
foreach ($extra as $key => $val) {
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->extraPrefix . $key . $val);
if ($len > $this->maxLength) {
$message->setAdditional($this->extraPrefix . $key, Utils::substr($val, 0, $this->maxLength));
$message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));
continue;
}
$message->setAdditional($this->extraPrefix . $key, $val);
}
foreach ($record['context'] as $key => $val) {
foreach ($context as $key => $val) {
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->contextPrefix . $key . $val);
if ($len > $this->maxLength) {
$message->setAdditional($this->contextPrefix . $key, Utils::substr($val, 0, $this->maxLength));
$message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));
continue;
}
@@ -137,8 +144,8 @@ class GelfMessageFormatter extends NormalizerFormatter
}
/** @phpstan-ignore-next-line */
if (null === $message->getFile() && isset($record['context']['exception']['file'])) {
if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) {
if (null === $message->getFile() && isset($context['exception']['file'])) {
if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
$message->setFile($matches[1]);
$message->setLine($matches[2]);
}

View File

@@ -110,6 +110,9 @@ class LineFormatter extends NormalizerFormatter
// remove leftover %extra.xxx% and %context.xxx% if any
if (false !== strpos($output, '%')) {
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
if (null === $output) {
throw new \RuntimeException('Failed to run preg_replace: ' . preg_last_error() . ' / ' . preg_last_error_msg());
}
}
return $output;

View File

@@ -52,7 +52,7 @@ class LogstashFormatter extends NormalizerFormatter
// logstash requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP');
$this->systemName = $systemName === null ? gethostname() : $systemName;
$this->systemName = $systemName === null ? (string) gethostname() : $systemName;
$this->applicationName = $applicationName;
$this->extraKey = $extraKey;
$this->contextKey = $contextKey;

View File

@@ -37,23 +37,26 @@ class MongoDBFormatter implements FormatterInterface
$this->maxNestingLevel = max($maxNestingLevel, 0);
$this->exceptionTraceAsString = $exceptionTraceAsString;
$this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare(phpversion('mongodb'), '1.1.9', '<=');
$this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<=');
}
/**
* {@inheritDoc}
*
* @return scalar[]
* @return mixed[]
*/
public function format(array $record): array
{
return $this->formatArray($record);
/** @var mixed[] $res */
$res = $this->formatArray($record);
return $res;
}
/**
* {@inheritDoc}
*
* @return array<scalar[]>
* @return array<mixed[]>
*/
public function formatBatch(array $records): array
{
@@ -152,6 +155,7 @@ class MongoDBFormatter implements FormatterInterface
? (int) $milliseconds
: (string) $milliseconds;
// @phpstan-ignore-next-line
return new UTCDateTime($milliseconds);
}
}

View File

@@ -47,6 +47,8 @@ class NormalizerFormatter implements FormatterInterface
/**
* {@inheritdoc}
*
* @param mixed[] $record
*/
public function format(array $record)
{
@@ -123,7 +125,7 @@ class NormalizerFormatter implements FormatterInterface
/**
* @param mixed $data
* @return scalar|array<scalar>
* @return null|scalar|array<array|scalar|null>
*/
protected function normalize($data, int $depth = 0)
{

View File

@@ -22,20 +22,21 @@ class ScalarFormatter extends NormalizerFormatter
/**
* {@inheritdoc}
*
* @phpstan-return scalar[] $record
* @phpstan-return array<string, scalar|null> $record
*/
public function format(array $record): array
{
$result = [];
foreach ($record as $key => $value) {
$record[$key] = $this->normalizeValue($value);
$result[$key] = $this->normalizeValue($value);
}
return $record;
return $result;
}
/**
* @param mixed $value
* @return string|int|bool|null
* @param mixed $value
* @return scalar|null
*/
protected function normalizeValue($value)
{

View File

@@ -69,6 +69,7 @@ class WildfireFormatter extends NormalizerFormatter
unset($record['extra']['line']);
}
/** @var mixed[] $record */
$record = $this->normalize($record);
$message = ['message' => $record['message']];
$handleError = false;
@@ -125,7 +126,7 @@ class WildfireFormatter extends NormalizerFormatter
/**
* {@inheritdoc}
*
* @return scalar|array<scalar>|object
* @return null|scalar|array<array|scalar|null>|object
*/
protected function normalize($data, int $depth = 0)
{

View File

@@ -21,6 +21,7 @@ namespace Monolog\Handler;
*
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type Record 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
@@ -38,6 +39,7 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}

View File

@@ -94,6 +94,7 @@ class AmqpHandler extends AbstractProcessingHandler
continue;
}
/** @var Record $record */
$record = $this->processRecord($record);
$data = $this->getFormatter()->format($record);

View File

@@ -197,7 +197,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'];
static $labels = [];
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) {
$style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) {
if (trim($m[1]) === 'autolabel') {
// Format the string as a label with consistent auto assigned background color
if (!isset($labels[$string])) {
@@ -210,6 +210,12 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return $m[1];
}, $style);
if (null === $style) {
throw new \RuntimeException('Failed to run preg_replace_callback: ' . preg_last_error() . ' / ' . preg_last_error_msg());
}
return $style;
}
/**

View File

@@ -80,6 +80,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}

View File

@@ -22,6 +22,8 @@ use Monolog\Utils;
* This also works out of the box with Firefox 43+
*
* @author Christophe Coevoet <stof@notk.org>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class ChromePHPHandler extends AbstractProcessingHandler
{
@@ -87,7 +89,9 @@ class ChromePHPHandler extends AbstractProcessingHandler
if ($record['level'] < $this->level) {
continue;
}
$messages[] = $this->processRecord($record);
/** @var Record $message */
$message = $this->processRecord($record);
$messages[] = $message;
}
if (!empty($messages)) {

View File

@@ -46,7 +46,7 @@ class CubeHandler extends AbstractProcessingHandler
{
$urlInfo = parse_url($url);
if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
}
@@ -76,11 +76,12 @@ class CubeHandler extends AbstractProcessingHandler
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
}
$this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
if (!$this->udpConnection) {
$udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
if (false === $udpConnection) {
throw new \LogicException('Unable to create a socket');
}
$this->udpConnection = $udpConnection;
if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
}
@@ -98,12 +99,12 @@ class CubeHandler extends AbstractProcessingHandler
throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler');
}
$this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
if (!$this->httpConnection) {
$httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
if (false === $httpConnection) {
throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
}
$this->httpConnection = $httpConnection;
curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
}
@@ -150,6 +151,10 @@ class CubeHandler extends AbstractProcessingHandler
$this->connectHttp();
}
if (null === $this->httpConnection) {
throw new \LogicException('No connection could be established');
}
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',

View File

@@ -12,6 +12,7 @@
namespace Monolog\Handler;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Simple handler wrapper that deduplicates log records across multiple requests
@@ -34,6 +35,8 @@ use Monolog\Logger;
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
*/
class DeduplicationHandler extends BufferHandler
{
@@ -43,7 +46,7 @@ class DeduplicationHandler extends BufferHandler
protected $deduplicationStore;
/**
* @var int
* @var Level
*/
protected $deduplicationLevel;
@@ -63,6 +66,8 @@ class DeduplicationHandler extends BufferHandler
* @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*
* @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel
*/
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true)
{

View File

@@ -79,6 +79,9 @@ class ErrorLogHandler extends AbstractProcessingHandler
}
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
if ($lines === false) {
throw new \RuntimeException('Failed to preg_split formatted string: '.preg_last_error().' / '.preg_last_error_msg());
}
foreach ($lines as $line) {
error_log($line, $this->messageType);
}

View File

@@ -13,6 +13,15 @@ namespace Monolog\Handler;
use Throwable;
/**
* Forwards records to at most one handler
*
* If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
*
* As soon as one handler handles a record successfully, the handling stops there.
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class FallbackGroupHandler extends GroupHandler
{
/**
@@ -21,6 +30,7 @@ class FallbackGroupHandler extends GroupHandler
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
foreach ($this->handlers as $handler) {
@@ -45,6 +55,7 @@ class FallbackGroupHandler extends GroupHandler
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
/** @var Record[] $records */
$records = $processed;
}

View File

@@ -64,6 +64,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* @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
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
{
@@ -89,6 +90,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* @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
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
*/
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
{
@@ -124,6 +126,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}

View File

@@ -12,6 +12,7 @@
namespace Monolog\Handler\FingersCrossed;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Channel and Error level based monolog activation strategy. Allows to trigger activation
@@ -35,11 +36,12 @@ use Monolog\Logger;
*
* @phpstan-import-type Record from \Monolog\Logger
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{
/**
* @var int
* @var Level
*/
private $defaultActionLevel;
@@ -52,7 +54,8 @@ class ChannelLevelActivationStrategy implements ActivationStrategyInterface
* @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
* @phpstan-param array<string, Level> $channelToActionLevel
* @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel
*/
public function __construct($defaultActionLevel, array $channelToActionLevel = [])
{

View File

@@ -12,21 +12,27 @@
namespace Monolog\Handler\FingersCrossed;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Error level based activation strategy.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{
/**
* @var int
* @var Level
*/
private $actionLevel;
/**
* @param int|string $actionLevel Level or name or value
*
* @phpstan-param Level|LevelName|LogLevel::* $actionLevel
*/
public function __construct($actionLevel)
{

View File

@@ -16,6 +16,7 @@ use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
/**
* Buffers all records until a certain level is reached
@@ -73,6 +74,9 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
* @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
*
* @phpstan-param Level|LevelName|LogLevel::* $passthruLevel
* @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy
*/
public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null)
{
@@ -127,6 +131,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@@ -152,7 +157,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
{
$this->flushBuffer();
$this->handler->close();
$this->getHandler()->close();
}
public function reset()

View File

@@ -67,11 +67,15 @@ class FirePHPHandler extends AbstractProcessingHandler
* @param string $message Log message
*
* @return array<string, string> Complete header string ready for the client as key and message as value
*
* @phpstan-return non-empty-array<string, string>
*/
protected function createHeader(array $meta, string $message): array
{
$header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta));
// See https://github.com/phpstan/phpstan/issues/5219
// @phpstan-ignore-next-line
return [$header => $message];
}
@@ -80,6 +84,8 @@ class FirePHPHandler extends AbstractProcessingHandler
*
* @return array<string, string>
*
* @phpstan-return non-empty-array<string, string>
*
* @see createHeader()
*
* @phpstan-param FormattedRecord $record

View File

@@ -43,8 +43,6 @@ class FleepHookHandler extends SocketHandler
* see https://fleep.io/integrations/webhooks/
*
* @param string $token Webhook token
* @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 MissingExtensionException
*/
public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true)

View File

@@ -37,9 +37,6 @@ class FlowdockHandler extends SocketHandler
protected $apiToken;
/**
* @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 MissingExtensionException if OpenSSL is missing
*/
public function __construct(string $apiToken, $level = Logger::DEBUG, bool $bubble = true)

View File

@@ -25,12 +25,12 @@ use Monolog\Formatter\FormatterInterface;
class GelfHandler extends AbstractProcessingHandler
{
/**
* @var PublisherInterface|null the publisher object that sends the message to the server
* @var PublisherInterface the publisher object that sends the message to the server
*/
protected $publisher;
/**
* @param PublisherInterface $publisher a publisher object
* @param PublisherInterface $publisher a gelf publisher object
*/
public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true)
{

View File

@@ -18,6 +18,8 @@ use Monolog\ResettableInterface;
* Forwards records to multiple handlers
*
* @author Lenar Lõhmus <lenar@city.ee>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
{
@@ -45,7 +47,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function isHandling(array $record): bool
{
@@ -59,11 +61,12 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@@ -75,7 +78,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@@ -84,6 +87,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
/** @var Record[] $records */
$records = $processed;
}
@@ -113,7 +117,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{

View File

@@ -30,8 +30,6 @@ class InsightOpsHandler extends SocketHandler
* @param string $token Log token supplied by InsightOps
* @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'.
* @param bool $useSSL Whether or not SSL encryption should be used
* @param string|int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/

View File

@@ -26,8 +26,6 @@ class LogEntriesHandler extends SocketHandler
/**
* @param string $token Log token supplied by LogEntries
* @param bool $useSSL Whether or not SSL encryption should be used.
* @param string|int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
* @param string $host Custom hostname to send the data to if needed
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing

View File

@@ -40,8 +40,6 @@ class LogmaticHandler extends SocketHandler
* @param string $hostname Host name supplied by Logmatic.
* @param string $appname Application name supplied by Logmatic.
* @param bool $useSSL Whether or not SSL encryption should be used.
* @param int|string $level The minimum logging level to trigger this handler.
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/

View File

@@ -34,7 +34,9 @@ abstract class MailHandler extends AbstractProcessingHandler
if ($record['level'] < $this->level) {
continue;
}
$messages[] = $this->processRecord($record);
/** @var Record $message */
$message = $this->processRecord($record);
$messages[] = $message;
}
if (!empty($messages)) {
@@ -61,7 +63,7 @@ abstract class MailHandler extends AbstractProcessingHandler
}
/**
* @phpstan-param Record[] $records
* @phpstan-param non-empty-array<Record> $records
* @phpstan-return Record
*/
protected function getHighestRecord(array $records): array

View File

@@ -162,7 +162,7 @@ class ProcessHandler extends AbstractProcessingHandler
*/
protected function readProcessErrors(): string
{
return stream_get_contents($this->pipes[2]);
return (string) stream_get_contents($this->pipes[2]);
}
/**

View File

@@ -13,6 +13,7 @@ namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Utils;
use Psr\Log\LogLevel;
/**
* Sends notifications through the pushover api to mobile phones
@@ -21,6 +22,8 @@ use Monolog\Utils;
* @see https://www.pushover.net/api
*
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class PushoverHandler extends SocketHandler
{
@@ -28,7 +31,7 @@ class PushoverHandler extends SocketHandler
private $token;
/** @var array<int|string> */
private $users;
/** @var ?string */
/** @var string */
private $title;
/** @var string|int|null */
private $user = null;
@@ -80,8 +83,6 @@ class PushoverHandler extends SocketHandler
* @param string $token Pushover api token
* @param string|array $users Pushover user id or array of ids the message will be sent to
* @param string|null $title Title sent to the Pushover API
* @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 bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
* the pushover.net app owner. OpenSSL is required for this option.
* @param string|int $highPriorityLevel The minimum logging level at which this handler will start
@@ -93,7 +94,9 @@ class PushoverHandler extends SocketHandler
* @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
* @phpstan-param string|array<int|string> $users
* @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel
* @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel
*/
public function __construct(
string $token,
@@ -112,7 +115,7 @@ class PushoverHandler extends SocketHandler
$this->token = $token;
$this->users = (array) $users;
$this->title = $title ?: gethostname();
$this->title = $title ?: (string) gethostname();
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
$this->retry = $retry;
@@ -195,6 +198,8 @@ class PushoverHandler extends SocketHandler
/**
* @param int|string $value
*
* @phpstan-param Level|LevelName|LogLevel::* $value
*/
public function setHighPriorityLevel($value): self
{
@@ -205,6 +210,8 @@ class PushoverHandler extends SocketHandler
/**
* @param int|string $value
*
* @phpstan-param Level|LevelName|LogLevel::* $value
*/
public function setEmergencyLevel($value): self
{

View File

@@ -97,6 +97,7 @@ class RollbarHandler extends AbstractProcessingHandler
$toLog = $record['message'];
}
// @phpstan-ignore-next-line
$this->rollbarLogger->log($context['level'], $toLog, $context);
$this->hasRecords = true;

View File

@@ -46,8 +46,6 @@ class RotatingFileHandler extends StreamHandler
/**
* @param string $filename
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
* @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 int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
* @param bool $useLocking Try to lock log file before doing any writes
*/
@@ -116,7 +114,7 @@ class RotatingFileHandler extends StreamHandler
{
// on the first record written, if the log is new, we should rotate (once per day)
if (null === $this->mustRotate) {
$this->mustRotate = !file_exists($this->url);
$this->mustRotate = null === $this->url || !file_exists($this->url);
}
if ($this->nextRotation <= $record['datetime']) {
@@ -142,6 +140,11 @@ class RotatingFileHandler extends StreamHandler
}
$logFiles = glob($this->getGlobPattern());
if (false === $logFiles) {
// failed to glob
return;
}
if ($this->maxFiles >= count($logFiles)) {
// no files to remove
return;
@@ -176,7 +179,7 @@ class RotatingFileHandler extends StreamHandler
$fileInfo['dirname'] . '/' . $this->filenameFormat
);
if (!empty($fileInfo['extension'])) {
if (isset($fileInfo['extension'])) {
$timedFilename .= '.'.$fileInfo['extension'];
}
@@ -191,7 +194,7 @@ class RotatingFileHandler extends StreamHandler
[$fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'],
$fileInfo['dirname'] . '/' . $this->filenameFormat
);
if (!empty($fileInfo['extension'])) {
if (isset($fileInfo['extension'])) {
$glob .= '.'.$fileInfo['extension'];
}

View File

@@ -36,7 +36,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
/**
* @var HandlerInterface|callable
* @phpstan-var HandlerInterface|callable(Record, HandlerInterface): HandlerInterface
* @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface
*/
protected $handler;
@@ -46,7 +46,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
protected $factor;
/**
* @psalm-param HandlerInterface|callable(Record, HandlerInterface): HandlerInterface $handler
* @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, 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)
@@ -71,6 +71,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
{
if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}

View File

@@ -25,6 +25,7 @@ use Monolog\Formatter\FormatterInterface;
* @see https://api.slack.com/docs/message-attachments
*
* @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler
* @phpstan-import-type Record from \Monolog\Logger
*/
class SlackRecord
{
@@ -121,7 +122,7 @@ class SlackRecord
* is expecting.
*
* @phpstan-param FormattedRecord $record
* @phpstan-return string[]
* @phpstan-return mixed[]
*/
public function getSlackData(array $record): array
{
@@ -137,6 +138,7 @@ class SlackRecord
}
if ($this->formatter && !$this->useAttachment) {
/** @phpstan-ignore-next-line */
$message = $this->formatter->format($record);
} else {
$message = $record['message'];
@@ -221,6 +223,7 @@ class SlackRecord
*/
public function stringify(array $fields): string
{
/** @var Record $fields */
$normalized = $this->normalizerFormatter->format($fields);
$hasSecondDimension = count(array_filter($normalized, 'is_array'));
@@ -341,8 +344,11 @@ class SlackRecord
*/
private function generateAttachmentFields(array $data): array
{
/** @var Record $data */
$normalized = $this->normalizerFormatter->format($data);
$fields = array();
foreach ($this->normalizerFormatter->format($data) as $key => $value) {
foreach ($normalized as $key => $value) {
$fields[] = $this->generateAttachmentField((string) $key, $value);
}

View File

@@ -216,7 +216,7 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Wrapper to allow mocking
*
* @return resource|bool
* @return resource|false
*/
protected function pfsockopen()
{
@@ -226,7 +226,7 @@ class SocketHandler extends AbstractProcessingHandler
/**
* Wrapper to allow mocking
*
* @return resource|bool
* @return resource|false
*/
protected function fsockopen()
{
@@ -245,6 +245,10 @@ class SocketHandler extends AbstractProcessingHandler
$seconds = floor($this->timeout);
$microseconds = round(($this->timeout - $seconds) * 1e6);
if (!is_resource($this->resource)) {
throw new \LogicException('streamSetTimeout called but $this->resource is not a resource');
}
return stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds);
}
@@ -257,6 +261,10 @@ class SocketHandler extends AbstractProcessingHandler
*/
protected function streamSetChunkSize()
{
if (!is_resource($this->resource)) {
throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource');
}
return stream_set_chunk_size($this->resource, $this->chunkSize);
}
@@ -267,6 +275,10 @@ class SocketHandler extends AbstractProcessingHandler
*/
protected function fwrite(string $data)
{
if (!is_resource($this->resource)) {
throw new \LogicException('fwrite called but $this->resource is not a resource');
}
return @fwrite($this->resource, $data);
}
@@ -277,6 +289,10 @@ class SocketHandler extends AbstractProcessingHandler
*/
protected function streamGetMetadata()
{
if (!is_resource($this->resource)) {
throw new \LogicException('streamGetMetadata called but $this->resource is not a resource');
}
return stream_get_meta_data($this->resource);
}
@@ -325,7 +341,7 @@ class SocketHandler extends AbstractProcessingHandler
} else {
$resource = $this->fsockopen();
}
if (!$resource) {
if (is_bool($resource)) {
throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
}
$this->resource = $resource;
@@ -361,7 +377,7 @@ class SocketHandler extends AbstractProcessingHandler
}
$sent += $chunk;
$socketInfo = $this->streamGetMetadata();
if ($socketInfo['timed_out']) {
if (is_array($socketInfo) && $socketInfo['timed_out']) {
throw new \RuntimeException("Write timed-out");
}

View File

@@ -101,34 +101,41 @@ class StreamHandler extends AbstractProcessingHandler
protected function write(array $record): void
{
if (!is_resource($this->stream)) {
if (null === $this->url || '' === $this->url) {
$url = $this->url;
if (null === $url || '' === $url) {
throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
}
$this->createDir();
$this->createDir($url);
$this->errorMessage = null;
set_error_handler([$this, 'customErrorHandler']);
$this->stream = fopen($this->url, 'a');
$stream = fopen($url, 'a');
if ($this->filePermission !== null) {
@chmod($this->url, $this->filePermission);
@chmod($url, $this->filePermission);
}
restore_error_handler();
if (!is_resource($this->stream)) {
if (!is_resource($stream)) {
$this->stream = null;
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $this->url));
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $url));
}
stream_set_chunk_size($this->stream, self::MAX_CHUNK_SIZE);
stream_set_chunk_size($stream, self::MAX_CHUNK_SIZE);
$this->stream = $stream;
}
$stream = $this->stream;
if (!is_resource($stream)) {
throw new \LogicException('No stream was opened yet');
}
if ($this->useLocking) {
// ignoring errors here, there's not much we can do about them
flock($this->stream, LOCK_EX);
flock($stream, LOCK_EX);
}
$this->streamWrite($this->stream, $record);
$this->streamWrite($stream, $record);
if ($this->useLocking) {
flock($this->stream, LOCK_UN);
flock($stream, LOCK_UN);
}
}
@@ -165,14 +172,14 @@ class StreamHandler extends AbstractProcessingHandler
return null;
}
private function createDir(): void
private function createDir(string $url): void
{
// Do not try to create dir if it has already been tried.
if ($this->dirCreated) {
return;
}
$dir = $this->getDirFromStream($this->url);
$dir = $this->getDirFromStream($url);
if (null !== $dir && !is_dir($dir)) {
$this->errorMessage = null;
set_error_handler([$this, 'customErrorHandler']);

View File

@@ -36,8 +36,6 @@ class SyslogHandler extends AbstractSyslogHandler
/**
* @param string $ident
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
* @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 int $logopts Option flags for the openlog() call, defaults to LOG_PID
*/
public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID)

View File

@@ -45,7 +45,6 @@ class SyslogUdpHandler extends AbstractSyslogHandler
* @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then)
* @param int $port Port number, or 0 if $host is a unix socket
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
* @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 string $ident Program name or tag for each log message.
* @param int $rfc RFC to format the message for.
@@ -88,7 +87,12 @@ class SyslogUdpHandler extends AbstractSyslogHandler
$message = implode("\n", $message);
}
return preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY);
$lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY);
if (false === $lines) {
throw new \RuntimeException('Could not preg_split: '.preg_last_error().' / '.preg_last_error_msg());
}
return $lines;
}
/**

View File

@@ -27,6 +27,8 @@ use Monolog\Logger;
* @link https://core.telegram.org/bots/api
*
* @author Mazur Alexandr <alexandrmazur96@gmail.com>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class TelegramBotHandler extends AbstractProcessingHandler
{
@@ -127,6 +129,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
*/
public function handleBatch(array $records): void
{
/** @var Record[] $messages */
$messages = [];
foreach ($records as $record) {
@@ -135,6 +138,7 @@ class TelegramBotHandler extends AbstractProcessingHandler
}
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@@ -174,6 +178,9 @@ class TelegramBotHandler extends AbstractProcessingHandler
]));
$result = Curl\Util::execute($ch);
if (!is_string($result)) {
throw new RuntimeException('Telegram API error. Description: No response');
}
$result = json_decode($result, true);
if ($result['ok'] === false) {

View File

@@ -118,6 +118,8 @@ class TestHandler extends AbstractProcessingHandler
/**
* @param string|int $level Logging level value or name
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecords($level): bool
{
@@ -151,6 +153,8 @@ class TestHandler extends AbstractProcessingHandler
/**
* @param string|int $level Logging level value or name
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function hasRecordThatContains(string $message, $level): bool
{
@@ -214,10 +218,11 @@ class TestHandler extends AbstractProcessingHandler
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
$level = constant('Monolog\Logger::' . strtoupper($matches[2]));
if (method_exists($this, $genericMethod)) {
$callback = [$this, $genericMethod];
if (is_callable($callback)) {
$args[] = $level;
return call_user_func_array([$this, $genericMethod], $args);
return call_user_func_array($callback, $args);
}
}

View File

@@ -16,15 +16,18 @@ namespace Monolog\Handler;
* and continuing through to give every handler a chance to succeed.
*
* @author Craig D'Amelio <craig@damelio.ca>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
class WhatFailureGroupHandler extends GroupHandler
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
/** @var Record $record */
$record = $this->processRecord($record);
}
@@ -40,7 +43,7 @@ class WhatFailureGroupHandler extends GroupHandler
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function handleBatch(array $records): void
{
@@ -49,6 +52,7 @@ class WhatFailureGroupHandler extends GroupHandler
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
/** @var Record[] $records */
$records = $processed;
}

View File

@@ -12,12 +12,16 @@
namespace Monolog\Processor;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Injects Git branch and Git commit SHA in all records
*
* @author Nick Otter
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class GitProcessor implements ProcessorInterface
{
@@ -28,6 +32,8 @@ class GitProcessor implements ProcessorInterface
/**
* @param string|int $level The minimum logging level at which this Processor will be triggered
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function __construct($level = Logger::DEBUG)
{

View File

@@ -12,6 +12,7 @@
namespace Monolog\Processor;
use Monolog\Logger;
use Psr\Log\LogLevel;
/**
* Injects line/file:class/function where the log message came from
@@ -23,6 +24,9 @@ use Monolog\Logger;
* triggered the FingersCrossedHandler.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Level from \Monolog\Logger
* @phpstan-import-type LevelName from \Monolog\Logger
*/
class IntrospectionProcessor implements ProcessorInterface
{
@@ -41,6 +45,8 @@ class IntrospectionProcessor implements ProcessorInterface
/**
* @param string|int $level The minimum logging level at which this Processor will be triggered
* @param string[] $skipClassesPartials
*
* @phpstan-param Level|LevelName|LogLevel::* $level
*/
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
{

View File

@@ -138,6 +138,8 @@ final class Utils
* @param int $code return code of json_last_error function
* @param mixed $data data that was meant to be encoded
* @throws \RuntimeException
*
* @return never
*/
private static function throwEncodeError(int $code, $data): void
{
@@ -186,6 +188,9 @@ final class Utils
},
$data
);
if (!is_string($data)) {
throw new \RuntimeException('Failed to preg_replace_callback: '.preg_last_error().' / '.preg_last_error_msg());
}
$data = str_replace(
['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'],
['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'],