1<?php declare(strict_types=1);
2
3/*
4 * This file is part of the Monolog package.
5 *
6 * (c) Jordi Boggiano <j.boggiano@seld.be>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Monolog\Handler;
13
14use Monolog\Formatter\FormatterInterface;
15use Monolog\ResettableInterface;
16
17/**
18 * Forwards records to multiple handlers
19 *
20 * @author Lenar Lõhmus <lenar@city.ee>
21 *
22 * @phpstan-import-type Record from \Monolog\Logger
23 */
24class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
25{
26    use ProcessableHandlerTrait;
27
28    /** @var HandlerInterface[] */
29    protected $handlers;
30    /** @var bool */
31    protected $bubble;
32
33    /**
34     * @param HandlerInterface[] $handlers Array of Handlers.
35     * @param bool               $bubble   Whether the messages that are handled can bubble up the stack or not
36     */
37    public function __construct(array $handlers, bool $bubble = true)
38    {
39        foreach ($handlers as $handler) {
40            if (!$handler instanceof HandlerInterface) {
41                throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.');
42            }
43        }
44
45        $this->handlers = $handlers;
46        $this->bubble = $bubble;
47    }
48
49    /**
50     * {@inheritDoc}
51     */
52    public function isHandling(array $record): bool
53    {
54        foreach ($this->handlers as $handler) {
55            if ($handler->isHandling($record)) {
56                return true;
57            }
58        }
59
60        return false;
61    }
62
63    /**
64     * {@inheritDoc}
65     */
66    public function handle(array $record): bool
67    {
68        if ($this->processors) {
69            /** @var Record $record */
70            $record = $this->processRecord($record);
71        }
72
73        foreach ($this->handlers as $handler) {
74            $handler->handle($record);
75        }
76
77        return false === $this->bubble;
78    }
79
80    /**
81     * {@inheritDoc}
82     */
83    public function handleBatch(array $records): void
84    {
85        if ($this->processors) {
86            $processed = [];
87            foreach ($records as $record) {
88                $processed[] = $this->processRecord($record);
89            }
90            /** @var Record[] $records */
91            $records = $processed;
92        }
93
94        foreach ($this->handlers as $handler) {
95            $handler->handleBatch($records);
96        }
97    }
98
99    public function reset()
100    {
101        $this->resetProcessors();
102
103        foreach ($this->handlers as $handler) {
104            if ($handler instanceof ResettableInterface) {
105                $handler->reset();
106            }
107        }
108    }
109
110    public function close(): void
111    {
112        parent::close();
113
114        foreach ($this->handlers as $handler) {
115            $handler->close();
116        }
117    }
118
119    /**
120     * {@inheritDoc}
121     */
122    public function setFormatter(FormatterInterface $formatter): HandlerInterface
123    {
124        foreach ($this->handlers as $handler) {
125            if ($handler instanceof FormattableHandlerInterface) {
126                $handler->setFormatter($formatter);
127            }
128        }
129
130        return $this;
131    }
132}
133