123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Stdlib;
- /**
- * Provides methods for nested array manipulations
- *
- * @api
- * @since 100.1.0
- */
- class ArrayManager
- {
- /**
- * Default node delimiter for path
- */
- const DEFAULT_PATH_DELIMITER = '/';
- /**
- * @var array
- * @since 100.1.0
- */
- protected $parentNode;
- /**
- * @var string
- * @since 100.1.0
- */
- protected $nodeIndex;
- /**
- * Check if node exists
- *
- * @param array|string $path
- * @param array $data
- * @param string $delimiter
- * @return bool
- * @since 100.1.0
- */
- public function exists($path, array $data, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- return $this->find($path, $data, $delimiter);
- }
- /**
- * Retrieve node
- *
- * @param array|string $path
- * @param array $data
- * @param null $defaultValue
- * @param string $delimiter
- * @return mixed|null
- * @since 100.1.0
- */
- public function get($path, array $data, $defaultValue = null, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- return $this->find($path, $data, $delimiter) ? $this->parentNode[$this->nodeIndex] : $defaultValue;
- }
- /**
- * Set value into node and return modified data
- *
- * @param array|string $path
- * @param array $data
- * @param mixed $value
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function set($path, array $data, $value, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- if ($this->find($path, $data, $delimiter, true)) {
- $this->parentNode[$this->nodeIndex] = $value;
- }
- return $data;
- }
- /**
- * Set value into existing node and return modified data
- *
- * @param array|string $path
- * @param array $data
- * @param mixed $value
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function replace($path, array $data, $value, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- if ($this->find($path, $data, $delimiter)) {
- $this->parentNode[$this->nodeIndex] = $value;
- }
- return $data;
- }
- /**
- * Move value from one location to another
- *
- * @param array|string $path
- * @param string $targetPath
- * @param array $data
- * @param bool $overwrite
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function move($path, $targetPath, array $data, $overwrite = false, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- if ($this->find($path, $data, $delimiter)) {
- $parentNode = &$this->parentNode;
- $nodeIndex = &$this->nodeIndex;
- if ((!$this->find($targetPath, $data, $delimiter) || $overwrite)
- && $this->find($targetPath, $data, $delimiter, true)
- ) {
- $this->parentNode[$this->nodeIndex] = $parentNode[$nodeIndex];
- unset($parentNode[$nodeIndex]);
- }
- }
- return $data;
- }
- /**
- * Merge value with node and return modified data
- *
- * @param array|string $path
- * @param array $data
- * @param array $value
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function merge($path, array $data, array $value, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- if ($this->find($path, $data, $delimiter) && is_array($this->parentNode[$this->nodeIndex])) {
- $this->parentNode[$this->nodeIndex] = array_replace_recursive(
- $this->parentNode[$this->nodeIndex],
- $value
- );
- }
- return $data;
- }
- /**
- * Populate nested array if possible and needed
- *
- * @param array|string $path
- * @param array $data
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function populate($path, array $data, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- $this->find($path, $data, $delimiter, true);
- return $data;
- }
- /**
- * Remove node and return modified data
- *
- * @param array|string $path
- * @param array $data
- * @param string $delimiter
- * @return array
- * @since 100.1.0
- */
- public function remove($path, array $data, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- if ($this->find($path, $data, $delimiter)) {
- unset($this->parentNode[$this->nodeIndex]);
- }
- return $data;
- }
- /**
- * Finds node in nested array and saves its index and parent node reference
- *
- * @param array|string $path
- * @param array $data
- * @param string $delimiter
- * @param bool $populate
- * @return bool
- * @since 100.1.0
- */
- protected function find($path, array &$data, $delimiter, $populate = false)
- {
- if (is_array($path)) {
- $path = implode($delimiter, $path);
- }
- if ($path === null) {
- return false;
- }
- $currentNode = &$data;
- $path = explode($delimiter, $path);
- foreach ($path as $index) {
- if (!is_array($currentNode)) {
- return false;
- }
- if (!array_key_exists($index, $currentNode)) {
- if (!$populate) {
- return false;
- }
- $currentNode[$index] = [];
- }
- $this->nodeIndex = $index;
- $this->parentNode = &$currentNode;
- $currentNode = &$currentNode[$index];
- }
- return true;
- }
- /**
- * Get matching paths for elements with specified indexes
- *
- * @param array|mixed $indexes
- * @param array $data
- * @param string|array|null $startPath
- * @param string|array|null $internalPath
- * @param int|null $maxResults
- * @param string $delimiter
- * @return array
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @since 100.1.0
- */
- public function findPaths(
- $indexes,
- array $data,
- $startPath = null,
- $internalPath = null,
- $maxResults = null,
- $delimiter = self::DEFAULT_PATH_DELIMITER
- ) {
- $indexes = (array)$indexes;
- $startPath = is_array($startPath) ? implode($delimiter, $startPath) : $startPath;
- $internalPath = is_array($internalPath) ? implode($delimiter, $internalPath) : $internalPath;
- $data = $startPath !== null ? $this->get($startPath, $data, [], $delimiter) : $data;
- $checkList = [$startPath => ['start' => $startPath === null, 'children' => $data]];
- $paths = [];
- while ($checkList) {
- $nextCheckList = [];
- foreach ($checkList as $path => $config) {
- foreach ($config['children'] as $childIndex => $childData) {
- $childPath = $path . (!$config['start'] ? $delimiter : '') . $childIndex;
- if (in_array($childIndex, $indexes, true)) {
- $paths[] = $childPath;
- if ($maxResults !== null && count($paths) >= $maxResults) {
- return $paths;
- }
- }
- $searchData = $internalPath !== null && is_array($childData)
- ? $this->get($internalPath, $childData, null, $delimiter)
- : $childData;
- if (!empty($searchData) && is_array($searchData)) {
- $searchPath = $childPath . ($internalPath !== null ? $delimiter . $internalPath : '');
- $nextCheckList[$searchPath] = ['start' => false, 'children' => $searchData];
- }
- }
- }
- $checkList = $nextCheckList;
- }
- return $paths;
- }
- /**
- * Get first matching path for elements with specified indexes
- *
- * @param array|mixed $indexes
- * @param array $data
- * @param string|array|null $startPath
- * @param string|array|null $internalPath
- * @param string $delimiter
- * @return string|null
- * @since 100.1.0
- */
- public function findPath(
- $indexes,
- array $data,
- $startPath = null,
- $internalPath = null,
- $delimiter = self::DEFAULT_PATH_DELIMITER
- ) {
- $paths = $this->findPaths($indexes, $data, $startPath, $internalPath, 1, $delimiter);
- return $paths ? reset($paths) : null;
- }
- /**
- * Retrieve slice of specified path
- *
- * @param string $path
- * @param int $offset
- * @param int|null $length
- * @param string $delimiter
- * @return string
- * @since 100.1.0
- */
- public function slicePath($path, $offset, $length = null, $delimiter = self::DEFAULT_PATH_DELIMITER)
- {
- return implode($delimiter, array_slice(explode($delimiter, $path), $offset, $length));
- }
- }
|