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