Problem/Motivation

When adding a workflow state with an integer ID / machine name the error Error: Call to a member function id() on integer is thrown.

Proposed resolution

The error happens because of in \Drupal\workflows\Entity\Workflow::getStates we sort the states using array_multisort(). This gets confused when we have a numeric ID / label.

Remaining tasks

Find someone who can wrangle array_multisort().

User interface changes

API changes

Data model changes

Comments

timmillwood created an issue. See original summary.

manuel garcia’s picture

Title: Adding a workflow state with an integer ID breaks things » Adding a workflow state or transition with an integer ID breaks things

This also happens if you add a transition.
If you do happen to create a state or transition with a numeric machine name then the workflow edit page just shows you a blank page: The website encountered an unexpected error. Please try again later.

Adding a workflow with a numeric machine name is fine.

timmillwood’s picture

This is because the same sorting is done in \Drupal\workflows\Entity\Workflow::getTransitions.

timmillwood’s picture

Status: Active » Needs review
StatusFileSize
new7.31 KB

So going with the solution of, instead of making this work, prevent numeric only machine names for states and transitions.

In doing this I found the exceptions we throw in addState and addTransition actually prevent the validation from running, so fixing that too.

manuel garcia’s picture

Status: Needs review » Needs work

Looks good @timmillwood - this currently prevents the user from breaking things.

