class AnnotatedClassDiscovery

Same name in this branch
  1. 10 core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery
Same name in other branches
  1. 9 core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery
  2. 9 core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery
  3. 8.9.x core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery
  4. 8.9.x core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery
  5. 11.x core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery
  6. 11.x core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery

Defines a discovery mechanism to find annotated plugins in PSR-4 namespaces.

Hierarchy

Expanded class hierarchy of AnnotatedClassDiscovery

4 files declare their use of AnnotatedClassDiscovery
AnnotatedClassDiscovery.php in core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
AnnotatedClassDiscoveryCachedTest.php in core/tests/Drupal/Tests/Component/Annotation/AnnotatedClassDiscoveryCachedTest.php
AnnotatedClassDiscoveryTest.php in core/tests/Drupal/Tests/Component/Annotation/AnnotatedClassDiscoveryTest.php
AnnotatedClassDiscoveryTest.php in core/tests/Drupal/Tests/Component/Plugin/Discovery/AnnotatedClassDiscoveryTest.php

File

core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php, line 18

Namespace

Drupal\Component\Annotation\Plugin\Discovery
View source
class AnnotatedClassDiscovery implements DiscoveryInterface {
    use DiscoveryTrait;
    
    /**
     * The namespaces within which to find plugin classes.
     *
     * @var string[]
     */
    protected $pluginNamespaces;
    
    /**
     * The name of the annotation that contains the plugin definition.
     *
     * The class corresponding to this name must implement
     * \Drupal\Component\Annotation\AnnotationInterface.
     *
     * @var string
     */
    protected $pluginDefinitionAnnotationName;
    
    /**
     * The doctrine annotation reader.
     *
     * @var \Doctrine\Common\Annotations\Reader
     */
    protected $annotationReader;
    
    /**
     * Additional namespaces to be scanned for annotation classes.
     *
     * @var string[]
     */
    protected $annotationNamespaces = [];
    
    /**
     * The file cache object.
     *
     * @var \Drupal\Component\FileCache\FileCacheInterface
     */
    protected $fileCache;
    
    /**
     * Constructs a new instance.
     *
     * @param string[] $plugin_namespaces
     *   (optional) An array of namespace that may contain plugin implementations.
     *   Defaults to an empty array.
     * @param string $plugin_definition_annotation_name
     *   (optional) The name of the annotation that contains the plugin definition.
     *   Defaults to 'Drupal\Component\Annotation\Plugin'.
     * @param string[] $annotation_namespaces
     *   (optional) Additional namespaces to be scanned for annotation classes.
     */
    public function __construct($plugin_namespaces = [], $plugin_definition_annotation_name = 'Drupal\\Component\\Annotation\\Plugin', array $annotation_namespaces = []) {
        $this->pluginNamespaces = $plugin_namespaces;
        $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
        $this->annotationNamespaces = $annotation_namespaces;
        $file_cache_suffix = str_replace('\\', '_', $plugin_definition_annotation_name);
        $file_cache_suffix .= ':' . Crypt::hashBase64(serialize($annotation_namespaces));
        $this->fileCache = FileCacheFactory::get('annotation_discovery:' . $file_cache_suffix);
    }
    
