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