Passed
Pull Request — master (#66)
by Witold
03:08
created

MonologServiceFactory   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 306
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 97.44%

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 5
dl 0
loc 306
ccs 114
cts 117
cp 0.9744
rs 8.48
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A createService() 0 6 1
A __invoke() 0 6 1
A createLogger() 0 15 3
B createFormatter() 0 34 8
C createProcessor() 0 59 14
B createInstanceFromArguments() 0 57 8
C createHandler() 0 57 14

How to fix   Complexity   

Complex Class

Complex classes like MonologServiceFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MonologServiceFactory, and based on these observations, apply Extract Interface, too.

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