Coverage Report

Created: 2026-06-13 07:01

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