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