Skip to content

[Workflow] Introduce concept of SupportStrategyInterface #20751

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed

[Workflow] Introduce concept of SupportStrategyInterface #20751

wants to merge 13 commits into from

Conversation

andesk
Copy link
Contributor

@andesk andesk commented Dec 4, 2016

Q A
Branch? 3.2
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

As discussed during #symfonycon with @Nyholm and @xabbuh , in it's first implementation, the Workflow component's Registry class is checking on a subject's class instance to decide, if it is supported or not. In our current project we are using a CMS which uses a generic class to hold content of any content type. But we would want to be able to use the registry to identify the supported workflow for a content by checking the class's content type. As this is a very propriatary use case, but I believe similar requirements might be coming for other projects, I decided to introduce a strategy pattern to generalize it.

Disclaimer: Current state is PoC to indicate the need, feel free to find/suggest a different approach or give me directions to adjust/change it.

As the Registry:add() method expected a $className (string) attribute and I changed it to accept any SupportStrategyInterface instead, this PR introduces a BC break. As the component is quite fresh and the Registry is kind of a helper construct, we might be ok to introduce this BC nevertheless? I tried to keep the "supports" configuration key as it is to avoid conflicts with existing workflow service definitions and keep the changes as local as possible. Other ideas to avoid this are welcome of course :)

While adjusting the existing tests for FrameworkBundle I realized that probably not every Workflow related code is covered by tests yet. I decided to skip adding more tests (at least for now) as we first need to agree on if we introduce a generalized approach at all and if the current one is acceptable to others.

@@ -38,6 +38,9 @@ public function process(ContainerBuilder $container)
if (!array_key_exists('marking_store', $tag)) {
throw new RuntimeException(sprintf('The "marking_store" for the tag "workflow.definition" of service "%s" must be set.', $id));
}
if (!array_key_exists('supports', $tag) && !array_key_exists('support_strategy', $tag)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before this, "supports" was marked as required in Configuration.php, see below.

Copy link
Member

Choose a reason for hiding this comment

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

This kind of validation should be done in the Configuration class

Copy link
Contributor Author

Choose a reason for hiding this comment

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

K, will fix that.

Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)
);
$strategyDefinition->setPublic(false);
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClassName));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So instead of using the supported class name directly, I am more or less wrapping it in a support strategy service definition.

}
}
if (isset($workflow['support_strategy'])) {
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy'])));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As a second option, "support_strategy" expects to be a reference to a registered service implementing the introduced SupportStrategyInterface.

@@ -165,6 +165,10 @@ public function testWorkflows()
$this->assertSame(array('workflow.definition' => array(array('name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state'))), $stateMachineDefinition->getTags());
$this->assertCount(9, $stateMachineDefinition->getArgument(1));
$this->assertSame('start', $stateMachineDefinition->getArgument(2));

$this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service'));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Until now, there was not coverage for registry at all. I did not want to introduce too much, but the following should we ok?

@@ -37,16 +37,25 @@ public function __construct($subject, Marking $marking, Transition $transition)
$this->transition = $transition;
}

/**
* @return Marking
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added some docblocks, never bad to have them :)

Copy link
Member

Choose a reason for hiding this comment

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

It does not bring anything IMHO. and it's not related to your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So should I revert it? Or create a separate PR? For me, adding some of these pointed out some details. Sure I might see that looking at the code, but...

Copy link
Member

Choose a reason for hiding this comment

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

Yes, should be revert. IDEs should be able to infer the return type from code (as the $marking property is private and is set during initialisation).

Copy link
Member

Choose a reason for hiding this comment

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

Yes, should be reverted. IDEs should be able to infer the return type from code (as the $marking property is private and is set during initialisation).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

K.

*/
public function add(Workflow $workflow, $className)
public function add(Workflow $workflow, SupportStrategyInterface $supportStrategy)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So here is the mentioned BC break... Alternatively we could intrdoduce a second method or keep $className and introduce an additional optional parameter for support strategy? Would be ok with my use case and probably with most others as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Regardless of architecture (not familiar with the workings), you don't implement an interface here. This means you can safely remove the typehint and do a type check:

