xref: /PHP-8.0/ext/spl/spl_array.c (revision 1762a879)
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(&params[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(&params[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(&params[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(&params[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