+++ b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
@@ -111,6 +111,12 @@ public function testWorkflowCreation() {
+    $this->assertSession()->pageTextContains('The machine-readable name must contain only lowercase letters, numbers, and underscores.');

@@ -118,6 +124,11 @@ public function testWorkflowCreation() {
+    $this->assertSession()->pageTextContains('The machine-readable name must contain only lowercase letters, numbers, and underscores.');

We'll need to update this text in the add forms and the tests to reflect the new restriction.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new5.09 KB
new8.28 KB

Updated the error text.

manuel garcia’s picture

Issue summary: View changes
Status: Needs review » Needs work
StatusFileSize
new20.9 KB

Thanks @timmillwood, errors look fine now.
We should change the #description for the Machine-readable name fields as well:

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new1.41 KB
new8.63 KB
new37.35 KB

Updating machine-name description:

manuel garcia’s picture

Status: Needs review » Reviewed & tested by the community

Perfect thanks @timmillwood, from my perspective this is good to go now.

tstoeckler’s picture

Status: Reviewed & tested by the community » Needs review

So the try-catch is explained in #4 with:

In doing this I found the exceptions we throw in addState and addTransition actually prevent the validation from running, so fixing that too.

Not 100% sure about this, though. Over in #2842193: Exception in Workflow::addState when an invalid machine name is given we came to the agreement, that, although it is ugly, we should explicitly duplicate the validation ::copyFormValuesToEntity(). This seems to go against that. I am definitely against it, but even if we do go with a try-catch:

  1. I think we should remove the validation introduced in #2842193: Exception in Workflow::addState when an invalid machine name is given as it is then obsolete.
  2. We should be catching something more specific than \Exception. If the database server dies in the middle of the request,
    it should not be handled equivalently to someone inputting an invalid value in a form.

Marking needs review, though, to get some more opinions on this.

manuel garcia’s picture

StatusFileSize
new1.76 KB
new8.58 KB

Thanks @tstoeckler for the review - tackling #10.1 and #10.2 here.

tstoeckler’s picture

+++ b/core/modules/workflows/src/Form/WorkflowStateAddForm.php
@@ -89,7 +89,7 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
       // Replicate the validation that Workflow::addState() does internally as the
       // form values have not been validated at this point.

This can also be removed then.

Again, though, I am not endorsing the try-catch. I would actually prefer duplicating the validation. But let's hear what others think.

manuel garcia’s picture

StatusFileSize
new739 bytes
new8.44 KB

Argh - good catch... removing the comments as well.

Yup, we should have more input on this... just making the changes you suggested because they make sense, in case we do want to go the try/catch route =)

sam152’s picture

Component: content_moderation.module » workflows.module
timmillwood’s picture

Status: Needs review » Reviewed & tested by the community

In #2830584: Use modals for creating, updating, and deleting workflows, with a new DialogFormTrait it was agreed to go with the try/catch, so it looks like this one can go back to RTBC again.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 13: 2886567-13.patch, failed testing. View results

manuel garcia’s picture

Issue tags: +Needs reroll
vj’s picture

Status: Needs work » Needs review
StatusFileSize
new7.88 KB

Patch rerolled

Status: Needs review » Needs work

The last submitted patch, 18: state_transition_as_integer-2886567-18.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

timmillwood’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new3.64 KB
new9 KB

Fixing broken tests.

Status: Needs review » Needs work

The last submitted patch, 20: 2886567-20.patch, failed testing. View results

vj’s picture

Status: Needs work » Needs review
StatusFileSize
new9.13 KB

@timmillwood Thanks for fixing tests.

Patch re-rolled.

manuel garcia’s picture

Status: Needs review » Needs work

Looking good!

+++ b/core/modules/workflows/src/Form/WorkflowStateAddForm.php
@@ -82,17 +86,20 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
+          // @todo move to submitStateConfigurationForm. #2849827.

#2849827: Move workflow "settings" setters and getters to WorkflowTypeInterface has landed now :) - Im guessing this affects WorkflowTransitionAddForm as well.

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

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now 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.

sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new3.14 KB

I think we should explore supporting integer IDs, any reason we abandon that so quickly? Seems like it'd be good for consistency, especially given even the workflow entity itself can have numeric IDs. I've opened #2899187: For consistency, workflow states and transitions should support numeric machine names to have a look at this.

One thing I found looking into this is that we previously took the approach in #2842193: Exception in Workflow::addState when an invalid machine name is given to duplicate the machine name validation logic in copyFormValuesToEntity so we didn't trigger the exception. Why didn't we apply the same treatment to transitions? Because the transition forms already had a pretty good solution for this.

I think we should slightly rejig things, so this issue becomes "Make validation in transition and state forms consistent" and the prior issue I linked dedicated to making ID based machine name handling consistent. That way we avoid our snow-flake error messages about not containing only numbers etc.

If we decide to do that, here is a patch which introduces that consistency. Proposed issue name:

Don't reimplement validation rules for workflow state add/edit forms in ::copyFormValuesToEntity and make them consistent with transition forms

timmillwood’s picture

Status: Needs review » Needs work

@Sam152 With #26 we still get the same issue which stems from \Drupal\workflows\Plugin\WorkflowTypeBase::labelWeightMultisort not sorting them correctly.

I think we need a separate issue to not reimplement the validation, rather than changing the scope of this one. This should focus on the issue of using integers for state and transition IDs.

Changing the validation to prevent integers IDs is a good first step, we could then have a follow up to fix the sorting, thus allow integer IDs again.

sam152’s picture

Ah, I logged #2899187: For consistency, workflow states and transitions should support numeric machine names to sort the numeric IDs, but makes sense to use this issue as well.

sam152’s picture

I've logged #2899248: Don't reimplement validation rules for workflow state add/edit forms in ::copyFormValuesToEntity to capture the validation rejig, looking into numeric IDs now.

sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new1.17 KB
new2.11 KB

Attached brings id handling into line, by allowing numeric IDs.

timmillwood’s picture

Status: Needs review » Reviewed & tested by the community

Manually tested, seems to work perfectly!

Thanks @Sam152

sam152’s picture

Issue summary: View changes

Removing screenshot of the error message introduced in earlier patches from the IS.

The last submitted patch, 30: 2886567-30_TEST_ONLY.patch, failed testing. View results

xjm’s picture

Version: 8.5.x-dev » 8.4.x-dev
Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
@@ -282,14 +282,17 @@ public function getTransitions(array $transition_ids = NULL) {
-        $labels, SORT_NATURAL, SORT_ASC
+        $labels, SORT_NATURAL, SORT_ASC,
+        $keys

This is kind of weird formatting for the parameters; it doesn't really follow our coding standards. Either the parameters should be all on one line, or they should be each on a separate line. I'd suggest the former.

Also, let's add inline comments explaining the sort logic, since it's totally not obvious by reading the method itself why we're doing this. The test coverage proves the fix nicely, but we should also make it clear in the code.

Thanks!

I think this is a patch-safe backport as it should be BC for the normal non-integer ID case.

timmillwood’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new1.31 KB
new2.48 KB

I've opened an issue #2900700: Add orderby to content_moderation schema sequences to order the states and transitions in the config object, but this is not to be confused by the sorting for display and use within Drupal, which is what this issue is fixing.

Changing the formatting and adding some inline comments as @xjm suggests in #34.

Moving back to RTBC as there are no code changes.

vj’s picture

Tested patch #35. Tested add new state & add new transition, works without any error.

wim leers’s picture

but this is not to be confused by the sorting for display and use within Drupal,

Indeed, #2900700: Add orderby to content_moderation schema sequences is conceptually related, but very different.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 35: 2886567-35.patch, failed testing. View results

timmillwood’s picture

Status: Needs work » Reviewed & tested by the community
xjm’s picture

Title: Adding a workflow state or transition with an integer ID breaks things » Adding a workflow state or transition with an integer ID results in unrecoverable fatal errors
Issue summary: View changes
Priority: Normal » Major
Issue tags: +Triaged core major

Promoting to major according to the criteria in https://www.drupal.org/core/issue-priority#major-bug; this results in a fatal error and renders the workflow module unusable based on actions taken only in the UI and does not have a workaround.

xjm’s picture

Issue summary: View changes

So a couple of concerns here still:

The test-only patch doesn't show the fatal error; it just shows the invalid sort. This is presumably because it's a unit test. This means we don't have any test coverage exercising the case where the fatal error is thrown.

Even with the added comments I'm still struggling to see how the fatal is triggered and how the change fixes it; this could be a combination of array_multisort() being evil and insufficient caffeine.

Edit: Left at RTBC because this is most likely still a me-problem.

xjm’s picture

Okay, so part of my confusion is with the documentation of labelWeightMultisort(). This method is passed a list of state or transition keyed by state or transition ID, but the documentation for the method does not suggest a meaningful key/value association.

I'm wondering if we need a deeper fix; I keep coming back to words like "brittle" and "code smell". Going to get a second opinion since it might be worthwhile to fix the major with the quickfix here and then improve it more in a followup, except I haven't quite decided yet what that followup would be exactly so it's not very actionable feedback.

Thanks, and sorry for the delay reviewing this and other issues.

xjm’s picture

Okay, discussed with @catch; we agreed it at least needs an additional functional test case that exercises the fatal. The inline comments also could spell out what's happening a bit more explicitly; always a good idea when there's lots of array manipulation.

I did manually test and confirm that this patch does indeed fix the fatal.

I realize now why the array was formatted as it was in #30; it was grouping each array with its flags with the array_multisort(). Sorry for the kneejerk about that. While I think it may violate coding standards, it also does make the function bit more readable. I also realized it was out of scope, because the array is already sorted this way. So we can revert those changes and it was a mistake on my part to push back on them here.

This is reminding me of all the reasons I hate array_multisort(). :P

Another thing I'd want to see (probably also out of scope) is fixing the documentation for this method to indicate that the key/value association is important. However, that plus the need for inline comments to explain what's going wrong (the current comments still aren't quite enough for me to understand what's going to break just by reading the code) plus the existence of the bug in the first place makes me wonder if these methods shouldn't just be refactored. I don't have a concrete suggestion right now on how to improve them, and I don't want to block what is a nasty major on a vague request for refactoring, but maybe @Sam152 or @timmillwood has ideas? Are there past issues related to this?

timmillwood’s picture

I think adding a functional test for creating a state and transition with an numeric machine name and label is a reasonable request. Then we can have a follow up to look at other sorting options.

I'm not going to get to this today, but will take a look in the morning unless @Sam152 or anyone else takes a look first.

timmillwood’s picture

Assigned: Unassigned » timmillwood

Working on a test for this.

timmillwood’s picture

Assigned: timmillwood » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new4.87 KB
new103.24 KB

Here's a UI test to test creating a workflow with a numeric ID and label, with a state and a transition with a numeric ID and label.
Also reverted the array_multisort(), updated some comments, and fixed empty ComplexTestType forms.

timmillwood’s picture

StatusFileSize
new4.87 KB
new103.24 KB

Adding the interdiff from #46 as a test-only patch, and re-uploading the real patch from #46 so the latest patch is still the correct one.

The last submitted patch, 47: 2886567-47-test-only.patch, failed testing. View results

timmillwood’s picture

StatusFileSize
new4.98 KB
new6.56 KB

Let's try that again, I was diffing my 8.5.x codebase against 8.4.x, oops!

The last submitted patch, 49: 2886567-49-test-only.patch, failed testing. View results

timmillwood’s picture

StatusFileSize
new1.65 KB
new5.17 KB
new6.75 KB

Looks like the issue only surfaces with two or more states.

The last submitted patch, 51: 2886567-51-test-only.patch, failed testing. View results

xjm’s picture

+++ b/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
@@ -277,15 +277,23 @@ public function getTransitions(array $transition_ids = NULL) {
         $labels[$id] = $object->label();
...
-        $labels, SORT_NATURAL, SORT_ASC
+        $labels, SORT_NATURAL, SORT_ASC,

So $labels is never used again after this... why is it there?

Edit: Because it's the secondary sort for the weights which we do return, disregard. But really inline comments would be good.

timmillwood’s picture

Because if there are two with the same weight it then sorts by label?

xjm’s picture

@timmillwood, yeah, saw that on second read; just edited my comment. Sorry, I hate array_multisort(). :P Inline comments would really help. Like:

      // Sort weights, labels, and keys in the same order as each other.
       array_multisort(
         // Use the numerical weight as the primary sort.
         $weights, SORT_NUMERIC, SORT_ASC,
         // When objects have the same weight, sort them alphabetically by label.
        $labels, SORT_NATURAL, SORT_ASC
        // Ensure that the keys (the object IDs) are sorted in the same order as the weights.
       );
      // Combine the sorted keys and weights to make sure the weights are keyed with
      // the correct keys.
      $weights = array_combine($keys, $weights);
      // Return the objects sorted by weight and label.

And there should be test cases for sorting with duplicate weights, duplicate labels, etc.

xjm’s picture

timmillwood’s picture

StatusFileSize
new3.47 KB
new9.11 KB

Here we are @xjm, added your comment suggestions, and a test to test the sorting.

xjm’s picture

+++ b/core/modules/workflows/src/Plugin/WorkflowTypeBase.php
@@ -277,15 +277,26 @@ public function getTransitions(array $transition_ids = NULL) {
+        // Ensure that the keys (the object IDs) are sorted in the same order as the weights.

Minor issue here with the comment being over the 80 char line.

On a quick scan, the added tests and comments look great. Thanks @timmillwood.

timmillwood’s picture

StatusFileSize
new835 bytes
new9.12 KB

Serves me right for just copying and pasting from #55.

Status: Needs review » Needs work

The last submitted patch, 59: 2886567-59.patch, failed testing. View results

timmillwood’s picture

Status: Needs work » Needs review
xjm’s picture

Status: Needs review » Reviewed & tested by the community

Alright, this looks good to me. Thanks @timmillwood for adding the tests. I keep thinking there must be a better way but that's not actually helpful and no reason to hold back a major bugfix.

  • catch committed 76fd2bd on 8.5.x
    Issue #2886567 by timmillwood, Sam152, Manuel Garcia, Vj, xjm,...

  • catch committed 88ec031 on 8.4.x
    Issue #2886567 by timmillwood, Sam152, Manuel Garcia, Vj, xjm,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

No comments other than what xjm said in #62, so:

Committed/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.