/**
  * @test
  */
 public function arrayMergeRecursiveCallbackConvertsSimpleValuesWithGivenClosureAndReturnedSimpleTypesOverwrite()
 {
     $inputArray1 = array('k1' => 'v1', 'k2' => array('k2.1' => 'v2.1'), 'k3' => array('k3.1' => 'value'));
     $inputArray2 = array('k2' => 'v2.2', 'k3' => NULL);
     $expected = array('k1' => 'v1', 'k2' => array('k2.1' => 'v2.1', '__convertedValue' => 'v2.2'), 'k3' => NULL);
     $actual = \TYPO3\Flow\Utility\Arrays::arrayMergeRecursiveOverruleWithCallback($inputArray1, $inputArray2, function ($simpleType) {
         if ($simpleType === NULL) {
             return NULL;
         }
         return array('__convertedValue' => $simpleType);
     });
     $this->assertSame($expected, $actual);
 }
    /**
     * Get the TypoScript Configuration for the given TypoScript path
     *
     * @param string $typoScriptPath
     * @return array
     * @throws \TYPO3\TypoScript\Exception
     */
    protected function getConfigurationForPath($typoScriptPath)
    {
        $pathParts = explode('/', $typoScriptPath);
        $configuration = $this->typoScriptConfiguration;
        $simpleTypeToArrayClosure = function ($simpleType) {
            return $simpleType === null ? null : array('__eelExpression' => null, '__value' => $simpleType, '__objectType' => null);
        };
        $pathUntilNow = '';
        $currentPrototypeDefinitions = array();
        if (isset($configuration['__prototypes'])) {
            $currentPrototypeDefinitions = $configuration['__prototypes'];
        }
        // we also store the configuration on the *last* level such that we are
        // able to add __processors to eel expressions and values.
        foreach ($pathParts as $pathPart) {
            $pathUntilNow .= '/' . $pathPart;
            if (isset($this->configurationOnPathRuntimeCache[$pathUntilNow])) {
                $configuration = $this->configurationOnPathRuntimeCache[$pathUntilNow]['c'];
                $currentPrototypeDefinitions = $this->configurationOnPathRuntimeCache[$pathUntilNow]['p'];
                continue;
            }
            if (preg_match('#^([^<]*)(<(.*?)>)?$#', $pathPart, $matches)) {
                $currentPathSegment = $matches[1];
                if (isset($configuration[$currentPathSegment])) {
                    $configuration = is_array($configuration[$currentPathSegment]) ? $configuration[$currentPathSegment] : $simpleTypeToArrayClosure($configuration[$currentPathSegment]);
                } else {
                    $configuration = array();
                }
                if (isset($configuration['__prototypes'])) {
                    $currentPrototypeDefinitions = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeDefinitions, $configuration['__prototypes'], $simpleTypeToArrayClosure);
                }
                if (isset($matches[3])) {
                    $currentPathSegmentType = $matches[3];
                } elseif (isset($configuration['__objectType'])) {
                    $currentPathSegmentType = $configuration['__objectType'];
                } else {
                    $currentPathSegmentType = null;
                }
                if ($currentPathSegmentType !== null) {
                    if (isset($currentPrototypeDefinitions[$currentPathSegmentType])) {
                        if (isset($currentPrototypeDefinitions[$currentPathSegmentType]['__prototypeChain'])) {
                            $prototypeMergingOrder = $currentPrototypeDefinitions[$currentPathSegmentType]['__prototypeChain'];
                            $prototypeMergingOrder[] = $currentPathSegmentType;
                        } else {
                            $prototypeMergingOrder = array($currentPathSegmentType);
                        }
                        $currentPrototypeWithInheritanceTakenIntoAccount = array();
                        foreach ($prototypeMergingOrder as $prototypeName) {
                            if (array_key_exists($prototypeName, $currentPrototypeDefinitions)) {
                                $currentPrototypeWithInheritanceTakenIntoAccount = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeWithInheritanceTakenIntoAccount, $currentPrototypeDefinitions[$prototypeName], $simpleTypeToArrayClosure);
                            } else {
                                throw new Exception(sprintf('The TypoScript object `%s` which you tried to inherit from does not exist.
									Maybe you have a typo on the right hand side of your inheritance statement for `%s`.', $prototypeName, $currentPathSegmentType), 1427134340);
                            }
                        }
                        // We merge the already flattened prototype with the current configuration (in that order),
                        // to make sure that the current configuration (not being defined in the prototype) wins.
                        $configuration = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeWithInheritanceTakenIntoAccount, $configuration, $simpleTypeToArrayClosure);
                        // If context-dependent prototypes are set (such as prototype("foo").prototype("baz")),
                        // we update the current prototype definitions.
                        if (isset($currentPrototypeWithInheritanceTakenIntoAccount['__prototypes'])) {
                            $currentPrototypeDefinitions = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeDefinitions, $currentPrototypeWithInheritanceTakenIntoAccount['__prototypes'], $simpleTypeToArrayClosure);
                        }
                    }
                    $configuration['__objectType'] = $currentPathSegmentType;
                }
                if (is_array($configuration) && !isset($configuration['__value']) && !isset($configuration['__eelExpression']) && !isset($configuration['__meta']['class']) && !isset($configuration['__objectType']) && isset($configuration['__meta']['process'])) {
                    $configuration['__value'] = '';
                }
                $this->configurationOnPathRuntimeCache[$pathUntilNow]['c'] = $configuration;
                $this->configurationOnPathRuntimeCache[$pathUntilNow]['p'] = $currentPrototypeDefinitions;
            } else {
                throw new Exception('Path Part ' . $pathPart . ' not well-formed', 1332494645);
            }
        }
        return $configuration;
    }
    /**
     * Merges the prototype chain into the configuration.
     *
     * @param array $configuration
     * @param array $currentPrototypeDefinitions
     * @return array
     * @throws Exception
     */
    protected function mergePrototypesWithConfigurationForPathSegment($configuration, &$currentPrototypeDefinitions)
    {
        $currentPathSegmentType = $configuration['__objectType'];
        if (isset($currentPrototypeDefinitions[$currentPathSegmentType])) {
            $prototypeMergingOrder = [$currentPathSegmentType];
            if (isset($currentPrototypeDefinitions[$currentPathSegmentType]['__prototypeChain'])) {
                $prototypeMergingOrder = array_merge($currentPrototypeDefinitions[$currentPathSegmentType]['__prototypeChain'], $prototypeMergingOrder);
            }
            $currentPrototypeWithInheritanceTakenIntoAccount = [];
            foreach ($prototypeMergingOrder as $prototypeName) {
                if (!array_key_exists($prototypeName, $currentPrototypeDefinitions)) {
                    throw new Exception(sprintf('The TypoScript object `%s` which you tried to inherit from does not exist.
									Maybe you have a typo on the right hand side of your inheritance statement for `%s`.', $prototypeName, $currentPathSegmentType), 1427134340);
                }
                $currentPrototypeWithInheritanceTakenIntoAccount = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeWithInheritanceTakenIntoAccount, $currentPrototypeDefinitions[$prototypeName], $this->simpleTypeToArrayClosure);
            }
            // We merge the already flattened prototype with the current configuration (in that order),
            // to make sure that the current configuration (not being defined in the prototype) wins.
            $configuration = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeWithInheritanceTakenIntoAccount, $configuration, $this->simpleTypeToArrayClosure);
            // If context-dependent prototypes are set (such as prototype("foo").prototype("baz")),
            // we update the current prototype definitions.
            if (isset($currentPrototypeWithInheritanceTakenIntoAccount['__prototypes'])) {
                $currentPrototypeDefinitions = Arrays::arrayMergeRecursiveOverruleWithCallback($currentPrototypeDefinitions, $currentPrototypeWithInheritanceTakenIntoAccount['__prototypes'], $this->simpleTypeToArrayClosure);
            }
        }
        return $configuration;
    }