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