AnnotatedClassDiscovery.php 4.81 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery.
6 7 8 9
 */

namespace Drupal\Core\Plugin\Discovery;

10
use Drupal\Component\Annotation\AnnotationInterface;
11
use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery;
12
use Drupal\Component\Utility\Unicode;
13 14 15 16

/**
 * Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
 */
17
class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
18

19
  /**
20 21
   * A suffix to append to each PSR-4 directory associated with a base
   * namespace, to form the directories where plugins are found.
22
   *
23 24 25 26 27 28 29
   * @var string
   */
  protected $directorySuffix = '';

  /**
   * A suffix to append to each base namespace, to obtain the namespaces where
   * plugins are found.
30 31 32
   *
   * @var string
   */
33
  protected $namespaceSuffix = '';
34 35

  /**
36
   * A list of base namespaces with their PSR-4 directories.
37 38 39 40 41
   *
   * @var \Traversable
   */
  protected $rootNamespacesIterator;

42 43
  /**
   * Constructs an AnnotatedClassDiscovery object.
44
   *
45
   * @param string $subdir
46 47
   *   Either the plugin's subdirectory, for example 'Plugin/views/filter', or
   *   empty string if plugins are located at the top level of the namespace.
48 49
   * @param \Traversable $root_namespaces
   *   An object that implements \Traversable which contains the root paths
50 51
   *   keyed by the corresponding namespace to look for plugin implementations.
   *   If $subdir is not an empty string, it will be appended to each namespace.
52 53 54
   * @param string $plugin_definition_annotation_name
   *   (optional) The name of the annotation that contains the plugin definition.
   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
55 56
   * @param string[] $annotation_namespaces
   *   (optional) Additional namespaces to scan for annotation definitions.
57
   */
58
  function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', array $annotation_namespaces = []) {
59
    if ($subdir) {
60 61 62 63 64 65 66
      // Prepend a directory separator to $subdir,
      // if it does not already have one.
      if ('/' !== $subdir[0]) {
        $subdir = '/' . $subdir;
      }
      $this->directorySuffix = $subdir;
      $this->namespaceSuffix = str_replace('/', '\\', $subdir);
67
    }
68
    $this->rootNamespacesIterator = $root_namespaces;
69
    $plugin_namespaces = array();
70
    parent::__construct($plugin_namespaces, $plugin_definition_annotation_name, $annotation_namespaces);
71 72
  }

73 74 75 76 77 78 79 80
  /**
   * {@inheritdoc}
   */
  protected function getAnnotationReader() {
    if (!isset($this->annotationReader)) {
      $reader = parent::getAnnotationReader();

      // Add the Core annotation classes like @Translation.
81
      $reader->addNamespace('Drupal\Core\Annotation');
82 83 84 85 86
      $this->annotationReader = $reader;
    }
    return $this->annotationReader;
  }

87 88 89
  /**
   * {@inheritdoc}
   */
90 91 92 93 94
  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
    parent::prepareAnnotationDefinition($annotation, $class);

    if (!$annotation->getProvider()) {
      $annotation->setProvider($this->getProviderFromNamespace($class));
95 96 97 98
    }
  }

  /**
99
   * Extracts the provider name from a Drupal namespace.
100 101
   *
   * @param string $namespace
102
   *   The namespace to extract the provider from.
103 104
   *
   * @return string|null
105
   *   The matching provider name, or NULL otherwise.
106
   */
107 108
  protected function getProviderFromNamespace($namespace) {
    preg_match('|^Drupal\\\\(?<provider>[\w]+)\\\\|', $namespace, $matches);
109

110
    if (isset($matches['provider'])) {
111
      return Unicode::strtolower($matches['provider']);
112 113 114 115 116
    }

    return NULL;
  }

117 118 119 120 121
  /**
   * {@inheritdoc}
   */
  protected function getPluginNamespaces() {
    $plugin_namespaces = array();
122 123 124
    if ($this->namespaceSuffix) {
      foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
        // Append the namespace suffix to the base namespace, to obtain the
125
        // plugin namespace; for example, 'Drupal\Views' may become
126 127 128 129
        // 'Drupal\Views\Plugin\Block'.
        $namespace .= $this->namespaceSuffix;
        foreach ((array) $dirs as $dir) {
          // Append the directory suffix to the PSR-4 base directory, to obtain
130 131
          // the directory where plugins are found. For example,
          // DRUPAL_ROOT . '/core/modules/views/src' may become
132 133 134 135 136 137 138 139 140 141 142
          // DRUPAL_ROOT . '/core/modules/views/src/Plugin/Block'.
          $plugin_namespaces[$namespace][] = $dir . $this->directorySuffix;
        }
      }
    }
    else {
      // Both the namespace suffix and the directory suffix are empty,
      // so the plugin namespaces and directories are the same as the base
      // directories.
      foreach ($this->rootNamespacesIterator as $namespace => $dirs) {
        $plugin_namespaces[$namespace] = (array) $dirs;
143
      }
144 145 146 147 148
    }

    return $plugin_namespaces;
  }

149
}