Coverage Report

Created: 2025-09-27 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/spl/spl_array.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Marcus Boerger <helly@php.net>                              |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#ifdef HAVE_CONFIG_H
18
# include "config.h"
19
#endif
20
21
#include "php.h"
22
#include "ext/standard/php_var.h"
23
#include "zend_smart_str.h"
24
#include "zend_interfaces.h"
25
#include "zend_exceptions.h"
26
27
#include "spl_iterators.h"
28
#include "spl_array.h"
29
#include "spl_array_arginfo.h"
30
#include "spl_exceptions.h"
31
#include "spl_functions.h" /* For spl_set_private_debug_info_property() */
32
33
/* Defined later in the file */
34
PHPAPI zend_class_entry  *spl_ce_ArrayIterator;
35
PHPAPI zend_class_entry  *spl_ce_RecursiveArrayIterator;
36
37
/* ArrayObject class */
38
static zend_object_handlers spl_handler_ArrayObject;
39
PHPAPI zend_class_entry  *spl_ce_ArrayObject;
40
41
typedef struct _spl_array_object {
42
  zval              array;
43
  HashTable         *sentinel_array;
44
  uint32_t          ht_iter;
45
  int               ar_flags;
46
  unsigned char   nApplyCount;
47
  bool        is_child;
48
  Bucket        *bucket;
49
  zend_function     *fptr_offset_get;
50
  zend_function     *fptr_offset_set;
51
  zend_function     *fptr_offset_has;
52
  zend_function     *fptr_offset_del;
53
  zend_function     *fptr_count;
54
  zend_class_entry* ce_get_iterator;
55
  zend_object       std;
56
} spl_array_object;
57
58
947k
static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ {
59
947k
  return (spl_array_object*)((char*)(obj) - XtOffsetOf(spl_array_object, std));
60
947k
}
61
/* }}} */
62
63
214k
#define Z_SPLARRAY_P(zv)  spl_array_from_obj(Z_OBJ_P((zv)))
64
65
2.28k
static inline HashTable **spl_array_get_hash_table_ptr(spl_array_object* intern) { /* {{{ */
66
  //??? TODO: Delay duplication for arrays; only duplicate for write operations
67
2.28k
  if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
68
    /* rebuild properties */
69
0
    zend_std_get_properties_ex(&intern->std);
70
0
    return &intern->std.properties;
71
2.28k
  } else if (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
72
0
    spl_array_object *other = Z_SPLARRAY_P(&intern->array);
73
0
    return spl_array_get_hash_table_ptr(other);
74
2.28k
  } else if (Z_TYPE(intern->array) == IS_ARRAY) {
75
2.15k
    return &Z_ARRVAL(intern->array);
76
2.15k
  } else {
77
129
    zend_object *obj = Z_OBJ(intern->array);
78
    /* Since we're directly playing with the properties table, we shall initialize the lazy object directly.
79
     * If we don't, it's possible to continue working with the wrong object in case we're using a proxy. */
80
129
    if (UNEXPECTED(zend_lazy_object_must_init(obj))) {
81
0
      obj = zend_lazy_object_init(obj);
82
0
      if (UNEXPECTED(!obj)) {
83
0
        if (!intern->sentinel_array) {
84
0
          intern->sentinel_array = zend_new_array(0);
85
0
        }
86
0
        return &intern->sentinel_array;
87
0
      }
88
0
    }
89
    /* should no longer be lazy */
90
129
    ZEND_ASSERT(!zend_lazy_object_must_init(obj));
91
    /* rebuild properties */
92
129
    zend_std_get_properties_ex(obj);
93
129
    if (GC_REFCOUNT(obj->properties) > 1) {
94
0
      if (EXPECTED(!(GC_FLAGS(obj->properties) & IS_ARRAY_IMMUTABLE))) {
95
0
        GC_DELREF(obj->properties);
96
0
      }
97
0
      obj->properties = zend_array_dup(obj->properties);
98
0
    }
99
129
    return &obj->properties;
100
129
  }
101
2.28k
}
102
/* }}} */
103
104
2.28k
static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */
105
2.28k
  return *spl_array_get_hash_table_ptr(intern);
106
2.28k
}
107
/* }}} */
108
109
static inline bool spl_array_is_object(spl_array_object *intern) /* {{{ */
110
1.04k
{
111
1.04k
  while (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
112
0
    intern = Z_SPLARRAY_P(&intern->array);
113
0
  }
114
1.04k
  return (intern->ar_flags & SPL_ARRAY_IS_SELF) || Z_TYPE(intern->array) == IS_OBJECT;
115
1.04k
}
116
/* }}} */
117
118
static zend_result spl_array_skip_protected(spl_array_object *intern, HashTable *aht);
119
120
static zend_never_inline void spl_array_create_ht_iter(HashTable *ht, spl_array_object* intern) /* {{{ */
121
185
{
122
185
  intern->ht_iter = zend_hash_iterator_add(ht, zend_hash_get_current_pos(ht));
123
185
  zend_hash_internal_pointer_reset_ex(ht, &EG(ht_iterators)[intern->ht_iter].pos);
124
185
  spl_array_skip_protected(intern, ht);
125
185
}
126
/* }}} */
127
128
static zend_always_inline uint32_t *spl_array_get_pos_ptr(HashTable *ht, spl_array_object* intern) /* {{{ */
129
1.64k
{
130
1.64k
  if (UNEXPECTED(intern->ht_iter == (uint32_t)-1)) {
131
185
    spl_array_create_ht_iter(ht, intern);
132
185
  }
133
1.64k
  return &EG(ht_iterators)[intern->ht_iter].pos;
134
1.64k
}
135
/* }}} */
136
137
/* {{{ spl_array_object_free_storage */
138
static void spl_array_object_free_storage(zend_object *object)
139
215k
{
140
215k
  spl_array_object *intern = spl_array_from_obj(object);
141
142
215k
  if (intern->ht_iter != (uint32_t) -1) {
143
185
    zend_hash_iterator_del(intern->ht_iter);
144
185
  }
145
146
215k
  if (UNEXPECTED(intern->sentinel_array)) {
147
0
    zend_array_release(intern->sentinel_array);
148
0
  }
149
150
215k
  zend_object_std_dtor(&intern->std);
151
152
215k
  zval_ptr_dtor(&intern->array);
153
215k
}
154
/* }}} */
155
156
/* {{{ spl_array_object_new_ex */
157
static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig)
158
215k
{
159
215k
  spl_array_object *intern;
160
215k
  zend_class_entry *parent = class_type;
161
215k
  int inherited = 0;
162
163
215k
  intern = zend_object_alloc(sizeof(spl_array_object), parent);
164
165
215k
  zend_object_std_init(&intern->std, class_type);
166
215k
  object_properties_init(&intern->std, class_type);
167
168
215k
  intern->ar_flags = 0;
169
215k
  intern->is_child = false;
170
215k
  intern->bucket = NULL;
171
215k
  intern->ce_get_iterator = spl_ce_ArrayIterator;
172
215k
  if (orig) {
173
0
    spl_array_object *other = spl_array_from_obj(orig);
174
175
0
    intern->ar_flags &= ~ SPL_ARRAY_CLONE_MASK;
176
0
    intern->ar_flags |= (other->ar_flags & SPL_ARRAY_CLONE_MASK);
177
0
    intern->ce_get_iterator = other->ce_get_iterator;
178
0
    if (clone_orig) {
179
0
      if (other->ar_flags & SPL_ARRAY_IS_SELF) {
180
0
        ZVAL_UNDEF(&intern->array);
181
0
      } else if (instanceof_function(class_type, spl_ce_ArrayObject)) {
182
0
        ZVAL_ARR(&intern->array,
183
0
          zend_array_dup(spl_array_get_hash_table(other)));
184
0
      } else {
185
0
        #if ZEND_DEBUG
186
        /* This is because the call to instanceof_function will remain because
187
         * the compiler can't prove in this compile unit that this function is
188
         * side-effect-free.
189
         * See https://github.com/php/php-src/pull/14518#discussion_r1638740932 */
190
0
        ZEND_ASSERT(instanceof_function(class_type, spl_ce_ArrayIterator));
191
0
        #endif
192
193
0
        ZVAL_OBJ_COPY(&intern->array, orig);
194
0
        intern->ar_flags |= SPL_ARRAY_USE_OTHER;
195
0
      }
196
0
    } else {
197
0
      ZVAL_OBJ_COPY(&intern->array, orig);
198
0
      intern->ar_flags |= SPL_ARRAY_USE_OTHER;
199
0
    }
200
215k
  } else {
201
215k
    array_init(&intern->array);
202
215k
  }
203
204
215k
  while (parent) {
205
215k
    if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator || parent == spl_ce_ArrayObject) {
206
215k
      break;
207
215k
    }
208
42
    parent = parent->parent;
209
42
    inherited = 1;
210
42
  }
211
212
215k
  ZEND_ASSERT(parent);
213
214
215k
  if (inherited) {
215
42
    intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
216
42
    if (intern->fptr_offset_get->common.scope == parent) {
217
42
      intern->fptr_offset_get = NULL;
218
42
    }
219
42
    intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
220
42
    if (intern->fptr_offset_set->common.scope == parent) {
221
42
      intern->fptr_offset_set = NULL;
222
42
    }
223
42
    intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
224
42
    if (intern->fptr_offset_has->common.scope == parent) {
225
42
      intern->fptr_offset_has = NULL;
226
42
    }
227
42
    intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset",  sizeof("offsetunset") - 1);
228
42
    if (intern->fptr_offset_del->common.scope == parent) {
229
42
      intern->fptr_offset_del = NULL;
230
42
    }
231
    /* Find count() method */
232
42
    intern->fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT));
233
42
    if (intern->fptr_count->common.scope == parent) {
234
42
      intern->fptr_count = NULL;
235
42
    }
236
42
  }
237
238
215k
  intern->ht_iter = (uint32_t)-1;
239
215k
  return &intern->std;
