1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Igni\Storage\Mapping\MetaData\Strategy; |
4
|
|
|
|
5
|
|
|
use Cache\Adapter\PHPArray\ArrayCachePool; |
6
|
|
|
use Doctrine\Common\Annotations\Annotation; |
7
|
|
|
use Doctrine\Common\Annotations\AnnotationReader; |
8
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
9
|
|
|
use Doctrine\Common\Annotations\IndexedReader; |
10
|
|
|
use Doctrine\Common\Annotations\Reader; |
11
|
|
|
use Igni\Storage\Exception\MappingException; |
12
|
|
|
use Igni\Storage\Mapping\Annotation\EmbeddedEntity; |
13
|
|
|
use Igni\Storage\Mapping\Annotation\Entity; |
14
|
|
|
use Igni\Storage\Mapping\Annotation\Property as Property; |
15
|
|
|
use Igni\Storage\Mapping\MetaData\EntityMetaData; |
16
|
|
|
use Igni\Storage\Mapping\MetaData\MetaDataFactory; |
17
|
|
|
use Igni\Storage\Mapping\MetaData\PropertyMetaData; |
18
|
|
|
use Igni\Storage\Mapping\Type; |
19
|
|
|
use Igni\Utils\ReflectionApi; |
20
|
|
|
use Psr\SimpleCache\CacheInterface; |
21
|
|
|
use ReflectionProperty; |
22
|
|
|
use ReflectionClass; |
23
|
|
|
|
24
|
|
|
class AnnotationMetaDataFactory implements MetaDataFactory |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @var CacheInterface |
28
|
|
|
*/ |
29
|
|
|
private $cache; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Reader |
33
|
|
|
*/ |
34
|
|
|
private $reader; |
35
|
|
|
|
36
|
51 |
|
public function __construct(CacheInterface $cache = null) |
37
|
|
|
{ |
38
|
51 |
|
AnnotationRegistry::registerUniqueLoader('class_exists'); |
|
|
|
|
39
|
51 |
|
$this->reader = new IndexedReader(new AnnotationReader()); |
40
|
|
|
|
41
|
51 |
|
if ($cache === null) { |
42
|
51 |
|
$cache = new ArrayCachePool(); |
43
|
|
|
} |
44
|
|
|
|
45
|
51 |
|
$this->cache = $cache; |
46
|
51 |
|
} |
47
|
|
|
|
48
|
23 |
|
public function getMetaData(string $entity): EntityMetaData |
49
|
|
|
{ |
50
|
23 |
|
$cacheKey = str_replace('\\', '', $entity) . '.metadata'; |
51
|
23 |
|
if ($this->cache->has($cacheKey)) { |
52
|
|
|
return $this->cache->get($cacheKey); |
53
|
|
|
} |
54
|
|
|
|
55
|
23 |
|
$metaData = $this->parseMetaData($entity); |
56
|
23 |
|
$this->cache->set($cacheKey, $metaData); |
57
|
|
|
|
58
|
23 |
|
return $metaData; |
59
|
|
|
} |
60
|
|
|
|
61
|
23 |
|
protected function parseMetaData(string $entityClass): EntityMetaData |
62
|
|
|
{ |
63
|
23 |
|
$metaData = new EntityMetaData($entityClass); |
64
|
23 |
|
$reflection = ReflectionApi::reflectClass($entityClass); |
65
|
|
|
|
66
|
23 |
|
$this->parseClassAnnotations($reflection, $metaData); |
67
|
23 |
|
$this->parseProperties($reflection, $metaData); |
68
|
|
|
|
69
|
23 |
|
return $metaData; |
70
|
|
|
} |
71
|
|
|
|
72
|
23 |
|
private function parseClassAnnotations(ReflectionClass $reflection, EntityMetaData $metaData): void |
73
|
|
|
{ |
74
|
23 |
|
$classAnnotations = $this->reader->getClassAnnotations($reflection); |
75
|
|
|
|
76
|
23 |
|
foreach ($classAnnotations as $type => $annotation) { |
77
|
|
|
switch ($type) { |
78
|
23 |
|
case Entity::class: |
79
|
23 |
|
$source = $annotation->source ?? $annotation->value; |
80
|
23 |
|
$metaData->setSource($source); |
81
|
23 |
|
$metaData->setConnection($annotation->connection); |
82
|
23 |
|
$this->setCustomHydrator($annotation, $metaData); |
83
|
23 |
|
break; |
84
|
3 |
|
case EmbeddedEntity::class: |
85
|
3 |
|
$metaData->makeEmbed(); |
86
|
3 |
|
$this->setCustomHydrator($annotation, $metaData); |
87
|
23 |
|
break; |
88
|
|
|
} |
89
|
|
|
} |
90
|
23 |
|
} |
91
|
|
|
|
92
|
23 |
|
private function parseProperties(ReflectionClass $reflection, EntityMetaData $metaData): void |
93
|
|
|
{ |
94
|
23 |
|
foreach ($reflection->getProperties() as $property) { |
95
|
23 |
|
$annotations = $this->reader->getPropertyAnnotations($property); |
96
|
23 |
|
foreach ($annotations as $annotation) { |
97
|
23 |
|
if ($annotation instanceof Property) { |
98
|
23 |
|
$this->addProperty($property, $annotation, $metaData); |
99
|
23 |
|
break; |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
23 |
|
if (!$metaData->definesProperties()) { |
105
|
|
|
throw MappingException::forEmptyMapping($metaData->getClass()); |
106
|
|
|
} |
107
|
23 |
|
} |
108
|
|
|
|
109
|
23 |
|
private function setCustomHydrator(Annotation $annotation, EntityMetaData $metaData) |
110
|
|
|
{ |
111
|
23 |
|
if ($annotation->hydrator !== null) { |
112
|
22 |
|
if (!class_exists($annotation->hydrator)) { |
113
|
|
|
throw new MappingException("Cannot use hydrator {$annotation->hydrator} class does not exist."); |
114
|
|
|
} |
115
|
|
|
|
116
|
22 |
|
$metaData->setCustomHydratorClass($annotation->hydrator); |
117
|
|
|
} |
118
|
23 |
|
} |
119
|
|
|
|
120
|
23 |
|
private function addProperty(ReflectionProperty $property, Property $annotation, EntityMetaData $metaData): void |
121
|
|
|
{ |
122
|
23 |
|
if (!Type::has($annotation->getType())) { |
123
|
|
|
throw new MappingException("Cannot map property {$property->getDeclaringClass()->getName()}::{$property->getName()} - unknown type {$annotation->getType()}."); |
124
|
|
|
} |
125
|
|
|
|
126
|
23 |
|
$property = new PropertyMetaData( |
127
|
23 |
|
$property->getName(), |
128
|
23 |
|
Type::get($annotation->getType()) |
129
|
|
|
); |
130
|
23 |
|
$property->setFieldName($annotation->name ?? $property->getName()); |
131
|
23 |
|
$property->setAttributes($annotation->getAttributes()); |
132
|
23 |
|
$metaData->addProperty($property); |
133
|
23 |
|
} |
134
|
|
|
} |
135
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.