vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php line 217

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