1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* File was created 06.10.2015 06:22 |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace PeekAndPoke\Component\Slumber\Core\LookUp; |
7
|
|
|
|
8
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
9
|
|
|
use Doctrine\Common\Annotations\Reader; |
10
|
|
|
use PeekAndPoke\Component\Creator\Creator; |
11
|
|
|
use PeekAndPoke\Component\Creator\CreatorFactory; |
12
|
|
|
use PeekAndPoke\Component\Creator\CreatorFactoryImpl; |
13
|
|
|
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactory; |
14
|
|
|
use PeekAndPoke\Component\PropertyAccess\PropertyAccessFactoryImpl; |
15
|
|
|
use PeekAndPoke\Component\Psi\Psi; |
16
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\ClassCreatorMarker; |
17
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\ClassMarker; |
18
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\PropertyMappingMarker; |
19
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\PropertyMarker; |
20
|
|
|
use PeekAndPoke\Component\Slumber\Core\Exception\SlumberException; |
21
|
|
|
use PeekAndPoke\Component\Slumber\Core\Validation\ClassAnnotationValidationContext; |
22
|
|
|
use PeekAndPoke\Component\Slumber\Core\Validation\PropertyAnnotationValidationContext; |
23
|
|
|
use Psr\Container\ContainerInterface; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @author Karsten J. Gerber <[email protected]> |
27
|
|
|
*/ |
28
|
|
|
class AnnotatedEntityConfigReader implements EntityConfigReader |
29
|
|
|
{ |
30
|
|
|
/** @var Reader */ |
31
|
|
|
private $annotationReader; |
32
|
|
|
/** @var ContainerInterface */ |
33
|
|
|
private $serviceProvider; |
34
|
|
|
/** @var PropertyMarker2Mapper */ |
35
|
|
|
private $mappings; |
36
|
|
|
/** @var CreatorFactory */ |
37
|
|
|
private $creatorFactory; |
38
|
|
|
/** @var PropertyAccessFactory */ |
39
|
|
|
private $propertyAccessFactory; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @param ContainerInterface $serviceProvider |
43
|
|
|
* @param Reader $annotationReader |
44
|
|
|
* @param PropertyMarker2Mapper $mappings |
45
|
|
|
*/ |
46
|
394 |
|
public function __construct(ContainerInterface $serviceProvider, Reader $annotationReader, PropertyMarker2Mapper $mappings) |
47
|
|
|
{ |
48
|
394 |
|
static $autoloader = false; |
49
|
|
|
|
50
|
394 |
|
if ($autoloader === false) { |
51
|
1 |
|
$autoloader = true; |
52
|
1 |
|
AnnotationRegistry::registerUniqueLoader('class_exists'); |
|
|
|
|
53
|
|
|
} |
54
|
|
|
|
55
|
394 |
|
$this->annotationReader = $annotationReader; |
56
|
394 |
|
$this->serviceProvider = $serviceProvider; |
57
|
394 |
|
$this->mappings = $mappings; |
58
|
|
|
|
59
|
394 |
|
$this->creatorFactory = new CreatorFactoryImpl(); |
60
|
394 |
|
$this->propertyAccessFactory = new PropertyAccessFactoryImpl(); |
61
|
394 |
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param \ReflectionClass $subject |
65
|
|
|
* |
66
|
|
|
* @return EntityConfig |
67
|
|
|
* @throws SlumberException |
68
|
|
|
*/ |
69
|
38 |
|
public function getEntityConfig(\ReflectionClass $subject) |
70
|
|
|
{ |
71
|
38 |
|
$config = new EntityConfig( |
72
|
38 |
|
$subject->name, |
73
|
38 |
|
$this->getCreator($subject), |
74
|
38 |
|
$this->getClassMarkers($subject), |
75
|
38 |
|
$this->getPropertyMarkersRecursive($subject) |
76
|
|
|
); |
77
|
|
|
|
78
|
38 |
|
return $config; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param PropertyMarkedForSlumber $marked |
83
|
|
|
* |
84
|
|
|
* @return PropertyMarkedForSlumber |
85
|
|
|
*/ |
86
|
36 |
|
protected function enrich(PropertyMarkedForSlumber $marked) |
87
|
|
|
{ |
88
|
36 |
|
$marked->mapper = $this->mappings->createMapper($marked->marker); |
89
|
|
|
|
90
|
36 |
|
return $marked; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param \ReflectionClass $subject |
95
|
|
|
* |
96
|
|
|
* @return Creator |
97
|
|
|
*/ |
98
|
38 |
|
private function getCreator(\ReflectionClass $subject) : Creator |
99
|
|
|
{ |
100
|
38 |
|
$validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject); |
101
|
|
|
|
102
|
38 |
|
return Psi::it($this->annotationReader->getClassAnnotations($subject)) |
103
|
38 |
|
->filter(new Psi\IsInstanceOf(ClassCreatorMarker::class)) |
104
|
38 |
|
->each($validationContext) |
105
|
|
|
// map the first one to a Creator |
106
|
|
|
->map(function (ClassCreatorMarker $marker) { return $marker->getCreator($this->creatorFactory); }) |
107
|
|
|
// or get the default creator |
108
|
38 |
|
->getFirst($this->creatorFactory->create($subject)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param \ReflectionClass $subject |
113
|
|
|
* |
114
|
|
|
* @return ClassMarker[] |
115
|
|
|
*/ |
116
|
38 |
|
private function getClassMarkers(\ReflectionClass $subject) : array |
117
|
|
|
{ |
118
|
38 |
|
$validationContext = new ClassAnnotationValidationContext($this->serviceProvider, $subject); |
119
|
|
|
|
120
|
38 |
|
return Psi::it($this->annotationReader->getClassAnnotations($subject)) |
121
|
38 |
|
->filter(new Psi\IsInstanceOf(ClassMarker::class)) |
122
|
38 |
|
->each($validationContext) |
123
|
38 |
|
->toArray(); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @param \ReflectionClass $subject |
128
|
|
|
* |
129
|
|
|
* @return PropertyMarkedForSlumber[] |
130
|
|
|
*/ |
131
|
38 |
|
private function getPropertyMarkersRecursive(\ReflectionClass $subject) : array |
132
|
|
|
{ |
133
|
|
|
/** @var PropertyMarkedForSlumber[] $result */ |
134
|
38 |
|
$result = []; |
135
|
|
|
|
136
|
38 |
|
$base = $subject; |
137
|
|
|
|
138
|
|
|
// We climb up the inheritance ladder. Be doing so we can also capture private properties of base classes |
139
|
|
|
// that are NOT shadowed by the inheriting classes. |
140
|
38 |
|
while ($base instanceof \ReflectionClass && $base->isUserDefined()) { |
141
|
|
|
|
142
|
36 |
|
$this->getPropertyMarkersForClass($subject, $base, $result); |
143
|
|
|
|
144
|
36 |
|
$base = $base->getParentClass(); |
145
|
|
|
} |
146
|
|
|
|
147
|
38 |
|
return array_values($result); |
148
|
|
|
} |
149
|
|
|
|
150
|
36 |
|
private function getPropertyMarkersForClass(\ReflectionClass $subject, \ReflectionClass $base, array &$result) : void |
151
|
|
|
{ |
152
|
36 |
|
$properties = $base->getProperties(); |
153
|
|
|
|
154
|
36 |
|
foreach ($properties as $property) { |
155
|
|
|
|
156
|
36 |
|
$propertyName = $property->getName(); |
157
|
|
|
|
158
|
36 |
|
if (! isset($result[$propertyName])) { |
159
|
|
|
|
160
|
36 |
|
$marker = $this->getPropertyAnnotationsOfType($subject, $base, $property); |
|
|
|
|
161
|
|
|
|
162
|
36 |
|
if ($marker) { |
163
|
36 |
|
$result[$propertyName] = $this->enrich($marker); |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
} |
167
|
36 |
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param \ReflectionClass $cls |
171
|
|
|
* @param \ReflectionProperty $property |
172
|
|
|
* |
173
|
|
|
* @return PropertyAnnotationValidationContext |
174
|
|
|
*/ |
175
|
36 |
|
private function getPropertyValidationContext(\ReflectionClass $cls, \ReflectionProperty $property) |
176
|
|
|
{ |
177
|
36 |
|
return new PropertyAnnotationValidationContext($this->serviceProvider, $cls, $property); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* @param \ReflectionClass $subject |
182
|
|
|
* @param \ReflectionClass $base |
183
|
|
|
* @param \ReflectionProperty $property |
184
|
|
|
* |
185
|
|
|
* @return null|PropertyMarkedForSlumber |
186
|
|
|
*/ |
187
|
36 |
|
private function getPropertyAnnotationsOfType(\ReflectionClass $subject, \ReflectionClass $base, \ReflectionProperty $property) : ?PropertyMarkedForSlumber |
188
|
|
|
{ |
189
|
36 |
|
$annotations = $this->annotationReader->getPropertyAnnotations($property); |
190
|
|
|
|
191
|
|
|
// get all slumber marker annotations |
192
|
36 |
|
$allMarkers = Psi::it($annotations) |
193
|
36 |
|
->filter(new Psi\IsInstanceOf(PropertyMarker::class)) |
194
|
36 |
|
->each($this->getPropertyValidationContext($base, $property)) |
195
|
36 |
|
->toArray(); |
196
|
|
|
|
197
|
|
|
// get the FIRST mapping marker like AsString() or AsObject() ... |
198
|
36 |
|
$mappingMarker = Psi::it($allMarkers) |
199
|
36 |
|
->filter(new Psi\IsInstanceOf(PropertyMappingMarker::class)) |
200
|
36 |
|
->getFirst(); |
201
|
|
|
|
202
|
36 |
|
if ($mappingMarker === null) { |
203
|
10 |
|
return null; |
204
|
|
|
} |
205
|
|
|
|
206
|
36 |
|
$newEntry = new PropertyMarkedForSlumber(); |
207
|
|
|
|
208
|
36 |
|
$newEntry->name = $property->getName(); |
209
|
36 |
|
$newEntry->alias = $mappingMarker->hasAlias() ? $mappingMarker->getAlias() : $property->getName(); |
210
|
36 |
|
$newEntry->marker = $mappingMarker; |
211
|
36 |
|
$newEntry->allMarkers = $allMarkers; |
212
|
36 |
|
$newEntry->mapper = $this->mappings->createMapper($mappingMarker); |
213
|
|
|
|
214
|
|
|
// We need to create the property access with the main subject class in mind. |
215
|
|
|
// By doing so we can access private properties of base classes. |
216
|
36 |
|
$newEntry->propertyAccess = $this->propertyAccessFactory->create($subject, $property); |
217
|
|
|
|
218
|
36 |
|
return $newEntry; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
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.