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