Problem/Motivation

\Drupal\Core\Condition\ConditionManager::evaluate() negates the outcome of condition evaluation results. While ConditionInterface has an ::isNegated() method, negation is in fact part of the conditions' implementations and one would expect the negation to performed as such. However, if that is done, ConditionManager will negate the outcome yet again.

This is bad design at least, and maybe a bug, because plugins should be usable without the default condition manager implementation.

Proposed resolution

- Refactor Condition and Condition Plugins - correcting the evaluate implementations and isNegated usage (after "condition" results)
Consider whether to fix or document this inconsistency/bug.

Remaining tasks

Write test
Code review

User interface changes

None.

API changes

T.b.d.

Data model changes

None.

Issue fork drupal-2535896

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

andypost’s picture

Issue tags: +Needs tests

and the behaviour should b covered with tests

Xano’s picture

The responsibility for executing conditions was moved from the conditions themselves to ConditionManager in #1743686: Condition Plugin System. I asked the authors why on Twitter.

Xano’s picture

It seems \Drupal\Core\Condition\ConditionInterface::isNegated() should not have been a public method, as it should be irrelevant to calling code whether a plugin is negated or not (in fact it makes sense for simple boolean conditions not to be negatable at all). The internal configuration concerns only the plugin conditions themselves, and calling code should use the evaluation result directly.
Five calls show up in core, of which only one outside condition plugins, and that's in ConditionManager in order to negate the results. Three of the remaining calls are in conditions themselves, used to negate the result in ConditionInterface::evaluate().

Xano’s picture

Status: Active » Needs review
FileSize
1.41 KB

Let's see what fixing this directly actually breaks, so we can decide on the best course of action.

Status: Needs review » Needs work

The last submitted patch, 4: drupal_2535896_4.patch, failed testing.

Xano’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
4.84 KB
6.25 KB

This fixes all test failures and adds a test for ConditionManager.

Status: Needs review » Needs work

The last submitted patch, 6: drupal_2535896_6.patch, failed testing.

Xano’s picture

Status: Needs work » Needs review
FileSize
579 bytes
6.24 KB

Xano queued 8: drupal_2535896_8.patch for re-testing.

andypost’s picture

