data = $data; } } /** * Get value of path, default value if path doesn't exist or all data * * @param array $array Source Array * @param mixed|null $key Path * @param mixed|null $default Default value * @return mixed Value of path */ public static function getValue($array, $key, $default = null) { if (is_string($key)) { // Iterate path $keys = explode('.', $key); foreach ($keys as $key) { if (!isset($array[$key])) { return $default; } $array = &$array[$key]; } // Get value return $array; } elseif (is_null($key)) { // Get all data return $array; } return null; } /** * Set value or array of values to path * * @param array $array Target array with data * @param mixed $key Path or array of paths and values * @param mixed|null $value Value to set if path is not an array */ public static function setValue(&$array, $key, $value) { if (is_string($key)) { // Iterate path $keys = explode('.', $key); foreach ($keys as $key) { if (!isset($array[$key]) || !is_array($array[$key])) { $array[$key] = []; } $array = &$array[$key]; } // Set value to path $array = $value; } elseif (is_array($key)) { // Iterate array of paths and values foreach ($key as $k => $v) { self::setValue($array, $k, $v); } } } /** * Add value or array of values to path * * @param array $array Target array with data * @param mixed $key Path or array of paths and values * @param mixed|null $value Value to set if path is not an array * @param boolean $pop Helper to pop out last key if value is an array */ public static function addValue(&$array, $key, $value = null, $pop = false) { if (is_array($key)) { // Iterate array of paths and values foreach ($key as $k => $v) { self::addValue($array, $k, $v); } } else { // Iterate path $keys = explode('.', (string) $key); if ($pop === true) { array_pop($keys); } foreach ($keys as $key) { if (!isset($array[$key]) || !is_array($array[$key])) { $array[$key] = []; } $array = &$array[$key]; } // Add value to path $array[] = $value; } } /** * Delete path or array of paths * * @param array $array Target array with data * @param mixed $key Path or array of paths to delete */ public static function deleteValue(&$array, $key) { if (is_string($key)) { // Iterate path $keys = explode('.', $key); $last = array_pop($keys); foreach ($keys as $key) { if (!isset($array[$key])) { return; } $array = &$array[$key]; } if (isset($array[$last])) { // Detele path unset($array[$last]); } } elseif (is_array($key)) { // Iterate array of paths foreach ($key as $k) { self::deleteValue($array, $k); } } } /** * Get value of path, default value if path doesn't exist or all data * * @param mixed|null $key Path * @param mixed|null $default Default value * @return mixed Value of path */ public function get($key, $default = null, $asObject = false) { $value = self::getValue($this->data, $key, $default); if ($asObject && is_array($value)) { return new self($value); } return $value; } /** * Set value or array of values to path * * @param mixed $key Path or array of paths and values * @param mixed|null $value Value to set if path is not an array * @return $this */ public function set($key, $value = null) { self::setValue($this->data, $key, $value); return $this; } /** * Add value or array of values to path * * @param mixed $key Path or array of paths and values * @param mixed|null $value Value to set if path is not an array * @param boolean $pop Helper to pop out last key if value is an array * @return $this */ public function add($key, $value = null, $pop = false) { self::addValue($this->data, $key, $value); return $this; } /** * Check if path exists * * @param string $key Path * @return boolean */ public function has($key) { $keys = explode('.', (string) $key); $data = &$this->data; foreach ($keys as $key) { if (!isset($data[$key])) { return false; } $data = &$data[$key]; } return true; } /** * Delete path or array of paths * * @param mixed $key Path or array of paths to delete * @return $this */ public function delete($key) { self::deleteValue($this->data, $key); return $this; } /** * Increase numeric value * * @param string $key * @param float $number * @return float */ public function plus(string $key, float $number): float { $newAmount = $this->get($key, 0) + $number; $this->set($key, $newAmount); return $newAmount; } /** * Reduce numeric value * * @param string $key * @param float $number * @return float */ public function minus(string $key, float $number): float { $newAmount = $this->get($key, 0) - $number; $this->set($key, $newAmount); return $newAmount; } /** * Delete all data, data from path or array of paths and * optionally format path if it doesn't exist * * @param mixed|null $key Path or array of paths to clean * @param boolean $format Format option */ public function clear($key = null, $format = false) { if (is_string($key)) { // Iterate path $keys = explode('.', $key); $data = &$this->data; foreach ($keys as $key) { if (!isset($data[$key]) || !is_array($data[$key])) { if ($format === true) { $data[$key] = []; } else { return; } } $data = &$data[$key]; } // Clear path $data = []; } elseif (is_array($key)) { // Iterate array foreach ($key as $k) { $this->clear($k, $format); } } elseif (is_null($key)) { // Clear all data $this->data = []; } } /** * Set data * * @param array $data */ public function setData(array $data) { $this->data = $data; } /** * Set data as a reference * * @param array $data */ public function setDataAsRef(array &$data) { $this->data = &$data; } /** * @inheritDoc */ public function offsetSet($offset, $value): void { $this->set($offset, $value); } /** * @inheritDoc */ public function offsetExists($offset): bool { return $this->has($offset); } /** * @inheritDoc */ public function offsetGet($offset): mixed { return $this->get($offset); } /** * @inheritDoc */ public function offsetUnset($offset): void { $this->delete($offset); } /** * Magic methods */ public function __set($key, $value = null) { $this->set($key, $value); } public function __get($key) { return $this->get($key); } public function __isset($key) { return $this->has($key); } public function __unset($key) { $this->delete($key); } /** * Check for emptiness * * @return bool */ public function isEmpty(): bool { return !(bool) count($this->data); } /** * Return all data as array * * @return array */ public function toArray(): array { return $this->data; } /** * Return as json string * * @return false|string */ public function toJson() { return json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } /** * @return string */ public function __toString(): string { return $this->toJson(); } /** * @return array */ public function __toArray(): array { return $this->toArray(); } /** * Return the current element * @link https://php.net/manual/en/iterator.current.php * @return mixed Can return any type. * @since 5.0.0 */ public function current(): mixed { return current($this->data); } /** * Move forward to next element * @link https://php.net/manual/en/iterator.next.php * @return void Any returned value is ignored. * @since 5.0.0 */ public function next(): void { next($this->data); } /** * Return the key of the current element * @link https://php.net/manual/en/iterator.key.php * @return mixed scalar on success, or null on failure. * @since 5.0.0 */ public function key(): mixed { return key($this->data); } /** * Checks if current position is valid * @link https://php.net/manual/en/iterator.valid.php * @return bool The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. * @since 5.0.0 */ public function valid(): bool { $key = key($this->data); return ($key !== NULL && $key !== FALSE); } /** * Rewind the Iterator to the first element * @link https://php.net/manual/en/iterator.rewind.php * @return void Any returned value is ignored. * @since 5.0.0 */ public function rewind(): void { reset($this->data); } /** * @inheritDoc */ public function count(): int { return count($this->data); } }