/** * @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; }