Skip to content

Commit

Permalink
Merge pull request #1557 from Seldaek/phpstan
Browse files Browse the repository at this point in the history
Add Record/Level/LevelName type aliases and improve phpstan type coverage to level 8
  • Loading branch information
Seldaek authored Jul 4, 2021
2 parents 5c9d9bf + 8b5278d commit 50729e0
Show file tree
Hide file tree
Showing 104 changed files with 1,088 additions and 352 deletions.
16 changes: 15 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 5
level: 8

treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false
Expand All @@ -18,3 +18,17 @@ 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 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\(\) 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.#'
85 changes: 60 additions & 25 deletions src/Monolog/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,30 @@
*/
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)
Expand All @@ -50,10 +61,10 @@ public function __construct(LoggerInterface $logger)
*
* 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
Expand All @@ -73,7 +84,11 @@ public static function register(LoggerInterface $logger, $errorLevelMap = [], $e
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);
Expand All @@ -91,12 +106,18 @@ public function registerExceptionHandler($levelMap = [], $callPrevious = true):
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;
Expand All @@ -105,20 +126,23 @@ public function registerErrorHandler(array $levelMap = [], $callPrevious = true,
}

/**
* @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 [
Expand All @@ -127,6 +151,9 @@ protected function defaultExceptionLevelMap(): array
];
}

/**
* @return array<int, LogLevel::*>
*/
protected function defaultErrorLevelMap(): array
{
return [
Expand All @@ -148,7 +175,10 @@ protected function defaultErrorLevelMap(): array
];
}

private function handleException(\Throwable $e)
/**
* @phpstan-return never
*/
private function handleException(\Throwable $e): void
{
$level = LogLevel::ERROR;
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
Expand Down Expand Up @@ -177,11 +207,13 @@ private function handleException(\Throwable $e)

/**
* @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
Expand All @@ -197,7 +229,7 @@ public function handleError($code, $message, $file = '', $line = 0, $context = [
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;
Expand All @@ -206,14 +238,14 @@ public function handleError($code, $message, $file = '', $line = 0, $context = [
/**
* @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]
);
Expand All @@ -226,6 +258,9 @@ public function handleFatalError()
}
}

/**
* @param int $code
*/
private static function codeToString($code): string
{
switch ($code) {
Expand Down
2 changes: 2 additions & 0 deletions src/Monolog/Formatter/ChromePHPFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
10 changes: 7 additions & 3 deletions src/Monolog/Formatter/ElasticaFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* 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
{
Expand Down Expand Up @@ -63,19 +65,21 @@ public function getIndex(): string
*/
public function getType(): string
{
/** @phpstan-ignore-next-line */
return $this->type;
}

/**
* Convert a log message into an Elastica Document
* @param array $record
* @return Document
*
* @phpstan-param Record $record
*/
protected function getDocument(array $record): Document
{
$document = new Document();
$document->setData($record);
if(method_exists($document, 'setType')) {
if (method_exists($document, 'setType')) {
/** @phpstan-ignore-next-line */
$document->setType($this->type);
}
$document->setIndex($this->index);
Expand Down
8 changes: 4 additions & 4 deletions src/Monolog/Formatter/ElasticsearchFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Monolog\Formatter;

use DateTime;
use DateTimeInterface;

/**
* Format a log message into an Elasticsearch record
Expand All @@ -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;
Expand Down Expand Up @@ -76,8 +76,8 @@ public function getType(): string
/**
* 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
{
Expand Down
4 changes: 4 additions & 0 deletions src/Monolog/Formatter/FlowdockFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public function __construct(string $source, string $sourceEmail)

/**
* {@inheritdoc}
*
* @return mixed[]
*/
public function format(array $record): array
{
Expand Down Expand Up @@ -70,6 +72,8 @@ public function format(array $record): array

/**
* {@inheritdoc}
*
* @return mixed[][]
*/
public function formatBatch(array $records): array
{
Expand Down
6 changes: 6 additions & 0 deletions src/Monolog/Formatter/FormatterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* Interface for formatters
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @phpstan-import-type Record from \Monolog\Logger
*/
interface FormatterInterface
{
Expand All @@ -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);

Expand All @@ -31,6 +35,8 @@ public function format(array $record);
*
* @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);
}
Loading

0 comments on commit 50729e0

Please sign in to comment.