1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Marcus Boerger <helly@php.net>                              |
16    |          Etienne Kneuss <colder@php.net>                             |
17    +----------------------------------------------------------------------+
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/info.h"
27 #include "ext/standard/php_array.h"
28 #include "ext/standard/php_var.h"
29 #include "zend_smart_str.h"
30 #include "zend_interfaces.h"
31 #include "zend_exceptions.h"
32 
33 #include "php_spl.h"
34 #include "spl_functions.h"
35 #include "spl_engine.h"
36 #include "spl_observer.h"
37 #include "spl_iterators.h"
38 #include "spl_array.h"
39 #include "spl_exceptions.h"
40 
41 SPL_METHOD(SplObserver, update);
42 SPL_METHOD(SplSubject, attach);
43 SPL_METHOD(SplSubject, detach);
44 SPL_METHOD(SplSubject, notify);
45 
46 ZEND_BEGIN_ARG_INFO(arginfo_SplObserver_update, 0)
47 	ZEND_ARG_OBJ_INFO(0, subject, SplSubject, 0)
48 ZEND_END_ARG_INFO();
49 
50 static const zend_function_entry spl_funcs_SplObserver[] = {
51 	SPL_ABSTRACT_ME(SplObserver, update,   arginfo_SplObserver_update)
52 	PHP_FE_END
53 };
54 
55 ZEND_BEGIN_ARG_INFO(arginfo_SplSubject_attach, 0)
56 	ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0)
57 ZEND_END_ARG_INFO();
58 
59 ZEND_BEGIN_ARG_INFO(arginfo_SplSubject_void, 0)
60 ZEND_END_ARG_INFO();
61 
62 /*ZEND_BEGIN_ARG_INFO_EX(arginfo_SplSubject_notify, 0, 0, 1)
63 	ZEND_ARG_OBJ_INFO(0, ignore, SplObserver, 1)
64 ZEND_END_ARG_INFO();*/
65 
66 static const zend_function_entry spl_funcs_SplSubject[] = {
67 	SPL_ABSTRACT_ME(SplSubject,  attach,   arginfo_SplSubject_attach)
68 	SPL_ABSTRACT_ME(SplSubject,  detach,   arginfo_SplSubject_attach)
69 	SPL_ABSTRACT_ME(SplSubject,  notify,   arginfo_SplSubject_void)
70 	PHP_FE_END
71 };
72 
73 PHPAPI zend_class_entry     *spl_ce_SplObserver;
74 PHPAPI zend_class_entry     *spl_ce_SplSubject;
75 PHPAPI zend_class_entry     *spl_ce_SplObjectStorage;
76 PHPAPI zend_class_entry     *spl_ce_MultipleIterator;
77 
78 PHPAPI zend_object_handlers spl_handler_SplObjectStorage;
79 
80 typedef struct _spl_SplObjectStorage { /* {{{ */
81 	HashTable         storage;
82 	zend_long         index;
83 	HashPosition      pos;
84 	zend_long         flags;
85 	zend_function    *fptr_get_hash;
86 	zval             *gcdata;
87 	size_t            gcdata_num;
88 	zend_object       std;
89 } spl_SplObjectStorage; /* }}} */
90 
91 /* {{{ storage is an assoc array of [zend_object*]=>[zval *obj, zval *inf] */
92 typedef struct _spl_SplObjectStorageElement {
93 	zval obj;
94 	zval inf;
95 } spl_SplObjectStorageElement; /* }}} */
96 
spl_object_storage_from_obj(zend_object *obj)97 static inline spl_SplObjectStorage *spl_object_storage_from_obj(zend_object *obj) /* {{{ */ {
98 	return (spl_SplObjectStorage*)((char*)(obj) - XtOffsetOf(spl_SplObjectStorage, std));
99 }
100 /* }}} */
101 
102 #define Z_SPLOBJSTORAGE_P(zv)  spl_object_storage_from_obj(Z_OBJ_P((zv)))
103 
spl_SplObjectStorage_free_storage(zend_object *object)104 void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */
105 {
106 	spl_SplObjectStorage *intern = spl_object_storage_from_obj(object);
107 
108 	zend_object_std_dtor(&intern->std);
109 
110 	zend_hash_destroy(&intern->storage);
111 
112 	if (intern->gcdata != NULL) {
113 		efree(intern->gcdata);
114 	}
115 
116 } /* }}} */
117 
spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zval *this, zval *obj)118 static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zval *this, zval *obj) {
119 	if (intern->fptr_get_hash) {
120 		zval rv;
121 		zend_call_method_with_1_params(this, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, obj);
122 		if (!Z_ISUNDEF(rv)) {
123 			if (Z_TYPE(rv) == IS_STRING) {
124 				key->key = Z_STR(rv);
125 				return SUCCESS;
126 			} else {
127 				zend_throw_exception(spl_ce_RuntimeException, "Hash needs to be a string", 0);
128 
129 				zval_ptr_dtor(&rv);
130 				return FAILURE;
131 			}
132 		} else {
133 			return FAILURE;
134 		}
135 	} else {
136 		key->key = NULL;
137 		key->h = Z_OBJ_HANDLE_P(obj);
138 		return SUCCESS;
139 	}
140 }
141 
spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_hash_key *key)142 static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_hash_key *key) {
143 	if (key->key) {
144 		zend_string_release_ex(key->key, 0);
145 	}
146 }
147 
spl_object_storage_dtor(zval *element)148 static void spl_object_storage_dtor(zval *element) /* {{{ */
149 {
150 	spl_SplObjectStorageElement *el = Z_PTR_P(element);
151 	zval_ptr_dtor(&el->obj);
152 	zval_ptr_dtor(&el->inf);
153 	efree(el);
154 } /* }}} */
155 
spl_object_storage_get(spl_SplObjectStorage *intern, zend_hash_key *key)156 static spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zend_hash_key *key) /* {{{ */
157 {
158 	if (key->key) {
159 		return zend_hash_find_ptr(&intern->storage, key->key);
160 	} else {
161 		return zend_hash_index_find_ptr(&intern->storage, key->h);
162 	}
163 } /* }}} */
164 
spl_object_storage_attach(spl_SplObjectStorage *intern, zval *this, zval *obj, zval *inf)165 spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zval *this, zval *obj, zval *inf) /* {{{ */
166 {
167 	spl_SplObjectStorageElement *pelement, element;
168 	zend_hash_key key;
169 	if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
170 		return NULL;
171 	}
172 
173 	pelement = spl_object_storage_get(intern, &key);
174 
175 	if (pelement) {
176 		zval_ptr_dtor(&pelement->inf);
177 		if (inf) {
178 			ZVAL_COPY(&pelement->inf, inf);
179 		} else {
180 			ZVAL_NULL(&pelement->inf);
181 		}
182 		spl_object_storage_free_hash(intern, &key);
183 		return pelement;
184 	}
185 
186 	ZVAL_COPY(&element.obj, obj);
187 	if (inf) {
188 		ZVAL_COPY(&element.inf, inf);
189 	} else {
190 		ZVAL_NULL(&element.inf);
191 	}
192 	if (key.key) {
193 		pelement = zend_hash_update_mem(&intern->storage, key.key, &element, sizeof(spl_SplObjectStorageElement));
194 	} else {
195 		pelement = zend_hash_index_update_mem(&intern->storage, key.h, &element, sizeof(spl_SplObjectStorageElement));
196 	}
197 	spl_object_storage_free_hash(intern, &key);
198 	return pelement;
199 } /* }}} */
200 
spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj)201 static int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
202 {
203 	int ret = FAILURE;
204 	zend_hash_key key;
205 	if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
206 		return ret;
207 	}
208 	if (key.key) {
209 		ret = zend_hash_del(&intern->storage, key.key);
210 	} else {
211 		ret = zend_hash_index_del(&intern->storage, key.h);
212 	}
213 	spl_object_storage_free_hash(intern, &key);
214 
215 	return ret;
216 } /* }}}*/
217 
spl_object_storage_addall(spl_SplObjectStorage *intern, zval *this, spl_SplObjectStorage *other)218 void spl_object_storage_addall(spl_SplObjectStorage *intern, zval *this, spl_SplObjectStorage *other) { /* {{{ */
219 	spl_SplObjectStorageElement *element;
220 
221 	ZEND_HASH_FOREACH_PTR(&other->storage, element) {
222 		spl_object_storage_attach(intern, this, &element->obj, &element->inf);
223 	} ZEND_HASH_FOREACH_END();
224 
225 	intern->index = 0;
226 } /* }}} */
227 
spl_object_storage_new_ex(zend_class_entry *class_type, zval *orig)228 static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zval *orig) /* {{{ */
229 {
230 	spl_SplObjectStorage *intern;
231 	zend_class_entry *parent = class_type;
232 
233 	intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(parent));
234 	memset(intern, 0, sizeof(spl_SplObjectStorage) - sizeof(zval));
235 	intern->pos = 0;
236 
237 	zend_object_std_init(&intern->std, class_type);
238 	object_properties_init(&intern->std, class_type);
239 
240 	zend_hash_init(&intern->storage, 0, NULL, spl_object_storage_dtor, 0);
241 
242 	intern->std.handlers = &spl_handler_SplObjectStorage;
243 
244 	while (parent) {
245 		if (parent == spl_ce_SplObjectStorage) {
246 			if (class_type != spl_ce_SplObjectStorage) {
247 				intern->fptr_get_hash = zend_hash_str_find_ptr(&class_type->function_table, "gethash", sizeof("gethash") - 1);
248 				if (intern->fptr_get_hash->common.scope == spl_ce_SplObjectStorage) {
249 					intern->fptr_get_hash = NULL;
250 				}
251 			}
252 			break;
253 		}
254 
255 		parent = parent->parent;
256 	}
257 
258 	if (orig) {
259 		spl_SplObjectStorage *other = Z_SPLOBJSTORAGE_P(orig);
260 		spl_object_storage_addall(intern, orig, other);
261 	}
262 
263 	return &intern->std;
264 }
265 /* }}} */
266 
267 /* {{{ spl_object_storage_clone */
spl_object_storage_clone(zval *zobject)268 static zend_object *spl_object_storage_clone(zval *zobject)
269 {
270 	zend_object *old_object;
271 	zend_object *new_object;
272 
273 	old_object = Z_OBJ_P(zobject);
274 	new_object = spl_object_storage_new_ex(old_object->ce, zobject);
275 
276 	zend_objects_clone_members(new_object, old_object);
277 
278 	return new_object;
279 }
280 /* }}} */
281 
spl_object_storage_debug_info(zval *obj)282 static inline HashTable* spl_object_storage_debug_info(zval *obj) /* {{{ */
283 {
284 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(obj);
285 	spl_SplObjectStorageElement *element;
286 	HashTable *props;
287 	zval tmp, storage;
288 	zend_string *md5str;
289 	zend_string *zname;
290 	HashTable *debug_info;
291 
292 	props = Z_OBJPROP_P(obj);
293 
294 	debug_info = zend_new_array(zend_hash_num_elements(props) + 1);
295 	zend_hash_copy(debug_info, props, (copy_ctor_func_t)zval_add_ref);
296 
297 	array_init(&storage);
298 
299 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
300 		md5str = php_spl_object_hash(&element->obj);
301 		array_init(&tmp);
302 		/* Incrementing the refcount of obj and inf would confuse the garbage collector.
303 		 * Prefer to null the destructor */
304 		Z_ARRVAL_P(&tmp)->pDestructor = NULL;
305 		add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &element->obj);
306 		add_assoc_zval_ex(&tmp, "inf", sizeof("inf") - 1, &element->inf);
307 		zend_hash_update(Z_ARRVAL(storage), md5str, &tmp);
308 		zend_string_release_ex(md5str, 0);
309 	} ZEND_HASH_FOREACH_END();
310 
311 	zname = spl_gen_private_prop_name(spl_ce_SplObjectStorage, "storage", sizeof("storage")-1);
312 	zend_symtable_update(debug_info, zname, &storage);
313 	zend_string_release_ex(zname, 0);
314 
315 	return debug_info;
316 }
317 /* }}} */
318 
319 /* overridden for garbage collection */
spl_object_storage_get_gc(zval *obj, zval **table, int *n)320 static HashTable *spl_object_storage_get_gc(zval *obj, zval **table, int *n) /* {{{ */
321 {
322 	int i = 0;
323 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(obj);
324 	spl_SplObjectStorageElement *element;
325 
326 	if (intern->storage.nNumOfElements * 2 > intern->gcdata_num) {
327 		intern->gcdata_num = intern->storage.nNumOfElements * 2;
328 		intern->gcdata = (zval*)erealloc(intern->gcdata, sizeof(zval) * intern->gcdata_num);
329 	}
330 
331 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
332 		ZVAL_COPY_VALUE(&intern->gcdata[i++], &element->obj);
333 		ZVAL_COPY_VALUE(&intern->gcdata[i++], &element->inf);
334 	} ZEND_HASH_FOREACH_END();
335 
336 	*table = intern->gcdata;
337 	*n = i;
338 
339 	return zend_std_get_properties(obj);
340 }
341 /* }}} */
342 
spl_object_storage_compare_info(zval *e1, zval *e2)343 static int spl_object_storage_compare_info(zval *e1, zval *e2) /* {{{ */
344 {
345 	spl_SplObjectStorageElement *s1 = (spl_SplObjectStorageElement*)Z_PTR_P(e1);
346 	spl_SplObjectStorageElement *s2 = (spl_SplObjectStorageElement*)Z_PTR_P(e2);
347 	zval result;
348 
349 	if (compare_function(&result, &s1->inf, &s2->inf) == FAILURE) {
350 		return 1;
351 	}
352 
353 	return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
354 }
355 /* }}} */
356 
spl_object_storage_compare_objects(zval *o1, zval *o2)357 static int spl_object_storage_compare_objects(zval *o1, zval *o2) /* {{{ */
358 {
359 	zend_object *zo1 = (zend_object *)Z_OBJ_P(o1);
360 	zend_object *zo2 = (zend_object *)Z_OBJ_P(o2);
361 
362 	if (zo1->ce != spl_ce_SplObjectStorage || zo2->ce != spl_ce_SplObjectStorage) {
363 		return 1;
364 	}
365 
366 	return zend_hash_compare(&(Z_SPLOBJSTORAGE_P(o1))->storage, &(Z_SPLOBJSTORAGE_P(o2))->storage, (compare_func_t)spl_object_storage_compare_info, 0);
367 }
368 /* }}} */
369 
370 /* {{{ spl_array_object_new */
spl_SplObjectStorage_new(zend_class_entry *class_type)371 static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
372 {
373 	return spl_object_storage_new_ex(class_type, NULL);
374 }
375 /* }}} */
376 
spl_object_storage_contains(spl_SplObjectStorage *intern, zval *this, zval *obj)377 int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
378 {
379 	int found;
380 	zend_hash_key key;
381 	if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
382 		return 0;
383 	}
384 
385 	if (key.key) {
386 		found = zend_hash_exists(&intern->storage, key.key);
387 	} else {
388 		found = zend_hash_index_exists(&intern->storage, key.h);
389 	}
390 	spl_object_storage_free_hash(intern, &key);
391 	return found;
392 } /* }}} */
393 
394 /* {{{ proto void SplObjectStorage::attach(object obj, mixed data = NULL)
395  Attaches an object to the storage if not yet contained */
SPL_METHODnull396 SPL_METHOD(SplObjectStorage, attach)
397 {
398 	zval *obj, *inf = NULL;
399 
400 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
401 
402 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|z!", &obj, &inf) == FAILURE) {
403 		return;
404 	}
405 	spl_object_storage_attach(intern, ZEND_THIS, obj, inf);
406 } /* }}} */
407 
408 /* {{{ proto void SplObjectStorage::detach(object obj)
409  Detaches an object from the storage */
SPL_METHODnull410 SPL_METHOD(SplObjectStorage, detach)
411 {
412 	zval *obj;
413 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
414 
415 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
416 		return;
417 	}
418 	spl_object_storage_detach(intern, ZEND_THIS, obj);
419 
420 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
421 	intern->index = 0;
422 } /* }}} */
423 
424 /* {{{ proto string SplObjectStorage::getHash(object obj)
425  Returns the hash of an object */
SPL_METHODnull426 SPL_METHOD(SplObjectStorage, getHash)
427 {
428 	zval *obj;
429 
430 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
431 		return;
432 	}
433 
434 	RETURN_NEW_STR(php_spl_object_hash(obj));
435 
436 } /* }}} */
437 
438 /* {{{ proto mixed SplObjectStorage::offsetGet(object obj)
439  Returns associated information for a stored object */
SPL_METHODnull440 SPL_METHOD(SplObjectStorage, offsetGet)
441 {
442 	zval *obj;
443 	spl_SplObjectStorageElement *element;
444 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
445 	zend_hash_key key;
446 
447 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
448 		return;
449 	}
450 
451 	if (spl_object_storage_get_hash(&key, intern, ZEND_THIS, obj) == FAILURE) {
452 		return;
453 	}
454 
455 	element = spl_object_storage_get(intern, &key);
456 	spl_object_storage_free_hash(intern, &key);
457 
458 	if (!element) {
459 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Object not found");
460 	} else {
461 		zval *value = &element->inf;
462 
463 		ZVAL_COPY_DEREF(return_value, value);
464 	}
465 } /* }}} */
466 
467 /* {{{ proto bool SplObjectStorage::addAll(SplObjectStorage $os)
468  Add all elements contained in $os */
SPL_METHODnull469 SPL_METHOD(SplObjectStorage, addAll)
470 {
471 	zval *obj;
472 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
473 	spl_SplObjectStorage *other;
474 
475 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
476 		return;
477 	}
478 
479 	other = Z_SPLOBJSTORAGE_P(obj);
480 
481 	spl_object_storage_addall(intern, ZEND_THIS, other);
482 
483 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
484 } /* }}} */
485 
486 /* {{{ proto bool SplObjectStorage::removeAll(SplObjectStorage $os)
487  Remove all elements contained in $os */
SPL_METHODnull488 SPL_METHOD(SplObjectStorage, removeAll)
489 {
490 	zval *obj;
491 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
492 	spl_SplObjectStorage *other;
493 	spl_SplObjectStorageElement *element;
494 
495 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
496 		return;
497 	}
498 
499 	other = Z_SPLOBJSTORAGE_P(obj);
500 
501 	zend_hash_internal_pointer_reset(&other->storage);
502 	while ((element = zend_hash_get_current_data_ptr(&other->storage)) != NULL) {
503 		if (spl_object_storage_detach(intern, ZEND_THIS, &element->obj) == FAILURE) {
504 			zend_hash_move_forward(&other->storage);
505 		}
506 	}
507 
508 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
509 	intern->index = 0;
510 
511 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
512 } /* }}} */
513 
514 /* {{{ proto bool SplObjectStorage::removeAllExcept(SplObjectStorage $os)
515  Remove elements not common to both this SplObjectStorage instance and $os */
SPL_METHODnull516 SPL_METHOD(SplObjectStorage, removeAllExcept)
517 {
518 	zval *obj;
519 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
520 	spl_SplObjectStorage *other;
521 	spl_SplObjectStorageElement *element;
522 
523 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
524 		return;
525 	}
526 
527 	other = Z_SPLOBJSTORAGE_P(obj);
528 
529 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
530 		if (!spl_object_storage_contains(other, ZEND_THIS, &element->obj)) {
531 			spl_object_storage_detach(intern, ZEND_THIS, &element->obj);
532 		}
533 	} ZEND_HASH_FOREACH_END();
534 
535 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
536 	intern->index = 0;
537 
538 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
539 }
540 /* }}} */
541 
542 /* {{{ proto bool SplObjectStorage::contains(object obj)
543  Determine whethe an object is contained in the storage */
SPL_METHODnull544 SPL_METHOD(SplObjectStorage, contains)
545 {
546 	zval *obj;
547 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
548 
549 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
550 		return;
551 	}
552 	RETURN_BOOL(spl_object_storage_contains(intern, ZEND_THIS, obj));
553 } /* }}} */
554 
555 /* {{{ proto int SplObjectStorage::count()
556  Determine number of objects in storage */
SPL_METHODnull557 SPL_METHOD(SplObjectStorage, count)
558 {
559 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
560 	zend_long mode = COUNT_NORMAL;
561 
562 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &mode) == FAILURE) {
563 		return;
564 	}
565 
566 	if (mode == COUNT_RECURSIVE) {
567 		zend_long ret;
568 
569 		if (mode != COUNT_RECURSIVE) {
570 			ret = zend_hash_num_elements(&intern->storage);
571 		} else {
572 			ret = php_count_recursive(&intern->storage);
573 		}
574 
575 		RETURN_LONG(ret);
576 		return;
577 	}
578 
579 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
580 } /* }}} */
581 
582 /* {{{ proto void SplObjectStorage::rewind()
583  Rewind to first position */
SPL_METHODnull584 SPL_METHOD(SplObjectStorage, rewind)
585 {
586 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
587 
588 	if (zend_parse_parameters_none() == FAILURE) {
589 		return;
590 	}
591 
592 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
593 	intern->index = 0;
594 } /* }}} */
595 
596 /* {{{ proto bool SplObjectStorage::valid()
597  Returns whether current position is valid */
SPL_METHODnull598 SPL_METHOD(SplObjectStorage, valid)
599 {
600 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
601 
602 	if (zend_parse_parameters_none() == FAILURE) {
603 		return;
604 	}
605 
606 	RETURN_BOOL(zend_hash_has_more_elements_ex(&intern->storage, &intern->pos) == SUCCESS);
607 } /* }}} */
608 
609 /* {{{ proto mixed SplObjectStorage::key()
610  Returns current key */
SPL_METHODnull611 SPL_METHOD(SplObjectStorage, key)
612 {
613 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
614 
615 	if (zend_parse_parameters_none() == FAILURE) {
616 		return;
617 	}
618 
619 	RETURN_LONG(intern->index);
620 } /* }}} */
621 
622 /* {{{ proto mixed SplObjectStorage::current()
623  Returns current element */
SPL_METHODnull624 SPL_METHOD(SplObjectStorage, current)
625 {
626 	spl_SplObjectStorageElement *element;
627 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
628 
629 	if (zend_parse_parameters_none() == FAILURE) {
630 		return;
631 	}
632 
633 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
634 		return;
635 	}
636 	ZVAL_COPY(return_value, &element->obj);
637 } /* }}} */
638 
639 /* {{{ proto mixed SplObjectStorage::getInfo()
640  Returns associated information to current element */
SPL_METHODnull641 SPL_METHOD(SplObjectStorage, getInfo)
642 {
643 	spl_SplObjectStorageElement *element;
644 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
645 
646 	if (zend_parse_parameters_none() == FAILURE) {
647 		return;
648 	}
649 
650 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
651 		return;
652 	}
653 	ZVAL_COPY(return_value, &element->inf);
654 } /* }}} */
655 
656 /* {{{ proto mixed SplObjectStorage::setInfo(mixed $inf)
657  Sets associated information of current element to $inf */
SPL_METHODnull658 SPL_METHOD(SplObjectStorage, setInfo)
659 {
660 	spl_SplObjectStorageElement *element;
661 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
662 	zval *inf;
663 
664 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &inf) == FAILURE) {
665 		return;
666 	}
667 
668 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
669 		return;
670 	}
671 	zval_ptr_dtor(&element->inf);
672 	ZVAL_COPY(&element->inf, inf);
673 } /* }}} */
674 
675 /* {{{ proto void SplObjectStorage::next()
676  Moves position forward */
SPL_METHODnull677 SPL_METHOD(SplObjectStorage, next)
678 {
679 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
680 
681 	if (zend_parse_parameters_none() == FAILURE) {
682 		return;
683 	}
684 
685 	zend_hash_move_forward_ex(&intern->storage, &intern->pos);
686 	intern->index++;
687 } /* }}} */
688 
689 /* {{{ proto string SplObjectStorage::serialize()
690  Serializes storage */
SPL_METHODnull691 SPL_METHOD(SplObjectStorage, serialize)
692 {
693 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
694 
695 	spl_SplObjectStorageElement *element;
696 	zval members, flags;
697 	HashPosition      pos;
698 	php_serialize_data_t var_hash;
699 	smart_str buf = {0};
700 
701 	if (zend_parse_parameters_none() == FAILURE) {
702 		return;
703 	}
704 
705 	PHP_VAR_SERIALIZE_INIT(var_hash);
706 
707 	/* storage */
708 	smart_str_appendl(&buf, "x:", 2);
709 	ZVAL_LONG(&flags, zend_hash_num_elements(&intern->storage));
710 	php_var_serialize(&buf, &flags, &var_hash);
711 
712 	zend_hash_internal_pointer_reset_ex(&intern->storage, &pos);
713 
714 	while (zend_hash_has_more_elements_ex(&intern->storage, &pos) == SUCCESS) {
715 		if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &pos)) == NULL) {
716 			smart_str_free(&buf);
717 			PHP_VAR_SERIALIZE_DESTROY(var_hash);
718 			RETURN_NULL();
719 		}
720 		php_var_serialize(&buf, &element->obj, &var_hash);
721 		smart_str_appendc(&buf, ',');
722 		php_var_serialize(&buf, &element->inf, &var_hash);
723 		smart_str_appendc(&buf, ';');
724 		zend_hash_move_forward_ex(&intern->storage, &pos);
725 	}
726 
727 	/* members */
728 	smart_str_appendl(&buf, "m:", 2);
729 
730 	ZVAL_ARR(&members, zend_array_dup(zend_std_get_properties(ZEND_THIS)));
731 	php_var_serialize(&buf, &members, &var_hash); /* finishes the string */
732 	zval_ptr_dtor(&members);
733 
734 	/* done */
735 	PHP_VAR_SERIALIZE_DESTROY(var_hash);
736 
737 	if (buf.s) {
738 		RETURN_NEW_STR(buf.s);
739 	} else {
740 		RETURN_NULL();
741 	}
742 
743 } /* }}} */
744 
745 /* {{{ proto void SplObjectStorage::unserialize(string serialized)
746  Unserializes storage */
SPL_METHODnull747 SPL_METHOD(SplObjectStorage, unserialize)
748 {
749 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
750 
751 	char *buf;
752 	size_t buf_len;
753 	const unsigned char *p, *s;
754 	php_unserialize_data_t var_hash;
755 	zval entry, inf;
756 	zval *pcount, *pmembers;
757 	spl_SplObjectStorageElement *element;
758 	zend_long count;
759 
760 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) {
761 		return;
762 	}
763 
764 	if (buf_len == 0) {
765 		return;
766 	}
767 
768 	/* storage */
769 	s = p = (const unsigned char*)buf;
770 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
771 
772 	if (*p!= 'x' || *++p != ':') {
773 		goto outexcept;
774 	}
775 	++p;
776 
777 	pcount = var_tmp_var(&var_hash);
778 	if (!php_var_unserialize(pcount, &p, s + buf_len, &var_hash) || Z_TYPE_P(pcount) != IS_LONG) {
779 		goto outexcept;
780 	}
781 
782 	--p; /* for ';' */
783 	count = Z_LVAL_P(pcount);
784 	if (count < 0) {
785 		goto outexcept;
786 	}
787 
788 	ZVAL_UNDEF(&entry);
789 	ZVAL_UNDEF(&inf);
790 
791 	while (count-- > 0) {
792 		spl_SplObjectStorageElement *pelement;
793 		zend_hash_key key;
794 
795 		if (*p != ';') {
796 			goto outexcept;
797 		}
798 		++p;
799 		if(*p != 'O' && *p != 'C' && *p != 'r') {
800 			goto outexcept;
801 		}
802 		/* store reference to allow cross-references between different elements */
803 		if (!php_var_unserialize(&entry, &p, s + buf_len, &var_hash)) {
804 			zval_ptr_dtor(&entry);
805 			goto outexcept;
806 		}
807 		if (*p == ',') { /* new version has inf */
808 			++p;
809 			if (!php_var_unserialize(&inf, &p, s + buf_len, &var_hash)) {
810 				zval_ptr_dtor(&entry);
811 				zval_ptr_dtor(&inf);
812 				goto outexcept;
813 			}
814 		}
815 		if (Z_TYPE(entry) != IS_OBJECT) {
816 			zval_ptr_dtor(&entry);
817 			zval_ptr_dtor(&inf);
818 			goto outexcept;
819 		}
820 
821 		if (spl_object_storage_get_hash(&key, intern, ZEND_THIS, &entry) == FAILURE) {
822 			zval_ptr_dtor(&entry);
823 			zval_ptr_dtor(&inf);
824 			goto outexcept;
825 		}
826 		pelement = spl_object_storage_get(intern, &key);
827 		spl_object_storage_free_hash(intern, &key);
828 		if (pelement) {
829 			if (!Z_ISUNDEF(pelement->inf)) {
830 				var_push_dtor(&var_hash, &pelement->inf);
831 			}
832 			if (!Z_ISUNDEF(pelement->obj)) {
833 				var_push_dtor(&var_hash, &pelement->obj);
834 			}
835 		}
836 		element = spl_object_storage_attach(intern, ZEND_THIS, &entry, Z_ISUNDEF(inf)?NULL:&inf);
837 		var_replace(&var_hash, &entry, &element->obj);
838 		var_replace(&var_hash, &inf, &element->inf);
839 		zval_ptr_dtor(&entry);
840 		ZVAL_UNDEF(&entry);
841 		zval_ptr_dtor(&inf);
842 		ZVAL_UNDEF(&inf);
843 	}
844 
845 	if (*p != ';') {
846 		goto outexcept;
847 	}
848 	++p;
849 
850 	/* members */
851 	if (*p!= 'm' || *++p != ':') {
852 		goto outexcept;
853 	}
854 	++p;
855 
856 	pmembers = var_tmp_var(&var_hash);
857 	if (!php_var_unserialize(pmembers, &p, s + buf_len, &var_hash) || Z_TYPE_P(pmembers) != IS_ARRAY) {
858 		goto outexcept;
859 	}
860 
861 	/* copy members */
862 	object_properties_load(&intern->std, Z_ARRVAL_P(pmembers));
863 
864 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
865 	return;
866 
867 outexcept:
868 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
869 	zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %zd of %zd bytes", ((char*)p - buf), buf_len);
870 	return;
871 
872 } /* }}} */
873 
874 /* {{{ proto auto SplObjectStorage::__serialize() */
SPL_METHODnull875 SPL_METHOD(SplObjectStorage, __serialize)
876 {
877 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
878 	spl_SplObjectStorageElement *elem;
879 	zval tmp;
880 
881 	if (zend_parse_parameters_none_throw() == FAILURE) {
882 		return;
883 	}
884 
885 	array_init(return_value);
886 
887 	/* storage */
888 	array_init_size(&tmp, 2 * zend_hash_num_elements(&intern->storage));
889 	ZEND_HASH_FOREACH_PTR(&intern->storage, elem) {
890 		Z_TRY_ADDREF(elem->obj);
891 		zend_hash_next_index_insert(Z_ARRVAL(tmp), &elem->obj);
892 		Z_TRY_ADDREF(elem->inf);
893 		zend_hash_next_index_insert(Z_ARRVAL(tmp), &elem->inf);
894 	} ZEND_HASH_FOREACH_END();
895 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
896 
897 	/* members */
898 	ZVAL_ARR(&tmp, zend_std_get_properties(ZEND_THIS));
899 	Z_TRY_ADDREF(tmp);
900 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
901 } /* }}} */
902 
903 /* {{{ proto void SplObjectStorage::__unserialize(array serialized) */
SPL_METHODnull904 SPL_METHOD(SplObjectStorage, __unserialize)
905 {
906 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
907 	HashTable *data;
908 	zval *storage_zv, *members_zv, *key, *val;
909 
910 	if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
911 		return;
912 	}
913 
914 	storage_zv = zend_hash_index_find(data, 0);
915 	members_zv = zend_hash_index_find(data, 1);
916 	if (!storage_zv || !members_zv ||
917 			Z_TYPE_P(storage_zv) != IS_ARRAY || Z_TYPE_P(members_zv) != IS_ARRAY) {
918 		zend_throw_exception(spl_ce_UnexpectedValueException,
919 			"Incomplete or ill-typed serialization data", 0);
920 		return;
921 	}
922 
923 	if (zend_hash_num_elements(Z_ARRVAL_P(storage_zv)) % 2 != 0) {
924 		zend_throw_exception(spl_ce_UnexpectedValueException, "Odd number of elements", 0);
925 		return;
926 	}
927 
928 	key = NULL;
929 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(storage_zv), val) {
930 		if (key) {
931 			if (Z_TYPE_P(key) != IS_OBJECT) {
932 				zend_throw_exception(spl_ce_UnexpectedValueException, "Non-object key", 0);
933 				return;
934 			}
935 
936 			spl_object_storage_attach(intern, ZEND_THIS, key, val);
937 			key = NULL;
938 		} else {
939 			key = val;
940 		}
941 	} ZEND_HASH_FOREACH_END();
942 
943 	object_properties_load(&intern->std, Z_ARRVAL_P(members_zv));
944 }
945 
946 /* {{{ proto array SplObjectStorage::__debugInfo() */
SPL_METHODnull947 SPL_METHOD(SplObjectStorage, __debugInfo)
948 {
949 	if (zend_parse_parameters_none() == FAILURE) {
950 		return;
951 	}
952 
953 	RETURN_ARR(spl_object_storage_debug_info(getThis()));
954 }
955 /* }}} */
956 
957 ZEND_BEGIN_ARG_INFO(arginfo_Object, 0)
958 	ZEND_ARG_INFO(0, object)
959 ZEND_END_ARG_INFO();
960 
961 ZEND_BEGIN_ARG_INFO_EX(arginfo_attach, 0, 0, 1)
962 	ZEND_ARG_INFO(0, object)
963 	ZEND_ARG_INFO(0, data)
964 ZEND_END_ARG_INFO();
965 
966 ZEND_BEGIN_ARG_INFO(arginfo_Serialized, 0)
967 	ZEND_ARG_INFO(0, serialized)
968 ZEND_END_ARG_INFO();
969 
970 ZEND_BEGIN_ARG_INFO(arginfo_setInfo, 0)
971 	ZEND_ARG_INFO(0, info)
972 ZEND_END_ARG_INFO();
973 
974 ZEND_BEGIN_ARG_INFO(arginfo_getHash, 0)
975 	ZEND_ARG_INFO(0, object)
976 ZEND_END_ARG_INFO();
977 
978 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
979 	ZEND_ARG_INFO(0, object)
980 ZEND_END_ARG_INFO()
981 
982 ZEND_BEGIN_ARG_INFO(arginfo_splobject_void, 0)
983 ZEND_END_ARG_INFO()
984 
985 static const zend_function_entry spl_funcs_SplObjectStorage[] = {
986 	SPL_ME(SplObjectStorage,  attach,      arginfo_attach,        0)
987 	SPL_ME(SplObjectStorage,  detach,      arginfo_Object,        0)
988 	SPL_ME(SplObjectStorage,  contains,    arginfo_Object,        0)
989 	SPL_ME(SplObjectStorage,  addAll,      arginfo_Object,        0)
990 	SPL_ME(SplObjectStorage,  removeAll,   arginfo_Object,        0)
991 	SPL_ME(SplObjectStorage,  removeAllExcept,   arginfo_Object,  0)
992 	SPL_ME(SplObjectStorage,  getInfo,     arginfo_splobject_void,0)
993 	SPL_ME(SplObjectStorage,  setInfo,     arginfo_setInfo,       0)
994 	SPL_ME(SplObjectStorage,  getHash,     arginfo_getHash,       0)
995 	SPL_ME(SplObjectStorage,  __debugInfo, arginfo_splobject_void,0)
996 	/* Countable */
997 	SPL_ME(SplObjectStorage,  count,       arginfo_splobject_void,0)
998 	/* Iterator */
999 	SPL_ME(SplObjectStorage,  rewind,      arginfo_splobject_void,0)
1000 	SPL_ME(SplObjectStorage,  valid,       arginfo_splobject_void,0)
1001 	SPL_ME(SplObjectStorage,  key,         arginfo_splobject_void,0)
1002 	SPL_ME(SplObjectStorage,  current,     arginfo_splobject_void,0)
1003 	SPL_ME(SplObjectStorage,  next,        arginfo_splobject_void,0)
1004 	/* Serializable */
1005 	SPL_ME(SplObjectStorage,  unserialize, arginfo_Serialized,    0)
1006 	SPL_ME(SplObjectStorage,  serialize,   arginfo_splobject_void,0)
1007 	SPL_ME(SplObjectStorage,  __unserialize, arginfo_Serialized,    0)
1008 	SPL_ME(SplObjectStorage,  __serialize,   arginfo_splobject_void,0)
1009 	/* ArrayAccess */
1010 	SPL_MA(SplObjectStorage, offsetExists, SplObjectStorage, contains, arginfo_offsetGet, 0)
1011 	SPL_MA(SplObjectStorage, offsetSet,    SplObjectStorage, attach,   arginfo_attach, 0)
1012 	SPL_MA(SplObjectStorage, offsetUnset,  SplObjectStorage, detach,   arginfo_offsetGet, 0)
1013 	SPL_ME(SplObjectStorage, offsetGet,    arginfo_offsetGet,     0)
1014 	PHP_FE_END
1015 };
1016 
1017 typedef enum {
1018 	MIT_NEED_ANY     = 0,
1019 	MIT_NEED_ALL     = 1,
1020 	MIT_KEYS_NUMERIC = 0,
1021 	MIT_KEYS_ASSOC   = 2
1022 } MultipleIteratorFlags;
1023 
1024 #define SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT   1
1025 #define SPL_MULTIPLE_ITERATOR_GET_ALL_KEY       2
1026 
1027 /* {{{ proto MultipleIterator::__construct([int flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC])
1028    Iterator that iterates over several iterators one after the other */
SPL_METHODnull1029 SPL_METHOD(MultipleIterator, __construct)
1030 {
1031 	spl_SplObjectStorage   *intern;
1032 	zend_long               flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC;
1033 
1034 	if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
1035 		return;
1036 	}
1037 
1038 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1039 	intern->flags = flags;
1040 }
1041 /* }}} */
1042 
1043 /* {{{ proto int MultipleIterator::getFlags()
1044    Return current flags */
SPL_METHODnull1045 SPL_METHOD(MultipleIterator, getFlags)
1046 {
1047 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1048 
1049 	if (zend_parse_parameters_none() == FAILURE) {
1050 		return;
1051 	}
1052 	RETURN_LONG(intern->flags);
1053 }
1054 /* }}} */
1055 
1056 /* {{{ proto int MultipleIterator::setFlags(int flags)
1057    Set flags */
SPL_METHODnull1058 SPL_METHOD(MultipleIterator, setFlags)
1059 {
1060 	spl_SplObjectStorage *intern;
1061 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1062 
1063 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &intern->flags) == FAILURE) {
1064 		return;
1065 	}
1066 }
1067 /* }}} */
1068 
1069 /* {{{ proto void attachIterator(Iterator iterator[, mixed info]) throws InvalidArgumentException
1070    Attach a new iterator */
SPL_METHODnull1071 SPL_METHOD(MultipleIterator, attachIterator)
1072 {
1073 	spl_SplObjectStorage        *intern;
1074 	zval                        *iterator = NULL, *info = NULL;
1075 
1076 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!", &iterator, zend_ce_iterator, &info) == FAILURE) {
1077 		return;
1078 	}
1079 
1080 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1081 
1082 	if (info != NULL) {
1083 		spl_SplObjectStorageElement *element;
1084 
1085 		if (Z_TYPE_P(info) != IS_LONG && Z_TYPE_P(info) != IS_STRING) {
1086 			zend_throw_exception(spl_ce_InvalidArgumentException, "Info must be NULL, integer or string", 0);
1087 			return;
1088 		}
1089 
1090 		zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1091 		while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL) {
1092 			if (fast_is_identical_function(info, &element->inf)) {
1093 				zend_throw_exception(spl_ce_InvalidArgumentException, "Key duplication error", 0);
1094 				return;
1095 			}
1096 			zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1097 		}
1098 	}
1099 
1100 	spl_object_storage_attach(intern, ZEND_THIS, iterator, info);
1101 }
1102 /* }}} */
1103 
1104 /* {{{ proto void MultipleIterator::rewind()
1105    Rewind all attached iterator instances */
SPL_METHODnull1106 SPL_METHOD(MultipleIterator, rewind)
1107 {
1108 	spl_SplObjectStorage        *intern;
1109 	spl_SplObjectStorageElement *element;
1110 	zval                        *it;
1111 
1112 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1113 
1114 	if (zend_parse_parameters_none() == FAILURE) {
1115 		return;
1116 	}
1117 
1118 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1119 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1120 		it = &element->obj;
1121 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_rewind, "rewind", NULL);
1122 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1123 	}
1124 }
1125 /* }}} */
1126 
1127 /* {{{ proto void MultipleIterator::next()
1128    Move all attached iterator instances forward */
SPL_METHODnull1129 SPL_METHOD(MultipleIterator, next)
1130 {
1131 	spl_SplObjectStorage        *intern;
1132 	spl_SplObjectStorageElement *element;
1133 	zval                        *it;
1134 
1135 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1136 
1137 	if (zend_parse_parameters_none() == FAILURE) {
1138 		return;
1139 	}
1140 
1141 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1142 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1143 		it = &element->obj;
1144 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_next, "next", NULL);
1145 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1146 	}
1147 }
1148 /* }}} */
1149 
1150 /* {{{ proto bool MultipleIterator::valid()
1151    Return whether all or one sub iterator is valid depending on flags */
SPL_METHODnull1152 SPL_METHOD(MultipleIterator, valid)
1153 {
1154 	spl_SplObjectStorage        *intern;
1155 	spl_SplObjectStorageElement *element;
1156 	zval                        *it, retval;
1157 	zend_long                         expect, valid;
1158 
1159 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1160 
1161 	if (zend_parse_parameters_none() == FAILURE) {
1162 		return;
1163 	}
1164 
1165 	if (!zend_hash_num_elements(&intern->storage)) {
1166 		RETURN_FALSE;
1167 	}
1168 
1169 	expect = (intern->flags & MIT_NEED_ALL) ? 1 : 0;
1170 
1171 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1172 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1173 		it = &element->obj;
1174 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
1175 
1176 		if (!Z_ISUNDEF(retval)) {
1177 			valid = (Z_TYPE(retval) == IS_TRUE);
1178 			zval_ptr_dtor(&retval);
1179 		} else {
1180 			valid = 0;
1181 		}
1182 
1183 		if (expect != valid) {
1184 			RETURN_BOOL(!expect);
1185 		}
1186 
1187 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1188 	}
1189 
1190 	RETURN_BOOL(expect);
1191 }
1192 /* }}} */
1193 
spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_type, zval *return_value)1194 static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_type, zval *return_value) /* {{{ */
1195 {
1196 	spl_SplObjectStorageElement *element;
1197 	zval                        *it, retval;
1198 	int                          valid = 1, num_elements;
1199 
1200 	num_elements = zend_hash_num_elements(&intern->storage);
1201 	if (num_elements < 1) {
1202 		RETURN_FALSE;
1203 	}
1204 
1205 	array_init_size(return_value, num_elements);
1206 
1207 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1208 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1209 		it = &element->obj;
1210 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
1211 
1212 		if (!Z_ISUNDEF(retval)) {
1213 			valid = Z_TYPE(retval) == IS_TRUE;
1214 			zval_ptr_dtor(&retval);
1215 		} else {
1216 			valid = 0;
1217 		}
1218 
1219 		if (valid) {
1220 			if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
1221 				zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_current, "current", &retval);
1222 			} else {
1223 				zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_key, "key", &retval);
1224 			}
1225 			if (Z_ISUNDEF(retval)) {
1226 				zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0);
1227 				return;
1228 			}
1229 		} else if (intern->flags & MIT_NEED_ALL) {
1230 			if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
1231 				zend_throw_exception(spl_ce_RuntimeException, "Called current() with non valid sub iterator", 0);
1232 			} else {
1233 				zend_throw_exception(spl_ce_RuntimeException, "Called key() with non valid sub iterator", 0);
1234 			}
1235 			return;
1236 		} else {
1237 			ZVAL_NULL(&retval);
1238 		}
1239 
1240 		if (intern->flags & MIT_KEYS_ASSOC) {
1241 			switch (Z_TYPE(element->inf)) {
1242 				case IS_LONG:
1243 					add_index_zval(return_value, Z_LVAL(element->inf), &retval);
1244 					break;
1245 				case IS_STRING:
1246 					zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR(element->inf), &retval);
1247 					break;
1248 				default:
1249 					zval_ptr_dtor(&retval);
1250 					zend_throw_exception(spl_ce_InvalidArgumentException, "Sub-Iterator is associated with NULL", 0);
1251 					return;
1252 			}
1253 		} else {
1254 			add_next_index_zval(return_value, &retval);
1255 		}
1256 
1257 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1258 	}
1259 }
1260 /* }}} */
1261 
1262 /* {{{ proto array current() throws RuntimeException throws InvalidArgumentException
1263    Return an array of all registered Iterator instances current() result */
SPL_METHODnull1264 SPL_METHOD(MultipleIterator, current)
1265 {
1266 	spl_SplObjectStorage        *intern;
1267 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1268 
1269 	if (zend_parse_parameters_none() == FAILURE) {
1270 		return;
1271 	}
1272 
1273 	spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT, return_value);
1274 }
1275 /* }}} */
1276 
1277 /* {{{ proto array MultipleIterator::key()
1278    Return an array of all registered Iterator instances key() result */
SPL_METHODnull1279 SPL_METHOD(MultipleIterator, key)
1280 {
1281 	spl_SplObjectStorage *intern;
1282 	intern = Z_SPLOBJSTORAGE_P(ZEND_THIS);
1283 
1284 	if (zend_parse_parameters_none() == FAILURE) {
1285 		return;
1286 	}
1287 
1288 	spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_KEY, return_value);
1289 }
1290 /* }}} */
1291 
1292 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_attachIterator, 0, 0, 1)
1293 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1294 	ZEND_ARG_INFO(0, infos)
1295 ZEND_END_ARG_INFO();
1296 
1297 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_detachIterator, 0, 0, 1)
1298 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1299 ZEND_END_ARG_INFO();
1300 
1301 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_containsIterator, 0, 0, 1)
1302 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1303 ZEND_END_ARG_INFO();
1304 
1305 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_setflags, 0, 0, 1)
1306 	ZEND_ARG_INFO(0, flags)
1307 ZEND_END_ARG_INFO();
1308 
1309 static const zend_function_entry spl_funcs_MultipleIterator[] = {
1310 	SPL_ME(MultipleIterator,  __construct,            arginfo_MultipleIterator_setflags,          0)
1311 	SPL_ME(MultipleIterator,  getFlags,               arginfo_splobject_void,                     0)
1312 	SPL_ME(MultipleIterator,  setFlags,               arginfo_MultipleIterator_setflags,          0)
1313 	SPL_ME(MultipleIterator,  attachIterator,         arginfo_MultipleIterator_attachIterator,    0)
1314 	SPL_MA(MultipleIterator,  detachIterator,         SplObjectStorage, detach,      arginfo_MultipleIterator_detachIterator,   0)
1315 	SPL_MA(MultipleIterator,  containsIterator,       SplObjectStorage, contains,    arginfo_MultipleIterator_containsIterator, 0)
1316 	SPL_MA(MultipleIterator,  countIterators,         SplObjectStorage, count,       arginfo_splobject_void,                    0)
1317 	SPL_MA(MultipleIterator, __debugInfo,             SplObjectStorage, __debugInfo, arginfo_splobject_void,                    0)
1318 	/* Iterator */
1319 	SPL_ME(MultipleIterator,  rewind,                 arginfo_splobject_void,                     0)
1320 	SPL_ME(MultipleIterator,  valid,                  arginfo_splobject_void,                     0)
1321 	SPL_ME(MultipleIterator,  key,                    arginfo_splobject_void,                     0)
1322 	SPL_ME(MultipleIterator,  current,                arginfo_splobject_void,                     0)
1323 	SPL_ME(MultipleIterator,  next,                   arginfo_splobject_void,                     0)
1324 	PHP_FE_END
1325 };
1326 
1327 /* {{{ PHP_MINIT_FUNCTION(spl_observer) */
PHP_MINIT_FUNCTIONnull1328 PHP_MINIT_FUNCTION(spl_observer)
1329 {
1330 	REGISTER_SPL_INTERFACE(SplObserver);
1331 	REGISTER_SPL_INTERFACE(SplSubject);
1332 
1333 	REGISTER_SPL_STD_CLASS_EX(SplObjectStorage, spl_SplObjectStorage_new, spl_funcs_SplObjectStorage);
1334 	memcpy(&spl_handler_SplObjectStorage, &std_object_handlers, sizeof(zend_object_handlers));
1335 
1336 	spl_handler_SplObjectStorage.offset          = XtOffsetOf(spl_SplObjectStorage, std);
1337 	spl_handler_SplObjectStorage.compare_objects = spl_object_storage_compare_objects;
1338 	spl_handler_SplObjectStorage.clone_obj       = spl_object_storage_clone;
1339 	spl_handler_SplObjectStorage.get_gc          = spl_object_storage_get_gc;
1340 	spl_handler_SplObjectStorage.dtor_obj        = zend_objects_destroy_object;
1341 	spl_handler_SplObjectStorage.free_obj        = spl_SplObjectStorage_free_storage;
1342 
1343 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Countable);
1344 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Iterator);
1345 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Serializable);
1346 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, ArrayAccess);
1347 
1348 	REGISTER_SPL_STD_CLASS_EX(MultipleIterator, spl_SplObjectStorage_new, spl_funcs_MultipleIterator);
1349 	REGISTER_SPL_ITERATOR(MultipleIterator);
1350 
1351 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ANY",     MIT_NEED_ANY);
1352 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ALL",     MIT_NEED_ALL);
1353 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_NUMERIC", MIT_KEYS_NUMERIC);
1354 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_ASSOC",   MIT_KEYS_ASSOC);
1355 
1356 	return SUCCESS;
1357 }
1358 /* }}} */
1359