From 52d118ad0b6dfa05f3d323b6f847d49ddcd55070 Mon Sep 17 00:00:00 2001 From: Nicolas Joubert Date: Tue, 20 Oct 2026 14:59:49 +0200 Subject: [PATCH] #312 - Read-only interfaces for the process manager and the registries - New ProcessManagerInterface (execute()), ProcessConfigurationRegistryInterface (getProcessConfiguration(), getProcessConfigurations(), hasProcessConfiguration()), TransformerRegistryInterface (getTransformer(), getTransformers(), hasTransformer(), not the deprecated addTransformer()) and TaskRegistryInterface (getTask()), implemented by the existing classes. - Autowiring aliases for the four interfaces. - Tests, documentation (advanced workflow) and CHANGELOG updated. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + config/services/manager.yaml | 2 + config/services/registry.yaml | 6 +++ docs/04-advanced_workflow.md | 7 +++- src/Manager/ProcessManager.php | 2 +- src/Manager/ProcessManagerInterface.php | 34 ++++++++++++++++ src/Registry/ProcessConfigurationRegistry.php | 6 +-- .../ProcessConfigurationRegistryInterface.php | 39 +++++++++++++++++++ src/Registry/TaskRegistry.php | 2 +- src/Registry/TaskRegistryInterface.php | 28 +++++++++++++ src/Registry/TransformerRegistry.php | 2 +- src/Registry/TransformerRegistryInterface.php | 37 ++++++++++++++++++ .../CleverAgeProcessExtensionTest.php | 22 +++++++++++ 13 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 src/Manager/ProcessManagerInterface.php create mode 100644 src/Registry/ProcessConfigurationRegistryInterface.php create mode 100644 src/Registry/TaskRegistryInterface.php create mode 100644 src/Registry/TransformerRegistryInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a24625..e0a21922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Latest ------ ## Changes +* [#312](https://github.com/cleverage/process-bundle/issues/312) Add read-only interfaces for the process manager and the registries, implemented by the existing classes and autowired by their interface: `ProcessManagerInterface` (`execute()`), `ProcessConfigurationRegistryInterface` (`getProcessConfiguration()`, `getProcessConfigurations()`, `hasProcessConfiguration()`), `TransformerRegistryInterface` (`getTransformer()`, `getTransformers()`, `hasTransformer()`) and `TaskRegistryInterface` (`getTask()`). Type your services on them: the classes will be `final` in v6.0 (the services of the bundle will then be typed on the interfaces). Update documentation, add tests. * [#305](https://github.com/cleverage/process-bundle/issues/305) Bundle best practices, residues: the generic transformers are no longer autowired (explicit arguments). `ProcessHelpCommand` receives the `TaskRegistry` instead of the service container (passing a container is deprecated). Fetching from the service container a task of the application made public by the `TaskInterface` autoconfiguration is deprecated (`container.private` tag, new internal `PublicTaskDeprecationCompilerPass`); the tasks made public by the application itself are not concerned. Trigger the missing deprecation notices: `CheckSerializerCompilerPass`, `errors` task configuration key (use `error_outputs`). `TaskConfiguration::getErrors()` triggers a single notice on PHP 8.4+. `CleverAgeProcessBundle` is marked `@final since 5.4`. Update documentation, add tests. * [#299](https://github.com/cleverage/process-bundle/issues/299) Require `symfony/polyfill-php83`, `symfony/polyfill-php84` and `symfony/polyfill-php85`: the functions of PHP 8.3 to 8.5 can be used while supporting PHP 8.2. Rector targets PHP 8.2 (`withPhpVersion()`, `withPhpSets()` read from `composer.json`) and applies the rules using a newer function when its polyfill is required: `ProcessConfiguration` uses `array_any()` again; `array_first()` replaces `reset()` / `current()` in `ProcessConfiguration::getMainTask()`, `ConstraintLoader` and `ProcessHelpCommand` (no internal array pointer, same results). * [#279](https://github.com/cleverage/process-bundle/issues/279) Define the public API (Symfony BC promise): the classes not meant to be extended are marked `@final since 5.4` (the engine: `ProcessManager`, `ProcessConfigurationRegistry`, `TransformerRegistry`, `ContextualOptionResolver`; the models: `ProcessState`, `ProcessHistory`, `SubprocessInstance`, the configurations, the events, the `Filesystem` files; the concrete tasks and transformers), they will be `final` in v6.0: extending them triggers a deprecation with the Symfony `DebugClassLoader`. The abstract classes, interfaces, traits, exceptions and the file tasks extended by other tasks (`FileReaderTask`, `LineReaderTask`, `FolderBrowserTask`, `CsvReaderTask`, `InputCsvReaderTask`, `Filesystem\CsvResource`) stay open. `TaskRegistry`, new in this version, is `final`. The infrastructure classes are marked `@internal`: `CleverAgeProcessExtension`, `Configuration`, the commands, `DataQueueEventListener`, `PhpFunctionProvider`, the Monolog processors, `ProcessLogger` and `TaskLogger`. Update documentation. diff --git a/config/services/manager.yaml b/config/services/manager.yaml index a1fe6cfd..c11d9fd7 100644 --- a/config/services/manager.yaml +++ b/config/services/manager.yaml @@ -16,6 +16,8 @@ services: CleverAge\ProcessBundle\Manager\ProcessManager: alias: cleverage_process.manager.process + CleverAge\ProcessBundle\Manager\ProcessManagerInterface: + alias: cleverage_process.manager.process cleverage_process.context.contextual_option_resolver: class: CleverAge\ProcessBundle\Context\ContextualOptionResolver diff --git a/config/services/registry.yaml b/config/services/registry.yaml index 0db42234..73c10514 100644 --- a/config/services/registry.yaml +++ b/config/services/registry.yaml @@ -8,6 +8,8 @@ services: - ~ CleverAge\ProcessBundle\Registry\ProcessConfigurationRegistry: alias: cleverage_process.registry.process_configuration + CleverAge\ProcessBundle\Registry\ProcessConfigurationRegistryInterface: + alias: cleverage_process.registry.process_configuration cleverage_process.registry.transformer: class: CleverAge\ProcessBundle\Registry\TransformerRegistry @@ -16,6 +18,8 @@ services: - ~ # Service locator of the transformers by code, see TransformerLocatorCompilerPass CleverAge\ProcessBundle\Registry\TransformerRegistry: alias: cleverage_process.registry.transformer + CleverAge\ProcessBundle\Registry\TransformerRegistryInterface: + alias: cleverage_process.registry.transformer cleverage_process.registry.task: class: CleverAge\ProcessBundle\Registry\TaskRegistry @@ -25,3 +29,5 @@ services: - '@service_container' # Deprecated fallback, removed in v6.0 CleverAge\ProcessBundle\Registry\TaskRegistry: alias: cleverage_process.registry.task + CleverAge\ProcessBundle\Registry\TaskRegistryInterface: + alias: cleverage_process.registry.task diff --git a/docs/04-advanced_workflow.md b/docs/04-advanced_workflow.md index 0dac8136..2e6720c8 100644 --- a/docs/04-advanced_workflow.md +++ b/docs/04-advanced_workflow.md @@ -29,8 +29,11 @@ the process code, the task code and the original message. The original exception ### Executing a process from PHP -The process manager service (`cleverage_process.manager.process`, private) can be autowired by its class, -`CleverAge\ProcessBundle\Manager\ProcessManager`, or injected explicitly: +The process manager service (`cleverage_process.manager.process`, private) can be autowired by its interface, +`CleverAge\ProcessBundle\Manager\ProcessManagerInterface` (or by its class, `ProcessManager`), or injected +explicitly. Type your services on the interface: the class will be `final` in v6.0 (the services of the bundle will +then be typed on the interfaces, so that they can be decorated). The registries have their interface too: `ProcessConfigurationRegistryInterface`, +`TransformerRegistryInterface` and `TaskRegistryInterface` (namespace `CleverAge\ProcessBundle\Registry`). ```yaml # config/services.yaml diff --git a/src/Manager/ProcessManager.php b/src/Manager/ProcessManager.php index ba9ce711..7e3bba10 100644 --- a/src/Manager/ProcessManager.php +++ b/src/Manager/ProcessManager.php @@ -42,7 +42,7 @@ * * @final since 5.4 */ -class ProcessManager implements ResetInterface +class ProcessManager implements ProcessManagerInterface, ResetInterface { protected const EXECUTE_PROCESS = 1; diff --git a/src/Manager/ProcessManagerInterface.php b/src/Manager/ProcessManagerInterface.php new file mode 100644 index 00000000..234782a5 --- /dev/null +++ b/src/Manager/ProcessManagerInterface.php @@ -0,0 +1,34 @@ + $context + * + * @throws MissingProcessException when the process does not exist + * @throws ProcessFailedException when a task fails with the "stop" error strategy + */ + public function execute(string $processCode, mixed $input = null, array $context = []): mixed; +} diff --git a/src/Registry/ProcessConfigurationRegistry.php b/src/Registry/ProcessConfigurationRegistry.php index 848cea51..915fa392 100644 --- a/src/Registry/ProcessConfigurationRegistry.php +++ b/src/Registry/ProcessConfigurationRegistry.php @@ -24,10 +24,10 @@ * * @final since 5.4 */ -class ProcessConfigurationRegistry +class ProcessConfigurationRegistry implements ProcessConfigurationRegistryInterface { /** - * @var ProcessConfiguration[] + * @var array */ protected array $processConfigurations = []; @@ -53,7 +53,7 @@ public function getProcessConfiguration(string $processCode): ProcessConfigurati } /** - * @return ProcessConfiguration[] + * @return array */ public function getProcessConfigurations(): array { diff --git a/src/Registry/ProcessConfigurationRegistryInterface.php b/src/Registry/ProcessConfigurationRegistryInterface.php new file mode 100644 index 00000000..d727d8cd --- /dev/null +++ b/src/Registry/ProcessConfigurationRegistryInterface.php @@ -0,0 +1,39 @@ + indexed by process code + * + * @throws InvalidProcessConfigurationException when a process configuration is not valid + */ + public function getProcessConfigurations(): array; + + public function hasProcessConfiguration(string $processCode): bool; +} diff --git a/src/Registry/TaskRegistry.php b/src/Registry/TaskRegistry.php index ba000ab3..dc04656d 100644 --- a/src/Registry/TaskRegistry.php +++ b/src/Registry/TaskRegistry.php @@ -24,7 +24,7 @@ * (see TaskLocatorCompilerPass). The fallback container is only used, with a deprecation, for services that are not * in the locator (e.g. a process configuration added at runtime). */ -final class TaskRegistry +final class TaskRegistry implements TaskRegistryInterface { public function __construct( protected ContainerInterface $locator, diff --git a/src/Registry/TaskRegistryInterface.php b/src/Registry/TaskRegistryInterface.php new file mode 100644 index 00000000..227758e7 --- /dev/null +++ b/src/Registry/TaskRegistryInterface.php @@ -0,0 +1,28 @@ + indexed by code + */ + public function getTransformers(): array; + + /** + * @throws MissingTransformerException when the transformer does not exist + */ + public function getTransformer(string $code): TransformerInterface; + + public function hasTransformer(string $code): bool; +} diff --git a/tests/DependencyInjection/CleverAgeProcessExtensionTest.php b/tests/DependencyInjection/CleverAgeProcessExtensionTest.php index a0f5d545..b94004ec 100644 --- a/tests/DependencyInjection/CleverAgeProcessExtensionTest.php +++ b/tests/DependencyInjection/CleverAgeProcessExtensionTest.php @@ -19,9 +19,13 @@ use CleverAge\ProcessBundle\DependencyInjection\CleverAgeProcessExtension; use CleverAge\ProcessBundle\DependencyInjection\Configuration; use CleverAge\ProcessBundle\Manager\ProcessManager; +use CleverAge\ProcessBundle\Manager\ProcessManagerInterface; use CleverAge\ProcessBundle\Model\ProcessStopCheckerInterface; use CleverAge\ProcessBundle\Registry\ProcessConfigurationRegistry; +use CleverAge\ProcessBundle\Registry\ProcessConfigurationRegistryInterface; +use CleverAge\ProcessBundle\Registry\TaskRegistryInterface; use CleverAge\ProcessBundle\Registry\TransformerRegistry; +use CleverAge\ProcessBundle\Registry\TransformerRegistryInterface; use CleverAge\ProcessBundle\Task\DummyTask; use CleverAge\ProcessBundle\Tests\Fixtures\AttributedTask; use CleverAge\ProcessBundle\Tests\Fixtures\AttributedTransformer; @@ -91,6 +95,24 @@ public function testServicesCanBeAutowiredByClassName(): void } } + public function testServicesCanBeAutowiredByInterface(): void + { + $container = new ContainerBuilder(); + (new CleverAgeProcessExtension())->load([], $container); + + foreach ([ + ProcessManagerInterface::class => 'cleverage_process.manager.process', + ProcessConfigurationRegistryInterface::class => 'cleverage_process.registry.process_configuration', + TransformerRegistryInterface::class => 'cleverage_process.registry.transformer', + TaskRegistryInterface::class => 'cleverage_process.registry.task', + ] as $interface => $id) { + self::assertSame($id, (string) $container->getAlias($interface)); + $class = $container->getDefinition($id)->getClass(); + self::assertIsString($class); + self::assertTrue(is_a($class, $interface, true), $class); + } + } + public function testTasksAndTransformersAreAutoconfigured(): void { $container = new ContainerBuilder();