1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Marcus Boerger <helly@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_API.h"
21 #include "zend_interfaces.h"
22 #include "zend_exceptions.h"
23 #include "zend_interfaces_arginfo.h"
24 #include "zend_property_hooks.h"
25
26 ZEND_API zend_class_entry *zend_ce_traversable;
27 ZEND_API zend_class_entry *zend_ce_aggregate;
28 ZEND_API zend_class_entry *zend_ce_iterator;
29 ZEND_API zend_class_entry *zend_ce_arrayaccess;
30 ZEND_API zend_class_entry *zend_ce_serializable;
31 ZEND_API zend_class_entry *zend_ce_countable;
32 ZEND_API zend_class_entry *zend_ce_stringable;
33 ZEND_API zend_class_entry *zend_ce_internal_iterator;
34
35 static zend_object_handlers zend_internal_iterator_handlers;
36
37 /* {{{ zend_call_method
38 Only returns the returned zval if retval_ptr != NULL */
zend_call_method(zend_object * object,zend_class_entry * obj_ce,zend_function ** fn_proxy,const char * function_name,size_t function_name_len,zval * retval_ptr,uint32_t param_count,zval * arg1,zval * arg2)39 ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval_ptr, uint32_t param_count, zval* arg1, zval* arg2)
40 {
41 zend_function *fn;
42 zend_class_entry *called_scope;
43 zval params[2];
44
45 if (param_count > 0) {
46 ZVAL_COPY_VALUE(¶ms[0], arg1);
47 }
48 if (param_count > 1) {
49 ZVAL_COPY_VALUE(¶ms[1], arg2);
50 }
51
52 if (!obj_ce) {
53 obj_ce = object ? object->ce : NULL;
54 }
55 if (!fn_proxy || !*fn_proxy) {
56 if (EXPECTED(obj_ce)) {
57 fn = zend_hash_str_find_ptr_lc(
58 &obj_ce->function_table, function_name, function_name_len);
59 if (UNEXPECTED(fn == NULL)) {
60 /* error at c-level */
61 zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s::%s", ZSTR_VAL(obj_ce->name), function_name);
62 }
63 } else {
64 fn = zend_fetch_function_str(function_name, function_name_len);
65 if (UNEXPECTED(fn == NULL)) {
66 /* error at c-level */
67 zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for function %s", function_name);
68 }
69 }
70 if (fn_proxy) {
71 *fn_proxy = fn;
72 }
73 } else {
74 fn = *fn_proxy;
75 }
76
77 if (object) {
78 called_scope = object->ce;
79 } else {
80 called_scope = obj_ce;
81 }
82
83 zend_call_known_function(fn, object, called_scope, retval_ptr, param_count, params, NULL);
84 return retval_ptr;
85 }
86 /* }}} */
87
88 /* iterator interface, c-level functions used by engine */
89
90 /* {{{ zend_user_it_new_iterator */
zend_user_it_new_iterator(zend_class_entry * ce,zval * object,zval * retval)91 ZEND_API void zend_user_it_new_iterator(zend_class_entry *ce, zval *object, zval *retval)
92 {
93 zend_call_known_instance_method_with_0_params(
94 ce->iterator_funcs_ptr->zf_new_iterator, Z_OBJ_P(object), retval);
95 }
96 /* }}} */
97
98 /* {{{ zend_user_it_invalidate_current */
zend_user_it_invalidate_current(zend_object_iterator * _iter)99 ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter)
100 {
101 zend_user_iterator *iter = (zend_user_iterator*)_iter;
102
103 if (!Z_ISUNDEF(iter->value)) {
104 zval_ptr_dtor(&iter->value);
105 ZVAL_UNDEF(&iter->value);
106 }
107 }
108 /* }}} */
109
110 /* {{{ zend_user_it_dtor */
zend_user_it_dtor(zend_object_iterator * _iter)111 static void zend_user_it_dtor(zend_object_iterator *_iter)
112 {
113 zend_user_iterator *iter = (zend_user_iterator*)_iter;
114 zval *object = &iter->it.data;
115
116 zend_user_it_invalidate_current(_iter);
117 zval_ptr_dtor(object);
118 }
119 /* }}} */
120
121 /* {{{ zend_user_it_valid */
zend_user_it_valid(zend_object_iterator * _iter)122 ZEND_API zend_result zend_user_it_valid(zend_object_iterator *_iter)
123 {
124 if (_iter) {
125 zend_user_iterator *iter = (zend_user_iterator*)_iter;
126 zval *object = &iter->it.data;
127 zval more;
128
129 zend_call_known_instance_method_with_0_params(iter->ce->iterator_funcs_ptr->zf_valid, Z_OBJ_P(object), &more);
130 bool result = i_zend_is_true(&more);
131 zval_ptr_dtor(&more);
132 return result ? SUCCESS : FAILURE;
133 }
134 return FAILURE;
135 }
136 /* }}} */
137
138 /* {{{ zend_user_it_get_current_data */
zend_user_it_get_current_data(zend_object_iterator * _iter)139 ZEND_API zval *zend_user_it_get_current_data(zend_object_iterator *_iter)
140 {
141 zend_user_iterator *iter = (zend_user_iterator*)_iter;
142 zval *object = &iter->it.data;
143
144 if (Z_ISUNDEF(iter->value)) {
145 zend_call_known_instance_method_with_0_params(iter->ce->iterator_funcs_ptr->zf_current, Z_OBJ_P(object), &iter->value);
146 }
147 return &iter->value;
148 }
149 /* }}} */
150
151 /* {{{ zend_user_it_get_current_key */
zend_user_it_get_current_key(zend_object_iterator * _iter,zval * key)152 ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key)
153 {
154 zend_user_iterator *iter = (zend_user_iterator*)_iter;
155 zval *object = &iter->it.data;
156 zend_call_known_instance_method_with_0_params(iter->ce->iterator_funcs_ptr->zf_key, Z_OBJ_P(object), key);
157 if (UNEXPECTED(Z_ISREF_P(key))) {
158 zend_unwrap_reference(key);
159 }
160 }
161 /* }}} */
162
163 /* {{{ zend_user_it_move_forward */
zend_user_it_move_forward(zend_object_iterator * _iter)164 ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter)
165 {
166 zend_user_iterator *iter = (zend_user_iterator*)_iter;
167 zval *object = &iter->it.data;
168
169 zend_user_it_invalidate_current(_iter);
170 zend_call_known_instance_method_with_0_params(iter->ce->iterator_funcs_ptr->zf_next, Z_OBJ_P(object), NULL);
171 }
172 /* }}} */
173
174 /* {{{ zend_user_it_rewind */
zend_user_it_rewind(zend_object_iterator * _iter)175 ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter)
176 {
177 zend_user_iterator *iter = (zend_user_iterator*)_iter;
178 zval *object = &iter->it.data;
179
180 zend_user_it_invalidate_current(_iter);
181 zend_call_known_instance_method_with_0_params(iter->ce->iterator_funcs_ptr->zf_rewind, Z_OBJ_P(object), NULL);
182 }
183 /* }}} */
184
zend_user_it_get_gc(zend_object_iterator * _iter,zval ** table,int * n)185 ZEND_API HashTable *zend_user_it_get_gc(zend_object_iterator *_iter, zval **table, int *n)
186 {
187 zend_user_iterator *iter = (zend_user_iterator*)_iter;
188 if (Z_ISUNDEF(iter->value)) {
189 *table = &iter->it.data;
190 *n = 1;
191 } else {
192 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
193 zend_get_gc_buffer_add_zval(gc_buffer, &iter->it.data);
194 zend_get_gc_buffer_add_zval(gc_buffer, &iter->value);
195 zend_get_gc_buffer_use(gc_buffer, table, n);
196 }
197 return NULL;
198 }
199
200 static const zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
201 zend_user_it_dtor,
202 zend_user_it_valid,
203 zend_user_it_get_current_data,
204 zend_user_it_get_current_key,
205 zend_user_it_move_forward,
206 zend_user_it_rewind,
207 zend_user_it_invalidate_current,
208 zend_user_it_get_gc,
209 };
210
211 /* {{{ zend_user_it_get_iterator */
212 /* by_ref is int due to Iterator API */
zend_user_it_get_iterator(zend_class_entry * ce,zval * object,int by_ref)213 static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
214 {
215 zend_user_iterator *iterator;
216
217 if (by_ref) {
218 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
219 return NULL;
220 }
221
222 iterator = emalloc(sizeof(zend_user_iterator));
223
224 zend_iterator_init((zend_object_iterator*)iterator);
225
226 ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
227 iterator->it.funcs = &zend_interface_iterator_funcs_iterator;
228 iterator->ce = Z_OBJCE_P(object);
229 ZVAL_UNDEF(&iterator->value);
230 return (zend_object_iterator*)iterator;
231 }
232 /* }}} */
233
234 /* {{{ zend_user_it_get_new_iterator */
235 /* by_ref is int due to Iterator API */
zend_user_it_get_new_iterator(zend_class_entry * ce,zval * object,int by_ref)236 ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object, int by_ref)
237 {
238 zval iterator;
239 zend_object_iterator *new_iterator;
240 zend_class_entry *ce_it;
241
242 zend_user_it_new_iterator(ce, object, &iterator);
243 ce_it = (Z_TYPE(iterator) == IS_OBJECT) ? Z_OBJCE(iterator) : NULL;
244
245 if (!ce_it || !ce_it->get_iterator || (ce_it->get_iterator == zend_user_it_get_new_iterator && Z_OBJ(iterator) == Z_OBJ_P(object))) {
246 if (!EG(exception)) {
247 zend_throw_exception_ex(NULL, 0, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce ? ZSTR_VAL(ce->name) : ZSTR_VAL(Z_OBJCE_P(object)->name));
248 }
249 zval_ptr_dtor(&iterator);
250 return NULL;
251 }
252
253 new_iterator = ce_it->get_iterator(ce_it, &iterator, by_ref);
254 zval_ptr_dtor(&iterator);
255 return new_iterator;
256 }
257 /* }}} */
258
259 /* {{{ zend_implement_traversable */
zend_implement_traversable(zend_class_entry * interface,zend_class_entry * class_type)260 static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
261 {
262 /* Abstract class can implement Traversable only, in which case the extending class must
263 * implement Iterator or IteratorAggregate. */
264 if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
265 return SUCCESS;
266 }
267
268 /* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
269 if (class_type->num_interfaces) {
270 ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
271 for (uint32_t i = 0; i < class_type->num_interfaces; i++) {
272 if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
273 return SUCCESS;
274 }
275 }
276 }
277 zend_error_noreturn(E_CORE_ERROR, "%s %s must implement interface %s as part of either %s or %s",
278 zend_get_object_type_uc(class_type),
279 ZSTR_VAL(class_type->name),
280 ZSTR_VAL(zend_ce_traversable->name),
281 ZSTR_VAL(zend_ce_iterator->name),
282 ZSTR_VAL(zend_ce_aggregate->name));
283 return FAILURE;
284 }
285 /* }}} */
286
287 /* {{{ zend_implement_aggregate */
zend_implement_aggregate(zend_class_entry * interface,zend_class_entry * class_type)288 static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type)
289 {
290 if (zend_class_implements_interface(class_type, zend_ce_iterator)) {
291 zend_error_noreturn(E_ERROR,
292 "Class %s cannot implement both Iterator and IteratorAggregate at the same time",
293 ZSTR_VAL(class_type->name));
294 }
295
296 /* Always initialize iterator_funcs_ptr. */
297 ZEND_ASSERT(!class_type->iterator_funcs_ptr && "Iterator funcs already set?");
298 zend_class_iterator_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
299 ? pemalloc(sizeof(zend_class_iterator_funcs), 1)
300 : zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
301 class_type->iterator_funcs_ptr = funcs_ptr;
302
303 memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
304 funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(
305 &class_type->function_table, "getiterator", sizeof("getiterator") - 1);
306
307 if (class_type->get_iterator
308 && class_type->get_iterator != zend_user_it_get_new_iterator
309 && class_type->get_iterator != zend_hooked_object_get_iterator) {
310 /* get_iterator was explicitly assigned for an internal class. */
311 if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) {
312 ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS);
313 return SUCCESS;
314 }
315
316 /* The getIterator() method has not been overwritten, use inherited get_iterator(). */
317 if (funcs_ptr->zf_new_iterator->common.scope != class_type) {
318 return SUCCESS;
319 }
320
321 /* getIterator() has been overwritten, switch to zend_user_it_get_new_iterator. */
322 }
323
324 class_type->get_iterator = zend_user_it_get_new_iterator;
325 return SUCCESS;
326 }
327 /* }}} */
328
329 /* {{{ zend_implement_iterator */
zend_implement_iterator(zend_class_entry * interface,zend_class_entry * class_type)330 static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type)
331 {
332 if (zend_class_implements_interface(class_type, zend_ce_aggregate)) {
333 zend_error_noreturn(E_ERROR,
334 "Class %s cannot implement both Iterator and IteratorAggregate at the same time",
335 ZSTR_VAL(class_type->name));
336 }
337
338 ZEND_ASSERT(!class_type->iterator_funcs_ptr && "Iterator funcs already set?");
339 zend_class_iterator_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
340 ? pemalloc(sizeof(zend_class_iterator_funcs), 1)
341 : zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
342 class_type->iterator_funcs_ptr = funcs_ptr;
343
344 memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
345 funcs_ptr->zf_rewind = zend_hash_str_find_ptr(
346 &class_type->function_table, "rewind", sizeof("rewind") - 1);
347 funcs_ptr->zf_valid = zend_hash_str_find_ptr(
348 &class_type->function_table, "valid", sizeof("valid") - 1);
349 funcs_ptr->zf_key = zend_hash_find_ptr(
350 &class_type->function_table, ZSTR_KNOWN(ZEND_STR_KEY));
351 funcs_ptr->zf_current = zend_hash_str_find_ptr(
352 &class_type->function_table, "current", sizeof("current") - 1);
353 funcs_ptr->zf_next = zend_hash_str_find_ptr(
354 &class_type->function_table, "next", sizeof("next") - 1);
355
356 if (class_type->get_iterator
357 && class_type->get_iterator != zend_user_it_get_iterator
358 && class_type->get_iterator != zend_hooked_object_get_iterator) {
359 if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) {
360 /* get_iterator was explicitly assigned for an internal class. */
361 ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS);
362 return SUCCESS;
363 }
364
365 /* None of the Iterator methods have been overwritten, use inherited get_iterator(). */
366 if (funcs_ptr->zf_rewind->common.scope != class_type &&
367 funcs_ptr->zf_valid->common.scope != class_type &&
368 funcs_ptr->zf_key->common.scope != class_type &&
369 funcs_ptr->zf_current->common.scope != class_type &&
370 funcs_ptr->zf_next->common.scope != class_type) {
371 return SUCCESS;
372 }
373
374 /* One of the Iterator methods has been overwritten,
375 * switch to zend_user_it_get_iterator. */
376 }
377
378 class_type->get_iterator = zend_user_it_get_iterator;
379 return SUCCESS;
380 }
381 /* }}} */
382
383 /* {{{ zend_implement_arrayaccess */
zend_implement_arrayaccess(zend_class_entry * interface,zend_class_entry * class_type)384 static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type)
385 {
386 ZEND_ASSERT(!class_type->arrayaccess_funcs_ptr && "ArrayAccess funcs already set?");
387 zend_class_arrayaccess_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
388 ? pemalloc(sizeof(zend_class_arrayaccess_funcs), 1)
389 : zend_arena_alloc(&CG(arena), sizeof(zend_class_arrayaccess_funcs));
390 class_type->arrayaccess_funcs_ptr = funcs_ptr;
391
392 funcs_ptr->zf_offsetget = zend_hash_str_find_ptr(
393 &class_type->function_table, "offsetget", sizeof("offsetget") - 1);
394 funcs_ptr->zf_offsetexists = zend_hash_str_find_ptr(
395 &class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
396 funcs_ptr->zf_offsetset = zend_hash_str_find_ptr(
397 &class_type->function_table, "offsetset", sizeof("offsetset") - 1);
398 funcs_ptr->zf_offsetunset = zend_hash_str_find_ptr(
399 &class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
400
401 return SUCCESS;
402 }
403 /* }}} */
404
405 /* {{{ zend_user_serialize */
zend_user_serialize(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)406 ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data)
407 {
408 zend_class_entry * ce = Z_OBJCE_P(object);
409 zval retval;
410 zend_result result;
411
412 zend_call_method_with_0_params(
413 Z_OBJ_P(object), Z_OBJCE_P(object), NULL, "serialize", &retval);
414
415 if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
416 result = FAILURE;
417 } else {
418 switch(Z_TYPE(retval)) {
419 case IS_NULL:
420 /* we could also make this '*buf_len = 0' but this allows to skip variables */
421 zval_ptr_dtor(&retval);
422 return FAILURE;
423 case IS_STRING:
424 *buffer = (unsigned char*)estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
425 *buf_len = Z_STRLEN(retval);
426 result = SUCCESS;
427 break;
428 default: /* failure */
429 result = FAILURE;
430 break;
431 }
432 zval_ptr_dtor(&retval);
433 }
434
435 if (result == FAILURE && !EG(exception)) {
436 zend_throw_exception_ex(NULL, 0, "%s::serialize() must return a string or NULL", ZSTR_VAL(ce->name));
437 }
438 return result;
439 }
440 /* }}} */
441
442 /* {{{ zend_user_unserialize */
zend_user_unserialize(zval * object,zend_class_entry * ce,const unsigned char * buf,size_t buf_len,zend_unserialize_data * data)443 ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data)
444 {
445 zval zdata;
446
447 if (UNEXPECTED(object_init_ex(object, ce) != SUCCESS)) {
448 return FAILURE;
449 }
450
451 ZVAL_STRINGL(&zdata, (char*)buf, buf_len);
452 zend_call_method_with_1_params(
453 Z_OBJ_P(object), Z_OBJCE_P(object), NULL, "unserialize", NULL, &zdata);
454 zval_ptr_dtor(&zdata);
455
456 if (EG(exception)) {
457 return FAILURE;
458 } else {
459 return SUCCESS;
460 }
461 }
462 /* }}} */
463
464 /* {{{ zend_implement_serializable */
zend_implement_serializable(zend_class_entry * interface,zend_class_entry * class_type)465 static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type)
466 {
467 if (class_type->parent
468 && (class_type->parent->serialize || class_type->parent->unserialize)
469 && !zend_class_implements_interface(class_type->parent, zend_ce_serializable)) {
470 return FAILURE;
471 }
472 if (!class_type->serialize) {
473 class_type->serialize = zend_user_serialize;
474 }
475 if (!class_type->unserialize) {
476 class_type->unserialize = zend_user_unserialize;
477 }
478 if (!(class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)
479 && (!class_type->__serialize || !class_type->__unserialize)) {
480 zend_error(E_DEPRECATED, "%s implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary)", ZSTR_VAL(class_type->name));
481 if (EG(exception)) {
482 zend_exception_uncaught_error(
483 "During inheritance of %s, while implementing Serializable", ZSTR_VAL(class_type->name));
484 }
485 }
486 return SUCCESS;
487 }
488 /* }}}*/
489
490 typedef struct {
491 zend_object std;
492 zend_object_iterator *iter;
493 bool rewind_called;
494 } zend_internal_iterator;
495
zend_internal_iterator_create(zend_class_entry * ce)496 static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
497 zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
498 zend_object_std_init(&intern->std, ce);
499 intern->iter = NULL;
500 intern->rewind_called = 0;
501 return &intern->std;
502 }
503
zend_create_internal_iterator_zval(zval * return_value,zval * obj)504 ZEND_API zend_result zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
505 zend_class_entry *scope = EG(current_execute_data)->func->common.scope;
506 ZEND_ASSERT(scope->get_iterator != zend_user_it_get_new_iterator);
507 zend_object_iterator *iter = scope->get_iterator(Z_OBJCE_P(obj), obj, /* by_ref */ 0);
508 if (!iter) {
509 return FAILURE;
510 }
511
512 zend_internal_iterator *intern =
513 (zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
514 intern->iter = iter;
515 intern->iter->index = 0;
516 ZVAL_OBJ(return_value, &intern->std);
517 return SUCCESS;
518 }
519
zend_internal_iterator_free(zend_object * obj)520 static void zend_internal_iterator_free(zend_object *obj) {
521 zend_internal_iterator *intern = (zend_internal_iterator *) obj;
522 if (intern->iter) {
523 zend_iterator_dtor(intern->iter);
524 }
525 zend_object_std_dtor(&intern->std);
526 }
527
zend_internal_iterator_fetch(zval * This)528 static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
529 zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
530 if (!intern->iter) {
531 zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
532 return NULL;
533 }
534 return intern;
535 }
536
537 /* Many iterators will not behave correctly if rewind() is not called, make sure it happens. */
zend_internal_iterator_ensure_rewound(zend_internal_iterator * intern)538 static zend_result zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
539 if (!intern->rewind_called) {
540 zend_object_iterator *iter = intern->iter;
541 intern->rewind_called = 1;
542 if (iter->funcs->rewind) {
543 iter->funcs->rewind(iter);
544 if (UNEXPECTED(EG(exception))) {
545 return FAILURE;
546 }
547 }
548 }
549 return SUCCESS;
550 }
551
552
ZEND_METHOD(InternalIterator,__construct)553 ZEND_METHOD(InternalIterator, __construct) {
554 zend_throw_error(NULL, "Cannot manually construct InternalIterator");
555 }
556
ZEND_METHOD(InternalIterator,current)557 ZEND_METHOD(InternalIterator, current) {
558 ZEND_PARSE_PARAMETERS_NONE();
559
560 zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
561 if (!intern) {
562 RETURN_THROWS();
563 }
564
565 if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
566 RETURN_THROWS();
567 }
568
569 zval *data = intern->iter->funcs->get_current_data(intern->iter);
570 if (data) {
571 RETURN_COPY_DEREF(data);
572 }
573 }
574
ZEND_METHOD(InternalIterator,key)575 ZEND_METHOD(InternalIterator, key) {
576 ZEND_PARSE_PARAMETERS_NONE();
577
578 zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
579 if (!intern) {
580 RETURN_THROWS();
581 }
582
583 if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
584 RETURN_THROWS();
585 }
586
587 if (intern->iter->funcs->get_current_key) {
588 intern->iter->funcs->get_current_key(intern->iter, return_value);
589 } else {
590 RETURN_LONG(intern->iter->index);
591 }
592 }
593
ZEND_METHOD(InternalIterator,next)594 ZEND_METHOD(InternalIterator, next) {
595 ZEND_PARSE_PARAMETERS_NONE();
596
597 zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
598 if (!intern) {
599 RETURN_THROWS();
600 }
601
602 if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
603 RETURN_THROWS();
604 }
605
606 /* Advance index first to match foreach behavior. */
607 intern->iter->index++;
608 intern->iter->funcs->move_forward(intern->iter);
609 }
610
ZEND_METHOD(InternalIterator,valid)611 ZEND_METHOD(InternalIterator, valid) {
612 ZEND_PARSE_PARAMETERS_NONE();
613
614 zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
615 if (!intern) {
616 RETURN_THROWS();
617 }
618
619 if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
620 RETURN_THROWS();
621 }
622
623 RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
624 }
625
ZEND_METHOD(InternalIterator,rewind)626 ZEND_METHOD(InternalIterator, rewind) {
627 ZEND_PARSE_PARAMETERS_NONE();
628
629 zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
630 if (!intern) {
631 RETURN_THROWS();
632 }
633
634 intern->rewind_called = 1;
635 if (!intern->iter->funcs->rewind) {
636 /* Allow calling rewind() if no iteration has happened yet,
637 * even if the iterator does not support rewinding. */
638 if (intern->iter->index != 0) {
639 zend_throw_error(NULL, "Iterator does not support rewinding");
640 RETURN_THROWS();
641 }
642 intern->iter->index = 0;
643 return;
644 }
645
646 intern->iter->funcs->rewind(intern->iter);
647 intern->iter->index = 0;
648 }
649
650 /* {{{ zend_register_interfaces */
zend_register_interfaces(void)651 ZEND_API void zend_register_interfaces(void)
652 {
653 zend_ce_traversable = register_class_Traversable();
654 zend_ce_traversable->interface_gets_implemented = zend_implement_traversable;
655
656 zend_ce_aggregate = register_class_IteratorAggregate(zend_ce_traversable);
657 zend_ce_aggregate->interface_gets_implemented = zend_implement_aggregate;
658
659 zend_ce_iterator = register_class_Iterator(zend_ce_traversable);
660 zend_ce_iterator->interface_gets_implemented = zend_implement_iterator;
661
662 zend_ce_serializable = register_class_Serializable();
663 zend_ce_serializable->interface_gets_implemented = zend_implement_serializable;
664
665 zend_ce_arrayaccess = register_class_ArrayAccess();
666 zend_ce_arrayaccess->interface_gets_implemented = zend_implement_arrayaccess;
667
668 zend_ce_countable = register_class_Countable();
669
670 zend_ce_stringable = register_class_Stringable();
671
672 zend_ce_internal_iterator = register_class_InternalIterator(zend_ce_iterator);
673 zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
674 zend_ce_internal_iterator->default_object_handlers = &zend_internal_iterator_handlers;
675
676 memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
677 sizeof(zend_object_handlers));
678 zend_internal_iterator_handlers.clone_obj = NULL;
679 zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
680 }
681 /* }}} */
682