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