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