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(&params[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(&params[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(&params[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(&params[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