Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workflow] Add a way to add more definition validator #54398

Open
wants to merge 1 commit into
base: 7.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -183,6 +183,7 @@
use Symfony\Component\Webhook\Controller\WebhookController;
use Symfony\Component\WebLink\HttpHeaderSerializer;
use Symfony\Component\Workflow;
use Symfony\Component\Workflow\Validator\ConfiguredDefinitionValidatorInterface;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand;
use Symfony\Component\Yaml\Yaml;
Expand Down Expand Up @@ -1010,6 +1011,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
$definitionDefinition->addArgument($transitions);
$definitionDefinition->addArgument($initialMarking);
$definitionDefinition->addArgument(new Reference(sprintf('%s.metadata_store', $workflowId)));
$definitionDefinition->addTag('workflow.definition', ['name' => $name]);

// Create MarkingStore
$markingStoreDefinition = null;
Expand Down Expand Up @@ -1104,6 +1106,9 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
$container->setParameter('workflow.has_guard_listeners', true);
}
}

$container->registerForAutoconfiguration(ConfiguredDefinitionValidatorInterface::class)
->addTag('workflow.definition_validator');
}

private function registerDebugConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -70,6 +70,7 @@
use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass;
use Symfony\Component\VarExporter\Internal\Hydrator;
use Symfony\Component\VarExporter\Internal\Registry;
use Symfony\Component\Workflow\DependencyInjection\DefinitionValidatorPass;
use Symfony\Component\Workflow\DependencyInjection\WorkflowDebugPass;
use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass;

Expand Down Expand Up @@ -158,6 +159,7 @@ public function build(ContainerBuilder $container): void
$container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING);
$this->addCompilerPassIfExists($container, FormPass::class);
$this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class);
$this->addCompilerPassIfExists($container, DefinitionValidatorPass::class);
$container->addCompilerPass(new ResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
$container->addCompilerPass(new RegisterLocaleAwareServicesPass());
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
Expand Down
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Workflow\CacheWarmer\DefinitionValidatorCacheWarmer;
use Symfony\Component\Workflow\EventListener\ExpressionLanguage;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Component\Workflow\Registry;
Expand Down Expand Up @@ -42,5 +43,10 @@
->set('workflow.registry', Registry::class)
->alias(Registry::class, 'workflow.registry')
->set('workflow.security.expression_language', ExpressionLanguage::class)
->set('workflow.cache_warmer.definition_validator', DefinitionValidatorCacheWarmer::class)
->args([
abstract_arg('definition and validators'),
])
->tag('kernel.cache_warmer')
;
};
@@ -0,0 +1,25 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Workflow\CacheWarmer;

use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface;

final class DefinitionAndValidator
{
public function __construct(
public readonly DefinitionValidatorInterface $validator,
public readonly Definition $definition,
public readonly string $name,
) {
}
}
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Workflow\CacheWarmer;

use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;

final class DefinitionValidatorCacheWarmer implements CacheWarmerInterface
{
/**
* @param iterable<DefinitionAndValidator> $definitionAndValidators
*/
public function __construct(
private readonly iterable $definitionAndValidators,
) {
}

public function isOptional(): bool
{
return false;
}

public function warmUp(string $cacheDir, ?string $buildDir = null): array
{
foreach ($this->definitionAndValidators as $definitionAndValidator) {
$definitionAndValidator
->validator
->validate($definitionAndValidator->definition, $definitionAndValidator->name)
;
}

return [];
}
}
@@ -0,0 +1,84 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Workflow\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Workflow\CacheWarmer\DefinitionAndValidator;
use Symfony\Component\Workflow\Validator\ConfiguredDefinitionValidatorInterface;

/**
* @author Gr茅goire Pineau <lyrixx@lyrixx.info>
*/
class DefinitionValidatorPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->hasDefinition('workflow.cache_warmer.definition_validator')) {
return;
}

$definitions = [];
foreach ($container->findTaggedServiceIds('workflow.definition') as $id => $attributes) {
$name = $attributes[0]['name'] ?? throw new InvalidArgumentException(sprintf('The "name" attribute is mandatory for the "workflow.definition" tag. Check the tag for service "%s".', $id));
$definitions[$name] = new Reference($id);
}

$definitionAndValidators = [];
foreach ($container->findTaggedServiceIds('workflow.definition_validator') as $id => $attributes) {
$def = $container->getDefinition($id);

$class = $def->getClass();

if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(ConfiguredDefinitionValidatorInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, ConfiguredDefinitionValidatorInterface::class));
}

foreach ($class::getSupportedWorkflows() as $name) {

Check failure on line 52 in src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php:52:22: UndefinedClass: Type null cannot be called as a class (see https://psalm.dev/019)

Check failure on line 52 in src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php:52:22: UndefinedClass: Type null cannot be called as a class (see https://psalm.dev/019)
if ('*' === $name) {
foreach ($definitions as $definitionName => $definition) {
$definitionAndValidators[] = new Definition(
DefinitionAndValidator::class,
[
new Reference($id),
$definition,
$definitionName,
]
);
}
} elseif (isset($definitions[$name])) {
$definitionAndValidators[] = new Definition(
DefinitionAndValidator::class,
[
new Reference($id),
$definitions[$name],
$name,
]
);
} else {
throw new InvalidArgumentException(sprintf('The workflow "%s" does not exist. Check the "getConfiguration()" method of the service "%s".', $name, $id));
}
}
}

$container
->getDefinition('workflow.cache_warmer.definition_validator')
->replaceArgument(0, $definitionAndValidators)
;
}
}
@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Workflow\Validator;

/**
* @author Gr茅goire Pineau <lyrixx@lyrixx.info>
*/
interface ConfiguredDefinitionValidatorInterface extends DefinitionValidatorInterface
{
/**
* @return list<string> A list of workflow name, or "*" for all workflows
*/
public static function getSupportedWorkflows(): iterable;
}