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