240
215k
}
241
/* }}} */
242
243
/* {{{ spl_array_object_new */
244
static zend_object *spl_array_object_new(zend_class_entry *class_type)
245
215k
{
246
215k
  return spl_array_object_new_ex(class_type, NULL, 0);
247
215k
}
248
/* }}} */
249
250
/* {{{ spl_array_object_clone */
251
static zend_object *spl_array_object_clone(zend_object *old_object)
252
0
{
253
0
  zend_object *new_object;
254
255
0
  new_object = spl_array_object_new_ex(old_object->ce, old_object, 1);
256
257
0
  zend_objects_clone_members(new_object, old_object);
258
259
0
  return new_object;
260
0
}
261
/* }}} */
262
263
typedef struct {
264
  zend_string *key;
265
  zend_ulong h;
266
  bool release_key;
267
} spl_hash_key;
268
269
130
static void spl_hash_key_release(spl_hash_key *key) {
270
130
  if (key->release_key) {
271
0
    zend_string_release_ex(key->key, 0);
272
0
  }
273
130
}
274
275
/* This function does not throw any exceptions for illegal offsets, calls to
276
 * zend_illegal_container_offset(); need to be made if the return value is FAILURE */
277
static zend_result get_hash_key(spl_hash_key *key, spl_array_object *intern, zval *offset)
278
603
{
279
603
  key->release_key = false;
280
603
try_again:
281
603
  switch (Z_TYPE_P(offset)) {
282
0
  case IS_NULL:
283
0
    zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead");
284
0
    key->key = ZSTR_EMPTY_ALLOC();
285
0
    return SUCCESS;
286
165
  case IS_STRING:
287
165
    key->key = Z_STR_P(offset);
288
165
    if (ZEND_HANDLE_NUMERIC(key->key, key->h)) {
289
35
      key->key = NULL;
290
35
      break;
291
35
    }
292
130
    return SUCCESS;
293
0
  case IS_RESOURCE:
294
0
    zend_use_resource_as_offset(offset);
295
0
    key->key = NULL;
296
0
    key->h = Z_RES_P(offset)->handle;
297
0
    break;
298
6
  case IS_DOUBLE:
299
6
    key->key = NULL;
300
6
    key->h = zend_dval_to_lval_safe(Z_DVAL_P(offset));
301
6
    break;
302
9
  case IS_FALSE:
303
9
    key->key = NULL;
304
9
    key->h = 0;
305
9
    break;
306
0
  case IS_TRUE:
307
0
    key->key = NULL;
308
0
    key->h = 1;
309
0
    break;
310
423
  case IS_LONG:
311
423
    key->key = NULL;
312
423
    key->h = Z_LVAL_P(offset);
313
423
    break;
314
0
  case IS_REFERENCE:
315
0
    ZVAL_DEREF(offset);
316
0
    goto try_again;
317
0
  default:
318
0
    return FAILURE;
319
603
  }
320
321
473
  if (spl_array_is_object(intern)) {
322
0
    key->key = zend_long_to_str(key->h);
323
0
    key->release_key = true;
324
0
  }
325
473
  return SUCCESS;
326
603
}
327
328
static zval *spl_array_get_dimension_ptr(bool check_inherited, spl_array_object *intern, const zend_string *ce_name,
329
  zval *offset, int type) /* {{{ */
330
250
{
331
250
  zval *retval;
332
250
  spl_hash_key key;
333
250
  HashTable *ht = spl_array_get_hash_table(intern);
334
335
250
  if (!offset || Z_ISUNDEF_P(offset) || !ht) {
336
0
    return &EG(uninitialized_zval);
337
0
  }
338
339
250
  if ((type == BP_VAR_W || type == BP_VAR_RW) && intern->nApplyCount > 0) {
340
0
    zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
341
0
    return &EG(error_zval);
342
0
  }
343
344
250
  if (get_hash_key(&key, intern, offset) == FAILURE) {
345
0
    zend_illegal_container_offset(ce_name, offset, type);
346
0
    return (type == BP_VAR_W || type == BP_VAR_RW) ?
347
0
      &EG(error_zval) : &EG(uninitialized_zval);
348
0
  }
349
350
250
  if (key.key) {
351
54
    retval = zend_hash_find(ht, key.key);
352
54
    if (retval) {
353
49
      if (Z_TYPE_P(retval) == IS_INDIRECT) {
354
0
        retval = Z_INDIRECT_P(retval);
355
0
        if (Z_TYPE_P(retval) == IS_UNDEF) {
356
0
          switch (type) {
357
0
            case BP_VAR_R:
358
0
              zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(key.key));
359
0
              ZEND_FALLTHROUGH;
360
0
            case BP_VAR_UNSET:
361
0
            case BP_VAR_IS:
362
0
              retval = &EG(uninitialized_zval);
363
0
              break;
364
0
            case BP_VAR_RW:
365
0
              zend_error(E_WARNING,"Undefined array key \"%s\"", ZSTR_VAL(key.key));
366
0
              ZEND_FALLTHROUGH;
367
0
            case BP_VAR_W: {
368
0
              ZVAL_NULL(retval);
369
0
            }
370
0
          }
371
0
        }
372
0
      }
373
49
    } else {
374
5
      switch (type) {
375
5
        case BP_VAR_R:
376
5
          zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(key.key));
377
5
          ZEND_FALLTHROUGH;
378
5
        case BP_VAR_UNSET:
379
5
        case BP_VAR_IS:
380
5
          retval = &EG(uninitialized_zval);
381
5
          break;
382
0
        case BP_VAR_RW:
383
0
          zend_error(E_WARNING,"Undefined array key \"%s\"", ZSTR_VAL(key.key));
384
0
          ZEND_FALLTHROUGH;
385
0
        case BP_VAR_W: {
386
0
            zval value;
387
0
          ZVAL_NULL(&value);
388
0
            retval = zend_hash_update(ht, key.key, &value);
389
0
        }
390
5
      }
391
5
    }
392
54
    spl_hash_key_release(&key);
393
196
  } else {
394
196
    if ((retval = zend_hash_index_find(ht, key.h)) == NULL) {
395
106
      switch (type) {
396
12
        case BP_VAR_R:
397
12
          zend_error(E_WARNING, "Undefined array key " ZEND_LONG_FMT, key.h);
398
12
          ZEND_FALLTHROUGH;
399
12
        case BP_VAR_UNSET:
400
12
        case BP_VAR_IS:
401
12
          retval = &EG(uninitialized_zval);
402
12
          break;
403
0
        case BP_VAR_RW:
404
0
          zend_error(E_WARNING, "Undefined array key " ZEND_LONG_FMT, key.h);
405
0
          ZEND_FALLTHROUGH;
406
94
        case BP_VAR_W: {
407
94
            zval value;
408
94
          ZVAL_NULL(&value);
409
94
          retval = zend_hash_index_update(ht, key.h, &value);
410
94
         }
411
106
      }
412
106
    }
413
196
  }
414
250
  return retval;
415
250
} /* }}} */
416
417
static int spl_array_has_dimension(zend_object *object, zval *offset, int check_empty);
418
419
static zval *spl_array_read_dimension_ex(int check_inherited, zend_object *object, zval *offset, int type, zval *rv) /* {{{ */
420
250
{
421
250
  spl_array_object *intern = spl_array_from_obj(object);
422
250
  zval *ret;
423
424
250
  if (check_inherited &&
425
226
      (intern->fptr_offset_get || (type == BP_VAR_IS && intern->fptr_offset_has))) {
426
0
    if (type == BP_VAR_IS) {
427
0
      if (!spl_array_has_dimension(object, offset, 0)) {
428
0
        return &EG(uninitialized_zval);
429
0
      }
430
0
    }
431
432
0
    if (intern->fptr_offset_get) {
433
0
      zval tmp;
434
0
      if (!offset) {
435
0
        ZVAL_UNDEF(&tmp);
436
0
        offset = &tmp;
437
0
      }
438
0
      zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_get, "offsetGet", rv, offset);
439
440
0
      if (!Z_ISUNDEF_P(rv)) {
441
0
        return rv;
442
0
      }
443
0
      return &EG(uninitialized_zval);
444
0
    }
445
0
  }
446
447
250
  ret = spl_array_get_dimension_ptr(check_inherited, intern, object->ce->name, offset, type);
448
449
  /* When in a write context,
450
   * ZE has to be fooled into thinking this is in a reference set
451
   * by separating (if necessary) and returning as IS_REFERENCE (with refcount == 1)
452
   */
453
454
250
  if ((type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) &&
455
250
      !Z_ISREF_P(ret) &&
456
139
      EXPECTED(ret != &EG(uninitialized_zval))) {
457
139
    ZVAL_NEW_REF(ret, ret);
458
139
  }
459
460
250
  return ret;
461
250
} /* }}} */
462
463
static zval *spl_array_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */
464
226
{
465
226
  return spl_array_read_dimension_ex(1, object, offset, type, rv);
466
226
} /* }}} */
467
468
/*
469
 * The assertion(HT_ASSERT_RC1(ht)) failed because the refcount was increased manually when intern->is_child is true.
470
 * We have to set the refcount to 1 to make assertion success and restore the refcount to the original value after
471
 * modifying the array when intern->is_child is true.
472
 */
