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