Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbe94e07

Browse files
committed
[Workflow] Add support for executing custom workflow definition validators during the container compilation
1 parent79fa5f2 commitbe94e07

File tree

14 files changed

+246
-21
lines changed

14 files changed

+246
-21
lines changed

‎src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ CHANGELOG
2525
* Set`framework.rate_limiter.limiters.*.lock_factory` to`auto` by default
2626
* Deprecate`RateLimiterFactory` autowiring aliases, use`RateLimiterFactoryInterface` instead
2727
* Allow configuring compound rate limiters
28+
* Add support for executing custom workflow definition validators during the
29+
container compilation
2830

2931
7.2
3032
---

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
useSymfony\Component\Validator\Validation;
5252
useSymfony\Component\Webhook\Controller\WebhookController;
5353
useSymfony\Component\WebLink\HttpHeaderSerializer;
54+
useSymfony\Component\Workflow\Validator\DefinitionValidatorInterface;
5455
useSymfony\Component\Workflow\WorkflowEvents;
5556

5657
/**
@@ -402,6 +403,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
402403
->useAttributeAsKey('name')
403404
->prototype('array')
404405
->fixXmlConfig('support')
406+
->fixXmlConfig('definition_validator')
405407
->fixXmlConfig('place')
406408
->fixXmlConfig('transition')
407409
->fixXmlConfig('event_to_dispatch','events_to_dispatch')
@@ -436,6 +438,23 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
436438
->end()
437439
->end()
438440
->end()
441+
->arrayNode('definition_validators')
442+
->prototype('scalar')
443+
->cannotBeEmpty()
444+
->validate()
445+
->ifTrue(fn ($v) => !class_exists($v))
446+
->thenInvalid('The validation class %s does not exist.')
447+
->end()
448+
->validate()
449+
->ifTrue(fn ($v) => !is_a($v, DefinitionValidatorInterface::class,true))
450+
->thenInvalid(\sprintf('The validation class %%s is not an instance of %s.', DefinitionValidatorInterface::class))
451+
->end()
452+
->validate()
453+
->ifTrue(fn ($v) =>1 <= (new \ReflectionClass($v))->getConstructor()?->getNumberOfRequiredParameters())
454+
->thenInvalid('The validation class %s constructor must not have any arguments.')
455+
->end()
456+
->end()
457+
->end()
439458
->scalarNode('support_strategy')
440459
->cannotBeEmpty()
441460
->end()

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
11171117
}
11181118
}
11191119
$metadataStoreDefinition->replaceArgument(2,$transitionsMetadataDefinition);
1120-
$container->setDefinition(\sprintf('%s.metadata_store',$workflowId),$metadataStoreDefinition);
1120+
$metadataStoreId =\sprintf('%s.metadata_store',$workflowId);
1121+
$container->setDefinition($metadataStoreId,$metadataStoreDefinition);
11211122

11221123
// Create places
11231124
$places =array_column($workflow['places'],'name');
@@ -1128,7 +1129,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
11281129
$definitionDefinition->addArgument($places);
11291130
$definitionDefinition->addArgument($transitions);
11301131
$definitionDefinition->addArgument($initialMarking);
1131-
$definitionDefinition->addArgument(newReference(\sprintf('%s.metadata_store',$workflowId)));
1132+
$definitionDefinition->addArgument(newReference($metadataStoreId));
1133+
$definitionDefinitionId =\sprintf('%s.definition',$workflowId);
11321134

11331135
// Create MarkingStore
11341136
$markingStoreDefinition =null;
@@ -1142,14 +1144,26 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
11421144
$markingStoreDefinition =newReference($workflow['marking_store']['service']);
11431145
}
11441146

1147+
// Validation
1148+
$workflow['definition_validators'][] =match($workflow['type']) {
1149+
'state_machine' =>Workflow\Validator\StateMachineValidator::class,
1150+
'workflow' =>Workflow\Validator\WorkflowValidator::class,
1151+
default =>thrownew \LogicException('Invalid workflow type.'),
1152+
};
1153+
11451154
// Create Workflow
11461155
$workflowDefinition =newChildDefinition(\sprintf('%s.abstract',$type));
1147-
$workflowDefinition->replaceArgument(0,newReference(\sprintf('%s.definition',$workflowId)));
1156+
$workflowDefinition->replaceArgument(0,newReference($definitionDefinitionId));
11481157
$workflowDefinition->replaceArgument(1,$markingStoreDefinition);
11491158
$workflowDefinition->replaceArgument(3,$name);
11501159
$workflowDefinition->replaceArgument(4,$workflow['events_to_dispatch']);
11511160

1152-
$workflowDefinition->addTag('workflow', ['name' =>$name,'metadata' =>$workflow['metadata']]);
1161+
$workflowDefinition->addTag('workflow', [
1162+
'name' =>$name,
1163+
'metadata' =>$workflow['metadata'],
1164+
'definition_validators' =>$workflow['definition_validators'],
1165+
'definition_id' =>$definitionDefinitionId,
1166+
]);
11531167
if ('workflow' ===$type) {
11541168
$workflowDefinition->addTag('workflow.workflow', ['name' =>$name]);
11551169
}elseif ('state_machine' ===$type) {
@@ -1158,21 +1172,10 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $
11581172

11591173
// Store to container
11601174
$container->setDefinition($workflowId,$workflowDefinition);
1161-
$container->setDefinition(\sprintf('%s.definition',$workflowId),$definitionDefinition);
1175+
$container->setDefinition($definitionDefinitionId,$definitionDefinition);
11621176
$container->registerAliasForArgument($workflowId, WorkflowInterface::class,$name.'.'.$type);
11631177
$container->registerAliasForArgument($workflowId, WorkflowInterface::class,$name);
11641178

1165-
// Validate Workflow
1166-
if ('state_machine' ===$workflow['type']) {
1167-
$validator =newWorkflow\Validator\StateMachineValidator();
1168-
}else {
1169-
$validator =newWorkflow\Validator\WorkflowValidator();
1170-
}
1171-
1172-
$trs =array_map(fn (Reference$ref):Workflow\Transition =>$container->get((string)$ref),$transitions);
1173-
$realDefinition =newWorkflow\Definition($places,$trs,$initialMarking);
1174-
$validator->validate($realDefinition,$name);
1175-
11761179
// Add workflow to Registry
11771180
if ($workflow['supports']) {
11781181
foreach ($workflow['supports']as$supportedClassName) {

‎src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
useSymfony\Component\VarExporter\Internal\Registry;
7878
useSymfony\Component\Workflow\DependencyInjection\WorkflowDebugPass;
7979
useSymfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass;
80+
useSymfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass;
8081

8182
// Help opcache.preload discover always-needed symbols
8283
class_exists(ApcuAdapter::class);
@@ -173,6 +174,7 @@ public function build(ContainerBuilder $container): void
173174
$container->addCompilerPass(newCachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING);
174175
$this->addCompilerPassIfExists($container, FormPass::class);
175176
$this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class);
177+
$this->addCompilerPassIfExists($container, WorkflowValidatorPass::class);
176178
$container->addCompilerPass(newResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
177179
$container->addCompilerPass(newRegisterLocaleAwareServicesPass());
178180
$container->addCompilerPass(newTestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@
449449
<xsd:elementname="initial-marking"type="xsd:string"minOccurs="0"maxOccurs="unbounded" />
450450
<xsd:elementname="marking-store"type="marking_store"minOccurs="0"maxOccurs="1" />
451451
<xsd:elementname="support"type="xsd:string"minOccurs="0"maxOccurs="unbounded" />
452+
<xsd:elementname="definition-validator"type="xsd:string"minOccurs="0"maxOccurs="unbounded" />
452453
<xsd:elementname="event-to-dispatch"type="event_to_dispatch"minOccurs="0"maxOccurs="unbounded" />
453454
<xsd:elementname="place"type="place"minOccurs="0"maxOccurs="unbounded" />
454455
<xsd:elementname="transition"type="transition"minOccurs="0"maxOccurs="unbounded" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespaceSymfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator;
4+
5+
useSymfony\Component\Workflow\Definition;
6+
useSymfony\Component\Workflow\Validator\DefinitionValidatorInterface;
7+
8+
class DefinitionValidatorimplements DefinitionValidatorInterface
9+
{
10+
publicstaticbool$called =false;
11+
12+
publicfunctionvalidate(Definition$definition,string$name):void
13+
{
14+
self::$called =true;
15+
}
16+
}

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
'supports' => [
1414
FrameworkExtensionTestCase::class,
1515
],
16+
'definition_validators' => [
17+
Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator::class,
18+
],
1619
'initial_marking' => ['draft'],
1720
'metadata' => [
1821
'title' =>'article workflow',

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<framework:audit-trailenabled="true"/>
1414
<framework:initial-marking>draft</framework:initial-marking>
1515
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase</framework:support>
16+
<framework:definition-validator>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator</framework:definition-validator>
1617
<framework:placename="draft" />
1718
<framework:placename="wait_for_journalist" />
1819
<framework:placename="approved_by_journalist" />

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ framework:
99
type:workflow
1010
supports:
1111
-Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase
12+
definition_validators:
13+
-Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator
1214
initial_marking:[draft]
1315
metadata:
1416
title:article workflow

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
usePsr\Log\LoggerAwareInterface;
1616
useSymfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
1717
useSymfony\Bundle\FrameworkBundle\FrameworkBundle;
18+
useSymfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator;
1819
useSymfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage;
1920
useSymfony\Bundle\FrameworkBundle\Tests\TestCase;
2021
useSymfony\Bundle\FullStack;
@@ -287,7 +288,11 @@ public function testProfilerCollectSerializerDataEnabled()
287288

288289
publicfunctiontestWorkflows()
289290
{
290-
$container =$this->createContainerFromFile('workflows');
291+
DefinitionValidator::$called =false;
292+
293+
$container =$this->createContainerFromFile('workflows', compile:false);
294+
$container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass());
295+
$container->compile();
291296

292297
$this->assertTrue($container->hasDefinition('workflow.article'),'Workflow is registered as a service');
293298
$this->assertSame('workflow.abstract',$container->getDefinition('workflow.article')->getParent());
@@ -310,6 +315,7 @@ public function testWorkflows()
310315
],$tags['workflow'][0]['metadata'] ??null);
311316

312317
$this->assertTrue($container->hasDefinition('workflow.article.definition'),'Workflow definition is registered as a service');
318+
$this->assertTrue(DefinitionValidator::$called,'DefinitionValidator is called');
313319

314320
$workflowDefinition =$container->getDefinition('workflow.article.definition');
315321

@@ -403,7 +409,9 @@ public function testWorkflowAreValidated()
403409
{
404410
$this->expectException(InvalidDefinitionException::class);
405411
$this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" were found on StateMachine "my_workflow".');
406-
$this->createContainerFromFile('workflow_not_valid');
412+
$container =$this->createContainerFromFile('workflow_not_valid', compile:false);
413+
$container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass());
414+
$container->compile();
407415
}
408416

409417
publicfunctiontestWorkflowCannotHaveBothSupportsAndSupportStrategy()

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function testWorkflowValidationStateMachine()
101101
{
102102
$this->expectException(InvalidDefinitionException::class);
103103
$this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "a_to_b" from place/state "a" were found on StateMachine "article".');
104-
$this->createContainerFromClosure(function ($container) {
104+
$this->createContainerFromClosure(function (ContainerBuilder$container) {
105105
$container->loadFromExtension('framework', [
106106
'annotations' =>false,
107107
'http_method_override' =>false,
@@ -127,9 +127,57 @@ public function testWorkflowValidationStateMachine()
127127
],
128128
],
129129
]);
130+
$container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass());
131+
});
132+
}
133+
134+
/**
135+
* @dataProvider provideWorkflowValidationCustomTests
136+
*/
137+
publicfunctiontestWorkflowValidationCustomBroken(string$class,string$message)
138+
{
139+
$this->expectException(InvalidConfigurationException::class);
140+
$this->expectExceptionMessage($message);
141+
$this->createContainerFromClosure(function ($container)use ($class) {
142+
$container->loadFromExtension('framework', [
143+
'annotations' =>false,
144+
'http_method_override' =>false,
145+
'handle_all_throwables' =>true,
146+
'php_errors' => ['log' =>true],
147+
'workflows' => [
148+
'article' => [
149+
'type' =>'state_machine',
150+
'supports' => [
151+
__CLASS__,
152+
],
153+
'places' => [
154+
'a',
155+
'b',
156+
],
157+
'transitions' => [
158+
'a_to_b' => [
159+
'from' => ['a'],
160+
'to' => ['b'],
161+
],
162+
],
163+
'definition_validators' => [
164+
$class,
165+
],
166+
],
167+
],
168+
]);
130169
});
131170
}
132171