473
static uint32_t spl_array_set_refcount(bool is_child, HashTable *ht, uint32_t refcount) /* {{{ */
474
260
{
475
260
  uint32_t old_refcount = 0;
476
260
  if (is_child) {
477
0
    old_refcount = GC_REFCOUNT(ht);
478
0
    GC_SET_REFCOUNT(ht, refcount);
479
0
  }
480
481
260
  return old_refcount;
482
260
} /* }}} */
483
484
static void spl_array_write_dimension_ex(int check_inherited, zend_object *object, zval *offset, zval *value) /* {{{ */
485
165
{
486
165
  spl_array_object *intern = spl_array_from_obj(object);
487
165
  HashTable *ht;
488
165
  spl_hash_key key;
489
490
165
  if (check_inherited && intern->fptr_offset_set) {
491
0
    zval tmp;
492
493
0
    if (!offset) {
494
0
      ZVAL_NULL(&tmp);
495
0
      offset = &tmp;
496
0
    }
497
0
    zend_call_method_with_2_params(object, object->ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
498
0
    return;
499
0
  }
500
501
165
  if (intern->nApplyCount > 0) {
502
0
    zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
503
0
    return;
504
0
  }
505
506
165
  Z_TRY_ADDREF_P(value);
507
508
165
  uint32_t refcount = 0;
509
165
  if (!offset || Z_TYPE_P(offset) == IS_NULL) {
510
5
    ht = spl_array_get_hash_table(intern);
511
5
    if (UNEXPECTED(ht == intern->sentinel_array)) {
512
0
      return;
513
0
    }
514
5
    refcount = spl_array_set_refcount(intern->is_child, ht, 1);
515
5
    zend_hash_next_index_insert(ht, value);
516
517
5
    if (refcount) {
518
0
      spl_array_set_refcount(intern->is_child, ht, refcount);
519
0
    }
520
5
    return;
521
5
  }
522
523
160
  if (get_hash_key(&key, intern, offset) == FAILURE) {
524
0
    zend_illegal_container_offset(object->ce->name, offset, BP_VAR_W);
525
0
    zval_ptr_dtor(value);
526
0
    return;
527
0
  }
528
529
160
  ht = spl_array_get_hash_table(intern);
530
160
  if (UNEXPECTED(ht == intern->sentinel_array)) {
531
0
    spl_hash_key_release(&key);
532
0
    return;
533
0
  }
534
160
  refcount = spl_array_set_refcount(intern->is_child, ht, 1);
535
160
  if (key.key) {
536
70
    zend_hash_update_ind(ht, key.key, value);
537
70
    spl_hash_key_release(&key);
538
90
  } else {
539
90
    zend_hash_index_update(ht, key.h, value);
540
90
  }
541
542
160
  if (refcount) {
543
0
    spl_array_set_refcount(intern->is_child, ht, refcount);
544
0
  }
545
160
} /* }}} */
546
547
static void spl_array_write_dimension(zend_object *object, zval *offset, zval *value) /* {{{ */
548
165
{
549
165
  spl_array_write_dimension_ex(1, object, offset, value);
550
165
} /* }}} */
551
552
static void spl_array_unset_dimension_ex(int check_inherited, zend_object *object, zval *offset) /* {{{ */
553
95
{
554
95
  HashTable *ht;
555
95
  spl_array_object *intern = spl_array_from_obj(object);
556
95
  spl_hash_key key;
557
558
95
  if (check_inherited && intern->fptr_offset_del) {
559
0
    zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset);
560
0
    return;
561
0
  }
562
563
95
  if (intern->nApplyCount > 0) {
564
0
    zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
565
0
    return;
566
0
  }
567
568
95
  if (get_hash_key(&key, intern, offset) == FAILURE) {
569
0
    zend_illegal_container_offset(object->ce->name, offset, BP_VAR_UNSET);
570
0
    return;
571
0
  }
572
573
95
  ht = spl_array_get_hash_table(intern);
574
95
  uint32_t refcount = spl_array_set_refcount(intern->is_child, ht, 1);
575
576
95
  if (key.key) {
577
6
    zval *data = zend_hash_find(ht, key.key);
578
6
    if (data) {
579
0
      if (Z_TYPE_P(data) == IS_INDIRECT) {
580
0
        data = Z_INDIRECT_P(data);
581
0
        if (Z_TYPE_P(data) != IS_UNDEF) {
582
0
          zval garbage;
583
0
          ZVAL_COPY_VALUE(&garbage, data);
584
0
          ZVAL_UNDEF(data);
585
0
          HT_FLAGS(ht) |= HASH_FLAG_HAS_EMPTY_IND;
586
0
          zend_hash_move_forward_ex(ht, spl_array_get_pos_ptr(ht, intern));
587
0
          if (spl_array_is_object(intern)) {
588
0
            spl_array_skip_protected(intern, ht);
589
0
          }
590
0
          zval_ptr_dtor(&garbage);
591
0
        }
592
0
      } else {
593
0
        zend_hash_del(ht, key.key);
594
0
      }
595
0
    }
596
6
    spl_hash_key_release(&key);
597
89
  } else {
598
89
    zend_hash_index_del(ht, key.h);
599
89
  }
600
601
95
  if (refcount) {
602
0
    spl_array_set_refcount(intern->is_child, ht, refcount);
603
0
  }
604
95
} /* }}} */
605
606
static void spl_array_unset_dimension(zend_object *object, zval *offset) /* {{{ */
607
89
{
608
89
  spl_array_unset_dimension_ex(1, object, offset);
609
89
} /* }}} */
610
611
/* check_empty can take value 0, 1, or 2
612
 * 0/1 are used as normal boolean, but 2 is used for the case when this function is called from
613
 * the offsetExists() method, in which case it needs to report the offset exist even if the value is null */
614
static bool spl_array_has_dimension_ex(bool check_inherited, zend_object *object, zval *offset, int check_empty) /* {{{ */
615
98
{
616
98
  spl_array_object *intern = spl_array_from_obj(object);
617
98
  zval rv, *value = NULL, *tmp;
618
619
98
  if (check_inherited && intern->fptr_offset_has) {
620
0
    zend_call_method_with_1_params(object, object->ce, &intern->fptr_offset_has, "offsetExists", &rv, offset);
621
622
0
    if (!zend_is_true(&rv)) {
623
0
      zval_ptr_dtor(&rv);
624
0
      return false;
625
0
    }
626
0
    zval_ptr_dtor(&rv);
627
628
    /* For isset calls we don't need to check the value, so return early */
629
0
    if (!check_empty) {
630
0
      return true;
631
0
    } else if (intern->fptr_offset_get) {
632
0
      value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R, &rv);
633
0
    }
634
0
  }
635
636
98
  if (!value) {
637
98
    HashTable *ht = spl_array_get_hash_table(intern);
638
98
    spl_hash_key key;
639
640
98
    if (get_hash_key(&key, intern, offset) == FAILURE) {
641
0
      zend_illegal_container_offset(object->ce->name, offset, BP_VAR_IS);
642
0
      return false;
643
0
    }
644
645
98
    if (key.key) {
646
0
      tmp = zend_hash_find(ht, key.key);
647
0
      spl_hash_key_release(&key);
648
98
    } else {
649
98
      tmp = zend_hash_index_find(ht, key.h);
650
98
    }
651
652
98
    if (!tmp) {
653
4
      return false;
654
4
    }
655
656
    /* check_empty is only equal to 2 if it is called from offsetExists on this class,
657
     * where it needs to report an offset exists even if the value is null */
658
94
    if (check_empty == 2) {
659
0
      return true;
660
0
    }
661
662
94
    if (check_empty && check_inherited && intern->fptr_offset_get) {
663
0
      value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R, &rv);
664
94
    } else {
665
94
      value = tmp;
666
94
    }
667
94
  }
668
669
  /* empty() check the value is not falsy, isset() only check it is not null */
670
98
  bool result = check_empty ? zend_is_true(value) : Z_TYPE_P(value) != IS_NULL;
671
672
94
  if (value == &rv) {
673
0
    zval_ptr_dtor(&rv);
674
0
  }
675
676
94
  return result;
677
98
} /* }}} */
678
679
static int spl_array_has_dimension(zend_object *object, zval *offset, int check_empty) /* {{{ */
680
98
{
681
98
  return spl_array_has_dimension_ex(/* check_inherited */ true, object, offset, check_empty);
682
98
} /* }}} */
683
684
/* {{{ Returns whether the requested $index exists. */
685
PHP_METHOD(ArrayObject, offsetExists)
686
0
{
687
0
  zval *index;
688
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
689
0
    RETURN_THROWS();
690
0
  }
691
0
  RETURN_BOOL(spl_array_has_dimension_ex(/* check_inherited */ false, Z_OBJ_P(ZEND_THIS), index, 2));
692
0
} /* }}} */
693
694
/* {{{ Returns the value at the specified $index. */
695
PHP_METHOD(ArrayObject, offsetGet)
696
24
{
697
24
  zval *value, *index;
698
24
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
699
0
    RETURN_THROWS();
700
0
  }
701
24
  value = spl_array_read_dimension_ex(0, Z_OBJ_P(ZEND_THIS), index, BP_VAR_R, return_value);
702
24
  if (value != return_value) {
703
24
    RETURN_COPY_DEREF(value);
704
24
  }
705
24
} /* }}} */
706
707
/* {{{ Sets the value at the specified $index to $newval. */
708
PHP_METHOD(ArrayObject, offsetSet)
709
0
{
710
0
  zval *index, *value;
711
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &index, &value) == FAILURE) {
712
0
    RETURN_THROWS();
713
0
  }
714
0
  spl_array_write_dimension_ex(0, Z_OBJ_P(ZEND_THIS), index, value);
715
0
} /* }}} */
716
717
void spl_array_iterator_append(zval *object, zval *append_value) /* {{{ */
718
0
{
719
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
720
721
0
  if (spl_array_is_object(intern)) {
722
0
    zend_throw_error(NULL, "Cannot append properties to objects, use %s::offsetSet() instead", ZSTR_VAL(Z_OBJCE_P(object)->name));
723
0
    return;
724
0
  }
725
726
0
  spl_array_write_dimension(Z_OBJ_P(object), NULL, append_value);
727
0
} /* }}} */
728
729
/* {{{ Appends the value (cannot be called for objects). */
730
PHP_METHOD(ArrayObject, append)
731
0
{
732
0
  zval *value;
733
734
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
735
0
    RETURN_THROWS();
736
0
  }
737
0
  spl_array_iterator_append(ZEND_THIS, value);
738
0
} /* }}} */
739
740
/* {{{ Unsets the value at the specified $index. */
741
PHP_METHOD(ArrayObject, offsetUnset)
742
6
{
743
6
  zval *index;
744
6
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &index) == FAILURE) {
745
0
    RETURN_THROWS();
746
0
  }
747
6
  spl_array_unset_dimension_ex(0, Z_OBJ_P(ZEND_THIS), index);
