From af31e19328b091a7016ac6c28872f68f2d5fb8a2 Mon Sep 17 00:00:00 2001
From: Daniel Goerz <dlg@lightwerk.com>
Date: Thu, 20 Apr 2017 15:46:16 +0200
Subject: [PATCH] [BUGFIX] Ensure extractDottedPathToLastElement() always
 returns a string

Resolves: #80919
Releases: master, 8.7
Change-Id: I3fddc6e83a117d25ec7abeb7d82130275321c2c7
Reviewed-on: https://review.typo3.org/52519
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Jan Helke <typo3@helke.de>
Tested-by: Jan Helke <typo3@helke.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 .../Hooks/DataStructureIdentifierHook.php     | 16 ++--
 .../Hooks/DataStructureIdentifierHookTest.php | 79 +++++++++++++++++++
 2 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php
index 0079ff7ef1dd..743e333265f6 100644
--- a/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php
+++ b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php
@@ -172,7 +172,7 @@ class DataStructureIdentifierHook
             $sheetElements = [];
             foreach ($finisherValue['options'] as $optionKey => $optionValue) {
                 if (is_array($optionValue)) {
-                    $optionKey = $optionKey . '.' . $this->extractDottedPathToLastElement($finisherValue['options'][$optionKey]);
+                    $optionKey = $optionKey . '.' . $this->implodeArrayKeys($finisherValue['options'][$optionKey]);
                     try {
                         $elementConfiguration = ArrayUtility::getValueByPath(
                             $finishersDefinition[$finisherIdentifier]['FormEngine']['elements'],
@@ -249,16 +249,16 @@ class DataStructureIdentifierHook
     /**
      * Recursive helper to implode a nested array to a dotted path notation
      *
-     * @param array $array
+     * ['a' => [ 'b' => 42 ] ] becomes 'a.b'
+     *
+     * @param array $nestedArray
      * @return string
      */
-    protected function extractDottedPathToLastElement(array $array): string
+    protected function implodeArrayKeys(array $nestedArray): string
     {
-        $dottedPath = key($array);
-        foreach ($array as $key => $value) {
-            if (is_array($value)) {
-                $dottedPath = $dottedPath . '.' . $this->extractDottedPathToLastElement($value);
-            }
+        $dottedPath = (string)key($nestedArray);
+        if (is_array($nestedArray[$dottedPath])) {
+            $dottedPath .= '.' . $this->implodeArrayKeys($nestedArray[$dottedPath]);
         }
         return $dottedPath;
     }
diff --git a/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php
index f5f837951b7d..1f5b2cfa8b09 100644
--- a/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php
+++ b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php
@@ -236,4 +236,83 @@ class DataStructureIdentifierHookTest extends \TYPO3\TestingFramework\Core\Unit\
 
         $this->assertEquals($expected, $result);
     }
+
+    /**
+     * Data provider for implodeArrayKeysReturnsString
+     *
+     * @return array
+     */
+    public function implodeArrayKeysReturnsStringDataProvider()
+    {
+        return [
+            'One string' => [
+                [
+                    'a' => 'b',
+                ],
+                'a'
+            ],
+            'Two strings' => [
+                [
+                    'a' => [
+                        'b' => 'c'
+                    ],
+                ],
+                'a.b'
+            ],
+            'One integer' => [
+                [
+                    20 => 'a',
+                ],
+                '20'
+            ],
+            'Two integers' => [
+                [
+                    20 => [
+                        30 => 'a'
+                    ],
+                ],
+                '20.30'
+            ],
+            'Mixed' => [
+                [
+                    20 => [
+                        'a' => 'b'
+                    ],
+                ],
+                '20.a'
+            ],
+            'Multiple Entries' => [
+                [
+                    1 => [
+                        'a' => 'b',
+                        'b' => 'foo',
+                    ],
+                ],
+                '1.a'
+            ],
+            'four levels' => [
+                [
+                    1 => [
+                        'a' => [
+                            '2' => [
+                                42 => 'foo',
+                            ],
+                        ],
+                        'b' => 22,
+                    ],
+                ],
+                '1.a.2.42',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider implodeArrayKeysReturnsStringDataProvider
+     * @test
+     */
+    public function implodeArrayKeysReturnsString($array, $expectation)
+    {
+        $hookMock = $this->getAccessibleMock(DataStructureIdentifierHook::class, [ 'dummy' ], [], '', false);
+        $this->assertEquals($expectation, $hookMock->_call('implodeArrayKeys', $array));
+    }
 }
-- 
GitLab