diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php b/core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php index a54e86087552fbb9444604830c9e948e019edb52..e4d2a51a8533b123d0cbe699f45f17ff4c3067d1 100644 --- a/core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php +++ b/core/lib/Drupal/Core/Plugin/Discovery/AttributeDiscoveryWithAnnotations.php @@ -114,7 +114,7 @@ protected function parseClass(string $class, \SplFileInfo $fileinfo): array { * @see \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery::prepareAnnotationDefinition() * @see \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery::prepareAnnotationDefinition() */ - private function prepareAnnotationDefinition(AnnotationInterface $annotation, string $class): void { + protected function prepareAnnotationDefinition(AnnotationInterface $annotation, string $class): void { $annotation->setClass($class); if (!$annotation->getProvider()) { $annotation->setProvider($this->getProviderFromNamespace($class)); @@ -133,7 +133,7 @@ private function prepareAnnotationDefinition(AnnotationInterface $annotation, st * @see \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery::getAnnotationReader() * @see \Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery::getAnnotationReader() */ - private function getAnnotationReader() : SimpleAnnotationReader { + protected function getAnnotationReader() : SimpleAnnotationReader { if (!isset($this->annotationReader)) { $this->annotationReader = new SimpleAnnotationReader(); diff --git a/core/modules/book/src/Plugin/migrate/destination/Book.php b/core/modules/book/src/Plugin/migrate/destination/Book.php index dcc5056c89af907650b90e5b02d5bb2df8531456..2f4ea33490660065bb02e2b8eab863d6d67c1865 100644 --- a/core/modules/book/src/Plugin/migrate/destination/Book.php +++ b/core/modules/book/src/Plugin/migrate/destination/Book.php @@ -3,15 +3,14 @@ namespace Drupal\book\Plugin\migrate\destination; use Drupal\Core\Entity\EntityInterface; +use Drupal\migrate\Attribute\MigrateDestination; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; use Drupal\migrate\Row; /** - * @MigrateDestination( - * id = "book", - * provider = "book" - * ) + * Provides migrate destination plugin for Book content. */ +#[MigrateDestination('book')] class Book extends EntityContentBase { /** diff --git a/core/modules/datetime/src/Plugin/migrate/field/DateField.php b/core/modules/datetime/src/Plugin/migrate/field/DateField.php index f6a33434d15bc057aee6650d7e43c49d0bb45699..9bfa3572e31e76774d86b4fa917e847eda5e923d 100644 --- a/core/modules/datetime/src/Plugin/migrate/field/DateField.php +++ b/core/modules/datetime/src/Plugin/migrate/field/DateField.php @@ -6,25 +6,25 @@ use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\Row; +use Drupal\migrate_drupal\Attribute\MigrateField; use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase; // cspell:ignore todate /** * Provides a field plugin for date and time fields. - * - * @MigrateField( - * id = "datetime", - * type_map = { - * "date" = "datetime", - * "datestamp" = "timestamp", - * "datetime" = "datetime", - * }, - * core = {6,7}, - * source_module = "date", - * destination_module = "datetime" - * ) */ +#[MigrateField( + id: 'datetime', + core: [6, 7], + type_map: [ + 'date' => 'datetime', + 'datestamp' => 'timestamp', + 'datetime' => 'datetime', + ], + source_module: 'date', + destination_module: 'datetime', +)] class DateField extends FieldPluginBase { /** diff --git a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php index 08b946f53f9abdc20baaaaecc9183163a959a8a0..c953a71869a9e98fae3f1e93d80b24d7edfbfb4e 100644 --- a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php +++ b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php @@ -3,15 +3,15 @@ namespace Drupal\file\Plugin\migrate\destination; use Drupal\Core\Field\Plugin\Field\FieldType\UriItem; +use Drupal\migrate\Attribute\MigrateDestination; use Drupal\migrate\Row; use Drupal\migrate\MigrateException; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; /** - * @MigrateDestination( - * id = "entity:file" - * ) + * Provides migrate destination plugin for File entities. */ +#[MigrateDestination('entity:file')] class EntityFile extends EntityContentBase { /** diff --git a/core/modules/migrate/migrate.api.php b/core/modules/migrate/migrate.api.php index 0339475ff0f79f3b9bc04870a9495a37fdbb6950..9b3ab67bb980ae1aa8361eaee32d9756020b2a39 100644 --- a/core/modules/migrate/migrate.api.php +++ b/core/modules/migrate/migrate.api.php @@ -47,8 +47,8 @@ * @section sec_source Migrate API source plugins * Migrate API source plugins implement * \Drupal\migrate\Plugin\MigrateSourceInterface and usually extend - * \Drupal\migrate\Plugin\migrate\source\SourcePluginBase. They are annotated - * with \Drupal\migrate\Annotation\MigrateSource annotation and must be in + * \Drupal\migrate\Plugin\migrate\source\SourcePluginBase. They have the + * \Drupal\migrate\Attribute\MigrateSource attribute and must be in * namespace subdirectory 'Plugin\migrate\source' under the namespace of the * module that defines them. Migrate API source plugins are managed by the * \Drupal\migrate\Plugin\MigrateSourcePluginManager class. @@ -59,8 +59,8 @@ * @section sec_process Migrate API process plugins * Migrate API process plugins implement * \Drupal\migrate\Plugin\MigrateProcessInterface and usually extend - * \Drupal\migrate\ProcessPluginBase. They are annotated with - * \Drupal\migrate\Annotation\MigrateProcessPlugin annotation and must be in + * \Drupal\migrate\ProcessPluginBase. They have the + * \Drupal\migrate\Attribute\MigrateProcess attribute and must be in * namespace subdirectory 'Plugin\migrate\process' under the namespace of the * module that defines them. Migrate API process plugins are managed by the * \Drupal\migrate\Plugin\MigratePluginManager class. @@ -70,12 +70,11 @@ * @section sec_destination Migrate API destination plugins * Migrate API destination plugins implement * \Drupal\migrate\Plugin\MigrateDestinationInterface and usually extend - * \Drupal\migrate\Plugin\migrate\destination\DestinationBase. They are - * annotated with \Drupal\migrate\Annotation\MigrateDestination annotation and - * must be in namespace subdirectory 'Plugin\migrate\destination' under the - * namespace of the module that defines them. Migrate API destination plugins - * are managed by the \Drupal\migrate\Plugin\MigrateDestinationPluginManager - * class. + * \Drupal\migrate\Plugin\migrate\destination\DestinationBase. They have the + * \Drupal\migrate\Attribute\MigrateDestination attribute and must be in + * namespace subdirectory 'Plugin\migrate\destination' under the namespace of + * the module that defines them. Migrate API destination plugins are managed by + * the \Drupal\migrate\Plugin\MigrateDestinationPluginManager class. * * @link https://api.drupal.org/api/drupal/namespace/Drupal!migrate!Plugin!migrate!destination List of destination plugins for Drupal configuration and content entities provided by the core Migrate module. @endlink * diff --git a/core/modules/migrate/migrate.services.yml b/core/modules/migrate/migrate.services.yml index d7ef2ded4b0db0a2601a128c46174bc80ea73491..b7068cf68250a375e3f099d2969baaaba2557dc6 100644 --- a/core/modules/migrate/migrate.services.yml +++ b/core/modules/migrate/migrate.services.yml @@ -14,7 +14,13 @@ services: arguments: [source, '@container.namespaces', '@cache.discovery', '@module_handler'] plugin.manager.migrate.process: class: Drupal\migrate\Plugin\MigratePluginManager - arguments: [process, '@container.namespaces', '@cache.discovery', '@module_handler', 'Drupal\migrate\Annotation\MigrateProcessPlugin'] + arguments: + - process + - '@container.namespaces' + - '@cache.discovery' + - '@module_handler' + - 'Drupal\migrate\Attribute\MigrateProcess' + - 'Drupal\migrate\Annotation\MigrateProcessPlugin' plugin.manager.migrate.destination: class: Drupal\migrate\Plugin\MigrateDestinationPluginManager arguments: [destination, '@container.namespaces', '@cache.discovery', '@module_handler', '@entity_type.manager'] diff --git a/core/modules/migrate/src/Attribute/MigrateDestination.php b/core/modules/migrate/src/Attribute/MigrateDestination.php new file mode 100644 index 0000000000000000000000000000000000000000..54e469b1381452ba1e0814a8ad69ae08a58dd707 --- /dev/null +++ b/core/modules/migrate/src/Attribute/MigrateDestination.php @@ -0,0 +1,53 @@ +setProviders([$provider]); + } + + /** + * {@inheritdoc} + */ + public function getProviders(): array { + return $this->providers; + } + + /** + * {@inheritdoc} + */ + public function setProviders(array $providers): void { + if ($providers) { + parent::setProvider(reset($providers)); + } + else { + $this->provider = NULL; + } + $this->providers = $providers; + } + +} diff --git a/core/modules/migrate/src/Attribute/MultipleProviderAttributeInterface.php b/core/modules/migrate/src/Attribute/MultipleProviderAttributeInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..437ddfb8b9aeb28d5ff5ff17929e94ebf4208a6b --- /dev/null +++ b/core/modules/migrate/src/Attribute/MultipleProviderAttributeInterface.php @@ -0,0 +1,44 @@ +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} */ diff --git a/core/modules/migrate/src/Plugin/Discovery/AnnotatedDiscoveryAutomatedProvidersTrait.php b/core/modules/migrate/src/Plugin/Discovery/AnnotatedDiscoveryAutomatedProvidersTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..aa9d8745a677f9767347b3ca32507a6a1a3b08da --- /dev/null +++ b/core/modules/migrate/src/Plugin/Discovery/AnnotatedDiscoveryAutomatedProvidersTrait.php @@ -0,0 +1,59 @@ +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_diff(array_unique(array_filter($providers)), ['component']); + $annotation->setProviders($providers); + } + +} diff --git a/core/modules/migrate/src/Plugin/Discovery/AttributeClassDiscoveryAutomatedProviders.php b/core/modules/migrate/src/Plugin/Discovery/AttributeClassDiscoveryAutomatedProviders.php new file mode 100644 index 0000000000000000000000000000000000000000..c5691a57bf9e1a7ffb854f358637bb661baaff5b --- /dev/null +++ b/core/modules/migrate/src/Plugin/Discovery/AttributeClassDiscoveryAutomatedProviders.php @@ -0,0 +1,42 @@ +setClass($class); + + // Loop through all the parent classes and add their providers (which we + // infer by parsing their namespaces) to the $providers array. + $providers = $attribute->getProviders(); + do { + $providers[] = $this->getProviderFromNamespace($class); + } while (($class = get_parent_class($class)) !== FALSE); + + $providers = array_diff(array_unique(array_filter($providers)), ['component']); + $attribute->setProviders($providers); + } + +} diff --git a/core/modules/migrate/src/Plugin/Discovery/AttributeDiscoveryWithAnnotationsAutomatedProviders.php b/core/modules/migrate/src/Plugin/Discovery/AttributeDiscoveryWithAnnotationsAutomatedProviders.php new file mode 100644 index 0000000000000000000000000000000000000000..fab94bf307fecda495906fbd9db11b5850e716a2 --- /dev/null +++ b/core/modules/migrate/src/Plugin/Discovery/AttributeDiscoveryWithAnnotationsAutomatedProviders.php @@ -0,0 +1,83 @@ +finder = new ClassFinder(); + $this->attributeDiscovery = new AttributeClassDiscoveryAutomatedProviders($subdir, $rootNamespaces, $pluginDefinitionAttributeName); + } + + /** + * {@inheritdoc} + */ + protected function prepareAttributeDefinition(AttributeInterface $attribute, string $class): void { + $this->attributeDiscovery->prepareAttributeDefinition($attribute, $class); + } + + /** + * {@inheritdoc} + */ + protected function parseClass(string $class, \SplFileInfo $fileinfo): array { + // 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); + + $reflection_class = $parser->getReflectionClass(); + // @todo Handle deprecating definitions discovery via annotations in + // https://www.drupal.org/project/drupal/issues/3265945. + /** @var \Drupal\Component\Annotation\AnnotationInterface $annotation */ + if ($annotation = $this->getAnnotationReader()->getClassAnnotation($reflection_class, $this->pluginDefinitionAnnotationName)) { + $this->prepareAnnotationDefinition($annotation, $class, $parser); + return ['id' => $annotation->getId(), 'content' => $annotation->get()]; + } + + // Annotations use static reflection and are able to analyze a class that + // extends classes or uses traits that do not exist. Attribute discovery + // will trigger a fatal error with such classes, so only call it if the + // class has a class attribute. + if ($reflection_class->hasClassAttribute($this->pluginDefinitionAttributeName)) { + return parent::parseClass($class, $fileinfo); + } + return ['id' => NULL, 'content' => NULL]; + } + +} diff --git a/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php b/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php index d2c7fb7ee9947571214d39999fe0e1a60dff5ec7..d2dbe2f68abd78b7c671ccd5729e2e8b4957e45e 100644 --- a/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php +++ b/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php @@ -13,7 +13,7 @@ * * @see \Drupal\migrate\Plugin\migrate\destination\DestinationBase * @see \Drupal\migrate\Plugin\MigrateDestinationPluginManager - * @see \Drupal\migrate\Annotation\MigrateDestination + * @see \Drupal\migrate\Attribute\MigrateDestination * @see plugin_api * * @ingroup migration diff --git a/core/modules/migrate/src/Plugin/MigrateDestinationPluginManager.php b/core/modules/migrate/src/Plugin/MigrateDestinationPluginManager.php index b65515c3afedb86ec18ea8993edb92ec24eab18d..6b7514435f2920478aafce228ad96065bfd11677 100644 --- a/core/modules/migrate/src/Plugin/MigrateDestinationPluginManager.php +++ b/core/modules/migrate/src/Plugin/MigrateDestinationPluginManager.php @@ -5,13 +5,14 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\migrate\Attribute\MigrateDestination; /** * Plugin manager for migrate destination plugins. * * @see \Drupal\migrate\Plugin\MigrateDestinationInterface * @see \Drupal\migrate\Plugin\migrate\destination\DestinationBase - * @see \Drupal\migrate\Annotation\MigrateDestination + * @see \Drupal\migrate\Attribute\MigrateDestination * @see plugin_api * * @ingroup migration @@ -40,12 +41,15 @@ class MigrateDestinationPluginManager extends MigratePluginManager { * The module handler to invoke the alter hook with. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. + * @param string $attribute + * (optional) The attribute class name. Defaults to + * 'Drupal\migrate\Attribute\MigrateDestination'. * @param string $annotation * (optional) The annotation class name. Defaults to * 'Drupal\migrate\Annotation\MigrateDestination'. */ - public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, $annotation = 'Drupal\migrate\Annotation\MigrateDestination') { - parent::__construct($type, $namespaces, $cache_backend, $module_handler, $annotation); + public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, $attribute = MigrateDestination::class, $annotation = 'Drupal\migrate\Annotation\MigrateDestination') { + parent::__construct($type, $namespaces, $cache_backend, $module_handler, $attribute, $annotation); $this->entityTypeManager = $entity_type_manager; } diff --git a/core/modules/migrate/src/Plugin/MigratePluginManager.php b/core/modules/migrate/src/Plugin/MigratePluginManager.php index b3645dbdd0e41c3bbf3419dfb33bea71bdf8fc7c..9eba756ab1be31715f37f0afc6dbb50cb1b17ca7 100644 --- a/core/modules/migrate/src/Plugin/MigratePluginManager.php +++ b/core/modules/migrate/src/Plugin/MigratePluginManager.php @@ -2,6 +2,8 @@ namespace Drupal\migrate\Plugin; +use Drupal\Component\Plugin\Attribute\AttributeInterface; +use Drupal\Component\Plugin\Attribute\PluginID; use Drupal\Component\Plugin\Factory\DefaultFactory; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -11,10 +13,10 @@ * Manages migrate plugins. * * @see hook_migrate_info_alter() - * @see \Drupal\migrate\Annotation\MigrateSource + * @see \Drupal\migrate\Attribute\MigrateSource * @see \Drupal\migrate\Plugin\MigrateSourceInterface * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase - * @see \Drupal\migrate\Annotation\MigrateProcessPlugin + * @see \Drupal\migrate\Attribute\MigrateProcess * @see \Drupal\migrate\Plugin\MigrateProcessInterface * @see \Drupal\migrate\Plugin\migrate\process\ProcessPluginBase * @see plugin_api @@ -36,12 +38,20 @@ class MigratePluginManager extends DefaultPluginManager implements MigratePlugin * Cache backend instance to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler to invoke the alter hook with. + * @param string $attribute + * (optional) The attribute class name. Defaults to + * 'Drupal\Component\Plugin\Attribute\PluginID'. * @param string $annotation * (optional) The annotation class name. Defaults to * 'Drupal\Component\Annotation\PluginID'. */ - public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $annotation = 'Drupal\Component\Annotation\PluginID') { - parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, NULL, $annotation); + public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $attribute = PluginID::class, $annotation = 'Drupal\Component\Annotation\PluginID') { + if (!is_subclass_of($attribute, AttributeInterface::class)) { + // Backward compatibility. + $annotation = $attribute; + $attribute = PluginID::class; + } + parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, NULL, $attribute, $annotation); $this->alterInfo('migrate_' . $type . '_info'); $this->setCacheBackend($cache_backend, 'migrate_plugins_' . $type); } diff --git a/core/modules/migrate/src/Plugin/MigrateProcessInterface.php b/core/modules/migrate/src/Plugin/MigrateProcessInterface.php index 6b3832c89b6f28fab66ec79aa959184eff8e03d0..7d2d365d881718722568f54bcfc2e266f6e61b54 100644 --- a/core/modules/migrate/src/Plugin/MigrateProcessInterface.php +++ b/core/modules/migrate/src/Plugin/MigrateProcessInterface.php @@ -15,7 +15,7 @@ * * @see \Drupal\migrate\Plugin\MigratePluginManager * @see \Drupal\migrate\ProcessPluginBase - * @see \Drupal\migrate\Annotation\MigrateProcessPlugin + * @see \Drupal\migrate\Attribute\MigrateProcess * @see plugin_api * * @ingroup migration diff --git a/core/modules/migrate/src/Plugin/MigrateSourceInterface.php b/core/modules/migrate/src/Plugin/MigrateSourceInterface.php index f33dc68cbc80f4719860d8c8bd6ffbe7a44f8948..b05236ad369adf239f28201ac03ef6134110f933 100644 --- a/core/modules/migrate/src/Plugin/MigrateSourceInterface.php +++ b/core/modules/migrate/src/Plugin/MigrateSourceInterface.php @@ -9,7 +9,7 @@ * Defines an interface for migrate sources. * * @see \Drupal\migrate\Plugin\MigratePluginManager - * @see \Drupal\migrate\Annotation\MigrateSource + * @see \Drupal\migrate\Attribute\MigrateSource * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * @see plugin_api * diff --git a/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php b/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php index 965da5dd766e49770b75ed347ffb933ca2b19972..74fb59b5944781d0b707ba27246700fdb862b392 100644 --- a/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php +++ b/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php @@ -4,8 +4,10 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\migrate\Plugin\Discovery\AnnotatedClassDiscoveryAutomatedProviders; use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator; +use Drupal\migrate\Plugin\Discovery\AnnotatedClassDiscoveryAutomatedProviders; +use Drupal\migrate\Plugin\Discovery\AttributeClassDiscoveryAutomatedProviders; +use Drupal\migrate\Plugin\Discovery\AttributeDiscoveryWithAnnotationsAutomatedProviders; use Drupal\migrate\Plugin\Discovery\ProviderFilterDecorator; /** @@ -13,7 +15,7 @@ * * @see \Drupal\migrate\Plugin\MigrateSourceInterface * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase - * @see \Drupal\migrate\Annotation\MigrateSource + * @see \Drupal\migrate\Attribute\MigrateSource * @see plugin_api * * @ingroup migration @@ -35,7 +37,7 @@ class MigrateSourcePluginManager extends MigratePluginManager { * The module handler to invoke the alter hook with. */ public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { - parent::__construct($type, $namespaces, $cache_backend, $module_handler, 'Drupal\migrate\Annotation\MigrateSource'); + parent::__construct($type, $namespaces, $cache_backend, $module_handler, 'Drupal\migrate\Attribute\MigrateSource', 'Drupal\migrate\Annotation\MigrateSource'); } /** @@ -43,7 +45,30 @@ public function __construct($type, \Traversable $namespaces, CacheBackendInterfa */ protected function getDiscovery() { if (!$this->discovery) { - $discovery = new AnnotatedClassDiscoveryAutomatedProviders($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces); + if (isset($this->pluginDefinitionAttributeName) && isset($this->pluginDefinitionAnnotationName)) { + $discovery = new AttributeDiscoveryWithAnnotationsAutomatedProviders( + $this->subdir, + $this->namespaces, + $this->pluginDefinitionAttributeName, + $this->pluginDefinitionAnnotationName, + $this->additionalAnnotationNamespaces, + ); + } + elseif (isset($this->pluginDefinitionAttributeName)) { + $discovery = new AttributeClassDiscoveryAutomatedProviders( + $this->subdir, + $this->namespaces, + $this->pluginDefinitionAttributeName, + ); + } + else { + $discovery = new AnnotatedClassDiscoveryAutomatedProviders( + $this->subdir, + $this->namespaces, + $this->pluginDefinitionAnnotationName, + $this->additionalAnnotationNamespaces, + ); + } $this->discovery = new ContainerDerivativeDiscoveryDecorator($discovery); } return $this->discovery; diff --git a/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php b/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php index 4a7fc59d827049809449db427b392f576abd2834..5712613e218bdd8353ff336deed264ce12ddc22e 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php @@ -21,7 +21,7 @@ * information, refer to \Drupal\migrate\Plugin\MigrateDestinationInterface. * * @see \Drupal\migrate\Plugin\MigrateDestinationPluginManager - * @see \Drupal\migrate\Annotation\MigrateDestination + * @see \Drupal\migrate\Attribute\MigrateDestination * @see plugin_api * * @ingroup migration diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/NullIdMap.php b/core/modules/migrate/src/Plugin/migrate/id_map/NullIdMap.php index 80c5da68a5a1e1d6814a2c9e6071dd6c432a590f..a6b2e3339e52333ff9240ad86d60189ac413865f 100644 --- a/core/modules/migrate/src/Plugin/migrate/id_map/NullIdMap.php +++ b/core/modules/migrate/src/Plugin/migrate/id_map/NullIdMap.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\id_map; +use Drupal\Component\Plugin\Attribute\PluginID; use Drupal\Core\Plugin\PluginBase; use Drupal\migrate\MigrateMessageInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -12,9 +13,8 @@ * Defines the null ID map implementation. * * This serves as a dummy in order to not store anything. - * - * @PluginID("null") */ +#[PluginID('null')] class NullIdMap extends PluginBase implements MigrateIdMapInterface { /** diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php index b9261d098943762007b561e63afa302b293de1ba..d66d3ce2fb2b34d291d8cb3308e9e22ad469d3d3 100644 --- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php +++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\id_map; +use Drupal\Component\Plugin\Attribute\PluginID; use Drupal\Core\Database\DatabaseException; use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Database\Exception\SchemaTableKeyTooLargeException; @@ -30,9 +31,8 @@ * * It creates one map and one message table per migration entity to store the * relevant information. - * - * @PluginID("sql") */ +#[PluginID('sql')] class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface, HighestIdInterface { /** diff --git a/core/modules/migrate/src/Plugin/migrate/process/Explode.php b/core/modules/migrate/src/Plugin/migrate/process/Explode.php index 38a58ed4a405e5ad374481707df3fdaca98a1094..c6d9024926593e6c97d5802d2d37e35cd0c12415 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/Explode.php +++ b/core/modules/migrate/src/Plugin/migrate/process/Explode.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\process; +use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\MigrateException; use Drupal\migrate\MigrateExecutableInterface; @@ -86,11 +87,8 @@ * configuration, if foo is '', NULL or FALSE, then bar will be []. * * @see \Drupal\migrate\Plugin\MigrateProcessInterface - * - * @MigrateProcessPlugin( - * id = "explode" - * ) */ +#[MigrateProcess('explode')] class Explode extends ProcessPluginBase { /** diff --git a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php index a61a951f3e055bd13193b6b852db1afa0a606855..251df66accba95e1dfe56afe557114275280cfe5 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php +++ b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\source; +use Drupal\migrate\Attribute\MigrateSource; use Drupal\migrate\Plugin\MigrationInterface; /** @@ -40,12 +41,11 @@ * * For additional configuration keys, refer to the parent class: * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase - * - * @MigrateSource( - * id = "embedded_data", - * source_module = "migrate" - * ) */ +#[MigrateSource( + id: 'embedded_data', + source_module: 'migrate' +)] class EmbeddedDataSource extends SourcePluginBase { /** diff --git a/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php b/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php index 2991aeec7a2626f58eb45680c9e8c876216365d9..ee56876cdc0b391e65bb27f2489dcda7d1fe8814 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php +++ b/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php @@ -2,6 +2,8 @@ namespace Drupal\migrate\Plugin\migrate\source; +use Drupal\migrate\Attribute\MigrateSource; + /** * Source returning a row based on the constants provided. * @@ -21,12 +23,11 @@ * * For additional configuration keys, refer to the parent class: * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase - * - * @MigrateSource( - * id = "empty", - * source_module = "migrate" - * ) */ +#[MigrateSource( + id: 'empty', + source_module: 'migrate', +)] class EmptySource extends SourcePluginBase { /** diff --git a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php index e6d5d786dd7fb8f70b5090ba1fafde544c659ff4..e98d41975d036c68fee07c800c8f6493cab7fc30 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php +++ b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php @@ -105,7 +105,7 @@ * In this example, the constant 'foo' is defined with a value of 'bar'. It is * later used in the process pipeline to set the value of the field baz. * - * @see \Drupal\migrate\Annotation\MigrateSource + * @see \Drupal\migrate\Attribute\MigrateSource * @see \Drupal\migrate\Plugin\MigrateIdMapInterface * @see \Drupal\migrate\Plugin\MigratePluginManager * @see \Drupal\migrate\Plugin\MigrateSourceInterface diff --git a/core/modules/migrate/src/ProcessPluginBase.php b/core/modules/migrate/src/ProcessPluginBase.php index 5c6978ec335d269ee81ad59880882a8d34d6db7a..e7b1fcc83126d0b884f0ad00b1f422707698e058 100644 --- a/core/modules/migrate/src/ProcessPluginBase.php +++ b/core/modules/migrate/src/ProcessPluginBase.php @@ -21,7 +21,7 @@ * @see https://www.drupal.org/node/2129651 * @see \Drupal\migrate\Plugin\MigratePluginManager * @see \Drupal\migrate\Plugin\MigrateProcessInterface - * @see \Drupal\migrate\Annotation\MigrateProcessPlugin + * @see \Drupal\migrate\Attribute\MigrateProcess * @see \Drupal\migrate\Plugin\migrate\process\SkipOnEmpty * @see d7_field_formatter_settings.yml * @see plugin_api diff --git a/core/modules/migrate/tests/modules/migrate_high_water_test/src/Plugin/migrate/source/HighWaterTest.php b/core/modules/migrate/tests/modules/migrate_high_water_test/src/Plugin/migrate/source/HighWaterTest.php index 601bbd942b785d2eb8628586959205b84e75b8e3..eff8f8fcf841efe2a2aae973d2cc2282cd18a19c 100644 --- a/core/modules/migrate/tests/modules/migrate_high_water_test/src/Plugin/migrate/source/HighWaterTest.php +++ b/core/modules/migrate/tests/modules/migrate_high_water_test/src/Plugin/migrate/source/HighWaterTest.php @@ -2,15 +2,15 @@ namespace Drupal\migrate_high_water_test\Plugin\migrate\source; +use Drupal\migrate\Attribute\MigrateSource; use Drupal\migrate\Plugin\migrate\source\SqlBase; /** * Source plugin for migration high water tests. - * - * @MigrateSource( - * id = "high_water_test" - * ) */ +#[MigrateSource( + id: "high_water_test", +)] class HighWaterTest extends SqlBase { /** diff --git a/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/migrate_source_annotation_bc_test.info.yml b/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/migrate_source_annotation_bc_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..a87b6c047ad28b21b3050b94c32eeecc9dab106e --- /dev/null +++ b/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/migrate_source_annotation_bc_test.info.yml @@ -0,0 +1,5 @@ +name: 'Migrate module source annotation bc tests' +type: module +description: 'Support module for source plugin annotation discovery backwards compatibility tests' +package: Testing +version: VERSION diff --git a/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/src/Plugin/migrate/source/MigrateSourceWithAnnotations.php b/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/src/Plugin/migrate/source/MigrateSourceWithAnnotations.php new file mode 100644 index 0000000000000000000000000000000000000000..145adcaa59a8227769db46ec81db1678539be314 --- /dev/null +++ b/core/modules/migrate/tests/modules/migrate_source_annotation_bc_test/src/Plugin/migrate/source/MigrateSourceWithAnnotations.php @@ -0,0 +1,50 @@ +container->get('plugin.manager.migrate.source')->getDefinitions(); + ksort($source_plugins); + $this->assertSame($expected, array_keys($source_plugins)); + + // Next, test discovery of both attributed and annotated plugins. The + // annotated plugin with multiple providers depends on migrate_drupal and + // should not be discovered with it uninstalled. + $expected = ['annotated', 'embedded_data', 'empty']; + $this->enableModules(['migrate_source_annotation_bc_test']); + $source_plugins = $this->container->get('plugin.manager.migrate.source')->getDefinitions(); + ksort($source_plugins); + $this->assertSame($expected, array_keys($source_plugins)); + + // Install migrate_drupal and now the annotated plugin that depends on it + // should be discovered. + $expected = [ + 'annotated', + 'annotated_multiple_providers', + 'embedded_data', + 'empty', + ]; + $this->enableModules(['migrate_drupal']); + $source_plugins = $this->container->get('plugin.manager.migrate.source')->getDefinitions(); + // Confirming here the that the source plugins that migrate and + // migrate_source_annotation_bc_test are discovered. There are additional + // plugins provided by migrate_drupal, but they do not need to be enumerated + // here. + $this->assertSame(array_diff($expected, array_keys($source_plugins)), []); + } + +} diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml index 80444d04750bfa4e85d1b16c105722b32f4506c3..9e3662341b980814c50a3e7fb5713179dc8a31b2 100644 --- a/core/modules/migrate_drupal/migrate_drupal.services.yml +++ b/core/modules/migrate_drupal/migrate_drupal.services.yml @@ -6,6 +6,7 @@ services: - '@container.namespaces' - '@cache.discovery' - '@module_handler' + - '\Drupal\migrate_drupal\Attribute\MigrateField' - '\Drupal\migrate_drupal\Annotation\MigrateField' Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface: '@plugin.manager.migrate.field' logger.channel.migrate_drupal: diff --git a/core/modules/migrate_drupal/src/Attribute/MigrateField.php b/core/modules/migrate_drupal/src/Attribute/MigrateField.php new file mode 100644 index 0000000000000000000000000000000000000000..ffb529e98000885922b5d6ad79953aa8021a74f0 --- /dev/null +++ b/core/modules/migrate_drupal/src/Attribute/MigrateField.php @@ -0,0 +1,74 @@ +