748
6
} /* }}} */
749
750
/* {{{ Return a copy of the contained array */
751
PHP_METHOD(ArrayObject, getArrayCopy)
752
0
{
753
0
  zval *object = ZEND_THIS;
754
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
755
756
0
  if (zend_parse_parameters_none() == FAILURE) {
757
0
    RETURN_THROWS();
758
0
  }
759
760
0
  RETURN_ARR(zend_array_dup(spl_array_get_hash_table(intern)));
761
0
} /* }}} */
762
763
static HashTable *spl_array_get_properties_for(zend_object *object, zend_prop_purpose purpose) /* {{{ */
764
54
{
765
54
  spl_array_object *intern = spl_array_from_obj(object);
766
54
  HashTable *ht;
767
54
  bool dup;
768
769
54
  if (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) {
770
0
    return zend_std_get_properties_for(object, purpose);
771
0
  }
772
773
  /* We are supposed to be the only owner of the internal hashtable.
774
   * The "dup" flag decides whether this is a "long-term" use where
775
   * we need to duplicate, or a "temporary" one, where we can expect
776
   * that no operations on the ArrayObject will be performed in the
777
   * meantime. */
778
54
  switch (purpose) {
779
7
    case ZEND_PROP_PURPOSE_ARRAY_CAST:
780
7
      dup = true;
781
7
      break;
782
0
    case ZEND_PROP_PURPOSE_VAR_EXPORT:
783
0
    case ZEND_PROP_PURPOSE_JSON:
784
0
      dup = false;
785
0
      break;
786
47
    default:
787
47
      return zend_std_get_properties_for(object, purpose);
788
54
  }
789
790
7
  ht = spl_array_get_hash_table(intern);
791
7
  if (dup) {
792
7
    ht = zend_array_dup(ht);
793
7
  } else {
794
0
    GC_ADDREF(ht);
795
0
  }
796
7
  return ht;
797
54
} /* }}} */
798
799
static inline HashTable* spl_array_get_debug_info(zend_object *obj) /* {{{ */
800
47
{
801
47
  spl_array_object *intern = spl_array_from_obj(obj);
802
47
  HashTable *properties = zend_std_get_properties_ex(&intern->std);
803
804
47
  if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
805
0
    return zend_array_dup(properties);
806
47
  } else {
807
47
    HashTable *debug_info;
808
809
47
    debug_info = zend_new_array(zend_hash_num_elements(properties) + 1);
810
47
    zend_hash_copy(debug_info, properties, (copy_ctor_func_t) zval_add_ref);
811
812
47
    zval *storage = &intern->array;
813
47
    Z_TRY_ADDREF_P(storage);
814
815
47
    const zend_class_entry *base_class_ce = instanceof_function(obj->ce, spl_ce_ArrayIterator)
816
47
      ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
817
818
47
    spl_set_private_debug_info_property(base_class_ce, "storage", strlen("storage"), debug_info, storage);
819
820
47
    return debug_info;
821
47
  }
822
47
}
823
/* }}} */
824
825
static HashTable *spl_array_get_gc(zend_object *obj, zval **gc_data, int *gc_data_count) /* {{{ */
826
516k
{
827
516k
  spl_array_object *intern = spl_array_from_obj(obj);
828
516k
  *gc_data = &intern->array;
829
516k
  *gc_data_count = 1;
830
516k
  return zend_std_get_properties(obj);
831
516k
}
832
/* }}} */
833
834
static zval *spl_array_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
835
0
{
836
0
  spl_array_object *intern = spl_array_from_obj(object);
837
838
0
  if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
839
0
    && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
840
0
    zval member;
841
0
    ZVAL_STR(&member, name);
842
0
    return spl_array_read_dimension(object, &member, type, rv);
843
0
  }
844
0
  return zend_std_read_property(object, name, type, cache_slot, rv);
845
0
} /* }}} */
846
847
static zval *spl_array_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) /* {{{ */
848
10
{
849
10
  spl_array_object *intern = spl_array_from_obj(object);
850
851
10
  if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
852
0
  && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
853
0
    zval member;
854
0
    ZVAL_STR(&member, name);
855
0
    spl_array_write_dimension(object, &member, value);
856
0
    return value;
857
0
  }
858
10
  return zend_std_write_property(object, name, value, cache_slot);
859
10
} /* }}} */
860
861
static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) /* {{{ */
862
0
{
863
0
  spl_array_object *intern = spl_array_from_obj(object);
864
865
0
  if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
866
0
    && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
867
0
    if (cache_slot) {
868
0
      cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL;
869
0
    }
870
871
    /* If object has offsetGet() overridden, then fallback to read_property,
872
     * which will call offsetGet(). */
873
0
    zval member;
874
0
    if (intern->fptr_offset_get) {
875
0
      return NULL;
876
0
    }
877
0
    ZVAL_STR(&member, name);
878
0
    return spl_array_get_dimension_ptr(true, intern, object->ce->name, &member, type);
879
0
  }
880
0
  return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
881
0
} /* }}} */
882
883
static int spl_array_has_property(zend_object *object, zend_string *name, int has_set_exists, void **cache_slot) /* {{{ */
884
7
{
885
7
  spl_array_object *intern = spl_array_from_obj(object);
886
887
7
  if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
888
0
    && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
889
0
    zval member;
890
0
    ZVAL_STR(&member, name);
891
0
    return spl_array_has_dimension(object, &member, has_set_exists);
892
0
  }
893
7
  return zend_std_has_property(object, name, has_set_exists, cache_slot);
894
7
} /* }}} */
895
896
static void spl_array_unset_property(zend_object *object, zend_string *name, void **cache_slot) /* {{{ */
897
0
{
898
0
  spl_array_object *intern = spl_array_from_obj(object);
899
900
0
  if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
901
0
    && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) {
902
0
    zval member;
903
0
    ZVAL_STR(&member, name);
904
0
    spl_array_unset_dimension(object, &member);
905
0
    return;
906
0
  }
907
0
  zend_std_unset_property(object, name, cache_slot);
908
0
} /* }}} */
909
910
static int spl_array_compare_objects(zval *o1, zval *o2) /* {{{ */
911
18
{
912
18
  HashTable     *ht1,
913
18
            *ht2;
914
18
  spl_array_object  *intern1,
915
18
            *intern2;
916
18
  int         result  = 0;
917
918
18
  ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
919
920
0
  intern1 = Z_SPLARRAY_P(o1);
921
0
  intern2 = Z_SPLARRAY_P(o2);
922
0
  ht1   = spl_array_get_hash_table(intern1);
923
0
  ht2   = spl_array_get_hash_table(intern2);
924
925
0
  result = zend_compare_symbol_tables(ht1, ht2);
926
  /* if we just compared std.properties, don't do it again */
927
0
  if (result == 0 &&
928
0
      !(ht1 == intern1->std.properties && ht2 == intern2->std.properties)) {
929
0
    result = zend_std_compare_objects(o1, o2);
930
0
  }
931
0
  return result;
932
18
} /* }}} */
933
934
static zend_result spl_array_skip_protected(spl_array_object *intern, HashTable *aht) /* {{{ */
935
215
{
936
215
  zend_string *string_key;
937
215
  zend_ulong num_key;
938
215
  zval *data;
939
940
215
  if (spl_array_is_object(intern)) {
941
49
    uint32_t *pos_ptr = spl_array_get_pos_ptr(aht, intern);
942
943
63
    do {
944
63
      if (zend_hash_get_current_key_ex(aht, &string_key, &num_key, pos_ptr) == HASH_KEY_IS_STRING) {
945
36
        data = zend_hash_get_current_data_ex(aht, pos_ptr);
946
36
        if (data && Z_TYPE_P(data) == IS_INDIRECT &&
947
34
            Z_TYPE_P(data = Z_INDIRECT_P(data)) == IS_UNDEF) {
948
          /* skip */
949
36
        } else if (!ZSTR_LEN(string_key) || ZSTR_VAL(string_key)[0]) {
950
22
          return SUCCESS;
951
22
        }
952
36
      } else {
953
27
        return SUCCESS;
954
27
      }
955
14
      if (zend_hash_has_more_elements_ex(aht, pos_ptr) != SUCCESS) {
956
0
        return FAILURE;
957
0
      }
958
14
      zend_hash_move_forward_ex(aht, pos_ptr);
959
14
    } while (1);
960
49
  }
961
166
  return FAILURE;
962
215
} /* }}} */
963
964
/* {{{ spl_array_set_array */
965
3.19k
static void spl_array_set_array(zval *object, spl_array_object *intern, zval *array, zend_long ar_flags, bool just_array) {
966
  /* Handled by ZPP prior to this, or for __unserialize() before passing to here */
967
3.19k
  ZEND_ASSERT(Z_TYPE_P(array) == IS_ARRAY || Z_TYPE_P(array) == IS_OBJECT);
968
3.19k
  zval garbage;
969
3.19k
  ZVAL_UNDEF(&garbage);
970
3.19k
  if (Z_TYPE_P(array) == IS_ARRAY) {
971
217
    ZVAL_COPY_VALUE(&garbage, &intern->array);
972
217
    if (Z_REFCOUNT_P(array) == 1) {
973
13
      ZVAL_COPY(&intern->array, array);
974
204
    } else {
975
      //??? TODO: try to avoid array duplication
976
204
      ZVAL_ARR(&intern->array, zend_array_dup(Z_ARR_P(array)));
977
978
204
      if (intern->is_child) {
979
5
        Z_TRY_DELREF(intern->bucket->val);
980
        /*
981
         * replace bucket->val with copied array, so the changes between
982
         * parent and child object can affect each other.
983
         */
984
5
        ZVAL_COPY(&intern->bucket->val, &intern->array);
985
5
      }
986
204
    }
987
2.97k
  } else {
988
2.97k
    php_error_docref(NULL, E_DEPRECATED,
989
2.97k
      "Using an object as a backing array for %s is deprecated, as it allows violating class constraints and invariants",
990
2.97k
      instanceof_function(Z_OBJCE_P(object), spl_ce_ArrayIterator) ? "ArrayIterator" : "ArrayObject");
991
2.97k
    if (UNEXPECTED(EG(exception))) {
992
0
      return;
993
0
    }
994
2.97k
    if (Z_OBJ_HT_P(array) == &spl_handler_ArrayObject) {
995
281
      ZVAL_COPY_VALUE(&garbage, &intern->array);
996
281
      if (just_array) {
997
281
        spl_array_object *other = Z_SPLARRAY_P(array);
998
281
        ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
999
281
      }
1000
281
      if (Z_OBJ_P(object) == Z_OBJ_P(array)) {
1001
60
        ar_flags |= SPL_ARRAY_IS_SELF;
1002
60
        ZVAL_UNDEF(&intern->array);
1003
221
      } else {
1004
221
        ar_flags |= SPL_ARRAY_USE_OTHER;
1005
221
        ZVAL_COPY(&intern->array, array);
1006
221
      }
1007
2.69k
    } else {
1008
2.69k
      zend_object_get_properties_t handler = Z_OBJ_HANDLER_P(array, get_properties);
1009
2.69k
      if (handler != zend_std_get_properties || Z_OBJ_HANDLER_P(array, get_properties_for)) {
1010
2
        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
1011
2
          "Overloaded object of type %s is not compatible with %s",
1012
2
          ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(intern->std.ce->name));
1013
2
        ZEND_ASSERT(Z_TYPE(garbage) == IS_UNDEF);
1014
2
        return;
1015
2
      }
1016
2.69k
      if (UNEXPECTED(Z_OBJCE_P(array)->ce_flags & ZEND_ACC_ENUM)) {
1017
0
        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
1018
0
          "Enums are not compatible with %s",
1019
0
          ZSTR_VAL(intern->std.ce->name));
1020
0
        ZEND_ASSERT(Z_TYPE(garbage) == IS_UNDEF);
1021
0
        return;
1022
0
      }
1023
2.69k
      ZVAL_COPY_VALUE(&garbage, &intern->array);
1024
2.69k
      ZVAL_COPY(&intern->array, array);
1025
2.69k
    }
