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