From 626bde615905ba99ccbe8a3d0da558434fc28aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 6 Sep 2019 14:55:25 +0200 Subject: [PATCH 1/5] Fixed forward compatibilty layer --- src/Monolog/Handler/FormattableHandlerInterface.php | 3 ++- src/Monolog/Handler/FormattableHandlerTrait.php | 3 ++- src/Monolog/Handler/ProcessableHandlerInterface.php | 5 +++-- src/Monolog/Handler/ProcessableHandlerTrait.php | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Monolog/Handler/FormattableHandlerInterface.php b/src/Monolog/Handler/FormattableHandlerInterface.php index b6d4c6ac..3e2f1b28 100644 --- a/src/Monolog/Handler/FormattableHandlerInterface.php +++ b/src/Monolog/Handler/FormattableHandlerInterface.php @@ -16,7 +16,8 @@ use Monolog\Formatter\FormatterInterface; /** * Interface to describe loggers that have a formatter * - * @internal This interface is present in monolog 1.x to ease forward compatibility. + * This interface is present in monolog 1.x to ease forward compatibility. + * * @author Jordi Boggiano */ interface FormattableHandlerInterface diff --git a/src/Monolog/Handler/FormattableHandlerTrait.php b/src/Monolog/Handler/FormattableHandlerTrait.php index 5233bf55..e9ec5e77 100644 --- a/src/Monolog/Handler/FormattableHandlerTrait.php +++ b/src/Monolog/Handler/FormattableHandlerTrait.php @@ -17,7 +17,8 @@ use Monolog\Formatter\LineFormatter; /** * Helper trait for implementing FormattableInterface * - * @internal This interface is present in monolog 1.x to ease forward compatibility. + * This trait is present in monolog 1.x to ease forward compatibility. + * * @author Jordi Boggiano */ trait FormattableHandlerTrait diff --git a/src/Monolog/Handler/ProcessableHandlerInterface.php b/src/Monolog/Handler/ProcessableHandlerInterface.php index bfa71497..66a3d83a 100644 --- a/src/Monolog/Handler/ProcessableHandlerInterface.php +++ b/src/Monolog/Handler/ProcessableHandlerInterface.php @@ -16,7 +16,8 @@ use Monolog\Processor\ProcessorInterface; /** * Interface to describe loggers that have processors * - * @internal This interface is present in monolog 1.x to ease forward compatibility. + * This interface is present in monolog 1.x to ease forward compatibility. + * * @author Jordi Boggiano */ interface ProcessableHandlerInterface @@ -27,7 +28,7 @@ interface ProcessableHandlerInterface * @param ProcessorInterface|callable $callback * @return HandlerInterface self */ - public function pushProcessor(callable $callback): HandlerInterface; + public function pushProcessor($callback): HandlerInterface; /** * Removes the processor on top of the stack and returns it. diff --git a/src/Monolog/Handler/ProcessableHandlerTrait.php b/src/Monolog/Handler/ProcessableHandlerTrait.php index a5092929..09f32a12 100644 --- a/src/Monolog/Handler/ProcessableHandlerTrait.php +++ b/src/Monolog/Handler/ProcessableHandlerTrait.php @@ -16,7 +16,8 @@ use Monolog\ResettableInterface; /** * Helper trait for implementing ProcessableInterface * - * @internal This interface is present in monolog 1.x to ease forward compatibility. + * This trait is present in monolog 1.x to ease forward compatibility. + * * @author Jordi Boggiano */ trait ProcessableHandlerTrait @@ -30,7 +31,7 @@ trait ProcessableHandlerTrait * {@inheritdoc} * @suppress PhanTypeMismatchReturn */ - public function pushProcessor(callable $callback): HandlerInterface + public function pushProcessor($callback): HandlerInterface { array_unshift($this->processors, $callback); From 70e65a5470a42cfec1a7da00d30edb6e617e8dcf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 6 Sep 2019 15:49:17 +0200 Subject: [PATCH 2/5] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1673dc8f..9b0f5280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 1.25.1 (2019-09-06) + + * Fixed forward-compatible interfaces to be compatible with Monolog 1.x too. + ### 1.25.0 (2019-09-06) * Deprecated SlackbotHandler, use SlackWebhookHandler or SlackHandler instead From c404cb324026cfb1956acf0351f9fa3e82c5f7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Pr=C3=A9vot?= Date: Sat, 7 Sep 2019 21:23:23 -1000 Subject: [PATCH 3/5] Fix file permission --- src/Monolog/Handler/Slack/SlackRecord.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/Monolog/Handler/Slack/SlackRecord.php diff --git a/src/Monolog/Handler/Slack/SlackRecord.php b/src/Monolog/Handler/Slack/SlackRecord.php old mode 100755 new mode 100644 From b271cd4294a2fb19f18d9f0fd0284f224b3e9d28 Mon Sep 17 00:00:00 2001 From: Garrick Lam Date: Thu, 3 Oct 2019 13:02:02 +0800 Subject: [PATCH 4/5] Don't even try to attempt normalizing iterators or generators in context Iterators and Generators may not be rewindable, so foreach is not safe to use on them. Iterators and especially Generators may trigger irreversible actions on calling next(), so iterating over all values can potentially cause harm, e.g. imagine an iterator over a set of HTTP POST requests that are sent when the next value is requested . The only sufficiently safe thing to iterate and include here are primitive arrays. --- src/Monolog/Formatter/JsonFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Monolog/Formatter/JsonFormatter.php b/src/Monolog/Formatter/JsonFormatter.php index 2ff119ea..8d10b825 100644 --- a/src/Monolog/Formatter/JsonFormatter.php +++ b/src/Monolog/Formatter/JsonFormatter.php @@ -145,7 +145,7 @@ class JsonFormatter extends NormalizerFormatter return 'Over 9 levels deep, aborting normalization'; } - if (is_array($data) || $data instanceof \Traversable) { + if (is_array($data)) { $normalized = array(); $count = 1; From 65f1f304d4e68759970a5c663ad2ae46faa47013 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 11 Nov 2019 18:21:38 +0100 Subject: [PATCH 5/5] Add forwarding of formatter functions to nested handlers in Sampling, Filter and FingersCrossed handlers, and fix handling of callable factory handler config, fixes #1386, closes #1387 --- src/Monolog/Handler/FilterHandler.php | 52 +++++++++++++---- src/Monolog/Handler/FingersCrossedHandler.php | 58 ++++++++++++++----- src/Monolog/Handler/SamplingHandler.php | 53 +++++++++++++---- 3 files changed, 127 insertions(+), 36 deletions(-) diff --git a/src/Monolog/Handler/FilterHandler.php b/src/Monolog/Handler/FilterHandler.php index 938c1a7e..11ede52e 100644 --- a/src/Monolog/Handler/FilterHandler.php +++ b/src/Monolog/Handler/FilterHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; /** * Simple handler wrapper that filters records based on a list of levels @@ -45,7 +46,7 @@ class FilterHandler extends AbstractHandler protected $bubble; /** - * @param callable|HandlerInterface $handler Handler or factory callable($record, $this). + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param bool $bubble Whether the messages that are handled can bubble up the stack or not @@ -104,21 +105,13 @@ class FilterHandler extends AbstractHandler return false; } - // The same logic as in FingersCrossedHandler - if (!$this->handler instanceof HandlerInterface) { - $this->handler = call_user_func($this->handler, $record, $this); - if (!$this->handler instanceof HandlerInterface) { - throw new \RuntimeException("The factory callable should return a HandlerInterface"); - } - } - if ($this->processors) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } } - $this->handler->handle($record); + $this->getHandler($record)->handle($record); return false === $this->bubble; } @@ -135,6 +128,43 @@ class FilterHandler extends AbstractHandler } } - $this->handler->handleBatch($filtered); + $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered); + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->getHandler()->setFormatter($formatter); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->getHandler()->getFormatter(); } } diff --git a/src/Monolog/Handler/FingersCrossedHandler.php b/src/Monolog/Handler/FingersCrossedHandler.php index 275fd513..cdabc445 100644 --- a/src/Monolog/Handler/FingersCrossedHandler.php +++ b/src/Monolog/Handler/FingersCrossedHandler.php @@ -15,6 +15,7 @@ use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Logger; use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; /** * Buffers all records until a certain level is reached @@ -39,7 +40,7 @@ class FingersCrossedHandler extends AbstractHandler protected $passthruLevel; /** - * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param bool $bubble Whether the messages that are handled can bubble up the stack or not @@ -88,15 +89,7 @@ class FingersCrossedHandler extends AbstractHandler if ($this->stopBuffering) { $this->buffering = false; } - if (!$this->handler instanceof HandlerInterface) { - $record = end($this->buffer) ?: null; - - $this->handler = call_user_func($this->handler, $record, $this); - if (!$this->handler instanceof HandlerInterface) { - throw new \RuntimeException("The factory callable should return a HandlerInterface"); - } - } - $this->handler->handleBatch($this->buffer); + $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); $this->buffer = array(); } @@ -120,7 +113,7 @@ class FingersCrossedHandler extends AbstractHandler $this->activate(); } } else { - $this->handler->handle($record); + $this->getHandler($record)->handle($record); } return false === $this->bubble; @@ -140,8 +133,8 @@ class FingersCrossedHandler extends AbstractHandler parent::reset(); - if ($this->handler instanceof ResettableInterface) { - $this->handler->reset(); + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); } } @@ -167,11 +160,48 @@ class FingersCrossedHandler extends AbstractHandler return $record['level'] >= $level; }); if (count($this->buffer) > 0) { - $this->handler->handleBatch($this->buffer); + $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); } } $this->buffer = array(); $this->buffering = true; } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->getHandler()->setFormatter($formatter); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->getHandler()->getFormatter(); + } } diff --git a/src/Monolog/Handler/SamplingHandler.php b/src/Monolog/Handler/SamplingHandler.php index 9509ae37..b547ed7d 100644 --- a/src/Monolog/Handler/SamplingHandler.php +++ b/src/Monolog/Handler/SamplingHandler.php @@ -11,6 +11,8 @@ namespace Monolog\Handler; +use Monolog\Formatter\FormatterInterface; + /** * Sampling handler * @@ -38,7 +40,7 @@ class SamplingHandler extends AbstractHandler protected $factor; /** - * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). * @param int $factor Sample factor */ public function __construct($handler, $factor) @@ -54,29 +56,58 @@ class SamplingHandler extends AbstractHandler public function isHandling(array $record) { - return $this->handler->isHandling($record); + return $this->getHandler($record)->isHandling($record); } public function handle(array $record) { if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { - // The same logic as in FingersCrossedHandler - if (!$this->handler instanceof HandlerInterface) { - $this->handler = call_user_func($this->handler, $record, $this); - if (!$this->handler instanceof HandlerInterface) { - throw new \RuntimeException("The factory callable should return a HandlerInterface"); - } - } - if ($this->processors) { foreach ($this->processors as $processor) { $record = call_user_func($processor, $record); } } - $this->handler->handle($record); + $this->getHandler($record)->handle($record); } return false === $this->bubble; } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->getHandler()->setFormatter($formatter); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->getHandler()->getFormatter(); + } }