You are here

abstract class AbstractClassMetadataFactory in Plug 7

The ClassMetadataFactory is used to create ClassMetadata objects that contain all the metadata mapping informations of a class which describes how a class should be mapped to a relational database.

This class was abstracted from the ORM ClassMetadataFactory.

@since 2.2 @author Benjamin Eberlei <kontakt@beberlei.de> @author Guilherme Blanco <guilhermeblanco@hotmail.com> @author Jonathan Wage <jonwage@gmail.com> @author Roman Borschel <roman@code-factory.org>

Hierarchy

Expanded class hierarchy of AbstractClassMetadataFactory

1 file declares its use of AbstractClassMetadataFactory
ClassMetadataFactoryTest.php in lib/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php

File

lib/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php, line 39

Namespace

Doctrine\Common\Persistence\Mapping
View source
abstract class AbstractClassMetadataFactory implements ClassMetadataFactory {

  /**
   * Salt used by specific Object Manager implementation.
   *
   * @var string
   */
  protected $cacheSalt = '$CLASSMETADATA';

  /**
   * @var \Doctrine\Common\Cache\Cache|null
   */
  private $cacheDriver;

  /**
   * @var ClassMetadata[]
   */
  private $loadedMetadata = array();

  /**
   * @var bool
   */
  protected $initialized = false;

  /**
   * @var ReflectionService|null
   */
  private $reflectionService = null;

  /**
   * Sets the cache driver used by the factory to cache ClassMetadata instances.
   *
   * @param \Doctrine\Common\Cache\Cache $cacheDriver
   *
   * @return void
   */
  public function setCacheDriver(Cache $cacheDriver = null) {
    $this->cacheDriver = $cacheDriver;
  }

  /**
   * Gets the cache driver used by the factory to cache ClassMetadata instances.
   *
   * @return \Doctrine\Common\Cache\Cache|null
   */
  public function getCacheDriver() {
    return $this->cacheDriver;
  }

  /**
   * Returns an array of all the loaded metadata currently in memory.
   *
   * @return ClassMetadata[]
   */
  public function getLoadedMetadata() {
    return $this->loadedMetadata;
  }

  /**
   * Forces the factory to load the metadata of all classes known to the underlying
   * mapping driver.
   *
   * @return array The ClassMetadata instances of all mapped classes.
   */
  public function getAllMetadata() {
    if (!$this->initialized) {
      $this
        ->initialize();
    }
    $driver = $this
      ->getDriver();
    $metadata = array();
    foreach ($driver
      ->getAllClassNames() as $className) {
      $metadata[] = $this
        ->getMetadataFor($className);
    }
    return $metadata;
  }

  /**
   * Lazy initialization of this stuff, especially the metadata driver,
   * since these are not needed at all when a metadata cache is active.
   *
   * @return void
   */
  protected abstract function initialize();

  /**
   * Gets the fully qualified class-name from the namespace alias.
   *
   * @param string $namespaceAlias
   * @param string $simpleClassName
   *
   * @return string
   */
  protected abstract function getFqcnFromAlias($namespaceAlias, $simpleClassName);

  /**
   * Returns the mapping driver implementation.
   *
   * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
   */
  protected abstract function getDriver();

  /**
   * Wakes up reflection after ClassMetadata gets unserialized from cache.
   *
   * @param ClassMetadata     $class
   * @param ReflectionService $reflService
   *
   * @return void
   */
  protected abstract function wakeupReflection(ClassMetadata $class, ReflectionService $reflService);

  /**
   * Initializes Reflection after ClassMetadata was constructed.
   *
   * @param ClassMetadata     $class
   * @param ReflectionService $reflService
   *
   * @return void
   */
  protected abstract function initializeReflection(ClassMetadata $class, ReflectionService $reflService);

  /**
   * Checks whether the class metadata is an entity.
   *
   * This method should return false for mapped superclasses or embedded classes.
   *
   * @param ClassMetadata $class
   *
   * @return boolean
   */
  protected abstract function isEntity(ClassMetadata $class);

