From e6cf4eca17647f5a9f45a1c8b10a69f01584bedc Mon Sep 17 00:00:00 2001 From: Joachim Noreiko Date: Sun, 25 Jan 2026 13:14:48 +0000 Subject: [PATCH 1/5] Added DI to plugin deriver classes. Fixes #410. --- Generator/PluginClassDiscovery.php | 11 ++- Generator/PluginDeriver.php | 77 +++++++++++++++++++ Generator/PluginYamlDiscovery.php | 11 ++- Test/Unit/ComponentPluginsAttribute11Test.php | 34 +++++++- 4 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 Generator/PluginDeriver.php diff --git a/Generator/PluginClassDiscovery.php b/Generator/PluginClassDiscovery.php index c2a5bcb7..e8bb3f01 100644 --- a/Generator/PluginClassDiscovery.php +++ b/Generator/PluginClassDiscovery.php @@ -91,6 +91,11 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio 'deriver' => PropertyDefinition::create('boolean') ->setLabel('Use deriver') ->setDescription("Adds a deriver class to dynamically derive plugins from a template."), + 'deriver_injected_services' => PropertyDefinition::create('string') + ->setLabel('Deriver injected services') + ->setDescription("Services to inject into the deriver class.") + ->setMultiple(TRUE) + ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')), 'deriver_plain_class_name' => PropertyDefinition::create('string') ->setInternal(TRUE) ->setDefault(DefaultDefinition::create() @@ -236,16 +241,14 @@ public function requiredComponents(): array { if (!empty($this->component_data->deriver->value)) { $components['deriver'] = [ - 'component_type' => 'PHPClassFile', + 'component_type' => 'PluginDeriver', 'class_docblock_lines' => [ 'Plugin deriver for ' . $this->component_data->plugin_name->value . '.', ], 'plain_class_name' => $this->component_data->deriver_plain_class_name->value, 'relative_namespace' => 'Plugin\Derivative', 'parent_class_name' => '\Drupal\Component\Plugin\Derivative\DeriverBase', - 'interfaces' => [ - '\Drupal\Core\Plugin\Discovery\ContainerDeriverInterface', - ], + 'injected_services' => $this->component_data->deriver_injected_services->values(), ]; $components['getDerivativeDefinitions'] = [ diff --git a/Generator/PluginDeriver.php b/Generator/PluginDeriver.php new file mode 100644 index 00000000..1d38bc8d --- /dev/null +++ b/Generator/PluginDeriver.php @@ -0,0 +1,77 @@ + 'base_plugin_id', + 'description' => 'The base plugin ID.', + 'typehint' => 'string', + ], + ]; + + /** + * {@inheritdoc} + */ + public static function addToGeneratorDefinition(PropertyListInterface $definition) { + parent::addToGeneratorDefinition($definition); + + $definition->getProperty('use_static_factory_method') + ->setLiteralDefault(TRUE); + } + + /** + * Produces the class declaration. + */ + function classDeclaration() { + if (!$this->needsDiInterface()) { + // Numeric key will clobber, so make something up! + // TODO: fix! + $this->component_data->interfaces->add(['CLASS_NO_DI_INTERFACE' => static::CLASS_NO_DI_INTERFACE]); + } + + return parent::classDeclaration(); + } + + /** + * {@inheritdoc} + */ + protected function getConstructBaseParameters() { + // Deriver classes do not pass on the $base_plugin_id create() parameter to + // the constructor. + return []; + } + + /** + * {@inheritdoc} + */ + protected function getCreateParameters() { + return static::STANDARD_FIXED_PARAMS; + } + +} diff --git a/Generator/PluginYamlDiscovery.php b/Generator/PluginYamlDiscovery.php index 1bc3d92c..35351351 100644 --- a/Generator/PluginYamlDiscovery.php +++ b/Generator/PluginYamlDiscovery.php @@ -33,6 +33,11 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio 'deriver' => PropertyDefinition::create('boolean') ->setLabel('Use deriver') ->setDescription("Adds a deriver class to dynamically derive plugins from a template."), + 'deriver_injected_services' => PropertyDefinition::create('string') + ->setLabel('Deriver injected services') + ->setDescription("Services to inject into the deriver class.") + ->setMultiple(TRUE) + ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')), 'deriver_plain_class_name' => PropertyDefinition::create('string') ->setInternal(TRUE) ->setDefault(DefaultDefinition::create() @@ -190,16 +195,14 @@ public function requiredComponents(): array { if (!empty($this->component_data->deriver->value)) { $components['deriver'] = [ - 'component_type' => 'PHPClassFile', + 'component_type' => 'PluginDeriver', 'class_docblock_lines' => [ 'Plugin deriver for ' . $this->component_data->plugin_name->value . '.', ], 'plain_class_name' => $this->component_data->deriver_plain_class_name->value, 'relative_namespace' => 'Plugin\Derivative', 'parent_class_name' => '\Drupal\Component\Plugin\Derivative\DeriverBase', - 'interfaces' => [ - '\Drupal\Core\Plugin\Discovery\ContainerDeriverInterface', - ], + 'injected_services' => $this->component_data->deriver_injected_services->values(), ]; $components['getDerivativeDefinitions'] = [ diff --git a/Test/Unit/ComponentPluginsAttribute11Test.php b/Test/Unit/ComponentPluginsAttribute11Test.php index a6bb4691..0077276d 100644 --- a/Test/Unit/ComponentPluginsAttribute11Test.php +++ b/Test/Unit/ComponentPluginsAttribute11Test.php @@ -264,6 +264,8 @@ public static function providerPluginsGenerationNamePrefixing() { /** * Tests plugin derivers. + * + * @group di */ function testPluginsGenerationDeriver() { // Create a module. @@ -293,7 +295,7 @@ function testPluginsGenerationDeriver() { $deriver = $files['src/Plugin/Derivative/AlphaFieldFormatterDeriver.php']; $php_tester = PHPTester::fromCodeFile($this->drupalMajorVersion, $deriver); $php_tester->assertClassHasParent('Drupal\Component\Plugin\Derivative\DeriverBase'); - $php_tester->assertClassHasInterfaces(['Drupal\Core\Plugin\Discovery\ContainerDeriverInterface']); + $php_tester->assertClassHasInterfaces(['Drupal\Component\Plugin\Derivative\DeriverInterface']); $php_tester->assertHasMethod('getDerivativeDefinitions'); // Check the plugin file declares the deriver. @@ -301,6 +303,36 @@ function testPluginsGenerationDeriver() { $php_tester = PHPTester::fromCodeFile($this->drupalMajorVersion, $plugin_file); $php_tester->assertImportsClassLike(['Drupal\test_module\Plugin\Derivative\AlphaFieldFormatterDeriver']); $php_tester->assertClassAttributeHasNamedParameterValue('deriver', 'AlphaFieldFormatterDeriver::class', 'FieldFormatter'); + + // Add DI to the deriver class. + $module_data['plugins'][0]['deriver_injected_services'] = [ + 'current_user', + 'entity_type.manager', + ]; + + $files = $this->generateModuleFiles($module_data); + + $this->assertArrayHasKey('src/Plugin/Derivative/AlphaFieldFormatterDeriver.php', $files); + $deriver = $files['src/Plugin/Derivative/AlphaFieldFormatterDeriver.php']; + + $php_tester = PHPTester::fromCodeFile($this->drupalMajorVersion, $deriver); + $php_tester->assertClassHasParent('Drupal\Component\Plugin\Derivative\DeriverBase'); + $php_tester->assertClassHasInterfaces(['Drupal\Core\Plugin\Discovery\ContainerDeriverInterface']); + // Check service injection. + $php_tester->assertInjectedServicesWithFactory([ + [ + 'typehint' => 'Drupal\Core\Session\AccountProxyInterface', + 'service_name' => 'current_user', + 'property_name' => 'currentUser', + 'parameter_name' => 'current_user', + ], + [ + 'typehint' => 'Drupal\Core\Entity\EntityTypeManagerInterface', + 'service_name' => 'entity_type.manager', + 'property_name' => 'entityTypeManager', + 'parameter_name' => 'entity_type_manager', + ], + ]); } /** From 8a59004c6613aeed7fe65bf81350f516ccfcd708 Mon Sep 17 00:00:00 2001 From: Joachim Noreiko Date: Sun, 25 Jan 2026 15:55:59 +0000 Subject: [PATCH 2/5] Added dependent values system to data properties. --- Definition/PropertyDefinition.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Definition/PropertyDefinition.php b/Definition/PropertyDefinition.php index 1bf4f97d..3635fc01 100644 --- a/Definition/PropertyDefinition.php +++ b/Definition/PropertyDefinition.php @@ -18,6 +18,7 @@ * - Presets * - Processing * - Auto-acquisition + * - Dependency values */ class PropertyDefinition extends BasePropertyDefinition implements PropertyListInterface, \ArrayAccess { @@ -36,6 +37,8 @@ class PropertyDefinition extends BasePropertyDefinition implements PropertyListI protected $autoAcquired = FALSE; + protected ?array $dependentValue = NULL; + /** * {@inheritdoc} */ @@ -322,6 +325,27 @@ public function loadLazyProperties() { } } + /** + * Sets dependent values. + * + * UIs can use these to determine whether to show a property. + * + * @param array $dependent_value + * An array of dependencies which this property may require to be shown. + * Keys are relative addresses. Values are either the target value, or for + * string data, TRUE to represent that the target property must be filled. + * + * @return self + */ + public function setDependencyValue(array $dependent_value): self { + $this->dependentValue = $dependent_value; + return $this; + } + + public function getDependencyValue(): ?array { + return $this->dependentValue; + } + public function offsetExists(mixed $offset): bool { dump($this); throw new \Exception("Accessing definition $this->name as array with offsetExists $offset."); From 5d95f8bdcf73329df88f1c076d8aafe7273d5e55 Mon Sep 17 00:00:00 2001 From: Joachim Noreiko Date: Sun, 25 Jan 2026 15:56:27 +0000 Subject: [PATCH 3/5] Added dependencies on plugin deriver injected services, and parent plugin replacement. --- Generator/PluginClassDiscovery.php | 10 ++++++++-- Generator/PluginYamlDiscovery.php | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Generator/PluginClassDiscovery.php b/Generator/PluginClassDiscovery.php index e8bb3f01..be663289 100644 --- a/Generator/PluginClassDiscovery.php +++ b/Generator/PluginClassDiscovery.php @@ -95,7 +95,10 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio ->setLabel('Deriver injected services') ->setDescription("Services to inject into the deriver class.") ->setMultiple(TRUE) - ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')), + ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')) + ->setDependencyValue([ + '..:deriver' => TRUE, + ]), 'deriver_plain_class_name' => PropertyDefinition::create('string') ->setInternal(TRUE) ->setDefault(DefaultDefinition::create() @@ -126,7 +129,10 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio ), 'replace_parent_plugin' => PropertyDefinition::create('boolean') ->setLabel('Replace parent plugin') - ->setDescription("Replace the parent plugin's class with the generated class, rather than define a new plugin."), + ->setDescription("Replace the parent plugin's class with the generated class, rather than define a new plugin.") + ->setDependencyValue([ + '..:parent_plugin_id' => TRUE, + ]), 'class_docblock_lines' => PropertyDefinition::create('mapping') ->setInternal(TRUE) ->setDefault( diff --git a/Generator/PluginYamlDiscovery.php b/Generator/PluginYamlDiscovery.php index 35351351..ea152412 100644 --- a/Generator/PluginYamlDiscovery.php +++ b/Generator/PluginYamlDiscovery.php @@ -37,7 +37,10 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio ->setLabel('Deriver injected services') ->setDescription("Services to inject into the deriver class.") ->setMultiple(TRUE) - ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')), + ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')) + ->setDependencyValue([ + '..:deriver' => TRUE, + ]), 'deriver_plain_class_name' => PropertyDefinition::create('string') ->setInternal(TRUE) ->setDefault(DefaultDefinition::create() @@ -96,7 +99,10 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio ->setLabel('Injected services for custom class') ->setDescription("Services to inject if using a custom plugin class.") ->setMultiple(TRUE) - ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')), + ->setOptionSetDefinition(\DrupalCodeBuilder\Factory::getTask('ReportServiceData')) + ->setDependencyValue([ + '..:plugin_custom_class' => TRUE, + ]), 'prefix_name' => PropertyDefinition::create('boolean') ->setInternal(TRUE) ->setLiteralDefault(TRUE), From cdac267fc49a0c9efbd900dd6f8e302883f4504c Mon Sep 17 00:00:00 2001 From: Joachim Noreiko Date: Sun, 25 Jan 2026 15:56:43 +0000 Subject: [PATCH 4/5] Added detail to label. --- Generator/RouterItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Generator/RouterItem.php b/Generator/RouterItem.php index c2d2ceee..08913b27 100644 --- a/Generator/RouterItem.php +++ b/Generator/RouterItem.php @@ -56,7 +56,7 @@ public static function addToGeneratorDefinition(PropertyListInterface $definitio ->setLabel('The title for the menu tab') ->setLiteralDefault('My Page'), 'base_route' => PropertyDefinition::create('string') - ->setLabel('Route that this tab shows on') + ->setLabel('Base route that this tab shows on') ]), // TODO: remove this if possible? Probably need to allow PHPClassFile From 40cfb7fc30ac67a134372d0ca1a9c41aaffcf027 Mon Sep 17 00:00:00 2001 From: Joachim Noreiko Date: Sun, 25 Jan 2026 16:07:50 +0000 Subject: [PATCH 5/5] Changed preprocess_ hooks to be available for OO. Fixes #417. --- Task/Collect/HooksCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Task/Collect/HooksCollector.php b/Task/Collect/HooksCollector.php index 14ebecce..d1c99244 100644 --- a/Task/Collect/HooksCollector.php +++ b/Task/Collect/HooksCollector.php @@ -279,7 +279,7 @@ protected function processHookData($hook_file_data) { $procedural = ( in_array($short_name, $obligate_procedural_hooks) || - preg_match('/^(post_update_|preprocess_|process_|update_\d+$)/', $short_name) + preg_match('/^(post_update_|process_|update_\d+$)/', $short_name) ); // Because we're working through the raw data array, we keep the incoming