@param SupportStrategyInterface|string
public function add(Workflow $workflow, $supportStrategy)

if not instance of SupportStrategyInterface
    if not string
        throw invalid argument exception

    trigger deprecation, argument must be a SupportStrategyInterface as of 4.0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, one could do that to avoid BC, but it's introducing kind of magic below, isn't it?

I ike the deprecation idea though 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

It's the standard way of introducing a BC layer in symfony. It's kinda like method overloading

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So should I change my code accordingly? ping @lyrixx

Copy link
Member

Choose a reason for hiding this comment

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

@andesk yes. Your current proposal is a BC break, and cannot be accepted for Symfony 3.3 (and would not even be acceptable for Symfony 4.0 as is, as it does not allow to have a progressive upgrade path)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

K, will do :)

Should I revert changes in Extension back to inject class name string only instead of wrapping it into the ClassInstanceSupportStrategy? Or keep them as that might be the target for 4.0 anyways?

Copy link
Member

Choose a reason for hiding this comment

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

no, don't revert this one, as injecting class names directly will trigger a deprecation warning. We don't want the normal configuration of the bundle to trigger a deprecation warning for our users (as they would not be in control of fixing it anyway)

@@ -64,6 +65,17 @@ public function testGetWithNoMatch()
$this->assertInstanceOf(Workflow::class, $w1);
$this->assertSame('workflow1', $w1->getName());
}

private function getSupportStrategy($supportedClassName)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The following might not be the best idea, but keeps the changes minimal...

@lyrixx
Copy link
Member

lyrixx commented Dec 5, 2016

Hello @andesk

Why can't you use the name of workflow to be able to get it? (the second argument of Registry::get)

@andesk
Copy link
Contributor Author

andesk commented Dec 5, 2016

Hi @lyrixx

Well, we don't want to hardcode the workflow's name to fetch it at all. It's kind of a generic approach to allow having different workflows for different content types in CMS (articles might need extended workflow, images a shorter one, etc), but rely on workflow configuration and the registry to identify the correct one. Is that use case so unexpected? :)

{
$this->workflows[] = array($workflow, $className);
if (!$supportStrategy instanceof SupportStrategyInterface) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As mentioned in previous comments, to avoid BC break we are allowing string as a fallback, checking it and otherwise throw deprecation warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No BC break anymore after doing suggested changes thanks to @iltar Though it seems triggering the deprecated error leads to CI integrations to fail... Any ideas how to fix this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks to @xabbuh I figured out the @group legacy annotation. Should be fine now :)

@andesk
Copy link
Contributor Author

andesk commented Dec 5, 2016

Updated the PR introducing suggested handling to avoid BC break. Anything else?

@andesk andesk changed the title [PoC][Workflow] Introduce concept of SupportStrategyInterface [Workflow] Introduce concept of SupportStrategyInterface Dec 5, 2016
@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Dec 6, 2016
@andesk
Copy link
Contributor Author

andesk commented Dec 10, 2016

Hmmm, fabbot.io is pending since days now. Any ideas how to find out what went wrong there? @nicolas-grekas maybe?

@@ -324,6 +326,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
->end()
->end()
->end()
->validate()
->ifTrue(function ($v) { return isset($v['supports']) && isset($v['support_strategy']); })
->thenInvalid('"supports" and "support_strategy" cannot be used together.')
Copy link
Member

Choose a reason for hiding this comment

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

Don't we also need to make sure that one of supports or support_strategy is configured?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have not seen other examples for that yet, but of course I can add a second validation for both being !isset()?

Copy link
Member

Choose a reason for hiding this comment

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

something like empty('supports') && empty('support_strategy')

