123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <?php
- namespace Drupal\Component\Annotation\Plugin\Discovery;
- use Drupal\Component\Annotation\AnnotationInterface;
- use Drupal\Component\FileCache\FileCacheFactory;
- use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
- use Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader;
- use Drupal\Component\Annotation\Reflection\MockFileFinder;
- use Doctrine\Common\Annotations\AnnotationRegistry;
- use Doctrine\Common\Reflection\StaticReflectionParser;
- use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
- use Drupal\Component\Utility\Crypt;
- /**
- * Defines a discovery mechanism to find annotated plugins in PSR-4 namespaces.
- */
- 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 $annotation \Drupal\Component\Annotation\AnnotationInterface */
- 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 reparsed 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;
- }
- }
|