AbstractAnnotatedFactory::readKeysFromArray()   B
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 11
cts 11
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 10
nc 3
nop 2
crap 5
1
<?php
2
namespace Acelaya\ZsmAnnotatedServices\Factory;
3
4
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
5
use Acelaya\ZsmAnnotatedServices\Exception;
6
use Acelaya\ZsmAnnotatedServices\Exception\InvalidArgumentException;
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\AnnotationRegistry;
9
use Doctrine\Common\Annotations\CachedReader;
10
use Doctrine\Common\Annotations\Reader;
11
use Doctrine\Common\Cache\Cache;
12
use Interop\Container\Exception\ContainerException;
13
use Interop\Container\Exception\NotFoundException;
14
use Zend\ServiceManager\ServiceLocatorInterface;
15
16
abstract class AbstractAnnotatedFactory
17
{
18
    const CACHE_SERVICE = 'Acelaya\ZsmAnnotatedServices\Cache';
19
20
    /**
21
     * @var Reader
22
     */
23
    private static $annotationReader;
24
25 10
    protected function processDependenciesFromAnnotations(ServiceLocatorInterface $container, $serviceName)
26
    {
27 10
        if (! class_exists($serviceName)) {
28 1
            throw new Exception\RuntimeException(sprintf(
29
                'Annotated factories can only be used with services that are identified by their FQCN. ' .
30 1
                'Provided "%s" service name is not a valid class.',
31
                $serviceName
32 1
            ));
33
        }
34
35 9
        $annotationReader = $this->createAnnotationReader($container);
36 9
        $refClass = new \ReflectionClass($serviceName);
37 9
        $constructor = $refClass->getConstructor();
38 9
        if ($constructor === null) {
39 1
            return new $serviceName();
40
        }
41
42
        /** @var Inject $inject */
43 8
        $inject = $annotationReader->getMethodAnnotation($constructor, Inject::class);
44 8
        if ($inject === null) {
45 1
            throw new Exception\RuntimeException(sprintf(
46 1
                'You need to use the "%s" annotation in "%s" constructor so that the "%s" can create it.',
47 1
                Inject::class,
48 1
                $serviceName,
49 1
                static::class
50 1
            ));
51
        }
52
53 7
        $services = [];
54 7
        foreach ($inject->getServices() as $serviceKey) {
55 7
            $parts = explode('.', $serviceKey);
56
57
            // Even when dots are found, try to find a service with the full name
58
            // If it is not found, then assume dots are used to get part of an array service
59 7
            if (count($parts) > 1 && ! $container->has($serviceKey)) {
60 6
                $serviceKey = array_shift($parts);
61 6
            } else {
62 6
                $parts = [];
63
            }
64
65 7
            if (! $container->has($serviceKey)) {
66 1
                throw new Exception\RuntimeException(sprintf(
67 1
                    'Defined injectable service "%s" could not be found in container.',
68
                    $serviceKey
69 1
                ));
70
            }
71
72 6
            $service = $container->get($serviceKey);
73 6
            $services[] = empty($parts) ? $service : $this->readKeysFromArray($parts, $service);
74 5
        }
75
76 5
        return new $serviceName(...$services);
77
    }
78
79
    /**
80
     * @param ServiceLocatorInterface $container
81
     * @return Reader
82
     * @throws NotFoundException
83
     * @throws ContainerException
84
     * @throws \InvalidArgumentException
85
     */
86 9
    private function createAnnotationReader(ServiceLocatorInterface $container)
87
    {
88 9
        if (self::$annotationReader !== null) {
89 7
            return self::$annotationReader;
90
        }
91
92 2
        AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\Common\Annotati...istry::registerLoader() has been deprecated with message: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists')

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
93
94 2
        if (! $container->has(self::CACHE_SERVICE)) {
95 1
            return self::$annotationReader = new AnnotationReader();
96
        } else {
97
            /** @var Cache $cache */
98 1
            $cache = $container->get(self::CACHE_SERVICE);
99 1
            return self::$annotationReader = new CachedReader(new AnnotationReader(), $cache);
100
        }
101
    }
102
103
    /**
104
     * @param array $keys
105
     * @param array|\ArrayAccess $array
106
     * @return mixed|null
107
     * @throws InvalidArgumentException
108
     */
109 6
    private function readKeysFromArray(array $keys, $array)
110
    {
111 6
        $key = array_shift($keys);
112
113
        // When one of the provided keys is not found, thorw an exception
114 6
        if (! isset($array[$key])) {
115 1
            throw new InvalidArgumentException(sprintf(
116 1
                'The key "%s" provided in the dotted notation could not be found in the array service',
117
                $key
118 1
            ));
119
        }
120
121 6
        $value = $array[$key];
122 6
        if (! empty($keys) && (is_array($value) || $value instanceof \ArrayAccess)) {
123 6
            $value = $this->readKeysFromArray($keys, $value);
124 5
        }
125
126 5
        return $value;
127
    }
128
}
129