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