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 if (intern->ht_iter != (uint32_t)-1) {
1133 zend_hash_iterator_del(intern->ht_iter);
1134 intern->ht_iter = (uint32_t)-1;
1135 }
1136 }
1137 /* }}} */
1138
1139 /* iterator handler table */
1140 static const zend_object_iterator_funcs spl_array_it_funcs = {
1141 spl_array_it_dtor,
1142 spl_array_it_valid,
1143 spl_array_it_get_current_data,
1144 spl_array_it_get_current_key,
1145 spl_array_it_move_forward,
1146 spl_array_it_rewind,
1147 NULL,
1148 spl_array_it_get_gc,
1149 };
1150
spl_array_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1151 zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1152 {
1153 zend_user_iterator *iterator;
1154 spl_array_object *array_object = Z_SPLARRAY_P(object);
1155
1156 if (by_ref && (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)) {
1157 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1158 return NULL;
1159 }
1160
1161 iterator = emalloc(sizeof(zend_user_iterator));
1162
1163 zend_iterator_init(&iterator->it);
1164
1165 ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
1166 iterator->it.funcs = &spl_array_it_funcs;
1167 iterator->ce = ce;
1168 ZVAL_UNDEF(&iterator->value);
1169
1170 return &iterator->it;
1171 }
1172 /* }}} */
1173
1174 /* {{{ Constructs a new array object from an array or object. */
PHP_METHOD(ArrayObject,__construct)1175 PHP_METHOD(ArrayObject, __construct)
1176 {
1177 zval *object = ZEND_THIS;
1178 spl_array_object *intern;
1179 zval *array;
1180 zend_long ar_flags = 0;
1181 zend_class_entry *ce_get_iterator = spl_ce_ArrayIterator;
1182
1183 if (ZEND_NUM_ARGS() == 0) {
1184 return; /* nothing to do */
1185 }
1186
1187 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|AlC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
1188 RETURN_THROWS();
1189 }
1190
1191 intern = Z_SPLARRAY_P(object);
1192
1193 if (ZEND_NUM_ARGS() > 2) {
1194 intern->ce_get_iterator = ce_get_iterator;
1195 }
1196
1197 ar_flags &= ~SPL_ARRAY_INT_MASK;
1198
1199 spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1);
1200 }
1201 /* }}} */
1202
1203 /* {{{ Constructs a new array iterator from an array or object. */
PHP_METHOD(ArrayIterator,__construct)1204 PHP_METHOD(ArrayIterator, __construct)
1205 {
1206 zval *object = ZEND_THIS;
1207 spl_array_object *intern;
1208 zval *array;
1209 zend_long ar_flags = 0;
1210
1211 if (ZEND_NUM_ARGS() == 0) {
1212 return; /* nothing to do */
1213 }
1214
1215 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Al", &array, &ar_flags) == FAILURE) {
1216 RETURN_THROWS();
1217 }
1218
1219 intern = Z_SPLARRAY_P(object);
1220
1221 ar_flags &= ~SPL_ARRAY_INT_MASK;
1222
1223 spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1);
1224 }
1225 /* }}} */
1226
1227 /* {{{ Set the class used in getIterator. */
PHP_METHOD(ArrayObject,setIteratorClass)1228 PHP_METHOD(ArrayObject, setIteratorClass)
1229 {
1230 zval *object = ZEND_THIS;
1231 spl_array_object *intern = Z_SPLARRAY_P(object);
1232 zend_class_entry *ce_get_iterator = spl_ce_ArrayIterator;
1233
1234 ZEND_PARSE_PARAMETERS_START(1, 1)
1235 Z_PARAM_CLASS(ce_get_iterator)
1236 ZEND_PARSE_PARAMETERS_END();
1237
1238 intern->ce_get_iterator = ce_get_iterator;
1239 }
1240 /* }}} */
1241
1242 /* {{{ Get the class used in getIterator. */
PHP_METHOD(ArrayObject,getIteratorClass)1243 PHP_METHOD(ArrayObject, getIteratorClass)
1244 {
1245 zval *object = ZEND_THIS;
1246 spl_array_object *intern = Z_SPLARRAY_P(object);
1247
1248 if (zend_parse_parameters_none() == FAILURE) {
1249 RETURN_THROWS();
1250 }
1251
1252 zend_string_addref(intern->ce_get_iterator->name);
1253 RETURN_STR(intern->ce_get_iterator->name);
1254 }
1255 /* }}} */
1256
1257 /* {{{ Get flags */
PHP_METHOD(ArrayObject,getFlags)1258 PHP_METHOD(ArrayObject, getFlags)
1259 {
1260 zval *object = ZEND_THIS;
1261 spl_array_object *intern = Z_SPLARRAY_P(object);
1262
1263 if (zend_parse_parameters_none() == FAILURE) {
1264 RETURN_THROWS();
1265 }
1266
1267 RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
1268 }
1269 /* }}} */
1270
1271 /* {{{ Set flags */
PHP_METHOD(ArrayObject,setFlags)1272 PHP_METHOD(ArrayObject, setFlags)
1273 {
1274 zval *object = ZEND_THIS;
1275 spl_array_object *intern = Z_SPLARRAY_P(object);
1276 zend_long ar_flags = 0;
1277
1278 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ar_flags) == FAILURE) {
1279 RETURN_THROWS();
1280 }
1281
1282 intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
1283 }
1284 /* }}} */
1285
1286 /* {{{ 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)1287 PHP_METHOD(ArrayObject, exchangeArray)
1288 {
1289 zval *object = ZEND_THIS, *array;
1290 spl_array_object *intern = Z_SPLARRAY_P(object);
1291
1292 if (zend_parse_parameters(ZEND_NUM_ARGS(), "A", &array) == FAILURE) {
1293 RETURN_THROWS();
1294 }
1295
1296 if (intern->nApplyCount > 0) {
1297 zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
1298 RETURN_THROWS();
1299 }
1300
1301 RETVAL_ARR(zend_array_dup(spl_array_get_hash_table(intern)));
1302 spl_array_set_array(object, intern, array, 0L, 1);
1303 }
1304 /* }}} */
1305
1306 /* {{{ Create a new iterator from a ArrayObject instance */
PHP_METHOD(ArrayObject,getIterator)1307 PHP_METHOD(ArrayObject, getIterator)
1308 {
1309 zval *object = ZEND_THIS;
1310 spl_array_object *intern = Z_SPLARRAY_P(object);
1311
1312 if (zend_parse_parameters_none() == FAILURE) {
1313 RETURN_THROWS();
1314 }
1315
1316 RETURN_OBJ(spl_array_object_new_ex(intern->ce_get_iterator, Z_OBJ_P(object), 0));
1317 }
1318 /* }}} */
1319
1320 /* {{{ Rewind array back to the start */
PHP_METHOD(ArrayIterator,rewind)1321 PHP_METHOD(ArrayIterator, rewind)
1322 {
1323 zval *object = ZEND_THIS;
1324 spl_array_object *intern = Z_SPLARRAY_P(object);
1325
1326 if (zend_parse_parameters_none() == FAILURE) {
1327 RETURN_THROWS();
1328 }
1329
1330 spl_array_rewind(intern);
1331 }
1332 /* }}} */
1333
1334 /* {{{ Seek to position. */
PHP_METHOD(ArrayIterator,seek)1335 PHP_METHOD(ArrayIterator, seek)
1336 {
1337 zend_long opos, position;
1338 zval *object = ZEND_THIS;
1339 spl_array_object *intern = Z_SPLARRAY_P(object);
1340 HashTable *aht = spl_array_get_hash_table(intern);
1341 int result;
1342
1343 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &position) == FAILURE) {
1344 RETURN_THROWS();
1345 }
1346
1347 opos = position;
1348
1349 if (position >= 0) { /* negative values are not supported */
1350 spl_array_rewind(intern);
1351 result = SUCCESS;
1352
1353 while (position-- > 0 && (result = spl_array_next(intern)) == SUCCESS);
1354
1355 if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS) {
1356 return; /* ok */
1357 }
1358 }
1359 zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", opos);
1360 } /* }}} */
1361
spl_array_object_count_elements_helper(spl_array_object * intern)1362 static zend_long spl_array_object_count_elements_helper(spl_array_object *intern) /* {{{ */
1363 {
1364 HashTable *aht = spl_array_get_hash_table(intern);
1365 if (spl_array_is_object(intern)) {
1366 zend_long count = 0;
1367 zend_string *key;
1368 zval *val;
1369 /* Count public/dynamic properties */
1370 ZEND_HASH_FOREACH_STR_KEY_VAL(aht, key, val) {
1371 if (Z_TYPE_P(val) == IS_INDIRECT) {
1372 if (Z_TYPE_P(Z_INDIRECT_P(val)) == IS_UNDEF) continue;
1373 if (key && ZSTR_VAL(key)[0] == '\0') continue;
1374 }
1375 count++;
1376 } ZEND_HASH_FOREACH_END();
1377 return count;
1378 } else {
1379 return zend_hash_num_elements(aht);
1380 }
1381 } /* }}} */
1382
spl_array_object_count_elements(zend_object * object,zend_long * count)1383 int spl_array_object_count_elements(zend_object *object, zend_long *count) /* {{{ */
1384 {
1385 spl_array_object *intern = spl_array_from_obj(object);
1386
1387 if (intern->fptr_count) {
1388 zval rv;
1389 zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
1390 if (Z_TYPE(rv) != IS_UNDEF) {
1391 *count = zval_get_long(&rv);
1392 zval_ptr_dtor(&rv);
1393 return SUCCESS;
1394 }
1395 *count = 0;
1396 return FAILURE;
1397 }
1398 *count = spl_array_object_count_elements_helper(intern);
1399 return SUCCESS;
1400 } /* }}} */
1401
1402 /* {{{ Return the number of elements in the Iterator. */
PHP_METHOD(ArrayObject,count)1403 PHP_METHOD(ArrayObject, count)
1404 {
1405 spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1406
1407 if (zend_parse_parameters_none() == FAILURE) {
1408 RETURN_THROWS();
1409 }
1410
1411 RETURN_LONG(spl_array_object_count_elements_helper(intern));
1412 } /* }}} */
1413
spl_array_method(INTERNAL_FUNCTION_PARAMETERS,char * fname,int fname_len,int use_arg)1414 static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) /* {{{ */
1415 {
1416 spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1417 HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
1418 HashTable *aht = *ht_ptr;
1419 zval function_name, params[2], *arg = NULL;
1420
1421 ZVAL_STRINGL(&function_name, fname, fname_len);
1422
1423 ZVAL_NEW_EMPTY_REF(¶ms[0]);
1424 ZVAL_ARR(Z_REFVAL(params[0]), aht);
1425 GC_ADDREF(aht);
1426
1427 if (!use_arg) {
1428 if (zend_parse_parameters_none() == FAILURE) {
1429 goto exit;
1430 }
1431
1432 intern->nApplyCount++;
1433 call_user_function(EG(function_table), NULL, &function_name, return_value, 1, params);
1434 intern->nApplyCount--;
1435 } else if (use_arg == SPL_ARRAY_METHOD_SORT_FLAGS_ARG) {
1436 zend_long sort_flags = 0;
1437 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) {
1438 goto exit;
1439 }
1440 ZVAL_LONG(¶ms[1], sort_flags);
1441 intern->nApplyCount++;
1442 call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params);
1443 intern->nApplyCount--;
1444 } else {
1445 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1446 goto exit;
1447 }
1448 ZVAL_COPY_VALUE(¶ms[1], arg);
1449 intern->nApplyCount++;
1450 call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params);
1451 intern->nApplyCount--;
1452 }
1453
1454 exit:
1455 {
1456 zval *ht_zv = Z_REFVAL(params[0]);
1457 zend_array_release(*ht_ptr);
1458 SEPARATE_ARRAY(ht_zv);
1459 *ht_ptr = Z_ARRVAL_P(ht_zv);
1460 ZVAL_NULL(ht_zv);
1461 zval_ptr_dtor(¶ms[0]);
1462 zend_string_free(Z_STR(function_name));
1463 }
1464 } /* }}} */
1465
1466 #define SPL_ARRAY_METHOD(cname, fname, use_arg) \
1467 PHP_METHOD(cname, fname) \
1468 { \
1469 spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
1470 }
1471
1472 /* {{{ Sort the entries by values. */
SPL_ARRAY_METHOD(ArrayObject,asort,SPL_ARRAY_METHOD_SORT_FLAGS_ARG)1473 SPL_ARRAY_METHOD(ArrayObject, asort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */
1474
1475 /* {{{ Sort the entries by key. */
1476 SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */
1477
1478 /* {{{ Sort the entries by values user defined function. */
1479 SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */
1480
1481 /* {{{ Sort the entries by key using user defined function. */
1482 SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */
1483
1484 /* {{{ Sort the entries by values using "natural order" algorithm. */
1485 SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1486
1487 /* {{{ Sort the entries by key using case insensitive "natural order" algorithm. */
1488 SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1489
1490 /* {{{ Return current array entry */
1491 PHP_METHOD(ArrayIterator, current)
1492 {
1493 zval *object = ZEND_THIS;
1494 spl_array_object *intern = Z_SPLARRAY_P(object);
1495 zval *entry;
1496 HashTable *aht = spl_array_get_hash_table(intern);
1497
1498 if (zend_parse_parameters_none() == FAILURE) {
1499 RETURN_THROWS();
1500 }
1501
1502 if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1503 return;
1504 }
1505 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1506 entry = Z_INDIRECT_P(entry);
1507 if (Z_TYPE_P(entry) == IS_UNDEF) {
1508 return;
1509 }
1510 }
1511 ZVAL_COPY_DEREF(return_value, entry);
1512 }
1513 /* }}} */
1514
1515 /* {{{ Return current array key */
PHP_METHOD(ArrayIterator,key)1516 PHP_METHOD(ArrayIterator, key)
1517 {
1518 if (zend_parse_parameters_none() == FAILURE) {
1519 RETURN_THROWS();
1520 }
1521
1522 spl_array_iterator_key(ZEND_THIS, return_value);
1523 } /* }}} */
1524
spl_array_iterator_key(zval * object,zval * return_value)1525 void spl_array_iterator_key(zval *object, zval *return_value) /* {{{ */
1526 {
1527 spl_array_object *intern = Z_SPLARRAY_P(object);
1528 HashTable *aht = spl_array_get_hash_table(intern);
1529
1530 zend_hash_get_current_key_zval_ex(aht, return_value, spl_array_get_pos_ptr(aht, intern));
1531 }
1532 /* }}} */
1533
1534 /* {{{ Move to next entry */
PHP_METHOD(ArrayIterator,next)1535 PHP_METHOD(ArrayIterator, next)
1536 {
1537 zval *object = ZEND_THIS;
1538 spl_array_object *intern = Z_SPLARRAY_P(object);
1539 HashTable *aht = spl_array_get_hash_table(intern);
1540
1541 if (zend_parse_parameters_none() == FAILURE) {
1542 RETURN_THROWS();
1543 }
1544
1545 spl_array_next_ex(intern, aht);
1546 }
1547 /* }}} */
1548
1549 /* {{{ Check whether array contains more entries */
PHP_METHOD(ArrayIterator,valid)1550 PHP_METHOD(ArrayIterator, valid)
1551 {
1552 zval *object = ZEND_THIS;
1553 spl_array_object *intern = Z_SPLARRAY_P(object);
1554 HashTable *aht = spl_array_get_hash_table(intern);
1555
1556 if (zend_parse_parameters_none() == FAILURE) {
1557 RETURN_THROWS();
1558 }
1559
1560 RETURN_BOOL(zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS);
1561 }
1562 /* }}} */
1563
1564 /* {{{ Check whether current element has children (e.g. is an array) */
PHP_METHOD(RecursiveArrayIterator,hasChildren)1565 PHP_METHOD(RecursiveArrayIterator, hasChildren)
1566 {
1567 zval *object = ZEND_THIS, *entry;
1568 spl_array_object *intern = Z_SPLARRAY_P(object);
1569 HashTable *aht = spl_array_get_hash_table(intern);
1570
1571 if (zend_parse_parameters_none() == FAILURE) {
1572 RETURN_THROWS();
1573 }
1574
1575 if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1576 RETURN_FALSE;
1577 }
1578
1579 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1580 entry = Z_INDIRECT_P(entry);
1581 }
1582
1583 ZVAL_DEREF(entry);
1584 RETURN_BOOL(Z_TYPE_P(entry) == IS_ARRAY || (Z_TYPE_P(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
1585 }
1586 /* }}} */
1587
1588 /* {{{ Create a sub iterator for the current element (same class as $this) */
PHP_METHOD(RecursiveArrayIterator,getChildren)1589 PHP_METHOD(RecursiveArrayIterator, getChildren)
1590 {
1591 zval *object = ZEND_THIS, *entry, flags;
1592 spl_array_object *intern = Z_SPLARRAY_P(object);
1593 HashTable *aht = spl_array_get_hash_table(intern);
1594
1595 if (zend_parse_parameters_none() == FAILURE) {
1596 RETURN_THROWS();
1597 }
1598
1599 if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1600 return;
1601 }
1602
1603 if (Z_TYPE_P(entry) == IS_INDIRECT) {
1604 entry = Z_INDIRECT_P(entry);
1605 }
1606
1607 ZVAL_DEREF(entry);
1608 if (Z_TYPE_P(entry) == IS_OBJECT) {
1609 if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
1610 return;
1611 }
1612 if (instanceof_function(Z_OBJCE_P(entry), Z_OBJCE_P(ZEND_THIS))) {
1613 RETURN_OBJ_COPY(Z_OBJ_P(entry));
1614 }
1615 }
1616
1617 ZVAL_LONG(&flags, intern->ar_flags);
1618 spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, entry, &flags);
1619 }
1620 /* }}} */
1621
1622 /* {{{ Serialize the object */
PHP_METHOD(ArrayObject,serialize)1623 PHP_METHOD(ArrayObject, serialize)
1624 {
1625 zval *object = ZEND_THIS;
1626 spl_array_object *intern = Z_SPLARRAY_P(object);
1627 zval members, flags;
1628 php_serialize_data_t var_hash;
1629 smart_str buf = {0};
1630
1631 if (zend_parse_parameters_none() == FAILURE) {
1632 RETURN_THROWS();
1633 }
1634
1635 PHP_VAR_SERIALIZE_INIT(var_hash);
1636
1637 ZVAL_LONG(&flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1638
1639 /* storage */
1640 smart_str_appendl(&buf, "x:", 2);
1641 php_var_serialize(&buf, &flags, &var_hash);
1642
1643 if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1644 php_var_serialize(&buf, &intern->array, &var_hash);
1645 smart_str_appendc(&buf, ';');
1646 }
1647
1648 /* members */
1649 smart_str_appendl(&buf, "m:", 2);
1650 if (!intern->std.properties) {
1651 rebuild_object_properties(&intern->std);
1652 }
1653
1654 ZVAL_ARR(&members, intern->std.properties);
1655
1656 php_var_serialize(&buf, &members, &var_hash); /* finishes the string */
1657
1658 /* done */
1659 PHP_VAR_SERIALIZE_DESTROY(var_hash);
1660
1661 RETURN_NEW_STR(buf.s);
1662 } /* }}} */
1663
1664 /* {{{ unserialize the object */
PHP_METHOD(ArrayObject,unserialize)1665 PHP_METHOD(ArrayObject, unserialize)
1666 {
1667 zval *object = ZEND_THIS;
1668 spl_array_object *intern = Z_SPLARRAY_P(object);
1669
1670 char *buf;
1671 size_t buf_len;
1672 const unsigned char *p, *s;
1673 php_unserialize_data_t var_hash;
1674 zval *members, *zflags, *array;
1675 zend_long flags;
1676
1677 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) {
1678 RETURN_THROWS();
1679 }
1680
1681 if (buf_len == 0) {
1682 return;
1683 }
1684
1685 if (intern->nApplyCount > 0) {
1686 zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
1687 return;
1688 }
1689
1690 /* storage */
1691 s = p = (const unsigned char*)buf;
1692 PHP_VAR_UNSERIALIZE_INIT(var_hash);
1693
1694 if (*p!= 'x' || *++p != ':') {
1695 goto outexcept;
1696 }
1697 ++p;
1698
1699 zflags = var_tmp_var(&var_hash);
1700 if (!php_var_unserialize(zflags, &p, s + buf_len, &var_hash) || Z_TYPE_P(zflags) != IS_LONG) {
1701 goto outexcept;
1702 }
1703
1704 --p; /* for ';' */
1705 flags = Z_LVAL_P(zflags);
1706 /* flags needs to be verified and we also need to verify whether the next
1707 * thing we get is ';'. After that we require an 'm' or something else
1708 * where 'm' stands for members and anything else should be an array. If
1709 * neither 'a' or 'm' follows we have an error. */
1710
1711 if (*p != ';') {
1712 goto outexcept;
1713 }
1714 ++p;
1715
1716 if (flags & SPL_ARRAY_IS_SELF) {
1717 /* If IS_SELF is used, the flags are not followed by an array/object */
1718 intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1719 intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1720 zval_ptr_dtor(&intern->array);
1721 ZVAL_UNDEF(&intern->array);
1722 } else {
1723 if (*p!='a' && *p!='O' && *p!='C' && *p!='r') {
1724 goto outexcept;
1725 }
1726
1727 array = var_tmp_var(&var_hash);
1728 if (!php_var_unserialize(array, &p, s + buf_len, &var_hash)
1729 || (Z_TYPE_P(array) != IS_ARRAY && Z_TYPE_P(array) != IS_OBJECT)) {
1730 goto outexcept;
1731 }
1732
1733 intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1734 intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1735
1736 if (Z_TYPE_P(array) == IS_ARRAY) {
1737 zval_ptr_dtor(&intern->array);
1738 ZVAL_COPY_VALUE(&intern->array, array);
1739 ZVAL_NULL(array);
1740 SEPARATE_ARRAY(&intern->array);
1741 } else {
1742 spl_array_set_array(object, intern, array, 0L, 1);
1743 }
1744
1745 if (*p != ';') {
1746 goto outexcept;
1747 }
1748 ++p;
1749 }
1750
1751 /* members */
1752 if (*p!= 'm' || *++p != ':') {
1753 goto outexcept;
1754 }
1755 ++p;
1756
1757 members = var_tmp_var(&var_hash);
1758 if (!php_var_unserialize(members, &p, s + buf_len, &var_hash) || Z_TYPE_P(members) != IS_ARRAY) {
1759 goto outexcept;
1760 }
1761
1762 /* copy members */
1763 object_properties_load(&intern->std, Z_ARRVAL_P(members));
1764
1765 /* done reading $serialized */
1766 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1767 return;
1768
1769 outexcept:
1770 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1771 zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset " ZEND_LONG_FMT " of %zd bytes", (zend_long)((char*)p - buf), buf_len);
1772 RETURN_THROWS();
1773
1774 } /* }}} */
1775
1776 /* {{{ */
PHP_METHOD(ArrayObject,__serialize)1777 PHP_METHOD(ArrayObject, __serialize)
1778 {
1779 spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1780 zval tmp;
1781
1782 if (zend_parse_parameters_none() == FAILURE) {
1783 RETURN_THROWS();
1784 }
1785
1786 array_init(return_value);
1787
1788 /* flags */
1789 ZVAL_LONG(&tmp, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1790 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1791
1792 /* storage */
1793 if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
1794 ZVAL_NULL(&tmp);
1795 } else {
1796 ZVAL_COPY(&tmp, &intern->array);
1797 }
1798 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1799
1800 /* members */
1801 ZVAL_ARR(&tmp, zend_std_get_properties(&intern->std));
1802 Z_TRY_ADDREF(tmp);
1803 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1804
1805 /* iterator class */
1806 if (intern->ce_get_iterator == spl_ce_ArrayIterator) {
1807 ZVAL_NULL(&tmp);
1808 } else {
1809 ZVAL_STR_COPY(&tmp, intern->ce_get_iterator->name);
1810 }
1811 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1812 }
1813 /* }}} */
1814
1815
1816 /* {{{ */
PHP_METHOD(ArrayObject,__unserialize)1817 PHP_METHOD(ArrayObject, __unserialize)
1818 {
1819 spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1820 HashTable *data;
1821 zval *flags_zv, *storage_zv, *members_zv, *iterator_class_zv;
1822 zend_long flags;
1823
1824 if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
1825 RETURN_THROWS();
1826 }
1827
1828 flags_zv = zend_hash_index_find(data, 0);
1829 storage_zv = zend_hash_index_find(data, 1);
1830 members_zv = zend_hash_index_find(data, 2);
1831 iterator_class_zv = zend_hash_index_find(data, 3);
1832
1833 if (!flags_zv || !storage_zv || !members_zv ||
1834 Z_TYPE_P(flags_zv) != IS_LONG || Z_TYPE_P(members_zv) != IS_ARRAY ||
1835 (iterator_class_zv && (Z_TYPE_P(iterator_class_zv) != IS_NULL &&
1836 Z_TYPE_P(iterator_class_zv) != IS_STRING))) {
1837 zend_throw_exception(spl_ce_UnexpectedValueException,
1838 "Incomplete or ill-typed serialization data", 0);
1839 RETURN_THROWS();
1840 }
1841
1842 flags = Z_LVAL_P(flags_zv);
1843 intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1844 intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1845
1846 if (flags & SPL_ARRAY_IS_SELF) {
1847 zval_ptr_dtor(&intern->array);
1848 ZVAL_UNDEF(&intern->array);
1849 } else {
1850 spl_array_set_array(ZEND_THIS, intern, storage_zv, 0L, 1);
1851 }
1852
1853 object_properties_load(&intern->std, Z_ARRVAL_P(members_zv));
1854
1855 if (iterator_class_zv && Z_TYPE_P(iterator_class_zv) == IS_STRING) {
1856 zend_class_entry *ce = zend_lookup_class(Z_STR_P(iterator_class_zv));
1857
1858 if (!ce) {
1859 zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1860 "Cannot deserialize ArrayObject with iterator class '%s'; no such class exists",
1861 ZSTR_VAL(Z_STR_P(iterator_class_zv)));
1862 RETURN_THROWS();
1863 }
1864
1865 if (!instanceof_function(ce, spl_ce_Iterator)) {
1866 zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1867 "Cannot deserialize ArrayObject with iterator class '%s'; this class does not implement the Iterator interface",
1868 ZSTR_VAL(Z_STR_P(iterator_class_zv)));
1869 RETURN_THROWS();
1870 }
1871
1872 intern->ce_get_iterator = ce;
1873 }
1874 }
1875 /* }}} */
1876
1877 /* {{{ */
PHP_METHOD(ArrayObject,__debugInfo)1878 PHP_METHOD(ArrayObject, __debugInfo)
1879 {
1880 if (zend_parse_parameters_none() == FAILURE) {
1881 return;
1882 }
1883
1884 RETURN_ARR(spl_array_get_debug_info(Z_OBJ_P(ZEND_THIS)));
1885 } /* }}} */
1886
1887 /* {{{ PHP_MINIT_FUNCTION(spl_array) */
PHP_MINIT_FUNCTION(spl_array)1888 PHP_MINIT_FUNCTION(spl_array)
1889 {
1890 REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, class_ArrayObject_methods);
1891 REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
1892 REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
1893 REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
1894 REGISTER_SPL_IMPLEMENTS(ArrayObject, Countable);
1895 memcpy(&spl_handler_ArrayObject, &std_object_handlers, sizeof(zend_object_handlers));
1896
1897 spl_handler_ArrayObject.offset = XtOffsetOf(spl_array_object, std);
1898
1899 spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
1900 spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
1901 spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
1902 spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
1903 spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
1904 spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
1905
1906 spl_handler_ArrayObject.get_properties_for = spl_array_get_properties_for;
1907 spl_handler_ArrayObject.get_gc = spl_array_get_gc;
1908 spl_handler_ArrayObject.read_property = spl_array_read_property;
1909 spl_handler_ArrayObject.write_property = spl_array_write_property;
1910 spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
1911 spl_handler_ArrayObject.has_property = spl_array_has_property;
1912 spl_handler_ArrayObject.unset_property = spl_array_unset_property;
1913
1914 spl_handler_ArrayObject.compare = spl_array_compare_objects;
1915 spl_handler_ArrayObject.dtor_obj = zend_objects_destroy_object;
1916 spl_handler_ArrayObject.free_obj = spl_array_object_free_storage;
1917
1918 REGISTER_SPL_STD_CLASS_EX(ArrayIterator, spl_array_object_new, class_ArrayIterator_methods);
1919 REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator);
1920 REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
1921 REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
1922 REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
1923 REGISTER_SPL_IMPLEMENTS(ArrayIterator, Countable);
1924 memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
1925 spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
1926 spl_ce_ArrayIterator->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
1927
1928
1929 REGISTER_SPL_CLASS_CONST_LONG(ArrayObject, "STD_PROP_LIST", SPL_ARRAY_STD_PROP_LIST);
1930 REGISTER_SPL_CLASS_CONST_LONG(ArrayObject, "ARRAY_AS_PROPS", SPL_ARRAY_ARRAY_AS_PROPS);
1931
1932 REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "STD_PROP_LIST", SPL_ARRAY_STD_PROP_LIST);
1933 REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "ARRAY_AS_PROPS", SPL_ARRAY_ARRAY_AS_PROPS);
1934
1935 REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, class_RecursiveArrayIterator_methods);
1936 REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
1937 spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
1938 spl_ce_RecursiveArrayIterator->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
1939
1940 REGISTER_SPL_CLASS_CONST_LONG(RecursiveArrayIterator, "CHILD_ARRAYS_ONLY", SPL_ARRAY_CHILD_ARRAYS_ONLY);
1941
1942 return SUCCESS;
1943 }
1944 /* }}} */
1945