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