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'); |
|
|
|
|
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
|
|
|
|
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.