Problem/Motivation
The documentation for #after_build
in FormBuilderInterface.php
describes the following:
$element['#after_build']: An array of callables called after self::doBuildForm() is done with its processing of the element. These are called in postorder traversal, meaning they are called for the child elements first, then for the parent element.
Using a closure as an #after_build
callable will result in a "Serialization of 'Closure' is not allowed" exception when the element is serialized.
One example is when this occurs is when submitting a Webform with the "Inline" or "Modal" confirmation type, and with a (custom) Webform element that uses #after_build
as described above. The form array is serialized to be cached, which triggers the above exception.
Steps to reproduce
Any form that gets serialized can be used to test this, but I'll write out the Webform example here:
Create a custom Webform element and use the #after_build
property:
$form['#after_build'][] = function (
array $form,
FormStateInterface $form_state
) {
return $form;
};
- Add the custom Webform element to a Webform and set its confirmation type to "Inline" or "Modal".
- Fill in the Webform, triggering the error.
Proposed resolution
There are already a few issues to standardise how callables are handled:
-
https://www.drupal.org/project/drupal/issues/2966711
📌
Limit what can be called by a callback in form arrays
Needs work
suggests limiting the kinds of callables, but still includes closures. This could be updated to not include closures.
-
https://www.drupal.org/project/drupal/issues/2982949
📌
Introduce CallableResolver to help standardise the DX and error handling for callbacks across various subsystems
Fixed
introduces a resolver for callables.
In both cases, the documentation for #after_build
should be updated to reflect the recommended solution. At the time of writing, the documentation has not yet changed.