Completed
Push — master ( c23c9d...3a4e05 )
by Alejandro
03:15
created

processDependenciesFromAnnotations()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 7

Importance

Changes 5
Bugs 1 Features 0
Metric Value
c 5
b 1
f 0
dl 0
loc 48
ccs 30
cts 30
cp 1
rs 6.7272
cc 7
eloc 29
nc 7
nop 2
crap 7
1
<?php
2
namespace Acelaya\ZsmAnnotatedServices\Factory;
3
4
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
5
use Acelaya\ZsmAnnotatedServices\Exception\RuntimeException;
6
use Doctrine\Common\Annotations\AnnotationReader;
7
use Doctrine\Common\Annotations\AnnotationRegistry;
8
use Doctrine\Common\Annotations\CachedReader;
9
use Doctrine\Common\Annotations\Reader;
10
use Doctrine\Common\Cache\Cache;
11
use Zend\ServiceManager\ServiceLocatorInterface;
12
13
abstract class AbstractAnnotatedFactory
14
{
15
    const CACHE_SERVICE = 'Acelaya\ZsmAnnotatedServices\Cache';
16
17
    /**
18
     * @var Reader
19
     */
20
    private static $annotationReader;
21
22 8
    protected function processDependenciesFromAnnotations(ServiceLocatorInterface $container, $serviceName)
23
    {
24 8
        if (! class_exists($serviceName)) {
25 1
            throw new RuntimeException(sprintf(
26
                'Annotated factories can only be used with services that are identified by their FQCN. ' .
27 1
                'Provided "%s" service name is not a valid class.',
28
                $serviceName
29 1
            ));
30
        }
31
32 7
        $annotationReader = $this->createAnnotationReader($container);
33 7
        $refClass = new \ReflectionClass($serviceName);
34 7
        $constructor = $refClass->getConstructor();
35 7
        if (! isset($constructor)) {
36 1
            return new $serviceName();
37
        }
38
39
        /** @var Inject $inject */
40 6
        $inject = $annotationReader->getMethodAnnotation($constructor, Inject::class);
41 6
        if (! isset($inject)) {
42 1
            throw new RuntimeException(sprintf(
43 1
                'You need to use the "%s" annotation in "%s" constructor so that the "%s" can create it.',
44 1
                Inject::class,
45 1
                $serviceName,
46 1
                static::class
47 1
            ));
48
        }
49
50 5
        $services = [];
51 5
        foreach ($inject->getServices() as $serviceKey) {
52 5
            $parts = explode('.', $serviceKey);
53 5
            $serviceKey = array_shift($parts);
54
55 5
            if (! $container->has($serviceKey)) {
56 1
                throw new RuntimeException(sprintf(
57 1
                    'Defined injectable service "%s" could not be found in container.',
58
                    $serviceKey
59 1
                ));
60
            }
61
62 4
            $service = $container->get($serviceKey);
63 4
            $services[] = empty($parts) ? $service : $this->readKeysFromArray($parts, $service);
64 4
        }
65
66
        // TODO use array unpacking instead of reflection when dropping PHP 5.5 support
67
        // return new $serviceName(...$services);
68 4
        return $refClass->newInstanceArgs($services);
69
    }
70
71
    /**
72
     * @param ServiceLocatorInterface $container
73
     * @return AnnotationReader|CachedReader
74
     */
75 7
    private function createAnnotationReader(ServiceLocatorInterface $container)
76
    {
77 7
        if (isset(self::$annotationReader)) {
78 5
            return self::$annotationReader;
79
        }
80
81 2
        AnnotationRegistry::registerLoader(function ($class) {
82 2
            $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
83 2
            $file = realpath(__DIR__ . '/../Annotation/' . basename($file));
84 2
            if (! $file) {
85 2
                return false;
86
            }
87
88 1
            require_once $file;
89 1
            return true;
90 2
        });
91
92 2
        if (! $container->has(self::CACHE_SERVICE)) {
93 1
            return self::$annotationReader = new AnnotationReader();
94
        } else {
95
            /** @var Cache $cache */
96 1
            $cache = $container->get(self::CACHE_SERVICE);
97 1
            return self::$annotationReader = new CachedReader(new AnnotationReader(), $cache);
98
        }
99
    }
100
101
    /**
102
     * @param array $keys
103
     * @param array $array
104
     * @return mixed|null
105
     */
106 4
    private function readKeysFromArray(array $keys, array $array)
107
    {
108 4
        $key = array_shift($keys);
109 4
        $value = isset($array[$key]) ? $array[$key] : null;
110 4
        if (! empty($keys) && is_array($value)) {
111 4
            $value = $this->readKeysFromArray($keys, $value);
112 4
        }
113
114 4
        return $value;
115
    }
116
}
117