xref: /PHP-7.3/ext/spl/spl_observer.c (revision 8509970e)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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, SplSubject, 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, SplObserver, 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,int * is_temp)282 static HashTable* spl_object_storage_debug_info(zval *obj, int *is_temp) /* {{{ */
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 	*is_temp = 1;
293 
294 	props = Z_OBJPROP_P(obj);
295 
296 	debug_info = zend_new_array(zend_hash_num_elements(props) + 1);
297 	zend_hash_copy(debug_info, props, (copy_ctor_func_t)zval_add_ref);
298 
299 	array_init(&storage);
300 
301 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
302 		md5str = php_spl_object_hash(&element->obj);
303 		array_init(&tmp);
304 		/* Incrementing the refcount of obj and inf would confuse the garbage collector.
305 		 * Prefer to null the destructor */
306 		Z_ARRVAL_P(&tmp)->pDestructor = NULL;
307 		add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &element->obj);
308 		add_assoc_zval_ex(&tmp, "inf", sizeof("inf") - 1, &element->inf);
309 		zend_hash_update(Z_ARRVAL(storage), md5str, &tmp);
310 		zend_string_release_ex(md5str, 0);
311 	} ZEND_HASH_FOREACH_END();
312 
313 	zname = spl_gen_private_prop_name(spl_ce_SplObjectStorage, "storage", sizeof("storage")-1);
314 	zend_symtable_update(debug_info, zname, &storage);
315 	zend_string_release_ex(zname, 0);
316 
317 	return debug_info;
318 }
319 /* }}} */
320 
321 /* overriden for garbage collection */
spl_object_storage_get_gc(zval * obj,zval ** table,int * n)322 static HashTable *spl_object_storage_get_gc(zval *obj, zval **table, int *n) /* {{{ */
323 {
324 	int i = 0;
325 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(obj);
326 	spl_SplObjectStorageElement *element;
327 
328 	if (intern->storage.nNumOfElements * 2 > intern->gcdata_num) {
329 		intern->gcdata_num = intern->storage.nNumOfElements * 2;
330 		intern->gcdata = (zval*)erealloc(intern->gcdata, sizeof(zval) * intern->gcdata_num);
331 	}
332 
333 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
334 		ZVAL_COPY_VALUE(&intern->gcdata[i++], &element->obj);
335 		ZVAL_COPY_VALUE(&intern->gcdata[i++], &element->inf);
336 	} ZEND_HASH_FOREACH_END();
337 
338 	*table = intern->gcdata;
339 	*n = i;
340 
341 	return zend_std_get_properties(obj);
342 }
343 /* }}} */
344 
spl_object_storage_compare_info(zval * e1,zval * e2)345 static int spl_object_storage_compare_info(zval *e1, zval *e2) /* {{{ */
346 {
347 	spl_SplObjectStorageElement *s1 = (spl_SplObjectStorageElement*)Z_PTR_P(e1);
348 	spl_SplObjectStorageElement *s2 = (spl_SplObjectStorageElement*)Z_PTR_P(e2);
349 	zval result;
350 
351 	if (compare_function(&result, &s1->inf, &s2->inf) == FAILURE) {
352 		return 1;
353 	}
354 
355 	return Z_LVAL(result) > 0 ? 1 : (Z_LVAL(result) < 0 ? -1 : 0);
356 }
357 /* }}} */
358 
spl_object_storage_compare_objects(zval * o1,zval * o2)359 static int spl_object_storage_compare_objects(zval *o1, zval *o2) /* {{{ */
360 {
361 	zend_object *zo1 = (zend_object *)Z_OBJ_P(o1);
362 	zend_object *zo2 = (zend_object *)Z_OBJ_P(o2);
363 
364 	if (zo1->ce != spl_ce_SplObjectStorage || zo2->ce != spl_ce_SplObjectStorage) {
365 		return 1;
366 	}
367 
368 	return zend_hash_compare(&(Z_SPLOBJSTORAGE_P(o1))->storage, &(Z_SPLOBJSTORAGE_P(o2))->storage, (compare_func_t)spl_object_storage_compare_info, 0);
369 }
370 /* }}} */
371 
372 /* {{{ spl_array_object_new */
spl_SplObjectStorage_new(zend_class_entry * class_type)373 static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
374 {
375 	return spl_object_storage_new_ex(class_type, NULL);
376 }
377 /* }}} */
378 
spl_object_storage_contains(spl_SplObjectStorage * intern,zval * this,zval * obj)379 int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
380 {
381 	int found;
382 	zend_hash_key key;
383 	if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
384 		return 0;
385 	}
386 
387 	if (key.key) {
388 		found = zend_hash_exists(&intern->storage, key.key);
389 	} else {
390 		found = zend_hash_index_exists(&intern->storage, key.h);
391 	}
392 	spl_object_storage_free_hash(intern, &key);
393 	return found;
394 } /* }}} */
395 
396 /* {{{ proto void SplObjectStorage::attach(object obj, mixed inf = NULL)
397  Attaches an object to the storage if not yet contained */
SPL_METHOD(SplObjectStorage,attach)398 SPL_METHOD(SplObjectStorage, attach)
399 {
400 	zval *obj, *inf = NULL;
401 
402 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
403 
404 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|z!", &obj, &inf) == FAILURE) {
405 		return;
406 	}
407 	spl_object_storage_attach(intern, getThis(), obj, inf);
408 } /* }}} */
409 
410 /* {{{ proto void SplObjectStorage::detach(object obj)
411  Detaches an object from the storage */
SPL_METHOD(SplObjectStorage,detach)412 SPL_METHOD(SplObjectStorage, detach)
413 {
414 	zval *obj;
415 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
416 
417 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
418 		return;
419 	}
420 	spl_object_storage_detach(intern, getThis(), obj);
421 
422 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
423 	intern->index = 0;
424 } /* }}} */
425 
426 /* {{{ proto string SplObjectStorage::getHash(object obj)
427  Returns the hash of an object */
SPL_METHOD(SplObjectStorage,getHash)428 SPL_METHOD(SplObjectStorage, getHash)
429 {
430 	zval *obj;
431 
432 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
433 		return;
434 	}
435 
436 	RETURN_NEW_STR(php_spl_object_hash(obj));
437 
438 } /* }}} */
439 
440 /* {{{ proto mixed SplObjectStorage::offsetGet(object obj)
441  Returns associated information for a stored object */
SPL_METHOD(SplObjectStorage,offsetGet)442 SPL_METHOD(SplObjectStorage, offsetGet)
443 {
444 	zval *obj;
445 	spl_SplObjectStorageElement *element;
446 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
447 	zend_hash_key key;
448 
449 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
450 		return;
451 	}
452 
453 	if (spl_object_storage_get_hash(&key, intern, getThis(), obj) == FAILURE) {
454 		return;
455 	}
456 
457 	element = spl_object_storage_get(intern, &key);
458 	spl_object_storage_free_hash(intern, &key);
459 
460 	if (!element) {
461 		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Object not found");
462 	} else {
463 		zval *value = &element->inf;
464 
465 		ZVAL_COPY_DEREF(return_value, value);
466 	}
467 } /* }}} */
468 
469 /* {{{ proto bool SplObjectStorage::addAll(SplObjectStorage $os)
470  Add all elements contained in $os */
SPL_METHOD(SplObjectStorage,addAll)471 SPL_METHOD(SplObjectStorage, addAll)
472 {
473 	zval *obj;
474 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
475 	spl_SplObjectStorage *other;
476 
477 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
478 		return;
479 	}
480 
481 	other = Z_SPLOBJSTORAGE_P(obj);
482 
483 	spl_object_storage_addall(intern, getThis(), other);
484 
485 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
486 } /* }}} */
487 
488 /* {{{ proto bool SplObjectStorage::removeAll(SplObjectStorage $os)
489  Remove all elements contained in $os */
SPL_METHOD(SplObjectStorage,removeAll)490 SPL_METHOD(SplObjectStorage, removeAll)
491 {
492 	zval *obj;
493 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
494 	spl_SplObjectStorage *other;
495 	spl_SplObjectStorageElement *element;
496 
497 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
498 		return;
499 	}
500 
501 	other = Z_SPLOBJSTORAGE_P(obj);
502 
503 	zend_hash_internal_pointer_reset(&other->storage);
504 	while ((element = zend_hash_get_current_data_ptr(&other->storage)) != NULL) {
505 		if (spl_object_storage_detach(intern, getThis(), &element->obj) == FAILURE) {
506 			zend_hash_move_forward(&other->storage);
507 		}
508 	}
509 
510 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
511 	intern->index = 0;
512 
513 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
514 } /* }}} */
515 
516 /* {{{ proto bool SplObjectStorage::removeAllExcept(SplObjectStorage $os)
517  Remove elements not common to both this SplObjectStorage instance and $os */
SPL_METHOD(SplObjectStorage,removeAllExcept)518 SPL_METHOD(SplObjectStorage, removeAllExcept)
519 {
520 	zval *obj;
521 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
522 	spl_SplObjectStorage *other;
523 	spl_SplObjectStorageElement *element;
524 
525 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
526 		return;
527 	}
528 
529 	other = Z_SPLOBJSTORAGE_P(obj);
530 
531 	ZEND_HASH_FOREACH_PTR(&intern->storage, element) {
532 		if (!spl_object_storage_contains(other, getThis(), &element->obj)) {
533 			spl_object_storage_detach(intern, getThis(), &element->obj);
534 		}
535 	} ZEND_HASH_FOREACH_END();
536 
537 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
538 	intern->index = 0;
539 
540 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
541 }
542 /* }}} */
543 
544 /* {{{ proto bool SplObjectStorage::contains(object obj)
545  Determine whethe an object is contained in the storage */
SPL_METHOD(SplObjectStorage,contains)546 SPL_METHOD(SplObjectStorage, contains)
547 {
548 	zval *obj;
549 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
550 
551 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
552 		return;
553 	}
554 	RETURN_BOOL(spl_object_storage_contains(intern, getThis(), obj));
555 } /* }}} */
556 
557 /* {{{ proto int SplObjectStorage::count()
558  Determine number of objects in storage */
SPL_METHOD(SplObjectStorage,count)559 SPL_METHOD(SplObjectStorage, count)
560 {
561 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
562 	zend_long mode = COUNT_NORMAL;
563 
564 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &mode) == FAILURE) {
565 		return;
566 	}
567 
568 	if (mode == COUNT_RECURSIVE) {
569 		zend_long ret;
570 
571 		if (mode != COUNT_RECURSIVE) {
572 			ret = zend_hash_num_elements(&intern->storage);
573 		} else {
574 			ret = php_count_recursive(&intern->storage);
575 		}
576 
577 		RETURN_LONG(ret);
578 		return;
579 	}
580 
581 	RETURN_LONG(zend_hash_num_elements(&intern->storage));
582 } /* }}} */
583 
584 /* {{{ proto void SplObjectStorage::rewind()
585  Rewind to first position */
SPL_METHOD(SplObjectStorage,rewind)586 SPL_METHOD(SplObjectStorage, rewind)
587 {
588 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
589 
590 	if (zend_parse_parameters_none() == FAILURE) {
591 		return;
592 	}
593 
594 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
595 	intern->index = 0;
596 } /* }}} */
597 
598 /* {{{ proto bool SplObjectStorage::valid()
599  Returns whether current position is valid */
SPL_METHOD(SplObjectStorage,valid)600 SPL_METHOD(SplObjectStorage, valid)
601 {
602 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
603 
604 	if (zend_parse_parameters_none() == FAILURE) {
605 		return;
606 	}
607 
608 	RETURN_BOOL(zend_hash_has_more_elements_ex(&intern->storage, &intern->pos) == SUCCESS);
609 } /* }}} */
610 
611 /* {{{ proto mixed SplObjectStorage::key()
612  Returns current key */
SPL_METHOD(SplObjectStorage,key)613 SPL_METHOD(SplObjectStorage, key)
614 {
615 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
616 
617 	if (zend_parse_parameters_none() == FAILURE) {
618 		return;
619 	}
620 
621 	RETURN_LONG(intern->index);
622 } /* }}} */
623 
624 /* {{{ proto mixed SplObjectStorage::current()
625  Returns current element */
SPL_METHOD(SplObjectStorage,current)626 SPL_METHOD(SplObjectStorage, current)
627 {
628 	spl_SplObjectStorageElement *element;
629 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
630 
631 	if (zend_parse_parameters_none() == FAILURE) {
632 		return;
633 	}
634 
635 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
636 		return;
637 	}
638 	ZVAL_COPY(return_value, &element->obj);
639 } /* }}} */
640 
641 /* {{{ proto mixed SplObjectStorage::getInfo()
642  Returns associated information to current element */
SPL_METHOD(SplObjectStorage,getInfo)643 SPL_METHOD(SplObjectStorage, getInfo)
644 {
645 	spl_SplObjectStorageElement *element;
646 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
647 
648 	if (zend_parse_parameters_none() == FAILURE) {
649 		return;
650 	}
651 
652 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
653 		return;
654 	}
655 	ZVAL_COPY(return_value, &element->inf);
656 } /* }}} */
657 
658 /* {{{ proto mixed SplObjectStorage::setInfo(mixed $inf)
659  Sets associated information of current element to $inf */
SPL_METHOD(SplObjectStorage,setInfo)660 SPL_METHOD(SplObjectStorage, setInfo)
661 {
662 	spl_SplObjectStorageElement *element;
663 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
664 	zval *inf;
665 
666 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &inf) == FAILURE) {
667 		return;
668 	}
669 
670 	if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) {
671 		return;
672 	}
673 	zval_ptr_dtor(&element->inf);
674 	ZVAL_COPY(&element->inf, inf);
675 } /* }}} */
676 
677 /* {{{ proto void SplObjectStorage::next()
678  Moves position forward */
SPL_METHOD(SplObjectStorage,next)679 SPL_METHOD(SplObjectStorage, next)
680 {
681 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
682 
683 	if (zend_parse_parameters_none() == FAILURE) {
684 		return;
685 	}
686 
687 	zend_hash_move_forward_ex(&intern->storage, &intern->pos);
688 	intern->index++;
689 } /* }}} */
690 
691 /* {{{ proto string SplObjectStorage::serialize()
692  Serializes storage */
SPL_METHOD(SplObjectStorage,serialize)693 SPL_METHOD(SplObjectStorage, serialize)
694 {
695 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
696 
697 	spl_SplObjectStorageElement *element;
698 	zval members, flags;
699 	HashPosition      pos;
700 	php_serialize_data_t var_hash;
701 	smart_str buf = {0};
702 
703 	if (zend_parse_parameters_none() == FAILURE) {
704 		return;
705 	}
706 
707 	PHP_VAR_SERIALIZE_INIT(var_hash);
708 
709 	/* storage */
710 	smart_str_appendl(&buf, "x:", 2);
711 	ZVAL_LONG(&flags, zend_hash_num_elements(&intern->storage));
712 	php_var_serialize(&buf, &flags, &var_hash);
713 
714 	zend_hash_internal_pointer_reset_ex(&intern->storage, &pos);
715 
716 	while (zend_hash_has_more_elements_ex(&intern->storage, &pos) == SUCCESS) {
717 		if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &pos)) == NULL) {
718 			smart_str_free(&buf);
719 			PHP_VAR_SERIALIZE_DESTROY(var_hash);
720 			RETURN_NULL();
721 		}
722 		php_var_serialize(&buf, &element->obj, &var_hash);
723 		smart_str_appendc(&buf, ',');
724 		php_var_serialize(&buf, &element->inf, &var_hash);
725 		smart_str_appendc(&buf, ';');
726 		zend_hash_move_forward_ex(&intern->storage, &pos);
727 	}
728 
729 	/* members */
730 	smart_str_appendl(&buf, "m:", 2);
731 
732 	ZVAL_ARR(&members, zend_array_dup(zend_std_get_properties(getThis())));
733 	php_var_serialize(&buf, &members, &var_hash); /* finishes the string */
734 	zval_ptr_dtor(&members);
735 
736 	/* done */
737 	PHP_VAR_SERIALIZE_DESTROY(var_hash);
738 
739 	if (buf.s) {
740 		RETURN_NEW_STR(buf.s);
741 	} else {
742 		RETURN_NULL();
743 	}
744 
745 } /* }}} */
746 
747 /* {{{ proto void SplObjectStorage::unserialize(string serialized)
748  Unserializes storage */
SPL_METHOD(SplObjectStorage,unserialize)749 SPL_METHOD(SplObjectStorage, unserialize)
750 {
751 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
752 
753 	char *buf;
754 	size_t buf_len;
755 	const unsigned char *p, *s;
756 	php_unserialize_data_t var_hash;
757 	zval entry, inf;
758 	zval *pcount, *pmembers;
759 	spl_SplObjectStorageElement *element;
760 	zend_long count;
761 
762 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) {
763 		return;
764 	}
765 
766 	if (buf_len == 0) {
767 		return;
768 	}
769 
770 	/* storage */
771 	s = p = (const unsigned char*)buf;
772 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
773 
774 	if (*p!= 'x' || *++p != ':') {
775 		goto outexcept;
776 	}
777 	++p;
778 
779 	pcount = var_tmp_var(&var_hash);
780 	if (!php_var_unserialize(pcount, &p, s + buf_len, &var_hash) || Z_TYPE_P(pcount) != IS_LONG) {
781 		goto outexcept;
782 	}
783 
784 	--p; /* for ';' */
785 	count = Z_LVAL_P(pcount);
786 	if (count < 0) {
787 		goto outexcept;
788 	}
789 
790 	ZVAL_UNDEF(&entry);
791 	ZVAL_UNDEF(&inf);
792 
793 	while (count-- > 0) {
794 		spl_SplObjectStorageElement *pelement;
795 		zend_hash_key key;
796 
797 		if (*p != ';') {
798 			goto outexcept;
799 		}
800 		++p;
801 		if(*p != 'O' && *p != 'C' && *p != 'r') {
802 			goto outexcept;
803 		}
804 		/* store reference to allow cross-references between different elements */
805 		if (!php_var_unserialize(&entry, &p, s + buf_len, &var_hash)) {
806 			zval_ptr_dtor(&entry);
807 			goto outexcept;
808 		}
809 		if (*p == ',') { /* new version has inf */
810 			++p;
811 			if (!php_var_unserialize(&inf, &p, s + buf_len, &var_hash)) {
812 				zval_ptr_dtor(&entry);
813 				zval_ptr_dtor(&inf);
814 				goto outexcept;
815 			}
816 		}
817 		if (Z_TYPE(entry) != IS_OBJECT) {
818 			zval_ptr_dtor(&entry);
819 			zval_ptr_dtor(&inf);
820 			goto outexcept;
821 		}
822 
823 		if (spl_object_storage_get_hash(&key, intern, getThis(), &entry) == FAILURE) {
824 			zval_ptr_dtor(&entry);
825 			zval_ptr_dtor(&inf);
826 			goto outexcept;
827 		}
828 		pelement = spl_object_storage_get(intern, &key);
829 		spl_object_storage_free_hash(intern, &key);
830 		if (pelement) {
831 			if (!Z_ISUNDEF(pelement->inf)) {
832 				var_push_dtor(&var_hash, &pelement->inf);
833 			}
834 			if (!Z_ISUNDEF(pelement->obj)) {
835 				var_push_dtor(&var_hash, &pelement->obj);
836 			}
837 		}
838 		element = spl_object_storage_attach(intern, getThis(), &entry, Z_ISUNDEF(inf)?NULL:&inf);
839 		var_replace(&var_hash, &entry, &element->obj);
840 		var_replace(&var_hash, &inf, &element->inf);
841 		zval_ptr_dtor(&entry);
842 		ZVAL_UNDEF(&entry);
843 		zval_ptr_dtor(&inf);
844 		ZVAL_UNDEF(&inf);
845 	}
846 
847 	if (*p != ';') {
848 		goto outexcept;
849 	}
850 	++p;
851 
852 	/* members */
853 	if (*p!= 'm' || *++p != ':') {
854 		goto outexcept;
855 	}
856 	++p;
857 
858 	pmembers = var_tmp_var(&var_hash);
859 	if (!php_var_unserialize(pmembers, &p, s + buf_len, &var_hash) || Z_TYPE_P(pmembers) != IS_ARRAY) {
860 		goto outexcept;
861 	}
862 
863 	/* copy members */
864 	object_properties_load(&intern->std, Z_ARRVAL_P(pmembers));
865 
866 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
867 	return;
868 
869 outexcept:
870 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
871 	zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset %zd of %zd bytes", ((char*)p - buf), buf_len);
872 	return;
873 
874 } /* }}} */
875 
876 ZEND_BEGIN_ARG_INFO(arginfo_Object, 0)
877 	ZEND_ARG_INFO(0, object)
878 ZEND_END_ARG_INFO();
879 
880 ZEND_BEGIN_ARG_INFO_EX(arginfo_attach, 0, 0, 1)
881 	ZEND_ARG_INFO(0, object)
882 	ZEND_ARG_INFO(0, inf)
883 ZEND_END_ARG_INFO();
884 
885 ZEND_BEGIN_ARG_INFO(arginfo_Serialized, 0)
886 	ZEND_ARG_INFO(0, serialized)
887 ZEND_END_ARG_INFO();
888 
889 ZEND_BEGIN_ARG_INFO(arginfo_setInfo, 0)
890 	ZEND_ARG_INFO(0, info)
891 ZEND_END_ARG_INFO();
892 
893 ZEND_BEGIN_ARG_INFO(arginfo_getHash, 0)
894 	ZEND_ARG_INFO(0, object)
895 ZEND_END_ARG_INFO();
896 
897 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
898 	ZEND_ARG_INFO(0, object)
899 ZEND_END_ARG_INFO()
900 
901 ZEND_BEGIN_ARG_INFO(arginfo_splobject_void, 0)
902 ZEND_END_ARG_INFO()
903 
904 static const zend_function_entry spl_funcs_SplObjectStorage[] = {
905 	SPL_ME(SplObjectStorage,  attach,      arginfo_attach,        0)
906 	SPL_ME(SplObjectStorage,  detach,      arginfo_Object,        0)
907 	SPL_ME(SplObjectStorage,  contains,    arginfo_Object,        0)
908 	SPL_ME(SplObjectStorage,  addAll,      arginfo_Object,        0)
909 	SPL_ME(SplObjectStorage,  removeAll,   arginfo_Object,        0)
910 	SPL_ME(SplObjectStorage,  removeAllExcept,   arginfo_Object,  0)
911 	SPL_ME(SplObjectStorage,  getInfo,     arginfo_splobject_void,0)
912 	SPL_ME(SplObjectStorage,  setInfo,     arginfo_setInfo,       0)
913 	SPL_ME(SplObjectStorage,  getHash,     arginfo_getHash,       0)
914 	/* Countable */
915 	SPL_ME(SplObjectStorage,  count,       arginfo_splobject_void,0)
916 	/* Iterator */
917 	SPL_ME(SplObjectStorage,  rewind,      arginfo_splobject_void,0)
918 	SPL_ME(SplObjectStorage,  valid,       arginfo_splobject_void,0)
919 	SPL_ME(SplObjectStorage,  key,         arginfo_splobject_void,0)
920 	SPL_ME(SplObjectStorage,  current,     arginfo_splobject_void,0)
921 	SPL_ME(SplObjectStorage,  next,        arginfo_splobject_void,0)
922 	/* Serializable */
923 	SPL_ME(SplObjectStorage,  unserialize, arginfo_Serialized,    0)
924 	SPL_ME(SplObjectStorage,  serialize,   arginfo_splobject_void,0)
925 	/* ArrayAccess */
926 	SPL_MA(SplObjectStorage, offsetExists, SplObjectStorage, contains, arginfo_offsetGet, 0)
927 	SPL_MA(SplObjectStorage, offsetSet,    SplObjectStorage, attach,   arginfo_attach, 0)
928 	SPL_MA(SplObjectStorage, offsetUnset,  SplObjectStorage, detach,   arginfo_offsetGet, 0)
929 	SPL_ME(SplObjectStorage, offsetGet,    arginfo_offsetGet,     0)
930 	PHP_FE_END
931 };
932 
933 typedef enum {
934 	MIT_NEED_ANY     = 0,
935 	MIT_NEED_ALL     = 1,
936 	MIT_KEYS_NUMERIC = 0,
937 	MIT_KEYS_ASSOC   = 2
938 } MultipleIteratorFlags;
939 
940 #define SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT   1
941 #define SPL_MULTIPLE_ITERATOR_GET_ALL_KEY       2
942 
943 /* {{{ proto MultipleIterator::__construct([int flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC])
944    Iterator that iterates over several iterators one after the other */
SPL_METHOD(MultipleIterator,__construct)945 SPL_METHOD(MultipleIterator, __construct)
946 {
947 	spl_SplObjectStorage   *intern;
948 	zend_long               flags = MIT_NEED_ALL|MIT_KEYS_NUMERIC;
949 
950 	if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|l", &flags) == FAILURE) {
951 		return;
952 	}
953 
954 	intern = Z_SPLOBJSTORAGE_P(getThis());
955 	intern->flags = flags;
956 }
957 /* }}} */
958 
959 /* {{{ proto int MultipleIterator::getFlags()
960    Return current flags */
SPL_METHOD(MultipleIterator,getFlags)961 SPL_METHOD(MultipleIterator, getFlags)
962 {
963 	spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
964 
965 	if (zend_parse_parameters_none() == FAILURE) {
966 		return;
967 	}
968 	RETURN_LONG(intern->flags);
969 }
970 /* }}} */
971 
972 /* {{{ proto int MultipleIterator::setFlags(int flags)
973    Set flags */
SPL_METHOD(MultipleIterator,setFlags)974 SPL_METHOD(MultipleIterator, setFlags)
975 {
976 	spl_SplObjectStorage *intern;
977 	intern = Z_SPLOBJSTORAGE_P(getThis());
978 
979 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &intern->flags) == FAILURE) {
980 		return;
981 	}
982 }
983 /* }}} */
984 
985 /* {{{ proto void attachIterator(Iterator iterator[, mixed info]) throws InvalidArgumentException
986    Attach a new iterator */
SPL_METHOD(MultipleIterator,attachIterator)987 SPL_METHOD(MultipleIterator, attachIterator)
988 {
989 	spl_SplObjectStorage        *intern;
990 	zval                        *iterator = NULL, *info = NULL;
991 
992 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!", &iterator, zend_ce_iterator, &info) == FAILURE) {
993 		return;
994 	}
995 
996 	intern = Z_SPLOBJSTORAGE_P(getThis());
997 
998 	if (info != NULL) {
999 		spl_SplObjectStorageElement *element;
1000 
1001 		if (Z_TYPE_P(info) != IS_LONG && Z_TYPE_P(info) != IS_STRING) {
1002 			zend_throw_exception(spl_ce_InvalidArgumentException, "Info must be NULL, integer or string", 0);
1003 			return;
1004 		}
1005 
1006 		zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1007 		while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL) {
1008 			if (fast_is_identical_function(info, &element->inf)) {
1009 				zend_throw_exception(spl_ce_InvalidArgumentException, "Key duplication error", 0);
1010 				return;
1011 			}
1012 			zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1013 		}
1014 	}
1015 
1016 	spl_object_storage_attach(intern, getThis(), iterator, info);
1017 }
1018 /* }}} */
1019 
1020 /* {{{ proto void MultipleIterator::rewind()
1021    Rewind all attached iterator instances */
SPL_METHOD(MultipleIterator,rewind)1022 SPL_METHOD(MultipleIterator, rewind)
1023 {
1024 	spl_SplObjectStorage        *intern;
1025 	spl_SplObjectStorageElement *element;
1026 	zval                        *it;
1027 
1028 	intern = Z_SPLOBJSTORAGE_P(getThis());
1029 
1030 	if (zend_parse_parameters_none() == FAILURE) {
1031 		return;
1032 	}
1033 
1034 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1035 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1036 		it = &element->obj;
1037 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_rewind, "rewind", NULL);
1038 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1039 	}
1040 }
1041 /* }}} */
1042 
1043 /* {{{ proto void MultipleIterator::next()
1044    Move all attached iterator instances forward */
SPL_METHOD(MultipleIterator,next)1045 SPL_METHOD(MultipleIterator, next)
1046 {
1047 	spl_SplObjectStorage        *intern;
1048 	spl_SplObjectStorageElement *element;
1049 	zval                        *it;
1050 
1051 	intern = Z_SPLOBJSTORAGE_P(getThis());
1052 
1053 	if (zend_parse_parameters_none() == FAILURE) {
1054 		return;
1055 	}
1056 
1057 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1058 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1059 		it = &element->obj;
1060 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_next, "next", NULL);
1061 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1062 	}
1063 }
1064 /* }}} */
1065 
1066 /* {{{ proto bool MultipleIterator::valid()
1067    Return whether all or one sub iterator is valid depending on flags */
SPL_METHOD(MultipleIterator,valid)1068 SPL_METHOD(MultipleIterator, valid)
1069 {
1070 	spl_SplObjectStorage        *intern;
1071 	spl_SplObjectStorageElement *element;
1072 	zval                        *it, retval;
1073 	zend_long                         expect, valid;
1074 
1075 	intern = Z_SPLOBJSTORAGE_P(getThis());
1076 
1077 	if (zend_parse_parameters_none() == FAILURE) {
1078 		return;
1079 	}
1080 
1081 	if (!zend_hash_num_elements(&intern->storage)) {
1082 		RETURN_FALSE;
1083 	}
1084 
1085 	expect = (intern->flags & MIT_NEED_ALL) ? 1 : 0;
1086 
1087 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1088 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1089 		it = &element->obj;
1090 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
1091 
1092 		if (!Z_ISUNDEF(retval)) {
1093 			valid = (Z_TYPE(retval) == IS_TRUE);
1094 			zval_ptr_dtor(&retval);
1095 		} else {
1096 			valid = 0;
1097 		}
1098 
1099 		if (expect != valid) {
1100 			RETURN_BOOL(!expect);
1101 		}
1102 
1103 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1104 	}
1105 
1106 	RETURN_BOOL(expect);
1107 }
1108 /* }}} */
1109 
spl_multiple_iterator_get_all(spl_SplObjectStorage * intern,int get_type,zval * return_value)1110 static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_type, zval *return_value) /* {{{ */
1111 {
1112 	spl_SplObjectStorageElement *element;
1113 	zval                        *it, retval;
1114 	int                          valid = 1, num_elements;
1115 
1116 	num_elements = zend_hash_num_elements(&intern->storage);
1117 	if (num_elements < 1) {
1118 		RETURN_FALSE;
1119 	}
1120 
1121 	array_init_size(return_value, num_elements);
1122 
1123 	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
1124 	while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) {
1125 		it = &element->obj;
1126 		zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_valid, "valid", &retval);
1127 
1128 		if (!Z_ISUNDEF(retval)) {
1129 			valid = Z_TYPE(retval) == IS_TRUE;
1130 			zval_ptr_dtor(&retval);
1131 		} else {
1132 			valid = 0;
1133 		}
1134 
1135 		if (valid) {
1136 			if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
1137 				zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_current, "current", &retval);
1138 			} else {
1139 				zend_call_method_with_0_params(it, Z_OBJCE_P(it), &Z_OBJCE_P(it)->iterator_funcs_ptr->zf_key, "key", &retval);
1140 			}
1141 			if (Z_ISUNDEF(retval)) {
1142 				zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0);
1143 				return;
1144 			}
1145 		} else if (intern->flags & MIT_NEED_ALL) {
1146 			if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) {
1147 				zend_throw_exception(spl_ce_RuntimeException, "Called current() with non valid sub iterator", 0);
1148 			} else {
1149 				zend_throw_exception(spl_ce_RuntimeException, "Called key() with non valid sub iterator", 0);
1150 			}
1151 			return;
1152 		} else {
1153 			ZVAL_NULL(&retval);
1154 		}
1155 
1156 		if (intern->flags & MIT_KEYS_ASSOC) {
1157 			switch (Z_TYPE(element->inf)) {
1158 				case IS_LONG:
1159 					add_index_zval(return_value, Z_LVAL(element->inf), &retval);
1160 					break;
1161 				case IS_STRING:
1162 					zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR(element->inf), &retval);
1163 					break;
1164 				default:
1165 					zval_ptr_dtor(&retval);
1166 					zend_throw_exception(spl_ce_InvalidArgumentException, "Sub-Iterator is associated with NULL", 0);
1167 					return;
1168 			}
1169 		} else {
1170 			add_next_index_zval(return_value, &retval);
1171 		}
1172 
1173 		zend_hash_move_forward_ex(&intern->storage, &intern->pos);
1174 	}
1175 }
1176 /* }}} */
1177 
1178 /* {{{ proto array current() throws RuntimeException throws InvalidArgumentException
1179    Return an array of all registered Iterator instances current() result */
SPL_METHOD(MultipleIterator,current)1180 SPL_METHOD(MultipleIterator, current)
1181 {
1182 	spl_SplObjectStorage        *intern;
1183 	intern = Z_SPLOBJSTORAGE_P(getThis());
1184 
1185 	if (zend_parse_parameters_none() == FAILURE) {
1186 		return;
1187 	}
1188 
1189 	spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT, return_value);
1190 }
1191 /* }}} */
1192 
1193 /* {{{ proto array MultipleIterator::key()
1194    Return an array of all registered Iterator instances key() result */
SPL_METHOD(MultipleIterator,key)1195 SPL_METHOD(MultipleIterator, key)
1196 {
1197 	spl_SplObjectStorage *intern;
1198 	intern = Z_SPLOBJSTORAGE_P(getThis());
1199 
1200 	if (zend_parse_parameters_none() == FAILURE) {
1201 		return;
1202 	}
1203 
1204 	spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_KEY, return_value);
1205 }
1206 /* }}} */
1207 
1208 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_attachIterator, 0, 0, 1)
1209 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1210 	ZEND_ARG_INFO(0, infos)
1211 ZEND_END_ARG_INFO();
1212 
1213 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_detachIterator, 0, 0, 1)
1214 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1215 ZEND_END_ARG_INFO();
1216 
1217 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_containsIterator, 0, 0, 1)
1218 	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
1219 ZEND_END_ARG_INFO();
1220 
1221 ZEND_BEGIN_ARG_INFO_EX(arginfo_MultipleIterator_setflags, 0, 0, 1)
1222 	ZEND_ARG_INFO(0, flags)
1223 ZEND_END_ARG_INFO();
1224 
1225 static const zend_function_entry spl_funcs_MultipleIterator[] = {
1226 	SPL_ME(MultipleIterator,  __construct,            arginfo_MultipleIterator_setflags,          0)
1227 	SPL_ME(MultipleIterator,  getFlags,               arginfo_splobject_void,                     0)
1228 	SPL_ME(MultipleIterator,  setFlags,               arginfo_MultipleIterator_setflags,          0)
1229 	SPL_ME(MultipleIterator,  attachIterator,         arginfo_MultipleIterator_attachIterator,    0)
1230 	SPL_MA(MultipleIterator,  detachIterator,         SplObjectStorage, detach,   arginfo_MultipleIterator_detachIterator,   0)
1231 	SPL_MA(MultipleIterator,  containsIterator,       SplObjectStorage, contains, arginfo_MultipleIterator_containsIterator, 0)
1232 	SPL_MA(MultipleIterator,  countIterators,         SplObjectStorage, count,    arginfo_splobject_void,                    0)
1233 	/* Iterator */
1234 	SPL_ME(MultipleIterator,  rewind,                 arginfo_splobject_void,                     0)
1235 	SPL_ME(MultipleIterator,  valid,                  arginfo_splobject_void,                     0)
1236 	SPL_ME(MultipleIterator,  key,                    arginfo_splobject_void,                     0)
1237 	SPL_ME(MultipleIterator,  current,                arginfo_splobject_void,                     0)
1238 	SPL_ME(MultipleIterator,  next,                   arginfo_splobject_void,                     0)
1239 	PHP_FE_END
1240 };
1241 
1242 /* {{{ PHP_MINIT_FUNCTION(spl_observer) */
PHP_MINIT_FUNCTION(spl_observer)1243 PHP_MINIT_FUNCTION(spl_observer)
1244 {
1245 	REGISTER_SPL_INTERFACE(SplObserver);
1246 	REGISTER_SPL_INTERFACE(SplSubject);
1247 
1248 	REGISTER_SPL_STD_CLASS_EX(SplObjectStorage, spl_SplObjectStorage_new, spl_funcs_SplObjectStorage);
1249 	memcpy(&spl_handler_SplObjectStorage, &std_object_handlers, sizeof(zend_object_handlers));
1250 
1251 	spl_handler_SplObjectStorage.offset          = XtOffsetOf(spl_SplObjectStorage, std);
1252 	spl_handler_SplObjectStorage.get_debug_info  = spl_object_storage_debug_info;
1253 	spl_handler_SplObjectStorage.compare_objects = spl_object_storage_compare_objects;
1254 	spl_handler_SplObjectStorage.clone_obj       = spl_object_storage_clone;
1255 	spl_handler_SplObjectStorage.get_gc          = spl_object_storage_get_gc;
1256 	spl_handler_SplObjectStorage.dtor_obj        = zend_objects_destroy_object;
1257 	spl_handler_SplObjectStorage.free_obj        = spl_SplObjectStorage_free_storage;
1258 
1259 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Countable);
1260 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Iterator);
1261 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Serializable);
1262 	REGISTER_SPL_IMPLEMENTS(SplObjectStorage, ArrayAccess);
1263 
1264 	REGISTER_SPL_STD_CLASS_EX(MultipleIterator, spl_SplObjectStorage_new, spl_funcs_MultipleIterator);
1265 	REGISTER_SPL_ITERATOR(MultipleIterator);
1266 
1267 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ANY",     MIT_NEED_ANY);
1268 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_NEED_ALL",     MIT_NEED_ALL);
1269 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_NUMERIC", MIT_KEYS_NUMERIC);
1270 	REGISTER_SPL_CLASS_CONST_LONG(MultipleIterator, "MIT_KEYS_ASSOC",   MIT_KEYS_ASSOC);
1271 
1272 	return SUCCESS;
1273 }
1274 /* }}} */
1275 
1276 /*
1277  * Local variables:
1278  * tab-width: 4
1279  * c-basic-offset: 4
1280  * End:
1281  * vim600: fdm=marker
1282  * vim: noet sw=4 ts=4
1283  */
1284