schmittjoh /
metadata
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | declare(strict_types=1); |
||||||
| 4 | |||||||
| 5 | namespace Metadata; |
||||||
| 6 | |||||||
| 7 | use Metadata\Cache\CacheInterface; |
||||||
| 8 | use Metadata\Driver\AdvancedDriverInterface; |
||||||
| 9 | use Metadata\Driver\DriverInterface; |
||||||
| 10 | |||||||
| 11 | class MetadataFactory implements AdvancedMetadataFactoryInterface |
||||||
| 12 | { |
||||||
| 13 | /** |
||||||
| 14 | * @var DriverInterface |
||||||
| 15 | */ |
||||||
| 16 | private $driver; |
||||||
| 17 | |||||||
| 18 | /** |
||||||
| 19 | * @var CacheInterface |
||||||
| 20 | */ |
||||||
| 21 | private $cache; |
||||||
| 22 | |||||||
| 23 | /** |
||||||
| 24 | * @var ClassMetadata[] |
||||||
| 25 | */ |
||||||
| 26 | private $loadedMetadata = []; |
||||||
| 27 | |||||||
| 28 | /** |
||||||
| 29 | * @var ClassMetadata[] |
||||||
| 30 | */ |
||||||
| 31 | private $loadedClassMetadata = []; |
||||||
| 32 | |||||||
| 33 | /** |
||||||
| 34 | * @var string|null |
||||||
| 35 | */ |
||||||
| 36 | private $hierarchyMetadataClass; |
||||||
| 37 | |||||||
| 38 | /** |
||||||
| 39 | * @var bool |
||||||
| 40 | */ |
||||||
| 41 | private $includeInterfaces = false; |
||||||
| 42 | |||||||
| 43 | /** |
||||||
| 44 | * @var bool |
||||||
| 45 | */ |
||||||
| 46 | private $debug = false; |
||||||
| 47 | |||||||
| 48 | 10 | public function __construct(DriverInterface $driver, ?string $hierarchyMetadataClass = 'Metadata\ClassHierarchyMetadata', bool $debug = false) |
|||||
| 49 | { |
||||||
| 50 | 10 | $this->driver = $driver; |
|||||
| 51 | 10 | $this->hierarchyMetadataClass = $hierarchyMetadataClass; |
|||||
| 52 | 10 | $this->debug = $debug; |
|||||
| 53 | 10 | } |
|||||
| 54 | |||||||
| 55 | 1 | public function setIncludeInterfaces(bool $include): void |
|||||
| 56 | { |
||||||
| 57 | 1 | $this->includeInterfaces = $include; |
|||||
| 58 | 1 | } |
|||||
| 59 | |||||||
| 60 | 3 | public function setCache(CacheInterface $cache): void |
|||||
| 61 | { |
||||||
| 62 | 3 | $this->cache = $cache; |
|||||
| 63 | 3 | } |
|||||
| 64 | |||||||
| 65 | /** |
||||||
| 66 | * {@inheritDoc} |
||||||
| 67 | */ |
||||||
| 68 | 8 | public function getMetadataForClass(string $className) |
|||||
| 69 | { |
||||||
| 70 | 8 | if (isset($this->loadedMetadata[$className])) { |
|||||
| 71 | 3 | return $this->filterNullMetadata($this->loadedMetadata[$className]); |
|||||
| 72 | } |
||||||
| 73 | |||||||
| 74 | 8 | $metadata = null; |
|||||
| 75 | 8 | foreach ($this->getClassHierarchy($className) as $class) { |
|||||
| 76 | 8 | if (isset($this->loadedClassMetadata[$name = $class->getName()])) { |
|||||
| 77 | 1 | if (null !== $classMetadata = $this->filterNullMetadata($this->loadedClassMetadata[$name])) { |
|||||
| 78 | 1 | $this->addClassMetadata($metadata, $classMetadata); |
|||||
| 79 | } |
||||||
| 80 | |||||||
| 81 | 1 | continue; |
|||||
| 82 | } |
||||||
| 83 | |||||||
| 84 | // check the cache |
||||||
| 85 | 8 | if (null !== $this->cache) { |
|||||
| 86 | 3 | if (($classMetadata = $this->cache->load($class->getName())) instanceof NullMetadata) { |
|||||
| 87 | 1 | $this->loadedClassMetadata[$name] = $classMetadata; |
|||||
| 88 | 1 | continue; |
|||||
| 89 | } |
||||||
| 90 | |||||||
| 91 | 3 | if (null !== $classMetadata) { |
|||||
| 92 | if (!$classMetadata instanceof ClassMetadata) { |
||||||
| 93 | throw new \LogicException(sprintf( |
||||||
| 94 | 'The cache must return instances of ClassMetadata for class %s, but got %s.', |
||||||
| 95 | $className, |
||||||
| 96 | var_export($classMetadata, true) |
||||||
| 97 | )); |
||||||
| 98 | } |
||||||
| 99 | |||||||
| 100 | if ($this->debug && !$classMetadata->isFresh()) { |
||||||
| 101 | $this->cache->evict($classMetadata->name); |
||||||
| 102 | } else { |
||||||
| 103 | $this->loadedClassMetadata[$name] = $classMetadata; |
||||||
| 104 | $this->addClassMetadata($metadata, $classMetadata); |
||||||
| 105 | continue; |
||||||
| 106 | } |
||||||
| 107 | } |
||||||
| 108 | } |
||||||
| 109 | |||||||
| 110 | // load from source |
||||||
| 111 | 8 | if (null !== $classMetadata = $this->driver->loadMetadataForClass($class)) { |
|||||
| 112 | 4 | $this->loadedClassMetadata[$name] = $classMetadata; |
|||||
| 113 | 4 | $this->addClassMetadata($metadata, $classMetadata); |
|||||
| 114 | |||||||
| 115 | 4 | if (null !== $this->cache) { |
|||||
| 116 | 1 | $this->cache->put($classMetadata); |
|||||
| 117 | } |
||||||
| 118 | |||||||
| 119 | 4 | continue; |
|||||
| 120 | } |
||||||
| 121 | |||||||
| 122 | 4 | if (null !== $this->cache && !$this->debug) { |
|||||
| 123 | 1 | $this->cache->put(new NullMetadata($class->getName())); |
|||||
| 124 | } |
||||||
| 125 | } |
||||||
| 126 | |||||||
| 127 | 8 | if (null === $metadata) { |
|||||
| 128 | 4 | $metadata = new NullMetadata($className); |
|||||
| 129 | } |
||||||
| 130 | |||||||
| 131 | 8 | return $this->filterNullMetadata($this->loadedMetadata[$className] = $metadata); |
|||||
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||||
| 132 | } |
||||||
| 133 | |||||||
| 134 | /** |
||||||
| 135 | * {@inheritDoc} |
||||||
| 136 | */ |
||||||
| 137 | 2 | public function getAllClassNames(): array |
|||||
| 138 | { |
||||||
| 139 | 2 | if (!$this->driver instanceof AdvancedDriverInterface) { |
|||||
| 140 | 1 | throw new \RuntimeException( |
|||||
| 141 | 1 | sprintf('Driver "%s" must be an instance of "AdvancedDriverInterface".', get_class($this->driver)) |
|||||
| 142 | ); |
||||||
| 143 | } |
||||||
| 144 | |||||||
| 145 | 1 | return $this->driver->getAllClassNames(); |
|||||
| 146 | } |
||||||
| 147 | |||||||
| 148 | /** |
||||||
| 149 | * @param MergeableInterface|ClassHierarchyMetadata $metadata |
||||||
| 150 | */ |
||||||
| 151 | 4 | private function addClassMetadata(&$metadata, ClassMetadata $toAdd): void |
|||||
| 152 | { |
||||||
| 153 | 4 | if ($toAdd instanceof MergeableInterface) { |
|||||
| 154 | 2 | if (null === $metadata) { |
|||||
|
0 ignored issues
–
show
|
|||||||
| 155 | 2 | $metadata = clone $toAdd; |
|||||
| 156 | } else { |
||||||
| 157 | 2 | $metadata->merge($toAdd); |
|||||
|
0 ignored issues
–
show
The method
merge() does not exist on Metadata\ClassHierarchyMetadata.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 158 | } |
||||||
| 159 | } else { |
||||||
| 160 | 2 | if (null === $metadata) { |
|||||
|
0 ignored issues
–
show
|
|||||||
| 161 | 2 | $class = $this->hierarchyMetadataClass; |
|||||
| 162 | 2 | $metadata = new $class(); |
|||||
| 163 | } |
||||||
| 164 | |||||||
| 165 | 2 | $metadata->addClassMetadata($toAdd); |
|||||
|
0 ignored issues
–
show
The method
addClassMetadata() does not exist on Metadata\MergeableInterface.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. Loading history...
|
|||||||
| 166 | } |
||||||
| 167 | 4 | } |
|||||
| 168 | |||||||
| 169 | /** |
||||||
| 170 | * @return \ReflectionClass[] |
||||||
| 171 | */ |
||||||
| 172 | 8 | private function getClassHierarchy(string $class): array |
|||||
| 173 | { |
||||||
| 174 | 8 | $classes = []; |
|||||
| 175 | 8 | $refl = new \ReflectionClass($class); |
|||||
| 176 | |||||||
| 177 | do { |
||||||
| 178 | 8 | $classes[] = $refl; |
|||||
| 179 | 8 | $refl = $refl->getParentClass(); |
|||||
| 180 | 8 | } while (false !== $refl); |
|||||
| 181 | |||||||
| 182 | 8 | $classes = array_reverse($classes, false); |
|||||
| 183 | |||||||
| 184 | 8 | if (!$this->includeInterfaces) { |
|||||
| 185 | 7 | return $classes; |
|||||
| 186 | } |
||||||
| 187 | |||||||
| 188 | 1 | $addedInterfaces = []; |
|||||
| 189 | 1 | $newHierarchy = []; |
|||||
| 190 | |||||||
| 191 | 1 | foreach ($classes as $class) { |
|||||
| 192 | 1 | foreach ($class->getInterfaces() as $interface) { |
|||||
| 193 | 1 | if (isset($addedInterfaces[$interface->getName()])) { |
|||||
| 194 | 1 | continue; |
|||||
| 195 | } |
||||||
| 196 | |||||||
| 197 | 1 | $addedInterfaces[$interface->getName()] = true; |
|||||
| 198 | |||||||
| 199 | 1 | $newHierarchy[] = $interface; |
|||||
| 200 | } |
||||||
| 201 | |||||||
| 202 | 1 | $newHierarchy[] = $class; |
|||||
| 203 | } |
||||||
| 204 | |||||||
| 205 | 1 | return $newHierarchy; |
|||||
| 206 | } |
||||||
| 207 | |||||||
| 208 | /** |
||||||
| 209 | * @param ClassMetadata|ClassHierarchyMetadata|MergeableInterface $metadata |
||||||
| 210 | * |
||||||
| 211 | * @return ClassMetadata|ClassHierarchyMetadata|MergeableInterface |
||||||
| 212 | */ |
||||||
| 213 | 8 | private function filterNullMetadata($metadata = null) |
|||||
| 214 | { |
||||||
| 215 | 8 | return !$metadata instanceof NullMetadata ? $metadata : null; |
|||||
| 216 | } |
||||||
| 217 | } |
||||||
| 218 |