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