PHP Fatal error due to FormStateValueResolver

Created on 1 June 2023, about 1 year ago
Updated 5 October 2023, 9 months ago

Problem/Motivation

The FormStateValueResolver component from mongodb_watchdog module trigger fatal errors in specifics conditions.
Fatal error occurs when an external form use extra build optionnal parameters combine with a route which don't use this parameters.

All undefined parameters in url will be replaced with a FormState object because of FormStateValueResolver.

By example, in ThemeSettingsForm class from core :

[...]
public function buildForm(array $form, FormStateInterface $form_state, $theme = '')
[...]

with the route 'system.theme_settings' defined in system.routing.yml :

[...]
system.theme_settings:
  path: '/admin/appearance/settings'
  defaults:
    _form: '\Drupal\system\Form\ThemeSettingsForm'
    _title: 'Appearance settings'
  requirements:
    _permission: 'administer themes'

system.theme_settings_theme:
  path: '/admin/appearance/settings/{theme}'
  defaults:
    _form: '\Drupal\system\Form\ThemeSettingsForm'
    _title_callback: 'theme_handler:getName'
  requirements:
    _permission: 'administer themes'
[...]

In this case, $theme value will be a FormState object when the form is called from route system.theme_settings, instead of expected string.

Steps to reproduce

Install Drupal 9.5.9 and mongodb_watchdog module.
Go to /admin/appearance/settings

Proposed resolution

I don'd really understand the need of FormStateValueResolver component in mongodb_watchdog module so maybe I'm wrong, but the condition in supports method seems suspicious to me.

  public function supports(Request $request, ArgumentMetadata $argument) {
    $argumentInterfaceMatches = $argument->getType() === FormStateInterface::class;
    $requestAttributeExists = $request->attributes->has(static::NAME_LEGACY);
    return $argumentInterfaceMatches || $requestAttributeExists;
  }

When argument is empty and different from 'form_state', the value of $requestAttributeExists is TRUE and the component catch the argument value resolution.
Don't we need a '&&' instead of '||' here ?

🐛 Bug report
Status

Fixed

Version

2.0

Component

Watchdog (logger)

Created by

🇫🇷France klelostec

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @klelostec
  • 🇫🇷France fgm Paris, France

    Thanks for the report, it seems already fairly detailed: are you able to write a test case failing (or even crashing) because of this error ?

  • Status changed to Postponed: needs info about 1 year ago
  • 🇫🇷France fgm Paris, France

    Downgrading because it doesn't prevent use of the module.

    Also, you reported this on alpha1, but the most current release has been alpha2 since 08/2022: is it just an issue error, or is the issue really on alpha1 ? If that is the case, can you reproduce it on alpha2 or dev HEAD ?

  • 🇫🇷France klelostec

    I change the version from alpha1 to alpha2 because I'm currently having the problem with the alpha2 version.
    I check the git to understand when the component FormStateValueResolver was added and it was on alpha1 that's why I've reported this version.
    I did some additional tests and I can reproduce the issue on alpha1, alpha2 and 2.x-dev versions.

    About writing a test case with PHPUnit, I don't know how to do it for this tricky problem, but I'll give it a try and complete this issue when done.

  • Status changed to Active about 1 year ago
  • Status changed to Needs review about 1 year ago
  • 🇫🇷France klelostec

    I do my best to understand why the component FormStateValueResolver has been used and the issue 3006502 🐛 Core should not rely on $form_state name Needs work helps me a lot.

    I disagree with the note https://www.drupal.org/project/mongodb/issues/3005108#comment-12813643 and consider the problem should be fixed in core, not in a contrib module but i respect the decision to implements the fix here, so the MR fix the issue and add 2 test cases in this way.

  • Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MariaDB 10.3.22
    last update about 1 year ago
    Waiting for branch to pass
  • @klelostec opened merge request.
  • 🇫🇷France fgm Paris, France

    Thanks, I'll look into it when I have some time (it's hectic these days).

    Not sure why you say you disagree with my comment ? My point is (a) this is due to core relying on the param name (hence 🐛 Core should not rely on $form_state name Needs work ) but (b) core won't move for years (that core issue is already 3.5 years old), hence (c) work around the issue in our contrib module. Which part(s) do you disagree with ?

  • 🇫🇷France klelostec

    I disagree with the quote below

    Trivial non satisfying (IMHO) solution: rename the argument. Better solution: support it, pretty much like route matches are supported based on class instead of name using the @argument_resolver.route_match service

    In my opinion, because of the impact on core/others modules,

    the work around with the argument resolver component is too risky compare to just renaming the variable $formState -> $form_state in the buildForm methods of mongodb_watchdog forms.

    When I encounter the issue, It was hard to understand and found the origin.

    But thanks, I learn a new

  • 🇫🇷France fgm Paris, France

    Thanks for clarifying your PoV. I do not share it, but it does make sense.

  • Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MariaDB 10.3.22
    last update 9 months ago
    Waiting for branch to pass
  • 🇫🇷France fgm Paris, France

    This being said, your fix looks good to me, thanks.

  • Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MariaDB 10.3.22
    last update 9 months ago
    Waiting for branch to pass
  • Status changed to Fixed 9 months ago
  • 🇫🇷France fgm Paris, France

    Merged to current HEAD, thanks.

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

Production build 0.69.0 2024