Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CHANGELOG
* Add `framework.type_info.aliases` option
* Add `KernelBrowser::getSession()`
* Add autoconfiguration tag `kernel.uri_signer` to `Symfony\Component\HttpFoundation\UriSigner`
* Add support for configuring workflow places with glob patterns matching consts/backed enums

7.3
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\Finder\Glob;
use Symfony\Component\Form\Form;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
use Symfony\Component\HttpClient\HttpClient;
Expand Down Expand Up @@ -364,7 +365,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->arrayNode('workflows', 'workflow')
->canBeEnabled()
->beforeNormalization()
->always(function ($v) {
->always(static function ($v) {
if (\is_array($v) && true === $v['enabled']) {
$workflows = $v;
unset($workflows['enabled']);
Expand Down Expand Up @@ -478,15 +479,36 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->end()
->arrayNode('places', 'place')
->beforeNormalization()
->always()
->then(static function ($places) {
if (!\is_array($places)) {
throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.');
->always(static function ($places) {
if (\is_string($places)) {
if (2 !== \count($places = explode('::', $places, 2))) {
throw new InvalidConfigurationException('The "places" option must be a "FQCN::glob" pattern in workflow configuration.');
}
[$class, $pattern] = $places;
if (!class_exists($class) && !interface_exists($class, false)) {
throw new InvalidConfigurationException(\sprintf('The "places" option must be a "FQCN::glob" pattern in workflow configuration, but class "%s" is not found.', $class));
}

$places = [];
$regex = Glob::toRegex($pattern, false);

foreach ((new \ReflectionClass($class))->getConstants() as $name => $value) {
if (preg_match($regex, $name)) {
$places[] = $value;
}
}
if (!$places) {
throw new InvalidConfigurationException(\sprintf('No places found for pattern "%s::%s" in workflow configuration.', $class, $pattern));
}
} elseif (!\is_array($places)) {
throw new InvalidConfigurationException('The "places" option must be an array or a "FQCN::glob" pattern in workflow configuration.');
}

$normalizedPlaces = [];
foreach ($places as $key => $value) {
if (!\is_array($value)) {
if ($value instanceof \BackedEnum) {
$value = ['name' => $value->value];
} elseif (!\is_array($value)) {
$value = ['name' => $value];
}
$value['name'] ??= $key;
Expand Down Expand Up @@ -514,8 +536,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->end()
->arrayNode('transitions', 'transition')
->beforeNormalization()
->always()
->then(static function ($transitions) {
->always(static function ($transitions) {
if (!\is_array($transitions)) {
throw new InvalidConfigurationException('The "transitions" option must be an array in workflow configuration.');
}
Expand Down Expand Up @@ -550,14 +571,18 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->example('is_fully_authenticated() and is_granted(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'')
->end()
->arrayNode('from')
->beforeNormalization()->castToArray()->end()
->beforeNormalization()
->always(static fn ($from) => array_map(static fn ($v) => $v instanceof \BackedEnum ? $v->value : $v, \is_array($from) ? $from : [$from]))
->end()
->requiresAtLeastOneElement()
->prototype('scalar')
->cannotBeEmpty()
->end()
->end()
->arrayNode('to')
->beforeNormalization()->castToArray()->end()
->beforeNormalization()
->always(static fn ($to) => array_map(static fn ($v) => $v instanceof \BackedEnum ? $v->value : $v, \is_array($to) ? $to : [$to]))
->end()
->requiresAtLeastOneElement()
->prototype('scalar')
->cannotBeEmpty()
Expand All @@ -582,28 +607,23 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->end()
->end()
->validate()
->ifTrue(static function ($v) {
return $v['supports'] && isset($v['support_strategy']);
})
->ifTrue(static fn ($v) => $v['supports'] && isset($v['support_strategy']))
->thenInvalid('"supports" and "support_strategy" cannot be used together.')
->end()
->validate()
->ifTrue(static function ($v) {
return !$v['supports'] && !isset($v['support_strategy']);
})
->ifTrue(static fn ($v) => !$v['supports'] && !isset($v['support_strategy']))
->thenInvalid('"supports" or "support_strategy" should be configured.')
->end()
->beforeNormalization()
->always()
->then(static function ($values) {
// Special case to deal with XML when the user wants an empty array
if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) {
$values['events_to_dispatch'] = [];
unset($values['event_to_dispatch']);
}
->always(static function ($values) {
// Special case to deal with XML when the user wants an empty array
if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) {
$values['events_to_dispatch'] = [];
unset($values['event_to_dispatch']);
}

return $values;
})
return $values;
})
->end()
->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@
<xsd:attribute name="initial-marking" type="xsd:string" />
<xsd:attribute name="support-strategy" type="xsd:string" />
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="places" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="php-errors">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow;

enum Places: string
{
case A = 'a';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In another PR, I want add to introduce an attribute to configure the Metadata. What would be the name of such attribute? #[WorkflowMetadata] / #[AsWorkflowMetadata]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just #[Place]?

case B = 'b';
case C = 'c';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places;
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase;

$container->loadFromExtension('framework', [
'workflows' => [
'enum' => [
'supports' => [
FrameworkExtensionTestCase::class,
],
'places' => Places::cases(),
'transitions' => [
'one' => [
'from' => Places::A->value,
'to' => Places::B->value,
],
'two' => [
'from' => Places::B->value,
'to' => Places::C->value,
],
],
]
],
]);
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places;
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase;

$container->loadFromExtension('framework', [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config http-method-override="false" handle-all-throwables="true">
<framework:workflow name="enum" type="state_machine" places="Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::*">
<framework:marking-store service="workflow_service"/>
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase</framework:support>
<framework:transition name="one">
<framework:from>a</framework:from>
<framework:to>b</framework:to>
</framework:transition>
<framework:transition name="two">
<framework:from>b</framework:from>
<framework:to>c</framework:to>
</framework:transition>
</framework:workflow>
</framework:config>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
framework:
workflows:
enum:
supports:
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase
places: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places
transitions:
one:
from: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::A
to: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::B
two:
from: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::B
to: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::C
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,14 @@ public function testWorkflowMultipleTransitionsWithSameName()
], $container->getDefinition($transitions[4])->getArguments());
}

public function testWorkflowEnum()
{
$container = $this->createContainerFromFile('workflow_enum');

$workflowDefinition = $container->getDefinition('state_machine.enum.definition');
$this->assertSame(['a', 'b', 'c'], $workflowDefinition->getArgument(0));
}

public function testWorkflowGuardExpressions()
{
$container = $this->createContainerFromFile('workflow_with_guard_expression');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function testAssetPackageCannotHavePathAndUrl()
public function testWorkflowValidationPlacesIsArray()
{
$this->expectException(InvalidConfigurationException::class);
$this->expectExceptionMessage('The "places" option must be an array in workflow configuration.');
$this->expectExceptionMessage('The "places" option must be an array or a "FQCN::glob" pattern in workflow configuration.');
$this->createContainerFromClosure(function ($container) {
$container->loadFromExtension('framework', [
'workflows' => [
Expand Down
Loading