* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ declare(strict_types=1); namespace PrestaShop\PrestaShop\Core\Util; use ArrayAccess; use Countable; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; /** * ArrayFinder allows to modify an array content using selectors such as $arrayFinder->get('property_a.property_3.4'); * * This class replaces https://github.com/Shudrum/ArrayFinder/blob/master/ArrayFinder.php that * was used in previous PrestaShop versions. * * Credits to Julien Martin https://github.com/Shudrum for the original class */ class ArrayFinder implements ArrayAccess, Countable { /** * @var array */ private $array; /** * @var PropertyAccessor */ private $propertyAccessor; /** * @param array $content the array to be searched and manager by ArrayFinder */ public function __construct(array $content = []) { $this->array = $content; $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); } /** * {@inheritdoc} */ public function count(): int { return count($this->array); } /** * @param string|null $path selector to find the desired value. if empty, will return full array * @param mixed|null $default default value to be returned if selector matches nothing * * @return mixed|null * * Examples of use: * $arrayFinder->get('a'); * $arrayFinder->get('a.e.9'); * $arrayFinder->get('4'); */ public function get(string $path = null, $default = null) { if ($path === null) { return $this->array; } $path = $this->convertDotPathToArrayPath($path); try { $value = $this->propertyAccessor->getValue($this->array, $path); } catch (UnexpectedTypeException $e) { // If a value within the path is neither object nor array return null; } if (null !== $value) { return $value; } return $default; } /** * @param string $path selector for the value to be set * @param mixed $value input value to be set inside array * * @return self * * Examples of use: * $arrayFinder->set('a', $aNewValue); * $arrayFinder->set('a.e.9', $aNewValue); * $arrayFinder->set('4', $aNewValue); */ public function set(string $path, $value): self { $this->propertyAccessor->setValue( $this->array, $this->convertDotPathToArrayPath($path), $value ); return $this; } /** * {@inheritdoc} * * Example of use: isset($this->arrayFinder['a'] */ public function offsetExists($offset): bool { if (is_int($offset)) { $offset = (string) $offset; } return $this->propertyAccessor->isReadable( $this->array, $this->convertDotPathToArrayPath($offset)) && ($this->get($offset) !== null) ; } /** * {@inheritdoc} * * Examples of use: * $arrayFinder[4]; * $arrayFinder['a']; */ #[\ReturnTypeWillChange] public function offsetGet($offset) { if (is_int($offset)) { $offset = (string) $offset; } return $this->get($offset); } /** * {@inheritdoc} * * Example of use: $this->arrayFinder['a'] = $value; */ public function offsetSet($offset, $value): void { if (is_int($offset)) { $offset = (string) $offset; } if ($offset === null) { $this->array[] = $value; } else { $this->set($offset, $value); } } /** * {@inheritdoc} * * Example of use: unset($this->arrayFinder['a']); */ public function offsetUnset($offset): void { if (is_int($offset)) { $offset = (string) $offset; } $this->set($offset, null); } /** * Converts paths following format 'dot' structure a.4.9.d.10 * to Symfony format [a][4][9][d][10] * * @param string $dotPath * * @return string */ private function convertDotPathToArrayPath(string $dotPath): string { if ($dotPath === '[]') { return '[0]'; } $expl = explode('.', $dotPath); $in = implode('][', $expl); return str_replace('[]', '[0]', '[' . $in . ']'); } }