xref: /PHP-8.4/Zend/zend_interfaces.c (revision 15a0c3a9)
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(&params[0], arg1);
47 	}
48 	if (param_count > 1) {
49 		ZVAL_COPY_VALUE(&params[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