Files
2019-11-20 07:44:43 +01:00

244 lines
5.5 KiB
PHP

<?php
namespace Shudrum\Component\ArrayFinder;
class ArrayFinder implements
\ArrayAccess,
\Countable,
\Iterator,
\Serializable
{
private $content = [];
private $position = 0;
private $pathSeparator = '.';
/**
* ArrayFinder constructor.
*
* @param array $content Content of the array
*/
public function __construct(array $content = [])
{
$this->content = $content;
}
/**
* {@inheritdoc}
*/
public function offsetExists($offset)
{
if (strpos($offset, $this->pathSeparator) !== false) {
$explodedPath = explode($this->pathSeparator, $offset);
$lastOffset = array_pop($explodedPath);
$offsetExists = false;
$containerPath = implode($this->pathSeparator, $explodedPath);
$this->callAtPath($containerPath, function($container) use ($lastOffset, &$offsetExists) {
$offsetExists = isset($container[$lastOffset]);
});
return $offsetExists;
} else {
return isset($this->content[$offset]);
}
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* {@inheritdoc}
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->content[] = $value;
} else {
$this->content[$offset] = $value;
}
}
/**
* {@inheritdoc}
*/
public function offsetUnset($offset)
{
$path = explode($this->pathSeparator, $offset);
$pathToUnset = array_pop($path);
$this->callAtPath(implode($this->pathSeparator, $path), function(&$offset) use (&$pathToUnset) {
unset($offset[$pathToUnset]);
});
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->content);
}
/**
* {@inheritdoc}
*/
public function current()
{
$keys = array_keys($this->content);
return $this->content[$keys[$this->position]];
}
/**
* {@inheritdoc}
*/
public function next()
{
++$this->position;
}
/**
* {@inheritdoc}
*/
public function key()
{
$keys = array_keys($this->content);
return $keys[$this->position];
}
/**
* {@inheritdoc}
*/
public function valid()
{
$keys = array_keys($this->content);
return isset($keys[$this->position]);
}
/**
* {@inheritdoc}
*/
public function rewind()
{
$this->position = 0;
}
/**
* {@inheritdoc}
*/
public function serialize() {
return serialize($this->content);
}
/**
* {@inheritdoc}
*/
public function unserialize($content) {
$this->content = unserialize($content);
}
/**
* Change the path separator of the array wrapper.
*
* By default, the separator is: .
*
* @param string $separator Separator to set.
*
* @return ArrayFinder Current instance.
*/
public function changeSeparator($separator)
{
$this->pathSeparator = $separator;
return $this;
}
/**
* Return a value from the array corresponding to the path.
* If the path is not set in the array, then $default is returned.
*
* ex:
* $a = ['a' => ['b' => 'yeah']];
* echo $this->get('a.b'); // yeah
* echo $this->get('a.b.c', 'nope'); // nope
*
* @param string|int|null $path Path to the value. If null, return all the content.
* @param mixed $default Default value to return when path is not contained in the array.
*
* @return mixed|null Value on the array corresponding to the path, null if the key does not exist.
*/
public function get($path = null, $default = null)
{
if ($path === null) {
return $this->content;
}
$value = $default;
$this->callAtPath($path, function(&$offset) use (&$value) {
$value = $offset;
});
return $value;
}
/**
* Insert a value to the array at the specified path.
*
* ex:
* $this->set('a.b', 'yeah); // ['a' => ['b' => 'yeah']]
*
* @param string $path Path where the values will be insered.
* @param mixed $value Value ti insert.
*
* @return ArrayFinder Current instance.
*/
public function set($path, $value)
{
$this->callAtPath($path, function(&$offset) use ($value) {
$offset = $value;
}, true);
return $this;
}
private function callAtPath($path, callable $callback, $createPath = false, &$currentOffset = null)
{
if ($currentOffset === null) {
$currentOffset = &$this->content;
if (is_string($path) && $path == '') {
$callback($currentOffset);
return;
}
}
$explodedPath = explode($this->pathSeparator, $path);
$nextPath = array_shift($explodedPath);
if (!isset($currentOffset[$nextPath])) {
if ($createPath) {
$currentOffset[$nextPath] = [];
} else {
return;
}
}
if (count($explodedPath) > 0) {
$this->callAtPath(
implode($this->pathSeparator, $explodedPath),
$callback,
$createPath,
$currentOffset[$nextPath]
);
} else {
$callback($currentOffset[$nextPath]);
}
}
}