I've just encountered an issue where the validation of the Source code for a webform fails to catch a misconfiguration on a Radio element resulting in a PHP error. It looks like this misconfiguration isn't being caught when validating the source code after saving.

It seems to happen only when adding the element to the Source view rather than adding it as an element and then editing it in Source view. I have not tested whether the issue also occurs on other element types.

I encountered this issue on a site with beta19, and then recreated it on another site with beta23.

To recreate the error, try adding the following element via Source to an existing form.

charges:
  '#type': radios
  '#title': Charges
  '#options':
    if_free: 'I want this item only if it is free'
    check_first: 'Please advise me of any cost'
    will_pay: 'I will pay any cost'
    '#required': true

It seems that with the '#required' element being indented 4 spaces instead of 2, it gets treated as another radio option. It is added as the value for the new option (including the #) but the title is left empty.

This causes a 500 error when you visit the form, with this in the log:

PHP Fatal error: Unsupported operand types in /var/www/html/mysite/web/core/lib/Drupal/Core/Render/Element/Radios.php on line 68, referer: http://mysite.com/admin/structure/webform/manage/contact/source

Once this happens, the only way to resolve it is to return to the Elements view and delete the element.

You can't simply fix it in Source view - the error keeps occurring, and editing the element in Elements view doesn't work because it seems that this issue also breaks Drupal's Ajax on the page, meaning that you can't save your changes when editing the element (you also can't edit or add any other elements for that form).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

millionleaves created an issue. See original summary.

jrockowitz’s picture

Status: Active » Needs review
FileSize
709 bytes

I was also randomly seeing this issue and did not figure out that is specific to PHP7 until now.

The attached patch should catch the exception being thrown.

millionleaves’s picture

Thanks - much appreciated.

However, it looks like the patch only addresses PHP7, which neither of my sites are running. One is on PHP 5.5.9 and the other is on 5.6.31. I tried the patch in the latter, but it didn't fix the issue.

jrockowitz’s picture

The attached patch still works for PHP7 but I need you to check 5.6.

millionleaves’s picture

Thanks again.

I couldn't apply the patch using my normal method - not sure why:

patch -p1 < source_validation_fails-2922331-4.patch

patching file p1
Hunk #1 FAILED at 340.
Hunk #2 FAILED at 358.
2 out of 2 hunks FAILED -- saving rejects to file p1.rej
patching file p1
Hunk #1 FAILED at 683.
1 out of 1 hunk FAILED -- saving rejects to file p1.rej
patch: **** Can't reopen file p1 : No such file or directory

I applied the changes manually, but the behaviour on saving the malformed element is unchanged on PHP5.6.

jrockowitz’s picture

I am not up for downgrading my dev environment to PHP 5.6. Someone is going to have step in and tweak the patch.

I will probably commit the patch because it does improve the overall exception handling because it now uses restore_error_handler() and restore_exception_handler().

millionleaves’s picture

Thanks. I have another site on the same server where I can try the patch again later. Otherwise, at least we have the issue understood and a workaround provided.

jrockowitz’s picture

The attached patch adds more inline comments to help explain what is going on to any reviewing this code and hopefully helping us fix the issue in PHP 5.6.

  • jrockowitz committed 8fe974e on 8.x-5.x
    Issue #2922331 by jrockowitz, millionleaves: Source validation fails on...
jrockowitz’s picture

Status: Needs review » Postponed (maintainer needs more info)

I committed the patch. I am marking this issue as postponed hoping someone with PHP 5.6 can help.

jrockowitz’s picture

Status: Postponed (maintainer needs more info) » Closed (cannot reproduce)
millionleaves’s picture

Status: Closed (cannot reproduce) » Active

Steps to reproduce on a site running PHP 5.6.32, Drupal 8.4.2, Webform 8.x-5.0-beta24.

  • Add a radio or checkboxes element to a form
  • Mark the element as Required
  • Switch to Source view
  • Indent the Required element two spaces so it looks like this:
test:
  '#type': checkboxes
  '#title': Test
  '#options':
    'on': 'on'
    'off': 'off'
	'#required': true
  • Switch back to Elements view.
  • Now, when you attempt to edit any element, before or after this element, you will be unable to save changes and the browser console will report an Ajax error:

↵An AJAX HTTP error occurred.↵HTTP Result Code: 500↵Debugging information follows.↵Path: /admin/structure/webform/manage/contact_us/element/message/edit?_wrapper_format=drupal_modal&ajax_form=1↵StatusText: Internal Server Error↵ResponseText: "

  • Similarly, if you switch to Source view and edit any element (including fixing the #required element indentation), saving the Source view edits will generate a 500 error.

I tested this with and without the patch in #8. The only solution appears to be to remove this component.

[edited for formatting]

jrockowitz’s picture

I recommend that everyone start upgrading to PHP 7. @see #2842431: [policy] Remove PHP 5.5, 5.6 support in Drupal 8.7

Patches to support PHP 5.6 are welcome but most Drupal devs are probably using PHP 7

jrockowitz’s picture

Status: Active » Postponed (maintainer needs more info)
bucefal91’s picture

Version: 8.x-5.0-beta23 » 8.x-5.x-dev
Status: Postponed (maintainer needs more info) » Active

That's the kind of moment when I praise my Gentoo Linux 8-) Switching PHP versions is dead easy there.

millionleaves, my experience is a little bit different from yours. I am running PHP 5.6.33. Everything I describe below is done on the latest webform 8.x-5.x-dev

So, if I submit the following (let's call it case #1):

charges:
  '#type': radios
  '#title': Charges
  '#options':
    if_free: 'I want this item only if it is free'
    check_first: 'Please advise me of any cost'
    will_pay: 'I will pay any cost'
    '#required': true

It dies with a PHP fatal error, but it does not save the messed up Yaml into database, so just hitting the "back" button gets you back in business as if you never submitted malformed Yaml.

Then I tried (let's call it case#2):

test:
  '#type': checkboxes
  '#title': Test
  '#options':
    'on': 'on'
    'off': 'off'
      '#required': true

It just shows me a nice form validation error message that reads: Elements (YAML) is not valid. Unable to parse at line 7 (near " '#required': true").

For the case #1: the submitted Yaml syntax is correct, but the way Webform then tries to leverage the parsed Yaml brings to a PHP fatal error. Thus this case contains not a syntax but a logical error (the option #required should have a string value but it has a boolean value.) It's not easy to build a logical validator, and basically, it's more of a developer user interface. One is expected to know the rules of the game once he crosses the boundary between a user and a developer. Additionally, catching fatal errors in PHP 5 is barely possible and using my judgment it's not worth the effort in this case.

For the case #2: It behaves very good - you supply a malformed Yaml (syntax error) and it warns you in a very user-friendly way.

millionleaves, can you try the 2 cases on the latest webforms? maybe something has changed? Side effects of this bug as you describe definitely should be fixed somehow, but the side effects I've encountered myself while testing seems much less hurtful and are not so easy to fix. So I want to see maybe I am missing something to bring the side effects to the level of yours :)

jrockowitz’s picture

Status: Active » Closed (won't fix)

I am marking this as won't fix since PHP 5.6 is not actively supported and this is not a showstopping issue.

@bucefal91 I think the nuance of this bug is some people have the native PHP-YAML add-on installed which Drupal will use. If the PHP-YAML add-on is not installed Drupal falls back to Symfony's built-in YAML parser.