Coverage Report

Created: 2025-06-13 06:43

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