root/ext/spl/spl_array.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. spl_array_from_obj
  2. spl_array_get_hash_table
  3. spl_array_is_object
  4. spl_array_create_ht_iter
  5. spl_array_get_pos_ptr
  6. spl_array_object_free_storage
  7. spl_array_object_new_ex
  8. spl_array_object_new
  9. spl_array_object_clone
  10. spl_array_get_dimension_ptr
  11. spl_array_read_dimension_ex
  12. spl_array_read_dimension
  13. spl_array_write_dimension_ex
  14. spl_array_write_dimension
  15. spl_array_unset_dimension_ex
  16. spl_array_unset_dimension
  17. spl_array_has_dimension_ex
  18. spl_array_has_dimension
  19. spl_array_object_verify_pos_ex
  20. spl_array_object_verify_pos
  21. SPL_METHOD
  22. SPL_METHOD
  23. SPL_METHOD
  24. spl_array_iterator_append
  25. SPL_METHOD
  26. SPL_METHOD
  27. SPL_METHOD
  28. spl_array_get_properties
  29. spl_array_get_debug_info
  30. spl_array_get_gc
  31. spl_array_read_property
  32. spl_array_write_property
  33. spl_array_get_property_ptr_ptr
  34. spl_array_has_property
  35. spl_array_unset_property
  36. spl_array_compare_objects
  37. spl_array_skip_protected
  38. spl_array_next_ex
  39. spl_array_next
  40. spl_array_it_dtor
  41. spl_array_it_valid
  42. spl_array_it_get_current_data
  43. spl_array_it_get_current_key
  44. spl_array_it_move_forward
  45. spl_array_rewind
  46. spl_array_it_rewind
  47. spl_array_set_array
  48. spl_array_get_iterator
  49. SPL_METHOD
  50. SPL_METHOD
  51. SPL_METHOD
  52. SPL_METHOD
  53. SPL_METHOD
  54. SPL_METHOD
  55. SPL_METHOD
  56. SPL_METHOD
  57. SPL_METHOD
  58. spl_array_object_count_elements_helper
  59. spl_array_object_count_elements
  60. SPL_METHOD
  61. spl_array_method
  62. SPL_ARRAY_METHOD
  63. SPL_METHOD
  64. spl_array_iterator_key
  65. SPL_METHOD
  66. SPL_METHOD
  67. SPL_METHOD
  68. SPL_METHOD
  69. SPL_METHOD
  70. SPL_METHOD
  71. PHP_MINIT_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Marcus Boerger <helly@php.net>                              |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 #ifdef HAVE_CONFIG_H
  22 # include "config.h"
  23 #endif
  24 
  25 #include "php.h"
  26 #include "php_ini.h"
  27 #include "ext/standard/info.h"
  28 #include "ext/standard/php_var.h"
  29 #include "zend_smart_str.h"
  30 #include "zend_interfaces.h"
  31 #include "zend_exceptions.h"
  32 
  33 #include "php_spl.h"
  34 #include "spl_functions.h"
  35 #include "spl_engine.h"
  36 #include "spl_iterators.h"
  37 #include "spl_array.h"
  38 #include "spl_exceptions.h"
  39 
  40 zend_object_handlers spl_handler_ArrayObject;
  41 PHPAPI zend_class_entry  *spl_ce_ArrayObject;
  42 
  43 zend_object_handlers spl_handler_ArrayIterator;
  44 PHPAPI zend_class_entry  *spl_ce_ArrayIterator;
  45 PHPAPI zend_class_entry  *spl_ce_RecursiveArrayIterator;
  46 
  47 #define SPL_ARRAY_STD_PROP_LIST      0x00000001
  48 #define SPL_ARRAY_ARRAY_AS_PROPS     0x00000002
  49 #define SPL_ARRAY_CHILD_ARRAYS_ONLY  0x00000004
  50 #define SPL_ARRAY_OVERLOADED_REWIND  0x00010000
  51 #define SPL_ARRAY_OVERLOADED_VALID   0x00020000
  52 #define SPL_ARRAY_OVERLOADED_KEY     0x00040000
  53 #define SPL_ARRAY_OVERLOADED_CURRENT 0x00080000
  54 #define SPL_ARRAY_OVERLOADED_NEXT    0x00100000
  55 #define SPL_ARRAY_IS_SELF            0x01000000
  56 #define SPL_ARRAY_USE_OTHER          0x02000000
  57 #define SPL_ARRAY_INT_MASK           0xFFFF0000
  58 #define SPL_ARRAY_CLONE_MASK         0x0100FFFF
  59 
  60 #define SPL_ARRAY_METHOD_NO_ARG                         0
  61 #define SPL_ARRAY_METHOD_USE_ARG                1
  62 #define SPL_ARRAY_METHOD_MAY_USER_ARG           2
  63 
  64 typedef struct _spl_array_object {
  65         zval              array;
  66         uint32_t          ht_iter;
  67         int               ar_flags;
  68         unsigned char     nApplyCount;
  69         zend_function     *fptr_offset_get;
  70         zend_function     *fptr_offset_set;
  71         zend_function     *fptr_offset_has;
  72         zend_function     *fptr_offset_del;
  73         zend_function     *fptr_count;
  74         zend_class_entry* ce_get_iterator;
  75         zend_object       std;
  76 } spl_array_object;
  77 
  78 static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ {
  79         return (spl_array_object*)((char*)(obj) - XtOffsetOf(spl_array_object, std));
  80 }
  81 /* }}} */
  82 
  83 #define Z_SPLARRAY_P(zv)  spl_array_from_obj(Z_OBJ_P((zv)))
  84 
  85 static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */
  86         //??? TODO: Delay duplication for arrays; only duplicate for write operations
  87         if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
  88                 if (!intern->std.properties) {
  89                         rebuild_object_properties(&intern->std);
  90                 }
  91                 return intern->std.properties;
  92         } else if (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
  93                 spl_array_object *other = Z_SPLARRAY_P(&intern->array);
  94                 return spl_array_get_hash_table(other);
  95         } else if (Z_TYPE(intern->array) == IS_ARRAY) {
  96                 return Z_ARRVAL(intern->array);
  97         } else {
  98                 zend_object *obj = Z_OBJ(intern->array);
  99                 if (!obj->properties) {
 100                         rebuild_object_properties(obj);
 101                 } else if (GC_REFCOUNT(obj->properties) > 1) {
 102                         if (EXPECTED(!(GC_FLAGS(obj->properties) & IS_ARRAY_IMMUTABLE))) {
 103                                 GC_REFCOUNT(obj->properties)--;
 104                         }
 105                         obj->properties = zend_array_dup(obj->properties);
 106                 }
 107                 return obj->properties;
 108         }
 109 } /* }}} */
 110 
 111 static inline zend_bool spl_array_is_object(spl_array_object *intern) /* {{{ */
 112 {
 113         while (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
 114                 intern = Z_SPLARRAY_P(&intern->array);
 115         }
 116         return (intern->ar_flags & SPL_ARRAY_IS_SELF) || Z_TYPE(intern->array) == IS_OBJECT;
 117 }
 118 /* }}} */
 119 
 120 static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht);
 121 
 122 static zend_never_inline void spl_array_create_ht_iter(HashTable *ht, spl_array_object* intern) /* {{{ */
 123 {
 124         intern->ht_iter = zend_hash_iterator_add(ht, ht->nInternalPointer);
 125         zend_hash_internal_pointer_reset_ex(ht, &EG(ht_iterators)[intern->ht_iter].pos);
 126         spl_array_skip_protected(intern, ht);
 127 }
 128 /* }}} */
 129 
 130 static zend_always_inline uint32_t *spl_array_get_pos_ptr(HashTable *ht, spl_array_object* intern) /* {{{ */
 131 {
 132         if (UNEXPECTED(intern->ht_iter == (uint32_t)-1)) {
 133                 spl_array_create_ht_iter(ht, intern);
 134         }
 135         return &EG(ht_iterators)[intern->ht_iter].pos;
 136 }
 137 /* }}} */
 138 
 139 /* {{{ spl_array_object_free_storage */
 140 static void spl_array_object_free_storage(zend_object *object)
 141 {
 142         spl_array_object *intern = spl_array_from_obj(object);
 143         
 144         if (intern->ht_iter != (uint32_t) -1) {
 145                 zend_hash_iterator_del(intern->ht_iter);
 146         }
 147 
 148         zend_object_std_dtor(&intern->std);
 149 
 150         zval_ptr_dtor(&intern->array);
 151 }
 152 /* }}} */
 153 
 154 zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
 155 
 156 /* {{{ spl_array_object_new_ex */
 157 static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zval *orig, int clone_orig)
 158 {
 159         spl_array_object *intern;
 160         zend_class_entry *parent = class_type;
 161         int inherited = 0;
 162 
 163         intern = ecalloc(1, sizeof(spl_array_object) + zend_object_properties_size(parent));
 164 
 165         zend_object_std_init(&intern->std, class_type);
 166         object_properties_init(&intern->std, class_type);
 167 
 168         intern->ar_flags = 0;
 169         intern->ce_get_iterator = spl_ce_ArrayIterator;
 170         if (orig) {
 171                 spl_array_object *other = Z_SPLARRAY_P(orig);
 172 
 173                 intern->ar_flags &= ~ SPL_ARRAY_CLONE_MASK;
 174                 intern->ar_flags |= (other->ar_flags & SPL_ARRAY_CLONE_MASK);
 175                 intern->ce_get_iterator = other->ce_get_iterator;
 176                 if (clone_orig) {
 177                         if (other->ar_flags & SPL_ARRAY_IS_SELF) {
 178                                 ZVAL_UNDEF(&intern->array);
 179                         } else if (Z_OBJ_HT_P(orig) == &spl_handler_ArrayObject) {
 180                                 ZVAL_ARR(&intern->array,
 181                                         zend_array_dup(spl_array_get_hash_table(other)));
 182                         } else {
 183                                 ZEND_ASSERT(Z_OBJ_HT_P(orig) == &spl_handler_ArrayIterator);
 184                                 ZVAL_COPY(&intern->array, orig);
 185                                 intern->ar_flags |= SPL_ARRAY_USE_OTHER;
 186                         }
 187                 } else {
 188                         ZVAL_COPY(&intern->array, orig);
 189                         intern->ar_flags |= SPL_ARRAY_USE_OTHER;
 190                 }
 191         } else {
 192                 array_init(&intern->array);
 193         }
 194 
 195         while (parent) {
 196                 if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator) {
 197                         intern->std.handlers = &spl_handler_ArrayIterator;
 198                         class_type->get_iterator = spl_array_get_iterator;
 199                         break;
 200                 } else if (parent == spl_ce_ArrayObject) {
 201                         intern->std.handlers = &spl_handler_ArrayObject;
 202                         break;
 203                 }
 204                 parent = parent->parent;
 205                 inherited = 1;
 206         }
 207         if (!parent) { /* this must never happen */
 208                 php_error_docref(NULL, E_COMPILE_ERROR, "Internal compiler error, Class is not child of ArrayObject or ArrayIterator");
 209         }
 210         if (inherited) {
 211                 intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
 212                 if (intern->fptr_offset_get->common.scope == parent) {
 213                         intern->fptr_offset_get = NULL;
 214                 }
 215                 intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
 216                 if (intern->fptr_offset_set->common.scope == parent) {
 217                         intern->fptr_offset_set = NULL;
 218                 }
 219                 intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
 220                 if (intern->fptr_offset_has->common.scope == parent) {
 221                         intern->fptr_offset_has = NULL;
 222                 }
 223                 intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset",  sizeof("offsetunset") - 1);
 224                 if (intern->fptr_offset_del->common.scope == parent) {
 225                         intern->fptr_offset_del = NULL;
 226                 }
 227                 intern->fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
 228                 if (intern->fptr_count->common.scope == parent) {
 229                         intern->fptr_count = NULL;
 230                 }
 231         }
 232         /* Cache iterator functions if ArrayIterator or derived. Check current's */
 233         /* cache since only current is always required */
 234         if (intern->std.handlers == &spl_handler_ArrayIterator) {
 235                 if (!class_type->iterator_funcs.zf_current) {
 236                         class_type->iterator_funcs.zf_rewind = zend_hash_str_find_ptr(&class_type->function_table, "rewind", sizeof("rewind") - 1);
 237                         class_type->iterator_funcs.zf_valid = zend_hash_str_find_ptr(&class_type->function_table, "valid", sizeof("valid") - 1);
 238                         class_type->iterator_funcs.zf_key = zend_hash_str_find_ptr(&class_type->function_table, "key", sizeof("key") - 1);
 239                         class_type->iterator_funcs.zf_current = zend_hash_str_find_ptr(&class_type->function_table, "current", sizeof("current") - 1);
 240                         class_type->iterator_funcs.zf_next = zend_hash_str_find_ptr(&class_type->function_table, "next", sizeof("next") - 1);
 241                 }
 242                 if (inherited) {
 243                         if (class_type->iterator_funcs.zf_rewind->common.scope  != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_REWIND;
 244                         if (class_type->iterator_funcs.zf_valid->common.scope   != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_VALID;
 245                         if (class_type->iterator_funcs.zf_key->common.scope     != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_KEY;
 246                         if (class_type->iterator_funcs.zf_current->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_CURRENT;
 247                         if (class_type->iterator_funcs.zf_next->common.scope    != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_NEXT;
 248                 }
 249         }
 250 
 251         intern->ht_iter = (uint32_t)-1;
 252         return &intern->std;
 253 }
 254 /* }}} */
 255 
 256 /* {{{ spl_array_object_new */
 257 static zend_object *spl_array_object_new(zend_class_entry *class_type)
 258 {
 259         return spl_array_object_new_ex(class_type, NULL, 0);
 260 }
 261 /* }}} */
 262 
 263 /* {{{ spl_array_object_clone */
 264 static zend_object *spl_array_object_clone(zval *zobject)
 265 {
 266         zend_object *old_object;
 267         zend_object *new_object;
 268 
 269         old_object = Z_OBJ_P(zobject);
 270         new_object = spl_array_object_new_ex(old_object->ce, zobject, 1);
 271 
 272         zend_objects_clone_members(new_object, old_object);
 273 
 274         return new_object;
 275 }
 276 /* }}} */
 277 
 278 static zval *spl_array_get_dimension_ptr(int check_inherited, zval *object, zval *offset, int type) /* {{{ */
 279 {
 280         zval *retval;
 281         zend_long index;
 282         zend_string *offset_key;
 283         spl_array_object *intern = Z_SPLARRAY_P(object);
 284         HashTable *ht = spl_array_get_hash_table(intern);
 285 
 286         if (!offset || Z_ISUNDEF_P(offset)) {
 287                 return &EG(uninitialized_zval);
 288         }
 289 
 290         if ((type == BP_VAR_W || type == BP_VAR_RW) && intern->nApplyCount > 0) {
 291                 zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
 292                 return &EG(error_zval);
 293         }
 294 
 295 try_again:
 296         switch (Z_TYPE_P(offset)) {
 297         case IS_NULL:
 298            offset_key = ZSTR_EMPTY_ALLOC();
 299            goto fetch_dim_string;
 300         case IS_STRING:
 301            offset_key = Z_STR_P(offset);
 302 fetch_dim_string:
 303                 retval = zend_symtable_find(ht, offset_key);
 304                 if (retval) {
 305                         if (Z_TYPE_P(retval) == IS_INDIRECT) {
 306                                 retval = Z_INDIRECT_P(retval);
 307                                 if (Z_TYPE_P(retval) == IS_UNDEF) {
 308                                         switch (type) {
 309                                                 case BP_VAR_R:
 310                                                         zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key));
 311                                                 case BP_VAR_UNSET:
 312                                                 case BP_VAR_IS:
 313                                                         retval = &EG(uninitialized_zval);
 314                                                         break;
 315                                                 case BP_VAR_RW:
 316                                                         zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key));
 317                                                 case BP_VAR_W: {
 318                                                         ZVAL_NULL(retval);
 319                                                 }
 320                                         }
 321                                 }
 322                         }
 323                 } else {
 324                         switch (type) {
 325                                 case BP_VAR_R:
 326                                         zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key));
 327                                 case BP_VAR_UNSET:
 328                                 case BP_VAR_IS:
 329                                         retval = &EG(uninitialized_zval);
 330                                         break;
 331                                 case BP_VAR_RW:
 332                                         zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key));
 333                                 case BP_VAR_W: {
 334                                     zval value;
 335                                         ZVAL_NULL(&value);
 336                                     retval = zend_symtable_update(ht, offset_key, &value);
 337                                 }
 338                         }
 339                 }
 340                 return retval;
 341         case IS_RESOURCE:
 342                 zend_error(E_NOTICE, "Resource ID#%pd used as offset, casting to integer (%pd)", Z_RES_P(offset)->handle, Z_RES_P(offset)->handle);
 343                 index = Z_RES_P(offset)->handle;
 344                 goto num_index;
 345         case IS_DOUBLE:
 346                 index = (zend_long)Z_DVAL_P(offset);
 347                 goto num_index;
 348         case IS_FALSE:
 349                 index = 0;
 350                 goto num_index;
 351         case IS_TRUE:
 352                 index = 1;
 353                 goto num_index;
 354         case IS_LONG:
 355                 index = Z_LVAL_P(offset);
 356 num_index:
 357                 if ((retval = zend_hash_index_find(ht, index)) == NULL) {
 358                         switch (type) {
 359                                 case BP_VAR_R:
 360                                         zend_error(E_NOTICE, "Undefined offset: %pd", index);
 361                                 case BP_VAR_UNSET:
 362                                 case BP_VAR_IS:
 363                                         retval = &EG(uninitialized_zval);
 364                                         break;
 365                                 case BP_VAR_RW:
 366                                         zend_error(E_NOTICE, "Undefined offset: %pd", index);
 367                                 case BP_VAR_W: {
 368                                     zval value;
 369                                         ZVAL_UNDEF(&value);
 370                                         retval = zend_hash_index_update(ht, index, &value);
 371                            }
 372                         }
 373                 }
 374                 return retval;
 375         case IS_REFERENCE:
 376                 ZVAL_DEREF(offset);
 377                 goto try_again;
 378         default:
 379                 zend_error(E_WARNING, "Illegal offset type");
 380                 return (type == BP_VAR_W || type == BP_VAR_RW) ?
 381                         &EG(error_zval) : &EG(uninitialized_zval);
 382         }
 383 } /* }}} */
 384 
 385 static zval *spl_array_read_dimension_ex(int check_inherited, zval *object, zval *offset, int type, zval *rv) /* {{{ */
 386 {
 387         zval *ret;
 388 
 389         if (check_inherited) {
 390                 spl_array_object *intern = Z_SPLARRAY_P(object);
 391                 if (intern->fptr_offset_get) {
 392                         zval tmp;
 393                         if (!offset) {
 394                                 ZVAL_UNDEF(&tmp);
 395                                 offset = &tmp;
 396                         } else {
 397                                 SEPARATE_ARG_IF_REF(offset);
 398                         }
 399                         zend_call_method_with_1_params(object, Z_OBJCE_P(object), &intern->fptr_offset_get, "offsetGet", rv, offset);
 400                         zval_ptr_dtor(offset);
 401 
 402                         if (!Z_ISUNDEF_P(rv)) {
 403                                 return rv;
 404                         }
 405                         return &EG(uninitialized_zval);
 406                 }
 407         }
 408         ret = spl_array_get_dimension_ptr(check_inherited, object, offset, type);
 409 
 410         /* When in a write context,
 411          * ZE has to be fooled into thinking this is in a reference set
 412          * by separating (if necessary) and returning as IS_REFERENCE (with refcount == 1)
 413          */
 414 
 415         if ((type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) &&
 416             !Z_ISREF_P(ret) &&
 417             EXPECTED(ret != &EG(uninitialized_zval))) {
 418                 ZVAL_NEW_REF(ret, ret);
 419         }
 420 
 421         return ret;
 422 } /* }}} */
 423 
 424 static zval *spl_array_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
 425 {
 426         return spl_array_read_dimension_ex(1, object, offset, type, rv);
 427 } /* }}} */
 428 
 429 static void spl_array_write_dimension_ex(int check_inherited, zval *object, zval *offset, zval *value) /* {{{ */
 430 {
 431         spl_array_object *intern = Z_SPLARRAY_P(object);
 432         zend_long index;
 433         HashTable *ht;
 434 
 435         if (check_inherited && intern->fptr_offset_set) {
 436                 zval tmp;
 437 
 438                 if (!offset) {
 439                         ZVAL_NULL(&tmp);
 440                         offset = &tmp;
 441                 } else {
 442                         SEPARATE_ARG_IF_REF(offset);
 443                 }
 444                 zend_call_method_with_2_params(object, Z_OBJCE_P(object), &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
 445                 zval_ptr_dtor(offset);
 446                 return;
 447         }
 448 
 449         if (intern->nApplyCount > 0) {
 450                 zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
 451                 return;
 452         }
 453 
 454         if (Z_REFCOUNTED_P(value)) {
 455                 Z_ADDREF_P(value);
 456         }
 457 
 458         if (!offset) {
 459                 ht = spl_array_get_hash_table(intern);
 460                 zend_hash_next_index_insert(ht, value);
 461                 return;
 462         }
 463 
 464 try_again:
 465         switch (Z_TYPE_P(offset)) {
 466                 case IS_STRING:
 467                         ht = spl_array_get_hash_table(intern);
 468                         zend_symtable_update_ind(ht, Z_STR_P(offset), value);
 469                         return;
 470                 case IS_DOUBLE:
 471                         index = (zend_long)Z_DVAL_P(offset);
 472                         goto num_index;
 473                 case IS_RESOURCE:
 474                         index = Z_RES_HANDLE_P(offset);
 475                         goto num_index;
 476                 case IS_FALSE:
 477                         index = 0;
 478                         goto num_index;
 479                 case IS_TRUE:
 480                         index = 1;
 481                         goto num_index;
 482                 case IS_LONG:
 483                         index = Z_LVAL_P(offset);
 484 num_index:
 485                         ht = spl_array_get_hash_table(intern);
 486                         zend_hash_index_update(ht, index, value);
 487                         return;
 488                 case IS_NULL:
 489                         ht = spl_array_get_hash_table(intern);
 490                         zend_hash_next_index_insert(ht, value);
 491                         return;
 492                 case IS_REFERENCE:
 493                         ZVAL_DEREF(offset);
 494                         goto try_again;
 495                 default:
 496                         zend_error(E_WARNING, "Illegal offset type");
 497                         zval_ptr_dtor(value);
 498                         return;
 499         }
 500 } /* }}} */
 501 
 502 static void spl_array_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
 503 {
 504         spl_array_write_dimension_ex(1, object, offset, value);
 505 } /* }}} */
 506 
 507 static void spl_array_unset_dimension_ex(int check_inherited, zval *object, zval *offset) /* {{{ */
 508 {
 509         zend_long index;
 510         HashTable *ht;
 511         spl_array_object *intern = Z_SPLARRAY_P(object);
 512 
 513         if (check_inherited && intern->fptr_offset_del) {
 514                 SEPARATE_ARG_IF_REF(offset);
 515                 zend_call_method_with_1_params(object, Z_OBJCE_P(object), &intern->fptr_offset_del, "offsetUnset", NULL, offset);
 516                 zval_ptr_dtor(offset);
 517                 return;
 518         }
 519 
 520         if (intern->nApplyCount > 0) {
 521                 zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
 522                 return;
 523         }
 524 
 525 try_again:
 526         switch (Z_TYPE_P(offset)) {
 527         case IS_STRING:
 528                 ht = spl_array_get_hash_table(intern);
 529                 if (ht == &EG(symbol_table)) {
 530                         if (zend_delete_global_variable(Z_STR_P(offset))) {
 531                                 zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
 532                         }
 533                 } else {
 534                         zval *data = zend_symtable_find(ht, Z_STR_P(offset));
 535 
 536                         if (data) {
 537                                 if (Z_TYPE_P(data) == IS_INDIRECT) {
 538                                         data = Z_INDIRECT_P(data);
 539                                         if (Z_TYPE_P(data) == IS_UNDEF) {
 540                                                 zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
 541                                         } else {
 542                                                 zval_ptr_dtor(data);
 543                                                 ZVAL_UNDEF(data);
 544                                                 ht->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
 545                                                 zend_hash_move_forward_ex(ht, spl_array_get_pos_ptr(ht, intern));
 546                                                 if (spl_array_is_object(intern)) {
 547                                                         spl_array_skip_protected(intern, ht);
 548                                                 }
 549                                         }
 550                                 } else if (zend_symtable_del(ht, Z_STR_P(offset)) == FAILURE) {
 551                                         zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
 552                                 }
 553                         } else {
 554                                 zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
 555                         }
 556                 }
 557                 break;
 558         case IS_DOUBLE:
 559                 index = (zend_long)Z_DVAL_P(offset);
 560                 goto num_index;
 561         case IS_RESOURCE:
 562                 index = Z_RES_HANDLE_P(offset);
 563                 goto num_index;
 564         case IS_FALSE:
 565                 index = 0;
 566                 goto num_index;
 567         case IS_TRUE:
 568                 index = 1;
 569                 goto num_index;
 570         case IS_LONG:
 571                 index = Z_LVAL_P(offset);
 572 num_index:
 573                 ht = spl_array_get_hash_table(intern);
 574                 if (zend_hash_index_del(ht, index) == FAILURE) {
 575                         zend_error(E_NOTICE,"Undefined offset: %pd", index);
 576                 }
 577                 break;
 578         case IS_REFERENCE:
 579                 ZVAL_DEREF(offset);
 580                 goto try_again;
 581         default:
 582                 zend_error(E_WARNING, "Illegal offset type");
 583                 return;
 584         }
 585 } /* }}} */
 586 
 587 static void spl_array_unset_dimension(zval *object, zval *offset) /* {{{ */
 588 {
 589         spl_array_unset_dimension_ex(1, object, offset);
 590 } /* }}} */
 591 
 592 static int spl_array_has_dimension_ex(int check_inherited, zval *object, zval *offset, int check_empty) /* {{{ */
 593 {
 594         spl_array_object *intern = Z_SPLARRAY_P(object);
 595         zend_long index;
 596         zval rv, *value = NULL, *tmp;
 597 
 598         if (check_inherited && intern->fptr_offset_has) {
 599                 SEPARATE_ARG_IF_REF(offset);
 600                 zend_call_method_with_1_params(object, Z_OBJCE_P(object), &intern->fptr_offset_has, "offsetExists", &rv, offset);
 601                 zval_ptr_dtor(offset);
 602 
 603                 if (!Z_ISUNDEF(rv) && zend_is_true(&rv)) {
 604                         zval_ptr_dtor(&rv);
 605                         if (check_empty != 1) {
 606                                 return 1;
 607                         } else if (intern->fptr_offset_get) {
 608                                 value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R, &rv);
 609                         }
 610                 } else {
 611                         zval_ptr_dtor(&rv);
 612                         return 0;
 613                 }
 614         }
 615 
 616         if (!value) {
 617                 HashTable *ht = spl_array_get_hash_table(intern);
 618 
 619 try_again:
 620                 switch (Z_TYPE_P(offset)) {
 621                         case IS_STRING:
 622                                 if ((tmp = zend_symtable_find(ht, Z_STR_P(offset))) != NULL) {
 623                                         if (check_empty == 2) {
 624                                                 return 1;
 625                                         }
 626                                 } else {
 627                                         return 0;
 628                                 }
 629                                 break;
 630 
 631                         case IS_DOUBLE:
 632                                 index = (zend_long)Z_DVAL_P(offset);
 633                                 goto num_index;
 634                         case IS_RESOURCE:
 635                                 index = Z_RES_HANDLE_P(offset);
 636                                 goto num_index;
 637                         case IS_FALSE:
 638                                 index = 0;
 639                                 goto num_index;
 640                         case IS_TRUE:
 641                                 index = 1;
 642                                 goto num_index;
 643                         case IS_LONG:
 644                                 index = Z_LVAL_P(offset);
 645 num_index:
 646                                 if ((tmp = zend_hash_index_find(ht, index)) != NULL) {
 647                                         if (check_empty == 2) {
 648                                                 return 1;
 649                                         }
 650                                 } else {
 651                                         return 0;
 652                                 }
 653                                 break;
 654                         case IS_REFERENCE:
 655                                 ZVAL_DEREF(offset);
 656                                 goto try_again;
 657                         default:
 658                                 zend_error(E_WARNING, "Illegal offset type");
 659                                 return 0;
 660                 }
 661 
 662                 if (check_empty && check_inherited && intern->fptr_offset_get) {
 663                         value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R, &rv);
 664                 } else {
 665                         value = tmp;
 666                 }
 667         }
 668 
 669         {
 670                 zend_bool result = check_empty ? zend_is_true(value) : Z_TYPE_P(value) != IS_NULL;
 671                 if (value == &rv) {
 672                         zval_ptr_dtor(&rv);
 673                 }
 674                 return result;
 675         }
 676 } /* }}} */
 677 
 678 static int spl_array_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
 679 {
 680         return spl_array_has_dimension_ex(1, object, offset, check_empty);
 681 } /* }}} */
 682 
 683 /* {{{ spl_array_object_verify_pos_ex */
 684 static inline int spl_array_object_verify_pos_ex(spl_array_object *object, HashTable *ht, const char *msg_prefix)
 685 {
 686         if (!ht) {
 687                 php_error_docref(NULL, E_NOTICE, "%sArray was modified outside object and is no longer an array", msg_prefix);
 688                 return FAILURE;
 689         }
 690 
 691         return SUCCESS;
 692 } /* }}} */
 693 
 694 /* {{{ spl_array_object_verify_pos */
 695 static inline int spl_array_object_verify_pos(spl_array_object *object, HashTable *ht)
 696 {
 697         return spl_array_object_verify_pos_ex(object, ht, "");
 698 } /* }}} */
 699 
 700 /* {{{ proto bool ArrayObject::offsetExists(mixed $index)
 701        proto bool ArrayIterator::offsetExists(mixed $index)
 702    Returns whether the requested $index exists. */
 703 SPL_METHOD(Array, offsetExists)
 704 {
 705         zval *index;
 706         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
 707                 return;
 708         }
 709         RETURN_BOOL(spl_array_has_dimension_ex(0, getThis(), index, 2));
 710 } /* }}} */
 711 
 712 /* {{{ proto mixed ArrayObject::offsetGet(mixed $index)
 713        proto mixed ArrayIterator::offsetGet(mixed $index)
 714    Returns the value at the specified $index. */
 715 SPL_METHOD(Array, offsetGet)
 716 {
 717         zval *value, *index;
 718         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
 719                 return;
 720         }
 721         value = spl_array_read_dimension_ex(0, getThis(), index, BP_VAR_R, return_value);
 722         if (value != return_value) {
 723                 ZVAL_DEREF(value);
 724                 ZVAL_COPY(return_value, value);
 725         }
 726 } /* }}} */
 727 
 728 /* {{{ proto void ArrayObject::offsetSet(mixed $index, mixed $newval)
 729        proto void ArrayIterator::offsetSet(mixed $index, mixed $newval)
 730    Sets the value at the specified $index to $newval. */
 731 SPL_METHOD(Array, offsetSet)
 732 {
 733         zval *index, *value;
 734         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &index, &value) == FAILURE) {
 735                 return;
 736         }
 737         spl_array_write_dimension_ex(0, getThis(), index, value);
 738 } /* }}} */
 739 
 740 void spl_array_iterator_append(zval *object, zval *append_value) /* {{{ */
 741 {
 742         spl_array_object *intern = Z_SPLARRAY_P(object);
 743         HashTable *aht = spl_array_get_hash_table(intern);
 744 
 745         if (!aht) {
 746                 php_error_docref(NULL, E_NOTICE, "Array was modified outside object and is no longer an array");
 747                 return;
 748         }
 749 
 750         if (spl_array_is_object(intern)) {
 751                 php_error_docref(NULL, E_RECOVERABLE_ERROR, "Cannot append properties to objects, use %s::offsetSet() instead", ZSTR_VAL(Z_OBJCE_P(object)->name));
 752                 return;
 753         }
 754 
 755         spl_array_write_dimension(object, NULL, append_value);
 756 } /* }}} */
 757 
 758 /* {{{ proto void ArrayObject::append(mixed $newval)
 759        proto void ArrayIterator::append(mixed $newval)
 760    Appends the value (cannot be called for objects). */
 761 SPL_METHOD(Array, append)
 762 {
 763         zval *value;
 764 
 765         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
 766                 return;
 767         }
 768         spl_array_iterator_append(getThis(), value);
 769 } /* }}} */
 770 
 771 /* {{{ proto void ArrayObject::offsetUnset(mixed $index)
 772        proto void ArrayIterator::offsetUnset(mixed $index)
 773    Unsets the value at the specified $index. */
 774 SPL_METHOD(Array, offsetUnset)
 775 {
 776         zval *index;
 777         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
 778                 return;
 779         }
 780         spl_array_unset_dimension_ex(0, getThis(), index);
 781 } /* }}} */
 782 
 783 /* {{{ proto array ArrayObject::getArrayCopy()
 784       proto array ArrayIterator::getArrayCopy()
 785    Return a copy of the contained array */
 786 SPL_METHOD(Array, getArrayCopy)
 787 {
 788         zval *object = getThis();
 789         spl_array_object *intern = Z_SPLARRAY_P(object);
 790 
 791         RETURN_ARR(zend_array_dup(spl_array_get_hash_table(intern)));
 792 } /* }}} */
 793 
 794 static HashTable *spl_array_get_properties(zval *object) /* {{{ */
 795 {
 796         spl_array_object *intern = Z_SPLARRAY_P(object);
 797 
 798         if (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) {
 799                 if (!intern->std.properties) {
 800                         rebuild_object_properties(&intern->std);
 801                 }
 802                 return intern->std.properties;
 803         }
 804 
 805         return spl_array_get_hash_table(intern);
 806 } /* }}} */
 807 
 808 static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp) /* {{{ */
 809 {
 810         zval *storage;
 811         zend_string *zname;
 812         zend_class_entry *base;
 813         spl_array_object *intern = Z_SPLARRAY_P(obj);
 814 
 815         if (!intern->std.properties) {
 816                 rebuild_object_properties(&intern->std);
 817         }
 818 
 819         if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
 820                 *is_temp = 0;
 821                 return intern->std.properties;
 822         } else {
 823                 HashTable *debug_info;
 824                 *is_temp = 1;
 825 
 826                 ALLOC_HASHTABLE(debug_info);
 827                 ZEND_INIT_SYMTABLE_EX(debug_info, zend_hash_num_elements(intern->std.properties) + 1, 0);
 828                 zend_hash_copy(debug_info, intern->std.properties, (copy_ctor_func_t) zval_add_ref);
 829 
 830                 storage = &intern->array;
 831                 Z_TRY_ADDREF_P(storage);
 832 
 833                 base = Z_OBJ_HT_P(obj) == &spl_handler_ArrayIterator
 834                         ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
 835                 zname = spl_gen_private_prop_name(base, "storage", sizeof("storage")-1);
 836                 zend_symtable_update(debug_info, zname, storage);
 837                 zend_string_release(zname);
 838 
 839                 return debug_info;
 840         }
 841 }
 842 /* }}} */
 843 
 844 static HashTable *spl_array_get_gc(zval *obj, zval **gc_data, int *gc_data_count) /* {{{ */
 845 {
 846         spl_array_object *intern = Z_SPLARRAY_P(obj);
 847         *gc_data = &intern->array;
 848         *gc_data_count = 1;
 849         return zend_std_get_properties(obj);
 850 }
 851 /* }}} */
 852 
 853 static zval *spl_array_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
 854 {
 855         spl_array_object *intern = Z_SPLARRAY_P(object);
 856 
 857         if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
 858                 && !std_object_handlers.has_property(object, member, 2, NULL)) {
 859                 return spl_array_read_dimension(object, member, type, rv);
 860         }
 861         return std_object_handlers.read_property(object, member, type, cache_slot, rv);
 862 } /* }}} */
 863 
 864 static void spl_array_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
 865 {
 866         spl_array_object *intern = Z_SPLARRAY_P(object);
 867 
 868         if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
 869         && !std_object_handlers.has_property(object, member, 2, NULL)) {
 870                 spl_array_write_dimension(object, member, value);
 871                 return;
 872         }
 873         std_object_handlers.write_property(object, member, value, cache_slot);
 874 } /* }}} */
 875 
 876 static zval *spl_array_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
 877 {
 878         spl_array_object *intern = Z_SPLARRAY_P(object);
 879 
 880         if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
 881                 && !std_object_handlers.has_property(object, member, 2, NULL)) {
 882                 return spl_array_get_dimension_ptr(1, object, member, type);
 883         }
 884         return std_object_handlers.get_property_ptr_ptr(object, member, type, cache_slot);
 885 } /* }}} */
 886 
 887 static int spl_array_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
 888 {
 889         spl_array_object *intern = Z_SPLARRAY_P(object);
 890 
 891         if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
 892                 && !std_object_handlers.has_property(object, member, 2, NULL)) {
 893                 return spl_array_has_dimension(object, member, has_set_exists);
 894         }
 895         return std_object_handlers.has_property(object, member, has_set_exists, cache_slot);
 896 } /* }}} */
 897 
 898 static void spl_array_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
 899 {
 900         spl_array_object *intern = Z_SPLARRAY_P(object);
 901 
 902         if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
 903                 && !std_object_handlers.has_property(object, member, 2, NULL)) {
 904                 spl_array_unset_dimension(object, member);
 905                 return;
 906         }
 907         std_object_handlers.unset_property(object, member, cache_slot);
 908 } /* }}} */
 909 
 910 static int spl_array_compare_objects(zval *o1, zval *o2) /* {{{ */
 911 {
 912         HashTable                       *ht1,
 913                                                 *ht2;
 914         spl_array_object        *intern1,
 915                                                 *intern2;
 916         int                                     result  = 0;
 917 
 918         intern1 = Z_SPLARRAY_P(o1);
 919         intern2 = Z_SPLARRAY_P(o2);
 920         ht1             = spl_array_get_hash_table(intern1);
 921         ht2             = spl_array_get_hash_table(intern2);
 922 
 923         result = zend_compare_symbol_tables(ht1, ht2);
 924         /* if we just compared std.properties, don't do it again */
 925         if (result == 0 &&
 926                         !(ht1 == intern1->std.properties && ht2 == intern2->std.properties)) {
 927                 result = std_object_handlers.compare_objects(o1, o2);
 928         }
 929         return result;
 930 } /* }}} */
 931 
 932 static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht) /* {{{ */
 933 {
 934         zend_string *string_key;
 935         zend_ulong num_key;
 936         zval *data;
 937 
 938         if (spl_array_is_object(intern)) {
 939                 uint32_t *pos_ptr = spl_array_get_pos_ptr(aht, intern);
 940 
 941                 do {
 942                         if (zend_hash_get_current_key_ex(aht, &string_key, &num_key, pos_ptr) == HASH_KEY_IS_STRING) {
 943                                 data = zend_hash_get_current_data_ex(aht, pos_ptr);
 944                                 if (data && Z_TYPE_P(data) == IS_INDIRECT &&
 945                                     Z_TYPE_P(data = Z_INDIRECT_P(data)) == IS_UNDEF) {
 946                                         /* skip */
 947                                 } else if (!ZSTR_LEN(string_key) || ZSTR_VAL(string_key)[0]) {
 948                                         return SUCCESS;
 949                                 }
 950                         } else {
 951                                 return SUCCESS;
 952                         }
 953                         if (zend_hash_has_more_elements_ex(aht, pos_ptr) != SUCCESS) {
 954                                 return FAILURE;
 955                         }
 956                         zend_hash_move_forward_ex(aht, pos_ptr);
 957                 } while (1);
 958         }
 959         return FAILURE;
 960 } /* }}} */
 961 
 962 static int spl_array_next_ex(spl_array_object *intern, HashTable *aht) /* {{{ */
 963 {
 964         uint32_t *pos_ptr = spl_array_get_pos_ptr(aht, intern);
 965 
 966         zend_hash_move_forward_ex(aht, pos_ptr);
 967         if (spl_array_is_object(intern)) {
 968                 return spl_array_skip_protected(intern, aht);
 969         } else {
 970                 return zend_hash_has_more_elements_ex(aht, pos_ptr);
 971         }
 972 } /* }}} */
 973 
 974 static int spl_array_next(spl_array_object *intern) /* {{{ */
 975 {
 976         HashTable *aht = spl_array_get_hash_table(intern);
 977 
 978         return spl_array_next_ex(intern, aht);
 979 
 980 } /* }}} */
 981 
 982 static void spl_array_it_dtor(zend_object_iterator *iter) /* {{{ */
 983 {
 984         zend_user_it_invalidate_current(iter);
 985         zval_ptr_dtor(&iter->data);
 986 }
 987 /* }}} */
 988 
 989 static int spl_array_it_valid(zend_object_iterator *iter) /* {{{ */
 990 {
 991         spl_array_object *object = Z_SPLARRAY_P(&iter->data);
 992         HashTable *aht = spl_array_get_hash_table(object);
 993 
 994         if (object->ar_flags & SPL_ARRAY_OVERLOADED_VALID) {
 995                 return zend_user_it_valid(iter);
 996         } else {
 997                 if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::valid(): ") == FAILURE) {
 998                         return FAILURE;
 999                 }
1000 
1001                 return zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, object));
1002         }
1003 }
1004 /* }}} */
1005 
1006 static zval *spl_array_it_get_current_data(zend_object_iterator *iter) /* {{{ */
1007 {
1008         spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1009         HashTable *aht = spl_array_get_hash_table(object);
1010 
1011         if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
1012                 return zend_user_it_get_current_data(iter);
1013         } else {
1014                 zval *data = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, object));
1015                 if (Z_TYPE_P(data) == IS_INDIRECT) {
1016                         data = Z_INDIRECT_P(data);
1017                 }
1018                 return data;
1019         }
1020 }
1021 /* }}} */
1022 
1023 static void spl_array_it_get_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
1024 {
1025         spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1026         HashTable *aht = spl_array_get_hash_table(object);
1027 
1028         if (object->ar_flags & SPL_ARRAY_OVERLOADED_KEY) {
1029                 zend_user_it_get_current_key(iter, key);
1030         } else {
1031                 if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::current(): ") == FAILURE) {
1032                         ZVAL_NULL(key);
1033                 } else {
1034                         zend_hash_get_current_key_zval_ex(aht, key, spl_array_get_pos_ptr(aht, object));
1035                 }
1036         }
1037 }
1038 /* }}} */
1039 
1040 static void spl_array_it_move_forward(zend_object_iterator *iter) /* {{{ */
1041 {
1042         spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1043         HashTable *aht = spl_array_get_hash_table(object);
1044 
1045         if (object->ar_flags & SPL_ARRAY_OVERLOADED_NEXT) {
1046                 zend_user_it_move_forward(iter);
1047         } else {
1048                 zend_user_it_invalidate_current(iter);
1049                 if (!aht) {
1050                         php_error_docref(NULL, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and is no longer an array");
1051                         return;
1052                 }
1053 
1054                 spl_array_next_ex(object, aht);
1055         }
1056 }
1057 /* }}} */
1058 
1059 static void spl_array_rewind(spl_array_object *intern) /* {{{ */
1060 {
1061         HashTable *aht = spl_array_get_hash_table(intern);
1062 
1063         if (!aht) {
1064                 php_error_docref(NULL, E_NOTICE, "ArrayIterator::rewind(): Array was modified outside object and is no longer an array");
1065                 return;
1066         }
1067 
1068         if (intern->ht_iter == (uint32_t)-1) {
1069                 spl_array_get_pos_ptr(aht, intern);
1070         } else {
1071                 zend_hash_internal_pointer_reset_ex(aht, spl_array_get_pos_ptr(aht, intern));
1072                 spl_array_skip_protected(intern, aht);
1073         }
1074 }
1075 /* }}} */
1076 
1077 static void spl_array_it_rewind(zend_object_iterator *iter) /* {{{ */
1078 {
1079         spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1080 
1081         if (object->ar_flags & SPL_ARRAY_OVERLOADED_REWIND) {
1082                 zend_user_it_rewind(iter);
1083         } else {
1084                 zend_user_it_invalidate_current(iter);
1085                 spl_array_rewind(object);
1086         }
1087 }
1088 /* }}} */
1089 
1090 /* {{{ spl_array_set_array */
1091 static void spl_array_set_array(zval *object, spl_array_object *intern, zval *array, zend_long ar_flags, int just_array) {
1092         if (Z_TYPE_P(array) != IS_OBJECT && Z_TYPE_P(array) != IS_ARRAY) {
1093                 zend_throw_exception(spl_ce_InvalidArgumentException, "Passed variable is not an array or object, using empty array instead", 0);
1094                 return;
1095         }
1096 
1097         if (Z_TYPE_P(array) == IS_ARRAY) {
1098                 //??? TODO: try to avoid array duplication
1099                 zval_ptr_dtor(&intern->array);
1100                 ZVAL_DUP(&intern->array, array);
1101         } else {
1102                 if (Z_OBJ_HT_P(array) == &spl_handler_ArrayObject || Z_OBJ_HT_P(array) == &spl_handler_ArrayIterator) {
1103                         zval_ptr_dtor(&intern->array);
1104                         if (just_array) {
1105                                 spl_array_object *other = Z_SPLARRAY_P(array);
1106                                 ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
1107                         }
1108                         if (Z_OBJ_P(object) == Z_OBJ_P(array)) {
1109                                 ar_flags |= SPL_ARRAY_IS_SELF;
1110                                 ZVAL_UNDEF(&intern->array);
1111                         } else {
1112                                 ar_flags |= SPL_ARRAY_USE_OTHER;
1113                                 ZVAL_COPY(&intern->array, array);
1114                         }
1115                 } else {
1116                         zend_object_get_properties_t handler = Z_OBJ_HANDLER_P(array, get_properties);
1117                         if (handler != std_object_handlers.get_properties) {
1118                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
1119                                         "Overloaded object of type %s is not compatible with %s",
1120                                         ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(intern->std.ce->name));
1121                                 return;
1122                         }
1123                         zval_ptr_dtor(&intern->array);
1124                         ZVAL_COPY(&intern->array, array);
1125                 }
1126         }
1127 
1128         intern->ar_flags &= ~SPL_ARRAY_IS_SELF & ~SPL_ARRAY_USE_OTHER;
1129         intern->ar_flags |= ar_flags;
1130         intern->ht_iter = (uint32_t)-1;
1131 }
1132 /* }}} */
1133 
1134 /* iterator handler table */
1135 zend_object_iterator_funcs spl_array_it_funcs = {
1136         spl_array_it_dtor,
1137         spl_array_it_valid,
1138         spl_array_it_get_current_data,
1139         spl_array_it_get_current_key,
1140         spl_array_it_move_forward,
1141         spl_array_it_rewind
1142 };
1143 
1144 zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1145 {
1146         zend_user_iterator *iterator;
1147         spl_array_object *array_object = Z_SPLARRAY_P(object);
1148 
1149         if (by_ref && (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)) {
1150                 zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
1151         }
1152 
1153         iterator = emalloc(sizeof(zend_user_iterator));
1154 
1155         zend_iterator_init(&iterator->it);
1156 
1157         ZVAL_COPY(&iterator->it.data, object);
1158         iterator->it.funcs = &spl_array_it_funcs;
1159         iterator->ce = ce;
1160         ZVAL_UNDEF(&iterator->value);
1161 
1162         return &iterator->it;
1163 }
1164 /* }}} */
1165 
1166 /* {{{ proto void ArrayObject::__construct([array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]]])
1167        proto void ArrayIterator::__construct([array|object ar = array() [, int flags = 0]])
1168    Constructs a new array iterator from a path. */
1169 SPL_METHOD(Array, __construct)
1170 {
1171         zval *object = getThis();
1172         spl_array_object *intern;
1173         zval *array;
1174         zend_long ar_flags = 0;
1175         zend_class_entry *ce_get_iterator = spl_ce_Iterator;
1176 
1177         if (ZEND_NUM_ARGS() == 0) {
1178                 return; /* nothing to do */
1179         }
1180 
1181         if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z|lC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
1182                 return;
1183         }
1184 
1185         intern = Z_SPLARRAY_P(object);
1186 
1187         if (ZEND_NUM_ARGS() > 2) {
1188                 intern->ce_get_iterator = ce_get_iterator;
1189         }
1190 
1191         ar_flags &= ~SPL_ARRAY_INT_MASK;
1192 
1193         spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1);
1194 }
1195  /* }}} */
1196 
1197 /* {{{ proto void ArrayObject::setIteratorClass(string iterator_class)
1198    Set the class used in getIterator. */
1199 SPL_METHOD(Array, setIteratorClass)
1200 {
1201         zval *object = getThis();
1202         spl_array_object *intern = Z_SPLARRAY_P(object);
1203         zend_class_entry * ce_get_iterator = spl_ce_Iterator;
1204 
1205 #ifndef FAST_ZPP
1206         if (zend_parse_parameters(ZEND_NUM_ARGS(), "C", &ce_get_iterator) == FAILURE) {
1207                 return;
1208         }
1209 #else
1210         ZEND_PARSE_PARAMETERS_START(1, 1)
1211                 Z_PARAM_CLASS(ce_get_iterator)
1212         ZEND_PARSE_PARAMETERS_END();
1213 #endif
1214 
1215         intern->ce_get_iterator = ce_get_iterator;
1216 }
1217 /* }}} */
1218 
1219 /* {{{ proto string ArrayObject::getIteratorClass()
1220    Get the class used in getIterator. */
1221 SPL_METHOD(Array, getIteratorClass)
1222 {
1223         zval *object = getThis();
1224         spl_array_object *intern = Z_SPLARRAY_P(object);
1225 
1226         if (zend_parse_parameters_none() == FAILURE) {
1227                 return;
1228         }
1229 
1230         zend_string_addref(intern->ce_get_iterator->name);
1231         RETURN_STR(intern->ce_get_iterator->name);
1232 }
1233 /* }}} */
1234 
1235 /* {{{ proto int ArrayObject::getFlags()
1236    Get flags */
1237 SPL_METHOD(Array, getFlags)
1238 {
1239         zval *object = getThis();
1240         spl_array_object *intern = Z_SPLARRAY_P(object);
1241 
1242         if (zend_parse_parameters_none() == FAILURE) {
1243                 return;
1244         }
1245 
1246         RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
1247 }
1248 /* }}} */
1249 
1250 /* {{{ proto void ArrayObject::setFlags(int flags)
1251    Set flags */
1252 SPL_METHOD(Array, setFlags)
1253 {
1254         zval *object = getThis();
1255         spl_array_object *intern = Z_SPLARRAY_P(object);
1256         zend_long ar_flags = 0;
1257 
1258         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ar_flags) == FAILURE) {
1259                 return;
1260         }
1261 
1262         intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
1263 }
1264 /* }}} */
1265 
1266 /* {{{ proto Array|Object ArrayObject::exchangeArray(Array|Object ar = array())
1267    Replace the referenced array or object with a new one and return the old one (right now copy - to be changed) */
1268 SPL_METHOD(Array, exchangeArray)
1269 {
1270         zval *object = getThis(), *array;
1271         spl_array_object *intern = Z_SPLARRAY_P(object);
1272 
1273         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &array) == FAILURE) {
1274                 return;
1275         }
1276 
1277         if (intern->nApplyCount > 0) {
1278                 zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
1279                 return;
1280         }
1281 
1282         RETVAL_ARR(zend_array_dup(spl_array_get_hash_table(intern)));
1283         spl_array_set_array(object, intern, array, 0L, 1);
1284 }
1285 /* }}} */
1286 
1287 /* {{{ proto ArrayIterator ArrayObject::getIterator()
1288    Create a new iterator from a ArrayObject instance */
1289 SPL_METHOD(Array, getIterator)
1290 {
1291         zval *object = getThis();
1292         spl_array_object *intern = Z_SPLARRAY_P(object);
1293         HashTable *aht = spl_array_get_hash_table(intern);
1294 
1295         if (zend_parse_parameters_none() == FAILURE) {
1296                 return;
1297         }
1298 
1299         if (!aht) {
1300                 php_error_docref(NULL, E_NOTICE, "Array was modified outside object and is no longer an array");
1301                 return;
1302         }
1303 
1304         ZVAL_OBJ(return_value, spl_array_object_new_ex(intern->ce_get_iterator, object, 0));
1305 }
1306 /* }}} */
1307 
1308 /* {{{ proto void ArrayIterator::rewind()
1309    Rewind array back to the start */
1310 SPL_METHOD(Array, rewind)
1311 {
1312         zval *object = getThis();
1313         spl_array_object *intern = Z_SPLARRAY_P(object);
1314 
1315         if (zend_parse_parameters_none() == FAILURE) {
1316                 return;
1317         }
1318 
1319         spl_array_rewind(intern);
1320 }
1321 /* }}} */
1322 
1323 /* {{{ proto void ArrayIterator::seek(int $position)
1324    Seek to position. */
1325 SPL_METHOD(Array, seek)
1326 {
1327         zend_long opos, position;
1328         zval *object = getThis();
1329         spl_array_object *intern = Z_SPLARRAY_P(object);
1330         HashTable *aht = spl_array_get_hash_table(intern);
1331         int result;
1332 
1333         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &position) == FAILURE) {
1334                 return;
1335         }
1336 
1337         if (!aht) {
1338                 php_error_docref(NULL, E_NOTICE, "Array was modified outside object and is no longer an array");
1339                 return;
1340         }
1341 
1342         opos = position;
1343 
1344         if (position >= 0) { /* negative values are not supported */
1345                 spl_array_rewind(intern);
1346                 result = SUCCESS;
1347 
1348                 while (position-- > 0 && (result = spl_array_next(intern)) == SUCCESS);
1349 
1350                 if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS) {
1351                         return; /* ok */
1352                 }
1353         }
1354         zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position %pd is out of range", opos);
1355 } /* }}} */
1356 
1357 int static spl_array_object_count_elements_helper(spl_array_object *intern, zend_long *count) /* {{{ */
1358 {
1359         HashTable *aht = spl_array_get_hash_table(intern);
1360         HashPosition pos, *pos_ptr;
1361 
1362         if (!aht) {
1363                 php_error_docref(NULL, E_NOTICE, "Array was modified outside object and is no longer an array");
1364                 *count = 0;
1365                 return FAILURE;
1366         }
1367 
1368         if (spl_array_is_object(intern)) {
1369                 /* We need to store the 'pos' since we'll modify it in the functions
1370                  * we're going to call and which do not support 'pos' as parameter. */
1371                 pos_ptr = spl_array_get_pos_ptr(aht, intern);
1372                 pos = *pos_ptr;
1373                 *count = 0;
1374                 spl_array_rewind(intern);
1375                 while (*pos_ptr != HT_INVALID_IDX && spl_array_next(intern) == SUCCESS) {
1376                         (*count)++;
1377                 }
1378                 *pos_ptr = pos;
1379                 return SUCCESS;
1380         } else {
1381                 *count = zend_hash_num_elements(aht);
1382                 return SUCCESS;
1383         }
1384 } /* }}} */
1385 
1386 int spl_array_object_count_elements(zval *object, zend_long *count) /* {{{ */
1387 {
1388         spl_array_object *intern = Z_SPLARRAY_P(object);
1389 
1390         if (intern->fptr_count) {
1391                 zval rv;
1392                 zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
1393                 if (Z_TYPE(rv) != IS_UNDEF) {
1394                         *count = zval_get_long(&rv);
1395                         zval_ptr_dtor(&rv);
1396                         return SUCCESS;
1397                 }
1398                 *count = 0;
1399                 return FAILURE;
1400         }
1401         return spl_array_object_count_elements_helper(intern, count);
1402 } /* }}} */
1403 
1404 /* {{{ proto int ArrayObject::count()
1405        proto int ArrayIterator::count()
1406    Return the number of elements in the Iterator. */
1407 SPL_METHOD(Array, count)
1408 {
1409         zend_long count;
1410         spl_array_object *intern = Z_SPLARRAY_P(getThis());
1411 
1412         if (zend_parse_parameters_none() == FAILURE) {
1413                 return;
1414         }
1415 
1416         spl_array_object_count_elements_helper(intern, &count);
1417 
1418         RETURN_LONG(count);
1419 } /* }}} */
1420 
1421 static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) /* {{{ */
1422 {
1423         spl_array_object *intern = Z_SPLARRAY_P(getThis());
1424         HashTable *aht = spl_array_get_hash_table(intern);
1425         zval function_name, params[2], *arg = NULL;
1426         uint32_t old_refcount;
1427 
1428         ZVAL_STRINGL(&function_name, fname, fname_len);
1429 
1430         /* A tricky way to pass "aht" by reference, reset refcount */
1431         //??? It may be not safe, if user comparison handler accesses "aht"
1432         old_refcount = GC_REFCOUNT(aht);
1433         GC_REFCOUNT(aht) = 1;
1434         ZVAL_NEW_EMPTY_REF(&params[0]);
1435         ZVAL_ARR(Z_REFVAL(params[0]), aht);
1436 
1437         if (!use_arg) {
1438                 intern->nApplyCount++;
1439                 call_user_function_ex(EG(function_table), NULL, &function_name, return_value, 1, params, 1, NULL);
1440                 intern->nApplyCount--;
1441         } else if (use_arg == SPL_ARRAY_METHOD_MAY_USER_ARG) {
1442                 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "|z", &arg) == FAILURE) {
1443                         zend_throw_exception(spl_ce_BadMethodCallException, "Function expects one argument at most", 0);
1444                         goto exit;
1445                 }
1446                 if (arg) {
1447                         ZVAL_COPY_VALUE(&params[1], arg);
1448                 }
1449                 intern->nApplyCount++;
1450                 call_user_function_ex(EG(function_table), NULL, &function_name, return_value, arg ? 2 : 1, params, 1, NULL);
1451                 intern->nApplyCount--;
1452         } else {
1453                 if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1454                         zend_throw_exception(spl_ce_BadMethodCallException, "Function expects exactly one argument", 0);
1455                         goto exit;
1456                 }
1457                 ZVAL_COPY_VALUE(&params[1], arg);
1458                 intern->nApplyCount++;
1459                 call_user_function_ex(EG(function_table), NULL, &function_name, return_value, 2, params, 1, NULL);
1460                 intern->nApplyCount--;
1461         }
1462 
1463 exit:
1464         /* A tricky way to pass "aht" by reference, copy back and cleanup */
1465         GC_REFCOUNT(aht) = old_refcount;
1466         efree(Z_REF(params[0]));
1467         zend_string_free(Z_STR(function_name));
1468 } /* }}} */
1469 
1470 #define SPL_ARRAY_METHOD(cname, fname, use_arg) \
1471 SPL_METHOD(cname, fname) \
1472 { \
1473         spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
1474 }
1475 
1476 /* {{{ proto int ArrayObject::asort([int $sort_flags = SORT_REGULAR ])
1477        proto int ArrayIterator::asort([int $sort_flags = SORT_REGULAR ])
1478    Sort the entries by values. */
1479 SPL_ARRAY_METHOD(Array, asort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1480 
1481 /* {{{ proto int ArrayObject::ksort([int $sort_flags = SORT_REGULAR ])
1482        proto int ArrayIterator::ksort([int $sort_flags = SORT_REGULAR ])
1483    Sort the entries by key. */
1484 SPL_ARRAY_METHOD(Array, ksort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1485 
1486 /* {{{ proto int ArrayObject::uasort(callback cmp_function)
1487        proto int ArrayIterator::uasort(callback cmp_function)
1488    Sort the entries by values user defined function. */
1489 SPL_ARRAY_METHOD(Array, uasort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1490 
1491 /* {{{ proto int ArrayObject::uksort(callback cmp_function)
1492        proto int ArrayIterator::uksort(callback cmp_function)
1493    Sort the entries by key using user defined function. */
1494 SPL_ARRAY_METHOD(Array, uksort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1495 
1496 /* {{{ proto int ArrayObject::natsort()
1497        proto int ArrayIterator::natsort()
1498    Sort the entries by values using "natural order" algorithm. */
1499 SPL_ARRAY_METHOD(Array, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1500 
1501 /* {{{ proto int ArrayObject::natcasesort()
1502        proto int ArrayIterator::natcasesort()
1503    Sort the entries by key using case insensitive "natural order" algorithm. */
1504 SPL_ARRAY_METHOD(Array, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1505 
1506 /* {{{ proto mixed|NULL ArrayIterator::current()
1507    Return current array entry */
1508 SPL_METHOD(Array, current)
1509 {
1510         zval *object = getThis();
1511         spl_array_object *intern = Z_SPLARRAY_P(object);
1512         zval *entry;
1513         HashTable *aht = spl_array_get_hash_table(intern);
1514 
1515         if (zend_parse_parameters_none() == FAILURE) {
1516                 return;
1517         }
1518 
1519         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1520                 return;
1521         }
1522 
1523         if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1524                 return;
1525         }
1526         if (Z_TYPE_P(entry) == IS_INDIRECT) {
1527                 entry = Z_INDIRECT_P(entry);
1528                 if (Z_TYPE_P(entry) == IS_UNDEF) {
1529                         return;
1530                 }
1531         }
1532         ZVAL_DEREF(entry);
1533         ZVAL_COPY(return_value, entry);
1534 }
1535 /* }}} */
1536 
1537 /* {{{ proto mixed|NULL ArrayIterator::key()
1538    Return current array key */
1539 SPL_METHOD(Array, key)
1540 {
1541         if (zend_parse_parameters_none() == FAILURE) {
1542                 return;
1543         }
1544 
1545         spl_array_iterator_key(getThis(), return_value);
1546 } /* }}} */
1547 
1548 void spl_array_iterator_key(zval *object, zval *return_value) /* {{{ */
1549 {
1550         spl_array_object *intern = Z_SPLARRAY_P(object);
1551         HashTable *aht = spl_array_get_hash_table(intern);
1552 
1553         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1554                 return;
1555         }
1556 
1557         zend_hash_get_current_key_zval_ex(aht, return_value, spl_array_get_pos_ptr(aht, intern));
1558 }
1559 /* }}} */
1560 
1561 /* {{{ proto void ArrayIterator::next()
1562    Move to next entry */
1563 SPL_METHOD(Array, next)
1564 {
1565         zval *object = getThis();
1566         spl_array_object *intern = Z_SPLARRAY_P(object);
1567         HashTable *aht = spl_array_get_hash_table(intern);
1568 
1569         if (zend_parse_parameters_none() == FAILURE) {
1570                 return;
1571         }
1572 
1573         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1574                 return;
1575         }
1576 
1577         spl_array_next_ex(intern, aht);
1578 }
1579 /* }}} */
1580 
1581 /* {{{ proto bool ArrayIterator::valid()
1582    Check whether array contains more entries */
1583 SPL_METHOD(Array, valid)
1584 {
1585         zval *object = getThis();
1586         spl_array_object *intern = Z_SPLARRAY_P(object);
1587         HashTable *aht = spl_array_get_hash_table(intern);
1588 
1589         if (zend_parse_parameters_none() == FAILURE) {
1590                 return;
1591         }
1592 
1593         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1594                 RETURN_FALSE;
1595         } else {
1596                 RETURN_BOOL(zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS);
1597         }
1598 }
1599 /* }}} */
1600 
1601 /* {{{ proto bool RecursiveArrayIterator::hasChildren()
1602    Check whether current element has children (e.g. is an array) */
1603 SPL_METHOD(Array, hasChildren)
1604 {
1605         zval *object = getThis(), *entry;
1606         spl_array_object *intern = Z_SPLARRAY_P(object);
1607         HashTable *aht = spl_array_get_hash_table(intern);
1608 
1609         if (zend_parse_parameters_none() == FAILURE) {
1610                 return;
1611         }
1612 
1613         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1614                 RETURN_FALSE;
1615         }
1616 
1617         if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1618                 RETURN_FALSE;
1619         }
1620 
1621         RETURN_BOOL(Z_TYPE_P(entry) == IS_ARRAY || (Z_TYPE_P(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
1622 }
1623 /* }}} */
1624 
1625 /* {{{ proto object RecursiveArrayIterator::getChildren()
1626    Create a sub iterator for the current element (same class as $this) */
1627 SPL_METHOD(Array, getChildren)
1628 {
1629         zval *object = getThis(), *entry, flags;
1630         spl_array_object *intern = Z_SPLARRAY_P(object);
1631         HashTable *aht = spl_array_get_hash_table(intern);
1632 
1633         if (zend_parse_parameters_none() == FAILURE) {
1634                 return;
1635         }
1636 
1637         if (spl_array_object_verify_pos(intern, aht) == FAILURE) {
1638                 return;
1639         }
1640 
1641         if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1642                 return;
1643         }
1644 
1645         if (Z_TYPE_P(entry) == IS_OBJECT) {
1646                 if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
1647                         return;
1648                 }
1649                 if (instanceof_function(Z_OBJCE_P(entry), Z_OBJCE_P(getThis()))) {
1650                         ZVAL_OBJ(return_value, Z_OBJ_P(entry));
1651                         Z_ADDREF_P(return_value);
1652                         return;
1653                 }
1654         }
1655 
1656         ZVAL_LONG(&flags, intern->ar_flags);
1657         spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), return_value, entry, &flags);
1658 }
1659 /* }}} */
1660 
1661 /* {{{ proto string ArrayObject::serialize()
1662    Serialize the object */
1663 SPL_METHOD(Array, serialize)
1664 {
1665         zval *object = getThis();
1666         spl_array_object *intern = Z_SPLARRAY_P(object);
1667         HashTable *aht = spl_array_get_hash_table(intern);
1668         zval members, flags;
1669         php_serialize_data_t var_hash;
1670         smart_str buf = {0};
1671 
1672         if (zend_parse_parameters_none() == FAILURE) {
1673                 return;
1674         }
1675 
1676         if (!aht) {
1677                 php_error_docref(NULL, E_NOTICE, "Array was modified outside object and is no longer an array");
1678                 return;
1679         }
1680 
1681         PHP_VAR_SERIALIZE_INIT(var_hash);
1682 
1683         ZVAL_LONG(&flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1684 
1685         /* storage */
1686         smart_str_appendl(&buf, "x:", 2);
1687         php_var_serialize(&buf, &flags, &var_hash);
1688 
1689         if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1690                 php_var_serialize(&buf, &intern->array, &var_hash);
1691                 smart_str_appendc(&buf, ';');
1692         }
1693 
1694         /* members */
1695         smart_str_appendl(&buf, "m:", 2);
1696         if (!intern->std.properties) {
1697                 rebuild_object_properties(&intern->std);
1698         }
1699 
1700         ZVAL_ARR(&members, intern->std.properties);
1701 
1702         php_var_serialize(&buf, &members, &var_hash); /* finishes the string */
1703 
1704         /* done */
1705         PHP_VAR_SERIALIZE_DESTROY(var_hash);
1706 
1707         if (buf.s) {
1708                 RETURN_NEW_STR(buf.s);
1709         }
1710 
1711         RETURN_NULL();
1712 } /* }}} */
1713 
1714 /* {{{ proto void ArrayObject::unserialize(string serialized)
1715  * unserialize the object
1716  */
1717 SPL_METHOD(Array, unserialize)
1718 {
1719         spl_array_object *intern = Z_SPLARRAY_P(getThis());
1720 
1721         char *buf;
1722         size_t buf_len;
1723         const unsigned char *p, *s;
1724         php_unserialize_data_t var_hash;
1725         zval *members, *zflags;
1726         zend_long flags;
1727 
1728         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) {
1729                 return;
1730         }
1731 
1732         if (buf_len == 0) {
1733                 return;
1734         }
1735 
1736         if (intern->nApplyCount > 0) {
1737                 zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
1738                 return;
1739         }
1740 
1741         /* storage */
1742         s = p = (const unsigned char*)buf;
1743         PHP_VAR_UNSERIALIZE_INIT(var_hash);
1744 
1745         if (*p!= 'x' || *++p != ':') {
1746                 goto outexcept;
1747         }
1748         ++p;
1749 
1750         zflags = var_tmp_var(&var_hash);
1751         if (!php_var_unserialize(zflags, &p, s + buf_len, &var_hash) || Z_TYPE_P(zflags) != IS_LONG) {
1752                 goto outexcept;
1753         }
1754 
1755         --p; /* for ';' */
1756         flags = Z_LVAL_P(zflags);
1757         /* flags needs to be verified and we also need to verify whether the next
1758          * thing we get is ';'. After that we require an 'm' or something else
1759          * where 'm' stands for members and anything else should be an array. If
1760          * neither 'a' or 'm' follows we have an error. */
1761 
1762         if (*p != ';') {
1763                 goto outexcept;
1764         }
1765         ++p;
1766 
1767         if (*p!='m') {
1768                 if (*p!='a' && *p!='O' && *p!='C' && *p!='r') {
1769                         goto outexcept;
1770                 }
1771                 intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1772                 intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1773                 zval_ptr_dtor(&intern->array);
1774                 ZVAL_UNDEF(&intern->array);
1775                 if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash)) {
1776                         goto outexcept;
1777                 }
1778                 var_push_dtor(&var_hash, &intern->array);
1779         }
1780         if (*p != ';') {
1781                 goto outexcept;
1782         }
1783         ++p;
1784 
1785         /* members */
1786         if (*p!= 'm' || *++p != ':') {
1787                 goto outexcept;
1788         }
1789         ++p;
1790 
1791         members = var_tmp_var(&var_hash);
1792         if (!php_var_unserialize(members, &p, s + buf_len, &var_hash) || Z_TYPE_P(members) != IS_ARRAY) {
1793                 goto outexcept;
1794         }
1795 
1796         /* copy members */
1797         object_properties_load(&intern->std, Z_ARRVAL_P(members));
1798 
1799         /* done reading $serialized */
1800         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1801         return;
1802 
1803 outexcept:
1804         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1805         zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %pd of %d bytes", (zend_long)((char*)p - buf), buf_len);
1806         return;
1807 
1808 } /* }}} */
1809 
1810 /* {{{ arginfo and function table */
1811 ZEND_BEGIN_ARG_INFO_EX(arginfo_array___construct, 0, 0, 0)
1812         ZEND_ARG_INFO(0, array)
1813         ZEND_ARG_INFO(0, ar_flags)
1814         ZEND_ARG_INFO(0, iterator_class)
1815 ZEND_END_ARG_INFO()
1816 
1817 ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
1818         ZEND_ARG_INFO(0, index)
1819 ZEND_END_ARG_INFO()
1820 
1821 ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
1822         ZEND_ARG_INFO(0, index)
1823         ZEND_ARG_INFO(0, newval)
1824 ZEND_END_ARG_INFO()
1825 
1826 ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
1827         ZEND_ARG_INFO(0, value)
1828 ZEND_END_ARG_INFO()
1829 
1830 ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
1831         ZEND_ARG_INFO(0, position)
1832 ZEND_END_ARG_INFO()
1833 
1834 ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
1835         ZEND_ARG_INFO(0, array)
1836 ZEND_END_ARG_INFO()
1837 
1838 ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
1839         ZEND_ARG_INFO(0, flags)
1840 ZEND_END_ARG_INFO()
1841 
1842 ZEND_BEGIN_ARG_INFO(arginfo_array_setIteratorClass, 0)
1843         ZEND_ARG_INFO(0, iteratorClass)
1844 ZEND_END_ARG_INFO()
1845 
1846 ZEND_BEGIN_ARG_INFO(arginfo_array_uXsort, 0)
1847         ZEND_ARG_INFO(0, cmp_function)
1848 ZEND_END_ARG_INFO();
1849 
1850 ZEND_BEGIN_ARG_INFO(arginfo_array_unserialize, 0)
1851         ZEND_ARG_INFO(0, serialized)
1852 ZEND_END_ARG_INFO();
1853 
1854 ZEND_BEGIN_ARG_INFO(arginfo_array_void, 0)
1855 ZEND_END_ARG_INFO()
1856 
1857 static const zend_function_entry spl_funcs_ArrayObject[] = {
1858         SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1859         SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1860         SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1861         SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1862         SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1863         SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1864         SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1865         SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1866         SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1867         SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1868         SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1869         SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1870         SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1871         SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1872         SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1873         SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1874         SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1875         SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1876         /* ArrayObject specific */
1877         SPL_ME(Array, getIterator,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1878         SPL_ME(Array, exchangeArray,    arginfo_array_exchangeArray,    ZEND_ACC_PUBLIC)
1879         SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC)
1880         SPL_ME(Array, getIteratorClass, arginfo_array_void,             ZEND_ACC_PUBLIC)
1881         PHP_FE_END
1882 };
1883 
1884 static const zend_function_entry spl_funcs_ArrayIterator[] = {
1885         SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1886         SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1887         SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1888         SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1889         SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1890         SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1891         SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1892         SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1893         SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1894         SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1895         SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1896         SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1897         SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1898         SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1899         SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1900         SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1901         SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1902         SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1903         /* ArrayIterator specific */
1904         SPL_ME(Array, rewind,           arginfo_array_void,             ZEND_ACC_PUBLIC)
1905         SPL_ME(Array, current,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1906         SPL_ME(Array, key,              arginfo_array_void,             ZEND_ACC_PUBLIC)
1907         SPL_ME(Array, next,             arginfo_array_void,             ZEND_ACC_PUBLIC)
1908         SPL_ME(Array, valid,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1909         SPL_ME(Array, seek,             arginfo_array_seek,             ZEND_ACC_PUBLIC)
1910         PHP_FE_END
1911 };
1912 
1913 static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = {
1914         SPL_ME(Array, hasChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1915         SPL_ME(Array, getChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1916         PHP_FE_END
1917 };
1918 /* }}} */
1919 
1920 /* {{{ PHP_MINIT_FUNCTION(spl_array) */
1921 PHP_MINIT_FUNCTION(spl_array)
1922 {
1923         REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, spl_funcs_ArrayObject);
1924         REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
1925         REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
1926         REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
1927         REGISTER_SPL_IMPLEMENTS(ArrayObject, Countable);
1928         memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1929 
1930         spl_handler_ArrayObject.offset = XtOffsetOf(spl_array_object, std);
1931 
1932         spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
1933         spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
1934         spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
1935         spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
1936         spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
1937         spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
1938 
1939         spl_handler_ArrayObject.get_properties = spl_array_get_properties;
1940         spl_handler_ArrayObject.get_debug_info = spl_array_get_debug_info;
1941         spl_handler_ArrayObject.get_gc = spl_array_get_gc;
1942         spl_handler_ArrayObject.read_property = spl_array_read_property;
1943         spl_handler_ArrayObject.write_property = spl_array_write_property;
1944         spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
1945         spl_handler_ArrayObject.has_property = spl_array_has_property;
1946         spl_handler_ArrayObject.unset_property = spl_array_unset_property;
1947 
1948         spl_handler_ArrayObject.compare_objects = spl_array_compare_objects;
1949         spl_handler_ArrayObject.dtor_obj = zend_objects_destroy_object;
1950         spl_handler_ArrayObject.free_obj = spl_array_object_free_storage;
1951 
1952         REGISTER_SPL_STD_CLASS_EX(ArrayIterator, spl_array_object_new, spl_funcs_ArrayIterator);
1953         REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator);
1954         REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
1955         REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
1956         REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
1957         REGISTER_SPL_IMPLEMENTS(ArrayIterator, Countable);
1958         memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
1959         spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
1960 
1961         REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, spl_funcs_RecursiveArrayIterator);
1962         REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
1963         spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
1964 
1965         REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1966         REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1967 
1968         REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1969         REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1970 
1971         REGISTER_SPL_CLASS_CONST_LONG(RecursiveArrayIterator, "CHILD_ARRAYS_ONLY", SPL_ARRAY_CHILD_ARRAYS_ONLY);
1972 
1973         return SUCCESS;
1974 }
1975 /* }}} */
1976 
1977 /*
1978  * Local variables:
1979  * tab-width: 4
1980  * c-basic-offset: 4
1981  * End:
1982  * vim600: fdm=marker
1983  * vim: noet sw=4 ts=4
1984  */

/* [<][>][^][v][top][bottom][index][help] */