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 }
|