  /**
   * Gets the class metadata descriptor for a class.
   *
   * @param string $className The name of the class.
   *
   * @return ClassMetadata
   *
   * @throws ReflectionException
   * @throws MappingException
   */
  public function getMetadataFor($className) {
    if (isset($this->loadedMetadata[$className])) {
      return $this->loadedMetadata[$className];
    }

    // Check for namespace alias
    if (strpos($className, ':') !== false) {
      list($namespaceAlias, $simpleClassName) = explode(':', $className, 2);
      $realClassName = $this
        ->getFqcnFromAlias($namespaceAlias, $simpleClassName);
    }
    else {
      $realClassName = ClassUtils::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->cacheDriver) {
        if (($cached = $this->cacheDriver
          ->fetch($realClassName . $this->cacheSalt)) !== false) {
          $this->loadedMetadata[$realClassName] = $cached;
          $this
            ->wakeupReflection($cached, $this
            ->getReflectionService());
        }
        else {
          foreach ($this
            ->loadMetadata($realClassName) as $loadedClassName) {
            $this->cacheDriver
              ->save($loadedClassName . $this->cacheSalt, $this->loadedMetadata[$loadedClassName], null);
          }
        }
      }
      else {
        $this
          ->loadMetadata($realClassName);
      }
    } catch (MappingException $loadingException) {
      if (!($fallbackMetadataResponse = $this
        ->onNotFoundMetadata($realClassName))) {
        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];
  }

  /**
   * Checks whether the factory has the metadata for a class loaded already.
   *
   * @param string $className
   *
   * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
   */
  public function hasMetadataFor($className) {
    return isset($this->loadedMetadata[$className]);
  }

  /**
   * Sets the metadata descriptor for a specific class.
   *
   * NOTE: This is only useful in very special cases, like when generating proxy classes.
   *
   * @param string        $className
   * @param ClassMetadata $class
   *
   * @return void
   */
  public function setMetadataFor($className, $class) {
    $this->loadedMetadata[$className] = $class;
  }

  /**
   * Gets an array of parent classes for the given entity class.
   *
   * @param string $name
   *
   * @return array
   */
  protected function getParentClasses($name) {

    // Collect parent classes, ignoring transient (not-mapped) classes.
    $parentClasses = array();
    foreach (array_reverse($this
      ->getReflectionService()
      ->getParentClasses($name)) as $parentClass) {
      if (!$this
        ->getDriver()
        ->isTransient($parentClass)) {
        $parentClasses[] = $parentClass;
      }
    }
    return $parentClasses;
  }

  /**
   * Loads the metadata of the class in question and all it's ancestors whose metadata
   * is still not loaded.
   *
   * Important: The class $name does not necesarily exist at this point here.
   * Scenarios in a code-generation setup might have access to XML/YAML
   * Mapping files without the actual PHP code existing here. That is why the
   * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface
   * should be used for reflection.
   *
   * @param string $name The name of the class for which the metadata should get loaded.
   *
   * @return array
   */
  protected function loadMetadata($name) {
    if (!$this->initialized) {
      $this
        ->initialize();
    }
    $loaded = array();
    $parentClasses = $this
      ->getParentClasses($name);
    $parentClasses[] = $name;

    // Move down the hierarchy of parent classes, starting from the topmost class
    $parent = null;
    $rootEntityFound = false;
    $visited = array();
    $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;
  }

  /**
   * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions
   *
   * Override this method to implement a fallback strategy for failed metadata loading
   *
   * @param string $className
   *
   * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null
   */
  protected function onNotFoundMetadata($className) {
    return null;
  }

