annotate core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\migrate\Plugin\Discovery;
Chris@0 4
Chris@0 5 use Doctrine\Common\Annotations\AnnotationRegistry;
Chris@0 6 use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
Chris@0 7 use Drupal\Component\Annotation\AnnotationInterface;
Chris@0 8 use Drupal\Component\Annotation\Reflection\MockFileFinder;
Chris@0 9 use Drupal\Component\ClassFinder\ClassFinder;
Chris@0 10 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
Chris@0 11 use Drupal\migrate\Annotation\MultipleProviderAnnotationInterface;
Chris@0 12
Chris@0 13 /**
Chris@0 14 * Determines providers based on a class's and its parent's namespaces.
Chris@0 15 *
Chris@0 16 * @internal
Chris@0 17 * This is a temporary solution to the fact that migration source plugins have
Chris@0 18 * more than one provider. This functionality will be moved to core in
Chris@0 19 * https://www.drupal.org/node/2786355.
Chris@0 20 */
Chris@0 21 class AnnotatedClassDiscoveryAutomatedProviders extends AnnotatedClassDiscovery {
Chris@0 22
Chris@0 23 /**
Chris@0 24 * A utility object that can use active autoloaders to find files for classes.
Chris@0 25 *
Chris@0 26 * @var \Doctrine\Common\Reflection\ClassFinderInterface
Chris@0 27 */
Chris@0 28 protected $finder;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * Constructs an AnnotatedClassDiscoveryAutomatedProviders object.
Chris@0 32 *
Chris@0 33 * @param string $subdir
Chris@0 34 * Either the plugin's subdirectory, for example 'Plugin/views/filter', or
Chris@0 35 * empty string if plugins are located at the top level of the namespace.
Chris@0 36 * @param \Traversable $root_namespaces
Chris@0 37 * An object that implements \Traversable which contains the root paths
Chris@0 38 * keyed by the corresponding namespace to look for plugin implementations.
Chris@0 39 * If $subdir is not an empty string, it will be appended to each namespace.
Chris@0 40 * @param string $plugin_definition_annotation_name
Chris@0 41 * The name of the annotation that contains the plugin definition.
Chris@0 42 * Defaults to 'Drupal\Component\Annotation\Plugin'.
Chris@0 43 * @param string[] $annotation_namespaces
Chris@0 44 * Additional namespaces to scan for annotation definitions.
Chris@0 45 */
Chris@0 46 public function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', array $annotation_namespaces = []) {
Chris@0 47 parent::__construct($subdir, $root_namespaces, $plugin_definition_annotation_name, $annotation_namespaces);
Chris@0 48 $this->finder = new ClassFinder();
Chris@0 49 }
Chris@0 50
Chris@0 51 /**
Chris@0 52 * {@inheritdoc}
Chris@0 53 */
Chris@0 54 protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class, BaseStaticReflectionParser $parser = NULL) {
Chris@0 55 if (!($annotation instanceof MultipleProviderAnnotationInterface)) {
Chris@0 56 throw new \LogicException('AnnotatedClassDiscoveryAutomatedProviders annotations must implement \Drupal\migrate\Annotation\MultipleProviderAnnotationInterface');
Chris@0 57 }
Chris@0 58 $annotation->setClass($class);
Chris@0 59 $providers = $annotation->getProviders();
Chris@0 60 // Loop through all the parent classes and add their providers (which we
Chris@0 61 // infer by parsing their namespaces) to the $providers array.
Chris@0 62 do {
Chris@0 63 $providers[] = $this->getProviderFromNamespace($parser->getNamespaceName());
Chris@0 64 } while ($parser = StaticReflectionParser::getParentParser($parser, $this->finder));
Chris@0 65 $providers = array_unique(array_filter($providers, function ($provider) {
Chris@0 66 return $provider && $provider !== 'component';
Chris@0 67 }));
Chris@0 68 $annotation->setProviders($providers);
Chris@0 69 }
Chris@0 70
Chris@0 71 /**
Chris@0 72 * {@inheritdoc}
Chris@0 73 */
Chris@0 74 public function getDefinitions() {
Chris@0 75 $definitions = [];
Chris@0 76
Chris@0 77 $reader = $this->getAnnotationReader();
Chris@0 78
Chris@0 79 // Clear the annotation loaders of any previous annotation classes.
Chris@0 80 AnnotationRegistry::reset();
Chris@0 81 // Register the namespaces of classes that can be used for annotations.
Chris@0 82 AnnotationRegistry::registerLoader('class_exists');
Chris@0 83
Chris@0 84 // Search for classes within all PSR-0 namespace locations.
Chris@0 85 foreach ($this->getPluginNamespaces() as $namespace => $dirs) {
Chris@0 86 foreach ($dirs as $dir) {
Chris@0 87 if (file_exists($dir)) {
Chris@0 88 $iterator = new \RecursiveIteratorIterator(
Chris@0 89 new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)
Chris@0 90 );
Chris@0 91 foreach ($iterator as $fileinfo) {
Chris@0 92 if ($fileinfo->getExtension() == 'php') {
Chris@0 93 if ($cached = $this->fileCache->get($fileinfo->getPathName())) {
Chris@0 94 if (isset($cached['id'])) {
Chris@0 95 // Explicitly unserialize this to create a new object instance.
Chris@0 96 $definitions[$cached['id']] = unserialize($cached['content']);
Chris@0 97 }
Chris@0 98 continue;
Chris@0 99 }
Chris@0 100
Chris@0 101 $sub_path = $iterator->getSubIterator()->getSubPath();
Chris@0 102 $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
Chris@0 103 $class = $namespace . '\\' . $sub_path . $fileinfo->getBasename('.php');
Chris@0 104
Chris@0 105 // The filename is already known, so there is no need to find the
Chris@0 106 // file. However, StaticReflectionParser needs a finder, so use a
Chris@0 107 // mock version.
Chris@0 108 $finder = MockFileFinder::create($fileinfo->getPathName());
Chris@0 109 $parser = new BaseStaticReflectionParser($class, $finder, FALSE);
Chris@0 110
Chris@0 111 /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
Chris@0 112 if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
Chris@0 113 $this->prepareAnnotationDefinition($annotation, $class, $parser);
Chris@0 114
Chris@0 115 $id = $annotation->getId();
Chris@0 116 $content = $annotation->get();
Chris@0 117 $definitions[$id] = $content;
Chris@0 118 // Explicitly serialize this to create a new object instance.
Chris@0 119 $this->fileCache->set($fileinfo->getPathName(), ['id' => $id, 'content' => serialize($content)]);
Chris@0 120 }
Chris@0 121 else {
Chris@0 122 // Store a NULL object, so the file is not reparsed again.
Chris@0 123 $this->fileCache->set($fileinfo->getPathName(), [NULL]);
Chris@0 124 }
Chris@0 125 }
Chris@0 126 }
Chris@0 127 }
Chris@0 128 }
Chris@0 129 }
Chris@0 130
Chris@0 131 // Don't let annotation loaders pile up.
Chris@0 132 AnnotationRegistry::reset();
Chris@0 133
Chris@0 134 return $definitions;
Chris@0 135 }
Chris@0 136
Chris@0 137 }