This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1 @@
<?php
@@ -0,0 +1 @@
<?php
@@ -0,0 +1,27 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use DateTimeZone;
class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
{
private $useMicroseconds;
public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
{
$this->useMicroseconds = $useMicroseconds;
// if you like to use a custom time to pass to Logger::addRecord directly,
// call modify() or setTimestamp() on this instance to change the date after creating it
parent::__construct('now', $timezone);
}
public function jsonSerialize() : string
{
if ($this->useMicroseconds) {
return $this->format('Y-m-d\\TH:i:s.uP');
}
return $this->format('Y-m-d\\TH:i:sP');
}
public function __toString() : string
{
return $this->jsonSerialize();
}
}
@@ -0,0 +1,176 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Psr\Log\LoggerInterface;
use MailPoetVendor\Psr\Log\LogLevel;
class ErrorHandler
{
private $logger;
private $previousExceptionHandler = null;
private $uncaughtExceptionLevelMap = [];
private $previousErrorHandler = null;
private $errorLevelMap = [];
private $handleOnlyReportedErrors = \true;
private $hasFatalErrorHandler = \false;
private $fatalLevel = LogLevel::ALERT;
private $reservedMemory = null;
private $lastFatalData = null;
private static $fatalErrors = [\E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR];
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null) : self
{
$handler = new static($logger);
if ($errorLevelMap !== \false) {
$handler->registerErrorHandler($errorLevelMap);
}
if ($exceptionLevelMap !== \false) {
$handler->registerExceptionHandler($exceptionLevelMap);
}
if ($fatalLevel !== \false) {
$handler->registerFatalHandler($fatalLevel);
}
return $handler;
}
public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = \true) : self
{
$prev = \set_exception_handler(function (\Throwable $e) : void {
$this->handleException($e);
});
$this->uncaughtExceptionLevelMap = $levelMap;
foreach ($this->defaultExceptionLevelMap() as $class => $level) {
if (!isset($this->uncaughtExceptionLevelMap[$class])) {
$this->uncaughtExceptionLevelMap[$class] = $level;
}
}
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
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;
return $this;
}
public function registerFatalHandler($level = null, int $reservedMemorySize = 20) : self
{
\register_shutdown_function([$this, 'handleFatalError']);
$this->reservedMemory = \str_repeat(' ', 1024 * $reservedMemorySize);
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
$this->hasFatalErrorHandler = \true;
return $this;
}
protected function defaultExceptionLevelMap() : array
{
return ['ParseError' => LogLevel::CRITICAL, 'Throwable' => LogLevel::ERROR];
}
protected function defaultErrorLevelMap() : array
{
return [\E_ERROR => LogLevel::CRITICAL, \E_WARNING => LogLevel::WARNING, \E_PARSE => LogLevel::ALERT, \E_NOTICE => LogLevel::NOTICE, \E_CORE_ERROR => LogLevel::CRITICAL, \E_CORE_WARNING => LogLevel::WARNING, \E_COMPILE_ERROR => LogLevel::ALERT, \E_COMPILE_WARNING => LogLevel::WARNING, \E_USER_ERROR => LogLevel::ERROR, \E_USER_WARNING => LogLevel::WARNING, \E_USER_NOTICE => LogLevel::NOTICE, \E_STRICT => LogLevel::NOTICE, \E_RECOVERABLE_ERROR => LogLevel::ERROR, \E_DEPRECATED => LogLevel::NOTICE, \E_USER_DEPRECATED => LogLevel::NOTICE];
}
private function handleException(\Throwable $e) : void
{
$level = LogLevel::ERROR;
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
if ($e instanceof $class) {
$level = $candidate;
break;
}
}
$this->logger->log($level, \sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e]);
if ($this->previousExceptionHandler) {
($this->previousExceptionHandler)($e);
}
if (!\headers_sent() && \in_array(\strtolower((string) \ini_get('display_errors')), ['0', '', 'false', 'off', 'none', 'no'], \true)) {
\http_response_code(500);
}
exit(255);
}
public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []) : bool
{
if ($this->handleOnlyReportedErrors && !(\error_reporting() & $code)) {
return \false;
}
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
if (!$this->hasFatalErrorHandler || !\in_array($code, self::$fatalErrors, \true)) {
$level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL;
$this->logger->log($level, self::codeToString($code) . ': ' . $message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]);
} else {
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
\array_shift($trace);
// Exclude handleError from trace
$this->lastFatalData = ['type' => $code, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => $trace];
}
if ($this->previousErrorHandler === \true) {
return \false;
} elseif ($this->previousErrorHandler) {
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
}
return \true;
}
public function handleFatalError() : void
{
$this->reservedMemory = '';
if (\is_array($this->lastFatalData)) {
$lastError = $this->lastFatalData;
} else {
$lastError = \error_get_last();
}
if ($lastError && \in_array($lastError['type'], self::$fatalErrors, \true)) {
$trace = $lastError['trace'] ?? null;
$this->logger->log($this->fatalLevel, 'Fatal Error (' . self::codeToString($lastError['type']) . '): ' . $lastError['message'], ['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $trace]);
if ($this->logger instanceof Logger) {
foreach ($this->logger->getHandlers() as $handler) {
$handler->close();
}
}
}
}
private static function codeToString($code) : string
{
switch ($code) {
case \E_ERROR:
return 'E_ERROR';
case \E_WARNING:
return 'E_WARNING';
case \E_PARSE:
return 'E_PARSE';
case \E_NOTICE:
return 'E_NOTICE';
case \E_CORE_ERROR:
return 'E_CORE_ERROR';
case \E_CORE_WARNING:
return 'E_CORE_WARNING';
case \E_COMPILE_ERROR:
return 'E_COMPILE_ERROR';
case \E_COMPILE_WARNING:
return 'E_COMPILE_WARNING';
case \E_USER_ERROR:
return 'E_USER_ERROR';
case \E_USER_WARNING:
return 'E_USER_WARNING';
case \E_USER_NOTICE:
return 'E_USER_NOTICE';
case \E_STRICT:
return 'E_STRICT';
case \E_RECOVERABLE_ERROR:
return 'E_RECOVERABLE_ERROR';
case \E_DEPRECATED:
return 'E_DEPRECATED';
case \E_USER_DEPRECATED:
return 'E_USER_DEPRECATED';
}
return 'Unknown PHP error';
}
}
@@ -0,0 +1,9 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Formatter;
if (!defined('ABSPATH')) exit;
interface FormatterInterface
{
public function format(array $record);
public function formatBatch(array $records);
}
@@ -0,0 +1,18 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Formatter;
if (!defined('ABSPATH')) exit;
use DateTimeInterface;
use MailPoetVendor\Monolog\LogRecord;
final class GoogleCloudLoggingFormatter extends JsonFormatter
{
public function format(array $record) : string
{
// Re-key level for GCP logging
$record['severity'] = $record['level_name'];
$record['time'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED);
// Remove keys that are not used by GCP
unset($record['level'], $record['level_name'], $record['datetime']);
return parent::format($record);
}
}
@@ -0,0 +1,169 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Formatter;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Utils;
class LineFormatter extends NormalizerFormatter
{
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected $format;
protected $allowInlineLineBreaks;
protected $ignoreEmptyContextAndExtra;
protected $includeStacktraces;
protected $stacktracesParser;
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = \false, bool $ignoreEmptyContextAndExtra = \false, bool $includeStacktraces = \false)
{
$this->format = $format === null ? static::SIMPLE_FORMAT : $format;
$this->allowInlineLineBreaks = $allowInlineLineBreaks;
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
$this->includeStacktraces($includeStacktraces);
parent::__construct($dateFormat);
}
public function includeStacktraces(bool $include = \true, ?callable $parser = null) : self
{
$this->includeStacktraces = $include;
if ($this->includeStacktraces) {
$this->allowInlineLineBreaks = \true;
$this->stacktracesParser = $parser;
}
return $this;
}
public function allowInlineLineBreaks(bool $allow = \true) : self
{
$this->allowInlineLineBreaks = $allow;
return $this;
}
public function ignoreEmptyContextAndExtra(bool $ignore = \true) : self
{
$this->ignoreEmptyContextAndExtra = $ignore;
return $this;
}
public function format(array $record) : string
{
$vars = parent::format($record);
$output = $this->format;
foreach ($vars['extra'] as $var => $val) {
if (\false !== \strpos($output, '%extra.' . $var . '%')) {
$output = \str_replace('%extra.' . $var . '%', $this->stringify($val), $output);
unset($vars['extra'][$var]);
}
}
foreach ($vars['context'] as $var => $val) {
if (\false !== \strpos($output, '%context.' . $var . '%')) {
$output = \str_replace('%context.' . $var . '%', $this->stringify($val), $output);
unset($vars['context'][$var]);
}
}
if ($this->ignoreEmptyContextAndExtra) {
if (empty($vars['context'])) {
unset($vars['context']);
$output = \str_replace('%context%', '', $output);
}
if (empty($vars['extra'])) {
unset($vars['extra']);
$output = \str_replace('%extra%', '', $output);
}
}
foreach ($vars as $var => $val) {
if (\false !== \strpos($output, '%' . $var . '%')) {
$output = \str_replace('%' . $var . '%', $this->stringify($val), $output);
}
}
// remove leftover %extra.xxx% and %context.xxx% if any
if (\false !== \strpos($output, '%')) {
$output = \preg_replace('/%(?:extra|context)\\..+?%/', '', $output);
if (null === $output) {
$pcreErrorCode = \preg_last_error();
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
}
}
return $output;
}
public function formatBatch(array $records) : string
{
$message = '';
foreach ($records as $record) {
$message .= $this->format($record);
}
return $message;
}
public function stringify($value) : string
{
return $this->replaceNewlines($this->convertToString($value));
}
protected function normalizeException(\Throwable $e, int $depth = 0) : string
{
$str = $this->formatException($e);
if ($previous = $e->getPrevious()) {
do {
$depth++;
if ($depth > $this->maxNormalizeDepth) {
$str .= "\n[previous exception] Over " . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
break;
}
$str .= "\n[previous exception] " . $this->formatException($previous);
} while ($previous = $previous->getPrevious());
}
return $str;
}
protected function convertToString($data) : string
{
if (null === $data || \is_bool($data)) {
return \var_export($data, \true);
}
if (\is_scalar($data)) {
return (string) $data;
}
return $this->toJson($data, \true);
}
protected function replaceNewlines(string $str) : string
{
if ($this->allowInlineLineBreaks) {
if (0 === \strpos($str, '{')) {
$str = \preg_replace('/(?<!\\\\)\\\\[rn]/', "\n", $str);
if (null === $str) {
$pcreErrorCode = \preg_last_error();
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
}
}
return $str;
}
return \str_replace(["\r\n", "\r", "\n"], ' ', $str);
}
private function formatException(\Throwable $e) : string
{
$str = '[object] (' . Utils::getClass($e) . '(code: ' . $e->getCode();
if ($e instanceof \SoapFault) {
if (isset($e->faultcode)) {
$str .= ' faultcode: ' . $e->faultcode;
}
if (isset($e->faultactor)) {
$str .= ' faultactor: ' . $e->faultactor;
}
if (isset($e->detail)) {
if (\is_string($e->detail)) {
$str .= ' detail: ' . $e->detail;
} elseif (\is_object($e->detail) || \is_array($e->detail)) {
$str .= ' detail: ' . $this->toJson($e->detail, \true);
}
}
}
$str .= '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')';
if ($this->includeStacktraces) {
$str .= $this->stacktracesParser($e);
}
return $str;
}
private function stacktracesParser(\Throwable $e) : string
{
$trace = $e->getTraceAsString();
if ($this->stacktracesParser) {
$trace = $this->stacktracesParserCustom($trace);
}
return "\n[stacktrace]\n" . $trace . "\n";
}
private function stacktracesParserCustom(string $trace) : string
{
return \implode("\n", \array_filter(\array_map($this->stacktracesParser, \explode("\n", $trace))));
}
}
@@ -0,0 +1,180 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Formatter;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\DateTimeImmutable;
use MailPoetVendor\Monolog\Utils;
use Throwable;
class NormalizerFormatter implements FormatterInterface
{
public const SIMPLE_DATE = "Y-m-d\\TH:i:sP";
protected $dateFormat;
protected $maxNormalizeDepth = 9;
protected $maxNormalizeItemCount = 1000;
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
public function __construct(?string $dateFormat = null)
{
$this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat;
if (!\function_exists('json_encode')) {
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter');
}
}
public function format(array $record)
{
return $this->normalize($record);
}
public function formatBatch(array $records)
{
foreach ($records as $key => $record) {
$records[$key] = $this->format($record);
}
return $records;
}
public function getDateFormat() : string
{
return $this->dateFormat;
}
public function setDateFormat(string $dateFormat) : self
{
$this->dateFormat = $dateFormat;
return $this;
}
public function getMaxNormalizeDepth() : int
{
return $this->maxNormalizeDepth;
}
public function setMaxNormalizeDepth(int $maxNormalizeDepth) : self
{
$this->maxNormalizeDepth = $maxNormalizeDepth;
return $this;
}
public function getMaxNormalizeItemCount() : int
{
return $this->maxNormalizeItemCount;
}
public function setMaxNormalizeItemCount(int $maxNormalizeItemCount) : self
{
$this->maxNormalizeItemCount = $maxNormalizeItemCount;
return $this;
}
public function setJsonPrettyPrint(bool $enable) : self
{
if ($enable) {
$this->jsonEncodeOptions |= \JSON_PRETTY_PRINT;
} else {
$this->jsonEncodeOptions &= ~\JSON_PRETTY_PRINT;
}
return $this;
}
protected function normalize($data, int $depth = 0)
{
if ($depth > $this->maxNormalizeDepth) {
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
}
if (null === $data || \is_scalar($data)) {
if (\is_float($data)) {
if (\is_infinite($data)) {
return ($data > 0 ? '' : '-') . 'INF';
}
if (\is_nan($data)) {
return 'NaN';
}
}
return $data;
}
if (\is_array($data)) {
$normalized = [];
$count = 1;
foreach ($data as $key => $value) {
if ($count++ > $this->maxNormalizeItemCount) {
$normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items (' . \count($data) . ' total), aborting normalization';
break;
}
$normalized[$key] = $this->normalize($value, $depth + 1);
}
return $normalized;
}
if ($data instanceof \DateTimeInterface) {
return $this->formatDate($data);
}
if (\is_object($data)) {
if ($data instanceof Throwable) {
return $this->normalizeException($data, $depth);
}
if ($data instanceof \JsonSerializable) {
$value = $data->jsonSerialize();
} elseif (\get_class($data) === '__PHP_Incomplete_Class') {
$accessor = new \ArrayObject($data);
$value = (string) $accessor['__PHP_Incomplete_Class_Name'];
} elseif (\method_exists($data, '__toString')) {
$value = $data->__toString();
} else {
// the rest is normalized by json encoding and decoding it
$value = \json_decode($this->toJson($data, \true), \true);
}
return [Utils::getClass($data) => $value];
}
if (\is_resource($data)) {
return \sprintf('[resource(%s)]', \get_resource_type($data));
}
return '[unknown(' . \gettype($data) . ')]';
}
protected function normalizeException(Throwable $e, int $depth = 0)
{
if ($depth > $this->maxNormalizeDepth) {
return ['Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'];
}
if ($e instanceof \JsonSerializable) {
return (array) $e->jsonSerialize();
}
$data = ['class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => (int) $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine()];
if ($e instanceof \SoapFault) {
if (isset($e->faultcode)) {
$data['faultcode'] = $e->faultcode;
}
if (isset($e->faultactor)) {
$data['faultactor'] = $e->faultactor;
}
if (isset($e->detail)) {
if (\is_string($e->detail)) {
$data['detail'] = $e->detail;
} elseif (\is_object($e->detail) || \is_array($e->detail)) {
$data['detail'] = $this->toJson($e->detail, \true);
}
}
}
$trace = $e->getTrace();
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data['trace'][] = $frame['file'] . ':' . $frame['line'];
}
}
if ($previous = $e->getPrevious()) {
$data['previous'] = $this->normalizeException($previous, $depth + 1);
}
return $data;
}
protected function toJson($data, bool $ignoreErrors = \false) : string
{
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
}
protected function formatDate(\DateTimeInterface $date)
{
// in case the date format isn't custom then we defer to the custom DateTimeImmutable
// formatting logic, which will pick the right format based on whether useMicroseconds is on
if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof DateTimeImmutable) {
return (string) $date;
}
return $date->format($this->dateFormat);
}
public function addJsonEncodeOption(int $option) : self
{
$this->jsonEncodeOptions |= $option;
return $this;
}
public function removeJsonEncodeOption(int $option) : self
{
$this->jsonEncodeOptions &= ~$option;
return $this;
}
}
@@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
use MailPoetVendor\Monolog\ResettableInterface;
use MailPoetVendor\Psr\Log\LogLevel;
abstract class AbstractHandler extends Handler implements ResettableInterface
{
protected $level = Logger::DEBUG;
protected $bubble = \true;
public function __construct($level = Logger::DEBUG, bool $bubble = \true)
{
$this->setLevel($level);
$this->bubble = $bubble;
}
public function isHandling(array $record) : bool
{
return $record['level'] >= $this->level;
}
public function setLevel($level) : self
{
$this->level = Logger::toMonologLevel($level);
return $this;
}
public function getLevel() : int
{
return $this->level;
}
public function setBubble(bool $bubble) : self
{
$this->bubble = $bubble;
return $this;
}
public function getBubble() : bool
{
return $this->bubble;
}
public function reset()
{
}
}
@@ -0,0 +1,27 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
use ProcessableHandlerTrait;
use FormattableHandlerTrait;
public function handle(array $record) : bool
{
if (!$this->isHandling($record)) {
return \false;
}
if ($this->processors) {
$record = $this->processRecord($record);
}
$record['formatted'] = $this->getFormatter()->format($record);
$this->write($record);
return \false === $this->bubble;
}
protected abstract function write(array $record) : void;
public function reset()
{
parent::reset();
$this->resetProcessors();
}
}
@@ -0,0 +1,41 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use Throwable;
class FallbackGroupHandler extends GroupHandler
{
public function handle(array $record) : bool
{
if ($this->processors) {
$record = $this->processRecord($record);
}
foreach ($this->handlers as $handler) {
try {
$handler->handle($record);
break;
} catch (Throwable $e) {
// What throwable?
}
}
return \false === $this->bubble;
}
public function handleBatch(array $records) : void
{
if ($this->processors) {
$processed = [];
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
$records = $processed;
}
foreach ($this->handlers as $handler) {
try {
$handler->handleBatch($records);
break;
} catch (Throwable $e) {
// What throwable?
}
}
}
}
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Formatter\FormatterInterface;
interface FormattableHandlerInterface
{
public function setFormatter(FormatterInterface $formatter) : HandlerInterface;
public function getFormatter() : FormatterInterface;
}
@@ -0,0 +1,26 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Formatter\FormatterInterface;
use MailPoetVendor\Monolog\Formatter\LineFormatter;
trait FormattableHandlerTrait
{
protected $formatter;
public function setFormatter(FormatterInterface $formatter) : HandlerInterface
{
$this->formatter = $formatter;
return $this;
}
public function getFormatter() : FormatterInterface
{
if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter();
}
return $this->formatter;
}
protected function getDefaultFormatter() : FormatterInterface
{
return new LineFormatter();
}
}
@@ -0,0 +1,36 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
abstract class Handler implements HandlerInterface
{
public function handleBatch(array $records) : void
{
foreach ($records as $record) {
$this->handle($record);
}
}
public function close() : void
{
}
public function __destruct()
{
try {
$this->close();
} catch (\Throwable $e) {
// do nothing
}
}
public function __sleep()
{
$this->close();
$reflClass = new \ReflectionClass($this);
$keys = [];
foreach ($reflClass->getProperties() as $reflProp) {
if (!$reflProp->isStatic()) {
$keys[] = $reflProp->getName();
}
}
return $keys;
}
}
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
interface HandlerInterface
{
public function isHandling(array $record) : bool;
public function handle(array $record) : bool;
public function handleBatch(array $records) : void;
public function close() : void;
}
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
class NoopHandler extends Handler
{
public function isHandling(array $record) : bool
{
return \true;
}
public function handle(array $record) : bool
{
return \false;
}
}
@@ -0,0 +1,61 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
use MailPoetVendor\Monolog\Formatter\FormatterInterface;
class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface
{
private $handler;
private $thresholdMap = [Logger::DEBUG => 0, Logger::INFO => 0, Logger::NOTICE => 0, Logger::WARNING => 0, Logger::ERROR => 0, Logger::CRITICAL => 0, Logger::ALERT => 0, Logger::EMERGENCY => 0];
private $buffer = [];
public function __construct(HandlerInterface $handler, array $thresholdMap = [], $level = Logger::DEBUG, bool $bubble = \true)
{
$this->handler = $handler;
foreach ($thresholdMap as $thresholdLevel => $threshold) {
$this->thresholdMap[$thresholdLevel] = $threshold;
}
parent::__construct($level, $bubble);
}
public function handle(array $record) : bool
{
if ($record['level'] < $this->level) {
return \false;
}
$level = $record['level'];
if (!isset($this->thresholdMap[$level])) {
$this->thresholdMap[$level] = 0;
}
if ($this->thresholdMap[$level] > 0) {
// The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1
$this->thresholdMap[$level]--;
$this->buffer[$level][] = $record;
return \false === $this->bubble;
}
if ($this->thresholdMap[$level] == 0) {
// This current message is breaking the threshold. Flush the buffer and continue handling the current record
foreach ($this->buffer[$level] ?? [] as $buffered) {
$this->handler->handle($buffered);
}
$this->thresholdMap[$level]--;
unset($this->buffer[$level]);
}
$this->handler->handle($record);
return \false === $this->bubble;
}
public function setFormatter(FormatterInterface $formatter) : HandlerInterface
{
if ($this->handler instanceof FormattableHandlerInterface) {
$this->handler->setFormatter($formatter);
return $this;
}
throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.');
}
public function getFormatter() : FormatterInterface
{
if ($this->handler instanceof FormattableHandlerInterface) {
return $this->handler->getFormatter();
}
throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.');
}
}
@@ -0,0 +1,89 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
class ProcessHandler extends AbstractProcessingHandler
{
private $process;
private $command;
private $cwd;
private $pipes = [];
protected const DESCRIPTOR_SPEC = [
0 => ['pipe', 'r'],
// STDIN is a pipe that the child will read from
1 => ['pipe', 'w'],
// STDOUT is a pipe that the child will write to
2 => ['pipe', 'w'],
];
public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = \true, ?string $cwd = null)
{
if ($command === '') {
throw new \InvalidArgumentException('The command argument must be a non-empty string.');
}
if ($cwd === '') {
throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.');
}
parent::__construct($level, $bubble);
$this->command = $command;
$this->cwd = $cwd;
}
protected function write(array $record) : void
{
$this->ensureProcessIsStarted();
$this->writeProcessInput($record['formatted']);
$errors = $this->readProcessErrors();
if (empty($errors) === \false) {
throw new \UnexpectedValueException(\sprintf('Errors while writing to process: %s', $errors));
}
}
private function ensureProcessIsStarted() : void
{
if (\is_resource($this->process) === \false) {
$this->startProcess();
$this->handleStartupErrors();
}
}
private function startProcess() : void
{
$this->process = \proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd);
foreach ($this->pipes as $pipe) {
\stream_set_blocking($pipe, \false);
}
}
private function handleStartupErrors() : void
{
$selected = $this->selectErrorStream();
if (\false === $selected) {
throw new \UnexpectedValueException('Something went wrong while selecting a stream.');
}
$errors = $this->readProcessErrors();
if (\is_resource($this->process) === \false || empty($errors) === \false) {
throw new \UnexpectedValueException(\sprintf('The process "%s" could not be opened: ' . $errors, $this->command));
}
}
protected function selectErrorStream()
{
$empty = [];
$errorPipes = [$this->pipes[2]];
return \stream_select($errorPipes, $empty, $empty, 1);
}
protected function readProcessErrors() : string
{
return (string) \stream_get_contents($this->pipes[2]);
}
protected function writeProcessInput(string $string) : void
{
\fwrite($this->pipes[0], $string);
}
public function close() : void
{
if (\is_resource($this->process)) {
foreach ($this->pipes as $pipe) {
\fclose($pipe);
}
\proc_close($this->process);
$this->process = null;
}
}
}
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Processor\ProcessorInterface;
interface ProcessableHandlerInterface
{
public function pushProcessor(callable $callback) : HandlerInterface;
public function popProcessor() : callable;
}
@@ -0,0 +1,37 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\ResettableInterface;
use MailPoetVendor\Monolog\Processor\ProcessorInterface;
trait ProcessableHandlerTrait
{
protected $processors = [];
public function pushProcessor(callable $callback) : HandlerInterface
{
\array_unshift($this->processors, $callback);
return $this;
}
public function popProcessor() : callable
{
if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.');
}
return \array_shift($this->processors);
}
protected function processRecord(array $record) : array
{
foreach ($this->processors as $processor) {
$record = $processor($record);
}
return $record;
}
protected function resetProcessors() : void
{
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
}
@@ -0,0 +1,61 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
use MailPoetVendor\Monolog\Utils;
use MailPoetVendor\Monolog\Formatter\FormatterInterface;
use MailPoetVendor\Monolog\Formatter\LineFormatter;
use MailPoetVendor\Symfony\Component\Mailer\MailerInterface;
use MailPoetVendor\Symfony\Component\Mailer\Transport\TransportInterface;
use MailPoetVendor\Symfony\Component\Mime\Email;
class SymfonyMailerHandler extends MailHandler
{
protected $mailer;
private $emailTemplate;
public function __construct($mailer, $email, $level = Logger::ERROR, bool $bubble = \true)
{
parent::__construct($level, $bubble);
$this->mailer = $mailer;
$this->emailTemplate = $email;
}
protected function send(string $content, array $records) : void
{
$this->mailer->send($this->buildMessage($content, $records));
}
protected function getSubjectFormatter(?string $format) : FormatterInterface
{
return new LineFormatter($format);
}
protected function buildMessage(string $content, array $records) : Email
{
$message = null;
if ($this->emailTemplate instanceof Email) {
$message = clone $this->emailTemplate;
} elseif (\is_callable($this->emailTemplate)) {
$message = ($this->emailTemplate)($content, $records);
}
if (!$message instanceof Email) {
$record = \reset($records);
throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : ''));
}
if ($records) {
$subjectFormatter = $this->getSubjectFormatter($message->getSubject());
$message->subject($subjectFormatter->format($this->getHighestRecord($records)));
}
if ($this->isHtmlBody($content)) {
if (null !== ($charset = $message->getHtmlCharset())) {
$message->html($content, $charset);
} else {
$message->html($content);
}
} else {
if (null !== ($charset = $message->getTextCharset())) {
$message->text($content, $charset);
} else {
$message->text($content);
}
}
return $message->date(new \DateTimeImmutable());
}
}
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Handler;
if (!defined('ABSPATH')) exit;
trait WebRequestRecognizerTrait
{
protected function isWebRequest() : bool
{
return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI;
}
}
@@ -0,0 +1,9 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use ArrayAccess;
interface LogRecord extends \ArrayAccess
{
public function toArray() : array;
}
@@ -0,0 +1,312 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use DateTimeZone;
use MailPoetVendor\Monolog\Handler\HandlerInterface;
use MailPoetVendor\Psr\Log\LoggerInterface;
use MailPoetVendor\Psr\Log\InvalidArgumentException;
use MailPoetVendor\Psr\Log\LogLevel;
use Throwable;
use Stringable;
class Logger implements LoggerInterface, ResettableInterface
{
public const DEBUG = 100;
public const INFO = 200;
public const NOTICE = 250;
public const WARNING = 300;
public const ERROR = 400;
public const CRITICAL = 500;
public const ALERT = 550;
public const EMERGENCY = 600;
public const API = 2;
protected static $levels = [self::DEBUG => 'DEBUG', self::INFO => 'INFO', self::NOTICE => 'NOTICE', self::WARNING => 'WARNING', self::ERROR => 'ERROR', self::CRITICAL => 'CRITICAL', self::ALERT => 'ALERT', self::EMERGENCY => 'EMERGENCY'];
private const RFC_5424_LEVELS = [7 => self::DEBUG, 6 => self::INFO, 5 => self::NOTICE, 4 => self::WARNING, 3 => self::ERROR, 2 => self::CRITICAL, 1 => self::ALERT, 0 => self::EMERGENCY];
protected $name;
protected $handlers;
protected $processors;
protected $microsecondTimestamps = \true;
protected $timezone;
protected $exceptionHandler;
private $logDepth = 0;
private $fiberLogDepth;
private $detectCycles = \true;
public function __construct(string $name, array $handlers = [], array $processors = [], ?DateTimeZone $timezone = null)
{
$this->name = $name;
$this->setHandlers($handlers);
$this->processors = $processors;
$this->timezone = $timezone ?: new DateTimeZone(\date_default_timezone_get() ?: 'UTC');
if (\PHP_VERSION_ID >= 80100) {
// Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412
$fiberLogDepth = new \WeakMap();
$this->fiberLogDepth = $fiberLogDepth;
}
}
public function getName() : string
{
return $this->name;
}
public function withName(string $name) : self
{
$new = clone $this;
$new->name = $name;
return $new;
}
public function pushHandler(HandlerInterface $handler) : self
{
\array_unshift($this->handlers, $handler);
return $this;
}
public function popHandler() : HandlerInterface
{
if (!$this->handlers) {
throw new \LogicException('You tried to pop from an empty handler stack.');
}
return \array_shift($this->handlers);
}
public function setHandlers(array $handlers) : self
{
$this->handlers = [];
foreach (\array_reverse($handlers) as $handler) {
$this->pushHandler($handler);
}
return $this;
}
public function getHandlers() : array
{
return $this->handlers;
}
public function pushProcessor(callable $callback) : self
{
\array_unshift($this->processors, $callback);
return $this;
}
public function popProcessor() : callable
{
if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.');
}
return \array_shift($this->processors);
}
public function getProcessors() : array
{
return $this->processors;
}
public function useMicrosecondTimestamps(bool $micro) : self
{
$this->microsecondTimestamps = $micro;
return $this;
}
public function useLoggingLoopDetection(bool $detectCycles) : self
{
$this->detectCycles = $detectCycles;
return $this;
}
public function addRecord(int $level, string $message, array $context = [], ?DateTimeImmutable $datetime = null) : bool
{
if (isset(self::RFC_5424_LEVELS[$level])) {
$level = self::RFC_5424_LEVELS[$level];
}
if ($this->detectCycles) {
if (\PHP_VERSION_ID >= 80100 && ($fiber = \Fiber::getCurrent())) {
$this->fiberLogDepth[$fiber] = $this->fiberLogDepth[$fiber] ?? 0;
$logDepth = ++$this->fiberLogDepth[$fiber];
} else {
$logDepth = ++$this->logDepth;
}
} else {
$logDepth = 0;
}
if ($logDepth === 3) {
$this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.');
return \false;
} elseif ($logDepth >= 5) {
// log depth 4 is let through, so we can log the warning above
return \false;
}
try {
$record = null;
foreach ($this->handlers as $handler) {
if (null === $record) {
// skip creating the record as long as no handler is going to handle it
if (!$handler->isHandling(['level' => $level])) {
continue;
}
$levelName = static::getLevelName($level);
$record = ['message' => $message, 'context' => $context, 'level' => $level, 'level_name' => $levelName, 'channel' => $this->name, 'datetime' => $datetime ?? new DateTimeImmutable($this->microsecondTimestamps, $this->timezone), 'extra' => []];
try {
foreach ($this->processors as $processor) {
$record = $processor($record);
}
} catch (Throwable $e) {
$this->handleException($e, $record);
return \true;
}
}
// once the record exists, send it to all handlers as long as the bubbling chain is not interrupted
try {
if (\true === $handler->handle($record)) {
break;
}
} catch (Throwable $e) {
$this->handleException($e, $record);
return \true;
}
}
} finally {
if ($this->detectCycles) {
if (isset($fiber)) {
$this->fiberLogDepth[$fiber]--;
} else {
$this->logDepth--;
}
}
}
return null !== $record;
}
public function close() : void
{
foreach ($this->handlers as $handler) {
$handler->close();
}
}
public function reset() : void
{
foreach ($this->handlers as $handler) {
if ($handler instanceof ResettableInterface) {
$handler->reset();
}
}
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
public static function getLevels() : array
{
return \array_flip(static::$levels);
}
public static function getLevelName(int $level) : string
{
if (!isset(static::$levels[$level])) {
throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . \implode(', ', \array_keys(static::$levels)));
}
return static::$levels[$level];
}
public static function toMonologLevel($level) : int
{
if (\is_string($level)) {
if (\is_numeric($level)) {
return \intval($level);
}
// Contains chars of all log levels and avoids using strtoupper() which may have
// strange results depending on locale (for example, "i" will become "İ" in Turkish locale)
$upper = \strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY');
if (\defined(__CLASS__ . '::' . $upper)) {
return \constant(__CLASS__ . '::' . $upper);
}
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) + static::$levels));
}
return $level;
}
public function isHandling(int $level) : bool
{
$record = ['level' => $level];
foreach ($this->handlers as $handler) {
if ($handler->isHandling($record)) {
return \true;
}
}
return \false;
}
public function setExceptionHandler(?callable $callback) : self
{
$this->exceptionHandler = $callback;
return $this;
}
public function getExceptionHandler() : ?callable
{
return $this->exceptionHandler;
}
public function log($level, $message, array $context = []) : void
{
if (!\is_int($level) && !\is_string($level)) {
throw new \InvalidArgumentException('$level is expected to be a string or int');
}
if (isset(self::RFC_5424_LEVELS[$level])) {
$level = self::RFC_5424_LEVELS[$level];
}
$level = static::toMonologLevel($level);
$this->addRecord($level, (string) $message, $context);
}
public function debug($message, array $context = []) : void
{
$this->addRecord(static::DEBUG, (string) $message, $context);
}
public function info($message, array $context = []) : void
{
$this->addRecord(static::INFO, (string) $message, $context);
}
public function notice($message, array $context = []) : void
{
$this->addRecord(static::NOTICE, (string) $message, $context);
}
public function warning($message, array $context = []) : void
{
$this->addRecord(static::WARNING, (string) $message, $context);
}
public function error($message, array $context = []) : void
{
$this->addRecord(static::ERROR, (string) $message, $context);
}
public function critical($message, array $context = []) : void
{
$this->addRecord(static::CRITICAL, (string) $message, $context);
}
public function alert($message, array $context = []) : void
{
$this->addRecord(static::ALERT, (string) $message, $context);
}
public function emergency($message, array $context = []) : void
{
$this->addRecord(static::EMERGENCY, (string) $message, $context);
}
public function setTimezone(DateTimeZone $tz) : self
{
$this->timezone = $tz;
return $this;
}
public function getTimezone() : DateTimeZone
{
return $this->timezone;
}
protected function handleException(Throwable $e, array $record) : void
{
if (!$this->exceptionHandler) {
throw $e;
}
($this->exceptionHandler)($e, $record);
}
public function __serialize() : array
{
return ['name' => $this->name, 'handlers' => $this->handlers, 'processors' => $this->processors, 'microsecondTimestamps' => $this->microsecondTimestamps, 'timezone' => $this->timezone, 'exceptionHandler' => $this->exceptionHandler, 'logDepth' => $this->logDepth, 'detectCycles' => $this->detectCycles];
}
public function __unserialize(array $data) : void
{
foreach (['name', 'handlers', 'processors', 'microsecondTimestamps', 'timezone', 'exceptionHandler', 'logDepth', 'detectCycles'] as $property) {
if (isset($data[$property])) {
$this->{$property} = $data[$property];
}
}
if (\PHP_VERSION_ID >= 80100) {
// Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412
$fiberLogDepth = new \WeakMap();
$this->fiberLogDepth = $fiberLogDepth;
}
}
}
@@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
class HostnameProcessor implements ProcessorInterface
{
private static $host;
public function __construct()
{
self::$host = (string) \gethostname();
}
public function __invoke(array $record) : array
{
$record['extra']['hostname'] = self::$host;
return $record;
}
}
@@ -0,0 +1,57 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
use MailPoetVendor\Psr\Log\LogLevel;
class IntrospectionProcessor implements ProcessorInterface
{
private $level;
private $skipClassesPartials;
private $skipStackFramesCount;
private $skipFunctions = ['call_user_func', 'call_user_func_array'];
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
{
$this->level = Logger::toMonologLevel($level);
$this->skipClassesPartials = \array_merge(['Monolog\\'], $skipClassesPartials);
$this->skipStackFramesCount = $skipStackFramesCount;
}
public function __invoke(array $record) : array
{
// return if the level is not high enough
if ($record['level'] < $this->level) {
return $record;
}
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
// skip first since it's always the current method
\array_shift($trace);
// the call_user_func call is also skipped
\array_shift($trace);
$i = 0;
while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
if (isset($trace[$i]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (\strpos($trace[$i]['class'], $part) !== \false) {
$i++;
continue 2;
}
}
} elseif (\in_array($trace[$i]['function'], $this->skipFunctions)) {
$i++;
continue;
}
break;
}
$i += $this->skipStackFramesCount;
// we should have the call source now
$record['extra'] = \array_merge($record['extra'], ['file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, 'callType' => isset($trace[$i]['type']) ? $trace[$i]['type'] : null, 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null]);
return $record;
}
private function isTraceClassOrSkippedFunction(array $trace, int $index) : bool
{
if (!isset($trace[$index])) {
return \false;
}
return isset($trace[$index]['class']) || \in_array($trace[$index]['function'], $this->skipFunctions);
}
}
@@ -0,0 +1,26 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
abstract class MemoryProcessor implements ProcessorInterface
{
protected $realUsage;
protected $useFormatting;
public function __construct(bool $realUsage = \true, bool $useFormatting = \true)
{
$this->realUsage = $realUsage;
$this->useFormatting = $useFormatting;
}
protected function formatBytes(int $bytes)
{
if (!$this->useFormatting) {
return $bytes;
}
if ($bytes > 1024 * 1024) {
return \round($bytes / 1024 / 1024, 2) . ' MB';
} elseif ($bytes > 1024) {
return \round($bytes / 1024, 2) . ' KB';
}
return $bytes . ' B';
}
}
@@ -0,0 +1,16 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
class MemoryUsageProcessor extends MemoryProcessor
{
public function __invoke(array $record) : array
{
$usage = \memory_get_usage($this->realUsage);
if ($this->useFormatting) {
$usage = $this->formatBytes($usage);
}
$record['extra']['memory_usage'] = $usage;
return $record;
}
}
@@ -0,0 +1,8 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
interface ProcessorInterface
{
public function __invoke(array $record);
}
@@ -0,0 +1,58 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Processor;
if (!defined('ABSPATH')) exit;
class WebProcessor implements ProcessorInterface
{
protected $serverData;
protected $extraFields = ['url' => 'REQUEST_URI', 'ip' => 'REMOTE_ADDR', 'http_method' => 'REQUEST_METHOD', 'server' => 'SERVER_NAME', 'referrer' => 'HTTP_REFERER', 'user_agent' => 'HTTP_USER_AGENT'];
public function __construct($serverData = null, ?array $extraFields = null)
{
if (null === $serverData) {
$this->serverData =& $_SERVER;
} elseif (\is_array($serverData) || $serverData instanceof \ArrayAccess) {
$this->serverData = $serverData;
} else {
throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.');
}
$defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer'];
if (isset($this->serverData['UNIQUE_ID'])) {
$this->extraFields['unique_id'] = 'UNIQUE_ID';
$defaultEnabled[] = 'unique_id';
}
if (null === $extraFields) {
$extraFields = $defaultEnabled;
}
if (isset($extraFields[0])) {
foreach (\array_keys($this->extraFields) as $fieldName) {
if (!\in_array($fieldName, $extraFields)) {
unset($this->extraFields[$fieldName]);
}
}
} else {
$this->extraFields = $extraFields;
}
}
public function __invoke(array $record) : array
{
// skip processing if for some reason request data
// is not present (CLI or wonky SAPIs)
if (!isset($this->serverData['REQUEST_URI'])) {
return $record;
}
$record['extra'] = $this->appendExtraFields($record['extra']);
return $record;
}
public function addExtraField(string $extraName, string $serverName) : self
{
$this->extraFields[$extraName] = $serverName;
return $this;
}
private function appendExtraFields(array $extra) : array
{
foreach ($this->extraFields as $extraName => $serverName) {
$extra[$extraName] = $this->serverData[$serverName] ?? null;
}
return $extra;
}
}
@@ -0,0 +1,50 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use InvalidArgumentException;
class Registry
{
private static $loggers = [];
public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = \false)
{
$name = $name ?: $logger->getName();
if (isset(self::$loggers[$name]) && !$overwrite) {
throw new InvalidArgumentException('Logger with the given name already exists');
}
self::$loggers[$name] = $logger;
}
public static function hasLogger($logger) : bool
{
if ($logger instanceof Logger) {
$index = \array_search($logger, self::$loggers, \true);
return \false !== $index;
}
return isset(self::$loggers[$logger]);
}
public static function removeLogger($logger) : void
{
if ($logger instanceof Logger) {
if (\false !== ($idx = \array_search($logger, self::$loggers, \true))) {
unset(self::$loggers[$idx]);
}
} else {
unset(self::$loggers[$logger]);
}
}
public static function clear() : void
{
self::$loggers = [];
}
public static function getInstance($name) : Logger
{
if (!isset(self::$loggers[$name])) {
throw new InvalidArgumentException(\sprintf('Requested "%s" logger instance is not in the registry', $name));
}
return self::$loggers[$name];
}
public static function __callStatic($name, $arguments)
{
return self::getInstance($name);
}
}
@@ -0,0 +1,8 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
interface ResettableInterface
{
public function reset();
}
@@ -0,0 +1,71 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Psr\Log\LoggerInterface;
use MailPoetVendor\Psr\Log\LogLevel;
use ReflectionExtension;
class SignalHandler
{
private $logger;
private $previousSignalHandler = [];
private $signalLevelMap = [];
private $signalRestartSyscalls = [];
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
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;
} else {
unset($this->previousSignalHandler[$signo]);
}
$this->signalLevelMap[$signo] = $level;
$this->signalRestartSyscalls[$signo] = $restartSyscalls;
if ($async !== null) {
\pcntl_async_signals($async);
}
\pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls);
return $this;
}
public function handleSignal(int $signo, $siginfo = null) : void
{
static $signals = [];
if (!$signals && \extension_loaded('pcntl')) {
$pcntl = new ReflectionExtension('pcntl');
// HHVM 3.24.2 returns an empty array.
foreach ($pcntl->getConstants() ?: \get_defined_constants(\true)['Core'] as $name => $value) {
if (\substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && \is_int($value)) {
$signals[$value] = $name;
}
}
}
$level = $this->signalLevelMap[$signo] ?? LogLevel::CRITICAL;
$signal = $signals[$signo] ?? $signo;
$context = $siginfo ?? [];
$this->logger->log($level, \sprintf('Program received signal %s', $signal), $context);
if (!isset($this->previousSignalHandler[$signo])) {
return;
}
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')) {
$restartSyscalls = $this->signalRestartSyscalls[$signo] ?? \true;
\pcntl_signal($signo, \SIG_DFL, $restartSyscalls);
\pcntl_sigprocmask(\SIG_UNBLOCK, [$signo], $oldset);
\posix_kill(\posix_getpid(), $signo);
\pcntl_signal_dispatch();
\pcntl_sigprocmask(\SIG_SETMASK, $oldset);
\pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls);
}
} elseif (\is_callable($this->previousSignalHandler[$signo])) {
$this->previousSignalHandler[$signo]($signo, $siginfo);
}
}
}
@@ -0,0 +1,33 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog\Test;
if (!defined('ABSPATH')) exit;
use MailPoetVendor\Monolog\Logger;
use MailPoetVendor\Monolog\DateTimeImmutable;
use MailPoetVendor\Monolog\Formatter\FormatterInterface;
class TestCase extends \MailPoetVendor\PHPUnit\Framework\TestCase
{
public function tearDown() : void
{
parent::tearDown();
if (isset($this->handler)) {
unset($this->handler);
}
}
protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []) : array
{
return ['message' => (string) $message, 'context' => $context, 'level' => $level, 'level_name' => Logger::getLevelName($level), 'channel' => 'test', 'datetime' => new DateTimeImmutable(\true), 'extra' => []];
}
protected function getMultipleRecords() : array
{
return [$this->getRecord(Logger::DEBUG, 'debug message 1'), $this->getRecord(Logger::DEBUG, 'debug message 2'), $this->getRecord(Logger::INFO, 'information'), $this->getRecord(Logger::WARNING, 'warning'), $this->getRecord(Logger::ERROR, 'error')];
}
protected function getIdentityFormatter() : FormatterInterface
{
$formatter = $this->createMock(FormatterInterface::class);
$formatter->expects($this->any())->method('format')->will($this->returnCallback(function ($record) {
return $record['message'];
}));
return $formatter;
}
}
@@ -0,0 +1,167 @@
<?php
declare (strict_types=1);
namespace MailPoetVendor\Monolog;
if (!defined('ABSPATH')) exit;
final class Utils
{
const DEFAULT_JSON_FLAGS = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_PRESERVE_ZERO_FRACTION | \JSON_INVALID_UTF8_SUBSTITUTE | \JSON_PARTIAL_OUTPUT_ON_ERROR;
public static function getClass(object $object) : string
{
$class = \get_class($object);
if (\false === ($pos = \strpos($class, "@anonymous\x00"))) {
return $class;
}
if (\false === ($parent = \get_parent_class($class))) {
return \substr($class, 0, $pos + 10);
}
return $parent . '@anonymous';
}
public static function substr(string $string, int $start, ?int $length = null) : string
{
if (\extension_loaded('mbstring')) {
return \mb_strcut($string, $start, $length);
}
return \substr($string, $start, null === $length ? \strlen($string) : $length);
}
public static function canonicalizePath(string $streamUrl) : string
{
$prefix = '';
if ('file://' === \substr($streamUrl, 0, 7)) {
$streamUrl = \substr($streamUrl, 7);
$prefix = 'file://';
}
// other type of stream, not supported
if (\false !== \strpos($streamUrl, '://')) {
return $streamUrl;
}
// already absolute
if (\substr($streamUrl, 0, 1) === '/' || \substr($streamUrl, 1, 1) === ':' || \substr($streamUrl, 0, 2) === '\\\\') {
return $prefix . $streamUrl;
}
$streamUrl = \getcwd() . '/' . $streamUrl;
return $prefix . $streamUrl;
}
public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = \false) : string
{
if (null === $encodeFlags) {
$encodeFlags = self::DEFAULT_JSON_FLAGS;
}
if ($ignoreErrors) {
$json = @\json_encode($data, $encodeFlags);
if (\false === $json) {
return 'null';
}
return $json;
}
$json = \json_encode($data, $encodeFlags);
if (\false === $json) {
$json = self::handleJsonError(\json_last_error(), $data);
}
return $json;
}
public static function handleJsonError(int $code, $data, ?int $encodeFlags = null) : string
{
if ($code !== \JSON_ERROR_UTF8) {
self::throwEncodeError($code, $data);
}
if (\is_string($data)) {
self::detectAndCleanUtf8($data);
} elseif (\is_array($data)) {
\array_walk_recursive($data, array('Monolog\\Utils', 'detectAndCleanUtf8'));
} else {
self::throwEncodeError($code, $data);
}
if (null === $encodeFlags) {
$encodeFlags = self::DEFAULT_JSON_FLAGS;
}
$json = \json_encode($data, $encodeFlags);
if ($json === \false) {
self::throwEncodeError(\json_last_error(), $data);
}
return $json;
}
public static function pcreLastErrorMessage(int $code) : string
{
if (\PHP_VERSION_ID >= 80000) {
return \preg_last_error_msg();
}
$constants = \get_defined_constants(\true)['pcre'];
$constants = \array_filter($constants, function ($key) {
return \substr($key, -6) == '_ERROR';
}, \ARRAY_FILTER_USE_KEY);
$constants = \array_flip($constants);
return $constants[$code] ?? 'UNDEFINED_ERROR';
}
private static function throwEncodeError(int $code, $data) : void
{
switch ($code) {
case \JSON_ERROR_DEPTH:
$msg = 'Maximum stack depth exceeded';
break;
case \JSON_ERROR_STATE_MISMATCH:
$msg = 'Underflow or the modes mismatch';
break;
case \JSON_ERROR_CTRL_CHAR:
$msg = 'Unexpected control character found';
break;
case \JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$msg = 'Unknown error';
}
throw new \RuntimeException('JSON encoding failed: ' . $msg . '. Encoding: ' . \var_export($data, \true));
}
private static function detectAndCleanUtf8(&$data) : void
{
if (\is_string($data) && !\preg_match('//u', $data)) {
$data = \preg_replace_callback('/[\\x80-\\xFF]+/', function ($m) {
return \function_exists('mb_convert_encoding') ? \mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1') : \utf8_encode($m[0]);
}, $data);
if (!\is_string($data)) {
$pcreErrorCode = \preg_last_error();
throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . self::pcreLastErrorMessage($pcreErrorCode));
}
$data = \str_replace(['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], $data);
}
}
public static function expandIniShorthandBytes($val)
{
if (!\is_string($val)) {
return \false;
}
// support -1
if ((int) $val < 0) {
return (int) $val;
}
if (!\preg_match('/^\\s*(?<val>\\d+)(?:\\.\\d+)?\\s*(?<unit>[gmk]?)\\s*$/i', $val, $match)) {
return \false;
}
$val = (int) $match['val'];
switch (\strtolower($match['unit'] ?? '')) {
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
return $val;
}
public static function getRecordMessageForException(array $record) : string
{
$context = '';
$extra = '';
try {
if ($record['context']) {
$context = "\nContext: " . \json_encode($record['context']);
}
if ($record['extra']) {
$extra = "\nExtra: " . \json_encode($record['extra']);
}
} catch (\Throwable $e) {
// noop
}
return "\nThe exception occurred while attempting to log: " . $record['message'] . $context . $extra;
}
}