Commit 2917b074 authored by Markus Klein's avatar Markus Klein Committed by Ernesto Baschny
Browse files

[TASK] Improve performance of array_merge_recursive_overrule

The method GeneralUtility::array_merge_recursive_overrule()
always works on a copy of the given array(s). This is highly
inefficient when it comes to really big arrays and recursion.

This patches moves the functionality into the class ArrayUtility
and changes the behaviour to use a reference to the
original array.
All calls in the core are adjusted accordingly.

Furthermore we deprecate the method in GeneralUtility and
preserve backward compatibility.

Resolves: #54251
Releases: 6.2
Change-Id: I5499905593c2124897de5998be985e546a3d05ee
Reviewed-on: https://review.typo3.org/25986
Reviewed-by: Michiel Roos
Tested-by: Michiel Roos
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Ernesto Baschny
Tested-by: Ernesto Baschny
parent 9e88bf74
......@@ -2260,7 +2260,7 @@ class InlineElement {
$PA = array();
$PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$field];
if ($PA['fieldConf'] && $conf['foreign_selector_fieldTcaOverride']) {
$PA['fieldConf'] = GeneralUtility::array_merge_recursive_overrule($PA['fieldConf'], $conf['foreign_selector_fieldTcaOverride']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($PA['fieldConf'], $conf['foreign_selector_fieldTcaOverride']);
}
$PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ? $PA['fieldConf']['config']['form_type'] : $PA['fieldConf']['config']['type'];
// Using "form_type" locally in this script
......
......@@ -121,7 +121,7 @@ class SuggestDefaultReceiver {
$depth = intval($config['pidDepth']);
foreach ($pageIds as $pageId) {
if ($pageId > 0) {
$allowedPages = GeneralUtility::array_merge_recursive_overrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
}
}
$this->allowedPages = array_unique($allowedPages);
......
......@@ -218,23 +218,23 @@ class SuggestElement {
}
$config = (array) $wizardConfig['default'];
if (is_array($wizardConfig[$queryTable])) {
$config = GeneralUtility::array_merge_recursive_overrule($config, $wizardConfig[$queryTable]);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($config, $wizardConfig[$queryTable]);
}
// merge the configurations of different "levels" to get the working configuration for this table and
// field (i.e., go from the most general to the most special configuration)
if (is_array($TSconfig['TCEFORM.']['suggest.']['default.'])) {
$config = GeneralUtility::array_merge_recursive_overrule($config, $TSconfig['TCEFORM.']['suggest.']['default.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($config, $TSconfig['TCEFORM.']['suggest.']['default.']);
}
if (is_array($TSconfig['TCEFORM.']['suggest.'][$queryTable . '.'])) {
$config = GeneralUtility::array_merge_recursive_overrule($config, $TSconfig['TCEFORM.']['suggest.'][$queryTable . '.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($config, $TSconfig['TCEFORM.']['suggest.'][$queryTable . '.']);
}
// use $table instead of $queryTable here because we overlay a config
// for the input-field here, not for the queried table
if (is_array($TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.']['default.'])) {
$config = GeneralUtility::array_merge_recursive_overrule($config, $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.']['default.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($config, $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.']['default.']);
}
if (is_array($TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.'][$queryTable . '.'])) {
$config = GeneralUtility::array_merge_recursive_overrule($config, $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.'][$queryTable . '.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($config, $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.'][$queryTable . '.']);
}
//process addWhere
if (!isset($config['addWhere']) && $foreign_table_where) {
......
......@@ -189,7 +189,8 @@ class FlexFormsHelper extends \TYPO3\CMS\Backend\Form\FormEngine {
unset($fieldConf['addItems']);
// Manipulate field
if (!empty($field['TCEforms']) && is_array($field['TCEforms'])) {
$sheet[$fieldName]['TCEforms'] = GeneralUtility::array_merge_recursive_overrule($field['TCEforms'], $fieldConf);
$sheet[$fieldName]['TCEforms'] = $field['TCEforms'];
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($sheet[$fieldName]['TCEforms'], $fieldConf);
}
// Manipulate only select fields, other field types will stop here
if (empty($field['TCEforms']['config']['type']) || $field['TCEforms']['config']['type'] != 'select') {
......
......@@ -3545,7 +3545,7 @@ TBE_EDITOR.customEvalFunctions[\'' . $evalData . '\'] = function(value) {
}
// Override $GLOBALS['TCA'] field config by remaining TSconfig['config']:
if (count($TSconfig['config'])) {
$fieldConfig = GeneralUtility::array_merge_recursive_overrule($fieldConfig, $TSconfig['config']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($fieldConfig, $TSconfig['config']);
}
}
}
......
......@@ -234,7 +234,8 @@ abstract class AbstractFunctionModule {
if ($this->localLangFile && (@is_file(($this->thisPath . '/' . $this->localLangFile)) || @is_file(($this->thisPath . '/' . substr($this->localLangFile, 0, -4) . '.xml')) || @is_file(($this->thisPath . '/' . substr($this->localLangFile, 0, -4) . '.xlf')))) {
$LOCAL_LANG = $GLOBALS['LANG']->includeLLFile($this->thisPath . '/' . $this->localLangFile, FALSE);
if (is_array($LOCAL_LANG)) {
$GLOBALS['LOCAL_LANG'] = GeneralUtility::array_merge_recursive_overrule((array) $GLOBALS['LOCAL_LANG'], $LOCAL_LANG);
$GLOBALS['LOCAL_LANG'] = (array)$GLOBALS['LOCAL_LANG'];
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['LOCAL_LANG'], $LOCAL_LANG);
}
}
}
......
......@@ -361,7 +361,7 @@ class DocumentTemplate {
// Make copy
$ovr = $GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID];
// merge styles.
$GLOBALS['TBE_STYLES'] = GeneralUtility::array_merge_recursive_overrule($GLOBALS['TBE_STYLES'], $ovr);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TBE_STYLES'], $ovr);
// Have to unset - otherwise the second instantiation will do it again!
unset($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID]);
}
......
......@@ -1169,7 +1169,7 @@ class BackendUtility {
// Get User TSconfig overlay
$userTSconfig = $GLOBALS['BE_USER']->userTS['page.'];
if (is_array($userTSconfig)) {
$TSconfig = GeneralUtility::array_merge_recursive_overrule($TSconfig, $userTSconfig);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($TSconfig, $userTSconfig);
}
if ($useCacheForCurrentPageId) {
......@@ -2704,8 +2704,8 @@ class BackendUtility {
if (is_null($BE_USER_modOptions['value'])) {
unset($BE_USER_modOptions['value']);
}
$modTSconfig = GeneralUtility::array_merge_recursive_overrule($pageTS_modOptions, $BE_USER_modOptions);
return $modTSconfig;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($pageTS_modOptions, $BE_USER_modOptions);
return $pageTS_modOptions;
}
/**
......@@ -3242,7 +3242,7 @@ class BackendUtility {
$res[$fieldN] = $val;
unset($res[$fieldN]['types.']);
if (strcmp($typeVal, '') && is_array($val['types.'][$typeVal . '.'])) {
$res[$fieldN] = GeneralUtility::array_merge_recursive_overrule($res[$fieldN], $val['types.'][$typeVal . '.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($res[$fieldN], $val['types.'][$typeVal . '.']);
}
}
}
......@@ -3400,10 +3400,10 @@ class BackendUtility {
$thisFieldConf = $RTEprop['config.'][$table . '.'][$field . '.'];
if (is_array($thisFieldConf)) {
unset($thisFieldConf['types.']);
$thisConfig = GeneralUtility::array_merge_recursive_overrule($thisConfig, $thisFieldConf);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($thisConfig, $thisFieldConf);
}
if ($type && is_array($RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.'])) {
$thisConfig = GeneralUtility::array_merge_recursive_overrule($thisConfig, $RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($thisConfig, $RTEprop['config.'][$table . '.'][$field . '.']['types.'][$type . '.']);
}
return $thisConfig;
}
......
......@@ -40,7 +40,8 @@ unset($MLANG);
$GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_misc.xlf');
$LOCAL_LANG_orig = $LOCAL_LANG;
$LANG->includeLLFile('EXT:cms/layout/locallang_db_new_content_el.xlf');
$LOCAL_LANG = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($LOCAL_LANG_orig, $LOCAL_LANG);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $LOCAL_LANG);
$LOCAL_LANG = $LOCAL_LANG_orig;
// Exits if 'cms' extension is not loaded:
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('cms', 1);
/**
......
......@@ -349,7 +349,7 @@ class CategoryRegistry implements \TYPO3\CMS\Core\SingletonInterface {
);
if (!empty($options['fieldConfiguration'])) {
$fieldConfiguration = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule(
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule(
$fieldConfiguration,
$options['fieldConfiguration']
);
......
......@@ -160,10 +160,8 @@ class ConfigurationManager {
* @return void
*/
public function updateLocalConfiguration(array $configurationToMerge) {
$newLocalConfiguration = Utility\GeneralUtility::array_merge_recursive_overrule(
$this->getLocalConfiguration(),
$configurationToMerge
);
$newLocalConfiguration = $this->getLocalConfiguration();
Utility\ArrayUtility::mergeRecursiveWithOverrule($newLocalConfiguration, $configurationToMerge);
$this->writeLocalConfiguration($newLocalConfiguration);
}
......@@ -195,12 +193,9 @@ class ConfigurationManager {
* @return mixed
*/
public function getConfigurationValueByPath($path) {
return Utility\ArrayUtility::getValueByPath(
Utility\GeneralUtility::array_merge_recursive_overrule(
$this->getDefaultConfiguration(), $this->getLocalConfiguration()
),
$path
);
$defaultConfiguration = $this->getDefaultConfiguration();
Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $this->getLocalConfiguration());
return Utility\ArrayUtility::getValueByPath($defaultConfiguration, $path);
}
/**
......@@ -280,7 +275,9 @@ class ConfigurationManager {
if (@is_file($this->getLocalConfigurationFileLocation())) {
$localConfiguration = $this->getLocalConfiguration();
if (is_array($localConfiguration)) {
$GLOBALS['TYPO3_CONF_VARS'] = Utility\GeneralUtility::array_merge_recursive_overrule($this->getDefaultConfiguration(), $localConfiguration);
$defaultConfiguration = $this->getDefaultConfiguration();
Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $localConfiguration);
$GLOBALS['TYPO3_CONF_VARS'] = $defaultConfiguration;
} else {
throw new \UnexpectedValueException('LocalConfiguration invalid.', 1349272276);
}
......@@ -359,7 +356,7 @@ class ConfigurationManager {
$additionalFactoryConfigurationFileLocation = $this->getAdditionalFactoryConfigurationFileLocation();
if (file_exists($additionalFactoryConfigurationFileLocation)) {
$additionalFactoryConfigurationArray = require $additionalFactoryConfigurationFileLocation;
$localConfigurationArray = Utility\GeneralUtility::array_merge_recursive_overrule(
Utility\ArrayUtility::mergeRecursiveWithOverrule(
$localConfigurationArray,
$additionalFactoryConfigurationArray
);
......
......@@ -1107,7 +1107,7 @@ class DataHandler {
$this->autoVersionIdMap[$origTable][$origId] = $newId;
}
}
$this->RTEmagic_copyIndex = GeneralUtility::array_merge_recursive_overrule($this->RTEmagic_copyIndex, $tce->RTEmagic_copyIndex);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->RTEmagic_copyIndex, $tce->RTEmagic_copyIndex);
// See where RTEmagic_copyIndex is used inside fillInFieldArray() for more information...
// Update registerDBList, that holds the copied relations to child records:
$registerDBList = array_merge($registerDBList, $tce->registerDBList);
......@@ -1306,7 +1306,7 @@ class DataHandler {
// IF $incomingFieldArray is an array, overlay it.
// The point is that when new records are created as copies with flex type fields there might be a field containing information about which DataStructure to use and without that information the flexforms cannot be correctly processed.... This should be OK since the $checkValueRecord is used by the flexform evaluation only anyways...
if (is_array($incomingFieldArray) && is_array($checkValueRecord)) {
$checkValueRecord = GeneralUtility::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($checkValueRecord, $incomingFieldArray);
}
} else {
// We must use the current values as basis for this!
......@@ -2184,8 +2184,8 @@ class DataHandler {
}
}
$arrValue = GeneralUtility::array_merge_recursive_overrule($currentValueArray, $arrValue);
$xmlValue = $this->checkValue_flexArray2Xml($arrValue, TRUE);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($currentValueArray, $arrValue);
$xmlValue = $this->checkValue_flexArray2Xml($currentValueArray, TRUE);
}
// Action commands (sorting order and removals of elements)
$actionCMDs = GeneralUtility::_GP('_ACTION_FLEX_FORMdata');
......@@ -2915,7 +2915,7 @@ class DataHandler {
}
}
// Merging the copy-array info together for remapping purposes.
$this->copyMappingArray_merged = GeneralUtility::array_merge_recursive_overrule($this->copyMappingArray_merged, $this->copyMappingArray);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->copyMappingArray_merged, $this->copyMappingArray);
}
}
}
......@@ -6437,7 +6437,8 @@ class DataHandler {
public function getTableEntries($table, $TSconfig) {
$tA = is_array($TSconfig['table.'][$table . '.']) ? $TSconfig['table.'][$table . '.'] : array();
$dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array();
return GeneralUtility::array_merge_recursive_overrule($dA, $tA);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($dA, $tA);
return $dA;
}
/**
......
......@@ -185,7 +185,7 @@ class Locales implements \TYPO3\CMS\Core\SingletonInterface {
}
// Merge user-provided locale dependencies
if (isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies'])) {
$instance->localeDependencies = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($instance->localeDependencies, $GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($instance->localeDependencies, $GLOBALS['TYPO3_CONF_VARS']['SYS']['localization']['locales']['dependencies']);
}
}
......
......@@ -150,7 +150,7 @@ class LocalizationFactory implements \TYPO3\CMS\Core\SingletonInterface {
if (count($overrides) > 0) {
foreach ($overrides as $overrideFile) {
$languageOverrideFileName = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($overrideFile);
$LOCAL_LANG = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($LOCAL_LANG, $this->getParsedData($languageOverrideFileName, $languageKey, $charset, $errorMode, TRUE));
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG, $this->getParsedData($languageOverrideFileName, $languageKey, $charset, $errorMode, TRUE));
}
}
}
......
......@@ -63,7 +63,8 @@ class LocallangXmlParser extends \TYPO3\CMS\Core\Localization\Parser\AbstractXml
$parsedTarget = $this->getParsedTargetData($this->sourcePath);
}
$LOCAL_LANG = array();
$LOCAL_LANG[$languageKey] = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($parsedSource, $parsedTarget);
$LOCAL_LANG[$languageKey] = $parsedSource;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG[$languageKey], $parsedTarget);
return $LOCAL_LANG;
}
......
......@@ -2507,7 +2507,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
if ($this->lang !== 'default' && isset($tempLL[$language])) {
// Merge current language labels onto labels from previous language
// This way we have a labels with fall back applied
$localLanguage[$this->lang] = GeneralUtility::array_merge_recursive_overrule($localLanguage[$this->lang], $tempLL[$language], FALSE, FALSE);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], TRUE, FALSE);
}
}
......
......@@ -166,10 +166,11 @@ class FileReference implements FileInterface {
*/
public function getProperties() {
if (empty($this->mergedProperties)) {
$this->mergedProperties = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule(
$this->propertiesOfFileReference,
$this->mergedProperties = $this->propertiesOfFileReference;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule(
$this->mergedProperties,
$this->originalFile->getProperties(),
FALSE,
TRUE,
TRUE,
FALSE
);
......
......@@ -182,7 +182,8 @@ class ConfigurationForm extends \TYPO3\CMS\Core\TypoScript\ExtendedTemplateServi
$parseObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
$parseObj->parse(implode(LF, $this->ext_incomingValues));
$arr2 = $parseObj->setup;
return \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($arr, $arr2);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($arr, $arr2);
return $arr;
}
// Extends:
......
......@@ -1073,7 +1073,7 @@ class TemplateService {
$parseObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
$parseObj->parse($userTS);
if (is_array($parseObj->setup['TSFE.']['constants.'])) {
$constArray = GeneralUtility::array_merge_recursive_overrule($constArray, $parseObj->setup['TSFE.']['constants.']);
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($constArray, $parseObj->setup['TSFE.']['constants.']);
}
return $constArray;
}
......
......@@ -513,4 +513,45 @@ class ArrayUtility {
return $renumberedArray;
}
/**
* Merges two arrays recursively and "binary safe" (integer keys are
* overridden as well), overruling similar values in the original array
* with the values of the overrule array.
* In case of identical keys, ie. keeping the values of the overrule array.
*
* This method takes the original array by reference for speed optimization with large arrays
*
* The differences to the existing PHP function array_merge_recursive() are:
* * Keys of the original array can be unset via the overrule array. ($enableUnsetFeature)
* * Much more control over what is actually merged. ($addKeys, $includeEmptyValues)
* * Elements or the original array get overwritten if the same key is present in the overrule array.
*
* @param array $original Original array. It will be *modified* by this method and contains the result afterwards!
* @param array $overrule Overrule array, overruling the original array
* @param boolean $addKeys If set to FALSE, keys that are NOT found in $original will not be set. Thus only existing value can/will be overruled from overrule array.
* @param boolean $includeEmptyValues If set, values from $overrule will overrule if they are empty or zero.
* @param boolean $enableUnsetFeature If set, special values "__UNSET" can be used in the overrule array in order to unset array keys in the original array.
* @return void
*/
static public function mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys = TRUE, $includeEmptyValues = TRUE, $enableUnsetFeature = TRUE) {
foreach (array_keys($overrule) as $key) {
if ($enableUnsetFeature && $overrule[$key] === '__UNSET') {
unset($original[$key]);
continue;
}
if (isset($original[$key]) && is_array($original[$key])) {
if (is_array($overrule[$key])) {
self::mergeRecursiveWithOverrule($original[$key], $overrule[$key], $addKeys, $includeEmptyValues, $enableUnsetFeature);
}
} elseif (
($addKeys || isset($original[$key])) &&
($includeEmptyValues || $overrule[$key])
) {
$original[$key] = $overrule[$key];
}
}
// This line is kept for backward compatibility reasons.
reset($original);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment