Passed
Pull Request — master (#48)
by Timothy
01:40
created

MonologServiceFactory::createLogger()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 15
ccs 0
cts 8
cp 0
rs 9.4285
c 1
b 0
f 0
cc 3
eloc 8
nc 4
nop 2
crap 12
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
    public function createService(ServiceLocatorInterface $serviceLocator)
28
    {
29
        /** @var MonologOptions $options */
30
        $options = $serviceLocator->get('EnliteMonologOptions');
31
        return $this->createLogger($serviceLocator, $options);
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     * @throws \Interop\Container\Exception\NotFoundException
37
     * @throws \RuntimeException
38
     */
39
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
40
    {
41
        /** @var MonologOptions $options */
42
        $options = $container->get('EnliteMonologOptions');
43
        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
    public function createLogger($container, MonologOptions $options)
55
    {
56
        $logger = new Logger($options->getName());
57
58
        $handlers = array_reverse($options->getHandlers());
59
        foreach ($handlers as $handler) {
60
            $logger->pushHandler($this->createHandler($container, $options, $handler));
61
        }
62
63
        foreach ($options->getProcessors() as $processor) {
64
            $logger->pushProcessor($this->createProcessor($container, $processor));
65
        }
66
67
        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 4
    public function createHandler($container, MonologOptions $options, $handler)
81
    {
82 4
        if (is_string($handler) && $container->has($handler)) {
83
            return $container->get($handler);
84
        }
85
86
87 4
        if (!isset($handler['name'])) {
88
            throw new RuntimeException('Cannot create logger handler');
89
        }
90
91 4
        $handlerClassName = $handler['name'];
92
93 4
        if (!class_exists($handlerClassName)) {
94
            throw new RuntimeException('Cannot create logger handler (' . $handlerClassName . ')');
95
        }
96
97 4
        $arguments = array_key_exists('args', $handler) ? $handler['args'] : array();
98
99 4
        if (!is_array($arguments)) {
100
            throw new RuntimeException('Arguments of handler(' . $handlerClassName . ') must be array');
101
        }
102
103 4
        if (isset($arguments['handler'])) {
104
            foreach ($options->getHandlers() as $key => $option) {
105
                if ($arguments['handler'] == $key) {
106
                    $arguments['handler'] = $this->createHandler($container, $options, $option);
107
                    break;
108
                }
109
            }
110
        }
111
112
        try {
113
            /** @var HandlerInterface $instance */
114 4
            $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 3
        if (isset($handler['formatter'])) {
123 1
            $formatter = $this->createFormatter($container, $handler['formatter']);
124 1
            $instance->setFormatter($formatter);
125
        }
126
127 3
        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($formatter['name'])) {
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 5
    public function createProcessor($container, $processor)
182
    {
183 5
        if ($processor instanceof Closure) {
184 1
            return $processor;
185
        }
186
187 4
        if (is_string($processor)) {
188
            try {
189 4
                $instance = $container->get($processor);
190 2
            } catch (Exception $ex) {
191 2
                $instance = null;
192
            }
193
194 4
            if ($instance && is_callable($instance)) {
195 1
                return $instance;
196
            }
197
198 3
            $processor = new $processor();
199
200 3
            if (is_callable($processor)) {
201 1
                return $processor;
202
            }
203
        }
204
205 2
        throw new RuntimeException('Unknown processor type, must be a Closure or the FQCN of an invokable class');
206
    }
207
208
    /**
209
     * Handles the constructor arguments and if they're named, just sort them to fit constructor ordering.
210
     *
211
     * @param string $className
212
     * @param array  $arguments
213
     *
214
     * @return object
215
     * @throws \InvalidArgumentException If given arguments are not valid for provided className constructor.
216
     */
217 11
    private function createInstanceFromArguments($className, array $arguments)
218
    {
219 11
        $reflection = new \ReflectionClass($className);
220 11
        $constructor = $reflection->getConstructor();
221
222
        // There is no or at least a non-accessible constructor for provided class name,
223
        // therefore there is no need to handle arguments anyway
224 11
        if ($constructor === null) {
225 1
            return $reflection->newInstanceArgs($arguments);
226
        }
227
228 10
        if (!$constructor->isPublic()) {
229 1
            throw new \InvalidArgumentException(sprintf(
230 1
                '%s::__construct is not accessible',
231
                $className
232
            ));
233
        }
234
235 9
        $requiredArgsCount = $constructor->getNumberOfRequiredParameters();
236 9
        $argumentCount = count($arguments);
237
238 9
        if ($requiredArgsCount > $argumentCount) {
239 2
            throw new \InvalidArgumentException(sprintf(
240 2
                '%s::__construct() requires at least %d arguments; %d given',
241
                $className,
242
                $requiredArgsCount,
243
                $argumentCount
244
            ));
245
        }
246
247
        // Arguments supposed to be ordered
248 7
        if (isset($arguments[0])) {
249
            return $reflection->newInstanceArgs($arguments);
250
        }
251
252 7
        $parameters = array();
253
254 7
        foreach ($constructor->getParameters() as $parameter) {
255 7
            $parameterName = $parameter->getName();
256
257 7
            if (array_key_exists($parameterName, $arguments)) {
258 2
                $parameters[$parameter->getPosition()] = $arguments[$parameterName];
259 2
                continue;
260
            }
261
262 7
            if (!$parameter->isOptional()) {
263 1
                throw new \InvalidArgumentException(sprintf(
264 1
                    'Missing at least one required parameters `%s`',
265
                    $parameterName
266
                ));
267
            }
268
269 6
            $parameters[$parameter->getPosition()] = $parameter->getDefaultValue();
270
        }
271
272 6
        return $reflection->newInstanceArgs($parameters);
273
    }
274
}
275