Commit e531ddce authored by Andreas Fernandez's avatar Andreas Fernandez Committed by Anja Leichsenring
Browse files

[TASK] Avoid slow array functions in loops

This patch moves some array functions used in loops (array_merge,
array_replace) out of their calling loop for performance reasons, where
applicable.

This this achieved by calling the functions via array unpacking ("splat
operator").

See for further reading:
https://github.com/kalessil/phpinspectionsea/blob/master/docs/performance.md#slow-array-function-used-in-loop

Resolves: #89056
Releases: master
Change-Id: Ia21b1909f77879918b84c6fa702f07a41149e83f
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/59083

Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent 4bb39f94
......@@ -372,7 +372,7 @@ class Clipboard
*/
public function getContentFromTab($pad)
{
$lines = [];
$lines = [[]];
if (is_array($this->clipData[$pad]['el'] ?? false)) {
foreach ($this->clipData[$pad]['el'] as $k => $v) {
if ($v) {
......@@ -434,7 +434,7 @@ class Clipboard
$localizationData = $this->getLocalizations($table, $rec);
if (!empty($localizationData)) {
$lines = array_merge($lines, $localizationData);
$lines[] = $localizationData;
}
} else {
unset($this->clipData[$pad]['el'][$k]);
......@@ -445,7 +445,7 @@ class Clipboard
}
}
$this->endClipboard();
return $lines;
return array_merge(...$lines);
}
/**
......
......@@ -360,7 +360,7 @@ class NewContentElementController
*/
protected function getWizards(): array
{
$wizardItems = [];
$wizardItems = [[]];
if (is_array($this->config)) {
$wizards = $this->config['wizardItems.'] ?? [];
$appendWizards = $this->getAppendWizards($wizards['elements.'] ?? []);
......@@ -393,13 +393,14 @@ class NewContentElementController
}
}
if (!empty($groupItems)) {
$wizardItems[$groupKey] = $this->getWizardGroupHeader($wizardGroup);
$wizardItems = array_merge($wizardItems, $groupItems);
$wizardItems[] = [$groupKey => $this->getWizardGroupHeader($wizardGroup)];
$wizardItems[] = $groupItems;
}
}
}
}
// Remove elements where preset values are not allowed:
$wizardItems = array_merge(...$wizardItems);
$this->removeInvalidWizardItems($wizardItems);
return $wizardItems;
}
......
......@@ -193,12 +193,12 @@ class TreeController
} else {
$entryPoints = $this->getAllEntryPointPageTrees();
}
$items = [];
$items = [[]];
foreach ($entryPoints as $page) {
$items = array_merge($items, $this->pagesToFlatArray($page, (int)$page['uid']));
$items[] = $this->pagesToFlatArray($page, (int)$page['uid']);
}
return new JsonResponse($items);
return new JsonResponse(array_merge(...$items));
}
/**
......@@ -280,8 +280,8 @@ class TreeController
$prefix = htmlspecialchars('[' . $pageId . '] ');
}
$items = [];
$items[] = [
$items = [[]];
$items[] = [[
// Used to track if the tree item is collapsed or not
'stateIdentifier' => $identifier,
'identifier' => $pageId,
......@@ -305,13 +305,13 @@ class TreeController
'isMountPoint' => $depth === 0,
'mountPoint' => $entryPoint,
'workspaceId' => !empty($page['t3ver_oid']) ? $page['t3ver_oid'] : $pageId,
];
]];
if (!$stopPageTree) {
foreach ($page['_children'] as $child) {
$items = array_merge($items, $this->pagesToFlatArray($child, $entryPoint, $depth + 1, ['backgroundColor' => $backgroundColor]));
$items[] = $this->pagesToFlatArray($child, $entryPoint, $depth + 1, ['backgroundColor' => $backgroundColor]);
}
}
return $items;
return array_merge(...$items);
}
/**
......
......@@ -752,7 +752,7 @@ abstract class AbstractItemProvider
protected function getExcludeFields()
{
$languageService = $this->getLanguageService();
$finalExcludeArray = [];
$finalExcludeArray = [[]];
// Fetch translations for table names
$tableToTranslation = [];
......@@ -836,11 +836,11 @@ abstract class AbstractItemProvider
}
return 0;
});
$finalExcludeArray = array_merge($finalExcludeArray, $excludeArrayTable);
$finalExcludeArray[] = $excludeArrayTable;
}
}
return $finalExcludeArray;
return array_merge(...$finalExcludeArray);
}
/**
......
......@@ -183,7 +183,7 @@ class TcaTypesShowitem implements FormDataProviderInterface
*/
protected function removeFieldsByBitmaskExcludeBits(array $result, $bitmaskValue, $recordTypeValue)
{
$removeListArray = [];
$removeListArray = [[]];
$bitmaskValue = MathUtility::forceIntegerInRange($bitmaskValue, 0);
$excludeListBitsArray = $this->processedTca['types'][$recordTypeValue]['bitmask_excludelist_bits'];
foreach ($excludeListBitsArray as $bitKey => $excludeList) {
......@@ -193,9 +193,10 @@ class TcaTypesShowitem implements FormDataProviderInterface
if (!$isNegative && ($bitmaskValue & pow(2, $bit))
|| $isNegative && !($bitmaskValue & pow(2, $bit))
) {
$removeListArray = array_merge($removeListArray, GeneralUtility::trimExplode(',', $excludeList, true));
$removeListArray[] = GeneralUtility::trimExplode(',', $excludeList, true);
}
}
$removeListArray = array_merge(...$removeListArray);
$result = $this->removeFields($result, $removeListArray, $recordTypeValue);
return $this->removeFieldsFromPalettes($result, $removeListArray);
}
......
......@@ -322,9 +322,9 @@ class SuggestWizardDefaultReceiver
}
$pageIds = array_keys($rows);
$pages = array_merge($pages, $pageIds);
$pages[] = $pageIds;
}
return $pages;
return array_merge(...$pages);
}
/**
......
......@@ -599,14 +599,14 @@ function jumpToUrl(URL) {
if (is_array($GLOBALS['TBE_STYLES']['skins'])) {
// loop over all registered skins
foreach ($GLOBALS['TBE_STYLES']['skins'] as $skinExtKey => $skin) {
$skinStylesheetDirs = $this->stylesheetsSkins;
$skinStylesheetDirs = [$this->stylesheetsSkins];
// Skins can add custom stylesheetDirectories using
// $GLOBALS['TBE_STYLES']['skins']['your_extension_key']['stylesheetDirectories']
if (is_array($skin['stylesheetDirectories'])) {
$skinStylesheetDirs = array_merge($skinStylesheetDirs, $skin['stylesheetDirectories']);
$skinStylesheetDirs[] = $skin['stylesheetDirectories'];
}
// Add all registered directories
foreach ($skinStylesheetDirs as $stylesheetDir) {
foreach (array_merge(...$skinStylesheetDirs) as $stylesheetDir) {
// for EXT:myskin/stylesheets/ syntax
if (strpos($stylesheetDir, 'EXT:') === 0) {
list($extKey, $path) = explode('/', substr($stylesheetDir, 4), 2);
......
......@@ -348,7 +348,7 @@ abstract class AbstractTreeView
$this->initializePositionSaving();
// Init done:
$lastMountPointPid = 0;
$treeArr = [];
$treeArr = [[]];
// Traverse mounts:
foreach ($this->MOUNTS as $idx => $uid) {
// Set first:
......@@ -396,10 +396,10 @@ abstract class AbstractTreeView
$this->getTree($uid, 999, $depthData);
}
// Add tree:
$treeArr = array_merge($treeArr, $this->tree);
$treeArr[] = $this->tree;
}
}
return $this->printTree($treeArr);
return $this->printTree(array_merge(...$treeArr));
}
/**
......
......@@ -266,14 +266,14 @@ class FolderTreeView extends AbstractTreeView
// Get stored tree structure AND updating it if needed according to incoming PM GET var.
$this->initializePositionSaving();
// Init done:
$treeItems = [];
$treeItems = [[]];
// Traverse mounts:
foreach ($this->storages as $storageObject) {
$this->getBrowseableTreeForStorage($storageObject);
// Add tree:
$treeItems = array_merge($treeItems, $this->tree);
$treeItems[] = $this->tree;
}
return $this->printTree($treeItems);
return $this->printTree(array_merge(...$treeItems));
}
/**
......
......@@ -3764,21 +3764,21 @@ class PageLayoutView implements LoggerAwareInterface
$tree->init('AND ' . $perms_clause);
$tree->makeHTML = 0;
$tree->fieldArray = ['uid', 'php_tree_stop'];
$idList = [];
$idList = [[]];
$allowedMounts = !$backendUser->isAdmin() && $id === 0
? $backendUser->returnWebmounts()
: [$id];
foreach ($allowedMounts as $allowedMount) {
$idList[] = $allowedMount;
$idList[] = [$allowedMount];
if ($depth) {
$tree->getTree($allowedMount, $depth, '');
}
$idList = array_merge($idList, $tree->ids);
$idList[] = $tree->ids;
}
return $idList;
return array_merge(...$idList);
}
/**
......
......@@ -269,7 +269,7 @@ class PageTreeView extends BrowseTreeView
// Get stored tree structure AND updating it if needed according to incoming PM GET var.
$this->initializePositionSaving();
// Init done:
$treeArr = [];
$treeArr = [[]];
// Traverse mounts:
$firstHtml = '';
foreach ($this->MOUNTS as $idx => $uid) {
......@@ -310,9 +310,9 @@ class PageTreeView extends BrowseTreeView
$this->getTree($uid);
}
// Add tree:
$treeArr = array_merge($treeArr, $this->tree);
$treeArr[] = $this->tree;
}
}
return $this->printTree($treeArr);
return $this->printTree(array_merge(...$treeArr));
}
}
......@@ -540,6 +540,7 @@ class Bootstrap
// substr is necessary, because the php frontend wraps php code around the cache value
$routesFromPackages = unserialize(substr($codeCache->get($cacheIdentifier), 6, -2));
} else {
$routesFromPackages = [[]];
// Loop over all packages and check for a Configuration/Backend/Routes.php file
$packageManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
$packages = $packageManager->getActivePackages();
......@@ -548,7 +549,7 @@ class Bootstrap
if (file_exists($routesFileNameForPackage)) {
$definedRoutesInPackage = require $routesFileNameForPackage;
if (is_array($definedRoutesInPackage)) {
$routesFromPackages = array_merge($routesFromPackages, $definedRoutesInPackage);
$routesFromPackages[] = $definedRoutesInPackage;
}
}
$routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/AjaxRoutes.php';
......@@ -558,12 +559,15 @@ class Bootstrap
foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) {
// prefix the route with "ajax_" as "namespace"
$routeOptions['path'] = '/ajax' . $routeOptions['path'];
$routesFromPackages['ajax_' . $routeIdentifier] = $routeOptions;
$routesFromPackages['ajax_' . $routeIdentifier]['ajax'] = true;
$routeOptions['ajax'] = true;
$routesFromPackages[] = [
'ajax_' . $routeIdentifier => $routeOptions,
];
}
}
}
}
$routesFromPackages = array_merge(...$routesFromPackages);
// Store the data from all packages in the cache
$codeCache->set($cacheIdentifier, serialize($routesFromPackages));
}
......
......@@ -69,14 +69,14 @@ class ClassLoadingInformationGenerator
*/
public function buildClassLoadingInformationForPackage(PackageInterface $package, $useRelativePaths = false)
{
$classMap = [];
$classMap = [[]];
$psr4 = [];
$packagePath = $package->getPackagePath();
$manifest = $package->getValueFromComposerManifest();
if (empty($manifest->autoload)) {
// Legacy mode: Scan the complete extension directory for class files
$classMap = $this->createClassMap($packagePath, $useRelativePaths, !$this->isDevMode);
$classMap[] = $this->createClassMap($packagePath, $useRelativePaths, !$this->isDevMode);
} else {
$autoloadPsr4 = $this->getAutoloadSectionFromManifest($manifest, 'psr-4');
if (!empty($autoloadPsr4)) {
......@@ -91,7 +91,7 @@ class ClassLoadingInformationGenerator
}
if (!empty($namespaceRealPath) && is_dir($namespaceRealPath)) {
// Add all prs-4 classes to the class map for improved class loading performance
$classMap = array_merge($classMap, $this->createClassMap($namespacePath, $useRelativePaths, false, $namespacePrefix));
$classMap[] = $this->createClassMap($namespacePath, $useRelativePaths, false, $namespacePrefix);
}
}
}
......@@ -99,12 +99,12 @@ class ClassLoadingInformationGenerator
$autoloadClassmap = $this->getAutoloadSectionFromManifest($manifest, 'classmap');
if (!empty($autoloadClassmap)) {
foreach ($autoloadClassmap as $path) {
$classMap = array_merge($classMap, $this->createClassMap($packagePath . $path, $useRelativePaths));
$classMap[] = $this->createClassMap($packagePath . $path, $useRelativePaths);
}
}
}
return ['classMap' => $classMap, 'psr-4' => $psr4];
return ['classMap' => array_merge(...$classMap), 'psr-4' => $psr4];
}
/**
......@@ -219,13 +219,15 @@ class ClassLoadingInformationGenerator
return array(
EOF;
$classMap = [];
$psr4 = [];
$classMap = [[]];
$psr4 = [[]];
foreach ($this->activeExtensionPackages as $package) {
$classLoadingInformation = $this->buildClassLoadingInformationForPackage($package, true);
$classMap = array_merge($classMap, $classLoadingInformation['classMap']);
$psr4 = array_merge($psr4, $classLoadingInformation['psr-4']);
$classMap[] = $classLoadingInformation['classMap'];
$psr4[] = $classLoadingInformation['psr-4'];
}
$classMap = array_merge(...$classMap);
$psr4 = array_merge(...$psr4);
ksort($classMap);
ksort($psr4);
......@@ -288,16 +290,16 @@ EOF;
*/
public function buildClassAliasMapFile()
{
$aliasToClassNameMapping = [];
$classNameToAliasMapping = [];
$aliasToClassNameMapping = [[]];
$classNameToAliasMapping = [[]];
foreach ($this->activeExtensionPackages as $package) {
$aliasMappingForPackage = $this->buildClassAliasMapForPackage($package);
$aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $aliasMappingForPackage['aliasToClassNameMapping']);
$classNameToAliasMapping = array_merge($classNameToAliasMapping, $aliasMappingForPackage['classNameToAliasMapping']);
$aliasToClassNameMapping[] = $aliasMappingForPackage['aliasToClassNameMapping'];
$classNameToAliasMapping[] = $aliasMappingForPackage['classNameToAliasMapping'];
}
$exportArray = [
'aliasToClassNameMapping' => $aliasToClassNameMapping,
'classNameToAliasMapping' => $classNameToAliasMapping
'aliasToClassNameMapping' => array_merge(...$aliasToClassNameMapping),
'classNameToAliasMapping' => array_merge(...$classNameToAliasMapping)
];
$fileContent = '<?php' . chr(10) . 'return ';
$fileContent .= var_export($exportArray, true);
......
......@@ -7819,8 +7819,8 @@ class DataHandler implements LoggerAwareInterface
*/
protected function processClearCacheQueue()
{
$tagsToClear = [];
$clearCacheCommands = [];
$tagsToClear = [[]];
$clearCacheCommands = [[]];
foreach (static::$recordsToClearCacheFor as $table => $uids) {
foreach (array_unique($uids) as $uid) {
......@@ -7832,18 +7832,18 @@ class DataHandler implements LoggerAwareInterface
foreach ($pageUids as $originalParent) {
list($tagsToClearFromPrepare, $clearCacheCommandsFromPrepare)
= $this->prepareCacheFlush($table, $uid, $originalParent);
$tagsToClear = array_merge($tagsToClear, $tagsToClearFromPrepare);
$clearCacheCommands = array_merge($clearCacheCommands, $clearCacheCommandsFromPrepare);
$tagsToClear[] = $tagsToClearFromPrepare;
$clearCacheCommands[] = $clearCacheCommandsFromPrepare;
}
}
}
/** @var CacheManager $cacheManager */
$cacheManager = $this->getCacheManager();
$cacheManager->flushCachesInGroupByTags('pages', array_keys($tagsToClear));
$cacheManager->flushCachesInGroupByTags('pages', array_keys(array_merge(...$tagsToClear)));
// Filter duplicate cache commands from cacheQueue
$clearCacheCommands = array_unique($clearCacheCommands);
$clearCacheCommands = array_unique(array_merge(...$clearCacheCommands));
// Execute collected clear cache commands from page TSConfig
foreach ($clearCacheCommands as $command) {
$this->clear_cacheCmd($command);
......
......@@ -271,14 +271,12 @@ class DataMapProcessor
*/
protected function sanitizeTranslationItem(DataMapItem $item)
{
$fieldNames = [];
$fieldNames = [[]];
foreach ($item->getApplicableScopes() as $scope) {
$fieldNames = array_merge(
$fieldNames,
$this->getFieldNamesForItemScope($item, $scope, false)
);
$fieldNames[] = $this->getFieldNamesForItemScope($item, $scope, false);
}
$fieldNames = array_merge(...$fieldNames);
$fieldNameMap = array_combine($fieldNames, $fieldNames);
// separate fields, that are submitted in data-map, but not defined as custom
$this->sanitizationMap[$item->getTableName()][$item->getId()] = array_intersect_key(
......
......@@ -173,7 +173,7 @@ class SchemaMigrator
{
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$tables = $this->parseCreateTableStatements($statements);
$result = [];
$result = [[]];
foreach ($connectionPool->getConnectionNames() as $connectionName) {
$connectionMigrator = ConnectionMigrator::create(
......@@ -181,11 +181,10 @@ class SchemaMigrator
$tables
);
$lastResult = $connectionMigrator->install($createOnly);
$result = array_merge($result, $lastResult);
$result[] = $connectionMigrator->install($createOnly);
}
return $result;
return array_merge(...$result);
}
/**
......
......@@ -196,18 +196,18 @@ class Locales implements SingletonInterface
*/
public function getLocaleDependencies($locale)
{
$dependencies = [];
$dependencies = [[]];
if (isset($this->localeDependencies[$locale])) {
$dependencies = $this->localeDependencies[$locale];
$dependencies[] = $this->localeDependencies[$locale];
// Search for dependencies recursively
$localeDependencies = $dependencies;
foreach ($localeDependencies as $dependency) {
if (isset($this->localeDependencies[$dependency])) {
$dependencies = array_merge($dependencies, $this->getLocaleDependencies($dependency));
$dependencies[] = $this->getLocaleDependencies($dependency);
}
}
}
return $dependencies;
return array_merge(...$dependencies);
}
/**
......
......@@ -117,16 +117,18 @@ class LocalizationFactory implements \TYPO3\CMS\Core\SingletonInterface
*/
protected function localizationOverride($fileReference, $languageKey, array &$LOCAL_LANG)
{
$overrides = [];
$overrides = [[]];
$fileReferenceWithoutExtension = $this->store->getFileReferenceWithoutExtension($fileReference);
$locallangXMLOverride = $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'];
foreach ($this->store->getSupportedExtensions() as $extension) {
if (isset($locallangXMLOverride[$languageKey][$fileReferenceWithoutExtension . '.' . $extension]) && is_array($locallangXMLOverride[$languageKey][$fileReferenceWithoutExtension . '.' . $extension])) {
$overrides = array_merge($overrides, $locallangXMLOverride[$languageKey][$fileReferenceWithoutExtension . '.' . $extension]);
$overrides[] = $locallangXMLOverride[$languageKey][$fileReferenceWithoutExtension . '.' . $extension];
} elseif (isset($locallangXMLOverride[$fileReferenceWithoutExtension . '.' . $extension]) && is_array($locallangXMLOverride[$fileReferenceWithoutExtension . '.' . $extension])) {
$overrides = array_merge($overrides, $locallangXMLOverride[$fileReferenceWithoutExtension . '.' . $extension]);
$overrides[] = $locallangXMLOverride[$fileReferenceWithoutExtension . '.' . $extension];
}
}
$overrides = array_merge(...$overrides);
if (!empty($overrides)) {
foreach ($overrides as $overrideFile) {
$languageOverrideFileName = GeneralUtility::getFileAbsFileName($overrideFile);
......
......@@ -142,7 +142,7 @@ class ResourceCompressor
'allWrap' => ''
];
// place the merged stylesheet on top of the stylesheets
$cssFiles = array_merge($cssFiles, [$targetFile => $concatenatedOptions]);
$cssFiles[$targetFile] = $concatenatedOptions;
}
return $cssFiles;
}
......@@ -194,7 +194,7 @@ class ResourceCompressor
'async' => $concatenatedJsFileIsAsync && $allFilesToConcatenateAreAsync,
];
// place the merged javascript on top of the JS files
$jsFiles = array_merge([$targetFile => $concatenatedOptions], $jsFiles);
$jsFiles[$targetFile] = $concatenatedOptions;
}
}
return $jsFiles;
......
......@@ -121,7 +121,7 @@ class UserFileMountService
if ($level > 99) {
return [];
}
$allFolderItems = [$parentFolder];
$allFolderItems = [[$parentFolder]];
$subFolders = $parentFolder->getSubfolders();
foreach ($subFolders as $subFolder) {
try {
......@@ -129,9 +129,9 @@ class UserFileMountService
} catch (InsufficientFolderReadPermissionsException $e) {
$subFolderItems = [];
}
$allFolderItems = array_merge($allFolderItems, $subFolderItems);
$allFolderItems[] = $subFolderItems;
}
return $allFolderItems;
return array_merge(...$allFolderItems);
}
/**
......
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