class AttributeDiscoveryWithAnnotations

Same name and namespace in other branches
  1. 10 core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php \Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations

Enables both attribute and annotation discovery for plugin definitions.

Hierarchy

Expanded class hierarchy of AttributeDiscoveryWithAnnotations

3 files declare their use of AttributeDiscoveryWithAnnotations
CKEditor5PluginManager.php in core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php
DefaultPluginManager.php in core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
LayoutPluginManager.php in core/lib/Drupal/Core/Layout/LayoutPluginManager.php

File

core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php, line 15

Namespace

Drupal\Core\Plugin\Discovery
View source
class AttributeDiscoveryWithAnnotations extends AttributeClassDiscovery {
    
    /**
     * The doctrine annotation reader.
     *
     * @var \Doctrine\Common\Annotations\Reader
     */
    protected $annotationReader;
    
    /**
     * Constructs an AttributeDiscoveryWithAnnotations object.
     *
     * @param string $subdir
     *   Either the plugin's subdirectory, for example 'Plugin/views/filter', or
     *   empty string if plugins are located at the top level of the namespace.
     * @param \Traversable $rootNamespaces
     *   An object that implements \Traversable which contains the root paths
     *   keyed by the corresponding namespace to look for plugin implementations.
     *   If $subdir is not an empty string, it will be appended to each namespace.
     * @param string $pluginDefinitionAttributeName
     *   (optional) The name of the attribute that contains the plugin definition.
     *   Defaults to 'Drupal\Component\Plugin\Attribute\Plugin'.
     * @param string $pluginDefinitionAnnotationName
     *   (optional) The name of the attribute that contains the plugin definition.
     *   Defaults to 'Drupal\Component\Annotation\Plugin'.
     * @param string[] $additionalNamespaces
     *   (optional) Additional namespaces to scan for attribute definitions.
     */
    public function __construct(string $subdir, \Traversable $rootNamespaces, string $pluginDefinitionAttributeName = 'Drupal\\Component\\Plugin\\Attribute\\Plugin', string $pluginDefinitionAnnotationName = 'Drupal\\Component\\Annotation\\Plugin', array $additionalNamespaces = []) {
        parent::__construct($subdir, $rootNamespaces, $pluginDefinitionAttributeName);
    }
    
    /**
     * {@inheritdoc}
     */
    protected function getFileCacheSuffix(string $default_suffix) : string {
        return $default_suffix . ':' . Crypt::hashBase64(serialize($this->additionalNamespaces)) . ':' . str_replace('\\', '_', $this->pluginDefinitionAnnotationName);
    }
    
    /**
     * {@inheritdoc}
     */
    public function getDefinitions() {
        // Clear the annotation loaders of any previous annotation classes.
        AnnotationRegistry::reset();
        $definitions = parent::getDefinitions();
        $this->annotationReader = NULL;
        return $definitions;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function parseClass(string $class, \SplFileInfo $fileinfo) : array {
        // 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);
        $reflection_class = $parser->getReflectionClass();
        // @todo Handle deprecating definitions discovery via annotations in
        // https://www.drupal.org/project/drupal/issues/3265945.
        
        /** @var \Drupal\Component\Annotation\AnnotationInterface $annotation */
        if ($annotation = $this->getAnnotationReader()
            ->getClassAnnotation($reflection_class, $this->pluginDefinitionAnnotationName)) {
            $this->prepareAnnotationDefinition($annotation, $class);
            return [
                'id' => $annotation->getId(),
                'content' => $annotation->get(),
            ];
        }
        // Annotations use static reflection and are able to analyze a class that
        // extends classes or uses traits that do not exist. Attribute discovery
        // will trigger a fatal error with such classes, so only call it if the
        // class has a class attribute.
        if ($reflection_class->hasClassAttribute($this->pluginDefinitionAttributeName)) {
            return parent::parseClass($class, $fileinfo);
        }
        return [
            'id' => NULL,
            'content' => NULL,
        ];
    }
    
    /**
     * Prepares the annotation definition.
     *
     * This is a copy of the prepareAnnotationDefinition method from annotated
     * class discovery.
     *
     * @param \Drupal\Component\Annotation\AnnotationInterface $annotation
     *   The annotation derived from the plugin.
     * @param class-string $class
     *   The class used for the plugin.
     *
     * @see \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery::prepareAnnotationDefinition()
     * @see \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery::prepareAnnotationDefinition()
     */
    private function prepareAnnotationDefinition(AnnotationInterface $annotation, string $class) : void {
        $annotation->setClass($class);
        if (!$annotation->getProvider()) {
            $annotation->setProvider($this->getProviderFromNamespace($class));
        }
    }
    
    /**
     * Gets the used doctrine annotation reader.
     *
     * This is a copy of the getAnnotationReader method from annotated class
     * discovery.
     *
     * @return \Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader
     *   The annotation reader.
     *
     * @see \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery::getAnnotationReader()
     * @see \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery::getAnnotationReader()
     */
    private function getAnnotationReader() : SimpleAnnotationReader {
        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->additionalNamespaces as $namespace) {
                $this->annotationReader
                    ->addNamespace($namespace);
            }
            // Add the Core annotation classes like @Translation.
            $this->annotationReader
                ->addNamespace('Drupal\\Core\\Annotation');
        }
        return $this->annotationReader;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
AttributeClassDiscovery::$directorySuffix protected property A suffix to append to each PSR-4 directory associated with a base namespace.
AttributeClassDiscovery::$fileCache protected property The file cache object.
AttributeClassDiscovery::$namespaceSuffix protected property A suffix to append to each base namespace.
AttributeClassDiscovery::getPluginNamespaces protected function Gets an array of PSR-4 namespaces to search for plugin classes. Overrides AttributeClassDiscovery::getPluginNamespaces
AttributeClassDiscovery::getProviderFromNamespace protected function Extracts the provider name from a Drupal namespace.
AttributeClassDiscovery::prepareAttributeDefinition protected function Prepares the attribute definition. Overrides AttributeClassDiscovery::prepareAttributeDefinition
AttributeDiscoveryWithAnnotations::$annotationReader protected property The doctrine annotation reader.
AttributeDiscoveryWithAnnotations::getAnnotationReader private function Gets the used doctrine annotation reader.
AttributeDiscoveryWithAnnotations::getDefinitions public function Gets the definition of all plugins for this type. Overrides AttributeClassDiscovery::getDefinitions
AttributeDiscoveryWithAnnotations::getFileCacheSuffix protected function Gets the file cache suffix. Overrides AttributeClassDiscovery::getFileCacheSuffix
AttributeDiscoveryWithAnnotations::parseClass protected function Parses attributes from a class. Overrides AttributeClassDiscovery::parseClass
AttributeDiscoveryWithAnnotations::prepareAnnotationDefinition private function Prepares the annotation definition.
AttributeDiscoveryWithAnnotations::__construct public function Constructs an AttributeDiscoveryWithAnnotations object. Overrides AttributeClassDiscovery::__construct
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.