<?php namespace MailPoetVendor\Doctrine\Persistence\Mapping; if (!defined('ABSPATH')) exit; use MailPoetVendor\Doctrine\Common\Cache\Cache; use MailPoetVendor\Doctrine\Common\Cache\Psr6\CacheAdapter; use MailPoetVendor\Doctrine\Common\Cache\Psr6\DoctrineProvider; use MailPoetVendor\Doctrine\Deprecations\Deprecation; use MailPoetVendor\Doctrine\Persistence\Mapping\Driver\MappingDriver; use MailPoetVendor\Doctrine\Persistence\Proxy; use MailPoetVendor\Psr\Cache\CacheItemPoolInterface; use ReflectionException; use function array_combine; use function array_keys; use function array_map; use function array_reverse; use function array_unshift; use function assert; use function explode; use function is_array; use function str_replace; use function strpos; use function strrpos; use function substr; abstract class AbstractClassMetadataFactory implements ClassMetadataFactory { protected $cacheSalt = '__CLASSMETADATA__'; private $cacheDriver; private $cache; private $loadedMetadata = []; protected $initialized = \false; private $reflectionService = null; private $proxyClassNameResolver = null; public function setCacheDriver(?Cache $cacheDriver = null) { Deprecation::trigger('doctrine/persistence', 'https://github.com/doctrine/persistence/issues/184', '%s is deprecated. Use setCache() with a PSR-6 cache instead.', __METHOD__); $this->cacheDriver = $cacheDriver; if ($cacheDriver === null) { $this->cache = null; return; } $this->cache = CacheAdapter::wrap($cacheDriver); } public function getCacheDriver() { Deprecation::trigger('doctrine/persistence', 'https://github.com/doctrine/persistence/issues/184', '%s is deprecated. Use getCache() instead.', __METHOD__); return $this->cacheDriver; } public function setCache(CacheItemPoolInterface $cache) : void { $this->cache = $cache; $this->cacheDriver = DoctrineProvider::wrap($cache); } protected final function getCache() : ?CacheItemPoolInterface { return $this->cache; } public function getLoadedMetadata() { return $this->loadedMetadata; } public function getAllMetadata() { if (!$this->initialized) { $this->initialize(); } $driver = $this->getDriver(); $metadata = []; foreach ($driver->getAllClassNames() as $className) { $metadata[] = $this->getMetadataFor($className); } return $metadata; } public function setProxyClassNameResolver(ProxyClassNameResolver $resolver) : void { $this->proxyClassNameResolver = $resolver; } protected abstract function initialize(); protected abstract function getFqcnFromAlias($namespaceAlias, $simpleClassName); protected abstract function getDriver(); protected abstract function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); protected abstract function initializeReflection(ClassMetadata $class, ReflectionService $reflService); protected abstract function isEntity(ClassMetadata $class); public function getMetadataFor($className) { if (isset($this->loadedMetadata[$className])) { return $this->loadedMetadata[$className]; } // Check for namespace alias if (strpos($className, ':') !== \false) { Deprecation::trigger('doctrine/persistence', 'https://github.com/doctrine/persistence/issues/204', 'Short namespace aliases such as "%s" are deprecated, use ::class constant instead.', $className); [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); } else { $realClassName = $this->getRealClass($className); } if (isset($this->loadedMetadata[$realClassName])) { // We do not have the alias name in the map, include it return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; } $loadingException = null; try { if ($this->cache) { $cached = $this->cache->getItem($this->getCacheKey($realClassName))->get(); if ($cached instanceof ClassMetadata) { $this->loadedMetadata[$realClassName] = $cached; $this->wakeupReflection($cached, $this->getReflectionService()); } else { $loadedMetadata = $this->loadMetadata($realClassName); $classNames = array_combine(array_map([$this, 'getCacheKey'], $loadedMetadata), $loadedMetadata); assert(is_array($classNames)); foreach ($this->cache->getItems(array_keys($classNames)) as $item) { if (!isset($classNames[$item->getKey()])) { continue; } $item->set($this->loadedMetadata[$classNames[$item->getKey()]]); $this->cache->saveDeferred($item); } $this->cache->commit(); } } else { $this->loadMetadata($realClassName); } } catch (MappingException $loadingException) { $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); if (!$fallbackMetadataResponse) { throw $loadingException; } $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; } if ($className !== $realClassName) { // We do not have the alias name in the map, include it $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; } return $this->loadedMetadata[$className]; } public function hasMetadataFor($className) { return isset($this->loadedMetadata[$className]); } public function setMetadataFor($className, $class) { $this->loadedMetadata[$className] = $class; } protected function getParentClasses($name) { // Collect parent classes, ignoring transient (not-mapped) classes. $parentClasses = []; foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { if ($this->getDriver()->isTransient($parentClass)) { continue; } $parentClasses[] = $parentClass; } return $parentClasses; } protected function loadMetadata($name) { if (!$this->initialized) { $this->initialize(); } $loaded = []; $parentClasses = $this->getParentClasses($name); $parentClasses[] = $name; // Move down the hierarchy of parent classes, starting from the topmost class $parent = null; $rootEntityFound = \false; $visited = []; $reflService = $this->getReflectionService(); foreach ($parentClasses as $className) { if (isset($this->loadedMetadata[$className])) { $parent = $this->loadedMetadata[$className]; if ($this->isEntity($parent)) { $rootEntityFound = \true; array_unshift($visited, $className); } continue; } $class = $this->newClassMetadataInstance($className); $this->initializeReflection($class, $reflService); $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); $this->loadedMetadata[$className] = $class; $parent = $class; if ($this->isEntity($class)) { $rootEntityFound = \true; array_unshift($visited, $className); } $this->wakeupReflection($class, $reflService); $loaded[] = $className; } return $loaded; } protected function onNotFoundMetadata($className) { return null; } protected abstract function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); protected abstract function newClassMetadataInstance($className); public function isTransient($className) { if (!$this->initialized) { $this->initialize(); } // Check for namespace alias if (strpos($className, ':') !== \false) { Deprecation::trigger('doctrine/persistence', 'https://github.com/doctrine/persistence/issues/204', 'Short namespace aliases such as "%s" are deprecated, use ::class constant instead.', $className); [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); $className = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); } return $this->getDriver()->isTransient($className); } public function setReflectionService(ReflectionService $reflectionService) { $this->reflectionService = $reflectionService; } public function getReflectionService() { if ($this->reflectionService === null) { $this->reflectionService = new RuntimeReflectionService(); } return $this->reflectionService; } protected function getCacheKey(string $realClassName) : string { return str_replace('\\', '__', $realClassName) . $this->cacheSalt; } private function getRealClass(string $class) : string { if ($this->proxyClassNameResolver === null) { $this->createDefaultProxyClassNameResolver(); } assert($this->proxyClassNameResolver !== null); return $this->proxyClassNameResolver->resolveClassName($class); } private function createDefaultProxyClassNameResolver() : void { $this->proxyClassNameResolver = new class implements ProxyClassNameResolver { public function resolveClassName(string $className) : string { $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); if ($pos === \false) { return $className; } return substr($className, $pos + Proxy::MARKER_LENGTH + 2); } }; } }