Passed
Pull Request — master (#53)
by Agaletskiy
02:23
created

MonologServiceFactory   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 96.4%

Importance

Changes 0
Metric Value
wmc 47
lcom 1
cbo 5
dl 0
loc 289
ccs 107
cts 111
cp 0.964
rs 8.439
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
C createHandler() 0 49 12
C createFormatter() 0 34 8
C createProcessor() 0 58 14
B createInstanceFromArguments() 0 57 8

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