1026
2.97k
  }
1027
1028
3.18k
  intern->ar_flags &= ~SPL_ARRAY_IS_SELF & ~SPL_ARRAY_USE_OTHER;
1029
3.18k
  intern->ar_flags |= ar_flags;
1030
3.18k
  if (intern->ht_iter != (uint32_t)-1) {
1031
0
    zend_hash_iterator_del(intern->ht_iter);
1032
0
    intern->ht_iter = (uint32_t)-1;
1033
0
  }
1034
1035
3.18k
  zval_ptr_dtor(&garbage);
1036
3.18k
}
1037
/* }}} */
1038
1039
/* {{{ Constructs a new array object from an array or object. */
1040
PHP_METHOD(ArrayObject, __construct)
1041
240
{
1042
240
  zval *object = ZEND_THIS;
1043
240
  spl_array_object *intern;
1044
240
  zval *array;
1045
240
  zend_long ar_flags = 0;
1046
240
  zend_class_entry *ce_get_iterator = spl_ce_ArrayIterator;
1047
1048
240
  if (ZEND_NUM_ARGS() == 0) {
1049
191
    return; /* nothing to do */
1050
191
  }
1051
1052
49
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|AlC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
1053
8
    RETURN_THROWS();
1054
8
  }
1055
1056
41
  intern = Z_SPLARRAY_P(object);
1057
1058
41
  if (ZEND_NUM_ARGS() > 2) {
1059
0
    intern->ce_get_iterator = ce_get_iterator;
1060
0
  }
1061
1062
41
  ar_flags &= ~SPL_ARRAY_INT_MASK;
1063
1064
41
  spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1);
1065
41
}
1066
/* }}} */
1067
1068
/* {{{ Set the class used in getIterator. */
1069
PHP_METHOD(ArrayObject, setIteratorClass)
1070
0
{
1071
0
  zval *object = ZEND_THIS;
1072
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1073
0
  zend_class_entry *ce_get_iterator = spl_ce_ArrayIterator;
1074
1075
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
1076
0
    Z_PARAM_CLASS(ce_get_iterator)
1077
0
  ZEND_PARSE_PARAMETERS_END();
1078
1079
0
  intern->ce_get_iterator = ce_get_iterator;
1080
0
}
1081
/* }}} */
1082
1083
/* {{{ Get the class used in getIterator. */
1084
PHP_METHOD(ArrayObject, getIteratorClass)
1085
0
{
1086
0
  zval *object = ZEND_THIS;
1087
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1088
1089
0
  if (zend_parse_parameters_none() == FAILURE) {
1090
0
    RETURN_THROWS();
1091
0
  }
1092
1093
0
  zend_string_addref(intern->ce_get_iterator->name);
1094
0
  RETURN_STR(intern->ce_get_iterator->name);
1095
0
}
1096
/* }}} */
1097
1098
/* {{{ Get flags */
1099
PHP_METHOD(ArrayObject, getFlags)
1100
0
{
1101
0
  zval *object = ZEND_THIS;
1102
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1103
1104
0
  if (zend_parse_parameters_none() == FAILURE) {
1105
0
    RETURN_THROWS();
1106
0
  }
1107
1108
0
  RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
1109
0
}
1110
/* }}} */
1111
1112
/* {{{ Set flags */
1113
PHP_METHOD(ArrayObject, setFlags)
1114
0
{
1115
0
  zval *object = ZEND_THIS;
1116
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1117
0
  zend_long ar_flags = 0;
1118
1119
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ar_flags) == FAILURE) {
1120
0
    RETURN_THROWS();
1121
0
  }
1122
1123
0
  intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
1124
0
}
1125
/* }}} */
1126
1127
/* {{{ Replace the referenced array or object with a new one and return the old one (right now copy - to be changed) */
1128
PHP_METHOD(ArrayObject, exchangeArray)
1129
0
{
1130
0
  zval *object = ZEND_THIS, *array;
1131
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1132
1133
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "A", &array) == FAILURE) {
1134
0
    RETURN_THROWS();
1135
0
  }
1136
1137
0
  if (intern->nApplyCount > 0) {
1138
0
    zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
1139
0
    RETURN_THROWS();
1140
0
  }
1141
1142
0
  RETVAL_ARR(zend_array_dup(spl_array_get_hash_table(intern)));
1143
0
  spl_array_set_array(object, intern, array, 0L, true);
1144
0
}
1145
/* }}} */
1146
1147
/* {{{ Create a new iterator from a ArrayObject instance */
1148
PHP_METHOD(ArrayObject, getIterator)
1149
0
{
1150
0
  zval *object = ZEND_THIS;
1151
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1152
1153
0
  if (zend_parse_parameters_none() == FAILURE) {
1154
0
    RETURN_THROWS();
1155
0
  }
1156
1157
0
  RETURN_OBJ(spl_array_object_new_ex(intern->ce_get_iterator, Z_OBJ_P(object), 0));
1158
0
}
1159
/* }}} */
1160
1161
static zend_long spl_array_object_count_elements_helper(spl_array_object *intern) /* {{{ */
1162
87
{
1163
87
  HashTable *aht = spl_array_get_hash_table(intern);
1164
87
  if (spl_array_is_object(intern)) {
1165
0
    zend_long count = 0;
1166
0
    zend_string *key;
1167
0
    zval *val;
1168
    /* Count public/dynamic properties */
1169
0
    ZEND_HASH_FOREACH_STR_KEY_VAL(aht, key, val) {
1170
0
      if (Z_TYPE_P(val) == IS_INDIRECT) {
1171
0
        if (Z_TYPE_P(Z_INDIRECT_P(val)) == IS_UNDEF) continue;
1172
0
        if (key && ZSTR_VAL(key)[0] == '\0') continue;
1173
0
      }
1174
0
      count++;
1175
0
    } ZEND_HASH_FOREACH_END();
1176
0
    return count;
1177
87
  } else {
1178
87
    return zend_hash_num_elements(aht);
1179
87
  }
1180
87
} /* }}} */
1181
1182
static zend_result spl_array_object_count_elements(zend_object *object, zend_long *count) /* {{{ */
1183
0
{
1184
0
  spl_array_object *intern = spl_array_from_obj(object);
1185
1186
0
  if (intern->fptr_count) {
1187
0
    zval rv;
1188
0
    zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
1189
0
    if (Z_TYPE(rv) != IS_UNDEF) {
1190
0
      *count = zval_get_long(&rv);
1191
0
      zval_ptr_dtor(&rv);
1192
0
      return SUCCESS;
1193
0
    }
1194
0
    *count = 0;
1195
0
    return FAILURE;
1196
0
  }
1197
0
  *count = spl_array_object_count_elements_helper(intern);
1198
0
  return SUCCESS;
1199
0
} /* }}} */
1200
1201
/* {{{ Return the number of elements in the Iterator. */
1202
PHP_METHOD(ArrayObject, count)
1203
87
{
1204
87
  spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1205
1206
87
  if (zend_parse_parameters_none() == FAILURE) {
1207
0
    RETURN_THROWS();
1208
0
  }
1209
1210
87
  RETURN_LONG(spl_array_object_count_elements_helper(intern));
1211
87
} /* }}} */
1212
1213
enum spl_array_object_sort_methods {
1214
  SPL_NAT_SORT,
1215
  SPL_CALLBACK_SORT,
1216
  SPL_OPTIONAL_FLAG_SORT
1217
};
1218
1219
static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, const char *fname, size_t fname_len, enum spl_array_object_sort_methods use_arg) /* {{{ */
1220
0
{
1221
0
  spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1222
0
  HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
1223
0
  HashTable *aht = *ht_ptr;
1224
0
  zval params[2], *arg = NULL;
1225
1226
0
  zend_function *fn = zend_hash_str_find_ptr(EG(function_table), fname, fname_len);
1227
0
  if (UNEXPECTED(fn == NULL)) {
1228
0
    zend_throw_error(NULL, "Cannot call method %s when function %s is disabled", fname, fname);
1229
0
    RETURN_THROWS();
1230
0
  }
1231
1232
0
  ZVAL_NEW_EMPTY_REF(&params[0]);
1233
0
  ZVAL_ARR(Z_REFVAL(params[0]), aht);
1234
0
  GC_ADDREF(aht);
1235
1236
0
  if (use_arg == SPL_NAT_SORT) {
1237
0
    if (zend_parse_parameters_none() == FAILURE) {
1238
0
      goto exit;
1239
0
    }
1240
1241
0
    intern->nApplyCount++;
1242
0
    zend_call_known_function(fn, NULL, NULL, return_value, 1, params, NULL);
1243
0
    intern->nApplyCount--;
1244
0
  } else if (use_arg == SPL_OPTIONAL_FLAG_SORT) {
1245
0
    zend_long sort_flags = 0;
1246
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) {
1247
0
      goto exit;
1248
0
    }
1249
0
    ZVAL_LONG(&params[1], sort_flags);
1250
0
    intern->nApplyCount++;
1251
0
    zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL);
