From 1c017c0e81e9682a6f42d39be9fb126462411ac2 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 15 Apr 2024 22:48:29 +0100
Subject: [PATCH] Issue #3427174 by longwave, quietone, dineshkumarbollu,
 mondrake, alexpott: Throw exception when calling NestedArray::setValue() when
 parents reference a non-array value instead of causing a PHP error

---
 core/.phpstan-baseline.php                                 | 6 ------
 core/lib/Drupal/Component/Utility/NestedArray.php          | 7 +++++--
 .../Drupal/Tests/Component/Utility/NestedArrayTest.php     | 4 ++++
 core/tests/Drupal/Tests/Core/Config/ConfigTest.php         | 3 ++-
 4 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php
index 9f651bdc7b81..9dec751955e7 100644
--- a/core/.phpstan-baseline.php
+++ b/core/.phpstan-baseline.php
@@ -2146,12 +2146,6 @@
 	'count' => 1,
 	'path' => __DIR__ . '/tests/Drupal/Tests/Composer/ComposerTest.php',
 ];
-$ignoreErrors[] = [
-	'message' => '#^Call to deprecated method expectError\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\:
-https\\://github\\.com/sebastianbergmann/phpunit/issues/5062$#',
-	'count' => 1,
-	'path' => __DIR__ . '/tests/Drupal/Tests/Core/Config/ConfigTest.php',
-];
 $ignoreErrors[] = [
 	'message' => '#^Trying to mock an undefined method getRevisionId\\(\\) on class Drupal\\\\Tests\\\\Core\\\\Entity\\\\UrlTestEntity\\.$#',
 	'count' => 1,
diff --git a/core/lib/Drupal/Component/Utility/NestedArray.php b/core/lib/Drupal/Component/Utility/NestedArray.php
index c9feb599d1b4..354d58183201 100644
--- a/core/lib/Drupal/Component/Utility/NestedArray.php
+++ b/core/lib/Drupal/Component/Utility/NestedArray.php
@@ -138,7 +138,7 @@ public static function &getValue(array &$array, array $parents, &$key_exists = N
    * @param bool $force
    *   (optional) If TRUE, the value is forced into the structure even if it
    *   requires the deletion of an already existing non-array parent value. If
-   *   FALSE, PHP throws an error if trying to add into a value that is not an
+   *   FALSE, throws an exception if trying to add into a value that is not an
    *   array. Defaults to FALSE.
    *
    * @see NestedArray::unsetValue()
@@ -149,7 +149,10 @@ public static function setValue(array &$array, array $parents, $value, $force =
     foreach ($parents as $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 ($force && isset($ref) && !is_array($ref)) {
+      if (isset($ref) && !is_array($ref)) {
+        if (!$force) {
+          throw new \LogicException('Cannot create key "' . $parent . '" on non-array value.');
+        }
         $ref = [];
       }
       $ref = &$ref[$parent];
diff --git a/core/tests/Drupal/Tests/Component/Utility/NestedArrayTest.php b/core/tests/Drupal/Tests/Component/Utility/NestedArrayTest.php
index f1d4436a5cfc..a48d5bab73f8 100644
--- a/core/tests/Drupal/Tests/Component/Utility/NestedArrayTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/NestedArrayTest.php
@@ -87,6 +87,10 @@ public function testSetValue() {
     NestedArray::setValue($this->form, $this->parents, $new_value);
     $this->assertSame('New value', $this->form['details']['element']['#value'], 'Changed nested element value found.');
     $this->assertTrue($this->form['details']['element']['#required'], 'New nested element value found.');
+
+    $this->expectException(\LogicException::class);
+    $this->expectExceptionMessage('Cannot create key "child" on non-array value.');
+    NestedArray::setValue($this->form, ['details', 'element', '#value', 'child'], $new_value);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
index 29032f8f54db..4ba8a31f727c 100644
--- a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
@@ -276,7 +276,8 @@ public function testSetIllegalOffsetValue() {
     $this->config->set('testData', 1);
 
     // Attempt to treat the single value as a nested item.
-    $this->expectError();
+    $this->expectException(\LogicException::class);
+    $this->expectExceptionMessage('Cannot create key "illegalOffset" on non-array value.');
     $this->config->set('testData.illegalOffset', 1);
   }
 
-- 
GitLab