$this->workflows[] = array($workflow, $className);
if (!$supportStrategy instanceof SupportStrategyInterface) {
if (!is_string($supportStrategy)) {
throw new InvalidArgumentException('Expecting instance of SupportStrategyInterface or class name as a string.');
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to have this check. The docblock is pretty about what types you are allowed to pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was suggested by @iltar and I think it's fine to keep it as a check?

Copy link
Member

Choose a reason for hiding this comment

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

If I am not mistaken, we don't do similar checks anywhere else, but rely on the fact that we claimed through the docblock which types we expect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any other oppinion on that? Should I remove it? ping @lyrixx @Nyholm

Copy link
Member

Choose a reason for hiding this comment

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

Indeed this should be removed.

@@ -23,19 +24,34 @@ class Registry

/**
* @param Workflow $workflow
Copy link
Member

Choose a reason for hiding this comment

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

this parameter needs to be aligned with the following line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, don't get your point. What do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, Symfony uses formatting of docblocks with aligned intendions?

* @param null|string $workflowName
*
* @return bool
*/
Copy link
Member

Choose a reason for hiding this comment

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

no need for this docblock IMO as the method is private

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Several other private methods (e.g. in Workflow.php) have a docblock as well. Is there any rule of thumb when to add it? I personally like having docblocks, but if they are not common in Symfony core I am fine with removing those.

interface SupportStrategyInterface
{
/**
* @param \Symfony\Component\Workflow\Workflow $workflow
Copy link
Member

Choose a reason for hiding this comment

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

no need for the FQCN

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

# Conflicts:
#	src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@lyrixx
Copy link
Member

lyrixx commented Dec 13, 2016

How would you use it from a twig template?

@andesk
Copy link
Contributor Author

andesk commented Dec 13, 2016

@lyrixx Not sure what you mean. This change does not break anything in twig usage. And as there was no support to call registry from twig before, I did not have anything to adjust. My current use case: I use the registry service in a REST endpoint. Introducing a twig function is out of scope to me, no?

@lyrixx
Copy link
Member

lyrixx commented Dec 13, 2016

If you update the Registry, you have to update this file. And, I don't know how to use these twig functions after that.

@andesk
Copy link
Contributor Author

andesk commented Dec 13, 2016

@lyrixx This should work in the same way as before? No changes made to this behavior. Or maybe I don't get your point? The public "API" of registry class stays exactly as it is. The only change is how to decide, which workflow is supposed to be used for a certain object. Until now, only instanceof class was supported, now with introduction of the strategy, anything can be used internally. From the outside, nothing changes. Only change is the (for now) optional configuration of a Strategy service in workflow configuration.

@lyrixx
Copy link
Member

lyrixx commented Dec 13, 2016

You are right. I missed something. I'm OK with this PR. But Could you rebase before the merge. Thanks.

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

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

I like this. I just had a minor comment.

$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass));
if (isset($workflow['supports'])) {
foreach ($workflow['supports'] as $supportedClassName) {
$strategyDefinition = new Definition(
Copy link
Member

Choose a reason for hiding this comment

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

You never register the $strategyDefinition in the container.

Copy link
Member

Choose a reason for hiding this comment

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

Should be put on one line.

Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)
);
$strategyDefinition->setPublic(false);
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClassName));
Copy link
Member

Choose a reason for hiding this comment

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

You should use $strategyDefinition here or deprecation notice will always trigger.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh wow, how could I not realize that?! Thank you @Nyholm , will fix

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

*
* @return Workflow
* @throws InvalidArgumentException
Copy link
Member

Choose a reason for hiding this comment

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

missing empty line before this line

{
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock();
$strategy->expects($this->any())->method('supports')
->will($this->returnCallback(function($workflow, $subject) use ($supportedClassName) {
Copy link
Member

Choose a reason for hiding this comment

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

missing space after function

@@ -436,7 +436,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde

// Create MarkingStore
if (isset($workflow['marking_store']['type'])) {
$markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.'.$workflow['marking_store']['type']);
$markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.' . $workflow['marking_store']['type']);
Copy link
Member

Choose a reason for hiding this comment

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

to be reverted

$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition));
}
}
if (isset($workflow['support_strategy'])) {
Copy link
Member

Choose a reason for hiding this comment

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

could probably be an elseif as the two options are exclusive

if (isset($workflow['supports'])) {
foreach ($workflow['supports'] as $supportedClassName) {
$strategyDefinition = new Definition(
Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)
Copy link
Member

Choose a reason for hiding this comment

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

You should use a use statement instead of using the FQCN here.

$this->workflows[] = array($workflow, $className);
if (!$supportStrategy instanceof SupportStrategyInterface) {
if (!is_string($supportStrategy)) {
throw new InvalidArgumentException('Expecting instance of SupportStrategyInterface or class name as a string.');
Copy link
Member

Choose a reason for hiding this comment

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

Indeed this should be removed.

}

/**
* @param object $subject
* @param string|null $workflowName
* @param null|string $workflowName
Copy link
Member

Choose a reason for hiding this comment

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

should be reverted


class ClassInstanceSupportStrategy implements SupportStrategyInterface
{
/** @var string */
Copy link
Member

Choose a reason for hiding this comment

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

Comment should be removed


/**
* @param string $className
*/
Copy link
Member

Choose a reason for hiding this comment

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

docblock should be removed

@andesk
Copy link
Contributor Author

andesk commented Dec 17, 2016

@fabpot Fixed proposed changes, fabbot.io still complaining about code style in FrameworkExtension class not related to this story. Anything I can do about it?

$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass));
if (isset($workflow['supports'])) {
foreach ($workflow['supports'] as $supportedClassName) {
$strategyDefinition = new Definition(
Copy link
Member

Choose a reason for hiding this comment

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

Should be put on one line.

$this->assertSame('workflow2', $workflow->getName());
}

private function getSupportStrategy($supportedClassName)
Copy link
Member

Choose a reason for hiding this comment

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

Should be named createSupportStrategy


private function getSupportStrategy($supportedClassName)
{
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock();
Copy link
Member

Choose a reason for hiding this comment

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

$this->getMock(SupportStrategyInterface::class) is enough

Copy link
Member

Choose a reason for hiding this comment

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

According to #20984 the current syntax should be kept

{
if (!$subject instanceof $className) {
if (is_string($supportStrategy) && !$subject instanceof $supportStrategy) {
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a comment before this line:

// Keep BC. To be removed in 4.0

Copy link
Member

Choose a reason for hiding this comment

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

Rather than dealing with BC here, I would convert the string to a SupportStrategy when setting it instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So @stof , you mean that inside the add() method I would create a ClassInstanceSupportStrategy in the deprecation case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I still add a BC comment somewhere? ping @lyrixx ?

Copy link
Member

Choose a reason for hiding this comment

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

The two conditions could then be simplified to:

if (!$supportStrategy->supports($workflow, $subject)) {
    return false;
}

return true;
}

return $name === $workflow->getName();
return $workflowName === $workflow->getName();
Copy link
Member

Choose a reason for hiding this comment

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

All this method could be simplified:

if (null !== $workflowName && $workflowName !== $workflow->getName()) {
      return false;
}
  
// Keep BC. To be removed in 4.0
if (is_string($supportStrategy)) {
    return $subject instanceof $supportStrategy;
}

return $supportStrategy->supports($workflow, $subject);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So if @stof 's idea above will be implemented, this can even be more simplified, right?

Copy link
Member

Choose a reason for hiding this comment

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

indeed.

(Please, ping me when you're ready. Thanks)

{
$registry = new Registry();

$registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class);
Copy link
Member

Choose a reason for hiding this comment

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

$this->getMock(SupportStrategyInterface::class) is enough
(same bellow)

Copy link
Member

Choose a reason for hiding this comment

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

According to #20984 the current syntax should be kept

$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock();
$strategy->expects($this->any())->method('supports')
->will($this->returnCallback(function ($workflow, $subject) use ($supportedClassName) {
return $subject instanceof $supportedClassName;
Copy link
Member

Choose a reason for hiding this comment

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

Why do you use a mock here when you can use the ClassInstanceSupportStrategy ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't know how this is handled in Symfony environment, but as I want to test the registry alone I personally prefer mocking all other collaborators. Should I change it?

@lyrixx
Copy link
Member

lyrixx commented Dec 26, 2016

Hello @andesk

Could you take time to finish this PR? If not, I could finish it for you if you allow me to push in your fork.

Thanks ;)

@@ -287,6 +286,9 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
->end()
->end()
->end()
->scalarNode('support_strategy')
->cannotBeEmpty()
Copy link
Member

Choose a reason for hiding this comment

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

this forbids resetting it to null in a later file to use supports again

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So should I simply remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ping @stof

@@ -325,6 +327,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode)
->end()
->end()
->end()
->validate()
->ifTrue(function ($v) { return isset($v['supports']) && isset($v['support_strategy']); })
Copy link
Member

Choose a reason for hiding this comment

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

supports will always be set as it is a prototyped array node and so has a default. You need to check whether it is the empty array instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So return !empty($v['supports']) && isset($v['support_strategy']); ?

Copy link
Member

Choose a reason for hiding this comment

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

yes

@@ -458,8 +459,16 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
$container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition);

// Add workflow to Registry
foreach ($workflow['supports'] as $supportedClass) {
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass));
if (isset($workflow['supports'])) {
Copy link
Member

Choose a reason for hiding this comment

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

it is always set here. You should check for the array not being empty

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So if (!empty($workflow['supports'])) instead?

Copy link
Member

Choose a reason for hiding this comment

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

yes

Copy link
Member

Choose a reason for hiding this comment

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

having a test covering this combination would probably be good

}

/**
* @param object $subject
* @param string|null $workflowName
*
* @return Workflow
*
* @throws InvalidArgumentException
Copy link
Member

Choose a reason for hiding this comment

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

we don't add this phpdoc when we cannot document in which case the exception is thrown

{
if (!$subject instanceof $className) {
if (is_string($supportStrategy) && !$subject instanceof $supportStrategy) {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than dealing with BC here, I would convert the string to a SupportStrategy when setting it instead.


use Symfony\Component\Workflow\Workflow;

class ClassInstanceSupportStrategy implements SupportStrategyInterface
Copy link
Member

Choose a reason for hiding this comment

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

you should add a docblock and add yourself as an author

Copy link
Member

Choose a reason for hiding this comment

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

same for the interface

Copy link
Member

Choose a reason for hiding this comment

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

and the class should become final

{
private $className;

public function __construct($className)
Copy link
Member

Choose a reason for hiding this comment

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

you should add a docblock documenting the argument as a string

@lyrixx
Copy link
Member

lyrixx commented Jan 18, 2017

Closing in favor of #21334

@lyrixx lyrixx closed this Jan 18, 2017
fabpot added a commit that referenced this pull request Jan 18, 2017
…ce (andesk, lyrixx)

This PR was merged into the 3.3-dev branch.

Discussion
----------

 [Workflow] Introduce concept of SupportStrategyInterface

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #20751
| License       | MIT
| Doc PR        | -

Commits
-------

134a58b [Workflow] Fixed code and tests
1843012 [Workflow] Introduce concept of SupprtStrategyInterface to allow other support checks than class instance
@nicolas-grekas nicolas-grekas modified the milestones: 3.x, 3.3 Mar 24, 2017
lyrixx added a commit that referenced this pull request Apr 5, 2017
This PR was merged into the 3.3-dev branch.

Discussion
----------

[Workflow] sync the changelog

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #20751, #21334, #21933, #21935, #21950
| License       | MIT
| Doc PR        |

Commits
-------

98a18ee [Workflow] sync the changelog
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants