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