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