172+
publicstaticfunctionprovideWorkflowValidationCustomTests()
173+
{
174+
yield ['classDoesNotExist','Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "classDoesNotExist" does not exist.'];
175+
176+
yield [\DateTime::class,'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "DateTime" is not an instance of Symfony\Component\Workflow\Validator\DefinitionValidatorInterface.'];
177+
178+
yield [WorkflowValidatorWithConstructor::class,'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "Symfony\\\\Bundle\\\\FrameworkBundle\\\\Tests\\\\DependencyInjection\\\\WorkflowValidatorWithConstructor" constructor must not have any arguments.'];
179+
}
180+
133181
publicfunctiontestWorkflowDefaultMarkingStoreDefinition()
134182
{
135183
$container =$this->createContainerFromClosure(function ($container) {
@@ -366,3 +414,14 @@ public function testRateLimiterCompoundPolicyInvalidLimiters()
366414
});
367415
}
368416
}
417+
418+
class WorkflowValidatorWithConstructorimplements \Symfony\Component\Workflow\Validator\DefinitionValidatorInterface
419+
{
420+
publicfunction__construct(bool$enabled)
421+
{
422+
}
423+
424+
publicfunctionvalidate(\Symfony\Component\Workflow\Definition$definition,string$name):void
425+
{
426+
}
427+
}

