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