1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM\Mapping\Driver\Annotation; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Annotations\AnnotationReader; |
8
|
|
|
use Doctrine\DBAL\Types\Type; |
9
|
|
|
use Doctrine\ORM\Annotation; |
10
|
|
|
use Doctrine\ORM\Events; |
11
|
|
|
use Doctrine\ORM\Mapping; |
12
|
|
|
use function array_diff; |
13
|
|
|
use function array_intersect; |
14
|
|
|
use function array_map; |
15
|
|
|
use function array_merge; |
16
|
|
|
use function array_unique; |
17
|
|
|
use function class_exists; |
18
|
|
|
use function constant; |
19
|
|
|
use function count; |
20
|
|
|
use function defined; |
21
|
|
|
use function get_class; |
22
|
|
|
use function get_declared_classes; |
23
|
|
|
use function in_array; |
24
|
|
|
use function is_dir; |
25
|
|
|
use function is_numeric; |
26
|
|
|
use function preg_match; |
27
|
|
|
use function preg_quote; |
28
|
|
|
use function realpath; |
29
|
|
|
use function sprintf; |
30
|
|
|
use function str_replace; |
31
|
|
|
use function strpos; |
32
|
|
|
use function strtolower; |
33
|
|
|
use function strtoupper; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* The AnnotationDriver reads the mapping metadata from docblock annotations. |
37
|
|
|
*/ |
38
|
|
|
class AnnotationDriver implements Mapping\Driver\MappingDriver |
39
|
|
|
{ |
40
|
|
|
/** |
41
|
|
|
* @var int[] |
42
|
|
|
*/ |
43
|
|
|
protected $entityAnnotationClasses = [ |
44
|
|
|
Annotation\Entity::class => 1, |
45
|
|
|
Annotation\MappedSuperclass::class => 2, |
46
|
|
|
Annotation\Embeddable::class => 3, |
47
|
|
|
]; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* The AnnotationReader. |
51
|
|
|
* |
52
|
|
|
* @var AnnotationReader |
53
|
|
|
*/ |
54
|
|
|
protected $reader; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* The paths where to look for mapping files. |
58
|
|
|
* |
59
|
|
|
* @var string[] |
60
|
|
|
*/ |
61
|
|
|
protected $paths = []; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* The paths excluded from path where to look for mapping files. |
65
|
|
|
* |
66
|
|
|
* @var string[] |
67
|
|
|
*/ |
68
|
|
|
protected $excludePaths = []; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* The file extension of mapping documents. |
72
|
|
|
* |
73
|
|
|
* @var string |
74
|
|
|
*/ |
75
|
|
|
protected $fileExtension = '.php'; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Cache for AnnotationDriver#getAllClassNames(). |
79
|
|
|
* |
80
|
|
|
* @var string[]|null |
81
|
|
|
*/ |
82
|
|
|
protected $classNames; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading |
86
|
|
|
* docblock annotations. |
87
|
|
|
* |
88
|
|
|
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed. |
89
|
|
|
* @param string|string[]|null $paths One or multiple paths where mapping classes can be found. |
90
|
|
|
*/ |
91
|
|
|
public function __construct($reader, $paths = null) |
92
|
|
|
{ |
93
|
|
|
$this->reader = $reader; |
94
|
|
|
|
95
|
|
|
if ($paths) { |
96
|
|
|
$this->addPaths((array) $paths); |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Appends lookup paths to metadata driver. |
102
|
|
|
* |
103
|
|
|
* @param string[] $paths |
104
|
|
|
*/ |
105
|
|
|
public function addPaths(array $paths) |
106
|
|
|
{ |
107
|
|
|
$this->paths = array_unique(array_merge($this->paths, $paths)); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Retrieves the defined metadata lookup paths. |
112
|
|
|
* |
113
|
|
|
* @return string[] |
114
|
|
|
*/ |
115
|
|
|
public function getPaths() |
116
|
|
|
{ |
117
|
|
|
return $this->paths; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Append exclude lookup paths to metadata driver. |
122
|
|
|
* |
123
|
|
|
* @param string[] $paths |
124
|
|
|
*/ |
125
|
|
|
public function addExcludePaths(array $paths) |
126
|
|
|
{ |
127
|
|
|
$this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Retrieve the defined metadata lookup exclude paths. |
132
|
|
|
* |
133
|
|
|
* @return string[] |
134
|
|
|
*/ |
135
|
|
|
public function getExcludePaths() |
136
|
|
|
{ |
137
|
|
|
return $this->excludePaths; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Retrieve the current annotation reader |
142
|
|
|
* |
143
|
|
|
* @return AnnotationReader |
144
|
|
|
*/ |
145
|
|
|
public function getReader() |
146
|
|
|
{ |
147
|
|
|
return $this->reader; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Gets the file extension used to look for mapping files under. |
152
|
|
|
* |
153
|
|
|
* @return string |
154
|
|
|
*/ |
155
|
|
|
public function getFileExtension() |
156
|
|
|
{ |
157
|
|
|
return $this->fileExtension; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Sets the file extension used to look for mapping files under. |
162
|
|
|
* |
163
|
|
|
* @param string $fileExtension The file extension to set. |
164
|
|
|
*/ |
165
|
|
|
public function setFileExtension($fileExtension) |
166
|
|
|
{ |
167
|
|
|
$this->fileExtension = $fileExtension; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Returns whether the class with the specified name is transient. Only non-transient |
172
|
|
|
* classes, that is entities and mapped superclasses, should have their metadata loaded. |
173
|
|
|
* |
174
|
|
|
* A class is non-transient if it is annotated with an annotation |
175
|
|
|
* from the {@see AnnotationDriver::entityAnnotationClasses}. |
176
|
|
|
* |
177
|
|
|
* @param string $className |
178
|
|
|
* |
179
|
|
|
* @return bool |
180
|
|
|
*/ |
181
|
|
|
public function isTransient($className) |
182
|
|
|
{ |
183
|
|
|
$classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); |
184
|
|
|
|
185
|
|
|
foreach ($classAnnotations as $annot) { |
186
|
|
|
if (isset($this->entityAnnotationClasses[get_class($annot)])) { |
187
|
|
|
return false; |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
return true; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* {@inheritDoc} |
195
|
|
|
*/ |
196
|
|
|
public function getAllClassNames() |
197
|
|
|
{ |
198
|
|
|
if ($this->classNames !== null) { |
199
|
|
|
return $this->classNames; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
if (! $this->paths) { |
|
|
|
|
203
|
|
|
throw Mapping\MappingException::pathRequired(); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
$classes = []; |
207
|
|
|
$includedFiles = []; |
208
|
|
|
|
209
|
|
|
foreach ($this->paths as $path) { |
210
|
|
|
if (! is_dir($path)) { |
211
|
|
|
throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
$iterator = new \RegexIterator( |
215
|
|
|
new \RecursiveIteratorIterator( |
216
|
|
|
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), |
217
|
|
|
\RecursiveIteratorIterator::LEAVES_ONLY |
218
|
|
|
), |
219
|
|
|
'/^.+' . preg_quote($this->fileExtension) . '$/i', |
220
|
|
|
\RecursiveRegexIterator::GET_MATCH |
221
|
|
|
); |
222
|
|
|
|
223
|
|
|
foreach ($iterator as $file) { |
224
|
|
|
$sourceFile = $file[0]; |
225
|
|
|
|
226
|
|
|
if (! preg_match('(^phar:)i', $sourceFile)) { |
227
|
|
|
$sourceFile = realpath($sourceFile); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
foreach ($this->excludePaths as $excludePath) { |
231
|
|
|
$exclude = str_replace('\\', '/', realpath($excludePath)); |
232
|
|
|
$current = str_replace('\\', '/', $sourceFile); |
233
|
|
|
|
234
|
|
|
if (strpos($current, $exclude) !== false) { |
235
|
|
|
continue 2; |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
require_once $sourceFile; |
240
|
|
|
|
241
|
|
|
$includedFiles[] = $sourceFile; |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
$declared = get_declared_classes(); |
246
|
|
|
|
247
|
|
|
foreach ($declared as $className) { |
248
|
|
|
$rc = new \ReflectionClass($className); |
249
|
|
|
$sourceFile = $rc->getFileName(); |
250
|
|
|
if (in_array($sourceFile, $includedFiles, true) && ! $this->isTransient($className)) { |
251
|
|
|
$classes[] = $className; |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
$this->classNames = $classes; |
256
|
|
|
|
257
|
|
|
return $classes; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* {@inheritdoc} |
262
|
|
|
* |
263
|
|
|
* @throws \UnexpectedValueException |
264
|
|
|
* @throws \ReflectionException |
265
|
|
|
* @throws Mapping\MappingException |
266
|
|
|
*/ |
267
|
|
|
public function loadMetadataForClass( |
268
|
|
|
string $className, |
269
|
|
|
Mapping\ClassMetadata $metadata, |
270
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
271
|
|
|
) : Mapping\ClassMetadata { |
272
|
|
|
$reflectionClass = $metadata->getReflectionClass(); |
273
|
|
|
|
274
|
|
|
if (! $reflectionClass) { |
275
|
|
|
// this happens when running annotation driver in combination with |
276
|
|
|
// static reflection services. This is not the nicest fix |
277
|
|
|
$reflectionClass = new \ReflectionClass($metadata->getClassName()); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
$classAnnotations = $this->getClassAnnotations($reflectionClass); |
281
|
|
|
$classMetadata = $this->convertClassAnnotationsToClassMetadata( |
282
|
|
|
$reflectionClass, |
283
|
|
|
$classAnnotations, |
284
|
|
|
$metadata, |
285
|
|
|
$metadataBuildingContext |
286
|
|
|
); |
287
|
|
|
|
288
|
|
|
// Evaluate @Cache annotation |
289
|
|
|
if (isset($classAnnotations[Annotation\Cache::class])) { |
290
|
|
|
$cacheAnnot = $classAnnotations[Annotation\Cache::class]; |
291
|
|
|
$cache = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata); |
292
|
|
|
|
293
|
|
|
$classMetadata->setCache($cache); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
// Evaluate annotations on properties/fields |
297
|
|
|
/* @var $reflProperty \ReflectionProperty */ |
298
|
|
|
foreach ($reflectionClass->getProperties() as $reflectionProperty) { |
299
|
|
|
if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) { |
300
|
|
|
continue; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); |
304
|
|
|
$property = $this->convertPropertyAnnotationsToProperty( |
305
|
|
|
$propertyAnnotations, |
306
|
|
|
$reflectionProperty, |
307
|
|
|
$classMetadata, |
308
|
|
|
$metadataBuildingContext |
309
|
|
|
); |
310
|
|
|
|
311
|
|
|
if (! $property) { |
312
|
|
|
continue; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
$metadata->addProperty($property); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
$this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext); |
319
|
|
|
|
320
|
|
|
return $classMetadata; |
|
|
|
|
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
325
|
|
|
* |
326
|
|
|
* @throws \UnexpectedValueException |
327
|
|
|
* @throws Mapping\MappingException |
328
|
|
|
*/ |
329
|
|
|
private function convertClassAnnotationsToClassMetadata( |
330
|
|
|
\ReflectionClass $reflectionClass, |
331
|
|
|
array $classAnnotations, |
332
|
|
|
Mapping\ClassMetadata $metadata, |
333
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
334
|
|
|
) : Mapping\ClassMetadata { |
335
|
|
|
switch (true) { |
336
|
|
|
case isset($classAnnotations[Annotation\Entity::class]): |
337
|
|
|
$binder = new Binder\EntityClassMetadataBinder( |
338
|
|
|
$reflectionClass, |
339
|
|
|
$classAnnotations, |
340
|
|
|
$metadata, |
341
|
|
|
$metadataBuildingContext |
342
|
|
|
); |
343
|
|
|
|
344
|
|
|
break; |
345
|
|
|
|
346
|
|
|
case isset($classAnnotations[Annotation\MappedSuperclass::class]): |
347
|
|
|
$binder = new Binder\MappedSuperClassMetadataBinder( |
348
|
|
|
$reflectionClass, |
349
|
|
|
$classAnnotations, |
350
|
|
|
$metadata, |
351
|
|
|
$metadataBuildingContext |
352
|
|
|
); |
353
|
|
|
break; |
354
|
|
|
|
355
|
|
|
case isset($classAnnotations[Annotation\Embeddable::class]): |
356
|
|
|
$binder = new Binder\EmbeddableClassMetadataBinder( |
357
|
|
|
$reflectionClass, |
358
|
|
|
$classAnnotations, |
359
|
|
|
$metadata, |
360
|
|
|
$metadataBuildingContext |
361
|
|
|
); |
362
|
|
|
break; |
363
|
|
|
|
364
|
|
|
default: |
365
|
|
|
throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName()); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
return $binder->bind(); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
373
|
|
|
* |
374
|
|
|
* @return Mapping\ClassMetadata |
375
|
|
|
* |
376
|
|
|
* @throws Mapping\MappingException |
377
|
|
|
* @throws \UnexpectedValueException |
378
|
|
|
*/ |
379
|
|
|
private function convertClassAnnotationsToEntityClassMetadata( |
|
|
|
|
380
|
|
|
array $classAnnotations, |
381
|
|
|
\ReflectionClass $reflectionClass, |
382
|
|
|
Mapping\ClassMetadata $metadata, |
383
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
384
|
|
|
) { |
385
|
|
|
/** @var Annotation\Entity $entityAnnot */ |
386
|
|
|
$entityAnnot = $classAnnotations[Annotation\Entity::class]; |
387
|
|
|
|
388
|
|
|
if ($entityAnnot->repositoryClass !== null) { |
389
|
|
|
$metadata->setCustomRepositoryClassName($entityAnnot->repositoryClass); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
if ($entityAnnot->readOnly) { |
393
|
|
|
$metadata->asReadOnly(); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
$metadata->isMappedSuperclass = false; |
397
|
|
|
$metadata->isEmbeddedClass = false; |
398
|
|
|
|
399
|
|
|
$this->attachTable($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext); |
400
|
|
|
|
401
|
|
|
// Evaluate @ChangeTrackingPolicy annotation |
402
|
|
|
if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) { |
403
|
|
|
$changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class]; |
404
|
|
|
|
405
|
|
|
$metadata->setChangeTrackingPolicy( |
406
|
|
|
constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value)) |
|
|
|
|
407
|
|
|
); |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
// Evaluate @InheritanceType annotation |
411
|
|
|
if (isset($classAnnotations[Annotation\InheritanceType::class])) { |
412
|
|
|
$inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class]; |
413
|
|
|
|
414
|
|
|
$metadata->setInheritanceType( |
415
|
|
|
constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value)) |
416
|
|
|
); |
417
|
|
|
|
418
|
|
|
if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) { |
419
|
|
|
$this->attachDiscriminatorColumn($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext); |
420
|
|
|
} |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
$this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); |
424
|
|
|
$this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata); |
425
|
|
|
|
426
|
|
|
return $metadata; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
431
|
|
|
*/ |
432
|
|
|
private function convertClassAnnotationsToMappedSuperClassMetadata( |
|
|
|
|
433
|
|
|
array $classAnnotations, |
434
|
|
|
\ReflectionClass $reflectionClass, |
435
|
|
|
Mapping\ClassMetadata $metadata, |
436
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
437
|
|
|
) : Mapping\ClassMetadata { |
438
|
|
|
/** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */ |
439
|
|
|
$mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class]; |
440
|
|
|
|
441
|
|
|
if ($mappedSuperclassAnnot->repositoryClass !== null) { |
442
|
|
|
$metadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
$metadata->isMappedSuperclass = true; |
446
|
|
|
$metadata->isEmbeddedClass = false; |
447
|
|
|
|
448
|
|
|
$this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); |
449
|
|
|
$this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata); |
450
|
|
|
|
451
|
|
|
return $metadata; |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/** |
455
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
456
|
|
|
* |
457
|
|
|
* @todo guilhermeblanco Remove nullable typehint once embeddables are back |
458
|
|
|
* |
459
|
|
|
* @throws Mapping\MappingException |
460
|
|
|
*/ |
461
|
|
|
private function convertPropertyAnnotationsToProperty( |
462
|
|
|
array $propertyAnnotations, |
463
|
|
|
\ReflectionProperty $reflectionProperty, |
464
|
|
|
Mapping\ClassMetadata $metadata, |
465
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
466
|
|
|
) : ?Mapping\Property { |
467
|
|
|
switch (true) { |
468
|
|
|
case isset($propertyAnnotations[Annotation\Column::class]): |
|
|
|
|
469
|
|
|
return $this->convertReflectionPropertyToFieldMetadata( |
470
|
|
|
$reflectionProperty, |
471
|
|
|
$propertyAnnotations, |
472
|
|
|
$metadata, |
473
|
|
|
$metadataBuildingContext |
474
|
|
|
); |
475
|
|
|
|
476
|
|
|
case isset($propertyAnnotations[Annotation\OneToOne::class]): |
477
|
|
|
return $this->convertReflectionPropertyToOneToOneAssociationMetadata( |
478
|
|
|
$reflectionProperty, |
479
|
|
|
$propertyAnnotations, |
480
|
|
|
$metadata, |
481
|
|
|
$metadataBuildingContext |
482
|
|
|
); |
483
|
|
|
|
484
|
|
|
case isset($propertyAnnotations[Annotation\ManyToOne::class]): |
485
|
|
|
return $this->convertReflectionPropertyToManyToOneAssociationMetadata( |
486
|
|
|
$reflectionProperty, |
487
|
|
|
$propertyAnnotations, |
488
|
|
|
$metadata, |
489
|
|
|
$metadataBuildingContext |
490
|
|
|
); |
491
|
|
|
|
492
|
|
|
case isset($propertyAnnotations[Annotation\OneToMany::class]): |
493
|
|
|
return $this->convertReflectionPropertyToOneToManyAssociationMetadata( |
494
|
|
|
$reflectionProperty, |
495
|
|
|
$propertyAnnotations, |
496
|
|
|
$metadata, |
497
|
|
|
$metadataBuildingContext |
498
|
|
|
); |
499
|
|
|
|
500
|
|
|
case isset($propertyAnnotations[Annotation\ManyToMany::class]): |
501
|
|
|
return $this->convertReflectionPropertyToManyToManyAssociationMetadata( |
502
|
|
|
$reflectionProperty, |
503
|
|
|
$propertyAnnotations, |
504
|
|
|
$metadata, |
505
|
|
|
$metadataBuildingContext |
506
|
|
|
); |
507
|
|
|
|
508
|
|
|
case isset($propertyAnnotations[Annotation\Embedded::class]): |
509
|
|
|
return null; |
510
|
|
|
|
511
|
|
|
default: |
512
|
|
|
return new Mapping\TransientMetadata($reflectionProperty->getName()); |
513
|
|
|
} |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
518
|
|
|
* |
519
|
|
|
* @throws Mapping\MappingException |
520
|
|
|
*/ |
521
|
|
|
private function convertReflectionPropertyToFieldMetadata( |
522
|
|
|
\ReflectionProperty $reflProperty, |
523
|
|
|
array $propertyAnnotations, |
524
|
|
|
Mapping\ClassMetadata $metadata, |
525
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
526
|
|
|
) : Mapping\FieldMetadata { |
527
|
|
|
$className = $metadata->getClassName(); |
528
|
|
|
$fieldName = $reflProperty->getName(); |
529
|
|
|
$isVersioned = isset($propertyAnnotations[Annotation\Version::class]); |
530
|
|
|
$columnAnnot = $propertyAnnotations[Annotation\Column::class]; |
531
|
|
|
|
532
|
|
|
if ($columnAnnot->type === null) { |
|
|
|
|
533
|
|
|
throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName); |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
$fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned); |
537
|
|
|
|
538
|
|
|
// Check for Id |
539
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
540
|
|
|
$fieldMetadata->setPrimaryKey(true); |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
// Check for GeneratedValue strategy |
544
|
|
|
if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) { |
545
|
|
|
$generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class]; |
546
|
|
|
$strategy = strtoupper($generatedValueAnnot->strategy); |
|
|
|
|
547
|
|
|
$idGeneratorType = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy)); |
548
|
|
|
|
549
|
|
|
if ($idGeneratorType !== Mapping\GeneratorType::NONE) { |
550
|
|
|
$idGeneratorDefinition = []; |
551
|
|
|
|
552
|
|
|
// Check for CustomGenerator/SequenceGenerator/TableGenerator definition |
553
|
|
|
switch (true) { |
554
|
|
|
case isset($propertyAnnotations[Annotation\SequenceGenerator::class]): |
555
|
|
|
$seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class]; |
556
|
|
|
|
557
|
|
|
$idGeneratorDefinition = [ |
558
|
|
|
'sequenceName' => $seqGeneratorAnnot->sequenceName, |
|
|
|
|
559
|
|
|
'allocationSize' => $seqGeneratorAnnot->allocationSize, |
|
|
|
|
560
|
|
|
]; |
561
|
|
|
|
562
|
|
|
break; |
563
|
|
|
|
564
|
|
|
case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]): |
565
|
|
|
$customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class]; |
566
|
|
|
|
567
|
|
|
$idGeneratorDefinition = [ |
568
|
|
|
'class' => $customGeneratorAnnot->class, |
569
|
|
|
'arguments' => $customGeneratorAnnot->arguments, |
570
|
|
|
]; |
571
|
|
|
|
572
|
|
|
break; |
573
|
|
|
|
574
|
|
|
/* @todo If it is not supported, why does this exist? */ |
575
|
|
|
case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']): |
576
|
|
|
throw Mapping\MappingException::tableIdGeneratorNotImplemented($className); |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
$fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition)); |
580
|
|
|
} |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
return $fieldMetadata; |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
588
|
|
|
* |
589
|
|
|
* @return Mapping\OneToOneAssociationMetadata |
590
|
|
|
*/ |
591
|
|
|
private function convertReflectionPropertyToOneToOneAssociationMetadata( |
592
|
|
|
\ReflectionProperty $reflectionProperty, |
593
|
|
|
array $propertyAnnotations, |
594
|
|
|
Mapping\ClassMetadata $metadata, |
595
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
596
|
|
|
) { |
597
|
|
|
$className = $metadata->getClassName(); |
598
|
|
|
$fieldName = $reflectionProperty->getName(); |
599
|
|
|
$oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class]; |
600
|
|
|
$assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName); |
601
|
|
|
$targetEntity = $oneToOneAnnot->targetEntity; |
|
|
|
|
602
|
|
|
|
603
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
604
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade)); |
|
|
|
|
605
|
|
|
$assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval); |
|
|
|
|
606
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch)); |
|
|
|
|
607
|
|
|
|
608
|
|
|
if (! empty($oneToOneAnnot->mappedBy)) { |
|
|
|
|
609
|
|
|
$assocMetadata->setMappedBy($oneToOneAnnot->mappedBy); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
if (! empty($oneToOneAnnot->inversedBy)) { |
|
|
|
|
613
|
|
|
$assocMetadata->setInversedBy($oneToOneAnnot->inversedBy); |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
// Check for Id |
617
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
618
|
|
|
$assocMetadata->setPrimaryKey(true); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
622
|
|
|
|
623
|
|
|
// Check for JoinColumn/JoinColumns annotations |
624
|
|
|
switch (true) { |
625
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
626
|
|
|
$joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
627
|
|
|
|
628
|
|
|
$assocMetadata->addJoinColumn( |
629
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
630
|
|
|
); |
631
|
|
|
|
632
|
|
|
break; |
633
|
|
|
|
634
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
635
|
|
|
$joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
636
|
|
|
|
637
|
|
|
foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
638
|
|
|
$assocMetadata->addJoinColumn( |
639
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
640
|
|
|
); |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
break; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
return $assocMetadata; |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
/** |
650
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
651
|
|
|
* |
652
|
|
|
* @return Mapping\ManyToOneAssociationMetadata |
653
|
|
|
*/ |
654
|
|
|
private function convertReflectionPropertyToManyToOneAssociationMetadata( |
655
|
|
|
\ReflectionProperty $reflectionProperty, |
656
|
|
|
array $propertyAnnotations, |
657
|
|
|
Mapping\ClassMetadata $metadata, |
658
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
659
|
|
|
) { |
660
|
|
|
$className = $metadata->getClassName(); |
661
|
|
|
$fieldName = $reflectionProperty->getName(); |
662
|
|
|
$manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class]; |
663
|
|
|
$assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName); |
664
|
|
|
$targetEntity = $manyToOneAnnot->targetEntity; |
|
|
|
|
665
|
|
|
|
666
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
667
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade)); |
|
|
|
|
668
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch)); |
|
|
|
|
669
|
|
|
|
670
|
|
|
if (! empty($manyToOneAnnot->inversedBy)) { |
|
|
|
|
671
|
|
|
$assocMetadata->setInversedBy($manyToOneAnnot->inversedBy); |
672
|
|
|
} |
673
|
|
|
|
674
|
|
|
// Check for Id |
675
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
676
|
|
|
$assocMetadata->setPrimaryKey(true); |
677
|
|
|
} |
678
|
|
|
|
679
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
680
|
|
|
|
681
|
|
|
// Check for JoinColumn/JoinColumns annotations |
682
|
|
|
switch (true) { |
683
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
684
|
|
|
$joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
685
|
|
|
|
686
|
|
|
$assocMetadata->addJoinColumn( |
687
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
688
|
|
|
); |
689
|
|
|
|
690
|
|
|
break; |
691
|
|
|
|
692
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
693
|
|
|
$joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
694
|
|
|
|
695
|
|
|
foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
696
|
|
|
$assocMetadata->addJoinColumn( |
697
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
698
|
|
|
); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
break; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
return $assocMetadata; |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
/** |
708
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
709
|
|
|
* |
710
|
|
|
* @throws Mapping\MappingException |
711
|
|
|
*/ |
712
|
|
|
private function convertReflectionPropertyToOneToManyAssociationMetadata( |
713
|
|
|
\ReflectionProperty $reflectionProperty, |
714
|
|
|
array $propertyAnnotations, |
715
|
|
|
Mapping\ClassMetadata $metadata, |
716
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
717
|
|
|
) : Mapping\OneToManyAssociationMetadata { |
718
|
|
|
$className = $metadata->getClassName(); |
719
|
|
|
$fieldName = $reflectionProperty->getName(); |
720
|
|
|
$oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class]; |
721
|
|
|
$assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName); |
722
|
|
|
$targetEntity = $oneToManyAnnot->targetEntity; |
|
|
|
|
723
|
|
|
|
724
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
725
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade)); |
|
|
|
|
726
|
|
|
$assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval); |
|
|
|
|
727
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch)); |
|
|
|
|
728
|
|
|
|
729
|
|
|
if (! empty($oneToManyAnnot->mappedBy)) { |
|
|
|
|
730
|
|
|
$assocMetadata->setMappedBy($oneToManyAnnot->mappedBy); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
if (! empty($oneToManyAnnot->indexBy)) { |
|
|
|
|
734
|
|
|
$assocMetadata->setIndexedBy($oneToManyAnnot->indexBy); |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
// Check for OrderBy |
738
|
|
|
if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
739
|
|
|
$orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
740
|
|
|
|
741
|
|
|
$assocMetadata->setOrderBy($orderByAnnot->value); |
|
|
|
|
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
// Check for Id |
745
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
746
|
|
|
throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
750
|
|
|
|
751
|
|
|
return $assocMetadata; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
756
|
|
|
* |
757
|
|
|
* @throws Mapping\MappingException |
758
|
|
|
*/ |
759
|
|
|
private function convertReflectionPropertyToManyToManyAssociationMetadata( |
760
|
|
|
\ReflectionProperty $reflectionProperty, |
761
|
|
|
array $propertyAnnotations, |
762
|
|
|
Mapping\ClassMetadata $metadata, |
763
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
764
|
|
|
) : Mapping\ManyToManyAssociationMetadata { |
765
|
|
|
$className = $metadata->getClassName(); |
766
|
|
|
$fieldName = $reflectionProperty->getName(); |
767
|
|
|
$manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class]; |
768
|
|
|
$assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName); |
769
|
|
|
$targetEntity = $manyToManyAnnot->targetEntity; |
|
|
|
|
770
|
|
|
|
771
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
772
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade)); |
|
|
|
|
773
|
|
|
$assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval); |
|
|
|
|
774
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch)); |
|
|
|
|
775
|
|
|
|
776
|
|
|
if (! empty($manyToManyAnnot->mappedBy)) { |
|
|
|
|
777
|
|
|
$assocMetadata->setMappedBy($manyToManyAnnot->mappedBy); |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
if (! empty($manyToManyAnnot->inversedBy)) { |
|
|
|
|
781
|
|
|
$assocMetadata->setInversedBy($manyToManyAnnot->inversedBy); |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
if (! empty($manyToManyAnnot->indexBy)) { |
|
|
|
|
785
|
|
|
$assocMetadata->setIndexedBy($manyToManyAnnot->indexBy); |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
// Check for JoinTable |
789
|
|
|
if (isset($propertyAnnotations[Annotation\JoinTable::class])) { |
790
|
|
|
$joinTableAnnot = $propertyAnnotations[Annotation\JoinTable::class]; |
791
|
|
|
$joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot); |
792
|
|
|
|
793
|
|
|
$assocMetadata->setJoinTable($joinTableMetadata); |
794
|
|
|
} |
795
|
|
|
|
796
|
|
|
// Check for OrderBy |
797
|
|
|
if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
798
|
|
|
$orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
799
|
|
|
|
800
|
|
|
$assocMetadata->setOrderBy($orderByAnnot->value); |
|
|
|
|
801
|
|
|
} |
802
|
|
|
|
803
|
|
|
// Check for Id |
804
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
805
|
|
|
throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName); |
806
|
|
|
} |
807
|
|
|
|
808
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
809
|
|
|
|
810
|
|
|
return $assocMetadata; |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
/** |
814
|
|
|
* Parse the given Column as FieldMetadata |
815
|
|
|
*/ |
816
|
|
|
private function convertColumnAnnotationToFieldMetadata( |
817
|
|
|
Annotation\Column $columnAnnot, |
818
|
|
|
string $fieldName, |
819
|
|
|
bool $isVersioned |
820
|
|
|
) : Mapping\FieldMetadata { |
821
|
|
|
$fieldMetadata = $isVersioned |
822
|
|
|
? new Mapping\VersionFieldMetadata($fieldName) |
823
|
|
|
: new Mapping\FieldMetadata($fieldName) |
824
|
|
|
; |
825
|
|
|
|
826
|
|
|
$fieldMetadata->setType(Type::getType($columnAnnot->type)); |
827
|
|
|
|
828
|
|
|
if (! empty($columnAnnot->name)) { |
829
|
|
|
$fieldMetadata->setColumnName($columnAnnot->name); |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
if (! empty($columnAnnot->columnDefinition)) { |
833
|
|
|
$fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition); |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
if (! empty($columnAnnot->length)) { |
837
|
|
|
$fieldMetadata->setLength($columnAnnot->length); |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
if ($columnAnnot->options) { |
|
|
|
|
841
|
|
|
$fieldMetadata->setOptions($columnAnnot->options); |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
$fieldMetadata->setScale($columnAnnot->scale); |
845
|
|
|
$fieldMetadata->setPrecision($columnAnnot->precision); |
846
|
|
|
$fieldMetadata->setNullable($columnAnnot->nullable); |
847
|
|
|
$fieldMetadata->setUnique($columnAnnot->unique); |
848
|
|
|
|
849
|
|
|
return $fieldMetadata; |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Parse the given Table as TableMetadata |
854
|
|
|
*/ |
855
|
|
|
private function convertTableAnnotationToTableMetadata( |
856
|
|
|
Annotation\Table $tableAnnot, |
857
|
|
|
Mapping\TableMetadata $table |
858
|
|
|
) : Mapping\TableMetadata { |
859
|
|
|
if (! empty($tableAnnot->name)) { |
860
|
|
|
$table->setName($tableAnnot->name); |
861
|
|
|
} |
862
|
|
|
|
863
|
|
|
if (! empty($tableAnnot->schema)) { |
864
|
|
|
$table->setSchema($tableAnnot->schema); |
865
|
|
|
} |
866
|
|
|
|
867
|
|
|
foreach ($tableAnnot->options as $optionName => $optionValue) { |
868
|
|
|
$table->addOption($optionName, $optionValue); |
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
foreach ($tableAnnot->indexes as $indexAnnot) { |
872
|
|
|
$table->addIndex([ |
873
|
|
|
'name' => $indexAnnot->name, |
874
|
|
|
'columns' => $indexAnnot->columns, |
875
|
|
|
'unique' => $indexAnnot->unique, |
876
|
|
|
'options' => $indexAnnot->options, |
877
|
|
|
'flags' => $indexAnnot->flags, |
878
|
|
|
]); |
879
|
|
|
} |
880
|
|
|
|
881
|
|
|
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { |
882
|
|
|
$table->addUniqueConstraint([ |
883
|
|
|
'name' => $uniqueConstraintAnnot->name, |
884
|
|
|
'columns' => $uniqueConstraintAnnot->columns, |
885
|
|
|
'options' => $uniqueConstraintAnnot->options, |
886
|
|
|
'flags' => $uniqueConstraintAnnot->flags, |
887
|
|
|
]); |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
return $table; |
891
|
|
|
} |
892
|
|
|
|
893
|
|
|
/** |
894
|
|
|
* Parse the given JoinTable as JoinTableMetadata |
895
|
|
|
*/ |
896
|
|
|
private function convertJoinTableAnnotationToJoinTableMetadata( |
897
|
|
|
Annotation\JoinTable $joinTableAnnot |
898
|
|
|
) : Mapping\JoinTableMetadata { |
899
|
|
|
$joinTable = new Mapping\JoinTableMetadata(); |
900
|
|
|
|
901
|
|
|
if (! empty($joinTableAnnot->name)) { |
902
|
|
|
$joinTable->setName($joinTableAnnot->name); |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
if (! empty($joinTableAnnot->schema)) { |
906
|
|
|
$joinTable->setSchema($joinTableAnnot->schema); |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) { |
910
|
|
|
$joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
911
|
|
|
|
912
|
|
|
$joinTable->addJoinColumn($joinColumn); |
913
|
|
|
} |
914
|
|
|
|
915
|
|
|
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) { |
916
|
|
|
$joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
917
|
|
|
|
918
|
|
|
$joinTable->addInverseJoinColumn($joinColumn); |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
return $joinTable; |
922
|
|
|
} |
923
|
|
|
|
924
|
|
|
/** |
925
|
|
|
* Parse the given JoinColumn as JoinColumnMetadata |
926
|
|
|
*/ |
927
|
|
|
private function convertJoinColumnAnnotationToJoinColumnMetadata( |
928
|
|
|
Annotation\JoinColumn $joinColumnAnnot |
929
|
|
|
) : Mapping\JoinColumnMetadata { |
930
|
|
|
$joinColumn = new Mapping\JoinColumnMetadata(); |
931
|
|
|
|
932
|
|
|
// @todo Remove conditionals for name and referencedColumnName once naming strategy is brought into drivers |
933
|
|
|
if (! empty($joinColumnAnnot->name)) { |
934
|
|
|
$joinColumn->setColumnName($joinColumnAnnot->name); |
935
|
|
|
} |
936
|
|
|
|
937
|
|
|
if (! empty($joinColumnAnnot->referencedColumnName)) { |
938
|
|
|
$joinColumn->setReferencedColumnName($joinColumnAnnot->referencedColumnName); |
939
|
|
|
} |
940
|
|
|
|
941
|
|
|
$joinColumn->setNullable($joinColumnAnnot->nullable); |
942
|
|
|
$joinColumn->setUnique($joinColumnAnnot->unique); |
943
|
|
|
|
944
|
|
|
if (! empty($joinColumnAnnot->fieldName)) { |
945
|
|
|
$joinColumn->setAliasedName($joinColumnAnnot->fieldName); |
946
|
|
|
} |
947
|
|
|
|
948
|
|
|
if (! empty($joinColumnAnnot->columnDefinition)) { |
949
|
|
|
$joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition); |
950
|
|
|
} |
951
|
|
|
|
952
|
|
|
if ($joinColumnAnnot->onDelete) { |
953
|
|
|
$joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete)); |
954
|
|
|
} |
955
|
|
|
|
956
|
|
|
return $joinColumn; |
957
|
|
|
} |
958
|
|
|
|
959
|
|
|
/** |
960
|
|
|
* Parse the given Cache as CacheMetadata |
961
|
|
|
* |
962
|
|
|
* @param string|null $fieldName |
963
|
|
|
*/ |
964
|
|
|
private function convertCacheAnnotationToCacheMetadata( |
965
|
|
|
Annotation\Cache $cacheAnnot, |
966
|
|
|
Mapping\ClassMetadata $metadata, |
967
|
|
|
$fieldName = null |
968
|
|
|
) : Mapping\CacheMetadata { |
969
|
|
|
$baseRegion = strtolower(str_replace('\\', '_', $metadata->getRootClassName())); |
970
|
|
|
$defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : ''); |
971
|
|
|
|
972
|
|
|
$usage = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage)); |
973
|
|
|
$region = $cacheAnnot->region ?: $defaultRegion; |
974
|
|
|
|
975
|
|
|
return new Mapping\CacheMetadata($usage, $region); |
976
|
|
|
} |
977
|
|
|
|
978
|
|
|
/** |
979
|
|
|
* @return mixed[] |
980
|
|
|
*/ |
981
|
|
|
private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping) |
|
|
|
|
982
|
|
|
{ |
983
|
|
|
$entities = []; |
984
|
|
|
|
985
|
|
|
foreach ($resultSetMapping->entities as $entityResultAnnot) { |
986
|
|
|
$entityResult = [ |
987
|
|
|
'fields' => [], |
988
|
|
|
'entityClass' => $entityResultAnnot->entityClass, |
989
|
|
|
'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, |
990
|
|
|
]; |
991
|
|
|
|
992
|
|
|
foreach ($entityResultAnnot->fields as $fieldResultAnnot) { |
993
|
|
|
$entityResult['fields'][] = [ |
994
|
|
|
'name' => $fieldResultAnnot->name, |
995
|
|
|
'column' => $fieldResultAnnot->column, |
996
|
|
|
]; |
997
|
|
|
} |
998
|
|
|
|
999
|
|
|
$entities[] = $entityResult; |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
$columns = []; |
1003
|
|
|
|
1004
|
|
|
foreach ($resultSetMapping->columns as $columnResultAnnot) { |
1005
|
|
|
$columns[] = [ |
1006
|
|
|
'name' => $columnResultAnnot->name, |
1007
|
|
|
]; |
1008
|
|
|
} |
1009
|
|
|
|
1010
|
|
|
return [ |
1011
|
|
|
'name' => $resultSetMapping->name, |
1012
|
|
|
'entities' => $entities, |
1013
|
|
|
'columns' => $columns, |
1014
|
|
|
]; |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
/** |
1018
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
1019
|
|
|
*/ |
1020
|
|
|
private function attachTable( |
1021
|
|
|
array $classAnnotations, |
1022
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1023
|
|
|
Mapping\ClassMetadata $metadata, |
1024
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
1025
|
|
|
) : void { |
1026
|
|
|
$parent = $metadata->getParent(); |
1027
|
|
|
|
1028
|
|
|
if ($parent !== null && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) { |
1029
|
|
|
$metadata->setTable($parent->table); |
1030
|
|
|
|
1031
|
|
|
return; |
1032
|
|
|
} |
1033
|
|
|
|
1034
|
|
|
$namingStrategy = $metadataBuildingContext->getNamingStrategy(); |
1035
|
|
|
$table = new Mapping\TableMetadata(); |
1036
|
|
|
|
1037
|
|
|
$table->setName($namingStrategy->classToTableName($metadata->getClassName())); |
1038
|
|
|
|
1039
|
|
|
// Evaluate @Table annotation |
1040
|
|
|
if (isset($classAnnotations[Annotation\Table::class])) { |
1041
|
|
|
$tableAnnot = $classAnnotations[Annotation\Table::class]; |
1042
|
|
|
|
1043
|
|
|
$this->convertTableAnnotationToTableMetadata($table, $tableAnnot); |
|
|
|
|
1044
|
|
|
} |
1045
|
|
|
|
1046
|
|
|
$metadata->setTable($table); |
1047
|
|
|
} |
1048
|
|
|
|
1049
|
|
|
/** |
1050
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
1051
|
|
|
* |
1052
|
|
|
* @throws Mapping\MappingException |
1053
|
|
|
*/ |
1054
|
|
|
private function attachDiscriminatorColumn( |
1055
|
|
|
array $classAnnotations, |
1056
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1057
|
|
|
Mapping\ClassMetadata $metadata, |
1058
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
1059
|
|
|
) : void { |
1060
|
|
|
$discriminatorColumn = new Mapping\DiscriminatorColumnMetadata(); |
1061
|
|
|
|
1062
|
|
|
$discriminatorColumn->setTableName($metadata->getTableName()); |
1063
|
|
|
$discriminatorColumn->setColumnName('dtype'); |
1064
|
|
|
$discriminatorColumn->setType(Type::getType('string')); |
1065
|
|
|
$discriminatorColumn->setLength(255); |
1066
|
|
|
|
1067
|
|
|
// Evaluate DiscriminatorColumn annotation |
1068
|
|
|
if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) { |
1069
|
|
|
/** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnotation */ |
1070
|
|
|
$discriminatorColumnAnnotation = $classAnnotations[Annotation\DiscriminatorColumn::class]; |
1071
|
|
|
$typeName = ! empty($discriminatorColumnAnnotation->type) |
1072
|
|
|
? $discriminatorColumnAnnotation->type |
1073
|
|
|
: 'string'; |
1074
|
|
|
|
1075
|
|
|
$discriminatorColumn->setType(Type::getType($typeName)); |
1076
|
|
|
$discriminatorColumn->setColumnName($discriminatorColumnAnnotation->name); |
1077
|
|
|
|
1078
|
|
|
if (! empty($discriminatorColumnAnnotation->columnDefinition)) { |
1079
|
|
|
$discriminatorColumn->setColumnDefinition($discriminatorColumnAnnotation->columnDefinition); |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
if (! empty($discriminatorColumnAnnotation->length)) { |
1083
|
|
|
$discriminatorColumn->setLength($discriminatorColumnAnnotation->length); |
1084
|
|
|
} |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
|
|
$metadata->setDiscriminatorColumn($discriminatorColumn); |
1088
|
|
|
|
1089
|
|
|
// Evaluate DiscriminatorMap annotation |
1090
|
|
|
if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) { |
1091
|
|
|
$discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class]; |
1092
|
|
|
$discriminatorMap = $discriminatorMapAnnotation->value; |
|
|
|
|
1093
|
|
|
|
1094
|
|
|
$metadata->setDiscriminatorMap($discriminatorMap); |
1095
|
|
|
} |
1096
|
|
|
} |
1097
|
|
|
|
1098
|
|
|
/** |
1099
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
1100
|
|
|
*/ |
1101
|
|
|
private function attachLifecycleCallbacks( |
1102
|
|
|
array $classAnnotations, |
1103
|
|
|
\ReflectionClass $reflectionClass, |
1104
|
|
|
Mapping\ClassMetadata $metadata |
1105
|
|
|
) : void { |
1106
|
|
|
// Evaluate @HasLifecycleCallbacks annotation |
1107
|
|
|
if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) { |
1108
|
|
|
/* @var $method \ReflectionMethod */ |
1109
|
|
|
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
1110
|
|
|
foreach ($this->getMethodCallbacks($method) as $callback) { |
1111
|
|
|
$metadata->addLifecycleCallback($method->getName(), $callback); |
1112
|
|
|
} |
1113
|
|
|
} |
1114
|
|
|
} |
1115
|
|
|
} |
1116
|
|
|
|
1117
|
|
|
/** |
1118
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
1119
|
|
|
* |
1120
|
|
|
* @throws \ReflectionException |
1121
|
|
|
* @throws Mapping\MappingException |
1122
|
|
|
*/ |
1123
|
|
|
private function attachEntityListeners( |
1124
|
|
|
array $classAnnotations, |
1125
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1126
|
|
|
Mapping\ClassMetadata $metadata |
1127
|
|
|
) : void { |
1128
|
|
|
// Evaluate @EntityListeners annotation |
1129
|
|
|
if (isset($classAnnotations[Annotation\EntityListeners::class])) { |
1130
|
|
|
/** @var Annotation\EntityListeners $entityListenersAnnot */ |
1131
|
|
|
$entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class]; |
1132
|
|
|
|
1133
|
|
|
foreach ($entityListenersAnnot->value as $listenerClassName) { |
1134
|
|
|
if (! class_exists($listenerClassName)) { |
1135
|
|
|
throw Mapping\MappingException::entityListenerClassNotFound( |
1136
|
|
|
$listenerClassName, |
1137
|
|
|
$metadata->getClassName() |
1138
|
|
|
); |
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
$listenerClass = new \ReflectionClass($listenerClassName); |
1142
|
|
|
|
1143
|
|
|
/* @var $method \ReflectionMethod */ |
1144
|
|
|
foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
1145
|
|
|
foreach ($this->getMethodCallbacks($method) as $callback) { |
1146
|
|
|
$metadata->addEntityListener($callback, $listenerClassName, $method->getName()); |
1147
|
|
|
} |
1148
|
|
|
} |
1149
|
|
|
} |
1150
|
|
|
} |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* @param Annotation\Annotation[] $classAnnotations |
1155
|
|
|
* |
1156
|
|
|
* @throws Mapping\MappingException |
1157
|
|
|
*/ |
1158
|
|
|
private function attachPropertyOverrides( |
1159
|
|
|
array $classAnnotations, |
1160
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1161
|
|
|
Mapping\ClassMetadata $metadata, |
1162
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
|
|
|
|
1163
|
|
|
) : void { |
1164
|
|
|
// Evaluate AssociationOverrides annotation |
1165
|
|
|
if (isset($classAnnotations[Annotation\AssociationOverrides::class])) { |
1166
|
|
|
$associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class]; |
1167
|
|
|
|
1168
|
|
|
foreach ($associationOverridesAnnot->value as $associationOverride) { |
|
|
|
|
1169
|
|
|
$fieldName = $associationOverride->name; |
1170
|
|
|
$property = $metadata->getProperty($fieldName); |
1171
|
|
|
|
1172
|
|
|
if (! $property) { |
1173
|
|
|
throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); |
1174
|
|
|
} |
1175
|
|
|
|
1176
|
|
|
$existingClass = get_class($property); |
1177
|
|
|
$override = new $existingClass($fieldName); |
1178
|
|
|
|
1179
|
|
|
// Check for JoinColumn/JoinColumns annotations |
1180
|
|
|
if ($associationOverride->joinColumns) { |
1181
|
|
|
$joinColumns = []; |
1182
|
|
|
|
1183
|
|
|
foreach ($associationOverride->joinColumns as $joinColumnAnnot) { |
1184
|
|
|
$joinColumns[] = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
1185
|
|
|
} |
1186
|
|
|
|
1187
|
|
|
$override->setJoinColumns($joinColumns); |
1188
|
|
|
} |
1189
|
|
|
|
1190
|
|
|
// Check for JoinTable annotations |
1191
|
|
|
if ($associationOverride->joinTable) { |
1192
|
|
|
$joinTableAnnot = $associationOverride->joinTable; |
1193
|
|
|
$joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot); |
1194
|
|
|
|
1195
|
|
|
$override->setJoinTable($joinTableMetadata); |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
|
// Check for inversedBy |
1199
|
|
|
if ($associationOverride->inversedBy) { |
1200
|
|
|
$override->setInversedBy($associationOverride->inversedBy); |
1201
|
|
|
} |
1202
|
|
|
|
1203
|
|
|
// Check for fetch |
1204
|
|
|
if ($associationOverride->fetch) { |
1205
|
|
|
$override->setFetchMode( |
1206
|
|
|
constant(Mapping\FetchMode::class . '::' . $associationOverride->fetch) |
1207
|
|
|
); |
1208
|
|
|
} |
1209
|
|
|
|
1210
|
|
|
$metadata->setPropertyOverride($override); |
1211
|
|
|
} |
1212
|
|
|
} |
1213
|
|
|
|
1214
|
|
|
// Evaluate AttributeOverrides annotation |
1215
|
|
|
if (isset($classAnnotations[Annotation\AttributeOverrides::class])) { |
1216
|
|
|
$attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class]; |
1217
|
|
|
|
1218
|
|
|
foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { |
1219
|
|
|
$fieldMetadata = $this->convertColumnAnnotationToFieldMetadata( |
1220
|
|
|
$attributeOverrideAnnot->column, |
1221
|
|
|
$attributeOverrideAnnot->name, |
1222
|
|
|
false |
1223
|
|
|
); |
1224
|
|
|
|
1225
|
|
|
$metadata->setPropertyOverride($fieldMetadata); |
1226
|
|
|
} |
1227
|
|
|
} |
1228
|
|
|
} |
1229
|
|
|
|
1230
|
|
|
/** |
1231
|
|
|
* @param Annotation\Annotation[] $propertyAnnotations |
1232
|
|
|
*/ |
1233
|
|
|
private function attachAssociationPropertyCache( |
1234
|
|
|
array $propertyAnnotations, |
1235
|
|
|
\ReflectionProperty $reflectionProperty, |
1236
|
|
|
Mapping\AssociationMetadata $assocMetadata, |
1237
|
|
|
Mapping\ClassMetadata $metadata |
1238
|
|
|
) : void { |
1239
|
|
|
// Check for Cache |
1240
|
|
|
if (isset($propertyAnnotations[Annotation\Cache::class])) { |
1241
|
|
|
$cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
1242
|
|
|
$cacheMetadata = $this->convertCacheAnnotationToCacheMetadata( |
1243
|
|
|
$cacheAnnot, |
1244
|
|
|
$metadata, |
1245
|
|
|
$reflectionProperty->getName() |
1246
|
|
|
); |
1247
|
|
|
|
1248
|
|
|
$assocMetadata->setCache($cacheMetadata); |
1249
|
|
|
} |
1250
|
|
|
} |
1251
|
|
|
|
1252
|
|
|
/** |
1253
|
|
|
* Attempts to resolve the cascade modes. |
1254
|
|
|
* |
1255
|
|
|
* @param string $className The class name. |
1256
|
|
|
* @param string $fieldName The field name. |
1257
|
|
|
* @param string[] $originalCascades The original unprocessed field cascades. |
1258
|
|
|
* |
1259
|
|
|
* @return string[] The processed field cascades. |
1260
|
|
|
* |
1261
|
|
|
* @throws Mapping\MappingException If a cascade option is not valid. |
1262
|
|
|
*/ |
1263
|
|
|
private function getCascade(string $className, string $fieldName, array $originalCascades) : array |
1264
|
|
|
{ |
1265
|
|
|
$cascadeTypes = ['remove', 'persist', 'refresh']; |
1266
|
|
|
$cascades = array_map('strtolower', $originalCascades); |
1267
|
|
|
|
1268
|
|
|
if (in_array('all', $cascades, true)) { |
1269
|
|
|
$cascades = $cascadeTypes; |
1270
|
|
|
} |
1271
|
|
|
|
1272
|
|
|
if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) { |
1273
|
|
|
$diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes)); |
1274
|
|
|
|
1275
|
|
|
throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName); |
1276
|
|
|
} |
1277
|
|
|
|
1278
|
|
|
return $cascades; |
1279
|
|
|
} |
1280
|
|
|
|
1281
|
|
|
/** |
1282
|
|
|
* Attempts to resolve the fetch mode. |
1283
|
|
|
* |
1284
|
|
|
* @param string $className The class name. |
1285
|
|
|
* @param string $fetchMode The fetch mode. |
1286
|
|
|
* |
1287
|
|
|
* @return string The fetch mode as defined in ClassMetadata. |
1288
|
|
|
* |
1289
|
|
|
* @throws Mapping\MappingException If the fetch mode is not valid. |
1290
|
|
|
*/ |
1291
|
|
|
private function getFetchMode($className, $fetchMode) : string |
1292
|
|
|
{ |
1293
|
|
|
$fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode); |
1294
|
|
|
|
1295
|
|
|
if (! defined($fetchModeConstant)) { |
1296
|
|
|
throw Mapping\MappingException::invalidFetchMode($className, $fetchMode); |
1297
|
|
|
} |
1298
|
|
|
|
1299
|
|
|
return constant($fetchModeConstant); |
1300
|
|
|
} |
1301
|
|
|
|
1302
|
|
|
/** |
1303
|
|
|
* Parses the given method. |
1304
|
|
|
* |
1305
|
|
|
* @return string[] |
1306
|
|
|
*/ |
1307
|
|
|
private function getMethodCallbacks(\ReflectionMethod $method) : array |
1308
|
|
|
{ |
1309
|
|
|
$annotations = $this->getMethodAnnotations($method); |
1310
|
|
|
$events = [ |
1311
|
|
|
Events::prePersist => Annotation\PrePersist::class, |
1312
|
|
|
Events::postPersist => Annotation\PostPersist::class, |
1313
|
|
|
Events::preUpdate => Annotation\PreUpdate::class, |
1314
|
|
|
Events::postUpdate => Annotation\PostUpdate::class, |
1315
|
|
|
Events::preRemove => Annotation\PreRemove::class, |
1316
|
|
|
Events::postRemove => Annotation\PostRemove::class, |
1317
|
|
|
Events::postLoad => Annotation\PostLoad::class, |
1318
|
|
|
Events::preFlush => Annotation\PreFlush::class, |
1319
|
|
|
]; |
1320
|
|
|
|
1321
|
|
|
// Check for callbacks |
1322
|
|
|
$callbacks = []; |
1323
|
|
|
|
1324
|
|
|
foreach ($events as $eventName => $annotationClassName) { |
1325
|
|
|
if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) { |
1326
|
|
|
$callbacks[] = $eventName; |
1327
|
|
|
} |
1328
|
|
|
} |
1329
|
|
|
|
1330
|
|
|
return $callbacks; |
1331
|
|
|
} |
1332
|
|
|
|
1333
|
|
|
/** |
1334
|
|
|
* @return Annotation\Annotation[] |
1335
|
|
|
*/ |
1336
|
|
|
private function getClassAnnotations(\ReflectionClass $reflectionClass) : array |
1337
|
|
|
{ |
1338
|
|
|
$classAnnotations = $this->reader->getClassAnnotations($reflectionClass); |
1339
|
|
|
|
1340
|
|
|
foreach ($classAnnotations as $key => $annot) { |
1341
|
|
|
if (! is_numeric($key)) { |
1342
|
|
|
continue; |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
$classAnnotations[get_class($annot)] = $annot; |
1346
|
|
|
} |
1347
|
|
|
|
1348
|
|
|
return $classAnnotations; |
1349
|
|
|
} |
1350
|
|
|
|
1351
|
|
|
/** |
1352
|
|
|
* @return Annotation\Annotation[] |
1353
|
|
|
*/ |
1354
|
|
|
private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty) : array |
1355
|
|
|
{ |
1356
|
|
|
$propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty); |
1357
|
|
|
|
1358
|
|
|
foreach ($propertyAnnotations as $key => $annot) { |
1359
|
|
|
if (! is_numeric($key)) { |
1360
|
|
|
continue; |
1361
|
|
|
} |
1362
|
|
|
|
1363
|
|
|
$propertyAnnotations[get_class($annot)] = $annot; |
1364
|
|
|
} |
1365
|
|
|
|
1366
|
|
|
return $propertyAnnotations; |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
|
|
/** |
1370
|
|
|
* @return Annotation\Annotation[] |
1371
|
|
|
*/ |
1372
|
|
|
private function getMethodAnnotations(\ReflectionMethod $reflectionMethod) : array |
1373
|
|
|
{ |
1374
|
|
|
$methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod); |
1375
|
|
|
|
1376
|
|
|
foreach ($methodAnnotations as $key => $annot) { |
1377
|
|
|
if (! is_numeric($key)) { |
1378
|
|
|
continue; |
1379
|
|
|
} |
1380
|
|
|
|
1381
|
|
|
$methodAnnotations[get_class($annot)] = $annot; |
1382
|
|
|
} |
1383
|
|
|
|
1384
|
|
|
return $methodAnnotations; |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
|
|
/** |
1388
|
|
|
* Factory method for the Annotation Driver. |
1389
|
|
|
* |
1390
|
|
|
* @param string|string[] $paths |
1391
|
|
|
* |
1392
|
|
|
* @return AnnotationDriver |
1393
|
|
|
*/ |
1394
|
|
|
public static function create($paths = [], ?AnnotationReader $reader = null) |
1395
|
|
|
{ |
1396
|
|
|
if ($reader === null) { |
1397
|
|
|
$reader = new AnnotationReader(); |
1398
|
|
|
} |
1399
|
|
|
|
1400
|
|
|
return new self($reader, $paths); |
1401
|
|
|
} |
1402
|
|
|
} |
1403
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.