‎src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"symfony/json-streamer":"7.3.*",
7474
"symfony/uid":"^6.4|^7.0",
7575
"symfony/web-link":"^6.4|^7.0",
76-
"symfony/webhook":"^7.2",
76+
"symfony/webhook":"^7.3",
7777
"phpdocumentor/reflection-docblock":"^3.0|^4.0|^5.0",
7878
"twig/twig":"^3.12"
7979
},
@@ -108,7 +108,7 @@
108108
"symfony/validator":"<6.4",
109109
"symfony/web-profiler-bundle":"<6.4",
110110
"symfony/webhook":"<7.2",
111-
"symfony/workflow":"<6.4"
111+
"symfony/workflow":"<7.3"
112112
},
113113
"autoload": {
114114
"psr-4": {"Symfony\\Bundle\\FrameworkBundle\\":"" },
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Workflow\DependencyInjection;
13+
14+
useSymfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
useSymfony\Component\DependencyInjection\ContainerBuilder;
16+
useSymfony\Component\DependencyInjection\Exception\LogicException;
17+
useSymfony\Component\Workflow\Exception\InvalidDefinitionException;
18+
19+
/**
20+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
21+
*/
22+
class WorkflowValidatorPassimplements CompilerPassInterface
23+
{
24+
publicfunctionprocess(ContainerBuilder$container):void
25+
{
26+
foreach ($container->findTaggedServiceIds('workflow')as$id =>$attributes) {
27+
foreach ($attributesas$attribute) {
28+
foreach ($attribute['definition_validators'] ?? []as$validatorClass) {
29+
$realDefinition =$container->get($attribute['definition_id'] ??thrownew \LogicException('The "definition_id" attribute is required.'));
30+
(new$validatorClass())->validate($realDefinition,$attribute['name'] ??thrownew \LogicException('The "name" attribute is required.'));
31+
}
32+
}
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp