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