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