Completed
Pull Request — master (#57)
by Witold
05:20
created

MonologServiceFactory::__invoke()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
/**
3
 * @author Evgeny Shpilevsky <[email protected]>
4
 */
5
6
namespace EnliteMonolog\Service;
7
8
use Closure;
9
use Exception;
10
use Interop\Container\ContainerInterface;
11
use Monolog\Handler\FormattableHandlerInterface;
12
use Monolog\Handler\HandlerInterface;
13
use Monolog\Logger;
14
use Monolog\Formatter\FormatterInterface;
15
use RuntimeException;
16
use Zend\ServiceManager\FactoryInterface;
17
use Zend\ServiceManager\ServiceLocatorInterface;
18
19
final class MonologServiceFactory implements FactoryInterface
0 ignored issues
show
Deprecated Code introduced by
The interface Zend\ServiceManager\FactoryInterface has been deprecated with message: Use Zend\ServiceManager\Factory\FactoryInterface instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
20
{
21
22
    /**
23
     * {@inheritdoc}
24
     * @throws \Interop\Container\Exception\ContainerException
25
     * @throws \RuntimeException
26
     * @throws \Interop\Container\Exception\NotFoundException
27
     */
28 2
    public function createService(ServiceLocatorInterface $serviceLocator)
29
    {
30
        /** @var MonologOptions $options */
31 2
        $options = $serviceLocator->get('EnliteMonologOptions');
32 2
        return $this->createLogger($serviceLocator, $options);
33
    }
34
35
    /**
36
     * {@inheritdoc}
37
     * @throws \Interop\Container\Exception\NotFoundException
38
     * @throws \RuntimeException
39
     */
40 1
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
41
    {
42
        /** @var MonologOptions $options */
43 1
        $options = $container->get('EnliteMonologOptions');
44 1
        return $this->createLogger($container, $options);
45
    }
46
47
    /**
48
     * @param ServiceLocatorInterface|ContainerInterface $container
49
     * @param MonologOptions $options
50
     * @throws \Interop\Container\Exception\NotFoundException
51
     * @throws \RuntimeException
52
     * @throws \Interop\Container\Exception\ContainerException
53
     */
54 4
    public function createLogger($container, MonologOptions $options): Logger
55
    {
56 4
        $logger = new Logger($options->getName());
57
58 4
        $handlers = array_reverse($options->getHandlers());
59 4
        foreach ($handlers as $handler) {
60 3
            $logger->pushHandler($this->createHandler($container, $options, $handler));
61
        }
62
63 4
        foreach ($options->getProcessors() as $processor) {
64 1
            $logger->pushProcessor($this->createProcessor($container, $processor));
65
        }
66
67 4
        return $logger;
68
    }
69
70
    /**
71
     * @param ServiceLocatorInterface|ContainerInterface $container
72
     * @param MonologOptions $options
73
     * @param string|array $handler
74
     * @throws \RuntimeException
75
     * @throws \Interop\Container\Exception\NotFoundException
76
     * @throws \Interop\Container\Exception\ContainerException
77
     *
78
     */
79 15
    public function createHandler($container, MonologOptions $options, $handler): HandlerInterface
80
    {
81 15
        if (is_string($handler) && $container->has($handler)) {
82 2
            return $container->get($handler);
83
        }
84
85
86 14
        if (!isset($handler['name'])) {
87 1
            throw new RuntimeException('Cannot create logger handler');
88
        }
89
90 13
        $handlerClassName = $handler['name'];
91
92 13
        if (!class_exists($handlerClassName)) {
93 1
            throw new RuntimeException('Cannot create logger handler (' . $handlerClassName . ')');
94
        }
95
96 12
        $arguments = array_key_exists('args', $handler) ? $handler['args'] : [];
97
98 12
        if (!is_array($arguments)) {
99 1
            throw new RuntimeException('Arguments of handler(' . $handlerClassName . ') must be array');
100
        }
101
102 11
        if (isset($arguments['handler'])) {
103 1
            foreach ($options->getHandlers() as $key => $option) {
104 1
                if ($arguments['handler'] == $key) {
105 1
                    $arguments['handler'] = $this->createHandler($container, $options, $option);
106 1
                    break;
107
                }
108
            }
109
        }
110
111
        try {
112
            /** @var HandlerInterface $instance */
113 11
            $instance = $this->createInstanceFromArguments($handlerClassName, $arguments);
114 1
        } catch (\InvalidArgumentException $exception) {
115 1
            throw new RuntimeException(sprintf(
116 1
                'Handler(%s) has an invalid argument configuration',
117
                $handlerClassName
118 1
            ), 0, $exception);
119
        }
120
121 10
        if (isset($handler['formatter']) && $instance instanceof FormattableHandlerInterface) {
122 1
            $formatter = $this->createFormatter($container, $handler['formatter']);
123 1
            $instance->setFormatter($formatter);
124
        }
125
126 10
        return $instance;
127
    }
128
129
    /**
130
     * @param ServiceLocatorInterface|ContainerInterface $container
131
     * @param string|array $formatter
132
     * @throws \Interop\Container\Exception\NotFoundException
133
     * @throws \Interop\Container\Exception\ContainerException
134
     * @throws RuntimeException
135
     */
136 11
    public function createFormatter($container, $formatter): FormatterInterface
137
    {
138 11
        if (is_string($formatter) && $container->has($formatter)) {
139 1
            return $container->get($formatter);
140
        }
141
142 10
        if (!isset($formatter['name'])) {
143 1
            throw new RuntimeException('Cannot create logger formatter');
144
        }
145
146 9
        $formatterClassName = $formatter['name'];
147
148 9
        if (!class_exists($formatterClassName)) {
149 1
            throw new RuntimeException('Cannot create logger formatter (' . $formatterClassName . ')');
150
        }
151
152 8
        $arguments = array_key_exists('args', $formatter) ? $formatter['args'] : [];
153
154 8
        if (!is_array($arguments)) {
155 1
            throw new RuntimeException('Arguments of formatter(' . $formatterClassName . ') must be array');
156
        }
157
158
        try {
159
            /** @var FormatterInterface $instance */
160 7
            $instance = $this->createInstanceFromArguments($formatterClassName, $arguments);
161 3
        } catch (\InvalidArgumentException $exception) {
162 3
            throw new RuntimeException(sprintf(
163 3
                'Formatter(%s) has an invalid argument configuration',
164
                $formatterClassName
165 3
            ), 0, $exception);
166
        }
167
168 4
        return $instance;
169
    }
170
171
    /**
172
     * @param ServiceLocatorInterface|ContainerInterface $container
173
     * @param $processor
174
     * @throws \Interop\Container\Exception\NotFoundException
175
     * @throws \Interop\Container\Exception\ContainerException
176
     * @throws RuntimeException
177
     */
178 13
    public function createProcessor($container, $processor): callable
179
    {
180 13
        if (is_callable($processor)) {
181 1
            return $processor;
182
        }
183
184 12
        if (is_string($processor)) {
185
            try {
186 5
                $instance = $container->get($processor);
187 3
            } catch (Exception $ex) {
188 3
                $instance = null;
189
            }
190
191 5
            if ($instance && is_callable($instance)) {
192 1
                return $instance;
193
            }
194
195 4
            $processor = new $processor();
196
197 4
            if (is_callable($processor)) {
198 2
                return $processor;
199
            }
200
        }
201
202 9
        if (is_array($processor)) {
203 7
            if (!isset($processor['name'])) {
204 1
                throw new RuntimeException('Cannot create logger processor');
205
            }
206
207 6
            $processorClassName = $processor['name'];
208
209 6
            if (!class_exists($processorClassName)) {
210 1
                throw new RuntimeException('Cannot create logger processor (' . $processorClassName . ')');
211
            }
212
213 5
            $arguments = array_key_exists('args', $processor) ? $processor['args'] : [];
214
215 5
            if (!is_array($arguments)) {
216 1
                throw new RuntimeException('Arguments of processor (' . $processorClassName . ') must be array');
217
            }
218
219
            try {
220 4
                $instance = $this->createInstanceFromArguments($processorClassName, $arguments);
221 1
            } catch (\InvalidArgumentException $exception) {
222 1
                throw new RuntimeException(sprintf(
223 1
                    'Processor(%s) has an invalid argument configuration',
224
                    $processorClassName
225 1
                ), 0, $exception);
226
            }
227
228 3
            if (is_callable($instance)) {
229 3
                return $instance;
230
            }
231
        }
232
233 2
        throw new RuntimeException(
234 2
            'Unknown processor type, must be a Closure, array or the FQCN of an invokable class'
235
        );
236
    }
237
238
    /**
239
     * Handles the constructor arguments and if they're named, just sort them to fit constructor ordering.
240
     *
241
     * @throws \InvalidArgumentException If given arguments are not valid for provided className constructor.
242
     */
243 21
    private function createInstanceFromArguments(string $className, array $arguments): object
244
    {
245 21
        $reflection = new \ReflectionClass($className);
246 21
        $constructor = $reflection->getConstructor();
247
248
        // There is no or at least a non-accessible constructor for provided class name,
249
        // therefore there is no need to handle arguments anyway
250 21
        if ($constructor === null) {
251
            return $reflection->newInstanceArgs($arguments);
252
        }
253
254 21
        if (!$constructor->isPublic()) {
255 1
            throw new \InvalidArgumentException(sprintf(
256 1
                '%s::__construct is not accessible',
257
                $className
258
            ));
259
        }
260
261 20
        $requiredArgsCount = $constructor->getNumberOfRequiredParameters();
262 20
        $argumentCount = count($arguments);
263
264 20
        if ($requiredArgsCount > $argumentCount) {
265 2
            throw new \InvalidArgumentException(sprintf(
266 2
                '%s::__construct() requires at least %d arguments; %d given',
267
                $className,
268
                $requiredArgsCount,
269
                $argumentCount
270
            ));
271
        }
272
273
        // Arguments supposed to be ordered
274 18
        if (isset($arguments[0])) {
275 2
            return $reflection->newInstanceArgs($arguments);
276
        }
277
278 16
        $parameters = [];
279
280 16
        foreach ($constructor->getParameters() as $parameter) {
281 16
            $parameterName = $parameter->getName();
282
283 16
            if (array_key_exists($parameterName, $arguments)) {
284 6
                $parameters[$parameter->getPosition()] = $arguments[$parameterName];
285 6
                continue;
286
            }
287
288 14
            if (!$parameter->isOptional()) {
289 2
                throw new \InvalidArgumentException(sprintf(
290 2
                    'Missing at least one required parameters `%s`',
291
                    $parameterName
292
                ));
293
            }
294
295 12
            $parameters[$parameter->getPosition()] = $parameter->getDefaultValue();
296
        }
297
298 14
        return $reflection->newInstanceArgs($parameters);
299
    }
300
}
301