vendor/jms/metadata/src/MetadataFactory.php line 145

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Metadata;
  4. use Metadata\Cache\CacheInterface;
  5. use Metadata\Driver\AdvancedDriverInterface;
  6. use Metadata\Driver\DriverInterface;
  7. class MetadataFactory implements AdvancedMetadataFactoryInterface
  8. {
  9. /**
  10. * @var DriverInterface
  11. */
  12. private $driver;
  13. /**
  14. * @var CacheInterface
  15. */
  16. private $cache;
  17. /**
  18. * @var ClassMetadata[]
  19. */
  20. private $loadedMetadata = [];
  21. /**
  22. * @var ClassMetadata[]
  23. */
  24. private $loadedClassMetadata = [];
  25. /**
  26. * @var string|null
  27. */
  28. private $hierarchyMetadataClass;
  29. /**
  30. * @var bool
  31. */
  32. private $includeInterfaces = false;
  33. /**
  34. * @var bool
  35. */
  36. private $debug = false;
  37. public function __construct(DriverInterface $driver, ?string $hierarchyMetadataClass = 'Metadata\ClassHierarchyMetadata', bool $debug = false)
  38. {
  39. $this->driver = $driver;
  40. $this->hierarchyMetadataClass = $hierarchyMetadataClass;
  41. $this->debug = $debug;
  42. }
  43. public function setIncludeInterfaces(bool $include): void
  44. {
  45. $this->includeInterfaces = $include;
  46. }
  47. public function setCache(CacheInterface $cache): void
  48. {
  49. $this->cache = $cache;
  50. }
  51. /**
  52. * {@inheritDoc}
  53. */
  54. public function getMetadataForClass(string $className)
  55. {
  56. if (isset($this->loadedMetadata[$className])) {
  57. return $this->filterNullMetadata($this->loadedMetadata[$className]);
  58. }
  59. $metadata = null;
  60. foreach ($this->getClassHierarchy($className) as $class) {
  61. if (isset($this->loadedClassMetadata[$name = $class->getName()])) {
  62. if (null !== $classMetadata = $this->filterNullMetadata($this->loadedClassMetadata[$name])) {
  63. $this->addClassMetadata($metadata, $classMetadata);
  64. }
  65. continue;
  66. }
  67. // check the cache
  68. if (null !== $this->cache) {
  69. if (($classMetadata = $this->cache->load($class->getName())) instanceof NullMetadata) {
  70. $this->loadedClassMetadata[$name] = $classMetadata;
  71. continue;
  72. }
  73. if (null !== $classMetadata) {
  74. if (!$classMetadata instanceof ClassMetadata) {
  75. throw new \LogicException(sprintf(
  76. 'The cache must return instances of ClassMetadata for class %s, but got %s.',
  77. $className,
  78. var_export($classMetadata, true)
  79. ));
  80. }
  81. if ($this->debug && !$classMetadata->isFresh()) {
  82. $this->cache->evict($classMetadata->name);
  83. } else {
  84. $this->loadedClassMetadata[$name] = $classMetadata;
  85. $this->addClassMetadata($metadata, $classMetadata);
  86. continue;
  87. }
  88. }
  89. }
  90. // load from source
  91. if (null !== $classMetadata = $this->driver->loadMetadataForClass($class)) {
  92. $this->loadedClassMetadata[$name] = $classMetadata;
  93. $this->addClassMetadata($metadata, $classMetadata);
  94. if (null !== $this->cache) {
  95. $this->cache->put($classMetadata);
  96. }
  97. continue;
  98. }
  99. if (null !== $this->cache && !$this->debug) {
  100. $this->cache->put(new NullMetadata($class->getName()));
  101. }
  102. }
  103. if (null === $metadata) {
  104. $metadata = new NullMetadata($className);
  105. }
  106. return $this->filterNullMetadata($this->loadedMetadata[$className] = $metadata);
  107. }
  108. /**
  109. * {@inheritDoc}
  110. */
  111. public function getAllClassNames(): array
  112. {
  113. if (!$this->driver instanceof AdvancedDriverInterface) {
  114. throw new \RuntimeException(
  115. sprintf('Driver "%s" must be an instance of "AdvancedDriverInterface".', get_class($this->driver))
  116. );
  117. }
  118. return $this->driver->getAllClassNames();
  119. }
  120. /**
  121. * @param MergeableInterface|ClassHierarchyMetadata $metadata
  122. */
  123. private function addClassMetadata(&$metadata, ClassMetadata $toAdd): void
  124. {
  125. if ($toAdd instanceof MergeableInterface) {
  126. if (null === $metadata) {
  127. $metadata = clone $toAdd;
  128. } else {
  129. $metadata->merge($toAdd);
  130. }
  131. } else {
  132. if (null === $metadata) {
  133. $class = $this->hierarchyMetadataClass;
  134. $metadata = new $class();
  135. }
  136. $metadata->addClassMetadata($toAdd);
  137. }
  138. }
  139. /**
  140. * @return \ReflectionClass[]
  141. */
  142. private function getClassHierarchy(string $class): array
  143. {
  144. $classes = [];
  145. $refl = new \ReflectionClass($class);
  146. do {
  147. $classes[] = $refl;
  148. $refl = $refl->getParentClass();
  149. } while (false !== $refl);
  150. $classes = array_reverse($classes, false);
  151. if (!$this->includeInterfaces) {
  152. return $classes;
  153. }
  154. $addedInterfaces = [];
  155. $newHierarchy = [];
  156. foreach ($classes as $class) {
  157. foreach ($class->getInterfaces() as $interface) {
  158. if (isset($addedInterfaces[$interface->getName()])) {
  159. continue;
  160. }
  161. $addedInterfaces[$interface->getName()] = true;
  162. $newHierarchy[] = $interface;
  163. }
  164. $newHierarchy[] = $class;
  165. }
  166. return $newHierarchy;
  167. }
  168. /**
  169. * @param ClassMetadata|ClassHierarchyMetadata|MergeableInterface $metadata
  170. *
  171. * @return ClassMetadata|ClassHierarchyMetadata|MergeableInterface
  172. */
  173. private function filterNullMetadata($metadata = null)
  174. {
  175. return !$metadata instanceof NullMetadata ? $metadata : null;
  176. }
  177. }