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