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