1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Marcus Boerger <helly@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "zend_exceptions.h"
25 #include "zend_interfaces.h"
26
27 #include "php_spl.h"
28 #include "spl_functions.h"
29 #include "spl_engine.h"
30 #include "spl_iterators.h"
31 #include "spl_iterators_arginfo.h"
32 #include "spl_directory.h"
33 #include "spl_array.h"
34 #include "spl_exceptions.h"
35 #include "zend_smart_str.h"
36
37 #ifdef accept
38 #undef accept
39 #endif
40
41 PHPAPI zend_class_entry *spl_ce_RecursiveIterator;
42 PHPAPI zend_class_entry *spl_ce_RecursiveIteratorIterator;
43 PHPAPI zend_class_entry *spl_ce_FilterIterator;
44 PHPAPI zend_class_entry *spl_ce_CallbackFilterIterator;
45 PHPAPI zend_class_entry *spl_ce_RecursiveFilterIterator;
46 PHPAPI zend_class_entry *spl_ce_RecursiveCallbackFilterIterator;
47 PHPAPI zend_class_entry *spl_ce_ParentIterator;
48 PHPAPI zend_class_entry *spl_ce_SeekableIterator;
49 PHPAPI zend_class_entry *spl_ce_LimitIterator;
50 PHPAPI zend_class_entry *spl_ce_CachingIterator;
51 PHPAPI zend_class_entry *spl_ce_RecursiveCachingIterator;
52 PHPAPI zend_class_entry *spl_ce_OuterIterator;
53 PHPAPI zend_class_entry *spl_ce_IteratorIterator;
54 PHPAPI zend_class_entry *spl_ce_NoRewindIterator;
55 PHPAPI zend_class_entry *spl_ce_InfiniteIterator;
56 PHPAPI zend_class_entry *spl_ce_EmptyIterator;
57 PHPAPI zend_class_entry *spl_ce_AppendIterator;
58 PHPAPI zend_class_entry *spl_ce_RegexIterator;
59 PHPAPI zend_class_entry *spl_ce_RecursiveRegexIterator;
60 PHPAPI zend_class_entry *spl_ce_RecursiveTreeIterator;
61
62 typedef enum {
63 RIT_LEAVES_ONLY = 0,
64 RIT_SELF_FIRST = 1,
65 RIT_CHILD_FIRST = 2
66 } RecursiveIteratorMode;
67
68 #define RIT_CATCH_GET_CHILD CIT_CATCH_GET_CHILD
69
70 typedef enum {
71 RTIT_BYPASS_CURRENT = 4,
72 RTIT_BYPASS_KEY = 8
73 } RecursiveTreeIteratorFlags;
74
75 typedef enum {
76 RS_NEXT = 0,
77 RS_TEST = 1,
78 RS_SELF = 2,
79 RS_CHILD = 3,
80 RS_START = 4
81 } RecursiveIteratorState;
82
83 typedef struct _spl_sub_iterator {
84 zend_object_iterator *iterator;
85 zval zobject;
86 zend_class_entry *ce;
87 RecursiveIteratorState state;
88 } spl_sub_iterator;
89
90 typedef struct _spl_recursive_it_object {
91 spl_sub_iterator *iterators;
92 int level;
93 RecursiveIteratorMode mode;
94 int flags;
95 int max_depth;
96 zend_bool in_iteration;
97 zend_function *beginIteration;
98 zend_function *endIteration;
99 zend_function *callHasChildren;
100 zend_function *callGetChildren;
101 zend_function *beginChildren;
102 zend_function *endChildren;
103 zend_function *nextElement;
104 zend_class_entry *ce;
105 smart_str prefix[6];
106 smart_str postfix[1];
107 zend_object std;
108 } spl_recursive_it_object;
109
110 typedef struct _spl_recursive_it_iterator {
111 zend_object_iterator intern;
112 } spl_recursive_it_iterator;
113
114 static zend_object_handlers spl_handlers_rec_it_it;
115 static zend_object_handlers spl_handlers_dual_it;
116
spl_recursive_it_from_obj(zend_object * obj)117 static inline spl_recursive_it_object *spl_recursive_it_from_obj(zend_object *obj) /* {{{ */ {
118 return (spl_recursive_it_object*)((char*)(obj) - XtOffsetOf(spl_recursive_it_object, std));
119 }
120 /* }}} */
121
122 #define Z_SPLRECURSIVE_IT_P(zv) spl_recursive_it_from_obj(Z_OBJ_P((zv)))
123
124 #define SPL_FETCH_AND_CHECK_DUAL_IT(var, objzval) \
125 do { \
126 spl_dual_it_object *it = Z_SPLDUAL_IT_P(objzval); \
127 if (it->dit_type == DIT_Unknown) { \
128 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called"); \
129 RETURN_THROWS(); \
130 } \
131 (var) = it; \
132 } while (0)
133
134 #define SPL_FETCH_SUB_ELEMENT(var, object, element) \
135 do { \
136 if(!(object)->iterators) { \
137 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called"); \
138 return; \
139 } \
140 (var) = (object)->iterators[(object)->level].element; \
141 } while (0)
142
143 #define SPL_FETCH_SUB_ELEMENT_ADDR(var, object, element) \
144 do { \
145 if(!(object)->iterators) { \
146 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called"); \
147 RETURN_THROWS(); \
148 } \
149 (var) = &(object)->iterators[(object)->level].element; \
150 } while (0)
151
152 #define SPL_FETCH_SUB_ITERATOR(var, object) SPL_FETCH_SUB_ELEMENT(var, object, iterator)
153
154
spl_recursive_it_dtor(zend_object_iterator * _iter)155 static void spl_recursive_it_dtor(zend_object_iterator *_iter)
156 {
157 spl_recursive_it_iterator *iter = (spl_recursive_it_iterator*)_iter;
158 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->intern.data);
159 zend_object_iterator *sub_iter;
160
161 while (object->level > 0) {
162 if (!Z_ISUNDEF(object->iterators[object->level].zobject)) {
163 sub_iter = object->iterators[object->level].iterator;
164 zend_iterator_dtor(sub_iter);
165 zval_ptr_dtor(&object->iterators[object->level].zobject);
166 }
167 object->level--;
168 }
169 object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
170 object->level = 0;
171
172 zval_ptr_dtor(&iter->intern.data);
173 }
174
spl_recursive_it_valid_ex(spl_recursive_it_object * object,zval * zthis)175 static int spl_recursive_it_valid_ex(spl_recursive_it_object *object, zval *zthis)
176 {
177 zend_object_iterator *sub_iter;
178 int level = object->level;
179
180 if(!object->iterators) {
181 return FAILURE;
182 }
183 while (level >=0) {
184 sub_iter = object->iterators[level].iterator;
185 if (sub_iter->funcs->valid(sub_iter) == SUCCESS) {
186 return SUCCESS;
187 }
188 level--;
189 }
190 if (object->endIteration && object->in_iteration) {
191 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endIteration, "endIteration", NULL);
192 }
193 object->in_iteration = 0;
194 return FAILURE;
195 }
196
spl_recursive_it_valid(zend_object_iterator * iter)197 static int spl_recursive_it_valid(zend_object_iterator *iter)
198 {
199 return spl_recursive_it_valid_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
200 }
201
spl_recursive_it_get_current_data(zend_object_iterator * iter)202 static zval *spl_recursive_it_get_current_data(zend_object_iterator *iter)
203 {
204 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
205 zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
206
207 return sub_iter->funcs->get_current_data(sub_iter);
208 }
209
spl_recursive_it_get_current_key(zend_object_iterator * iter,zval * key)210 static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *key)
211 {
212 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
213 zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
214
215 if (sub_iter->funcs->get_current_key) {
216 sub_iter->funcs->get_current_key(sub_iter, key);
217 } else {
218 ZVAL_LONG(key, iter->index);
219 }
220 }
221
spl_recursive_it_move_forward_ex(spl_recursive_it_object * object,zval * zthis)222 static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zval *zthis)
223 {
224 zend_object_iterator *iterator;
225 zval *zobject;
226 zend_class_entry *ce;
227 zval retval, child;
228 zend_object_iterator *sub_iter;
229 int has_children;
230
231 SPL_FETCH_SUB_ITERATOR(iterator, object);
232
233 while (!EG(exception)) {
234 next_step:
235 iterator = object->iterators[object->level].iterator;
236 switch (object->iterators[object->level].state) {
237 case RS_NEXT:
238 iterator->funcs->move_forward(iterator);
239 if (EG(exception)) {
240 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
241 return;
242 } else {
243 zend_clear_exception();
244 }
245 }
246 /* fall through */
247 case RS_START:
248 if (iterator->funcs->valid(iterator) == FAILURE) {
249 break;
250 }
251 object->iterators[object->level].state = RS_TEST;
252 /* break; */
253 case RS_TEST:
254 ce = object->iterators[object->level].ce;
255 zobject = &object->iterators[object->level].zobject;
256 if (object->callHasChildren) {
257 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->callHasChildren, "callHasChildren", &retval);
258 } else {
259 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, NULL, "haschildren", &retval);
260 }
261 if (EG(exception)) {
262 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
263 object->iterators[object->level].state = RS_NEXT;
264 return;
265 } else {
266 zend_clear_exception();
267 }
268 }
269 if (Z_TYPE(retval) != IS_UNDEF) {
270 has_children = zend_is_true(&retval);
271 zval_ptr_dtor(&retval);
272 if (has_children) {
273 if (object->max_depth == -1 || object->max_depth > object->level) {
274 switch (object->mode) {
275 case RIT_LEAVES_ONLY:
276 case RIT_CHILD_FIRST:
277 object->iterators[object->level].state = RS_CHILD;
278 goto next_step;
279 case RIT_SELF_FIRST:
280 object->iterators[object->level].state = RS_SELF;
281 goto next_step;
282 }
283 } else {
284 /* do not recurse into */
285 if (object->mode == RIT_LEAVES_ONLY) {
286 /* this is not a leave, so skip it */
287 object->iterators[object->level].state = RS_NEXT;
288 goto next_step;
289 }
290 }
291 }
292 }
293 if (object->nextElement) {
294 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->nextElement, "nextelement", NULL);
295 }
296 object->iterators[object->level].state = RS_NEXT;
297 if (EG(exception)) {
298 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
299 return;
300 } else {
301 zend_clear_exception();
302 }
303 }
304 return /* self */;
305 case RS_SELF:
306 if (object->nextElement && (object->mode == RIT_SELF_FIRST || object->mode == RIT_CHILD_FIRST)) {
307 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->nextElement, "nextelement", NULL);
308 }
309 if (object->mode == RIT_SELF_FIRST) {
310 object->iterators[object->level].state = RS_CHILD;
311 } else {
312 object->iterators[object->level].state = RS_NEXT;
313 }
314 return /* self */;
315 case RS_CHILD:
316 ce = object->iterators[object->level].ce;
317 zobject = &object->iterators[object->level].zobject;
318 if (object->callGetChildren) {
319 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->callGetChildren, "callGetChildren", &child);
320 } else {
321 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, NULL, "getchildren", &child);
322 }
323
324 if (EG(exception)) {
325 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
326 return;
327 } else {
328 zend_clear_exception();
329 zval_ptr_dtor(&child);
330 object->iterators[object->level].state = RS_NEXT;
331 goto next_step;
332 }
333 }
334
335 if (Z_TYPE(child) == IS_UNDEF || Z_TYPE(child) != IS_OBJECT ||
336 !((ce = Z_OBJCE(child)) && instanceof_function(ce, spl_ce_RecursiveIterator))) {
337 zval_ptr_dtor(&child);
338 zend_throw_exception(spl_ce_UnexpectedValueException, "Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator", 0);
339 return;
340 }
341
342 if (object->mode == RIT_CHILD_FIRST) {
343 object->iterators[object->level].state = RS_SELF;
344 } else {
345 object->iterators[object->level].state = RS_NEXT;
346 }
347 object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator) * (++object->level+1));
348 sub_iter = ce->get_iterator(ce, &child, 0);
349 ZVAL_COPY_VALUE(&object->iterators[object->level].zobject, &child);
350 object->iterators[object->level].iterator = sub_iter;
351 object->iterators[object->level].ce = ce;
352 object->iterators[object->level].state = RS_START;
353 if (sub_iter->funcs->rewind) {
354 sub_iter->funcs->rewind(sub_iter);
355 }
356 if (object->beginChildren) {
357 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginChildren, "beginchildren", NULL);
358 if (EG(exception)) {
359 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
360 return;
361 } else {
362 zend_clear_exception();
363 }
364 }
365 }
366 goto next_step;
367 }
368 /* no more elements */
369 if (object->level > 0) {
370 if (object->endChildren) {
371 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL);
372 if (EG(exception)) {
373 if (!(object->flags & RIT_CATCH_GET_CHILD)) {
374 return;
375 } else {
376 zend_clear_exception();
377 }
378 }
379 }
380 if (object->level > 0) {
381 zval garbage;
382 ZVAL_COPY_VALUE(&garbage, &object->iterators[object->level].zobject);
383 ZVAL_UNDEF(&object->iterators[object->level].zobject);
384 zval_ptr_dtor(&garbage);
385 zend_iterator_dtor(iterator);
386 object->level--;
387 }
388 } else {
389 return; /* done completeley */
390 }
391 }
392 }
393
spl_recursive_it_rewind_ex(spl_recursive_it_object * object,zval * zthis)394 static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zthis)
395 {
396 zend_object_iterator *sub_iter;
397
398 SPL_FETCH_SUB_ITERATOR(sub_iter, object);
399
400 while (object->level) {
401 sub_iter = object->iterators[object->level].iterator;
402 zend_iterator_dtor(sub_iter);
403 zval_ptr_dtor(&object->iterators[object->level--].zobject);
404 if (!EG(exception) && (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator)) {
405 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL);
406 }
407 }
408 object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
409 object->iterators[0].state = RS_START;
410 sub_iter = object->iterators[0].iterator;
411 if (sub_iter->funcs->rewind) {
412 sub_iter->funcs->rewind(sub_iter);
413 }
414 if (!EG(exception) && object->beginIteration && !object->in_iteration) {
415 zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginIteration, "beginIteration", NULL);
416 }
417 object->in_iteration = 1;
418 spl_recursive_it_move_forward_ex(object, zthis);
419 }
420
spl_recursive_it_move_forward(zend_object_iterator * iter)421 static void spl_recursive_it_move_forward(zend_object_iterator *iter)
422 {
423 spl_recursive_it_move_forward_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
424 }
425
spl_recursive_it_rewind(zend_object_iterator * iter)426 static void spl_recursive_it_rewind(zend_object_iterator *iter)
427 {
428 spl_recursive_it_rewind_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
429 }
430
431 static const zend_object_iterator_funcs spl_recursive_it_iterator_funcs = {
432 spl_recursive_it_dtor,
433 spl_recursive_it_valid,
434 spl_recursive_it_get_current_data,
435 spl_recursive_it_get_current_key,
436 spl_recursive_it_move_forward,
437 spl_recursive_it_rewind,
438 NULL,
439 NULL, /* get_gc */
440 };
441
spl_recursive_it_get_iterator(zend_class_entry * ce,zval * zobject,int by_ref)442 static zend_object_iterator *spl_recursive_it_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref)
443 {
444 if (by_ref) {
445 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
446 return NULL;
447 }
448
449 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(zobject);
450 if (object->iterators == NULL) {
451 zend_throw_error(NULL, "Object is not initialized");
452 return NULL;
453 }
454
455 spl_recursive_it_iterator *iterator = emalloc(sizeof(spl_recursive_it_iterator));
456 zend_iterator_init((zend_object_iterator*)iterator);
457
458 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(zobject));
459 iterator->intern.funcs = &spl_recursive_it_iterator_funcs;
460 return (zend_object_iterator*)iterator;
461 }
462
spl_get_iterator_from_aggregate(zval * retval,zend_class_entry * ce,zend_object * obj)463 static int spl_get_iterator_from_aggregate(zval *retval, zend_class_entry *ce, zend_object *obj) {
464 zend_function **getiterator_cache =
465 ce->iterator_funcs_ptr ? &ce->iterator_funcs_ptr->zf_new_iterator : NULL;
466 zend_call_method_with_0_params(obj, ce, getiterator_cache, "getiterator", retval);
467 if (EG(exception)) {
468 return FAILURE;
469 }
470 if (Z_TYPE_P(retval) != IS_OBJECT
471 || !instanceof_function(Z_OBJCE_P(retval), zend_ce_traversable)) {
472 zend_throw_exception_ex(spl_ce_LogicException, 0,
473 "%s::getIterator() must return an object that implements Traversable",
474 ZSTR_VAL(ce->name));
475 zval_ptr_dtor(retval);
476 return FAILURE;
477 }
478 return SUCCESS;
479 }
480
spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * ce_base,zend_class_entry * ce_inner,recursive_it_it_type rit_type)481 static void spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, recursive_it_it_type rit_type)
482 {
483 zval *object = ZEND_THIS;
484 spl_recursive_it_object *intern;
485 zval *iterator;
486 zend_class_entry *ce_iterator;
487 zend_long mode, flags;
488 zend_error_handling error_handling;
489 zval caching_it, aggregate_retval;
490
491 switch (rit_type) {
492 case RIT_RecursiveTreeIterator: {
493 zval caching_it_flags;
494 zend_long user_caching_it_flags = CIT_CATCH_GET_CHILD;
495 mode = RIT_SELF_FIRST;
496 flags = RTIT_BYPASS_KEY;
497
498 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|lll", &iterator, &flags, &user_caching_it_flags, &mode) == FAILURE) {
499 RETURN_THROWS();
500 }
501
502 zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
503 if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
504 if (spl_get_iterator_from_aggregate(
505 &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
506 RETURN_THROWS();
507 }
508 iterator = &aggregate_retval;
509 } else {
510 Z_ADDREF_P(iterator);
511 }
512
513 ZVAL_LONG(&caching_it_flags, user_caching_it_flags);
514 spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &caching_it, iterator, &caching_it_flags);
515 zval_ptr_dtor(&caching_it_flags);
516 zval_ptr_dtor(iterator);
517 iterator = &caching_it;
518 break;
519 }
520 case RIT_RecursiveIteratorIterator:
521 default: {
522 mode = RIT_LEAVES_ONLY;
523 flags = 0;
524 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|ll", &iterator, &mode, &flags) == FAILURE) {
525 RETURN_THROWS();
526 }
527
528 zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
529 if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
530 if (spl_get_iterator_from_aggregate(
531 &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
532 RETURN_THROWS();
533 }
534 iterator = &aggregate_retval;
535 } else {
536 Z_ADDREF_P(iterator);
537 }
538 break;
539 }
540 }
541 if (!instanceof_function(Z_OBJCE_P(iterator), spl_ce_RecursiveIterator)) {
542 if (iterator) {
543 zval_ptr_dtor(iterator);
544 }
545 zend_throw_exception(spl_ce_InvalidArgumentException, "An instance of RecursiveIterator or IteratorAggregate creating it is required", 0);
546 zend_restore_error_handling(&error_handling);
547 return;
548 }
549
550 intern = Z_SPLRECURSIVE_IT_P(object);
551 intern->iterators = emalloc(sizeof(spl_sub_iterator));
552 intern->level = 0;
553 intern->mode = mode;
554 intern->flags = (int)flags;
555 intern->max_depth = -1;
556 intern->in_iteration = 0;
557 intern->ce = Z_OBJCE_P(object);
558
559 intern->beginIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "beginiteration", sizeof("beginiteration") - 1);
560 if (intern->beginIteration->common.scope == ce_base) {
561 intern->beginIteration = NULL;
562 }
563 intern->endIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "enditeration", sizeof("enditeration") - 1);
564 if (intern->endIteration->common.scope == ce_base) {
565 intern->endIteration = NULL;
566 }
567 intern->callHasChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren") - 1);
568 if (intern->callHasChildren->common.scope == ce_base) {
569 intern->callHasChildren = NULL;
570 }
571 intern->callGetChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callgetchildren", sizeof("callGetChildren") - 1);
572 if (intern->callGetChildren->common.scope == ce_base) {
573 intern->callGetChildren = NULL;
574 }
575 intern->beginChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "beginchildren", sizeof("beginchildren") - 1);
576 if (intern->beginChildren->common.scope == ce_base) {
577 intern->beginChildren = NULL;
578 }
579 intern->endChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "endchildren", sizeof("endchildren") - 1);
580 if (intern->endChildren->common.scope == ce_base) {
581 intern->endChildren = NULL;
582 }
583 intern->nextElement = zend_hash_str_find_ptr(&intern->ce->function_table, "nextelement", sizeof("nextElement") - 1);
584 if (intern->nextElement->common.scope == ce_base) {
585 intern->nextElement = NULL;
586 }
587
588 ce_iterator = Z_OBJCE_P(iterator); /* respect inheritance, don't use spl_ce_RecursiveIterator */
589 intern->iterators[0].iterator = ce_iterator->get_iterator(ce_iterator, iterator, 0);
590 ZVAL_OBJ(&intern->iterators[0].zobject, Z_OBJ_P(iterator));
591 intern->iterators[0].ce = ce_iterator;
592 intern->iterators[0].state = RS_START;
593
594 zend_restore_error_handling(&error_handling);
595
596 if (EG(exception)) {
597 zend_object_iterator *sub_iter;
598
599 while (intern->level >= 0) {
600 sub_iter = intern->iterators[intern->level].iterator;
601 zend_iterator_dtor(sub_iter);
602 zval_ptr_dtor(&intern->iterators[intern->level--].zobject);
603 }
604 efree(intern->iterators);
605 intern->iterators = NULL;
606 }
607 }
608
609 /* {{{ Creates a RecursiveIteratorIterator from a RecursiveIterator. */
PHP_METHOD(RecursiveIteratorIterator,__construct)610 PHP_METHOD(RecursiveIteratorIterator, __construct)
611 {
612 spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveIteratorIterator, zend_ce_iterator, RIT_RecursiveIteratorIterator);
613 } /* }}} */
614
615 /* {{{ Rewind the iterator to the first element of the top level inner iterator. */
PHP_METHOD(RecursiveIteratorIterator,rewind)616 PHP_METHOD(RecursiveIteratorIterator, rewind)
617 {
618 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
619
620 if (zend_parse_parameters_none() == FAILURE) {
621 RETURN_THROWS();
622 }
623
624 spl_recursive_it_rewind_ex(object, ZEND_THIS);
625 } /* }}} */
626
627 /* {{{ Check whether the current position is valid */
PHP_METHOD(RecursiveIteratorIterator,valid)628 PHP_METHOD(RecursiveIteratorIterator, valid)
629 {
630 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
631
632 if (zend_parse_parameters_none() == FAILURE) {
633 RETURN_THROWS();
634 }
635
636 RETURN_BOOL(spl_recursive_it_valid_ex(object, ZEND_THIS) == SUCCESS);
637 } /* }}} */
638
639 /* {{{ Access the current key */
PHP_METHOD(RecursiveIteratorIterator,key)640 PHP_METHOD(RecursiveIteratorIterator, key)
641 {
642 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
643 zend_object_iterator *iterator;
644
645 if (zend_parse_parameters_none() == FAILURE) {
646 RETURN_THROWS();
647 }
648
649 SPL_FETCH_SUB_ITERATOR(iterator, object);
650
651 if (iterator->funcs->get_current_key) {
652 iterator->funcs->get_current_key(iterator, return_value);
653 } else {
654 RETURN_NULL();
655 }
656 } /* }}} */
657
658 /* {{{ Access the current element value */
PHP_METHOD(RecursiveIteratorIterator,current)659 PHP_METHOD(RecursiveIteratorIterator, current)
660 {
661 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
662 zend_object_iterator *iterator;
663 zval *data;
664
665 if (zend_parse_parameters_none() == FAILURE) {
666 RETURN_THROWS();
667 }
668
669 SPL_FETCH_SUB_ITERATOR(iterator, object);
670
671 data = iterator->funcs->get_current_data(iterator);
672 if (data) {
673 ZVAL_COPY_DEREF(return_value, data);
674 }
675 } /* }}} */
676
677 /* {{{ Move forward to the next element */
PHP_METHOD(RecursiveIteratorIterator,next)678 PHP_METHOD(RecursiveIteratorIterator, next)
679 {
680 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
681
682 if (zend_parse_parameters_none() == FAILURE) {
683 RETURN_THROWS();
684 }
685
686 spl_recursive_it_move_forward_ex(object, ZEND_THIS);
687 } /* }}} */
688
689 /* {{{ Get the current depth of the recursive iteration */
PHP_METHOD(RecursiveIteratorIterator,getDepth)690 PHP_METHOD(RecursiveIteratorIterator, getDepth)
691 {
692 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
693
694 if (zend_parse_parameters_none() == FAILURE) {
695 RETURN_THROWS();
696 }
697
698 RETURN_LONG(object->level);
699 } /* }}} */
700
701 /* {{{ The current active sub iterator or the iterator at specified level */
PHP_METHOD(RecursiveIteratorIterator,getSubIterator)702 PHP_METHOD(RecursiveIteratorIterator, getSubIterator)
703 {
704 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
705 zend_long level;
706 zend_bool level_is_null = 1;
707 zval *value;
708
709 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &level, &level_is_null) == FAILURE) {
710 RETURN_THROWS();
711 }
712
713 if (level_is_null) {
714 level = object->level;
715 } else if (level < 0 || level > object->level) {
716 RETURN_NULL();
717 }
718
719 if(!object->iterators) {
720 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
721 RETURN_THROWS();
722 }
723
724 value = &object->iterators[level].zobject;
725 ZVAL_COPY_DEREF(return_value, value);
726 } /* }}} */
727
728 /* {{{ The current active sub iterator */
PHP_METHOD(RecursiveIteratorIterator,getInnerIterator)729 PHP_METHOD(RecursiveIteratorIterator, getInnerIterator)
730 {
731 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
732 zval *zobject;
733
734 if (zend_parse_parameters_none() == FAILURE) {
735 RETURN_THROWS();
736 }
737
738 SPL_FETCH_SUB_ELEMENT_ADDR(zobject, object, zobject);
739
740 ZVAL_COPY_DEREF(return_value, zobject);
741 } /* }}} */
742
743 /* {{{ Called when iteration begins (after first rewind() call) */
PHP_METHOD(RecursiveIteratorIterator,beginIteration)744 PHP_METHOD(RecursiveIteratorIterator, beginIteration)
745 {
746 if (zend_parse_parameters_none() == FAILURE) {
747 RETURN_THROWS();
748 }
749 /* nothing to do */
750 } /* }}} */
751
752 /* {{{ Called when iteration ends (when valid() first returns false */
PHP_METHOD(RecursiveIteratorIterator,endIteration)753 PHP_METHOD(RecursiveIteratorIterator, endIteration)
754 {
755 if (zend_parse_parameters_none() == FAILURE) {
756 RETURN_THROWS();
757 }
758 /* nothing to do */
759 } /* }}} */
760
761 /* {{{ Called for each element to test whether it has children */
PHP_METHOD(RecursiveIteratorIterator,callHasChildren)762 PHP_METHOD(RecursiveIteratorIterator, callHasChildren)
763 {
764 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
765 zend_class_entry *ce;
766 zval *zobject;
767
768 if (zend_parse_parameters_none() == FAILURE) {
769 RETURN_THROWS();
770 }
771
772 if (!object->iterators) {
773 RETURN_FALSE;
774 }
775
776 SPL_FETCH_SUB_ELEMENT(ce, object, ce);
777
778 zobject = &object->iterators[object->level].zobject;
779 if (Z_TYPE_P(zobject) == IS_UNDEF) {
780 RETURN_FALSE;
781 } else {
782 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, NULL, "haschildren", return_value);
783 if (Z_TYPE_P(return_value) == IS_UNDEF) {
784 RETURN_FALSE;
785 }
786 }
787 } /* }}} */
788
789 /* {{{ Return children of current element */
PHP_METHOD(RecursiveIteratorIterator,callGetChildren)790 PHP_METHOD(RecursiveIteratorIterator, callGetChildren)
791 {
792 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
793 zend_class_entry *ce;
794 zval *zobject;
795
796 if (zend_parse_parameters_none() == FAILURE) {
797 RETURN_THROWS();
798 }
799
800 SPL_FETCH_SUB_ELEMENT(ce, object, ce);
801
802 zobject = &object->iterators[object->level].zobject;
803 if (Z_TYPE_P(zobject) == IS_UNDEF) {
804 return;
805 } else {
806 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, NULL, "getchildren", return_value);
807 if (Z_TYPE_P(return_value) == IS_UNDEF) {
808 RETURN_NULL();
809 }
810 }
811 } /* }}} */
812
813 /* {{{ Called when recursing one level down */
PHP_METHOD(RecursiveIteratorIterator,beginChildren)814 PHP_METHOD(RecursiveIteratorIterator, beginChildren)
815 {
816 if (zend_parse_parameters_none() == FAILURE) {
817 RETURN_THROWS();
818 }
819 /* nothing to do */
820 } /* }}} */
821
822 /* {{{ Called when end recursing one level */
PHP_METHOD(RecursiveIteratorIterator,endChildren)823 PHP_METHOD(RecursiveIteratorIterator, endChildren)
824 {
825 if (zend_parse_parameters_none() == FAILURE) {
826 RETURN_THROWS();
827 }
828 /* nothing to do */
829 } /* }}} */
830
831 /* {{{ Called when the next element is available */
PHP_METHOD(RecursiveIteratorIterator,nextElement)832 PHP_METHOD(RecursiveIteratorIterator, nextElement)
833 {
834 if (zend_parse_parameters_none() == FAILURE) {
835 RETURN_THROWS();
836 }
837 /* nothing to do */
838 } /* }}} */
839
840 /* {{{ Set the maximum allowed depth (or any depth if pmax_depth = -1] */
PHP_METHOD(RecursiveIteratorIterator,setMaxDepth)841 PHP_METHOD(RecursiveIteratorIterator, setMaxDepth)
842 {
843 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
844 zend_long max_depth = -1;
845
846 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max_depth) == FAILURE) {
847 RETURN_THROWS();
848 }
849 if (max_depth < -1) {
850 zend_argument_value_error(1, "must be greater than or equal to -1");
851 RETURN_THROWS();
852 } else if (max_depth > INT_MAX) {
853 max_depth = INT_MAX;
854 }
855
856 object->max_depth = (int)max_depth;
857 } /* }}} */
858
859 /* {{{ Return the maximum accepted depth or false if any depth is allowed */
PHP_METHOD(RecursiveIteratorIterator,getMaxDepth)860 PHP_METHOD(RecursiveIteratorIterator, getMaxDepth)
861 {
862 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
863
864 if (zend_parse_parameters_none() == FAILURE) {
865 RETURN_THROWS();
866 }
867
868 if (object->max_depth == -1) {
869 RETURN_FALSE;
870 } else {
871 RETURN_LONG(object->max_depth);
872 }
873 } /* }}} */
874
spl_recursive_it_get_method(zend_object ** zobject,zend_string * method,const zval * key)875 static zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_string *method, const zval *key)
876 {
877 zend_function *function_handler;
878 spl_recursive_it_object *object = spl_recursive_it_from_obj(*zobject);
879 zend_long level = object->level;
880 zval *zobj;
881
882 if (!object->iterators) {
883 zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name));
884 return NULL;
885 }
886 zobj = &object->iterators[level].zobject;
887
888 function_handler = zend_std_get_method(zobject, method, key);
889 if (!function_handler) {
890 if ((function_handler = zend_hash_find_ptr(&Z_OBJCE_P(zobj)->function_table, method)) == NULL) {
891 *zobject = Z_OBJ_P(zobj);
892 function_handler = (*zobject)->handlers->get_method(zobject, method, key);
893 } else {
894 *zobject = Z_OBJ_P(zobj);
895 }
896 }
897 return function_handler;
898 }
899
900 /* {{{ spl_RecursiveIteratorIterator_dtor */
spl_RecursiveIteratorIterator_dtor(zend_object * _object)901 static void spl_RecursiveIteratorIterator_dtor(zend_object *_object)
902 {
903 spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
904 zend_object_iterator *sub_iter;
905
906 /* call standard dtor */
907 zend_objects_destroy_object(_object);
908
909 if (object->iterators) {
910 while (object->level >= 0) {
911 sub_iter = object->iterators[object->level].iterator;
912 zend_iterator_dtor(sub_iter);
913 zval_ptr_dtor(&object->iterators[object->level--].zobject);
914 }
915 efree(object->iterators);
916 object->iterators = NULL;
917 }
918 }
919 /* }}} */
920
921 /* {{{ spl_RecursiveIteratorIterator_free_storage */
spl_RecursiveIteratorIterator_free_storage(zend_object * _object)922 static void spl_RecursiveIteratorIterator_free_storage(zend_object *_object)
923 {
924 spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
925
926 if (object->iterators) {
927 efree(object->iterators);
928 object->iterators = NULL;
929 object->level = 0;
930 }
931
932 zend_object_std_dtor(&object->std);
933 smart_str_free(&object->prefix[0]);
934 smart_str_free(&object->prefix[1]);
935 smart_str_free(&object->prefix[2]);
936 smart_str_free(&object->prefix[3]);
937 smart_str_free(&object->prefix[4]);
938 smart_str_free(&object->prefix[5]);
939
940 smart_str_free(&object->postfix[0]);
941 }
942 /* }}} */
943
944 /* {{{ spl_RecursiveIteratorIterator_new_ex */
spl_RecursiveIteratorIterator_new_ex(zend_class_entry * class_type,int init_prefix)945 static zend_object *spl_RecursiveIteratorIterator_new_ex(zend_class_entry *class_type, int init_prefix)
946 {
947 spl_recursive_it_object *intern;
948
949 intern = zend_object_alloc(sizeof(spl_recursive_it_object), class_type);
950
951 if (init_prefix) {
952 smart_str_appendl(&intern->prefix[0], "", 0);
953 smart_str_appendl(&intern->prefix[1], "| ", 2);
954 smart_str_appendl(&intern->prefix[2], " ", 2);
955 smart_str_appendl(&intern->prefix[3], "|-", 2);
956 smart_str_appendl(&intern->prefix[4], "\\-", 2);
957 smart_str_appendl(&intern->prefix[5], "", 0);
958
959 smart_str_appendl(&intern->postfix[0], "", 0);
960 }
961
962 zend_object_std_init(&intern->std, class_type);
963 object_properties_init(&intern->std, class_type);
964
965 intern->std.handlers = &spl_handlers_rec_it_it;
966 return &intern->std;
967 }
968 /* }}} */
969
970 /* {{{ spl_RecursiveIteratorIterator_new */
spl_RecursiveIteratorIterator_new(zend_class_entry * class_type)971 static zend_object *spl_RecursiveIteratorIterator_new(zend_class_entry *class_type)
972 {
973 return spl_RecursiveIteratorIterator_new_ex(class_type, 0);
974 }
975 /* }}} */
976
977 /* {{{ spl_RecursiveTreeIterator_new */
spl_RecursiveTreeIterator_new(zend_class_entry * class_type)978 static zend_object *spl_RecursiveTreeIterator_new(zend_class_entry *class_type)
979 {
980 return spl_RecursiveIteratorIterator_new_ex(class_type, 1);
981 }
982 /* }}} */
983
spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object * object,zval * return_value)984 static void spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object *object, zval *return_value)
985 {
986 smart_str str = {0};
987 zval has_next;
988 int level;
989
990 smart_str_appendl(&str, ZSTR_VAL(object->prefix[0].s), ZSTR_LEN(object->prefix[0].s));
991
992 for (level = 0; level < object->level; ++level) {
993 zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
994 if (Z_TYPE(has_next) != IS_UNDEF) {
995 if (Z_TYPE(has_next) == IS_TRUE) {
996 smart_str_appendl(&str, ZSTR_VAL(object->prefix[1].s), ZSTR_LEN(object->prefix[1].s));
997 } else {
998 smart_str_appendl(&str, ZSTR_VAL(object->prefix[2].s), ZSTR_LEN(object->prefix[2].s));
999 }
1000 zval_ptr_dtor(&has_next);
1001 }
1002 }
1003 zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
1004 if (Z_TYPE(has_next) != IS_UNDEF) {
1005 if (Z_TYPE(has_next) == IS_TRUE) {
1006 smart_str_appendl(&str, ZSTR_VAL(object->prefix[3].s), ZSTR_LEN(object->prefix[3].s));
1007 } else {
1008 smart_str_appendl(&str, ZSTR_VAL(object->prefix[4].s), ZSTR_LEN(object->prefix[4].s));
1009 }
1010 zval_ptr_dtor(&has_next);
1011 }
1012
1013 smart_str_appendl(&str, ZSTR_VAL(object->prefix[5].s), ZSTR_LEN(object->prefix[5].s));
1014 smart_str_0(&str);
1015
1016 RETURN_NEW_STR(str.s);
1017 }
1018
spl_recursive_tree_iterator_get_entry(spl_recursive_it_object * object,zval * return_value)1019 static void spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *object, zval *return_value)
1020 {
1021 zend_object_iterator *iterator = object->iterators[object->level].iterator;
1022 zval *data;
1023
1024 data = iterator->funcs->get_current_data(iterator);
1025 if (data) {
1026 ZVAL_DEREF(data);
1027 /* TODO: Remove this special case? */
1028 if (Z_TYPE_P(data) == IS_ARRAY) {
1029 RETVAL_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED));
1030 } else {
1031 ZVAL_COPY(return_value, data);
1032 convert_to_string(return_value);
1033 }
1034 }
1035 }
1036
spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object * object,zval * return_value)1037 static void spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object *object, zval *return_value)
1038 {
1039 RETVAL_STR(object->postfix[0].s);
1040 Z_ADDREF_P(return_value);
1041 }
1042
1043 /* {{{ RecursiveIteratorIterator to generate ASCII graphic trees for the entries in a RecursiveIterator */
PHP_METHOD(RecursiveTreeIterator,__construct)1044 PHP_METHOD(RecursiveTreeIterator, __construct)
1045 {
1046 spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveTreeIterator, zend_ce_iterator, RIT_RecursiveTreeIterator);
1047 } /* }}} */
1048
1049 /* {{{ Sets prefix parts as used in getPrefix() */
PHP_METHOD(RecursiveTreeIterator,setPrefixPart)1050 PHP_METHOD(RecursiveTreeIterator, setPrefixPart)
1051 {
1052 zend_long part;
1053 char* prefix;
1054 size_t prefix_len;
1055 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1056
1057 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &part, &prefix, &prefix_len) == FAILURE) {
1058 RETURN_THROWS();
1059 }
1060
1061 if (0 > part || part > 5) {
1062 zend_argument_value_error(1, "must be a RecursiveTreeIterator::PREFIX_* constant");
1063 RETURN_THROWS();
1064 }
1065
1066 smart_str_free(&object->prefix[part]);
1067 smart_str_appendl(&object->prefix[part], prefix, prefix_len);
1068 } /* }}} */
1069
1070 /* {{{ Returns the string to place in front of current element */
PHP_METHOD(RecursiveTreeIterator,getPrefix)1071 PHP_METHOD(RecursiveTreeIterator, getPrefix)
1072 {
1073 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1074
1075 if (zend_parse_parameters_none() == FAILURE) {
1076 RETURN_THROWS();
1077 }
1078
1079 if(!object->iterators) {
1080 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1081 RETURN_THROWS();
1082 }
1083
1084 spl_recursive_tree_iterator_get_prefix(object, return_value);
1085 } /* }}} */
1086
1087 /* {{{ Sets postfix as used in getPostfix() */
PHP_METHOD(RecursiveTreeIterator,setPostfix)1088 PHP_METHOD(RecursiveTreeIterator, setPostfix)
1089 {
1090 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1091 char* postfix;
1092 size_t postfix_len;
1093
1094 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &postfix, &postfix_len) == FAILURE) {
1095 RETURN_THROWS();
1096 }
1097
1098 smart_str_free(&object->postfix[0]);
1099 smart_str_appendl(&object->postfix[0], postfix, postfix_len);
1100 } /* }}} */
1101
1102 /* {{{ Returns the string presentation built for current element */
PHP_METHOD(RecursiveTreeIterator,getEntry)1103 PHP_METHOD(RecursiveTreeIterator, getEntry)
1104 {
1105 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1106
1107 if (zend_parse_parameters_none() == FAILURE) {
1108 RETURN_THROWS();
1109 }
1110
1111 if(!object->iterators) {
1112 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1113 RETURN_THROWS();
1114 }
1115
1116 spl_recursive_tree_iterator_get_entry(object, return_value);
1117 } /* }}} */
1118
1119 /* {{{ Returns the string to place after the current element */
PHP_METHOD(RecursiveTreeIterator,getPostfix)1120 PHP_METHOD(RecursiveTreeIterator, getPostfix)
1121 {
1122 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1123
1124 if (zend_parse_parameters_none() == FAILURE) {
1125 RETURN_THROWS();
1126 }
1127
1128 if(!object->iterators) {
1129 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1130 RETURN_THROWS();
1131 }
1132
1133 spl_recursive_tree_iterator_get_postfix(object, return_value);
1134 } /* }}} */
1135
1136 /* {{{ Returns the current element prefixed and postfixed */
PHP_METHOD(RecursiveTreeIterator,current)1137 PHP_METHOD(RecursiveTreeIterator, current)
1138 {
1139 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1140 zval prefix, entry, postfix;
1141 char *ptr;
1142 zend_string *str;
1143
1144 if (zend_parse_parameters_none() == FAILURE) {
1145 RETURN_THROWS();
1146 }
1147
1148 if(!object->iterators) {
1149 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1150 RETURN_THROWS();
1151 }
1152
1153 if (object->flags & RTIT_BYPASS_CURRENT) {
1154 zend_object_iterator *iterator = object->iterators[object->level].iterator;
1155 zval *data;
1156
1157 SPL_FETCH_SUB_ITERATOR(iterator, object);
1158 data = iterator->funcs->get_current_data(iterator);
1159 if (data) {
1160 ZVAL_COPY_DEREF(return_value, data);
1161 return;
1162 } else {
1163 RETURN_NULL();
1164 }
1165 }
1166
1167 ZVAL_NULL(&prefix);
1168 ZVAL_NULL(&entry);
1169 spl_recursive_tree_iterator_get_prefix(object, &prefix);
1170 spl_recursive_tree_iterator_get_entry(object, &entry);
1171 if (Z_TYPE(entry) != IS_STRING) {
1172 zval_ptr_dtor(&prefix);
1173 zval_ptr_dtor(&entry);
1174 RETURN_NULL();
1175 }
1176 spl_recursive_tree_iterator_get_postfix(object, &postfix);
1177
1178 str = zend_string_alloc(Z_STRLEN(prefix) + Z_STRLEN(entry) + Z_STRLEN(postfix), 0);
1179 ptr = ZSTR_VAL(str);
1180
1181 memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1182 ptr += Z_STRLEN(prefix);
1183 memcpy(ptr, Z_STRVAL(entry), Z_STRLEN(entry));
1184 ptr += Z_STRLEN(entry);
1185 memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1186 ptr += Z_STRLEN(postfix);
1187 *ptr = 0;
1188
1189 zval_ptr_dtor(&prefix);
1190 zval_ptr_dtor(&entry);
1191 zval_ptr_dtor(&postfix);
1192
1193 RETURN_NEW_STR(str);
1194 } /* }}} */
1195
1196 /* {{{ Returns the current key prefixed and postfixed */
PHP_METHOD(RecursiveTreeIterator,key)1197 PHP_METHOD(RecursiveTreeIterator, key)
1198 {
1199 spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1200 zend_object_iterator *iterator;
1201 zval prefix, key, postfix, key_copy;
1202 char *ptr;
1203 zend_string *str;
1204
1205 if (zend_parse_parameters_none() == FAILURE) {
1206 RETURN_THROWS();
1207 }
1208
1209 SPL_FETCH_SUB_ITERATOR(iterator, object);
1210
1211 if (iterator->funcs->get_current_key) {
1212 iterator->funcs->get_current_key(iterator, &key);
1213 } else {
1214 ZVAL_NULL(&key);
1215 }
1216
1217 if (object->flags & RTIT_BYPASS_KEY) {
1218 RETURN_COPY_VALUE(&key);
1219 }
1220
1221 if (Z_TYPE(key) != IS_STRING) {
1222 if (zend_make_printable_zval(&key, &key_copy)) {
1223 key = key_copy;
1224 }
1225 }
1226
1227 spl_recursive_tree_iterator_get_prefix(object, &prefix);
1228 spl_recursive_tree_iterator_get_postfix(object, &postfix);
1229
1230 str = zend_string_alloc(Z_STRLEN(prefix) + Z_STRLEN(key) + Z_STRLEN(postfix), 0);
1231 ptr = ZSTR_VAL(str);
1232
1233 memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1234 ptr += Z_STRLEN(prefix);
1235 memcpy(ptr, Z_STRVAL(key), Z_STRLEN(key));
1236 ptr += Z_STRLEN(key);
1237 memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1238 ptr += Z_STRLEN(postfix);
1239 *ptr = 0;
1240
1241 zval_ptr_dtor(&prefix);
1242 zval_ptr_dtor(&key);
1243 zval_ptr_dtor(&postfix);
1244
1245 RETURN_NEW_STR(str);
1246 } /* }}} */
1247
spl_dual_it_get_method(zend_object ** object,zend_string * method,const zval * key)1248 static zend_function *spl_dual_it_get_method(zend_object **object, zend_string *method, const zval *key)
1249 {
1250 zend_function *function_handler;
1251 spl_dual_it_object *intern;
1252
1253 intern = spl_dual_it_from_obj(*object);
1254
1255 function_handler = zend_std_get_method(object, method, key);
1256 if (!function_handler && intern->inner.ce) {
1257 if ((function_handler = zend_hash_find_ptr(&intern->inner.ce->function_table, method)) == NULL) {
1258 if (Z_OBJ_HT(intern->inner.zobject)->get_method) {
1259 *object = Z_OBJ(intern->inner.zobject);
1260 function_handler = (*object)->handlers->get_method(object, method, key);
1261 }
1262 } else {
1263 *object = Z_OBJ(intern->inner.zobject);
1264 }
1265 }
1266 return function_handler;
1267 }
1268
1269 #define SPL_CHECK_CTOR(intern, classname) \
1270 if (intern->dit_type == DIT_Unknown) { \
1271 /* TODO Normal Error? */ \
1272 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Classes derived from %s must call %s::__construct()", \
1273 ZSTR_VAL((spl_ce_##classname)->name), ZSTR_VAL((spl_ce_##classname)->name)); \
1274 RETURN_THROWS(); \
1275 }
1276
1277 #define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
1278
1279 static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more);
1280
spl_cit_check_flags(zend_long flags)1281 static inline int spl_cit_check_flags(zend_long flags)
1282 {
1283 zend_long cnt = 0;
1284
1285 cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
1286 cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
1287 cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
1288 cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
1289
1290 return cnt <= 1 ? SUCCESS : FAILURE;
1291 }
1292
spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * ce_base,zend_class_entry * ce_inner,dual_it_type dit_type)1293 static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, dual_it_type dit_type)
1294 {
1295 zval *zobject, retval;
1296 spl_dual_it_object *intern;
1297 zend_class_entry *ce = NULL;
1298 int inc_refcount = 1;
1299 zend_error_handling error_handling;
1300
1301 intern = Z_SPLDUAL_IT_P(ZEND_THIS);
1302
1303 if (intern->dit_type != DIT_Unknown) {
1304 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(ce_base->name));
1305 return NULL;
1306 }
1307
1308 switch (dit_type) {
1309 case DIT_LimitIterator: {
1310 intern->u.limit.offset = 0; /* start at beginning */
1311 intern->u.limit.count = -1; /* get all */
1312 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|ll", &zobject, ce_inner, &intern->u.limit.offset, &intern->u.limit.count) == FAILURE) {
1313 return NULL;
1314 }
1315 if (intern->u.limit.offset < 0) {
1316 zend_argument_value_error(2, "must be greater than or equal to 0");
1317 return NULL;
1318 }
1319 if (intern->u.limit.count < -1) {
1320 zend_argument_value_error(3, "must be greater than or equal to -1");
1321 return NULL;
1322 }
1323 break;
1324 }
1325 case DIT_CachingIterator:
1326 case DIT_RecursiveCachingIterator: {
1327 zend_long flags = CIT_CALL_TOSTRING;
1328 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &zobject, ce_inner, &flags) == FAILURE) {
1329 return NULL;
1330 }
1331 if (spl_cit_check_flags(flags) != SUCCESS) {
1332 zend_argument_value_error(2, "must contain only one of CachingIterator::CALL_TOSTRING, "
1333 "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
1334 "or CachingIterator::TOSTRING_USE_INNER");
1335 return NULL;
1336 }
1337 intern->u.caching.flags |= flags & CIT_PUBLIC;
1338 array_init(&intern->u.caching.zcache);
1339 break;
1340 }
1341 case DIT_IteratorIterator: {
1342 zend_class_entry *ce_cast;
1343 zend_string *class_name = NULL;
1344
1345 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|S!", &zobject, ce_inner, &class_name) == FAILURE) {
1346 return NULL;
1347 }
1348 ce = Z_OBJCE_P(zobject);
1349 if (!instanceof_function(ce, zend_ce_iterator)) {
1350 if (class_name) {
1351 if (!(ce_cast = zend_lookup_class(class_name))
1352 || !instanceof_function(ce, ce_cast)
1353 || !ce_cast->get_iterator
1354 ) {
1355 zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0);
1356 return NULL;
1357 }
1358 ce = ce_cast;
1359 }
1360 if (instanceof_function(ce, zend_ce_aggregate)) {
1361 if (spl_get_iterator_from_aggregate(&retval, ce, Z_OBJ_P(zobject)) == FAILURE) {
1362 return NULL;
1363 }
1364 zobject = &retval;
1365 ce = Z_OBJCE_P(zobject);
1366 inc_refcount = 0;
1367 }
1368 }
1369 break;
1370 }
1371 case DIT_AppendIterator:
1372 if (zend_parse_parameters_none() == FAILURE) {
1373 return NULL;
1374 }
1375 intern->dit_type = DIT_AppendIterator;
1376 zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
1377 object_init_ex(&intern->u.append.zarrayit, spl_ce_ArrayIterator);
1378 zend_call_method_with_0_params(Z_OBJ(intern->u.append.zarrayit), spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
1379 intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
1380 zend_restore_error_handling(&error_handling);
1381 return intern;
1382 case DIT_RegexIterator:
1383 case DIT_RecursiveRegexIterator: {
1384 zend_string *regex;
1385 zend_long mode = REGIT_MODE_MATCH;
1386
1387 intern->u.regex.use_flags = ZEND_NUM_ARGS() >= 5;
1388 intern->u.regex.flags = 0;
1389 intern->u.regex.preg_flags = 0;
1390 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|lll", &zobject, ce_inner, ®ex, &mode, &intern->u.regex.flags, &intern->u.regex.preg_flags) == FAILURE) {
1391 return NULL;
1392 }
1393 if (mode < 0 || mode >= REGIT_MODE_MAX) {
1394 zend_argument_value_error(3, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1395 "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1396 return NULL;
1397 }
1398
1399 zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
1400 intern->u.regex.pce = pcre_get_compiled_regex_cache(regex);
1401 zend_restore_error_handling(&error_handling);
1402
1403 if (intern->u.regex.pce == NULL) {
1404 /* pcre_get_compiled_regex_cache has already sent error */
1405 return NULL;
1406 }
1407 intern->u.regex.mode = mode;
1408 intern->u.regex.regex = zend_string_copy(regex);
1409 php_pcre_pce_incref(intern->u.regex.pce);
1410 break;
1411 }
1412 case DIT_CallbackFilterIterator:
1413 case DIT_RecursiveCallbackFilterIterator: {
1414 _spl_cbfilter_it_intern *cfi = emalloc(sizeof(*cfi));
1415 cfi->fci.object = NULL;
1416 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of", &zobject, ce_inner, &cfi->fci, &cfi->fcc) == FAILURE) {
1417 efree(cfi);
1418 return NULL;
1419 }
1420 Z_TRY_ADDREF(cfi->fci.function_name);
1421 cfi->object = cfi->fcc.object;
1422 if (cfi->object) GC_ADDREF(cfi->object);
1423 intern->u.cbfilter = cfi;
1424 break;
1425 }
1426 default:
1427 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zobject, ce_inner) == FAILURE) {
1428 return NULL;
1429 }
1430 break;
1431 }
1432
1433 intern->dit_type = dit_type;
1434 if (inc_refcount) {
1435 Z_ADDREF_P(zobject);
1436 }
1437 ZVAL_OBJ(&intern->inner.zobject, Z_OBJ_P(zobject));
1438
1439 intern->inner.ce = dit_type == DIT_IteratorIterator ? ce : Z_OBJCE_P(zobject);
1440 intern->inner.object = Z_OBJ_P(zobject);
1441 intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, zobject, 0);
1442
1443 return intern;
1444 }
1445
1446 /* {{{ Create an Iterator from another iterator */
PHP_METHOD(FilterIterator,__construct)1447 PHP_METHOD(FilterIterator, __construct)
1448 {
1449 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_FilterIterator, zend_ce_iterator, DIT_FilterIterator);
1450 } /* }}} */
1451
1452 /* {{{ Create an Iterator from another iterator */
PHP_METHOD(CallbackFilterIterator,__construct)1453 PHP_METHOD(CallbackFilterIterator, __construct)
1454 {
1455 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CallbackFilterIterator, zend_ce_iterator, DIT_CallbackFilterIterator);
1456 } /* }}} */
1457
1458 /* {{{ Get the inner iterator */
PHP_METHOD(IteratorIterator,getInnerIterator)1459 PHP_METHOD(IteratorIterator, getInnerIterator)
1460 {
1461 spl_dual_it_object *intern;
1462
1463 if (zend_parse_parameters_none() == FAILURE) {
1464 RETURN_THROWS();
1465 }
1466
1467 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1468
1469 if (!Z_ISUNDEF(intern->inner.zobject)) {
1470 zval *value = &intern->inner.zobject;
1471
1472 ZVAL_COPY_DEREF(return_value, value);
1473 } else {
1474 RETURN_NULL();
1475 }
1476 } /* }}} */
1477
spl_dual_it_free(spl_dual_it_object * intern)1478 static inline void spl_dual_it_free(spl_dual_it_object *intern)
1479 {
1480 if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
1481 intern->inner.iterator->funcs->invalidate_current(intern->inner.iterator);
1482 }
1483 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1484 zval_ptr_dtor(&intern->current.data);
1485 ZVAL_UNDEF(&intern->current.data);
1486 }
1487 if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1488 zval_ptr_dtor(&intern->current.key);
1489 ZVAL_UNDEF(&intern->current.key);
1490 }
1491 if (intern->dit_type == DIT_CachingIterator || intern->dit_type == DIT_RecursiveCachingIterator) {
1492 if (Z_TYPE(intern->u.caching.zstr) != IS_UNDEF) {
1493 zval_ptr_dtor(&intern->u.caching.zstr);
1494 ZVAL_UNDEF(&intern->u.caching.zstr);
1495 }
1496 if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
1497 zval_ptr_dtor(&intern->u.caching.zchildren);
1498 ZVAL_UNDEF(&intern->u.caching.zchildren);
1499 }
1500 }
1501 }
1502
spl_dual_it_rewind(spl_dual_it_object * intern)1503 static inline void spl_dual_it_rewind(spl_dual_it_object *intern)
1504 {
1505 spl_dual_it_free(intern);
1506 intern->current.pos = 0;
1507 if (intern->inner.iterator && intern->inner.iterator->funcs->rewind) {
1508 intern->inner.iterator->funcs->rewind(intern->inner.iterator);
1509 }
1510 }
1511
spl_dual_it_valid(spl_dual_it_object * intern)1512 static inline int spl_dual_it_valid(spl_dual_it_object *intern)
1513 {
1514 if (!intern->inner.iterator) {
1515 return FAILURE;
1516 }
1517 /* FAILURE / SUCCESS */
1518 return intern->inner.iterator->funcs->valid(intern->inner.iterator);
1519 }
1520
spl_dual_it_fetch(spl_dual_it_object * intern,int check_more)1521 static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more)
1522 {
1523 zval *data;
1524
1525 spl_dual_it_free(intern);
1526 if (!check_more || spl_dual_it_valid(intern) == SUCCESS) {
1527 data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
1528 if (data) {
1529 ZVAL_COPY(&intern->current.data, data);
1530 }
1531
1532 if (intern->inner.iterator->funcs->get_current_key) {
1533 intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &intern->current.key);
1534 if (EG(exception)) {
1535 zval_ptr_dtor(&intern->current.key);
1536 ZVAL_UNDEF(&intern->current.key);
1537 }
1538 } else {
1539 ZVAL_LONG(&intern->current.key, intern->current.pos);
1540 }
1541 return EG(exception) ? FAILURE : SUCCESS;
1542 }
1543 return FAILURE;
1544 }
1545
spl_dual_it_next(spl_dual_it_object * intern,int do_free)1546 static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free)
1547 {
1548 if (do_free) {
1549 spl_dual_it_free(intern);
1550 } else if (!intern->inner.iterator) {
1551 zend_throw_error(NULL, "The inner constructor wasn't initialized with an iterator instance");
1552 return;
1553 }
1554 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1555 intern->current.pos++;
1556 }
1557
1558 /* {{{ Rewind the iterator */
PHP_METHOD(IteratorIterator,rewind)1559 PHP_METHOD(IteratorIterator, rewind)
1560 {
1561 spl_dual_it_object *intern;
1562
1563 if (zend_parse_parameters_none() == FAILURE) {
1564 RETURN_THROWS();
1565 }
1566
1567 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1568
1569 spl_dual_it_rewind(intern);
1570 spl_dual_it_fetch(intern, 1);
1571 } /* }}} */
1572
1573 /* {{{ Check whether the current element is valid */
PHP_METHOD(IteratorIterator,valid)1574 PHP_METHOD(IteratorIterator, valid)
1575 {
1576 spl_dual_it_object *intern;
1577
1578 if (zend_parse_parameters_none() == FAILURE) {
1579 RETURN_THROWS();
1580 }
1581
1582 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1583
1584 RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
1585 } /* }}} */
1586
1587 /* {{{ Get the current key */
PHP_METHOD(IteratorIterator,key)1588 PHP_METHOD(IteratorIterator, key)
1589 {
1590 spl_dual_it_object *intern;
1591
1592 if (zend_parse_parameters_none() == FAILURE) {
1593 RETURN_THROWS();
1594 }
1595
1596 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1597
1598 if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1599 zval *value = &intern->current.key;
1600
1601 ZVAL_COPY_DEREF(return_value, value);
1602 } else {
1603 RETURN_NULL();
1604 }
1605 } /* }}} */
1606
1607 /* {{{ Get the current element value */
PHP_METHOD(IteratorIterator,current)1608 PHP_METHOD(IteratorIterator, current)
1609 {
1610 spl_dual_it_object *intern;
1611
1612 if (zend_parse_parameters_none() == FAILURE) {
1613 RETURN_THROWS();
1614 }
1615
1616 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1617
1618 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1619 zval *value = &intern->current.data;
1620
1621 ZVAL_COPY_DEREF(return_value, value);
1622 } else {
1623 RETURN_NULL();
1624 }
1625 } /* }}} */
1626
1627 /* {{{ Move the iterator forward */
PHP_METHOD(IteratorIterator,next)1628 PHP_METHOD(IteratorIterator, next)
1629 {
1630 spl_dual_it_object *intern;
1631
1632 if (zend_parse_parameters_none() == FAILURE) {
1633 RETURN_THROWS();
1634 }
1635
1636 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1637
1638 spl_dual_it_next(intern, 1);
1639 spl_dual_it_fetch(intern, 1);
1640 } /* }}} */
1641
spl_filter_it_fetch(zval * zthis,spl_dual_it_object * intern)1642 static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern)
1643 {
1644 zval retval;
1645
1646 while (spl_dual_it_fetch(intern, 1) == SUCCESS) {
1647 zend_call_method_with_0_params(Z_OBJ_P(zthis), intern->std.ce, NULL, "accept", &retval);
1648 if (Z_TYPE(retval) != IS_UNDEF) {
1649 if (zend_is_true(&retval)) {
1650 zval_ptr_dtor(&retval);
1651 return;
1652 }
1653 zval_ptr_dtor(&retval);
1654 }
1655 if (EG(exception)) {
1656 return;
1657 }
1658 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1659 }
1660 spl_dual_it_free(intern);
1661 }
1662
spl_filter_it_rewind(zval * zthis,spl_dual_it_object * intern)1663 static inline void spl_filter_it_rewind(zval *zthis, spl_dual_it_object *intern)
1664 {
1665 spl_dual_it_rewind(intern);
1666 spl_filter_it_fetch(zthis, intern);
1667 }
1668
spl_filter_it_next(zval * zthis,spl_dual_it_object * intern)1669 static inline void spl_filter_it_next(zval *zthis, spl_dual_it_object *intern)
1670 {
1671 spl_dual_it_next(intern, 1);
1672 spl_filter_it_fetch(zthis, intern);
1673 }
1674
1675 /* {{{ Rewind the iterator */
PHP_METHOD(FilterIterator,rewind)1676 PHP_METHOD(FilterIterator, rewind)
1677 {
1678 spl_dual_it_object *intern;
1679
1680 if (zend_parse_parameters_none() == FAILURE) {
1681 RETURN_THROWS();
1682 }
1683
1684 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1685 spl_filter_it_rewind(ZEND_THIS, intern);
1686 } /* }}} */
1687
1688 /* {{{ Move the iterator forward */
PHP_METHOD(FilterIterator,next)1689 PHP_METHOD(FilterIterator, next)
1690 {
1691 spl_dual_it_object *intern;
1692
1693 if (zend_parse_parameters_none() == FAILURE) {
1694 RETURN_THROWS();
1695 }
1696
1697 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1698 spl_filter_it_next(ZEND_THIS, intern);
1699 } /* }}} */
1700
1701 /* {{{ Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
PHP_METHOD(RecursiveCallbackFilterIterator,__construct)1702 PHP_METHOD(RecursiveCallbackFilterIterator, __construct)
1703 {
1704 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCallbackFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveCallbackFilterIterator);
1705 } /* }}} */
1706
1707
1708 /* {{{ Create a RecursiveFilterIterator from a RecursiveIterator */
PHP_METHOD(RecursiveFilterIterator,__construct)1709 PHP_METHOD(RecursiveFilterIterator, __construct)
1710 {
1711 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveFilterIterator);
1712 } /* }}} */
1713
1714 /* {{{ Check whether the inner iterator's current element has children */
PHP_METHOD(RecursiveFilterIterator,hasChildren)1715 PHP_METHOD(RecursiveFilterIterator, hasChildren)
1716 {
1717 spl_dual_it_object *intern;
1718
1719 if (zend_parse_parameters_none() == FAILURE) {
1720 RETURN_THROWS();
1721 }
1722
1723 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1724
1725 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", return_value);
1726 } /* }}} */
1727
1728 /* {{{ Return the inner iterator's children contained in a RecursiveFilterIterator */
PHP_METHOD(RecursiveFilterIterator,getChildren)1729 PHP_METHOD(RecursiveFilterIterator, getChildren)
1730 {
1731 spl_dual_it_object *intern;
1732 zval retval;
1733
1734 if (zend_parse_parameters_none() == FAILURE) {
1735 RETURN_THROWS();
1736 }
1737
1738 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1739
1740 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
1741 if (!EG(exception) && Z_TYPE(retval) != IS_UNDEF) {
1742 spl_instantiate_arg_ex1(Z_OBJCE_P(ZEND_THIS), return_value, &retval);
1743 }
1744 zval_ptr_dtor(&retval);
1745 } /* }}} */
1746
1747 /* {{{ Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
PHP_METHOD(RecursiveCallbackFilterIterator,getChildren)1748 PHP_METHOD(RecursiveCallbackFilterIterator, getChildren)
1749 {
1750 spl_dual_it_object *intern;
1751 zval retval;
1752
1753 if (zend_parse_parameters_none() == FAILURE) {
1754 RETURN_THROWS();
1755 }
1756
1757 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1758
1759 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
1760 if (!EG(exception) && Z_TYPE(retval) != IS_UNDEF) {
1761 spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, &retval, &intern->u.cbfilter->fci.function_name);
1762 }
1763 zval_ptr_dtor(&retval);
1764 } /* }}} */
1765 /* {{{ Create a ParentIterator from a RecursiveIterator */
PHP_METHOD(ParentIterator,__construct)1766 PHP_METHOD(ParentIterator, __construct)
1767 {
1768 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_ParentIterator, spl_ce_RecursiveIterator, DIT_ParentIterator);
1769 } /* }}} */
1770
1771 /* {{{ Create an RegexIterator from another iterator and a regular expression */
PHP_METHOD(RegexIterator,__construct)1772 PHP_METHOD(RegexIterator, __construct)
1773 {
1774 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RegexIterator, zend_ce_iterator, DIT_RegexIterator);
1775 } /* }}} */
1776
1777 /* {{{ Calls the callback with the current value, the current key and the inner iterator as arguments */
PHP_METHOD(CallbackFilterIterator,accept)1778 PHP_METHOD(CallbackFilterIterator, accept)
1779 {
1780 spl_dual_it_object *intern = Z_SPLDUAL_IT_P(ZEND_THIS);
1781
1782 if (zend_parse_parameters_none() == FAILURE) {
1783 RETURN_THROWS();
1784 }
1785
1786 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1787
1788 if (Z_TYPE(intern->current.data) == IS_UNDEF || Z_TYPE(intern->current.key) == IS_UNDEF) {
1789 RETURN_FALSE;
1790 }
1791
1792 zval params[3];
1793 ZVAL_COPY_VALUE(¶ms[0], &intern->current.data);
1794 ZVAL_COPY_VALUE(¶ms[1], &intern->current.key);
1795 ZVAL_COPY_VALUE(¶ms[2], &intern->inner.zobject);
1796
1797 zend_fcall_info *fci = &intern->u.cbfilter->fci;
1798 zend_fcall_info_cache *fcc = &intern->u.cbfilter->fcc;
1799 fci->retval = return_value;
1800 fci->param_count = 3;
1801 fci->params = params;
1802
1803 if (zend_call_function(fci, fcc) != SUCCESS || Z_ISUNDEF_P(return_value)) {
1804 RETURN_FALSE;
1805 }
1806 }
1807 /* }}} */
1808
1809 /* {{{ Match (string)current() against regular expression */
PHP_METHOD(RegexIterator,accept)1810 PHP_METHOD(RegexIterator, accept)
1811 {
1812 spl_dual_it_object *intern;
1813 zend_string *result, *subject;
1814 size_t count = 0;
1815 zval zcount, rv;
1816 pcre2_match_data *match_data;
1817 pcre2_code *re;
1818 int rc;
1819
1820 if (zend_parse_parameters_none() == FAILURE) {
1821 RETURN_THROWS();
1822 }
1823
1824 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1825
1826 if (Z_TYPE(intern->current.data) == IS_UNDEF) {
1827 RETURN_FALSE;
1828 }
1829
1830 if (intern->u.regex.flags & REGIT_USE_KEY) {
1831 subject = zval_get_string(&intern->current.key);
1832 } else {
1833 if (Z_TYPE(intern->current.data) == IS_ARRAY) {
1834 RETURN_FALSE;
1835 }
1836 subject = zval_get_string(&intern->current.data);
1837 }
1838
1839 /* Exception during string conversion. */
1840 if (EG(exception)) {
1841 RETURN_THROWS();
1842 }
1843
1844 switch (intern->u.regex.mode)
1845 {
1846 case REGIT_MODE_MAX: /* won't happen but makes compiler happy */
1847 case REGIT_MODE_MATCH:
1848 re = php_pcre_pce_re(intern->u.regex.pce);
1849 match_data = php_pcre_create_match_data(0, re);
1850 if (!match_data) {
1851 RETURN_FALSE;
1852 }
1853 rc = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(subject), ZSTR_LEN(subject), 0, 0, match_data, php_pcre_mctx());
1854 RETVAL_BOOL(rc >= 0);
1855 php_pcre_free_match_data(match_data);
1856 break;
1857
1858 case REGIT_MODE_ALL_MATCHES:
1859 case REGIT_MODE_GET_MATCH:
1860 zval_ptr_dtor(&intern->current.data);
1861 ZVAL_UNDEF(&intern->current.data);
1862 php_pcre_match_impl(intern->u.regex.pce, subject, &zcount,
1863 &intern->current.data, intern->u.regex.mode == REGIT_MODE_ALL_MATCHES, intern->u.regex.use_flags, intern->u.regex.preg_flags, 0);
1864 RETVAL_BOOL(Z_LVAL(zcount) > 0);
1865 break;
1866
1867 case REGIT_MODE_SPLIT:
1868 zval_ptr_dtor(&intern->current.data);
1869 ZVAL_UNDEF(&intern->current.data);
1870 php_pcre_split_impl(intern->u.regex.pce, subject, &intern->current.data, -1, intern->u.regex.preg_flags);
1871 count = zend_hash_num_elements(Z_ARRVAL(intern->current.data));
1872 RETVAL_BOOL(count > 1);
1873 break;
1874
1875 case REGIT_MODE_REPLACE: {
1876 zval *replacement = zend_read_property(intern->std.ce, Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv);
1877 zend_string *replacement_str = zval_try_get_string(replacement);
1878 if (UNEXPECTED(!replacement_str)) {
1879 return;
1880 }
1881
1882 result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count);
1883
1884 if (intern->u.regex.flags & REGIT_USE_KEY) {
1885 zval_ptr_dtor(&intern->current.key);
1886 ZVAL_STR(&intern->current.key, result);
1887 } else {
1888 zval_ptr_dtor(&intern->current.data);
1889 ZVAL_STR(&intern->current.data, result);
1890 }
1891
1892 zend_string_release(replacement_str);
1893 RETVAL_BOOL(count > 0);
1894 }
1895 }
1896
1897 if (intern->u.regex.flags & REGIT_INVERTED) {
1898 RETVAL_BOOL(Z_TYPE_P(return_value) != IS_TRUE);
1899 }
1900 zend_string_release_ex(subject, 0);
1901 } /* }}} */
1902
1903 /* {{{ Returns current regular expression */
PHP_METHOD(RegexIterator,getRegex)1904 PHP_METHOD(RegexIterator, getRegex)
1905 {
1906 spl_dual_it_object *intern = Z_SPLDUAL_IT_P(ZEND_THIS);
1907
1908 if (zend_parse_parameters_none() == FAILURE) {
1909 RETURN_THROWS();
1910 }
1911
1912 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1913
1914 RETURN_STR_COPY(intern->u.regex.regex);
1915 } /* }}} */
1916
1917 /* {{{ Returns current operation mode */
PHP_METHOD(RegexIterator,getMode)1918 PHP_METHOD(RegexIterator, getMode)
1919 {
1920 spl_dual_it_object *intern;
1921
1922 if (zend_parse_parameters_none() == FAILURE) {
1923 RETURN_THROWS();
1924 }
1925
1926 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1927
1928 RETURN_LONG(intern->u.regex.mode);
1929 } /* }}} */
1930
1931 /* {{{ Set new operation mode */
PHP_METHOD(RegexIterator,setMode)1932 PHP_METHOD(RegexIterator, setMode)
1933 {
1934 spl_dual_it_object *intern;
1935 zend_long mode;
1936
1937 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &mode) == FAILURE) {
1938 RETURN_THROWS();
1939 }
1940
1941 if (mode < 0 || mode >= REGIT_MODE_MAX) {
1942 zend_argument_value_error(1, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1943 "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1944 RETURN_THROWS();
1945 }
1946
1947 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1948
1949 intern->u.regex.mode = mode;
1950 } /* }}} */
1951
1952 /* {{{ Returns current operation flags */
PHP_METHOD(RegexIterator,getFlags)1953 PHP_METHOD(RegexIterator, getFlags)
1954 {
1955 spl_dual_it_object *intern;
1956
1957 if (zend_parse_parameters_none() == FAILURE) {
1958 RETURN_THROWS();
1959 }
1960
1961 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1962
1963 RETURN_LONG(intern->u.regex.flags);
1964 } /* }}} */
1965
1966 /* {{{ Set operation flags */
PHP_METHOD(RegexIterator,setFlags)1967 PHP_METHOD(RegexIterator, setFlags)
1968 {
1969 spl_dual_it_object *intern;
1970 zend_long flags;
1971
1972 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
1973 RETURN_THROWS();
1974 }
1975
1976 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1977
1978 intern->u.regex.flags = flags;
1979 } /* }}} */
1980
1981 /* {{{ Returns current PREG flags (if in use or NULL) */
PHP_METHOD(RegexIterator,getPregFlags)1982 PHP_METHOD(RegexIterator, getPregFlags)
1983 {
1984 spl_dual_it_object *intern;
1985
1986 if (zend_parse_parameters_none() == FAILURE) {
1987 RETURN_THROWS();
1988 }
1989
1990 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1991
1992 if (intern->u.regex.use_flags) {
1993 RETURN_LONG(intern->u.regex.preg_flags);
1994 } else {
1995 RETURN_LONG(0);
1996 }
1997 } /* }}} */
1998
1999 /* {{{ Set PREG flags */
PHP_METHOD(RegexIterator,setPregFlags)2000 PHP_METHOD(RegexIterator, setPregFlags)
2001 {
2002 spl_dual_it_object *intern;
2003 zend_long preg_flags;
2004
2005 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &preg_flags) == FAILURE) {
2006 RETURN_THROWS();
2007 }
2008
2009 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2010
2011 intern->u.regex.preg_flags = preg_flags;
2012 intern->u.regex.use_flags = 1;
2013 } /* }}} */
2014
2015 /* {{{ Create an RecursiveRegexIterator from another recursive iterator and a regular expression */
PHP_METHOD(RecursiveRegexIterator,__construct)2016 PHP_METHOD(RecursiveRegexIterator, __construct)
2017 {
2018 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveRegexIterator, spl_ce_RecursiveIterator, DIT_RecursiveRegexIterator);
2019 } /* }}} */
2020
2021 /* {{{ Return the inner iterator's children contained in a RecursiveRegexIterator */
PHP_METHOD(RecursiveRegexIterator,getChildren)2022 PHP_METHOD(RecursiveRegexIterator, getChildren)
2023 {
2024 spl_dual_it_object *intern;
2025 zval retval;
2026
2027 if (zend_parse_parameters_none() == FAILURE) {
2028 RETURN_THROWS();
2029 }
2030
2031 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2032
2033 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
2034 if (!EG(exception)) {
2035 zval args[5];
2036
2037 ZVAL_COPY(&args[0], &retval);
2038 ZVAL_STR_COPY(&args[1], intern->u.regex.regex);
2039 ZVAL_LONG(&args[2], intern->u.regex.mode);
2040 ZVAL_LONG(&args[3], intern->u.regex.flags);
2041 ZVAL_LONG(&args[4], intern->u.regex.preg_flags);
2042
2043 spl_instantiate_arg_n(Z_OBJCE_P(ZEND_THIS), return_value, 5, args);
2044
2045 zval_ptr_dtor(&args[0]);
2046 zval_ptr_dtor(&args[1]);
2047 }
2048 zval_ptr_dtor(&retval);
2049 } /* }}} */
2050
PHP_METHOD(RecursiveRegexIterator,accept)2051 PHP_METHOD(RecursiveRegexIterator, accept)
2052 {
2053 spl_dual_it_object *intern;
2054
2055 if (zend_parse_parameters_none() == FAILURE) {
2056 RETURN_THROWS();
2057 }
2058
2059 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2060
2061 if (Z_TYPE(intern->current.data) == IS_UNDEF) {
2062 RETURN_FALSE;
2063 } else if (Z_TYPE(intern->current.data) == IS_ARRAY) {
2064 RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL(intern->current.data)) > 0);
2065 }
2066
2067 zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), spl_ce_RegexIterator, NULL, "accept", return_value);
2068 }
2069
2070 /* {{{ spl_dual_it_dtor */
spl_dual_it_dtor(zend_object * _object)2071 static void spl_dual_it_dtor(zend_object *_object)
2072 {
2073 spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2074
2075 /* call standard dtor */
2076 zend_objects_destroy_object(_object);
2077
2078 spl_dual_it_free(object);
2079
2080 if (object->inner.iterator) {
2081 zend_iterator_dtor(object->inner.iterator);
2082 }
2083 }
2084 /* }}} */
2085
2086 /* {{{ spl_dual_it_free_storage */
spl_dual_it_free_storage(zend_object * _object)2087 static void spl_dual_it_free_storage(zend_object *_object)
2088 {
2089 spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2090
2091
2092 if (!Z_ISUNDEF(object->inner.zobject)) {
2093 zval_ptr_dtor(&object->inner.zobject);
2094 }
2095
2096 if (object->dit_type == DIT_AppendIterator) {
2097 zend_iterator_dtor(object->u.append.iterator);
2098 if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2099 zval_ptr_dtor(&object->u.append.zarrayit);
2100 }
2101 }
2102
2103 if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_RecursiveCachingIterator) {
2104 zval_ptr_dtor(&object->u.caching.zcache);
2105 }
2106
2107 if (object->dit_type == DIT_RegexIterator || object->dit_type == DIT_RecursiveRegexIterator) {
2108 if (object->u.regex.pce) {
2109 php_pcre_pce_decref(object->u.regex.pce);
2110 }
2111 if (object->u.regex.regex) {
2112 zend_string_release_ex(object->u.regex.regex, 0);
2113 }
2114 }
2115
2116 if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
2117 if (object->u.cbfilter) {
2118 _spl_cbfilter_it_intern *cbfilter = object->u.cbfilter;
2119 object->u.cbfilter = NULL;
2120 zval_ptr_dtor(&cbfilter->fci.function_name);
2121 if (cbfilter->fci.object) {
2122 OBJ_RELEASE(cbfilter->fci.object);
2123 }
2124 efree(cbfilter);
2125 }
2126 }
2127
2128 zend_object_std_dtor(&object->std);
2129 }
2130 /* }}} */
2131
spl_dual_it_get_gc(zend_object * obj,zval ** table,int * n)2132 static HashTable *spl_dual_it_get_gc(zend_object *obj, zval **table, int *n)
2133 {
2134 spl_dual_it_object *object = spl_dual_it_from_obj(obj);
2135 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
2136
2137 if (!Z_ISUNDEF(object->inner.zobject)) {
2138 zend_get_gc_buffer_add_zval(gc_buffer, &object->inner.zobject);
2139 }
2140
2141 switch (object->dit_type) {
2142 case DIT_Unknown:
2143 case DIT_Default:
2144 case DIT_IteratorIterator:
2145 case DIT_NoRewindIterator:
2146 case DIT_InfiniteIterator:
2147 case DIT_LimitIterator:
2148 case DIT_RegexIterator:
2149 case DIT_RecursiveRegexIterator:
2150 /* Nothing to do */
2151 break;
2152 case DIT_AppendIterator:
2153 zend_get_gc_buffer_add_obj(gc_buffer, &object->u.append.iterator->std);
2154 if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2155 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.append.zarrayit);
2156 }
2157 break;
2158 case DIT_CachingIterator:
2159 case DIT_RecursiveCachingIterator:
2160 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zcache);
2161 break;
2162 case DIT_CallbackFilterIterator:
2163 case DIT_RecursiveCallbackFilterIterator:
2164 if (object->u.cbfilter) {
2165 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.cbfilter->fci.function_name);
2166 if (object->u.cbfilter->fci.object) {
2167 zend_get_gc_buffer_add_obj(gc_buffer, object->u.cbfilter->fci.object);
2168 }
2169 }
2170 break;
2171 }
2172
2173 zend_get_gc_buffer_use(gc_buffer, table, n);
2174 return zend_std_get_properties(obj);
2175 }
2176
2177 /* {{{ spl_dual_it_new */
spl_dual_it_new(zend_class_entry * class_type)2178 static zend_object *spl_dual_it_new(zend_class_entry *class_type)
2179 {
2180 spl_dual_it_object *intern;
2181
2182 intern = zend_object_alloc(sizeof(spl_dual_it_object), class_type);
2183 intern->dit_type = DIT_Unknown;
2184
2185 zend_object_std_init(&intern->std, class_type);
2186 object_properties_init(&intern->std, class_type);
2187
2188 intern->std.handlers = &spl_handlers_dual_it;
2189 return &intern->std;
2190 }
2191 /* }}} */
2192
spl_limit_it_valid(spl_dual_it_object * intern)2193 static inline int spl_limit_it_valid(spl_dual_it_object *intern)
2194 {
2195 /* FAILURE / SUCCESS */
2196 if (intern->u.limit.count != -1 && intern->current.pos >= intern->u.limit.offset + intern->u.limit.count) {
2197 return FAILURE;
2198 } else {
2199 return spl_dual_it_valid(intern);
2200 }
2201 }
2202
spl_limit_it_seek(spl_dual_it_object * intern,zend_long pos)2203 static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos)
2204 {
2205 zval zpos;
2206
2207 spl_dual_it_free(intern);
2208 if (pos < intern->u.limit.offset) {
2209 zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset);
2210 return;
2211 }
2212 if (pos >= intern->u.limit.offset + intern->u.limit.count && intern->u.limit.count != -1) {
2213 zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count);
2214 return;
2215 }
2216 if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator)) {
2217 ZVAL_LONG(&zpos, pos);
2218 spl_dual_it_free(intern);
2219 zend_call_method_with_1_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "seek", NULL, &zpos);
2220 if (!EG(exception)) {
2221 intern->current.pos = pos;
2222 if (spl_limit_it_valid(intern) == SUCCESS) {
2223 spl_dual_it_fetch(intern, 0);
2224 }
2225 }
2226 } else {
2227 /* emulate the forward seek, by next() calls */
2228 /* a back ward seek is done by a previous rewind() */
2229 if (pos < intern->current.pos) {
2230 spl_dual_it_rewind(intern);
2231 }
2232 while (pos > intern->current.pos && spl_dual_it_valid(intern) == SUCCESS) {
2233 spl_dual_it_next(intern, 1);
2234 }
2235 if (spl_dual_it_valid(intern) == SUCCESS) {
2236 spl_dual_it_fetch(intern, 1);
2237 }
2238 }
2239 }
2240
2241 /* {{{ Construct a LimitIterator from an Iterator with a given starting offset and optionally a maximum count */
PHP_METHOD(LimitIterator,__construct)2242 PHP_METHOD(LimitIterator, __construct)
2243 {
2244 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_LimitIterator, zend_ce_iterator, DIT_LimitIterator);
2245 } /* }}} */
2246
2247 /* {{{ Rewind the iterator to the specified starting offset */
PHP_METHOD(LimitIterator,rewind)2248 PHP_METHOD(LimitIterator, rewind)
2249 {
2250 spl_dual_it_object *intern;
2251
2252 if (zend_parse_parameters_none() == FAILURE) {
2253 RETURN_THROWS();
2254 }
2255
2256 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2257 spl_dual_it_rewind(intern);
2258 spl_limit_it_seek(intern, intern->u.limit.offset);
2259 } /* }}} */
2260
2261 /* {{{ Check whether the current element is valid */
PHP_METHOD(LimitIterator,valid)2262 PHP_METHOD(LimitIterator, valid)
2263 {
2264 spl_dual_it_object *intern;
2265
2266 if (zend_parse_parameters_none() == FAILURE) {
2267 RETURN_THROWS();
2268 }
2269
2270 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2271
2272 /* RETURN_BOOL(spl_limit_it_valid(intern) == SUCCESS);*/
2273 RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF);
2274 } /* }}} */
2275
2276 /* {{{ Move the iterator forward */
PHP_METHOD(LimitIterator,next)2277 PHP_METHOD(LimitIterator, next)
2278 {
2279 spl_dual_it_object *intern;
2280
2281 if (zend_parse_parameters_none() == FAILURE) {
2282 RETURN_THROWS();
2283 }
2284
2285 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2286
2287 spl_dual_it_next(intern, 1);
2288 if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) {
2289 spl_dual_it_fetch(intern, 1);
2290 }
2291 } /* }}} */
2292
2293 /* {{{ Seek to the given position */
PHP_METHOD(LimitIterator,seek)2294 PHP_METHOD(LimitIterator, seek)
2295 {
2296 spl_dual_it_object *intern;
2297 zend_long pos;
2298
2299 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &pos) == FAILURE) {
2300 RETURN_THROWS();
2301 }
2302
2303 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2304 spl_limit_it_seek(intern, pos);
2305 RETURN_LONG(intern->current.pos);
2306 } /* }}} */
2307
2308 /* {{{ Return the current position */
PHP_METHOD(LimitIterator,getPosition)2309 PHP_METHOD(LimitIterator, getPosition)
2310 {
2311 spl_dual_it_object *intern;
2312
2313 if (zend_parse_parameters_none() == FAILURE) {
2314 RETURN_THROWS();
2315 }
2316
2317 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2318 RETURN_LONG(intern->current.pos);
2319 } /* }}} */
2320
spl_caching_it_valid(spl_dual_it_object * intern)2321 static inline int spl_caching_it_valid(spl_dual_it_object *intern)
2322 {
2323 return intern->u.caching.flags & CIT_VALID ? SUCCESS : FAILURE;
2324 }
2325
spl_caching_it_has_next(spl_dual_it_object * intern)2326 static inline int spl_caching_it_has_next(spl_dual_it_object *intern)
2327 {
2328 return spl_dual_it_valid(intern);
2329 }
2330
spl_caching_it_next(spl_dual_it_object * intern)2331 static inline void spl_caching_it_next(spl_dual_it_object *intern)
2332 {
2333 if (spl_dual_it_fetch(intern, 1) == SUCCESS) {
2334 intern->u.caching.flags |= CIT_VALID;
2335 /* Full cache ? */
2336 if (intern->u.caching.flags & CIT_FULL_CACHE) {
2337 zval *key = &intern->current.key;
2338 zval *data = &intern->current.data;
2339
2340 ZVAL_DEREF(data);
2341 array_set_zval_key(Z_ARRVAL(intern->u.caching.zcache), key, data);
2342 }
2343 /* Recursion ? */
2344 if (intern->dit_type == DIT_RecursiveCachingIterator) {
2345 zval retval, zchildren, zflags;
2346 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", &retval);
2347 if (EG(exception)) {
2348 zval_ptr_dtor(&retval);
2349 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2350 zend_clear_exception();
2351 } else {
2352 return;
2353 }
2354 } else {
2355 if (zend_is_true(&retval)) {
2356 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &zchildren);
2357 if (EG(exception)) {
2358 zval_ptr_dtor(&zchildren);
2359 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2360 zend_clear_exception();
2361 } else {
2362 zval_ptr_dtor(&retval);
2363 return;
2364 }
2365 } else {
2366 ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
2367 spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &intern->u.caching.zchildren, &zchildren, &zflags);
2368 zval_ptr_dtor(&zchildren);
2369 }
2370 }
2371 zval_ptr_dtor(&retval);
2372 if (EG(exception)) {
2373 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2374 zend_clear_exception();
2375 } else {
2376 return;
2377 }
2378 }
2379 }
2380 }
2381 if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
2382 int use_copy;
2383 zval expr_copy;
2384 if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
2385 ZVAL_COPY_VALUE(&intern->u.caching.zstr, &intern->inner.zobject);
2386 } else {
2387 ZVAL_COPY_VALUE(&intern->u.caching.zstr, &intern->current.data);
2388 }
2389 use_copy = zend_make_printable_zval(&intern->u.caching.zstr, &expr_copy);
2390 if (use_copy) {
2391 ZVAL_COPY_VALUE(&intern->u.caching.zstr, &expr_copy);
2392 } else {
2393 Z_TRY_ADDREF(intern->u.caching.zstr);
2394 }
2395 }
2396 spl_dual_it_next(intern, 0);
2397 } else {
2398 intern->u.caching.flags &= ~CIT_VALID;
2399 }
2400 }
2401
spl_caching_it_rewind(spl_dual_it_object * intern)2402 static inline void spl_caching_it_rewind(spl_dual_it_object *intern)
2403 {
2404 spl_dual_it_rewind(intern);
2405 zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2406 spl_caching_it_next(intern);
2407 }
2408
2409 /* {{{ Construct a CachingIterator from an Iterator */
PHP_METHOD(CachingIterator,__construct)2410 PHP_METHOD(CachingIterator, __construct)
2411 {
2412 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CachingIterator, zend_ce_iterator, DIT_CachingIterator);
2413 } /* }}} */
2414
2415 /* {{{ Rewind the iterator */
PHP_METHOD(CachingIterator,rewind)2416 PHP_METHOD(CachingIterator, rewind)
2417 {
2418 spl_dual_it_object *intern;
2419
2420 if (zend_parse_parameters_none() == FAILURE) {
2421 RETURN_THROWS();
2422 }
2423
2424 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2425
2426 spl_caching_it_rewind(intern);
2427 } /* }}} */
2428
2429 /* {{{ Check whether the current element is valid */
PHP_METHOD(CachingIterator,valid)2430 PHP_METHOD(CachingIterator, valid)
2431 {
2432 spl_dual_it_object *intern;
2433
2434 if (zend_parse_parameters_none() == FAILURE) {
2435 RETURN_THROWS();
2436 }
2437
2438 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2439
2440 RETURN_BOOL(spl_caching_it_valid(intern) == SUCCESS);
2441 } /* }}} */
2442
2443 /* {{{ Move the iterator forward */
PHP_METHOD(CachingIterator,next)2444 PHP_METHOD(CachingIterator, next)
2445 {
2446 spl_dual_it_object *intern;
2447
2448 if (zend_parse_parameters_none() == FAILURE) {
2449 RETURN_THROWS();
2450 }
2451
2452 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2453
2454 spl_caching_it_next(intern);
2455 } /* }}} */
2456
2457 /* {{{ Check whether the inner iterator has a valid next element */
PHP_METHOD(CachingIterator,hasNext)2458 PHP_METHOD(CachingIterator, hasNext)
2459 {
2460 spl_dual_it_object *intern;
2461
2462 if (zend_parse_parameters_none() == FAILURE) {
2463 RETURN_THROWS();
2464 }
2465
2466 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2467
2468 RETURN_BOOL(spl_caching_it_has_next(intern) == SUCCESS);
2469 } /* }}} */
2470
2471 /* {{{ Return the string representation of the current element */
PHP_METHOD(CachingIterator,__toString)2472 PHP_METHOD(CachingIterator, __toString)
2473 {
2474 spl_dual_it_object *intern;
2475
2476 if (zend_parse_parameters_none() == FAILURE) {
2477 RETURN_THROWS();
2478 }
2479
2480 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2481
2482 if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER))) {
2483 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not fetch string value (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2484 RETURN_THROWS();
2485 }
2486
2487 if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
2488 ZVAL_COPY(return_value, &intern->current.key);
2489 convert_to_string(return_value);
2490 return;
2491 } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
2492 ZVAL_COPY(return_value, &intern->current.data);
2493 convert_to_string(return_value);
2494 return;
2495 }
2496 if (Z_TYPE(intern->u.caching.zstr) == IS_STRING) {
2497 RETURN_STR_COPY(Z_STR_P(&intern->u.caching.zstr));
2498 } else {
2499 RETURN_EMPTY_STRING();
2500 }
2501 } /* }}} */
2502
2503 /* {{{ Set given index in cache */
PHP_METHOD(CachingIterator,offsetSet)2504 PHP_METHOD(CachingIterator, offsetSet)
2505 {
2506 spl_dual_it_object *intern;
2507 zend_string *key;
2508 zval *value;
2509
2510 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &value) == FAILURE) {
2511 RETURN_THROWS();
2512 }
2513
2514 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2515
2516 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2517 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2518 RETURN_THROWS();
2519 }
2520
2521 Z_TRY_ADDREF_P(value);
2522 zend_symtable_update(Z_ARRVAL(intern->u.caching.zcache), key, value);
2523 }
2524 /* }}} */
2525
2526 /* {{{ Return the internal cache if used */
PHP_METHOD(CachingIterator,offsetGet)2527 PHP_METHOD(CachingIterator, offsetGet)
2528 {
2529 spl_dual_it_object *intern;
2530 zend_string *key;
2531 zval *value;
2532
2533 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2534 RETURN_THROWS();
2535 }
2536
2537 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2538
2539 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2540 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2541 RETURN_THROWS();
2542 }
2543
2544 if ((value = zend_symtable_find(Z_ARRVAL(intern->u.caching.zcache), key)) == NULL) {
2545 zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(key));
2546 return;
2547 }
2548
2549 ZVAL_COPY_DEREF(return_value, value);
2550 }
2551 /* }}} */
2552
2553 /* {{{ Unset given index in cache */
PHP_METHOD(CachingIterator,offsetUnset)2554 PHP_METHOD(CachingIterator, offsetUnset)
2555 {
2556 spl_dual_it_object *intern;
2557 zend_string *key;
2558
2559 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2560
2561 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2562 RETURN_THROWS();
2563 }
2564
2565 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2566 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2567 RETURN_THROWS();
2568 }
2569
2570 zend_symtable_del(Z_ARRVAL(intern->u.caching.zcache), key);
2571 }
2572 /* }}} */
2573
2574 /* {{{ Return whether the requested index exists */
PHP_METHOD(CachingIterator,offsetExists)2575 PHP_METHOD(CachingIterator, offsetExists)
2576 {
2577 spl_dual_it_object *intern;
2578 zend_string *key;
2579
2580 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2581 RETURN_THROWS();
2582 }
2583
2584 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2585
2586 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2587 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2588 RETURN_THROWS();
2589 }
2590
2591 RETURN_BOOL(zend_symtable_exists(Z_ARRVAL(intern->u.caching.zcache), key));
2592 }
2593 /* }}} */
2594
2595 /* {{{ Return the cache */
PHP_METHOD(CachingIterator,getCache)2596 PHP_METHOD(CachingIterator, getCache)
2597 {
2598 spl_dual_it_object *intern;
2599
2600 if (zend_parse_parameters_none() == FAILURE) {
2601 RETURN_THROWS();
2602 }
2603
2604 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2605
2606 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2607 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2608 RETURN_THROWS();
2609 }
2610
2611 ZVAL_COPY(return_value, &intern->u.caching.zcache);
2612 }
2613 /* }}} */
2614
2615 /* {{{ Return the internal flags */
PHP_METHOD(CachingIterator,getFlags)2616 PHP_METHOD(CachingIterator, getFlags)
2617 {
2618 spl_dual_it_object *intern;
2619
2620 if (zend_parse_parameters_none() == FAILURE) {
2621 RETURN_THROWS();
2622 }
2623
2624 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2625
2626 RETURN_LONG(intern->u.caching.flags);
2627 }
2628 /* }}} */
2629
2630 /* {{{ Set the internal flags */
PHP_METHOD(CachingIterator,setFlags)2631 PHP_METHOD(CachingIterator, setFlags)
2632 {
2633 spl_dual_it_object *intern;
2634 zend_long flags;
2635
2636 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
2637 RETURN_THROWS();
2638 }
2639
2640 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2641
2642 if (spl_cit_check_flags(flags) != SUCCESS) {
2643 zend_argument_value_error(1, "must contain only one of CachingIterator::CALL_TOSTRING, "
2644 "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
2645 "or CachingIterator::TOSTRING_USE_INNER");
2646 RETURN_THROWS();
2647 }
2648 if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
2649 zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0);
2650 RETURN_THROWS();
2651 }
2652 if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
2653 zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0);
2654 RETURN_THROWS();
2655 }
2656 if ((flags & CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
2657 /* clear on (re)enable */
2658 zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2659 }
2660 intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
2661 }
2662 /* }}} */
2663
2664 /* {{{ Number of cached elements */
PHP_METHOD(CachingIterator,count)2665 PHP_METHOD(CachingIterator, count)
2666 {
2667 spl_dual_it_object *intern;
2668
2669 if (zend_parse_parameters_none() == FAILURE) {
2670 RETURN_THROWS();
2671 }
2672
2673 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2674
2675 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2676 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2677 RETURN_THROWS();
2678 }
2679
2680 RETURN_LONG(zend_hash_num_elements(Z_ARRVAL(intern->u.caching.zcache)));
2681 }
2682 /* }}} */
2683
2684 /* {{{ Create an iterator from a RecursiveIterator */
PHP_METHOD(RecursiveCachingIterator,__construct)2685 PHP_METHOD(RecursiveCachingIterator, __construct)
2686 {
2687 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCachingIterator, spl_ce_RecursiveIterator, DIT_RecursiveCachingIterator);
2688 } /* }}} */
2689
2690 /* {{{ Check whether the current element of the inner iterator has children */
PHP_METHOD(RecursiveCachingIterator,hasChildren)2691 PHP_METHOD(RecursiveCachingIterator, hasChildren)
2692 {
2693 spl_dual_it_object *intern;
2694
2695 if (zend_parse_parameters_none() == FAILURE) {
2696 RETURN_THROWS();
2697 }
2698
2699 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2700
2701 RETURN_BOOL(Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF);
2702 } /* }}} */
2703
2704 /* {{{ Return the inner iterator's children as a RecursiveCachingIterator */
PHP_METHOD(RecursiveCachingIterator,getChildren)2705 PHP_METHOD(RecursiveCachingIterator, getChildren)
2706 {
2707 spl_dual_it_object *intern;
2708
2709 if (zend_parse_parameters_none() == FAILURE) {
2710 RETURN_THROWS();
2711 }
2712
2713 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2714
2715 if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
2716 zval *value = &intern->u.caching.zchildren;
2717
2718 ZVAL_COPY_DEREF(return_value, value);
2719 } else {
2720 RETURN_NULL();
2721 }
2722 } /* }}} */
2723
2724 /* {{{ Create an iterator from anything that is traversable */
PHP_METHOD(IteratorIterator,__construct)2725 PHP_METHOD(IteratorIterator, __construct)
2726 {
2727 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_IteratorIterator, zend_ce_traversable, DIT_IteratorIterator);
2728 } /* }}} */
2729
2730 /* {{{ Create an iterator from another iterator */
PHP_METHOD(NoRewindIterator,__construct)2731 PHP_METHOD(NoRewindIterator, __construct)
2732 {
2733 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_NoRewindIterator, zend_ce_iterator, DIT_NoRewindIterator);
2734 } /* }}} */
2735
2736 /* {{{ Prevent a call to inner iterators rewind() */
PHP_METHOD(NoRewindIterator,rewind)2737 PHP_METHOD(NoRewindIterator, rewind)
2738 {
2739 if (zend_parse_parameters_none() == FAILURE) {
2740 RETURN_THROWS();
2741 }
2742 /* nothing to do */
2743 } /* }}} */
2744
2745 /* {{{ Return inner iterators valid() */
PHP_METHOD(NoRewindIterator,valid)2746 PHP_METHOD(NoRewindIterator, valid)
2747 {
2748 spl_dual_it_object *intern;
2749
2750 if (zend_parse_parameters_none() == FAILURE) {
2751 RETURN_THROWS();
2752 }
2753
2754 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2755 RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator) == SUCCESS);
2756 } /* }}} */
2757
2758 /* {{{ Return inner iterators key() */
PHP_METHOD(NoRewindIterator,key)2759 PHP_METHOD(NoRewindIterator, key)
2760 {
2761 spl_dual_it_object *intern;
2762
2763 if (zend_parse_parameters_none() == FAILURE) {
2764 RETURN_THROWS();
2765 }
2766
2767 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2768
2769 if (intern->inner.iterator->funcs->get_current_key) {
2770 intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, return_value);
2771 } else {
2772 RETURN_NULL();
2773 }
2774 } /* }}} */
2775
2776 /* {{{ Return inner iterators current() */
PHP_METHOD(NoRewindIterator,current)2777 PHP_METHOD(NoRewindIterator, current)
2778 {
2779 spl_dual_it_object *intern;
2780 zval *data;
2781
2782 if (zend_parse_parameters_none() == FAILURE) {
2783 RETURN_THROWS();
2784 }
2785
2786 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2787 data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
2788 if (data) {
2789 ZVAL_COPY_DEREF(return_value, data);
2790 }
2791 } /* }}} */
2792
2793 /* {{{ Return inner iterators next() */
PHP_METHOD(NoRewindIterator,next)2794 PHP_METHOD(NoRewindIterator, next)
2795 {
2796 spl_dual_it_object *intern;
2797
2798 if (zend_parse_parameters_none() == FAILURE) {
2799 RETURN_THROWS();
2800 }
2801
2802 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2803 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
2804 } /* }}} */
2805
2806 /* {{{ Create an iterator from another iterator */
PHP_METHOD(InfiniteIterator,__construct)2807 PHP_METHOD(InfiniteIterator, __construct)
2808 {
2809 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_InfiniteIterator, zend_ce_iterator, DIT_InfiniteIterator);
2810 } /* }}} */
2811
2812 /* {{{ Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid()) */
PHP_METHOD(InfiniteIterator,next)2813 PHP_METHOD(InfiniteIterator, next)
2814 {
2815 spl_dual_it_object *intern;
2816
2817 if (zend_parse_parameters_none() == FAILURE) {
2818 RETURN_THROWS();
2819 }
2820
2821 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2822
2823 spl_dual_it_next(intern, 1);
2824 if (spl_dual_it_valid(intern) == SUCCESS) {
2825 spl_dual_it_fetch(intern, 0);
2826 } else {
2827 spl_dual_it_rewind(intern);
2828 if (spl_dual_it_valid(intern) == SUCCESS) {
2829 spl_dual_it_fetch(intern, 0);
2830 }
2831 }
2832 } /* }}} */
2833
2834 /* {{{ Does nothing */
PHP_METHOD(EmptyIterator,rewind)2835 PHP_METHOD(EmptyIterator, rewind)
2836 {
2837 if (zend_parse_parameters_none() == FAILURE) {
2838 RETURN_THROWS();
2839 }
2840 } /* }}} */
2841
2842 /* {{{ Return false */
PHP_METHOD(EmptyIterator,valid)2843 PHP_METHOD(EmptyIterator, valid)
2844 {
2845 if (zend_parse_parameters_none() == FAILURE) {
2846 RETURN_THROWS();
2847 }
2848
2849 RETURN_FALSE;
2850 } /* }}} */
2851
2852 /* {{{ Throws exception BadMethodCallException */
PHP_METHOD(EmptyIterator,key)2853 PHP_METHOD(EmptyIterator, key)
2854 {
2855 if (zend_parse_parameters_none() == FAILURE) {
2856 RETURN_THROWS();
2857 }
2858
2859 zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the key of an EmptyIterator", 0);
2860 } /* }}} */
2861
2862 /* {{{ Throws exception BadMethodCallException */
PHP_METHOD(EmptyIterator,current)2863 PHP_METHOD(EmptyIterator, current)
2864 {
2865 if (zend_parse_parameters_none() == FAILURE) {
2866 RETURN_THROWS();
2867 }
2868
2869 zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the value of an EmptyIterator", 0);
2870 } /* }}} */
2871
2872 /* {{{ Does nothing */
PHP_METHOD(EmptyIterator,next)2873 PHP_METHOD(EmptyIterator, next)
2874 {
2875 if (zend_parse_parameters_none() == FAILURE) {
2876 RETURN_THROWS();
2877 }
2878 } /* }}} */
2879
spl_append_it_next_iterator(spl_dual_it_object * intern)2880 int spl_append_it_next_iterator(spl_dual_it_object *intern) /* {{{*/
2881 {
2882 spl_dual_it_free(intern);
2883
2884 if (!Z_ISUNDEF(intern->inner.zobject)) {
2885 zval_ptr_dtor(&intern->inner.zobject);
2886 ZVAL_UNDEF(&intern->inner.zobject);
2887 intern->inner.ce = NULL;
2888 if (intern->inner.iterator) {
2889 zend_iterator_dtor(intern->inner.iterator);
2890 intern->inner.iterator = NULL;
2891 }
2892 }
2893 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS) {
2894 zval *it;
2895
2896 it = intern->u.append.iterator->funcs->get_current_data(intern->u.append.iterator);
2897 ZVAL_COPY(&intern->inner.zobject, it);
2898 intern->inner.ce = Z_OBJCE_P(it);
2899 intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, it, 0);
2900 spl_dual_it_rewind(intern);
2901 return SUCCESS;
2902 } else {
2903 return FAILURE;
2904 }
2905 } /* }}} */
2906
spl_append_it_fetch(spl_dual_it_object * intern)2907 void spl_append_it_fetch(spl_dual_it_object *intern) /* {{{*/
2908 {
2909 while (spl_dual_it_valid(intern) != SUCCESS) {
2910 intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2911 if (spl_append_it_next_iterator(intern) != SUCCESS) {
2912 return;
2913 }
2914 }
2915 spl_dual_it_fetch(intern, 0);
2916 } /* }}} */
2917
spl_append_it_next(spl_dual_it_object * intern)2918 void spl_append_it_next(spl_dual_it_object *intern) /* {{{ */
2919 {
2920 if (spl_dual_it_valid(intern) == SUCCESS) {
2921 spl_dual_it_next(intern, 1);
2922 }
2923 spl_append_it_fetch(intern);
2924 } /* }}} */
2925
2926 /* {{{ Create an AppendIterator */
PHP_METHOD(AppendIterator,__construct)2927 PHP_METHOD(AppendIterator, __construct)
2928 {
2929 spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_AppendIterator, zend_ce_iterator, DIT_AppendIterator);
2930 } /* }}} */
2931
2932 /* {{{ Append an iterator */
PHP_METHOD(AppendIterator,append)2933 PHP_METHOD(AppendIterator, append)
2934 {
2935 spl_dual_it_object *intern;
2936 zval *it;
2937
2938 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &it, zend_ce_iterator) == FAILURE) {
2939 RETURN_THROWS();
2940 }
2941
2942 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2943
2944 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS && spl_dual_it_valid(intern) != SUCCESS) {
2945 spl_array_iterator_append(&intern->u.append.zarrayit, it);
2946 intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2947 }else{
2948 spl_array_iterator_append(&intern->u.append.zarrayit, it);
2949 }
2950
2951 if (!intern->inner.iterator || spl_dual_it_valid(intern) != SUCCESS) {
2952 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) != SUCCESS) {
2953 intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2954 }
2955 do {
2956 spl_append_it_next_iterator(intern);
2957 } while (Z_OBJ(intern->inner.zobject) != Z_OBJ_P(it));
2958 spl_append_it_fetch(intern);
2959 }
2960 } /* }}} */
2961
2962 /* {{{ Get the current element value */
PHP_METHOD(AppendIterator,current)2963 PHP_METHOD(AppendIterator, current)
2964 {
2965 spl_dual_it_object *intern;
2966
2967 if (zend_parse_parameters_none() == FAILURE) {
2968 RETURN_THROWS();
2969 }
2970
2971 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2972
2973 spl_dual_it_fetch(intern, 1);
2974 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
2975 zval *value = &intern->current.data;
2976
2977 ZVAL_COPY_DEREF(return_value, value);
2978 } else {
2979 RETURN_NULL();
2980 }
2981 } /* }}} */
2982
2983 /* {{{ Rewind to the first iterator and rewind the first iterator, too */
PHP_METHOD(AppendIterator,rewind)2984 PHP_METHOD(AppendIterator, rewind)
2985 {
2986 spl_dual_it_object *intern;
2987
2988 if (zend_parse_parameters_none() == FAILURE) {
2989 RETURN_THROWS();
2990 }
2991
2992 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2993
2994 intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2995 if (spl_append_it_next_iterator(intern) == SUCCESS) {
2996 spl_append_it_fetch(intern);
2997 }
2998 } /* }}} */
2999
3000 /* {{{ Check if the current state is valid */
PHP_METHOD(AppendIterator,valid)3001 PHP_METHOD(AppendIterator, valid)
3002 {
3003 spl_dual_it_object *intern;
3004
3005 if (zend_parse_parameters_none() == FAILURE) {
3006 RETURN_THROWS();
3007 }
3008
3009 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
3010
3011 RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
3012 } /* }}} */
3013
3014 /* {{{ Forward to next element */
PHP_METHOD(AppendIterator,next)3015 PHP_METHOD(AppendIterator, next)
3016 {
3017 spl_dual_it_object *intern;
3018
3019 if (zend_parse_parameters_none() == FAILURE) {
3020 RETURN_THROWS();
3021 }
3022
3023 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
3024
3025 spl_append_it_next(intern);
3026 } /* }}} */
3027
3028 /* {{{ Get index of iterator */
PHP_METHOD(AppendIterator,getIteratorIndex)3029 PHP_METHOD(AppendIterator, getIteratorIndex)
3030 {
3031 spl_dual_it_object *intern;
3032
3033 if (zend_parse_parameters_none() == FAILURE) {
3034 RETURN_THROWS();
3035 }
3036
3037 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
3038
3039 APPENDIT_CHECK_CTOR(intern);
3040 spl_array_iterator_key(&intern->u.append.zarrayit, return_value);
3041 } /* }}} */
3042
3043 /* {{{ Get access to inner ArrayIterator */
PHP_METHOD(AppendIterator,getArrayIterator)3044 PHP_METHOD(AppendIterator, getArrayIterator)
3045 {
3046 spl_dual_it_object *intern;
3047 zval *value;
3048
3049 if (zend_parse_parameters_none() == FAILURE) {
3050 RETURN_THROWS();
3051 }
3052
3053 SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
3054
3055 value = &intern->u.append.zarrayit;
3056 ZVAL_COPY_DEREF(return_value, value);
3057 } /* }}} */
3058
spl_iterator_apply(zval * obj,spl_iterator_apply_func_t apply_func,void * puser)3059 PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser)
3060 {
3061 zend_object_iterator *iter;
3062 zend_class_entry *ce = Z_OBJCE_P(obj);
3063
3064 iter = ce->get_iterator(ce, obj, 0);
3065
3066 if (EG(exception)) {
3067 goto done;
3068 }
3069
3070 iter->index = 0;
3071 if (iter->funcs->rewind) {
3072 iter->funcs->rewind(iter);
3073 if (EG(exception)) {
3074 goto done;
3075 }
3076 }
3077
3078 while (iter->funcs->valid(iter) == SUCCESS) {
3079 if (EG(exception)) {
3080 goto done;
3081 }
3082 if (apply_func(iter, puser) == ZEND_HASH_APPLY_STOP || EG(exception)) {
3083 goto done;
3084 }
3085 iter->index++;
3086 iter->funcs->move_forward(iter);
3087 if (EG(exception)) {
3088 goto done;
3089 }
3090 }
3091
3092 done:
3093 if (iter) {
3094 zend_iterator_dtor(iter);
3095 }
3096 return EG(exception) ? FAILURE : SUCCESS;
3097 }
3098 /* }}} */
3099
spl_iterator_to_array_apply(zend_object_iterator * iter,void * puser)3100 static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3101 {
3102 zval *data, *return_value = (zval*)puser;
3103
3104 data = iter->funcs->get_current_data(iter);
3105 if (EG(exception)) {
3106 return ZEND_HASH_APPLY_STOP;
3107 }
3108 if (data == NULL) {
3109 return ZEND_HASH_APPLY_STOP;
3110 }
3111 if (iter->funcs->get_current_key) {
3112 zval key;
3113 iter->funcs->get_current_key(iter, &key);
3114 if (EG(exception)) {
3115 return ZEND_HASH_APPLY_STOP;
3116 }
3117 array_set_zval_key(Z_ARRVAL_P(return_value), &key, data);
3118 zval_ptr_dtor(&key);
3119 } else {
3120 Z_TRY_ADDREF_P(data);
3121 add_next_index_zval(return_value, data);
3122 }
3123 return ZEND_HASH_APPLY_KEEP;
3124 }
3125 /* }}} */
3126
spl_iterator_to_values_apply(zend_object_iterator * iter,void * puser)3127 static int spl_iterator_to_values_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3128 {
3129 zval *data, *return_value = (zval*)puser;
3130
3131 data = iter->funcs->get_current_data(iter);
3132 if (EG(exception)) {
3133 return ZEND_HASH_APPLY_STOP;
3134 }
3135 if (data == NULL) {
3136 return ZEND_HASH_APPLY_STOP;
3137 }
3138 Z_TRY_ADDREF_P(data);
3139 add_next_index_zval(return_value, data);
3140 return ZEND_HASH_APPLY_KEEP;
3141 }
3142 /* }}} */
3143
3144 /* {{{ Copy the iterator into an array */
PHP_FUNCTION(iterator_to_array)3145 PHP_FUNCTION(iterator_to_array)
3146 {
3147 zval *obj;
3148 zend_bool use_keys = 1;
3149
3150 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &obj, zend_ce_traversable, &use_keys) == FAILURE) {
3151 RETURN_THROWS();
3152 }
3153
3154 array_init(return_value);
3155 spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
3156 } /* }}} */
3157
spl_iterator_count_apply(zend_object_iterator * iter,void * puser)3158 static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3159 {
3160 if (UNEXPECTED(*(zend_long*)puser == ZEND_LONG_MAX)) {
3161 return ZEND_HASH_APPLY_STOP;
3162 }
3163 (*(zend_long*)puser)++;
3164 return ZEND_HASH_APPLY_KEEP;
3165 }
3166 /* }}} */
3167
3168 /* {{{ Count the elements in an iterator */
PHP_FUNCTION(iterator_count)3169 PHP_FUNCTION(iterator_count)
3170 {
3171 zval *obj;
3172 zend_long count = 0;
3173
3174 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, zend_ce_traversable) == FAILURE) {
3175 RETURN_THROWS();
3176 }
3177
3178 if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count) == FAILURE) {
3179 return;
3180 }
3181
3182 RETURN_LONG(count);
3183 }
3184 /* }}} */
3185
3186 typedef struct {
3187 zval *obj;
3188 zval *args;
3189 zend_long count;
3190 zend_fcall_info fci;
3191 zend_fcall_info_cache fcc;
3192 } spl_iterator_apply_info;
3193
spl_iterator_func_apply(zend_object_iterator * iter,void * puser)3194 static int spl_iterator_func_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3195 {
3196 zval retval;
3197 spl_iterator_apply_info *apply_info = (spl_iterator_apply_info*)puser;
3198 int result;
3199
3200 apply_info->count++;
3201 zend_fcall_info_call(&apply_info->fci, &apply_info->fcc, &retval, NULL);
3202 result = zend_is_true(&retval) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_STOP;
3203 zval_ptr_dtor(&retval);
3204 return result;
3205 }
3206 /* }}} */
3207
3208 /* {{{ Calls a function for every element in an iterator */
PHP_FUNCTION(iterator_apply)3209 PHP_FUNCTION(iterator_apply)
3210 {
3211 spl_iterator_apply_info apply_info;
3212
3213 apply_info.args = NULL;
3214 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of|a!", &apply_info.obj, zend_ce_traversable, &apply_info.fci, &apply_info.fcc, &apply_info.args) == FAILURE) {
3215 RETURN_THROWS();
3216 }
3217
3218 apply_info.count = 0;
3219 zend_fcall_info_args(&apply_info.fci, apply_info.args);
3220 if (spl_iterator_apply(apply_info.obj, spl_iterator_func_apply, (void*)&apply_info) == FAILURE) {
3221 zend_fcall_info_args(&apply_info.fci, NULL);
3222 return;
3223 }
3224
3225 zend_fcall_info_args(&apply_info.fci, NULL);
3226 RETURN_LONG(apply_info.count);
3227 }
3228 /* }}} */
3229
3230 /* {{{ PHP_MINIT_FUNCTION(spl_iterators) */
PHP_MINIT_FUNCTION(spl_iterators)3231 PHP_MINIT_FUNCTION(spl_iterators)
3232 {
3233 REGISTER_SPL_INTERFACE(RecursiveIterator);
3234 REGISTER_SPL_ITERATOR(RecursiveIterator);
3235
3236 REGISTER_SPL_STD_CLASS_EX(RecursiveIteratorIterator, spl_RecursiveIteratorIterator_new, class_RecursiveIteratorIterator_methods);
3237 REGISTER_SPL_ITERATOR(RecursiveIteratorIterator);
3238
3239 memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers));
3240 spl_handlers_rec_it_it.offset = XtOffsetOf(spl_recursive_it_object, std);
3241 spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
3242 spl_handlers_rec_it_it.clone_obj = NULL;
3243 spl_handlers_rec_it_it.dtor_obj = spl_RecursiveIteratorIterator_dtor;
3244 spl_handlers_rec_it_it.free_obj = spl_RecursiveIteratorIterator_free_storage;
3245
3246 memcpy(&spl_handlers_dual_it, &std_object_handlers, sizeof(zend_object_handlers));
3247 spl_handlers_dual_it.offset = XtOffsetOf(spl_dual_it_object, std);
3248 spl_handlers_dual_it.get_method = spl_dual_it_get_method;
3249 spl_handlers_dual_it.clone_obj = NULL;
3250 spl_handlers_dual_it.dtor_obj = spl_dual_it_dtor;
3251 spl_handlers_dual_it.free_obj = spl_dual_it_free_storage;
3252 spl_handlers_dual_it.get_gc = spl_dual_it_get_gc;
3253
3254 spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
3255
3256 REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "LEAVES_ONLY", RIT_LEAVES_ONLY);
3257 REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "SELF_FIRST", RIT_SELF_FIRST);
3258 REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CHILD_FIRST", RIT_CHILD_FIRST);
3259 REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CATCH_GET_CHILD", RIT_CATCH_GET_CHILD);
3260
3261 REGISTER_SPL_INTERFACE(OuterIterator);
3262 REGISTER_SPL_ITERATOR(OuterIterator);
3263
3264 REGISTER_SPL_STD_CLASS_EX(IteratorIterator, spl_dual_it_new, class_IteratorIterator_methods);
3265 REGISTER_SPL_ITERATOR(IteratorIterator);
3266 REGISTER_SPL_IMPLEMENTS(IteratorIterator, OuterIterator);
3267
3268 REGISTER_SPL_SUB_CLASS_EX(FilterIterator, IteratorIterator, spl_dual_it_new, class_FilterIterator_methods);
3269 spl_ce_FilterIterator->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
3270
3271 REGISTER_SPL_SUB_CLASS_EX(RecursiveFilterIterator, FilterIterator, spl_dual_it_new, class_RecursiveFilterIterator_methods);
3272 REGISTER_SPL_IMPLEMENTS(RecursiveFilterIterator, RecursiveIterator);
3273
3274 REGISTER_SPL_SUB_CLASS_EX(CallbackFilterIterator, FilterIterator, spl_dual_it_new, class_CallbackFilterIterator_methods);
3275
3276 REGISTER_SPL_SUB_CLASS_EX(RecursiveCallbackFilterIterator, CallbackFilterIterator, spl_dual_it_new, class_RecursiveCallbackFilterIterator_methods);
3277 REGISTER_SPL_IMPLEMENTS(RecursiveCallbackFilterIterator, RecursiveIterator);
3278
3279
3280 REGISTER_SPL_SUB_CLASS_EX(ParentIterator, RecursiveFilterIterator, spl_dual_it_new, class_ParentIterator_methods);
3281
3282 REGISTER_SPL_INTERFACE(SeekableIterator);
3283 REGISTER_SPL_ITERATOR(SeekableIterator);
3284
3285 REGISTER_SPL_SUB_CLASS_EX(LimitIterator, IteratorIterator, spl_dual_it_new, class_LimitIterator_methods);
3286
3287 REGISTER_SPL_SUB_CLASS_EX(CachingIterator, IteratorIterator, spl_dual_it_new, class_CachingIterator_methods);
3288 REGISTER_SPL_IMPLEMENTS(CachingIterator, ArrayAccess);
3289 REGISTER_SPL_IMPLEMENTS(CachingIterator, Countable);
3290 REGISTER_SPL_IMPLEMENTS(CachingIterator, Stringable);
3291
3292 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CALL_TOSTRING", CIT_CALL_TOSTRING);
3293 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD", CIT_CATCH_GET_CHILD);
3294 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_KEY", CIT_TOSTRING_USE_KEY);
3295 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_CURRENT", CIT_TOSTRING_USE_CURRENT);
3296 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER", CIT_TOSTRING_USE_INNER);
3297 REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE", CIT_FULL_CACHE);
3298
3299 REGISTER_SPL_SUB_CLASS_EX(RecursiveCachingIterator, CachingIterator, spl_dual_it_new, class_RecursiveCachingIterator_methods);
3300 REGISTER_SPL_IMPLEMENTS(RecursiveCachingIterator, RecursiveIterator);
3301
3302 REGISTER_SPL_SUB_CLASS_EX(NoRewindIterator, IteratorIterator, spl_dual_it_new, class_NoRewindIterator_methods);
3303
3304 REGISTER_SPL_SUB_CLASS_EX(AppendIterator, IteratorIterator, spl_dual_it_new, class_AppendIterator_methods);
3305
3306 REGISTER_SPL_IMPLEMENTS(RecursiveIteratorIterator, OuterIterator);
3307
3308 REGISTER_SPL_SUB_CLASS_EX(InfiniteIterator, IteratorIterator, spl_dual_it_new, class_InfiniteIterator_methods);
3309 REGISTER_SPL_SUB_CLASS_EX(RegexIterator, FilterIterator, spl_dual_it_new, class_RegexIterator_methods);
3310 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "USE_KEY", REGIT_USE_KEY);
3311 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "INVERT_MATCH",REGIT_INVERTED);
3312 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "MATCH", REGIT_MODE_MATCH);
3313 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "GET_MATCH", REGIT_MODE_GET_MATCH);
3314 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "ALL_MATCHES", REGIT_MODE_ALL_MATCHES);
3315 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "SPLIT", REGIT_MODE_SPLIT);
3316 REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "REPLACE", REGIT_MODE_REPLACE);
3317 REGISTER_SPL_PROPERTY(RegexIterator, "replacement", 0);
3318 REGISTER_SPL_SUB_CLASS_EX(RecursiveRegexIterator, RegexIterator, spl_dual_it_new, class_RecursiveRegexIterator_methods);
3319 REGISTER_SPL_IMPLEMENTS(RecursiveRegexIterator, RecursiveIterator);
3320
3321 REGISTER_SPL_STD_CLASS_EX(EmptyIterator, NULL, class_EmptyIterator_methods);
3322 REGISTER_SPL_ITERATOR(EmptyIterator);
3323
3324 REGISTER_SPL_SUB_CLASS_EX(RecursiveTreeIterator, RecursiveIteratorIterator, spl_RecursiveTreeIterator_new, class_RecursiveTreeIterator_methods);
3325 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_CURRENT", RTIT_BYPASS_CURRENT);
3326 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_KEY", RTIT_BYPASS_KEY);
3327 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_LEFT", 0);
3328 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_HAS_NEXT", 1);
3329 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_LAST", 2);
3330 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_HAS_NEXT", 3);
3331 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_LAST", 4);
3332 REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_RIGHT", 5);
3333
3334 return SUCCESS;
3335 }
3336 /* }}} */
3337