Skip to content

Commit 8345a62

Browse files
[Workflow] Add AsMarkingStore attribute
1 parent 21673dc commit 8345a62

File tree

10 files changed

+250
-2
lines changed

10 files changed

+250
-2
lines changed

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
use Symfony\Component\VarExporter\Internal\Registry;
7272
use Symfony\Component\Workflow\DependencyInjection\WorkflowDebugPass;
7373
use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass;
74+
use Symfony\Component\Workflow\DependencyInjection\WorkflowMarkingStorePass;
7475

7576
// Help opcache.preload discover always-needed symbols
7677
class_exists(ApcuAdapter::class);
@@ -157,6 +158,7 @@ public function build(ContainerBuilder $container): void
157158
$container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING);
158159
$this->addCompilerPassIfExists($container, FormPass::class);
159160
$this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class);
161+
$this->addCompilerPassIfExists($container, WorkflowMarkingStorePass::class);
160162
$container->addCompilerPass(new ResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
161163
$container->addCompilerPass(new RegisterLocaleAwareServicesPass());
162164
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
namespace Symfony\Component\Workflow\Attribute;
13+
14+
/**
15+
* @author Alexandre Daubois <alex.daubois@gmail.com>
16+
*/
17+
#[\Attribute(\Attribute::TARGET_CLASS)]
18+
final class AsMarkingStore
19+
{
20+
public function __construct(
21+
/**
22+
* The name of the property where the marking will be stored in the subject.
23+
*/
24+
public string $markingName,
25+
/**
26+
* The name of the property where the marking name will be stored.
27+
*/
28+
public string $property = 'property',
29+
) {
30+
}
31+
}

src/Symfony/Component/Workflow/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Add `AsMarkingStore` attribute
8+
49
7.0
510
---
611

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
namespace Symfony\Component\Workflow\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\ChildDefinition;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Exception\LogicException;
18+
use Symfony\Component\Workflow\Attribute\AsMarkingStore;
19+
20+
/**
21+
* @author Alexandre Daubois <alex.daubois@gmail.com>
22+
*/
23+
class WorkflowMarkingStorePass implements CompilerPassInterface
24+
{
25+
public function process(ContainerBuilder $container): void
26+
{
27+
// Register marking store
28+
$container->registerAttributeForAutoconfiguration(AsMarkingStore::class, static function (ChildDefinition $definition, AsMarkingStore $attribute, \ReflectionClass $reflector): void {
29+
$tagAttributes = get_object_vars($attribute);
30+
$invalid = true;
31+
32+
if ($constructor = $reflector->getConstructor()) {
33+
foreach ($constructor->getParameters() as $parameters) {
34+
if ($tagAttributes['property'] === $parameters->getName()) {
35+
$type = $parameters->getType();
36+
$invalid = !$type instanceof \ReflectionNamedType || 'string' !== $type->getName();
37+
}
38+
}
39+
}
40+
41+
if ($invalid) {
42+
throw new LogicException(sprintf('The "%s" class doesn\'t have a constructor with a string type-hinted argument named "%s".', $reflector->getName(), $tagAttributes['property']));
43+
}
44+
45+
$definition->replaceArgument('$'.$tagAttributes['property'], $tagAttributes['markingName']);
46+
$definition->addTag('workflow.marking_store', $tagAttributes);
47+
});
48+
}
49+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
namespace Symfony\Component\Workflow\MarkingStore;
13+
14+
/**
15+
* @author Alexandre Daubois <alex.daubois@gmail.com>
16+
*/
17+
abstract class AbstractMarkingStore implements MarkingStoreInterface
18+
{
19+
public function __construct(protected string $property)
20+
{
21+
}
22+
}

src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
*
3030
* @author Grégoire Pineau <lyrixx@lyrixx.info>
3131
*/
32-
final class MethodMarkingStore implements MarkingStoreInterface
32+
final class MethodMarkingStore extends AbstractMarkingStore
3333
{
3434
/** @var array<class-string, MarkingStoreMethod> */
3535
private array $getters = [];
@@ -43,8 +43,9 @@ final class MethodMarkingStore implements MarkingStoreInterface
4343
*/
4444
public function __construct(
4545
private bool $singleState = false,
46-
private string $property = 'marking',
46+
string $property = 'marking',
4747
) {
48+
parent::__construct($property);
4849
}
4950

5051
public function getMarking(object $subject): Marking
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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+
namespace Symfony\Component\Workflow\Tests\DependencyInjection;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Compiler\AttributeAutoconfigurationPass;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Exception\LogicException;
18+
use Symfony\Component\Workflow\DependencyInjection\WorkflowMarkingStorePass;
19+
use Symfony\Component\Workflow\Tests\Fixtures\AttributeMarkingStore;
20+
use Symfony\Component\Workflow\Tests\Fixtures\AttributeMarkingStoreWithCustomProperty;
21+
use Symfony\Component\Workflow\Tests\Fixtures\AttributeMarkingStoreWithoutConstructor;
22+
23+
class WorkflowMarkingStorePassTest extends TestCase
24+
{
25+
private ContainerBuilder $container;
26+
private WorkflowMarkingStorePass $compilerPass;
27+
28+
protected function setUp(): void
29+
{
30+
$this->container = new ContainerBuilder();
31+
$this->compilerPass = new WorkflowMarkingStorePass();
32+
}
33+
34+
public function testRegistersMarkingStore()
35+
{
36+
$this->container->register(AttributeMarkingStore::class, AttributeMarkingStore::class)->setPublic(true)->setAutoconfigured(true);
37+
38+
(new AttributeAutoconfigurationPass())->process($this->container);
39+
$this->compilerPass->process($this->container);
40+
$this->container->compile();
41+
42+
$this->assertSame('currentPlace', $this->container->get(AttributeMarkingStore::class)->getProperty());
43+
$this->assertSame(['currentPlace'], $this->container->getDefinition(AttributeMarkingStore::class)->getArguments());
44+
}
45+
46+
public function testRegistersMarkingStoreWithCustomPropertyForMarking()
47+
{
48+
$this->container->register(AttributeMarkingStoreWithCustomProperty::class, AttributeMarkingStoreWithCustomProperty::class)->setPublic(true)->setAutoconfigured(true);
49+
50+
(new AttributeAutoconfigurationPass())->process($this->container);
51+
$this->compilerPass->process($this->container);
52+
$this->container->compile();
53+
54+
$this->assertSame('currentPlace', $this->container->get(AttributeMarkingStoreWithCustomProperty::class)->getAnother());
55+
$this->assertSame(['currentPlace'], $this->container->getDefinition(AttributeMarkingStoreWithCustomProperty::class)->getArguments());
56+
}
57+
58+
public function testRegisterMakingStoreWithoutConstructor()
59+
{
60+
$this->container->register(AttributeMarkingStoreWithoutConstructor::class, AttributeMarkingStoreWithoutConstructor::class)->setPublic(true)->setAutoconfigured(true);
61+
62+
(new AttributeAutoconfigurationPass())->process($this->container);
63+
$this->compilerPass->process($this->container);
64+
65+
$this->expectException(LogicException::class);
66+
$this->expectExceptionMessage('The "Symfony\Component\Workflow\Tests\Fixtures\AttributeMarkingStoreWithoutConstructor" class doesn\'t have a constructor with a string type-hinted argument named "another".');
67+
$this->container->compile();
68+
}
69+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Fixtures;
4+
5+
use Symfony\Component\Workflow\Attribute\AsMarkingStore;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\MarkingStore\AbstractMarkingStore;
8+
9+
#[AsMarkingStore('currentPlace')]
10+
class AttributeMarkingStore extends AbstractMarkingStore
11+
{
12+
public function getMarking(object $subject): Marking
13+
{
14+
return new Marking([$subject->{$this->property} => 1]);
15+
}
16+
17+
public function setMarking(object $subject, Marking $marking, array $context = []): void
18+
{
19+
$subject->{$this->property} = key($marking->getPlaces());
20+
}
21+
22+
public function getProperty(): string
23+
{
24+
return $this->property;
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Fixtures;
4+
5+
use Symfony\Component\Workflow\Attribute\AsMarkingStore;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\MarkingStore\AbstractMarkingStore;
8+
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
9+
10+
#[AsMarkingStore(markingName: 'currentPlace', property: 'another')]
11+
class AttributeMarkingStoreWithCustomProperty implements MarkingStoreInterface
12+
{
13+
public function __construct(private string $another)
14+
{
15+
}
16+
17+
public function getMarking(object $subject): Marking
18+
{
19+
return new Marking();
20+
}
21+
22+
public function setMarking(object $subject, Marking $marking, array $context = []): void
23+
{
24+
}
25+
26+
public function getAnother(): string
27+
{
28+
return $this->another;
29+
}
30+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Fixtures;
4+
5+
use Symfony\Component\Workflow\Attribute\AsMarkingStore;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\MarkingStore\AbstractMarkingStore;
8+
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
9+
10+
#[AsMarkingStore(markingName: 'currentPlace', property: 'another')]
11+
class AttributeMarkingStoreWithoutConstructor
12+
{
13+
}

0 commit comments

Comments
 (0)