If NestedArray::setValue() is called with an array structure that does not support adding the value at the given position, the page crashes with a
Warning: Illegal string offset 'id' in Drupal\Component\Utility\NestedArray::setValue()
and
Fatal error: Cannot create references to/from string offsets nor overloaded objects in /[..]/core/lib/Drupal/Component/Utility/NestedArray.php
The method does have a $force
parameter which can prevent such situations.
But in some cases it is called without this parameter.
E.g. in FormBuilder::handleInputElement()
we find this code:
NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
For me this blew up when I changed a form structure in PHP, and then resubmitted the form.
So it could happen when a user resubmits a form at the moment of a deployment.
It's a bit rare maybe but still..
A way to artificially reproduce this with custom code:
$array = ['x' => 'X'];
$parents = ['x', 'y'];
$value = 5;
$force = false;
\Drupal\Component\Utility\NestedArray::setValue($array, $parents, $value, $force);
My main point here is that the NestedArray::setValue() method is not programmed in a robust way.
It has no well-defined behavior in a case like above.
My first solution would be to let it throw an exception instead.
E.g. like this:
public static function setValue(array &$array, array $parents, $value, $force = FALSE) {
$ref = &$array;
foreach ($parents as $i => $parent) {
// PHP auto-creates container arrays and NULL entries without error if $ref
// is NULL, but throws an error if $ref is set, but not an array.
if (NULL !== $ref && !is_array($ref)) {
if ($force) {
$ref = [];
}
else {
$conflict_parents = [];
foreach ($parents as $j => $theparent) {
if ($i === $j) {
break;
}
$conflict_parents[] = $theparent;
}
$parents_str = '$[' . implode('][', $parents) . ']';
$conflict_parents_str = '$[' . implode('][', $conflict_parents) . ']';
$type_at_ref = gettype($ref);
throw new \RuntimeException("Tried to set nested value at $parents_str, but found a $type_at_ref at $conflict_parents_str.");
}
}
$ref = &$ref[$parent];
}
$ref = $value;
}
This may be a bit overkill, but most of the added logic is only executed if the exception is going to be thrown.
The logic takes into account that $parents might not be cleanly indexed.
In the above artificial example, the output would be:
RuntimeException: Tried to set nested value at $[x][y], but found a string at $[x]. in Drupal\Component\Utility\NestedArray::setValue()
This is already better, isn't it?
Needs work
11.0 π₯
Last updated
The issue particularly affects sites running on PHP version 8.0.0 or later.
Not all content is available!
It's likely this issue predates Contrib.social: some issue and comment data are missing.