Add ArrayObject and make Object not to implement ArrayAccess interface. Remove Toolbox dependency.

This commit is contained in:
Matias Griese
2017-09-08 14:44:37 +03:00
parent 758ccadb07
commit 8c3210332e
7 changed files with 316 additions and 38 deletions

View File

@@ -0,0 +1,69 @@
<?php
/**
* @package Grav\Framework\Object
*
* @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Object;
/**
* Object class.
*
* @package Grav\Framework\Object
*/
class ArrayObject extends Object implements \ArrayAccess
{
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
if (strpos($offset, '.') !== false) {
$test = new \stdClass();
return $this->getProperty($offset, $test) !== $test;
}
return $this->__isset($offset);
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return $this->getProperty($offset);
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
$this->setProperty($offset, $value);
}
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
*/
public function offsetUnset($offset)
{
if (strpos($offset, '.') !== false) {
$this->setProperty($offset, null);
} else {
$this->__unset($offset);
}
}
}

View File

@@ -8,9 +8,6 @@
namespace Grav\Framework\Object;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
/**
* Object class.
*
@@ -18,10 +15,126 @@ use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
*/
class Object implements ObjectInterface
{
use ObjectTrait, NestedArrayAccessWithGetters, Export {
NestedArrayAccessWithGetters::offsetExists as private parentOffsetExists;
NestedArrayAccessWithGetters::offsetGet as private parentOffsetGet;
NestedArrayAccessWithGetters::offsetSet as private parentOffsetSet;
use ObjectTrait;
static protected $prefix = 'o.';
static protected $type;
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example $value = $this->get('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return mixed Value.
*/
public function getProperty($name, $default = null, $separator = '.')
{
$path = explode($separator, $name);
$offset = array_shift($path);
$current = $this->__get($offset);
do {
// We are done: return current variable.
if (empty($path)) {
return $current;
}
// Get property of nested Object.
if ($current instanceof Object) {
return $current->getProperty(implode($separator, $path), $default, $separator);
}
$offset = array_shift($path);
if ((is_array($current) || is_a($current, 'ArrayAccess')) && isset($current[$offset])) {
$current = $current[$offset];
} elseif (is_object($current) && isset($current->{$offset})) {
$current = $current->{$offset};
} else {
return $default;
}
} while ($path);
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @example $data->set('this.is.my.nested.variable', $value);
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function setProperty($name, $value, $separator = '.')
{
$path = explode($separator, $name);
$offset = array_shift($path);
// Set simple variable.
if (empty($path)) {
$this->__set($offset, $value);
return $this;
}
$current = &$this->getRef($offset, true);
do {
// Set property of nested Object.
if ($current instanceof Object) {
$current->setProperty(implode($separator, $path), $value, $separator);
return $this;
}
$offset = array_shift($path);
if (is_object($current)) {
// Handle objects.
if (!isset($current->{$offset})) {
$current->{$offset} = [];
}
$current = &$current->{$offset};
} else {
// Handle arrays and scalars.
if (!is_array($current)) {
$current = [$offset => []];
} elseif (!isset($current[$offset])) {
$current[$offset] = [];
}
$current = &$current[$offset];
}
} while ($path);
$current = $value;
return $this;
}
/**
* Define value by using dot notation for nested arrays/objects.
*
* @example $data->defProperty('this.is.my.nested.variable', $value);
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function defProperty($name, $value, $separator = '.')
{
$test = new \stdClass;
if ($this->getProperty($name, $test, $separator) === $test) {
$this->setProperty($name, $value, $separator);
}
return $this;
}
/**
@@ -30,11 +143,9 @@ class Object implements ObjectInterface
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
public function __isset($offset)
{
$methodName = "offsetLoad_{$offset}";
return $this->parentOffsetExists($offset) || method_exists($this, $methodName);
return array_key_exists($offset, $this->items) || $this->isPropertyDefined($offset);
}
/**
@@ -43,18 +154,11 @@ class Object implements ObjectInterface
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
public function __get($offset)
{
$methodName = "offsetLoad_{$offset}";
if (!$this->parentOffsetExists($offset) && method_exists($this, $methodName)) {
$this->offsetSet($offset, $this->{$methodName}());
}
return $this->parentOffsetGet($offset);
return $this->getRef($offset);
}
/**
* Assigns a value to the specified offset with a possibility to check or alter the value by
* $this->offsetPrepare_{$offset}().
@@ -62,15 +166,41 @@ class Object implements ObjectInterface
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
public function __set($offset, $value)
{
$methodName = "offsetPrepare_{$offset}";
if ($this->isPropertyDefined($offset)) {
$methodName = "offsetPrepare_{$offset}";
if (method_exists($this, $methodName)) {
$value = $this->{$methodName}($value);
if (method_exists($this, $methodName)) {
$this->{$offset} = $this->{$methodName}($value);
}
}
$this->parentOffsetSet($offset, $value);
$this->items[$offset] = $value;
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
if ($this->isPropertyDefined($offset)) {
$this->{$offset} = null;
} else {
unset($this->items[$offset]);
}
}
/**
* Convert object into an array.
*
* @return array
*/
protected function toArray()
{
return $this->items;
}
/**
@@ -80,6 +210,40 @@ class Object implements ObjectInterface
*/
public function jsonSerialize()
{
return ['key' => $this->getKey(), 'object' => $this->toArray()];
return [
'key' => $this->getKey(),
'type' => $this->getType(true),
'object' => $this->toArray()
];
}
protected function &getRef($offset, $new = false)
{
if ($this->isPropertyDefined($offset)) {
if ($this->{$offset} === null) {
$methodName = "offsetLoad_{$offset}";
if (method_exists($this, $methodName)) {
$this->{$offset} = $this->{$methodName}();
}
}
return $this->{$offset};
}
if (!isset($this->items[$offset])) {
if (!$new) {
$null = null;
return $null;
}
$this->items[$offset] = [];
}
return $this->items[$offset];
}
protected function isPropertyDefined($offset)
{
return array_key_exists($offset, get_object_vars($this));
}
}

View File

@@ -18,6 +18,8 @@ class ObjectCollection extends ArrayCollection implements ObjectCollectionInterf
{
use ObjectCollectionTrait;
static protected $prefix = 'c.';
/**
* @param array $elements
* @param string $key
@@ -27,7 +29,11 @@ class ObjectCollection extends ArrayCollection implements ObjectCollectionInterf
{
parent::__construct($elements);
$this->key = $key ?: $this->getKey() ?: __CLASS__ . '@' . spl_object_hash($this);
$this->key = $key !== null ? $key : (string) $this;
if ($this->key === null) {
throw new \InvalidArgumentException('Object cannot be created without assigning a key to it');
}
}
/**

View File

@@ -36,14 +36,14 @@ interface ObjectCollectionInterface extends CollectionInterface, ObjectInterface
/**
* @param string $property Object property to be fetched.
* @return array Values of the property.
* @param mixed $default Default value if not set.
* @return array Property value.
*/
public function getProperty($property);
public function getProperty($property, $default = null);
/**
* @param string $property Object property to be updated.
* @param string $value New value.
* @return $this
*/
public function setProperty($property, $value);

View File

@@ -46,14 +46,16 @@ trait ObjectCollectionTrait
/**
* @param string $property Object property to be fetched.
* @param mixed $default Default value if not set.
* @return array Key/Value pairs of the properties.
*/
public function getProperty($property)
public function getProperty($property, $default = null)
{
$list = [];
/** @var ObjectInterface $element */
foreach ($this as $id => $element) {
$list[$id] = isset($element[$property]) ? $element[$property] : null;
$list[$id] = $element->getProperty($property, $default);
}
return $list;
@@ -62,12 +64,16 @@ trait ObjectCollectionTrait
/**
* @param string $property Object property to be updated.
* @param string $value New value.
* @return $this
*/
public function setProperty($property, $value)
{
/** @var ObjectInterface $element */
foreach ($this as $element) {
$element[$property] = $value;
$element->setProperty($property, $value);
}
return $this;
}
/**
@@ -98,8 +104,9 @@ trait ObjectCollectionTrait
{
$list = [];
/** @var ObjectInterface $element */
foreach ($this as $element) {
$list[$element[$property]][] = $element;
$list[(string) $element->getProperty($property)][] = $element;
}
return $list;

View File

@@ -12,7 +12,7 @@ namespace Grav\Framework\Object;
* Object Interface
* @package Grav\Framework\Object
*/
interface ObjectInterface extends \ArrayAccess, \JsonSerializable
interface ObjectInterface extends \JsonSerializable
{
/**
* @param array $elements
@@ -20,8 +20,26 @@ interface ObjectInterface extends \ArrayAccess, \JsonSerializable
*/
public function __construct(array $elements = [], $key = null);
/**
* @return string
*/
public function getType();
/**
* @return string
*/
public function getKey();
/**
* @param string $property Object property to be fetched.
* @param mixed $default Default value if not set.
* @return mixed Property value.
*/
public function getProperty($property, $default = null);
/**
* @param string $property Object property to be updated.
* @param string $value New value.
*/
public function setProperty($property, $value);
}

View File

@@ -35,13 +35,27 @@ trait ObjectTrait
{
$this->items = $elements;
$this->key = $key !== null ? $key : $this->getKey();
$this->key = $key !== null ? $key : (string) $this;
if ($this->key === null) {
throw new \InvalidArgumentException('Object cannot be created without assigning a key');
throw new \InvalidArgumentException('Object cannot be created without assigning a key to it');
}
}
/**
* @param bool $prefix
* @return string
*/
public function getType($prefix = false)
{
if (static::$type) {
return ($prefix ? static::$prefix : '') . static::$type;
}
$class = get_class($this);
return ($prefix ? static::$prefix : '') . strtolower(substr($class, strrpos($class, '\\') + 1));
}
/**
* @return string
*/
@@ -57,6 +71,6 @@ trait ObjectTrait
*/
public function __toString()
{
return __CLASS__ . '@' . spl_object_hash($this);
return $this->getKey() ?: $this->getType() . '@' . spl_object_hash($this);
}
}