vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php line 277

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Persistence\Mapping;
  3. use Doctrine\Common\Cache\Cache;
  4. use Doctrine\Persistence\Mapping\Driver\MappingDriver;
  5. use Doctrine\Persistence\Proxy;
  6. use ReflectionException;
  7. use function array_reverse;
  8. use function array_unshift;
  9. use function class_exists;
  10. use function explode;
  11. use function interface_exists;
  12. use function strpos;
  13. use function strrpos;
  14. use function substr;
  15. /**
  16.  * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
  17.  * metadata mapping informations of a class which describes how a class should be mapped
  18.  * to a relational database.
  19.  *
  20.  * This class was abstracted from the ORM ClassMetadataFactory.
  21.  */
  22. abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
  23. {
  24.     /**
  25.      * Salt used by specific Object Manager implementation.
  26.      *
  27.      * @var string
  28.      */
  29.     protected $cacheSalt '$CLASSMETADATA';
  30.     /** @var Cache|null */
  31.     private $cacheDriver;
  32.     /** @var ClassMetadata[] */
  33.     private $loadedMetadata = [];
  34.     /** @var bool */
  35.     protected $initialized false;
  36.     /** @var ReflectionService|null */
  37.     private $reflectionService null;
  38.     /**
  39.      * Sets the cache driver used by the factory to cache ClassMetadata instances.
  40.      *
  41.      * @return void
  42.      */
  43.     public function setCacheDriver(?Cache $cacheDriver null)
  44.     {
  45.         $this->cacheDriver $cacheDriver;
  46.     }
  47.     /**
  48.      * Gets the cache driver used by the factory to cache ClassMetadata instances.
  49.      *
  50.      * @return Cache|null
  51.      */
  52.     public function getCacheDriver()
  53.     {
  54.         return $this->cacheDriver;
  55.     }
  56.     /**
  57.      * Returns an array of all the loaded metadata currently in memory.
  58.      *
  59.      * @return ClassMetadata[]
  60.      */
  61.     public function getLoadedMetadata()
  62.     {
  63.         return $this->loadedMetadata;
  64.     }
  65.     /**
  66.      * Forces the factory to load the metadata of all classes known to the underlying
  67.      * mapping driver.
  68.      *
  69.      * @return ClassMetadata[] The ClassMetadata instances of all mapped classes.
  70.      */
  71.     public function getAllMetadata()
  72.     {
  73.         if (! $this->initialized) {
  74.             $this->initialize();
  75.         }
  76.         $driver   $this->getDriver();
  77.         $metadata = [];
  78.         foreach ($driver->getAllClassNames() as $className) {
  79.             $metadata[] = $this->getMetadataFor($className);
  80.         }
  81.         return $metadata;
  82.     }
  83.     /**
  84.      * Lazy initialization of this stuff, especially the metadata driver,
  85.      * since these are not needed at all when a metadata cache is active.
  86.      *
  87.      * @return void
  88.      */
  89.     abstract protected function initialize();
  90.     /**
  91.      * Gets the fully qualified class-name from the namespace alias.
  92.      *
  93.      * @param string $namespaceAlias
  94.      * @param string $simpleClassName
  95.      *
  96.      * @return string
  97.      */
  98.     abstract protected function getFqcnFromAlias($namespaceAlias$simpleClassName);
  99.     /**
  100.      * Returns the mapping driver implementation.
  101.      *
  102.      * @return MappingDriver
  103.      */
  104.     abstract protected function getDriver();
  105.     /**
  106.      * Wakes up reflection after ClassMetadata gets unserialized from cache.
  107.      *
  108.      * @return void
  109.      */
  110.     abstract protected function wakeupReflection(ClassMetadata $classReflectionService $reflService);
  111.     /**
  112.      * Initializes Reflection after ClassMetadata was constructed.
  113.      *
  114.      * @return void
  115.      */
  116.     abstract protected function initializeReflection(ClassMetadata $classReflectionService $reflService);
  117.     /**
  118.      * Checks whether the class metadata is an entity.
  119.      *
  120.      * This method should return false for mapped superclasses or embedded classes.
  121.      *
  122.      * @return bool
  123.      */
  124.     abstract protected function isEntity(ClassMetadata $class);
  125.     /**
  126.      * Gets the class metadata descriptor for a class.
  127.      *
  128.      * @param string $className The name of the class.
  129.      *
  130.      * @return ClassMetadata
  131.      *
  132.      * @throws ReflectionException
  133.      * @throws MappingException
  134.      */
  135.     public function getMetadataFor($className)
  136.     {
  137.         if (isset($this->loadedMetadata[$className])) {
  138.             return $this->loadedMetadata[$className];
  139.         }
  140.         // Check for namespace alias
  141.         if (strpos($className':') !== false) {
  142.             [$namespaceAlias$simpleClassName] = explode(':'$className2);
  143.             $realClassName $this->getFqcnFromAlias($namespaceAlias$simpleClassName);
  144.         } else {
  145.             $realClassName $this->getRealClass($className);
  146.         }
  147.         if (isset($this->loadedMetadata[$realClassName])) {
  148.             // We do not have the alias name in the map, include it
  149.             return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  150.         }
  151.         $loadingException null;
  152.         try {
  153.             if ($this->cacheDriver) {
  154.                 $cached $this->cacheDriver->fetch($realClassName $this->cacheSalt);
  155.                 if ($cached instanceof ClassMetadata) {
  156.                     $this->loadedMetadata[$realClassName] = $cached;
  157.                     $this->wakeupReflection($cached$this->getReflectionService());
  158.                 } else {
  159.                     foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
  160.                         $this->cacheDriver->save(
  161.                             $loadedClassName $this->cacheSalt,
  162.                             $this->loadedMetadata[$loadedClassName]
  163.                         );
  164.                     }
  165.                 }
  166.             } else {
  167.                 $this->loadMetadata($realClassName);
  168.             }
  169.         } catch (MappingException $loadingException) {
  170.             $fallbackMetadataResponse $this->onNotFoundMetadata($realClassName);
  171.             if (! $fallbackMetadataResponse) {
  172.                 throw $loadingException;
  173.             }
  174.             $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse;
  175.         }
  176.         if ($className !== $realClassName) {
  177.             // We do not have the alias name in the map, include it
  178.             $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  179.         }
  180.         return $this->loadedMetadata[$className];
  181.     }
  182.     /**
  183.      * Checks whether the factory has the metadata for a class loaded already.
  184.      *
  185.      * @param string $className
  186.      *
  187.      * @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
  188.      */
  189.     public function hasMetadataFor($className)
  190.     {
  191.         return isset($this->loadedMetadata[$className]);
  192.     }
  193.     /**
  194.      * Sets the metadata descriptor for a specific class.
  195.      *
  196.      * NOTE: This is only useful in very special cases, like when generating proxy classes.
  197.      *
  198.      * @param string        $className
  199.      * @param ClassMetadata $class
  200.      *
  201.      * @return void
  202.      */
  203.     public function setMetadataFor($className$class)
  204.     {
  205.         $this->loadedMetadata[$className] = $class;
  206.     }
  207.     /**
  208.      * Gets an array of parent classes for the given entity class.
  209.      *
  210.      * @param string $name
  211.      *
  212.      * @return string[]
  213.      */
  214.     protected function getParentClasses($name)
  215.     {
  216.         // Collect parent classes, ignoring transient (not-mapped) classes.
  217.         $parentClasses = [];
  218.         foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) {
  219.             if ($this->getDriver()->isTransient($parentClass)) {
  220.                 continue;
  221.             }
  222.             $parentClasses[] = $parentClass;
  223.         }
  224.         return $parentClasses;
  225.     }
  226.     /**
  227.      * Loads the metadata of the class in question and all it's ancestors whose metadata
  228.      * is still not loaded.
  229.      *
  230.      * Important: The class $name does not necessarily exist at this point here.
  231.      * Scenarios in a code-generation setup might have access to XML/YAML
  232.      * Mapping files without the actual PHP code existing here. That is why the
  233.      * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface
  234.      * should be used for reflection.
  235.      *
  236.      * @param string $name The name of the class for which the metadata should get loaded.
  237.      *
  238.      * @return string[]
  239.      */
  240.     protected function loadMetadata($name)
  241.     {
  242.         if (! $this->initialized) {
  243.             $this->initialize();
  244.         }
  245.         $loaded = [];
  246.         $parentClasses   $this->getParentClasses($name);
  247.         $parentClasses[] = $name;
  248.         // Move down the hierarchy of parent classes, starting from the topmost class
  249.         $parent          null;
  250.         $rootEntityFound false;
  251.         $visited         = [];
  252.         $reflService     $this->getReflectionService();
  253.         foreach ($parentClasses as $className) {
  254.             if (isset($this->loadedMetadata[$className])) {
  255.                 $parent $this->loadedMetadata[$className];
  256.                 if ($this->isEntity($parent)) {
  257.                     $rootEntityFound true;
  258.                     array_unshift($visited$className);
  259.                 }
  260.                 continue;
  261.             }
  262.             $class $this->newClassMetadataInstance($className);
  263.             $this->initializeReflection($class$reflService);
  264.             $this->doLoadMetadata($class$parent$rootEntityFound$visited);
  265.             $this->loadedMetadata[$className] = $class;
  266.             $parent $class;
  267.             if ($this->isEntity($class)) {
  268.                 $rootEntityFound true;
  269.                 array_unshift($visited$className);
  270.             }
  271.             $this->wakeupReflection($class$reflService);
  272.             $loaded[] = $className;
  273.         }
  274.         return $loaded;
  275.     }
  276.     /**
  277.      * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions
  278.      *
  279.      * Override this method to implement a fallback strategy for failed metadata loading
  280.      *
  281.      * @param string $className
  282.      *
  283.      * @return ClassMetadata|null
  284.      */
  285.     protected function onNotFoundMetadata($className)
  286.     {
  287.         return null;
  288.     }
  289.     /**
  290.      * Actually loads the metadata from the underlying metadata.
  291.      *
  292.      * @param ClassMetadata      $class
  293.      * @param ClassMetadata|null $parent
  294.      * @param bool               $rootEntityFound
  295.      * @param string[]           $nonSuperclassParents All parent class names
  296.      *                                                 that are not marked as mapped superclasses.
  297.      *
  298.      * @return void
  299.      */
  300.     abstract protected function doLoadMetadata($class$parent$rootEntityFound, array $nonSuperclassParents);
  301.     /**
  302.      * Creates a new ClassMetadata instance for the given class name.
  303.      *
  304.      * @param string $className
  305.      *
  306.      * @return ClassMetadata
  307.      */
  308.     abstract protected function newClassMetadataInstance($className);
  309.     /**
  310.      * {@inheritDoc}
  311.      */
  312.     public function isTransient($class)
  313.     {
  314.         if (! $this->initialized) {
  315.             $this->initialize();
  316.         }
  317.         // Check for namespace alias
  318.         if (strpos($class':') !== false) {
  319.             [$namespaceAlias$simpleClassName] = explode(':'$class2);
  320.             $class                              $this->getFqcnFromAlias($namespaceAlias$simpleClassName);
  321.         }
  322.         return $this->getDriver()->isTransient($class);
  323.     }
  324.     /**
  325.      * Sets the reflectionService.
  326.      *
  327.      * @return void
  328.      */
  329.     public function setReflectionService(ReflectionService $reflectionService)
  330.     {
  331.         $this->reflectionService $reflectionService;
  332.     }
  333.     /**
  334.      * Gets the reflection service associated with this metadata factory.
  335.      *
  336.      * @return ReflectionService
  337.      */
  338.     public function getReflectionService()
  339.     {
  340.         if ($this->reflectionService === null) {
  341.             $this->reflectionService = new RuntimeReflectionService();
  342.         }
  343.         return $this->reflectionService;
  344.     }
  345.     /**
  346.      * Gets the real class name of a class name that could be a proxy.
  347.      */
  348.     private function getRealClass(string $class) : string
  349.     {
  350.         $pos strrpos($class'\\' Proxy::MARKER '\\');
  351.         if ($pos === false) {
  352.             return $class;
  353.         }
  354.         return substr($class$pos Proxy::MARKER_LENGTH 2);
  355.     }
  356. }
  357. class_exists(\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory::class);
  358. interface_exists(ClassMetadata::class);
  359. interface_exists(ReflectionService::class);