<?php /** * StudipArrayObject * * This ArrayObject is a rewrite of the implementation to fix * issues with php's implementation of ArrayObject regarding cyclic references * based on Zend\Stdlib\ArrayObject\PhpReferenceCompatibility * * @author André Noack <noack@data-quest.de> * * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class StudipArrayObject implements IteratorAggregate, ArrayAccess, Countable { /** * Properties of the object have their normal functionality * when accessed as list (var_dump, foreach, etc.). */ const STD_PROP_LIST = 1; /** * Entries can be accessed as properties (read and write). */ const ARRAY_AS_PROPS = 2; /** * @var array */ protected $storage; /** * @var int */ protected $flag; /** * @var string */ protected $iteratorClass; /** * @var array */ protected $protectedProperties; /** * Constructor * * @param array $input * @param int $flags * @param string $iteratorClass */ public function __construct($input = [], $flags = self::STD_PROP_LIST, $iteratorClass = 'ArrayIterator') { $this->setFlags($flags); $this->storage = $input; $this->setIteratorClass($iteratorClass); $this->protectedProperties = array_keys(get_object_vars($this)); } /** * Returns whether the requested key exists * * @param mixed $key * @return bool */ public function __isset($key) { if ($this->flag == self::ARRAY_AS_PROPS) { return $this->offsetExists($key); } $this->validateKeyUsage($key); return isset($this->$key); } /** * Sets the value at the specified key to value * * @param mixed $key * @param mixed $value * @return void */ public function __set($key, $value) { if ($this->flag == self::ARRAY_AS_PROPS) { $this->offsetSet($key, $value); return; } $this->validateKeyUsage($key); $this->$key = $value; } /** * Unsets the value at the specified key * * @param mixed $key * @return void */ public function __unset($key) { if ($this->flag == self::ARRAY_AS_PROPS) { $this->offsetUnset($key); return; } $this->validateKeyUsage($key); unset($this->$key); } /** * Returns the value at the specified key * * @param mixed $key * @return mixed */ public function __get($key) { if ($this->flag == self::ARRAY_AS_PROPS) { $ret = $this->offsetGet($key); return $ret; } $this->validateKeyUsage($key); return $this->$key; } /** * Called when serializing an ArrayObject */ public function __serialize(): array { return get_object_vars($this); } /** * Called when unserializing an ArrayObject */ public function __unserialize(array $data): void { $this->protectedProperties = array_keys(get_object_vars($this)); foreach ($data as $k => $v) { switch ($k) { case 'flag': $this->setFlags($v); break; case 'storage': $this->exchangeArray($v); break; case 'iteratorClass': $this->setIteratorClass($v); break; case 'protectedProperties': break; default: $this->__set($k, $v); } } } /** * Appends the value * * @param mixed $value * @return void */ public function append($value) { $this->storage[] = $value; } /** * Sort the entries by value * * @return void */ public function asort() { asort($this->storage); } /** * Get the number of public properties in the ArrayObject */ public function count(): int { return count($this->storage); } /** * Exchange the array for another one. * * @param array|ArrayObject $data * @return array */ public function exchangeArray($data) { if (!is_array($data) && !is_object($data)) { throw new InvalidArgumentException('Passed variable is not an array or object, using empty array instead'); } if (is_object($data) && ($data instanceof self || $data instanceof \ArrayObject)) { $data = $data->getArrayCopy(); } if (!is_array($data)) { $data = (array) $data; } $storage = $this->storage; $this->storage = $data; return $storage; } /** * Creates a copy of the ArrayObject. * * @return array */ public function getArrayCopy() { return $this->storage; } /** * Gets the behavior flags. * * @return int */ public function getFlags() { return $this->flag; } /** * Create a new iterator from an ArrayObject instance */ public function getIterator(): Traversable { $class = $this->iteratorClass; return new $class($this->storage); } /** * Gets the iterator classname for the ArrayObject. * * @return string */ public function getIteratorClass() { return $this->iteratorClass; } /** * Sort the entries by key * * @return void */ public function ksort() { ksort($this->storage); } /** * Sort an array using a case insensitive "natural order" algorithm * * @return void */ public function natcasesort() { natcasesort($this->storage); } /** * Sort entries using a "natural order" algorithm * * @return void */ public function natsort() { natsort($this->storage); } /** * Returns whether the requested key exists * * @param mixed $key */ public function offsetExists($key): bool { return isset($this->storage[$key]); } /** * Returns the value at the specified key * * @param mixed $key */ public function offsetGet($key): mixed { $ret = null; if (!$this->offsetExists($key)) { return $ret; } $ret = $this->storage[$key]; return $ret; } /** * Sets the value at the specified key to value * * @param mixed $key * @param mixed $value */ public function offsetSet($key, $value): void { if (is_null($key)) { $this->append($value); return; } $this->storage[$key] = $value; } /** * Unsets the value at the specified key * * @param mixed $key */ public function offsetUnset($key): void { if ($this->offsetExists($key)) { unset($this->storage[$key]); } } /** * Sets the behavior flags * * @param int $flags * @return void */ public function setFlags($flags) { $this->flag = $flags; } /** * Sets the iterator classname for the ArrayObject * * @param string $class * @return void */ public function setIteratorClass($class) { if (class_exists($class)) { $this->iteratorClass = $class; return ; } if (mb_strpos($class, '\\') === 0) { $class = '\\' . $class; if (class_exists($class)) { $this->iteratorClass = $class; return ; } } throw new InvalidArgumentException('The iterator class does not exist'); } /** * Sort the entries with a user-defined comparison function and maintain key association * * @param callable $function * @return void */ public function uasort($function) { if (is_callable($function)) { uasort($this->storage, $function); } } /** * Sort the entries by keys using a user-defined comparison function * * @param callable $function * @return void */ public function uksort($function) { if (is_callable($function)) { uksort($this->storage, $function); } } /** * Validates whether the given key is a protected property. * * @param string $key The key to validate * @throws InvalidArgumentException when key is invalid */ protected function validateKeyUsage($key) { if (in_array($key, $this->protectedProperties)) { throw new InvalidArgumentException("{$key} is a protected property, use a different key"); } } /** * Returns whether the given value is in the underlying array. */ public function contains($value): bool { return in_array($value, $this->storage); } }