1252
0
    intern->nApplyCount--;
1253
0
  } else {
1254
0
    ZEND_ASSERT(use_arg == SPL_CALLBACK_SORT);
1255
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1256
0
      goto exit;
1257
0
    }
1258
0
    ZVAL_COPY_VALUE(&params[1], arg);
1259
0
    intern->nApplyCount++;
1260
0
    zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL);
1261
0
    intern->nApplyCount--;
1262
0
  }
1263
1264
0
exit:
1265
0
  {
1266
0
    zval *ht_zv = Z_REFVAL(params[0]);
1267
0
    zend_array_release(*ht_ptr);
1268
0
    SEPARATE_ARRAY(ht_zv);
1269
0
    *ht_ptr = Z_ARRVAL_P(ht_zv);
1270
0
    ZVAL_NULL(ht_zv);
1271
0
    zval_ptr_dtor(&params[0]);
1272
0
  }
1273
0
} /* }}} */
1274
1275
#define SPL_ARRAY_METHOD(cname, fname, use_arg) \
1276
0
PHP_METHOD(cname, fname) \
1277
0
{ \
1278
0
  spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
1279
0
}
Unexecuted instantiation: zim_ArrayObject_asort
Unexecuted instantiation: zim_ArrayObject_ksort
Unexecuted instantiation: zim_ArrayObject_uasort
Unexecuted instantiation: zim_ArrayObject_uksort
Unexecuted instantiation: zim_ArrayObject_natsort
Unexecuted instantiation: zim_ArrayObject_natcasesort
1280
1281
/* Sort the entries by values. */
1282
SPL_ARRAY_METHOD(ArrayObject, asort, SPL_OPTIONAL_FLAG_SORT)
1283
1284
/* Sort the entries by key. */
1285
SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_OPTIONAL_FLAG_SORT)
1286
1287
/* Sort the entries by values user defined function. */
1288
SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_CALLBACK_SORT)
1289
1290
/* Sort the entries by key using user defined function. */
1291
SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_CALLBACK_SORT)
1292
1293
/* Sort the entries by values using "natural order" algorithm. */
1294
SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_NAT_SORT)
1295
1296
/* {{{ Sort the entries by key using case-insensitive "natural order" algorithm. */
1297
SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_NAT_SORT)
1298
1299
/* {{{ Serialize the object */
1300
PHP_METHOD(ArrayObject, serialize)
1301
0
{
1302
0
  zval *object = ZEND_THIS;
1303
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1304
0
  zval members, flags;
1305
0
  php_serialize_data_t var_hash;
1306
0
  smart_str buf = {0};
1307
1308
0
  if (zend_parse_parameters_none() == FAILURE) {
1309
0
    RETURN_THROWS();
1310
0
  }
1311
1312
0
  PHP_VAR_SERIALIZE_INIT(var_hash);
1313
1314
0
  ZVAL_LONG(&flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1315
1316
  /* storage */
1317
0
  smart_str_appendl(&buf, "x:", 2);
1318
0
  php_var_serialize(&buf, &flags, &var_hash);
1319
1320
0
  if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1321
0
    php_var_serialize(&buf, &intern->array, &var_hash);
1322
0
    smart_str_appendc(&buf, ';');
1323
0
  }
1324
1325
  /* members */
1326
0
  smart_str_appendl(&buf, "m:", 2);
1327
1328
0
  ZVAL_ARR(&members, zend_std_get_properties_ex(&intern->std));
1329
1330
0
  php_var_serialize(&buf, &members, &var_hash); /* finishes the string */
1331
1332
  /* done */
1333
0
  PHP_VAR_SERIALIZE_DESTROY(var_hash);
1334
1335
0
  RETURN_STR(smart_str_extract(&buf));
1336
0
} /* }}} */
1337
1338
/* {{{ unserialize the object */
1339
PHP_METHOD(ArrayObject, unserialize)
1340
208k
{
1341
208k
  zval *object = ZEND_THIS;
1342
208k
  spl_array_object *intern = Z_SPLARRAY_P(object);
1343
1344
208k
  char *buf;
1345
208k
  size_t buf_len;
1346
208k
  const unsigned char *p, *s;
1347
208k
  php_unserialize_data_t var_hash;
1348
208k
  zval *members, *zflags, *array;
1349
208k
  zend_long flags;
1350
1351
208k
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) {
1352
0
    RETURN_THROWS();
1353
0
  }
1354
1355
208k
  if (buf_len == 0) {
1356
208k
    return;
1357
208k
  }
1358
1359
19
  if (intern->nApplyCount > 0) {
1360
0
    zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
1361
0
    RETURN_THROWS();
1362
0
  }
1363
1364
  /* storage */
1365
19
  s = p = (const unsigned char*)buf;
1366
19
  PHP_VAR_UNSERIALIZE_INIT(var_hash);
1367
1368
19
  if (*p!= 'x' || *++p != ':') {
1369
3
    goto outexcept;
1370
3
  }
1371
16
  ++p;
1372
1373
16
  zflags = var_tmp_var(&var_hash);
1374
16
  if (!php_var_unserialize(zflags, &p, s + buf_len, &var_hash) || Z_TYPE_P(zflags) != IS_LONG) {
1375
2
    goto outexcept;
1376
2
  }
1377
1378
14
  --p; /* for ';' */
1379
14
  flags = Z_LVAL_P(zflags);
1380
  /* flags needs to be verified and we also need to verify whether the next
1381
   * thing we get is ';'. After that we require an 'm' or something else
1382
   * where 'm' stands for members and anything else should be an array. If
1383
   * neither 'a' or 'm' follows we have an error. */
1384
1385
14
  if (*p != ';') {
1386
0
    goto outexcept;
1387
0
  }
1388
14
  ++p;
1389
1390
14
  if (flags & SPL_ARRAY_IS_SELF) {
1391
    /* If IS_SELF is used, the flags are not followed by an array/object */
1392
1
    intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1393
1
    intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1394
1
    zval_ptr_dtor(&intern->array);
1395
1
    ZVAL_UNDEF(&intern->array);
1396
13
  } else {
1397
13
    if (*p!='a' && *p!='O' && *p!='C' && *p!='r') {
1398
1
      goto outexcept;
1399
1
    }
1400
1401
12
    array = var_tmp_var(&var_hash);
1402
12
    if (!php_var_unserialize(array, &p, s + buf_len, &var_hash)
1403
7
        || (Z_TYPE_P(array) != IS_ARRAY && Z_TYPE_P(array) != IS_OBJECT)) {
1404
6
      goto outexcept;
1405
6
    }
1406
1407
6
    intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1408
6
    intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1409
1410
6
    if (Z_TYPE_P(array) == IS_ARRAY) {
1411
0
      zval_ptr_dtor(&intern->array);
1412
0
      ZVAL_COPY_VALUE(&intern->array, array);
1413
0
      ZVAL_NULL(array);
1414
0
      SEPARATE_ARRAY(&intern->array);
1415
6
    } else {
1416
6
      spl_array_set_array(object, intern, array, 0L, true);
1417
6
    }
1418
1419
6
    if (*p != ';') {
1420
1
      goto outexcept;
1421
1
    }
1422
5
    ++p;
1423
5
  }
1424
1425
  /* members */
1426
6
  if (*p!= 'm' || *++p != ':') {
1427
3
    goto outexcept;
1428
3
  }
1429
3
  ++p;
1430
1431
3
  members = var_tmp_var(&var_hash);
1432
3
  if (!php_var_unserialize(members, &p, s + buf_len, &var_hash) || Z_TYPE_P(members) != IS_ARRAY) {
1433
2
    goto outexcept;
1434
2
  }
1435
1436
  /* copy members */
1437
1
  object_properties_load(&intern->std, Z_ARRVAL_P(members));
1438
1439
  /* done reading $serialized */
1440
1
  PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1441
1
  return;
1442
1443
18
outexcept:
1444
18
  PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1445
18
  zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Error at offset " ZEND_LONG_FMT " of %zd bytes", (zend_long)((char*)p - buf), buf_len);
1446
18
  RETURN_THROWS();
1447
1448
18
} /* }}} */
1449
1450
/* {{{ */
1451
PHP_METHOD(ArrayObject, __serialize)
1452
0
{
1453
0
  spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1454
0
  zval tmp;
1455
1456
0
  if (zend_parse_parameters_none() == FAILURE) {
1457
0
    RETURN_THROWS();
1458
0
  }
1459
1460
0
  array_init(return_value);
1461
1462
  /* flags */
1463
0
  ZVAL_LONG(&tmp, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1464
0
  zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1465
1466
  /* storage */
1467
0
  if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
1468
0
    ZVAL_NULL(&tmp);
1469
0
  } else {
1470
0
    ZVAL_COPY(&tmp, &intern->array);
1471
0
  }
1472
0
  zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1473
1474
  /* members */
1475
0
  ZVAL_ARR(&tmp, zend_proptable_to_symtable(
1476
0
    zend_std_get_properties(&intern->std), /* always_duplicate */ 1));
1477
0
  zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1478
1479
  /* iterator class */
1480
0
  if (intern->ce_get_iterator == spl_ce_ArrayIterator) {
1481
0
    ZVAL_NULL(&tmp);
1482
0
  } else {
1483
0
    ZVAL_STR_COPY(&tmp, intern->ce_get_iterator->name);
1484
0
  }
1485
0
  zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
1486
0
}
1487
/* }}} */
1488
1489
1490
/* {{{ */
1491
PHP_METHOD(ArrayObject, __unserialize)
1492
3.16k
{
1493
3.16k
  spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1494
3.16k
  HashTable *data;
1495
3.16k
  zval *flags_zv, *storage_zv, *members_zv, *iterator_class_zv;
1496
3.16k
  zend_long flags;
1497
1498
3.16k
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
1499
0
    RETURN_THROWS();
1500
0
  }
1501
1502
3.16k
  flags_zv          = zend_hash_index_find(data, 0);
1503
3.16k
  storage_zv        = zend_hash_index_find(data, 1);
1504
3.16k
  members_zv        = zend_hash_index_find(data, 2);
1505
3.16k
  iterator_class_zv = zend_hash_index_find(data, 3);
