Coverage Report

Created: 2025-06-13 06:43

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