diff core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 129ea1e6d783
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,138 @@
+<?php
+
+namespace Drupal\migrate\Plugin\Discovery;
+
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
+use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Annotation\Reflection\MockFileFinder;
+use Drupal\Component\ClassFinder\ClassFinder;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\migrate\Annotation\MultipleProviderAnnotationInterface;
+
+/**
+ * Determines providers based on a class's and its parent's namespaces.
+ *
+ * @internal
+ *   This is a temporary solution to the fact that migration source plugins have
+ *   more than one provider. This functionality will be moved to core in
+ *   https://www.drupal.org/node/2786355.
+ */
+class AnnotatedClassDiscoveryAutomatedProviders extends AnnotatedClassDiscovery {
+
+  /**
+   * A utility object that can use active autoloaders to find files for classes.
+   *
+   * @var \Doctrine\Common\Reflection\ClassFinderInterface
+   */
+  protected $finder;
+
+  /**
+   * Constructs an AnnotatedClassDiscoveryAutomatedProviders 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 $root_namespaces
+   *   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 $plugin_definition_annotation_name
+   *   The name of the annotation that contains the plugin definition.
+   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param string[] $annotation_namespaces
+   *   Additional namespaces to scan for annotation definitions.
+   */
+  public function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', array $annotation_namespaces = []) {
+    parent::__construct($subdir, $root_namespaces, $plugin_definition_annotation_name, $annotation_namespaces);
+    $this->finder = new ClassFinder();
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class, BaseStaticReflectionParser $parser = NULL) {
+    if (!($annotation instanceof MultipleProviderAnnotationInterface)) {
+      throw new \LogicException('AnnotatedClassDiscoveryAutomatedProviders annotations must implement \Drupal\migrate\Annotation\MultipleProviderAnnotationInterface');
+    }
+    $annotation->setClass($class);
+    $providers = $annotation->getProviders();
+    // Loop through all the parent classes and add their providers (which we
+    // infer by parsing their namespaces) to the $providers array.
+    do {
+      $providers[] = $this->getProviderFromNamespace($parser->getNamespaceName());
+    } while ($parser = StaticReflectionParser::getParentParser($parser, $this->finder));
+    $providers = array_unique(array_filter($providers, function ($provider) {
+      return $provider && $provider !== 'component';
+    }));
+    $annotation->setProviders($providers);
+  }
+
+  /**
+   * {@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-0 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 BaseStaticReflectionParser($class, $finder, FALSE);
+
+              /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
+              if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
+                $this->prepareAnnotationDefinition($annotation, $class, $parser);
+
+                $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;
+  }
+
+}