From 458216441afdd85b74777241506b7e0f892f1002 Mon Sep 17 00:00:00 2001 From: Jakub Winkler Date: Fri, 19 Apr 2024 17:51:43 +0200 Subject: [PATCH 1/2] performance updates - array merge --- .../Framework/App/DeploymentConfig.php | 133 ++++++------------ .../Framework/App/DeploymentConfig/Reader.php | 36 +++-- .../Magento/Framework/Console/Cli.php | 25 ++-- 3 files changed, 80 insertions(+), 114 deletions(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index a14354b4fdb9a..232d4255bb4c1 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -51,15 +51,7 @@ class DeploymentConfig */ private $overrideData; - /** - * @var array - */ - private $envOverrides = []; - - /** - * @var array - */ - private $readerLoad = []; + private ?array $flattenParams = null; /** * Constructor @@ -94,9 +86,7 @@ public function get($key = null, $defaultValue = null) } $result = $this->getByKey($key); if ($result === null) { - if (empty($this->flatData) || count($this->getAllEnvOverrides())) { - $this->reloadData(); - } + $this->reloadData(); $result = $this->getByKey($key); } return $result ?? $defaultValue; @@ -126,13 +116,13 @@ public function getConfigData($key = null) { if ($key === null) { if (empty($this->data)) { - $this->reloadInitialData(); + $this->reloadData(); } return $this->data; } $result = $this->getConfigDataByKey($key); if ($result === null) { - $this->reloadInitialData(); + $this->reloadData(); $result = $this->getConfigDataByKey($key); } return $result; @@ -182,55 +172,29 @@ private function getEnvOverride() : array * @throws FileSystemException * @throws RuntimeException */ - private function reloadInitialData(): void + private function reloadData(): void { - if (empty($this->readerLoad) || empty($this->data) || empty($this->flatData)) { - $this->readerLoad = $this->reader->load(); - } $this->data = array_replace( - $this->readerLoad, + $this->reader->load(), $this->overrideData ?? [], - $this->getEnvOverride() + $this->getEnvOverride(), ); - } - /** - * Loads the configuration data - * - * @return void - * @throws FileSystemException - * @throws RuntimeException - */ - private function reloadData(): void - { - $this->reloadInitialData(); // flatten data for config retrieval using get() $this->flatData = $this->flattenParams($this->data); - $this->flatData = $this->getAllEnvOverrides() + $this->flatData; - } - /** - * Load all getenv() configs once - * - * @return array - */ - private function getAllEnvOverrides(): array - { - if (empty($this->envOverrides)) { - // allow reading values from env variables by convention - // MAGENTO_DC_{path}, like db/connection/default/host => - // can be overwritten by MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST - foreach (getenv() as $key => $value) { - if (false !== \strpos($key, self::MAGENTO_ENV_PREFIX) - && $key !== self::OVERRIDE_KEY - ) { - // convert MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST into db/connection/default/host - $flatKey = strtolower(str_replace([self::MAGENTO_ENV_PREFIX, '__'], ['', '/'], $key)); - $this->envOverrides[$flatKey] = $value; - } + // allow reading values from env variables by convention + // MAGENTO_DC_{path}, like db/connection/default/host => + // can be overwritten by MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST + foreach (getenv() as $key => $value) { + if (false !== \strpos($key, self::MAGENTO_ENV_PREFIX) + && $key !== self::OVERRIDE_KEY + ) { + // convert MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST into db/connection/default/host + $flatKey = strtolower(str_replace([self::MAGENTO_ENV_PREFIX, '__'], ['', '/'], $key)); + $this->flatData[$flatKey] = $value; } } - return $this->envOverrides; } /** @@ -247,37 +211,43 @@ private function getAllEnvOverrides(): array */ private function flattenParams(array $params, ?string $path = null, array &$flattenResult = null): array { - if (null === $flattenResult) { - $flattenResult = []; - } + $paramsKey = CRC32(json_encode($params ?? '')); - foreach ($params as $key => $param) { - if ($path) { - $newPath = $path . '/' . $key; - } else { - $newPath = $key; - } - if (isset($flattenResult[$newPath])) { - //phpcs:ignore Magento2.Exceptions.DirectThrow - throw new RuntimeException(new Phrase("Key collision '%1' is already defined.", [$newPath])); + if (!isset($this->flattenParams[$paramsKey])) { + if (null === $flattenResult) { + $flattenResult = []; } - if (is_array($param)) { - $flattenResult[$newPath] = $param; - $this->flattenParams($param, $newPath, $flattenResult); - } else { - // allow reading values from env variables - // value need to be specified in %env(NAME, "default value")% format - // like #env(DB_PASSWORD), #env(DB_NAME, "test") - if ($param !== null && preg_match(self::ENV_NAME_PATTERN, $param, $matches)) { - $param = getenv($matches['name']) ?: ($matches['default'] ?? null); + foreach ($params as $key => $param) { + if ($path) { + $newPath = $path . '/' . $key; + } else { + $newPath = $key; + } + if (isset($flattenResult[$newPath])) { + //phpcs:ignore Magento2.Exceptions.DirectThrow + throw new RuntimeException(new Phrase("Key collision '%1' is already defined.", [$newPath])); } - $flattenResult[$newPath] = $param; + if (is_array($param)) { + $flattenResult[$newPath] = $param; + $this->flattenParams($param, $newPath, $flattenResult); + } else { + // allow reading values from env variables + // value need to be specified in %env(NAME, "default value")% format + // like #env(DB_PASSWORD), #env(DB_NAME, "test") + if ($param !== null && preg_match(self::ENV_NAME_PATTERN, $param, $matches)) { + $param = getenv($matches['name']) ?: ($matches['default'] ?? null); + } + + $flattenResult[$newPath] = $param; + } } + + $this->flattenParams[$paramsKey] = $flattenResult; } - return $flattenResult; + return $this->flattenParams[$paramsKey] ?? []; } /** @@ -305,15 +275,4 @@ private function getConfigDataByKey(?string $key) { return $this->data[$key] ?? null; } - - /** - * Disable show internals with var_dump - * - * @see https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo - * @return array - */ - public function __debugInfo() - { - return []; - } } diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php index df9dee7459464..ecf7afca394fd 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php @@ -94,31 +94,43 @@ public function getFiles() */ public function load($fileKey = null) { + static $cache = []; + $path = $this->dirList->getPath(DirectoryList::CONFIG); $fileDriver = $this->driverPool->getDriver(DriverPool::FILE); $result = []; + if ($fileKey) { $filePath = $path . '/' . $this->configFilePool->getPath($fileKey); - if ($fileDriver->isExists($filePath)) { - $result = include $filePath; - if (!is_array($result)) { - throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$filePath])); + if (!isset($cache[$filePath])) { // Check if the file data is already cached + if ($fileDriver->isExists($filePath)) { + $fileData = include $filePath; + if (!is_array($fileData)) { + throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$filePath])); + } + $cache[$filePath] = $fileData; // Cache the file data + } else { + $cache[$filePath] = null; // Cache that the file does not exist or is invalid } } + $result = $cache[$filePath] ?: []; } else { $configFiles = $this->getFiles(); foreach ($configFiles as $file) { $configFile = $path . '/' . $file; - if ($fileDriver->isExists($configFile)) { - $fileData = include $configFile; - if (!is_array($fileData)) { - throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$configFile])); + if (!isset($cache[$configFile])) { // Check cache first + if ($fileDriver->isExists($configFile)) { + $fileData = include $configFile; + if (!is_array($fileData)) { + throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$configFile])); + } + $cache[$configFile] = $fileData; // Cache the file data + } else { + $cache[$configFile] = null; // Cache that the file does not exist or is invalid } - } else { - continue; } - if ($fileData) { - $result = array_replace_recursive($result, $fileData); + if ($cache[$configFile]) { + $result = array_replace_recursive($result, $cache[$configFile]); } } } diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index ca82e495e5a59..013358a9eaa6d 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -37,18 +37,16 @@ class Cli extends Console\Application /** * Name of input option. */ - public const INPUT_KEY_BOOTSTRAP = 'bootstrap'; + const INPUT_KEY_BOOTSTRAP = 'bootstrap'; /**#@+ * Cli exit codes. */ - public const RETURN_SUCCESS = 0; - public const RETURN_FAILURE = 1; + const RETURN_SUCCESS = 0; + const RETURN_FAILURE = 1; /**#@-*/ - /** - * @var $serviceManager - */ + /**#@-*/ private $serviceManager; /** @@ -59,6 +57,8 @@ class Cli extends Console\Application private $initException; /** + * Object Manager. + * * @var ObjectManagerInterface */ private $objectManager; @@ -88,10 +88,8 @@ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') $output->writeln( '' . $exception->getMessage() . '' ); - // phpcs:disable // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage exit(static::RETURN_FAILURE); - // phpcs:enable } if ($version == 'UNKNOWN') { @@ -132,7 +130,7 @@ public function doRun(Console\Input\InputInterface $input, Console\Output\Output /** * @inheritdoc */ - protected function getDefaultCommands():array + protected function getDefaultCommands() { return array_merge(parent::getDefaultCommands(), $this->getApplicationCommands()); } @@ -148,19 +146,16 @@ protected function getApplicationCommands() try { if (class_exists(\Magento\Setup\Console\CommandList::class)) { $setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager); - $commands = array_merge($commands, $setupCommandList->getCommands()); + $commands = [...$commands, ...$setupCommandList->getCommands()]; } if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) { /** @var CommandListInterface */ $commandList = $this->objectManager->create(CommandListInterface::class); - $commands = array_merge($commands, $commandList->getCommands()); + $commands = [...$commands, ...$commandList->getCommands()]; } - $commands = array_merge( - $commands, - $this->getVendorCommands($this->objectManager) - ); + $commands = [...$commands, ...$this->getVendorCommands($this->objectManager)]; } catch (\Exception $e) { $this->initException = $e; } From 620f8b08217d37172d33dfe36531ecc576e8d50b Mon Sep 17 00:00:00 2001 From: Jakub Winkler Date: Fri, 19 Apr 2024 17:58:22 +0200 Subject: [PATCH 2/2] refactor 2.4.7, removing duplicated code --- .../Framework/App/DeploymentConfig.php | 82 +++++++++++++++---- .../Framework/App/DeploymentConfig/Reader.php | 62 +++++++------- .../Magento/Framework/Console/Cli.php | 16 ++-- 3 files changed, 105 insertions(+), 55 deletions(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index 232d4255bb4c1..e121a140f8cbe 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -51,6 +51,17 @@ class DeploymentConfig */ private $overrideData; + /** + * @var array + */ + private $envOverrides = []; + + /** + * @var array + */ + private $readerLoad = []; + + /** @var array|null */ private ?array $flattenParams = null; /** @@ -86,7 +97,9 @@ public function get($key = null, $defaultValue = null) } $result = $this->getByKey($key); if ($result === null) { - $this->reloadData(); + if (empty($this->flatData) || count($this->getAllEnvOverrides())) { + $this->reloadData(); + } $result = $this->getByKey($key); } return $result ?? $defaultValue; @@ -116,13 +129,13 @@ public function getConfigData($key = null) { if ($key === null) { if (empty($this->data)) { - $this->reloadData(); + $this->reloadInitialData(); } return $this->data; } $result = $this->getConfigDataByKey($key); if ($result === null) { - $this->reloadData(); + $this->reloadInitialData(); $result = $this->getConfigDataByKey($key); } return $result; @@ -172,29 +185,55 @@ private function getEnvOverride() : array * @throws FileSystemException * @throws RuntimeException */ - private function reloadData(): void + private function reloadInitialData(): void { + if (empty($this->readerLoad) || empty($this->data) || empty($this->flatData)) { + $this->readerLoad = $this->reader->load(); + } $this->data = array_replace( - $this->reader->load(), + $this->readerLoad, $this->overrideData ?? [], - $this->getEnvOverride(), + $this->getEnvOverride() ); + } + /** + * Loads the configuration data + * + * @return void + * @throws FileSystemException + * @throws RuntimeException + */ + private function reloadData(): void + { + $this->reloadInitialData(); // flatten data for config retrieval using get() $this->flatData = $this->flattenParams($this->data); + $this->flatData = $this->getAllEnvOverrides() + $this->flatData; + } - // allow reading values from env variables by convention - // MAGENTO_DC_{path}, like db/connection/default/host => - // can be overwritten by MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST - foreach (getenv() as $key => $value) { - if (false !== \strpos($key, self::MAGENTO_ENV_PREFIX) - && $key !== self::OVERRIDE_KEY - ) { - // convert MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST into db/connection/default/host - $flatKey = strtolower(str_replace([self::MAGENTO_ENV_PREFIX, '__'], ['', '/'], $key)); - $this->flatData[$flatKey] = $value; + /** + * Load all getenv() configs once + * + * @return array + */ + private function getAllEnvOverrides(): array + { + if (empty($this->envOverrides)) { + // allow reading values from env variables by convention + // MAGENTO_DC_{path}, like db/connection/default/host => + // can be overwritten by MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST + foreach (getenv() as $key => $value) { + if (false !== \strpos($key, self::MAGENTO_ENV_PREFIX) + && $key !== self::OVERRIDE_KEY + ) { + // convert MAGENTO_DC_DB__CONNECTION__DEFAULT__HOST into db/connection/default/host + $flatKey = strtolower(str_replace([self::MAGENTO_ENV_PREFIX, '__'], ['', '/'], $key)); + $this->envOverrides[$flatKey] = $value; + } } } + return $this->envOverrides; } /** @@ -275,4 +314,15 @@ private function getConfigDataByKey(?string $key) { return $this->data[$key] ?? null; } + + /** + * Disable show internals with var_dump + * + * @see https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo + * @return array + */ + public function __debugInfo() + { + return []; + } } diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php index ecf7afca394fd..26aa8caf0de7e 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php @@ -81,16 +81,10 @@ public function getFiles() } /** - * Method loads merged configuration within all configuration files. - * To retrieve specific file configuration, use FileReader. - * $fileKey option is deprecated since version 2.2.0. - * - * @param string $fileKey The file key (deprecated) - * @return array - * @throws FileSystemException If file can not be read - * @throws RuntimeException If file is invalid - * @throws \Exception If file key is not correct - * @see FileReader + * @param $fileKey + * @return array|mixed + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException */ public function load($fileKey = null) { @@ -102,33 +96,13 @@ public function load($fileKey = null) if ($fileKey) { $filePath = $path . '/' . $this->configFilePool->getPath($fileKey); - if (!isset($cache[$filePath])) { // Check if the file data is already cached - if ($fileDriver->isExists($filePath)) { - $fileData = include $filePath; - if (!is_array($fileData)) { - throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$filePath])); - } - $cache[$filePath] = $fileData; // Cache the file data - } else { - $cache[$filePath] = null; // Cache that the file does not exist or is invalid - } - } + $cache = $this->getFileCache($cache, $filePath, $fileDriver); $result = $cache[$filePath] ?: []; } else { $configFiles = $this->getFiles(); foreach ($configFiles as $file) { $configFile = $path . '/' . $file; - if (!isset($cache[$configFile])) { // Check cache first - if ($fileDriver->isExists($configFile)) { - $fileData = include $configFile; - if (!is_array($fileData)) { - throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$configFile])); - } - $cache[$configFile] = $fileData; // Cache the file data - } else { - $cache[$configFile] = null; // Cache that the file does not exist or is invalid - } - } + $cache = $this->getFileCache($cache, $configFile, $fileDriver); if ($cache[$configFile]) { $result = array_replace_recursive($result, $cache[$configFile]); } @@ -136,4 +110,28 @@ public function load($fileKey = null) } return $result ?: []; } + + /** + * @param array $cache + * @param string $filePath + * @param \Magento\Framework\Filesystem\DriverInterface $fileDriver + * @return array + * @throws \Magento\Framework\Exception\FileSystemException + * @throws \Magento\Framework\Exception\RuntimeException + */ + public function getFileCache(array $cache, string $filePath, \Magento\Framework\Filesystem\DriverInterface $fileDriver): array + { + if (!isset($cache[$filePath])) { + if ($fileDriver->isExists($filePath)) { + $fileData = include $filePath; + if (!is_array($fileData)) { + throw new RuntimeException(new Phrase("Invalid configuration file: '%1'", [$filePath])); + } + $cache[$filePath] = $fileData; + } else { + $cache[$filePath] = null; + } + } + return $cache; + } } diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index 013358a9eaa6d..b8aa301ed5dfc 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -37,16 +37,18 @@ class Cli extends Console\Application /** * Name of input option. */ - const INPUT_KEY_BOOTSTRAP = 'bootstrap'; + public const INPUT_KEY_BOOTSTRAP = 'bootstrap'; /**#@+ * Cli exit codes. */ - const RETURN_SUCCESS = 0; - const RETURN_FAILURE = 1; + public const RETURN_SUCCESS = 0; + public const RETURN_FAILURE = 1; /**#@-*/ - /**#@-*/ + /** + * @var $serviceManager + */ private $serviceManager; /** @@ -57,8 +59,6 @@ class Cli extends Console\Application private $initException; /** - * Object Manager. - * * @var ObjectManagerInterface */ private $objectManager; @@ -88,8 +88,10 @@ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') $output->writeln( '' . $exception->getMessage() . '' ); + // phpcs:disable // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage exit(static::RETURN_FAILURE); + // phpcs:enable } if ($version == 'UNKNOWN') { @@ -130,7 +132,7 @@ public function doRun(Console\Input\InputInterface $input, Console\Output\Output /** * @inheritdoc */ - protected function getDefaultCommands() + protected function getDefaultCommands():array { return array_merge(parent::getDefaultCommands(), $this->getApplicationCommands()); }