Coverage Report

Created: 2026-04-01 06:49

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