Passed
Pull Request — master (#57)
by Witold
07:04
created

MonologServiceFactory::createHandler()   C

Complexity

Conditions 13
Paths 29

Size

Total Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 13

Importance

Changes 0
Metric Value
dl 0
loc 49
ccs 25
cts 25
cp 1
rs 6.6166
c 0
b 0
f 0
cc 13
nc 29
nop 3
crap 13

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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