    /**
     * Gets the used doctrine annotation reader.
     *
     * @return \Doctrine\Common\Annotations\Reader
     *   The annotation reader.
     */
    protected function getAnnotationReader() {
        if (!isset($this->annotationReader)) {
            $this->annotationReader = new SimpleAnnotationReader();
            // Add the namespaces from the main plugin annotation, like @EntityType.
            $namespace = substr($this->pluginDefinitionAnnotationName, 0, strrpos($this->pluginDefinitionAnnotationName, '\\'));
            $this->annotationReader
                ->addNamespace($namespace);
            // Register additional namespaces to be scanned for annotations.
            foreach ($this->annotationNamespaces as $namespace) {
                $this->annotationReader
                    ->addNamespace($namespace);
            }
        }
        return $this->annotationReader;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getDefinitions() {
        $definitions = [];
        $reader = $this->getAnnotationReader();
        // Clear the annotation loaders of any previous annotation classes.
        AnnotationRegistry::reset();
        // Register the namespaces of classes that can be used for annotations.
        AnnotationRegistry::registerLoader('class_exists');
        // Search for classes within all PSR-4 namespace locations.
        foreach ($this->getPluginNamespaces() as $namespace => $dirs) {
            foreach ($dirs as $dir) {
                if (file_exists($dir)) {
                    $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS));
                    foreach ($iterator as $fileinfo) {
                        if ($fileinfo->getExtension() == 'php') {
                            if ($cached = $this->fileCache
                                ->get($fileinfo->getPathName())) {
                                if (isset($cached['id'])) {
                                    // Explicitly unserialize this to create a new object instance.
                                    $definitions[$cached['id']] = unserialize($cached['content']);
                                }
                                continue;
                            }
                            $sub_path = $iterator->getSubIterator()
                                ->getSubPath();
                            $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
                            $class = $namespace . '\\' . $sub_path . $fileinfo->getBasename('.php');
                            // The filename is already known, so there is no need to find the
                            // file. However, StaticReflectionParser needs a finder, so use a
                            // mock version.
                            $finder = MockFileFinder::create($fileinfo->getPathName());
                            $parser = new StaticReflectionParser($class, $finder, TRUE);
                            
                            /** @var \Drupal\Component\Annotation\AnnotationInterface $annotation */
                            if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
                                $this->prepareAnnotationDefinition($annotation, $class);
                                $id = $annotation->getId();
                                $content = $annotation->get();
                                $definitions[$id] = $content;
                                // Explicitly serialize this to create a new object instance.
                                $this->fileCache
                                    ->set($fileinfo->getPathName(), [
                                    'id' => $id,
                                    'content' => serialize($content),
                                ]);
                            }
                            else {
                                // Store a NULL object, so the file is not parsed again.
                                $this->fileCache
                                    ->set($fileinfo->getPathName(), [
                                    NULL,
                                ]);
                            }
                        }
                    }
                }
            }
        }
        // Don't let annotation loaders pile up.
        AnnotationRegistry::reset();
        return $definitions;
    }
    
    /**
     * Prepares the annotation definition.
     *
     * @param \Drupal\Component\Annotation\AnnotationInterface $annotation
     *   The annotation derived from the plugin.
     * @param string $class
     *   The class used for the plugin.
     */
    protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
        $annotation->setClass($class);
    }
    
    /**
     * Gets an array of PSR-4 namespaces to search for plugin classes.
     *
     * @return string[]
     */
    protected function getPluginNamespaces() {
        return $this->pluginNamespaces;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
AnnotatedClassDiscovery::$annotationNamespaces protected property Additional namespaces to be scanned for annotation classes.
AnnotatedClassDiscovery::$annotationReader protected property The doctrine annotation reader.
AnnotatedClassDiscovery::$fileCache protected property The file cache object.
AnnotatedClassDiscovery::$pluginDefinitionAnnotationName protected property The name of the annotation that contains the plugin definition.
AnnotatedClassDiscovery::$pluginNamespaces protected property The namespaces within which to find plugin classes.
AnnotatedClassDiscovery::getAnnotationReader protected function Gets the used doctrine annotation reader. 1
AnnotatedClassDiscovery::getDefinitions public function Gets the definition of all plugins for this type. Overrides DiscoveryTrait::getDefinitions 1
AnnotatedClassDiscovery::getPluginNamespaces protected function Gets an array of PSR-4 namespaces to search for plugin classes. 1
AnnotatedClassDiscovery::prepareAnnotationDefinition protected function Prepares the annotation definition. 1
AnnotatedClassDiscovery::__construct public function Constructs a new instance. 1
DiscoveryTrait::doGetDefinition protected function Gets a specific plugin definition.
DiscoveryTrait::getDefinition public function 3
DiscoveryTrait::hasDefinition public function

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.