1506
1507
3.16k
  if (!flags_zv || !storage_zv || !members_zv ||
1508
3.07k
      Z_TYPE_P(flags_zv) != IS_LONG || Z_TYPE_P(members_zv) != IS_ARRAY ||
1509
3.07k
      (iterator_class_zv && (Z_TYPE_P(iterator_class_zv) != IS_NULL &&
1510
97
        Z_TYPE_P(iterator_class_zv) != IS_STRING))) {
1511
97
    zend_throw_exception(spl_ce_UnexpectedValueException,
1512
97
      "Incomplete or ill-typed serialization data", 0);
1513
97
    RETURN_THROWS();
1514
97
  }
1515
1516
3.07k
  flags = Z_LVAL_P(flags_zv);
1517
3.07k
  intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1518
3.07k
  intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1519
1520
3.07k
  if (flags & SPL_ARRAY_IS_SELF) {
1521
134
    zval_ptr_dtor(&intern->array);
1522
134
    ZVAL_UNDEF(&intern->array);
1523
2.93k
  } else {
1524
2.93k
    if (Z_TYPE_P(storage_zv) != IS_OBJECT && Z_TYPE_P(storage_zv) != IS_ARRAY) {
1525
      /* TODO Use UnexpectedValueException instead? And better error message? */
1526
1
      zend_throw_exception(spl_ce_InvalidArgumentException, "Passed variable is not an array or object", 0);
1527
1
      RETURN_THROWS();
1528
1
    }
1529
2.93k
    spl_array_set_array(ZEND_THIS, intern, storage_zv, 0L, true);
1530
2.93k
  }
1531
1532
3.07k
  object_properties_load(&intern->std, Z_ARRVAL_P(members_zv));
1533
3.07k
  if (EG(exception)) {
1534
2
    RETURN_THROWS();
1535
2
  }
1536
1537
3.06k
  if (iterator_class_zv && Z_TYPE_P(iterator_class_zv) == IS_STRING) {
1538
0
    zend_class_entry *ce = zend_lookup_class(Z_STR_P(iterator_class_zv));
1539
1540
0
    if (!ce) {
1541
0
      zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1542
0
        "Cannot deserialize ArrayObject with iterator class '%s'; no such class exists",
1543
0
        ZSTR_VAL(Z_STR_P(iterator_class_zv)));
1544
0
      RETURN_THROWS();
1545
0
    }
1546
1547
0
    if (!instanceof_function(ce, zend_ce_iterator)) {
1548
0
      zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
1549
0
        "Cannot deserialize ArrayObject with iterator class '%s'; this class does not implement the Iterator interface",
1550
0
        ZSTR_VAL(Z_STR_P(iterator_class_zv)));
1551
0
      RETURN_THROWS();
1552
0
    }
1553
1554
0
    intern->ce_get_iterator = ce;
1555
0
  }
1556
3.06k
}
1557
/* }}} */
1558
1559
/* {{{ */
1560
PHP_METHOD(ArrayObject, __debugInfo)
1561
47
{
1562
47
  if (zend_parse_parameters_none() == FAILURE) {
1563
0
    RETURN_THROWS();
1564
0
  }
1565
1566
47
  RETURN_ARR(spl_array_get_debug_info(Z_OBJ_P(ZEND_THIS)));
1567
47
} /* }}} */
1568
1569
/*** ArrayIterator class ***/
1570
typedef struct _spl_array_iterator {
1571
  zend_object_iterator it;
1572
  bool by_ref;
1573
} spl_array_iterator;
1574
1575
static zend_result spl_array_next_ex(spl_array_object *intern, HashTable *aht) /* {{{ */
1576
270
{
1577
270
  uint32_t *pos_ptr = spl_array_get_pos_ptr(aht, intern);
1578
1579
270
  zend_hash_move_forward_ex(aht, pos_ptr);
1580
270
  if (spl_array_is_object(intern)) {
1581
15
    return spl_array_skip_protected(intern, aht);
1582
255
  } else {
1583
255
    return zend_hash_has_more_elements_ex(aht, pos_ptr);
1584
255
  }
1585
270
} /* }}} */
1586
1587
static zend_result spl_array_next(spl_array_object *intern) /* {{{ */
1588
0
{
1589
0
  HashTable *aht = spl_array_get_hash_table(intern);
1590
1591
0
  return spl_array_next_ex(intern, aht);
1592
1593
0
} /* }}} */
1594
1595
static void spl_array_it_dtor(zend_object_iterator *iter) /* {{{ */
1596
167
{
1597
167
  zval_ptr_dtor(&iter->data);
1598
167
}
1599
/* }}} */
1600
1601
static zend_result spl_array_it_valid(zend_object_iterator *iter) /* {{{ */
1602
440
{
1603
440
  spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1604
440
  HashTable *aht = spl_array_get_hash_table(object);
1605
440
  return zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, object));
1606
440
}
1607
/* }}} */
1608
1609
static zval *spl_array_it_get_current_data(zend_object_iterator *iter) /* {{{ */
1610
313
{
1611
313
  spl_array_iterator *array_iter = (spl_array_iterator*)iter;
1612
313
  spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1613
313
  HashTable *aht = spl_array_get_hash_table(object);
1614
313
  zval *data = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, object));
1615
313
  if (data && Z_TYPE_P(data) == IS_INDIRECT) {
1616
20
    data = Z_INDIRECT_P(data);
1617
20
  }
1618
  // ZEND_FE_FETCH_RW converts the value to a reference but doesn't know the source is a property.
1619
  // Typed properties must add a type source to the reference, and readonly properties must fail.
1620
313
  if (array_iter->by_ref
1621
20
   && Z_TYPE_P(data) != IS_REFERENCE
1622
15
   && Z_TYPE(object->array) == IS_OBJECT
1623
15
   && !(object->ar_flags & (SPL_ARRAY_IS_SELF|SPL_ARRAY_USE_OTHER))) {
1624
15
    zend_string *key;
1625
15
    zend_hash_get_current_key_ex(aht, &key, NULL, spl_array_get_pos_ptr(aht, object));
1626
15
    zend_class_entry *ce = Z_OBJCE(object->array);
1627
15
    zend_property_info *prop_info = zend_get_property_info(ce, key, true);
1628
15
    ZEND_ASSERT(prop_info != ZEND_WRONG_PROPERTY_INFO);
1629
15
    if (EXPECTED(prop_info != NULL) && ZEND_TYPE_IS_SET(prop_info->type)) {
1630
15
      if (prop_info->flags & ZEND_ACC_READONLY) {
1631
5
        zend_throw_error(NULL,
1632
5
          "Cannot acquire reference to readonly property %s::$%s",
1633
5
          ZSTR_VAL(prop_info->ce->name), ZSTR_VAL(key));
1634
5
        return NULL;
1635
5
      }
1636
10
      ZVAL_NEW_REF(data, data);
1637
10
      ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), prop_info);
1638
10
    }
1639
15
  }
1640
308
  return data;
1641
313
}
1642
/* }}} */
1643
1644
static void spl_array_it_get_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
1645
288
{
1646
288
  spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1647
288
  HashTable *aht = spl_array_get_hash_table(object);
1648
288
  zend_hash_get_current_key_zval_ex(aht, key, spl_array_get_pos_ptr(aht, object));
1649
288
}
1650
/* }}} */
1651
1652
static void spl_array_it_move_forward(zend_object_iterator *iter) /* {{{ */
1653
257
{
1654
257
  spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1655
257
  HashTable *aht = spl_array_get_hash_table(object);
1656
257
  spl_array_next_ex(object, aht);
1657
257
}
1658
/* }}} */
1659
1660
static void spl_array_rewind(spl_array_object *intern) /* {{{ */
1661
200
{
1662
200
  HashTable *aht = spl_array_get_hash_table(intern);
1663
1664
200
  if (intern->ht_iter == (uint32_t)-1) {
1665
185
    spl_array_get_pos_ptr(aht, intern);
1666
185
  } else {
1667
15
    zend_hash_internal_pointer_reset_ex(aht, spl_array_get_pos_ptr(aht, intern));
1668
15
    spl_array_skip_protected(intern, aht);
1669
15
  }
1670
200
}
1671
/* }}} */
1672
1673
static void spl_array_it_rewind(zend_object_iterator *iter) /* {{{ */
1674
168
{
1675
168
  spl_array_object *object = Z_SPLARRAY_P(&iter->data);
1676
168
  spl_array_rewind(object);
1677
168
}
1678
/* }}} */
1679
1680
static HashTable *spl_array_it_get_gc(zend_object_iterator *iter, zval **table, int *n)
1681
167
{
1682
167
  *n = 1;
1683
167
  *table = &iter->data;
1684
167
  return NULL;
1685
167
}
1686
1687
/* iterator handler table */
1688
static const zend_object_iterator_funcs spl_array_it_funcs = {
1689
  spl_array_it_dtor,
1690
  spl_array_it_valid,
1691
  spl_array_it_get_current_data,
1692
  spl_array_it_get_current_key,
1693
  spl_array_it_move_forward,
1694
  spl_array_it_rewind,
1695
  NULL,
1696
  spl_array_it_get_gc,
1697
};
1698
1699
static zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1700
167
{
1701
167
  spl_array_iterator *iterator = emalloc(sizeof(spl_array_iterator));
1702
167
  zend_iterator_init(&iterator->it);
1703
1704
167
  ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
1705
167
  iterator->it.funcs = &spl_array_it_funcs;
1706
167
  iterator->by_ref = by_ref;
1707
1708
167
  return &iterator->it;
1709
167
}
1710
/* }}} */
1711
1712
/* {{{ Constructs a new array iterator from an array or object. */
1713
PHP_METHOD(ArrayIterator, __construct)
1714
281
{
1715
281
  zval *object = ZEND_THIS;
1716
281
  spl_array_object *intern;
1717
281
  zval *array;
1718
281
  zend_long ar_flags = 0;
1719
1720
281
  if (ZEND_NUM_ARGS() == 0) {
1721
73
    return; /* nothing to do */
1722
73
  }
1723
1724
208
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Al", &array, &ar_flags) == FAILURE) {
1725
1
    RETURN_THROWS();
1726
1
  }
1727
1728
207
  intern = Z_SPLARRAY_P(object);
1729
1730
207
  ar_flags &= ~SPL_ARRAY_INT_MASK;