I'd better change the docs for negated... but +1 to approach
implementation details of isNegated() should be able to access config and do some custom logic but return !empty($this->configuration['negate']) by default

  1. +++ b/core/lib/Drupal/Core/Condition/ConditionInterface.php
    @@ -54,6 +54,9 @@
    +   * @deprecated Deprecated as of 8.0.0. Condition negation is an implementation
    +   *   detail and should not be relied upon.
    ...
       public function isNegated();
    

    needs a point about removal

  2. +++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
    @@ -68,8 +68,11 @@ public function createInstance($plugin_id, array $configuration = array()) {
    +    if (!($condition instanceof ConditionInterface)) {
    +      throw new \InvalidArgumentException(sprintf('%s must be an instance of %s, but %s was given.', '$condition', '\Drupal\Core\Condition\ConditionInterface', get_class($condition)));
    

    scope-creep?

  3. +++ b/core/modules/language/src/Plugin/Condition/Language.php
    @@ -143,7 +143,8 @@ public function evaluate() {
    +    return $this->configuration['negate'] ? !$result : $result;
    
    +++ b/core/modules/system/src/Plugin/Condition/CurrentThemeCondition.php
    @@ -109,7 +109,9 @@ public function evaluate() {
    +    return $this->configuration['negate'] ? !$result : $result;
    
    +++ b/core/modules/system/src/Plugin/Condition/RequestPath.php
    @@ -148,15 +148,18 @@ public function evaluate() {
    +    return $this->configuration['negate'] ? !$result : $result;
    

    Why not configuration accessed directly?

    return $this->isNegated() ? ..
    will make more sense

Xano’s picture

FileSize
717 bytes
6.29 KB

scope-creep?

It's not directly related to the issue, but it makes sure IDEs know about the ::evaluate() method that is called there, and adds some error handling in case someone passes on an invalid value.

Xano queued 11: drupal_2535896_11.patch for re-testing.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -68,8 +68,11 @@ public function createInstance($plugin_id, array $configuration = array()) {
+    if (!($condition instanceof ConditionInterface)) {
+      throw new \InvalidArgumentException(sprintf('%s must be an instance of %s, but %s was given.', '$condition', '\Drupal\Core\Condition\ConditionInterface', get_class($condition)));

Isn't that a usecase of an assertioN?

Xano’s picture

FileSize
2.33 KB
6.36 KB

PHP 5.5 goodies!

Isn't that a usecase of an assertioN?

What do you mean with an assertion in this context?

dawehner’s picture

mgifford queued 14: drupal_2535896_14.patch for re-testing.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
    --- a/core/modules/language/src/Plugin/Condition/Language.php
    +++ b/core/modules/language/src/Plugin/Condition/Language.php
    
    +++ b/core/modules/language/src/Plugin/Condition/Language.php
    --- a/core/modules/system/src/Plugin/Condition/CurrentThemeCondition.php
    +++ b/core/modules/system/src/Plugin/Condition/CurrentThemeCondition.php
    
    +++ b/core/modules/system/src/Plugin/Condition/CurrentThemeCondition.php
    --- a/core/modules/system/src/Plugin/Condition/RequestPath.php
    +++ b/core/modules/system/src/Plugin/Condition/RequestPath.php
    

    Shouldn't we add some form of test coverage for all those fixes?

  2. +++ b/core/tests/Drupal/Tests/Core/Condition/ConditionManagerTest.php
    @@ -0,0 +1,80 @@
    +  public function testConstruct() {
    +    $namespaces = new ArrayObject();
    +    $this->sut = new ConditionManager($namespaces, $this->cache, $this->moduleHandler);
    +  }
    

    Mh, so this method doesn't have any assertions ... isn't there something we could check for?

Xano’s picture

Mh, so this method doesn't have any assertions ... isn't there something we could check for?

Besides the obvious feedback: why are tests without assertions that bad? I figured that just running code and verifies it does not causes errors is some sort of a test.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Pavel Ruban’s picture

do we have the patch for core 8.7.x? As current one doesn't apply & 8.7 still has this negate issue

tim.plunkett’s picture

Version: 8.6.x-dev » 8.9.x-dev
Component: base system » plugin system
Issue tags: +condition plugins
FileSize
6.48 KB
863 bytes

Rerolled for 8.9 (which is incidentally the same as 8.7 in this case)

I'm not sure what to do with isNegated(). It's not that it needs to go away, as it is important to the implementations. But it shouldn't be on the interface. For now I just turned the @deprecated into a doc comment.

Status: Needs review » Needs work

The last submitted patch, 26: 2535896-condition-26.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

andypost’s picture

+++ b/core/tests/Drupal/Tests/Core/Condition/ConditionManagerTest.php
@@ -0,0 +1,80 @@
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Condition\ConditionManagerTest.
+ */

should be removed

andypost’s picture

Status: Needs work » Needs review
FileSize
1.57 KB
6.22 KB

Clean-up and fix test but not sure the test is right

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

vasike’s picture

Status: Needs review » Needs work

It seems this doesn't cover the EntityBundle conditions
@see https://www.drupal.org/project/drupal/issues/3255496 - closed as it seems part of this.

ravi.shankar’s picture

Addressed Drupal CS issue of patch #29, keeping the status needs work for comment #36.

Anchal_gupta’s picture

I have fixed CS error. please review it

vasike’s picture

Status: Needs work » Needs review
FileSize
7.7 KB
2.89 KB

2 updates for the latest patch

1. use $this->isNegated(), instead of $this->configuration['negate']

2. Updated also for Entity Bundle condition, as specified on #36

vasike’s picture

2 more updates:

1. We don't need the "initial" $this->isNegated() condition, besides the ones about condition configuration check.

2. UserRole condition updated.

The last submitted patch, 39: 2535896-39.patch, failed testing. View results

smustgrave’s picture

Issue summary: View changes
Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative, +Needs tests

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

As a bug this will need a test case

vasike’s picture

MR created from the latest patch ... for anyone to contrib.

+ Added missing test file - it got lost in re-rolling/updating patches
+ Add negated condition for core/tests/Drupal/KernelTests/Core/Entity/EntityBundleConditionTest.php

But dunno what tests could be created that would fail for current core ... before those updates.

vasike’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests +Needs issue summary update

Tests appear to have been added so removing that tag.

Can the issue summary be updated, specially the proposed solution. Seems to be open ended.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

vasike’s picture

Version: 11.x-dev » 10.1.x-dev
Issue summary: View changes
Status: Needs work » Needs review

issue summary updated

smustgrave’s picture

Version: 10.1.x-dev » 11.x-dev

11.x is the latest development branch

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs issue summary update

Issue summary had been updated so removing that tag.

Test coverage seems good so think this is good for committer review too.

longwave’s picture

Status: Reviewed & tested by the community » Needs review

There are surely backward compatibility issues here. Contrib and custom plugins will need updating to handle isNegated internally, now the ConditionManager doesn't do it for them?

smustgrave’s picture

Status: Needs review » Needs work

So you think the safer bet would be to deprecate isNegated from ConditionManager?

vasike’s picture

Status: Needs work » Needs review

i think we're still Need Review, as there is no "clear" ToDo.

imho, i can't see why we need to "deprecate" isNegated.
i don't think $this->configuration['negate'] is an "universal" config/option for all condition plugin.

As a reminder: the issue is to (re)move isNegated usage from ConditionManager to Condition plugin level + make sure is properly used.

Maybe we could have this return $condition->isNegated() ? !$result : $result; in the ConditionPluginBase .. similar with buildConfigurationForm implementations and usage for condition plugins.

smustgrave’s picture

@longwave thoughts on

Maybe we could have this return $condition->isNegated() ? !$result : $result; in the ConditionPluginBase .. similar with buildConfigurationForm implementations and usage for condition plugins.

smustgrave’s picture

Hiding patches as work appears to be in the MR.

@vasike can the MR be updated to 11.x please maybe with return $condition->isNegated() ? !$result : $result;

longwave’s picture

Wondering if a possible way forward here is to add NegatableConditionInterface and/or NegatableConditionBase which signifies that a condition allows negation and will handle it internally, and then we can deprecate it from ConditionManager? But if most conditions allow negation this is still quite a change for them all to make.

vasike’s picture

Status: Needs work » Needs review

new MR for 11.x available.
This moves isNegated checking to ConditionPluginBase new "helper" evaluateIsNegated

But still not sure.

Maybe @longwave has a point with #57 suggestions ... but still i can't see "the ideal" solution.

However, for now ... Needs Review

vasike’s picture

sorry, But
Reminder: This is a Bug, so i think this should be fixed (already).

And if the fix is not ideal. then create follow-up ticket to chase the perfect solution of Negated Conditions.

borisson_’s picture

Status: Needs review » Needs work

http://codcontrib.hank.vps-private.net/search?text=configuration%5B%27ne...

There are multiple pages of plugins out there that are using the same negated, so we have to keep BC in mind here, I think I agree with having an interface to mark them as being able to do it themselves + a deprecation is needed here.

larowlan’s picture

Yes, I think #57 is the best way forward here

I have client projects with lots of condition plugins that would need to be communicated this change.

With #57 we can trigger a deprecation error if the condition plugin doesn't implement the new interface, which gives us a way to eventually consolidate them.

vasike’s picture

Status: Needs work » Needs review

Updated the MR - NegatableConditionInterface and NegatableConditionBase added and related updates.

a review it would be nice

smustgrave’s picture

Status: Needs review » Needs work

With #57 we can trigger a deprecation error if the condition plugin doesn't implement the new interface, which gives us a way to eventually consolidate them.

I don't see the deprecation

vasike’s picture

Status: Needs work » Needs review

@smustgrave i didn't say i added deprecation.
just the interface and the "base class"

and i asked for a review ... for those things ...

maybe there are other things ... to make them "nice" ... before the final touch

smustgrave’s picture

Status: Needs review » Needs work

left some comments

vasike’s picture

@smustgrave Type hints added. thanks
But unfortunately, it started failing for spellcheck ... i thought it was the new class name(s) - but not sure.
also not sure what are the "related" updates on CI ... "any help will be helpful"

smustgrave’s picture

andypost’s picture

rebased again and 11.x branch as well

andypost’s picture

and cleaned-up rebase

vasike’s picture

Status: Needs work » Needs review

@smustgrave / @andypost you're my brain saviours
thanks ... cleaned up my last changes. it looks "green" ... even it tried to "do it red"
then ... let's review

smustgrave’s picture

Believe all feedback has been addressed so +1 from me but leave in review for some additional eyes to avoid ping ponging around.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Been a few days and don't want the issue to stale so going to mark.

quietone’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs issue summary update, +Needs change record

I'm triaging RTBC issues.

Ah, a 9 year old issue. I like seeing the older issues getting near completion.

I read the IS and the comments. I didn't find any unanswered questions. However the proposed resolution does not match the direction taken in #57. This is more that a refactor, it should include that an interface is added. And also a CR.

I made comments in the MR that need to be addressed.

Changing to NW

vasike’s picture

Status: Needs work » Needs review

@quietone thanks a lot for the reviews (issue and MR/code) and their feedback

Update the MR trying to cover all of threads ... it looks nicer ... at least

However, i expected a feedback about #57 changes i added ... like a confirmation ... so we could update the issue summary ... add change record ...
But, i saw no one.

Maybe @longwave, as "initiator" could check it.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

vasike’s picture

Status: Needs work » Needs review

solved MR conflicts.

longwave’s picture

After looking at the new approach I'm still not entirely convinced this is worthwhile, and this only feels like a stopgap; somehow we have to later remove non-NegatableConditionInterfaces, but also we probably want to allow conditions to not actually support negation (like in the boolean case where it makes no sense), but not sure how to achieve that.

longwave’s picture

This is ultimately a framework manager decision so let's tag them for input.

catch’s picture

Status: Needs review » Needs work

Like #78 I'm not sure whether this gets us to the eventual end point and it doesn't look like much of an improvement on its own, if that's known, can follow-up issues be opened with an explanation?

I would be useful if the issue summary and a change record were fleshed out too, so marking needs work for that.