1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-stdlib for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-stdlib/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-stdlib/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Stdlib; 10 11use ArrayAccess; 12use Countable; 13use IteratorAggregate; 14use Serializable; 15 16/** 17 * Custom framework ArrayObject implementation 18 * 19 * Extends version-specific "abstract" implementation. 20 */ 21class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable 22{ 23 /** 24 * Properties of the object have their normal functionality 25 * when accessed as list (var_dump, foreach, etc.). 26 */ 27 const STD_PROP_LIST = 1; 28 29 /** 30 * Entries can be accessed as properties (read and write). 31 */ 32 const ARRAY_AS_PROPS = 2; 33 34 /** 35 * @var array 36 */ 37 protected $storage; 38 39 /** 40 * @var int 41 */ 42 protected $flag; 43 44 /** 45 * @var string 46 */ 47 protected $iteratorClass; 48 49 /** 50 * @var array 51 */ 52 protected $protectedProperties; 53 54 /** 55 * Constructor 56 * 57 * @param array $input 58 * @param int $flags 59 * @param string $iteratorClass 60 */ 61 public function __construct($input = [], $flags = self::STD_PROP_LIST, $iteratorClass = 'ArrayIterator') 62 { 63 $this->setFlags($flags); 64 $this->storage = $input; 65 $this->setIteratorClass($iteratorClass); 66 $this->protectedProperties = array_keys(get_object_vars($this)); 67 } 68 69 /** 70 * Returns whether the requested key exists 71 * 72 * @param mixed $key 73 * @return bool 74 */ 75 public function __isset($key) 76 { 77 if ($this->flag == self::ARRAY_AS_PROPS) { 78 return $this->offsetExists($key); 79 } 80 if (in_array($key, $this->protectedProperties)) { 81 throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); 82 } 83 84 return isset($this->$key); 85 } 86 87 /** 88 * Sets the value at the specified key to value 89 * 90 * @param mixed $key 91 * @param mixed $value 92 * @return void 93 */ 94 public function __set($key, $value) 95 { 96 if ($this->flag == self::ARRAY_AS_PROPS) { 97 return $this->offsetSet($key, $value); 98 } 99 if (in_array($key, $this->protectedProperties)) { 100 throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); 101 } 102 $this->$key = $value; 103 } 104 105 /** 106 * Unsets the value at the specified key 107 * 108 * @param mixed $key 109 * @return void 110 */ 111 public function __unset($key) 112 { 113 if ($this->flag == self::ARRAY_AS_PROPS) { 114 return $this->offsetUnset($key); 115 } 116 if (in_array($key, $this->protectedProperties)) { 117 throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); 118 } 119 unset($this->$key); 120 } 121 122 /** 123 * Returns the value at the specified key by reference 124 * 125 * @param mixed $key 126 * @return mixed 127 */ 128 public function &__get($key) 129 { 130 $ret = null; 131 if ($this->flag == self::ARRAY_AS_PROPS) { 132 $ret =& $this->offsetGet($key); 133 134 return $ret; 135 } 136 if (in_array($key, $this->protectedProperties)) { 137 throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); 138 } 139 140 return $this->$key; 141 } 142 143 /** 144 * Appends the value 145 * 146 * @param mixed $value 147 * @return void 148 */ 149 public function append($value) 150 { 151 $this->storage[] = $value; 152 } 153 154 /** 155 * Sort the entries by value 156 * 157 * @return void 158 */ 159 public function asort() 160 { 161 asort($this->storage); 162 } 163 164 /** 165 * Get the number of public properties in the ArrayObject 166 * 167 * @return int 168 */ 169 public function count() 170 { 171 return count($this->storage); 172 } 173 174 /** 175 * Exchange the array for another one. 176 * 177 * @param array|ArrayObject $data 178 * @return array 179 */ 180 public function exchangeArray($data) 181 { 182 if (! is_array($data) && ! is_object($data)) { 183 throw new Exception\InvalidArgumentException( 184 'Passed variable is not an array or object, using empty array instead' 185 ); 186 } 187 188 if (is_object($data) && ($data instanceof self || $data instanceof \ArrayObject)) { 189 $data = $data->getArrayCopy(); 190 } 191 if (! is_array($data)) { 192 $data = (array) $data; 193 } 194 195 $storage = $this->storage; 196 197 $this->storage = $data; 198 199 return $storage; 200 } 201 202 /** 203 * Creates a copy of the ArrayObject. 204 * 205 * @return array 206 */ 207 public function getArrayCopy() 208 { 209 return $this->storage; 210 } 211 212 /** 213 * Gets the behavior flags. 214 * 215 * @return int 216 */ 217 public function getFlags() 218 { 219 return $this->flag; 220 } 221 222 /** 223 * Create a new iterator from an ArrayObject instance 224 * 225 * @return \Iterator 226 */ 227 public function getIterator() 228 { 229 $class = $this->iteratorClass; 230 231 return new $class($this->storage); 232 } 233 234 /** 235 * Gets the iterator classname for the ArrayObject. 236 * 237 * @return string 238 */ 239 public function getIteratorClass() 240 { 241 return $this->iteratorClass; 242 } 243 244 /** 245 * Sort the entries by key 246 * 247 * @return void 248 */ 249 public function ksort() 250 { 251 ksort($this->storage); 252 } 253 254 /** 255 * Sort an array using a case insensitive "natural order" algorithm 256 * 257 * @return void 258 */ 259 public function natcasesort() 260 { 261 natcasesort($this->storage); 262 } 263 264 /** 265 * Sort entries using a "natural order" algorithm 266 * 267 * @return void 268 */ 269 public function natsort() 270 { 271 natsort($this->storage); 272 } 273 274 /** 275 * Returns whether the requested key exists 276 * 277 * @param mixed $key 278 * @return bool 279 */ 280 public function offsetExists($key) 281 { 282 return isset($this->storage[$key]); 283 } 284 285 /** 286 * Returns the value at the specified key 287 * 288 * @param mixed $key 289 * @return mixed 290 */ 291 public function &offsetGet($key) 292 { 293 $ret = null; 294 if (! $this->offsetExists($key)) { 295 return $ret; 296 } 297 $ret =& $this->storage[$key]; 298 299 return $ret; 300 } 301 302 /** 303 * Sets the value at the specified key to value 304 * 305 * @param mixed $key 306 * @param mixed $value 307 * @return void 308 */ 309 public function offsetSet($key, $value) 310 { 311 $this->storage[$key] = $value; 312 } 313 314 /** 315 * Unsets the value at the specified key 316 * 317 * @param mixed $key 318 * @return void 319 */ 320 public function offsetUnset($key) 321 { 322 if ($this->offsetExists($key)) { 323 unset($this->storage[$key]); 324 } 325 } 326 327 /** 328 * Serialize an ArrayObject 329 * 330 * @return string 331 */ 332 public function serialize() 333 { 334 return serialize(get_object_vars($this)); 335 } 336 337 /** 338 * Sets the behavior flags 339 * 340 * @param int $flags 341 * @return void 342 */ 343 public function setFlags($flags) 344 { 345 $this->flag = $flags; 346 } 347 348 /** 349 * Sets the iterator classname for the ArrayObject 350 * 351 * @param string $class 352 * @return void 353 */ 354 public function setIteratorClass($class) 355 { 356 if (class_exists($class)) { 357 $this->iteratorClass = $class; 358 359 return ; 360 } 361 362 if (strpos($class, '\\') === 0) { 363 $class = '\\' . $class; 364 if (class_exists($class)) { 365 $this->iteratorClass = $class; 366 367 return ; 368 } 369 } 370 371 throw new Exception\InvalidArgumentException('The iterator class does not exist'); 372 } 373 374 /** 375 * Sort the entries with a user-defined comparison function and maintain key association 376 * 377 * @param callable $function 378 * @return void 379 */ 380 public function uasort($function) 381 { 382 if (is_callable($function)) { 383 uasort($this->storage, $function); 384 } 385 } 386 387 /** 388 * Sort the entries by keys using a user-defined comparison function 389 * 390 * @param callable $function 391 * @return void 392 */ 393 public function uksort($function) 394 { 395 if (is_callable($function)) { 396 uksort($this->storage, $function); 397 } 398 } 399 400 /** 401 * Unserialize an ArrayObject 402 * 403 * @param string $data 404 * @return void 405 */ 406 public function unserialize($data) 407 { 408 $ar = unserialize($data); 409 $this->protectedProperties = array_keys(get_object_vars($this)); 410 411 $this->setFlags($ar['flag']); 412 $this->exchangeArray($ar['storage']); 413 $this->setIteratorClass($ar['iteratorClass']); 414 415 foreach ($ar as $k => $v) { 416 switch ($k) { 417 case 'flag': 418 $this->setFlags($v); 419 break; 420 case 'storage': 421 $this->exchangeArray($v); 422 break; 423 case 'iteratorClass': 424 $this->setIteratorClass($v); 425 break; 426 case 'protectedProperties': 427 break; 428 default: 429 $this->__set($k, $v); 430 } 431 } 432 } 433} 434