1731
1732
207
  spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1);
1733
207
}
1734
/* }}} */
1735
1736
/* {{{ Rewind array back to the start */
1737
PHP_METHOD(ArrayIterator, rewind)
1738
32
{
1739
32
  zval *object = ZEND_THIS;
1740
32
  spl_array_object *intern = Z_SPLARRAY_P(object);
1741
1742
32
  if (zend_parse_parameters_none() == FAILURE) {
1743
0
    RETURN_THROWS();
1744
0
  }
1745
1746
32
  spl_array_rewind(intern);
1747
32
}
1748
/* }}} */
1749
1750
/* {{{ Seek to position. */
1751
PHP_METHOD(ArrayIterator, seek)
1752
0
{
1753
0
  zend_long opos, position;
1754
0
  zval *object = ZEND_THIS;
1755
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1756
0
  HashTable *aht = spl_array_get_hash_table(intern);
1757
0
  int result;
1758
1759
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &position) == FAILURE) {
1760
0
    RETURN_THROWS();
1761
0
  }
1762
1763
0
  opos = position;
1764
1765
0
  if (position >= 0) { /* negative values are not supported */
1766
0
    spl_array_rewind(intern);
1767
0
    result = SUCCESS;
1768
1769
0
    while (position-- > 0 && (result = spl_array_next(intern)) == SUCCESS);
1770
1771
0
    if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS) {
1772
0
      return; /* ok */
1773
0
    }
1774
0
  }
1775
0
  zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", opos);
1776
0
} /* }}} */
1777
1778
/* {{{ Return current array entry */
1779
PHP_METHOD(ArrayIterator, current)
1780
22
{
1781
22
  zval *object = ZEND_THIS;
1782
22
  spl_array_object *intern = Z_SPLARRAY_P(object);
1783
22
  zval *entry;
1784
22
  HashTable *aht = spl_array_get_hash_table(intern);
1785
1786
22
  if (zend_parse_parameters_none() == FAILURE) {
1787
0
    RETURN_THROWS();
1788
0
  }
1789
1790
22
  if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1791
0
    RETURN_NULL();
1792
0
  }
1793
22
  if (Z_TYPE_P(entry) == IS_INDIRECT) {
1794
0
    entry = Z_INDIRECT_P(entry);
1795
0
    if (Z_TYPE_P(entry) == IS_UNDEF) {
1796
0
      RETURN_NULL();
1797
0
    }
1798
0
  }
1799
22
  RETURN_COPY_DEREF(entry);
1800
22
}
1801
/* }}} */
1802
1803
void spl_array_iterator_key(zval *object, zval *return_value) /* {{{ */
1804
0
{
1805
0
  spl_array_object *intern = Z_SPLARRAY_P(object);
1806
0
  HashTable *aht = spl_array_get_hash_table(intern);
1807
1808
0
  zend_hash_get_current_key_zval_ex(aht, return_value, spl_array_get_pos_ptr(aht, intern));
1809
0
}
1810
/* }}} */
1811
1812
/* {{{ Return current array key */
1813
PHP_METHOD(ArrayIterator, key)
1814
0
{
1815
0
  if (zend_parse_parameters_none() == FAILURE) {
1816
0
    RETURN_THROWS();
1817
0
  }
1818
1819
0
  spl_array_iterator_key(ZEND_THIS, return_value);
1820
0
} /* }}} */
1821
1822
/* {{{ Move to next entry */
1823
PHP_METHOD(ArrayIterator, next)
1824
13
{
1825
13
  zval *object = ZEND_THIS;
1826
13
  spl_array_object *intern = Z_SPLARRAY_P(object);
1827
13
  HashTable *aht = spl_array_get_hash_table(intern);
1828
1829
13
  if (zend_parse_parameters_none() == FAILURE) {
1830
0
    RETURN_THROWS();
1831
0
  }
1832
1833
13
  spl_array_next_ex(intern, aht);
1834
13
}
1835
/* }}} */
1836
1837
/* {{{ Check whether array contains more entries */
1838
PHP_METHOD(ArrayIterator, valid)
1839
30
{
1840
30
  zval *object = ZEND_THIS;
1841
30
  spl_array_object *intern = Z_SPLARRAY_P(object);
1842
30
  HashTable *aht = spl_array_get_hash_table(intern);
1843
1844
30
  if (zend_parse_parameters_none() == FAILURE) {
1845
0
    RETURN_THROWS();
1846
0
  }
1847
1848
30
  RETURN_BOOL(zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS);
1849
30
}
1850
/* }}} */
1851
1852
/*** RecursiveArrayIterator methods ***/
1853
1854
/* {{{ Check whether current element has children (e.g. is an array) */
1855
PHP_METHOD(RecursiveArrayIterator, hasChildren)
1856
10
{
1857
10
  zval *object = ZEND_THIS, *entry;
1858
10
  spl_array_object *intern = Z_SPLARRAY_P(object);
1859
10
  HashTable *aht = spl_array_get_hash_table(intern);
1860
1861
10
  if (zend_parse_parameters_none() == FAILURE) {
1862
0
    RETURN_THROWS();
1863
0
  }
1864
1865
10
  if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1866
0
    RETURN_FALSE;
1867
0
  }
1868
1869
10
  if (Z_TYPE_P(entry) == IS_INDIRECT) {
1870
0
    entry = Z_INDIRECT_P(entry);
1871
0
  }
1872
1873
10
  ZVAL_DEREF(entry);
1874
10
  RETURN_BOOL(Z_TYPE_P(entry) == IS_ARRAY || (Z_TYPE_P(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
1875
10
}
1876
/* }}} */
1877
1878
static void spl_instantiate_child_arg(zend_class_entry *pce, zval *retval, zval *arg1, zval *arg2) /* {{{ */
1879
5
{
1880
5
  object_init_ex(retval, pce);
1881
5
  spl_array_object *new_intern = Z_SPLARRAY_P(retval);
1882
  /*
1883
   * set new_intern->is_child is true to indicate that the object was created by
1884
   * RecursiveArrayIterator::getChildren() method.
1885
   */
1886
5
  new_intern->is_child = true;
1887
1888
  /* find the bucket of parent object. */
1889
5
  new_intern->bucket = (Bucket *)((char *)(arg1) - XtOffsetOf(Bucket, val));;
1890
5
  zend_call_known_instance_method_with_2_params(pce->constructor, Z_OBJ_P(retval), NULL, arg1, arg2);
1891
5
}
1892
/* }}} */
1893
1894
/* {{{ Create a sub iterator for the current element (same class as $this) */
1895
PHP_METHOD(RecursiveArrayIterator, getChildren)
1896
5
{
1897
5
  zval *object = ZEND_THIS, *entry, flags;
1898
5
  spl_array_object *intern = Z_SPLARRAY_P(object);
1899
5
  HashTable *aht = spl_array_get_hash_table(intern);
1900
1901
5
  if (zend_parse_parameters_none() == FAILURE) {
1902
0
    RETURN_THROWS();
1903
0
  }
1904
1905
5
  if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) {
1906
0
    RETURN_NULL();
1907
0
  }
1908
1909
5
  if (Z_TYPE_P(entry) == IS_INDIRECT) {
1910
0
    entry = Z_INDIRECT_P(entry);
1911
0
  }
1912
1913
5
  ZVAL_DEREF(entry);
1914
5
  if (Z_TYPE_P(entry) == IS_OBJECT) {
1915
0
    if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
1916
0
      RETURN_NULL();
1917
0
    }
1918
0
    if (instanceof_function(Z_OBJCE_P(entry), Z_OBJCE_P(ZEND_THIS))) {
1919
0
      RETURN_OBJ_COPY(Z_OBJ_P(entry));
1920
0
    }
1921
0
  }
1922
1923
5
  ZVAL_LONG(&flags, intern->ar_flags);
1924
5
  spl_instantiate_child_arg(Z_OBJCE_P(ZEND_THIS), return_value, entry, &flags);
1925
5
}
1926
/* }}} */
1927
1928
/* {{{ PHP_MINIT_FUNCTION(spl_array) */
1929
PHP_MINIT_FUNCTION(spl_array)
1930
16
{
1931
16
  spl_ce_ArrayObject = register_class_ArrayObject(zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable);
1932
16
  spl_ce_ArrayObject->create_object = spl_array_object_new;
1933
16
  spl_ce_ArrayObject->default_object_handlers = &spl_handler_ArrayObject;
1934
1935
16
  memcpy(&spl_handler_ArrayObject, &std_object_handlers, sizeof(zend_object_handlers));
1936
1937
16
  spl_handler_ArrayObject.offset = XtOffsetOf(spl_array_object, std);
1938
1939
16
  spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
1940
16
  spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
1941
16
  spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
1942
16
  spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
1943
16
  spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
1944
16
  spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
1945
1946
16
  spl_handler_ArrayObject.get_properties_for = spl_array_get_properties_for;
1947
16
  spl_handler_ArrayObject.get_gc = spl_array_get_gc;
1948
16
  spl_handler_ArrayObject.read_property = spl_array_read_property;
1949
16
  spl_handler_ArrayObject.write_property = spl_array_write_property;
1950
16
  spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
1951
16
  spl_handler_ArrayObject.has_property = spl_array_has_property;
1952
16
  spl_handler_ArrayObject.unset_property = spl_array_unset_property;
1953
1954
16
  spl_handler_ArrayObject.compare = spl_array_compare_objects;
1955
16
  spl_handler_ArrayObject.free_obj = spl_array_object_free_storage;
1956
1957
16
  spl_ce_ArrayIterator = register_class_ArrayIterator(spl_ce_SeekableIterator, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable);
1958
16
  spl_ce_ArrayIterator->create_object = spl_array_object_new;
1959
16
  spl_ce_ArrayIterator->default_object_handlers = &spl_handler_ArrayObject;
1960
16
  spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
1961
1962
16
  spl_ce_RecursiveArrayIterator = register_class_RecursiveArrayIterator(spl_ce_ArrayIterator, spl_ce_RecursiveIterator);
1963
16
  spl_ce_RecursiveArrayIterator->create_object = spl_array_object_new;
1964
16
  spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
1965
1966
16
  return SUCCESS;
1967
16
}
1968
/* }}} */