  /**
   * Actually loads the metadata from the underlying metadata.
   *
   * @param ClassMetadata      $class
   * @param ClassMetadata|null $parent
   * @param bool               $rootEntityFound
   * @param array              $nonSuperclassParents All parent class names
   *                                                 that are not marked as mapped superclasses.
   *
   * @return void
   */
  protected abstract function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents);

  /**
   * Creates a new ClassMetadata instance for the given class name.
   *
   * @param string $className
   *
   * @return ClassMetadata
   */
  protected abstract function newClassMetadataInstance($className);

  /**
   * {@inheritDoc}
   */
  public function isTransient($class) {
    if (!$this->initialized) {
      $this
        ->initialize();
    }

    // Check for namespace alias
    if (strpos($class, ':') !== false) {
      list($namespaceAlias, $simpleClassName) = explode(':', $class, 2);
      $class = $this
        ->getFqcnFromAlias($namespaceAlias, $simpleClassName);
    }
    return $this
      ->getDriver()
      ->isTransient($class);
  }

  /**
   * Sets the reflectionService.
   *
   * @param ReflectionService $reflectionService
   *
   * @return void
   */
  public function setReflectionService(ReflectionService $reflectionService) {
    $this->reflectionService = $reflectionService;
  }

  /**
   * Gets the reflection service associated with this metadata factory.
   *
   * @return ReflectionService
   */
  public function getReflectionService() {
    if ($this->reflectionService === null) {
      $this->reflectionService = new RuntimeReflectionService();
    }
    return $this->reflectionService;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AbstractClassMetadataFactory::$cacheDriver private property
AbstractClassMetadataFactory::$cacheSalt protected property Salt used by specific Object Manager implementation.
AbstractClassMetadataFactory::$initialized protected property
AbstractClassMetadataFactory::$loadedMetadata private property
AbstractClassMetadataFactory::$reflectionService private property
AbstractClassMetadataFactory::doLoadMetadata abstract protected function Actually loads the metadata from the underlying metadata. 1
AbstractClassMetadataFactory::getAllMetadata public function Forces the factory to load the metadata of all classes known to the underlying mapping driver. Overrides ClassMetadataFactory::getAllMetadata
AbstractClassMetadataFactory::getCacheDriver public function Gets the cache driver used by the factory to cache ClassMetadata instances.
AbstractClassMetadataFactory::getDriver abstract protected function Returns the mapping driver implementation. 1
AbstractClassMetadataFactory::getFqcnFromAlias abstract protected function Gets the fully qualified class-name from the namespace alias. 1
AbstractClassMetadataFactory::getLoadedMetadata public function Returns an array of all the loaded metadata currently in memory.
AbstractClassMetadataFactory::getMetadataFor public function Gets the class metadata descriptor for a class. Overrides ClassMetadataFactory::getMetadataFor
AbstractClassMetadataFactory::getParentClasses protected function Gets an array of parent classes for the given entity class.
AbstractClassMetadataFactory::getReflectionService public function Gets the reflection service associated with this metadata factory.
AbstractClassMetadataFactory::hasMetadataFor public function Checks whether the factory has the metadata for a class loaded already. Overrides ClassMetadataFactory::hasMetadataFor
AbstractClassMetadataFactory::initialize abstract protected function Lazy initialization of this stuff, especially the metadata driver, since these are not needed at all when a metadata cache is active. 1
AbstractClassMetadataFactory::initializeReflection abstract protected function Initializes Reflection after ClassMetadata was constructed. 1
AbstractClassMetadataFactory::isEntity abstract protected function Checks whether the class metadata is an entity. 1
AbstractClassMetadataFactory::isTransient public function Returns whether the class with the specified name should have its metadata loaded. This is only the case if it is either mapped directly or as a MappedSuperclass. Overrides ClassMetadataFactory::isTransient 1
AbstractClassMetadataFactory::loadMetadata protected function Loads the metadata of the class in question and all it's ancestors whose metadata is still not loaded.
AbstractClassMetadataFactory::newClassMetadataInstance abstract protected function Creates a new ClassMetadata instance for the given class name. 1
AbstractClassMetadataFactory::onNotFoundMetadata protected function Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions 1
AbstractClassMetadataFactory::setCacheDriver public function Sets the cache driver used by the factory to cache ClassMetadata instances.
AbstractClassMetadataFactory::setMetadataFor public function Sets the metadata descriptor for a specific class. Overrides ClassMetadataFactory::setMetadataFor
AbstractClassMetadataFactory::setReflectionService public function Sets the reflectionService.
AbstractClassMetadataFactory::wakeupReflection abstract protected function Wakes up reflection after ClassMetadata gets unserialized from cache. 1