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