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