Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/array.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Andi Gutmans <andi@php.net>                                 |
12
   |          Zeev Suraski <zeev@php.net>                                 |
13
   |          Rasmus Lerdorf <rasmus@php.net>                             |
14
   |          Andrei Zmievski <andrei@php.net>                            |
15
   |          Stig Venaas <venaas@php.net>                                |
16
   |          Jason Greene <jason@php.net>                                |
17
   +----------------------------------------------------------------------+
18
*/
19
20
#include "php.h"
21
#include "php_ini.h"
22
#include <stdarg.h>
23
#include <stdlib.h>
24
#include <math.h>
25
#include <time.h>
26
#include <string.h>
27
#include "zend_globals.h"
28
#include "zend_interfaces.h"
29
#include "php_array.h"
30
#include "basic_functions.h"
31
#include "php_string.h"
32
#include "php_math.h"
33
#include "zend_smart_str.h"
34
#include "zend_bitset.h"
35
#include "zend_exceptions.h"
36
#include "ext/random/php_random.h"
37
#include "zend_frameless_function.h"
38
39
/* {{{ defines */
40
41
0
#define DIFF_NORMAL     1
42
0
#define DIFF_KEY      2
43
0
#define DIFF_ASSOC      6
44
0
#define DIFF_COMP_DATA_NONE    -1
45
0
#define DIFF_COMP_DATA_INTERNAL 0
46
0
#define DIFF_COMP_DATA_USER     1
47
0
#define DIFF_COMP_KEY_INTERNAL  0
48
0
#define DIFF_COMP_KEY_USER      1
49
50
0
#define INTERSECT_NORMAL    1
51
0
#define INTERSECT_KEY     2
52
0
#define INTERSECT_ASSOC     6
53
0
#define INTERSECT_COMP_DATA_NONE    -1
54
0
#define INTERSECT_COMP_DATA_INTERNAL 0
55
0
#define INTERSECT_COMP_DATA_USER     1
56
0
#define INTERSECT_COMP_KEY_INTERNAL  0
57
0
#define INTERSECT_COMP_KEY_USER      1
58
/* }}} */
59
60
ZEND_DECLARE_MODULE_GLOBALS(array)
61
PHPAPI zend_class_entry *sort_direction_ce;
62
63
/* {{{ php_array_init_globals */
64
static void php_array_init_globals(zend_array_globals *array_globals)
65
16
{
66
16
  memset(array_globals, 0, sizeof(zend_array_globals));
67
16
}
68
/* }}} */
69
70
PHP_MINIT_FUNCTION(array) /* {{{ */
71
16
{
72
16
  ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
73
74
16
  return SUCCESS;
75
16
}
76
/* }}} */
77
78
PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
79
0
{
80
#ifdef ZTS
81
  ts_free_id(array_globals_id);
82
#endif
83
84
0
  return SUCCESS;
85
0
}
86
/* }}} */
87
88
9
static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
89
9
  if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
90
0
    return 1;
91
9
  } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
92
9
    return -1;
93
9
  } else {
94
0
    return 0;
95
0
  }
96
9
}
97
98
240
#define RETURN_STABLE_SORT(a, b, result) do { \
99
240
  int _result = (result); \
100
240
  if (EXPECTED(_result)) { \
101
231
    return _result; \
102
231
  } \
103
240
  return stable_sort_fallback((a), (b)); \
104
240
} while (0)
105
106
/* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
107
#define DEFINE_SORT_VARIANTS(name) \
108
0
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
109
0
    return php_array_##name##_unstable_i(a, b); \
110
0
  } \
Unexecuted instantiation: array.c:php_array_data_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_data_compare_string_case_unstable
Unexecuted instantiation: array.c:php_array_data_compare_string_unstable
Unexecuted instantiation: array.c:php_array_natural_case_compare_unstable
Unexecuted instantiation: array.c:php_array_natural_compare_unstable
Unexecuted instantiation: array.c:php_array_data_compare_string_locale_unstable
Unexecuted instantiation: array.c:php_array_data_compare_unstable
Unexecuted instantiation: array.c:php_array_key_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_key_compare_string_case_unstable
Unexecuted instantiation: array.c:php_array_key_compare_string_unstable
Unexecuted instantiation: array.c:php_array_key_compare_string_locale_unstable
Unexecuted instantiation: array.c:php_array_key_compare_unstable
111
240
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
240
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
240
  } \
array.c:php_array_natural_compare
Line
Count
Source
111
42
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
42
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
42
  } \
Unexecuted instantiation: array.c:php_array_natural_case_compare
array.c:php_array_data_compare_numeric
Line
Count
Source
111
7
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
7
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
7
  } \
array.c:php_array_data_compare_string_case
Line
Count
Source
111
17
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
17
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
17
  } \
array.c:php_array_data_compare_string
Line
Count
Source
111
16
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
16
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
16
  } \
Unexecuted instantiation: array.c:php_array_data_compare_string_locale
array.c:php_array_data_compare
Line
Count
Source
111
144
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
144
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
144
  } \
array.c:php_array_key_compare_numeric
Line
Count
Source
111
13
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
13
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
13
  } \
Unexecuted instantiation: array.c:php_array_key_compare_string_case
array.c:php_array_key_compare_string
Line
Count
Source
111
1
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
1
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
1
  } \
Unexecuted instantiation: array.c:php_array_key_compare_string_locale
Unexecuted instantiation: array.c:php_array_key_compare
114
0
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
115
0
    return php_array_##name##_unstable(a, b) * -1; \
116
0
  } \
Unexecuted instantiation: array.c:php_array_reverse_data_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_case_unstable
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_unstable
Unexecuted instantiation: array.c:php_array_reverse_natural_case_compare_unstable
Unexecuted instantiation: array.c:php_array_reverse_natural_compare_unstable
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_locale_unstable
Unexecuted instantiation: array.c:php_array_reverse_data_compare_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_case_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_locale_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_unstable
117
0
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
118
0
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
119
0
  } \
Unexecuted instantiation: array.c:php_array_reverse_data_compare_numeric
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_case
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string
Unexecuted instantiation: array.c:php_array_reverse_natural_case_compare
Unexecuted instantiation: array.c:php_array_reverse_natural_compare
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_locale
Unexecuted instantiation: array.c:php_array_reverse_data_compare
Unexecuted instantiation: array.c:php_array_reverse_key_compare_numeric
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_case
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_locale
Unexecuted instantiation: array.c:php_array_reverse_key_compare
120
121
static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
122
0
{
123
0
  zval first;
124
0
  zval second;
125
126
0
  if (f->key == NULL && s->key == NULL) {
127
0
    return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
128
0
  } else if (f->key && s->key) {
129
0
    return zendi_smart_strcmp(f->key, s->key);
130
0
  }
131
0
  if (f->key) {
132
0
    ZVAL_STR(&first, f->key);
133
0
  } else {
134
0
    ZVAL_LONG(&first, f->h);
135
0
  }
136
0
  if (s->key) {
137
0
    ZVAL_STR(&second, s->key);
138
0
  } else {
139
0
    ZVAL_LONG(&second, s->h);
140
0
  }
141
0
  return zend_compare(&first, &second);
142
0
}
143
/* }}} */
144
145
static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
146
13
{
147
13
  if (f->key == NULL && s->key == NULL) {
148
5
    return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
149
8
  } else {
150
8
    double d1, d2;
151
8
    if (f->key) {
152
5
      d1 = zend_strtod(f->key->val, NULL);
153
5
    } else {
154
3
      d1 = (double)(zend_long)f->h;
155
3
    }
156
8
    if (s->key) {
157
3
      d2 = zend_strtod(s->key->val, NULL);
158
5
    } else {
159
5
      d2 = (double)(zend_long)s->h;
160
5
    }
161
8
    return ZEND_THREEWAY_COMPARE(d1, d2);
162
8
  }
163
13
}
164
/* }}} */
165
166
static zend_always_inline int php_array_key_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
167
0
{
168
0
  const char *s1, *s2;
169
0
  size_t l1, l2;
170
0
  char buf1[MAX_LENGTH_OF_LONG + 1];
171
0
  char buf2[MAX_LENGTH_OF_LONG + 1];
172
173
0
  if (f->key) {
174
0
    s1 = f->key->val;
175
0
    l1 = f->key->len;
176
0
  } else {
177
0
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
178
0
    l1 = buf1 + sizeof(buf1) - 1 - s1;
179
0
  }
180
0
  if (s->key) {
181
0
    s2 = s->key->val;
182
0
    l2 = s->key->len;
183
0
  } else {
184
0
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
185
0
    l2 = buf2 + sizeof(buf2) - 1 - s2;
186
0
  }
187
0
  return zend_binary_strcasecmp(s1, l1, s2, l2);
188
0
}
189
/* }}} */
190
191
static zend_always_inline int php_array_key_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
192
1
{
193
1
  const char *s1, *s2;
194
1
  size_t l1, l2;
195
1
  char buf1[MAX_LENGTH_OF_LONG + 1];
196
1
  char buf2[MAX_LENGTH_OF_LONG + 1];
197
198
1
  if (f->key) {
199
1
    s1 = f->key->val;
200
1
    l1 = f->key->len;
201
1
  } else {
202
0
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
203
0
    l1 = buf1 + sizeof(buf1) - 1 - s1;
204
0
  }
205
1
  if (s->key) {
206
0
    s2 = s->key->val;
207
0
    l2 = s->key->len;
208
1
  } else {
209
1
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
210
1
    l2 = buf2 + sizeof(buf2) - 1 - s2;
211
1
  }
212
1
  return zend_binary_strcmp(s1, l1, s2, l2);
213
1
}
214
/* }}} */
215
216
static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, bool fold_case) /* {{{ */
217
0
{
218
0
  const char *s1, *s2;
219
0
  size_t l1, l2;
220
0
  char buf1[MAX_LENGTH_OF_LONG + 1];
221
0
  char buf2[MAX_LENGTH_OF_LONG + 1];
222
223
0
  if (f->key) {
224
0
    s1 = f->key->val;
225
0
    l1 = f->key->len;
226
0
  } else {
227
0
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
228
0
    l1 = buf1 + sizeof(buf1) - 1 - s1;
229
0
  }
230
0
  if (s->key) {
231
0
    s2 = s->key->val;
232
0
    l2 = s->key->len;
233
0
  } else {
234
0
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
235
0
    l2 = buf2 + sizeof(buf2) - 1 - s2;
236
0
  }
237
0
  return strnatcmp_ex(s1, l1, s2, l2, fold_case);
238
0
}
239
/* }}} */
240
241
static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
242
0
{
243
0
  RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, true));
244
0
}
245
/* }}} */
246
247
static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
248
0
{
249
0
  RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, true));
250
0
}
251
/* }}} */
252
253
static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
254
0
{
255
0
  RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, false));
256
0
}
257
/* }}} */
258
259
static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
260
0
{
261
0
  RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, false));
262
0
}
263
/* }}} */
264
265
static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
266
0
{
267
0
  const char *s1, *s2;
268
0
  char buf1[MAX_LENGTH_OF_LONG + 1];
269
0
  char buf2[MAX_LENGTH_OF_LONG + 1];
270
271
0
  if (f->key) {
272
0
    s1 = f->key->val;
273
0
  } else {
274
0
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
275
0
  }
276
0
  if (s->key) {
277
0
    s2 = s->key->val;
278
0
  } else {
279
0
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
280
0
  }
281
0
  return strcoll(s1, s2);
282
0
}
283
/* }}} */
284
285
static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
286
144
{
287
144
  int result = zend_compare(&f->val, &s->val);
288
  /* Special enums handling for array_unique. We don't want to add this logic to zend_compare as
289
   * that would be observable via comparison operators. */
290
144
  zval *rhs = &s->val;
291
144
  ZVAL_DEREF(rhs);
292
144
  if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
293
0
   && result == ZEND_UNCOMPARABLE
294
0
   && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
295
0
    zval *lhs = &f->val;
296
0
    ZVAL_DEREF(lhs);
297
0
    if (Z_TYPE_P(lhs) == IS_OBJECT && (Z_OBJCE_P(lhs)->ce_flags & ZEND_ACC_ENUM)) {
298
      // Order doesn't matter, we just need to group the same enum values
299
0
      uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
300
0
      uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
301
0
      return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
302
0
    } else {
303
      // Shift enums to the end of the array
304
0
      return -1;
305
0
    }
306
0
  }
307
144
  return result;
308
144
}
309
/* }}} */
310
311
static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
312
7
{
313
7
  return numeric_compare_function(&f->val, &s->val);
314
7
}
315
/* }}} */
316
317
static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
318
17
{
319
17
  return string_case_compare_function(&f->val, &s->val);
320
17
}
321
/* }}} */
322
323
static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
324
16
{
325
16
  return string_compare_function(&f->val, &s->val);
326
16
}
327
/* }}} */
328
329
static int php_array_natural_general_compare(Bucket *f, Bucket *s, bool fold_case) /* {{{ */
330
42
{
331
42
  zend_string *tmp_str1, *tmp_str2;
332
42
  zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
333
42
  zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
334
335
42
  int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
336
337
42
  zend_tmp_string_release(tmp_str1);
338
42
  zend_tmp_string_release(tmp_str2);
339
42
  return result;
340
42
}
341
/* }}} */
342
343
static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
344
42
{
345
42
  return php_array_natural_general_compare(a, b, false);
346
42
}
347
/* }}} */
348
349
static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
350
0
{
351
0
  return php_array_natural_general_compare(a, b, true);
352
0
}
353
/* }}} */
354
355
static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
356
0
{
357
0
  return string_locale_compare_function(&f->val, &s->val);
358
0
}
359
/* }}} */
360
361
DEFINE_SORT_VARIANTS(key_compare);
362
DEFINE_SORT_VARIANTS(key_compare_numeric);
363
DEFINE_SORT_VARIANTS(key_compare_string_case);
364
DEFINE_SORT_VARIANTS(key_compare_string);
365
DEFINE_SORT_VARIANTS(key_compare_string_locale);
366
DEFINE_SORT_VARIANTS(data_compare);
367
DEFINE_SORT_VARIANTS(data_compare_numeric);
368
DEFINE_SORT_VARIANTS(data_compare_string_case);
369
DEFINE_SORT_VARIANTS(data_compare_string);
370
DEFINE_SORT_VARIANTS(data_compare_string_locale);
371
DEFINE_SORT_VARIANTS(natural_compare);
372
DEFINE_SORT_VARIANTS(natural_case_compare);
373
374
static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type)
375
6
{
376
6
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
377
5
    case PHP_SORT_NUMERIC:
378
5
      return php_array_key_compare_numeric;
379
380
1
    case PHP_SORT_STRING:
381
1
      if (sort_type & PHP_SORT_FLAG_CASE) {
382
0
        return php_array_key_compare_string_case;
383
1
      } else {
384
1
        return php_array_key_compare_string;
385
1
      }
386
387
0
    case PHP_SORT_NATURAL:
388
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
389
0
        return php_array_key_compare_string_natural_case;
390
0
      } else {
391
0
        return php_array_key_compare_string_natural;
392
0
      }
393
394
0
    case PHP_SORT_LOCALE_STRING:
395
0
      return php_array_key_compare_string_locale;
396
397
0
    case PHP_SORT_REGULAR:
398
0
    default:
399
0
      return php_array_key_compare;
400
6
  }
401
0
  return NULL;
402
6
}
403
404
static bucket_compare_func_t php_get_key_reverse_compare_func(zend_long sort_type)
405
0
{
406
0
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
407
0
    case PHP_SORT_NUMERIC:
408
0
      return php_array_reverse_key_compare_numeric;
409
410
0
    case PHP_SORT_STRING:
411
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
412
0
        return php_array_reverse_key_compare_string_case;
413
0
      } else {
414
0
        return php_array_reverse_key_compare_string;
415
0
      }
416
417
0
    case PHP_SORT_NATURAL:
418
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
419
0
        return php_array_reverse_key_compare_string_natural_case;
420
0
      } else {
421
0
        return php_array_reverse_key_compare_string_natural;
422
0
      }
423
424
0
    case PHP_SORT_LOCALE_STRING:
425
0
      return php_array_reverse_key_compare_string_locale;
426
427
0
    case PHP_SORT_REGULAR:
428
0
    default:
429
0
      return php_array_reverse_key_compare;
430
0
  }
431
0
  return NULL;
432
0
}
433
434
static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type) /* {{{ */
435
12
{
436
12
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
437
1
    case PHP_SORT_NUMERIC:
438
1
      return php_array_data_compare_numeric;
439
440
4
    case PHP_SORT_STRING:
441
4
      if (sort_type & PHP_SORT_FLAG_CASE) {
442
2
        return php_array_data_compare_string_case;
443
2
      } else {
444
2
        return php_array_data_compare_string;
445
2
      }
446
447
3
    case PHP_SORT_NATURAL:
448
3
      if (sort_type & PHP_SORT_FLAG_CASE) {
449
0
        return php_array_natural_case_compare;
450
3
      } else {
451
3
        return php_array_natural_compare;
452
3
      }
453
454
1
    case PHP_SORT_LOCALE_STRING:
455
1
      return php_array_data_compare_string_locale;
456
457
1
    case PHP_SORT_REGULAR:
458
3
    default:
459
3
      return php_array_data_compare;
460
12
  }
461
0
  return NULL;
462
12
}
463
464
static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_type) /* {{{ */
465
0
{
466
0
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
467
0
    case PHP_SORT_NUMERIC:
468
0
      return php_array_reverse_data_compare_numeric;
469
470
0
    case PHP_SORT_STRING:
471
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
472
0
        return php_array_reverse_data_compare_string_case;
473
0
      } else {
474
0
        return php_array_reverse_data_compare_string;
475
0
      }
476
477
0
    case PHP_SORT_NATURAL:
478
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
479
0
        return php_array_reverse_natural_case_compare;
480
0
      } else {
481
0
        return php_array_reverse_natural_compare;
482
0
      }
483
484
0
    case PHP_SORT_LOCALE_STRING:
485
0
      return php_array_reverse_data_compare_string_locale;
486
487
0
    case PHP_SORT_REGULAR:
488
0
    default:
489
0
      return php_array_reverse_data_compare;
490
0
  }
491
0
  return NULL;
492
0
}
493
494
static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, bool reverse) /* {{{ */
495
3
{
496
3
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
497
0
    case PHP_SORT_NUMERIC:
498
0
      if (reverse) {
499
0
        return php_array_reverse_data_compare_numeric_unstable;
500
0
      } else {
501
0
        return php_array_data_compare_numeric_unstable;
502
0
      }
503
0
      break;
504
505
0
    case PHP_SORT_STRING:
506
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
507
0
        if (reverse) {
508
0
          return php_array_reverse_data_compare_string_case_unstable;
509
0
        } else {
510
0
          return php_array_data_compare_string_case_unstable;
511
0
        }
512
0
      } else {
513
0
        if (reverse) {
514
0
          return php_array_reverse_data_compare_string_unstable;
515
0
        } else {
516
0
          return php_array_data_compare_string_unstable;
517
0
        }
518
0
      }
519
0
      break;
520
521
0
    case PHP_SORT_NATURAL:
522
0
      if (sort_type & PHP_SORT_FLAG_CASE) {
523
0
        if (reverse) {
524
0
          return php_array_reverse_natural_case_compare_unstable;
525
0
        } else {
526
0
          return php_array_natural_case_compare_unstable;
527
0
        }
528
0
      } else {
529
0
        if (reverse) {
530
0
          return php_array_reverse_natural_compare_unstable;
531
0
        } else {
532
0
          return php_array_natural_compare_unstable;
533
0
        }
534
0
      }
535
0
      break;
536
537
0
    case PHP_SORT_LOCALE_STRING:
538
0
      if (reverse) {
539
0
        return php_array_reverse_data_compare_string_locale_unstable;
540
0
      } else {
541
0
        return php_array_data_compare_string_locale_unstable;
542
0
      }
543
0
      break;
544
545
3
    case PHP_SORT_REGULAR:
546
3
    default:
547
3
      if (reverse) {
548
0
        return php_array_reverse_data_compare_unstable;
549
3
      } else {
550
3
        return php_array_data_compare_unstable;
551
3
      }
552
0
      break;
553
3
  }
554
0
  return NULL;
555
3
}
556
/* }}} */
557
558
PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
559
0
{
560
0
  zend_long cnt = 0;
561
0
  zval *element;
562
563
0
#ifdef ZEND_CHECK_STACK_LIMIT
564
0
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
565
0
    zend_call_stack_size_error();
566
0
    return -1;
567
0
  }
568
0
#endif
569
570
0
  if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
571
0
    if (GC_IS_RECURSIVE(ht)) {
572
0
      php_error_docref(NULL, E_WARNING, "Recursion detected");
573
      /* A user error handler may have thrown. */
574
0
      return EG(exception) ? -1 : 0;
575
0
    }
576
0
    GC_PROTECT_RECURSION(ht);
577
0
  }
578
579
0
  cnt = zend_hash_num_elements(ht);
580
0
  ZEND_HASH_FOREACH_VAL(ht, element) {
581
0
    ZVAL_DEREF(element);
582
0
    if (Z_TYPE_P(element) == IS_ARRAY) {
583
0
      zend_long sub_cnt = php_count_recursive(Z_ARRVAL_P(element));
584
0
      if (UNEXPECTED(sub_cnt < 0)) {
585
0
        cnt = -1;
586
0
        break;
587
0
      }
588
0
      cnt += sub_cnt;
589
0
    }
590
0
  } ZEND_HASH_FOREACH_END();
591
592
0
  GC_TRY_UNPROTECT_RECURSION(ht);
593
0
  return cnt;
594
0
}
595
/* }}} */
596
597
/* Consumes `zv` */
598
static zend_always_inline zend_long php_get_long(zval *zv)
599
0
{
600
0
  if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) {
601
0
    return Z_LVAL_P(zv);
602
0
  } else {
603
0
    zend_long ret = zval_get_long_func(zv, false);
604
0
    zval_ptr_dtor(zv);
605
0
    return ret;
606
0
  }
607
0
}
608
609
/* {{{ Count the number of elements in a variable (usually an array) */
610
PHP_FUNCTION(count)
611
0
{
612
0
  zval *array;
613
0
  zend_long mode = PHP_COUNT_NORMAL;
614
0
  zend_long cnt;
615
616
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
617
0
    Z_PARAM_ZVAL(array)
618
0
    Z_PARAM_OPTIONAL
619
0
    Z_PARAM_LONG(mode)
620
0
  ZEND_PARSE_PARAMETERS_END();
621
622
0
  if (mode != PHP_COUNT_NORMAL && mode != PHP_COUNT_RECURSIVE) {
623
0
    zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
624
0
    RETURN_THROWS();
625
0
  }
626
627
0
  switch (Z_TYPE_P(array)) {
628
0
    case IS_ARRAY:
629
0
      if (mode != PHP_COUNT_RECURSIVE) {
630
0
        cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
631
0
      } else {
632
0
        cnt = php_count_recursive(Z_ARRVAL_P(array));
633
0
        if (UNEXPECTED(cnt < 0)) {
634
0
          RETURN_THROWS();
635
0
        }
636
0
      }
637
0
      RETURN_LONG(cnt);
638
0
    case IS_OBJECT: {
639
0
      zval retval;
640
      /* first, we check if the handler is defined */
641
0
      zend_object *zobj = Z_OBJ_P(array);
642
0
      if (zobj->handlers->count_elements) {
643
0
        RETVAL_LONG(1);
644
0
        if (SUCCESS == zobj->handlers->count_elements(zobj, &Z_LVAL_P(return_value))) {
645
0
          return;
646
0
        }
647
0
        if (EG(exception)) {
648
0
          RETURN_THROWS();
649
0
        }
650
0
      }
651
      /* if not and the object implements Countable we call its count() method */
652
0
      if (instanceof_function(zobj->ce, zend_ce_countable)) {
653
0
        zend_function *count_fn = zend_hash_find_ptr(&zobj->ce->function_table, ZSTR_KNOWN(ZEND_STR_COUNT));
654
0
        zend_call_known_instance_method_with_0_params(count_fn, zobj, &retval);
655
0
        if (Z_TYPE(retval) != IS_UNDEF) {
656
0
          RETVAL_LONG(php_get_long(&retval));
657
0
        }
658
0
        return;
659
0
      }
660
0
    }
661
0
    ZEND_FALLTHROUGH;
662
0
    default:
663
0
      zend_argument_type_error(1, "must be of type Countable|array, %s given", zend_zval_value_name(array));
664
0
      RETURN_THROWS();
665
0
  }
666
0
}
667
/* }}} */
668
669
static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t sort_fn)
670
0
{
671
0
  HashTable *array;
672
673
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
674
0
    Z_PARAM_ARRAY_HT_EX(array, 0, 1)
675
0
  ZEND_PARSE_PARAMETERS_END();
676
677
0
  zend_array_sort(array, sort_fn, false);
678
679
0
  RETURN_TRUE;
680
0
}
681
682
/* {{{ Sort an array using natural sort */
683
PHP_FUNCTION(natsort)
684
0
{
685
0
  php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_natural_compare);
686
0
}
687
/* }}} */
688
689
/* {{{ Sort an array using case-insensitive natural sort */
690
PHP_FUNCTION(natcasesort)
691
0
{
692
0
  php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_natural_case_compare);
693
0
}
694
/* }}} */
695
696
typedef bucket_compare_func_t(*get_compare_function)(zend_long);
697
698
18
static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) {
699
18
  HashTable *array;
700
18
  zend_long sort_type = PHP_SORT_REGULAR;
701
18
  bucket_compare_func_t cmp;
702
703
54
  ZEND_PARSE_PARAMETERS_START(1, 2)
704
72
    Z_PARAM_ARRAY_HT_EX(array, 0, 1)
705
18
    Z_PARAM_OPTIONAL
706
70
    Z_PARAM_LONG(sort_type)
707
18
  ZEND_PARSE_PARAMETERS_END();
708
709
18
  cmp = get_cmp(sort_type);
710
711
18
  zend_array_sort(array, cmp, renumber);
712
713
18
  RETURN_TRUE;
714
18
}
715
716
/* {{{ Sort an array and maintain index association */
717
PHP_FUNCTION(asort)
718
0
{
719
0
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, false);
720
0
}
721
/* }}} */
722
723
/* {{{ Sort an array in reverse order and maintain index association */
724
PHP_FUNCTION(arsort)
725
0
{
726
0
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, false);
727
0
}
728
/* }}} */
729
730
/* {{{ Sort an array */
731
PHP_FUNCTION(sort)
732
12
{
733
12
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, true);
734
12
}
735
/* }}} */
736
737
/* {{{ Sort an array in reverse order */
738
PHP_FUNCTION(rsort)
739
0
{
740
0
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, true);
741
0
}
742
/* }}} */
743
744
/* {{{ Sort an array by key value in reverse order */
745
PHP_FUNCTION(krsort)
746
0
{
747
0
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_reverse_compare_func, false);
748
0
}
749
/* }}} */
750
751
/* {{{ Sort an array by key */
752
PHP_FUNCTION(ksort)
753
6
{
754
6
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_compare_func, false);
755
6
}
756
/* }}} */
757
758
static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
759
0
{
760
0
  zval args[2];
761
0
  zval retval;
762
763
0
  ZVAL_COPY_VALUE(&args[0], &f->val);
764
0
  ZVAL_COPY_VALUE(&args[1], &s->val);
765
766
0
  BG(user_compare_fci).param_count = 2;
767
0
  BG(user_compare_fci).params = args;
768
0
  BG(user_compare_fci).retval = &retval;
769
0
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
770
771
0
  if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
772
0
    if (!ARRAYG(compare_deprecation_thrown)) {
773
0
      php_error_docref(NULL, E_DEPRECATED,
774
0
        "Returning bool from comparison function is deprecated, "
775
0
        "return an integer less than, equal to, or greater than zero");
776
0
      ARRAYG(compare_deprecation_thrown) = 1;
777
0
    }
778
779
0
    if (Z_TYPE(retval) == IS_FALSE) {
780
      /* Retry with swapped operands. */
781
0
      ZVAL_COPY_VALUE(&args[0], &s->val);
782
0
      ZVAL_COPY_VALUE(&args[1], &f->val);
783
0
      zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
784
785
0
      zend_long ret = php_get_long(&retval);
786
0
      return -ZEND_NORMALIZE_BOOL(ret);
787
0
    }
788
0
  }
789
790
0
  zend_long ret = php_get_long(&retval);
791
0
  return ZEND_NORMALIZE_BOOL(ret);
792
0
}
793
/* }}} */
794
795
static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
796
0
{
797
0
  RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
798
0
}
799
/* }}} */
800
801
#define PHP_ARRAY_CMP_FUNC_VARS \
802
0
  zend_fcall_info old_user_compare_fci; \
803
0
  zend_fcall_info_cache old_user_compare_fci_cache \
804
805
#define PHP_ARRAY_CMP_FUNC_BACKUP() \
806
0
  old_user_compare_fci = BG(user_compare_fci); \
807
0
  old_user_compare_fci_cache = BG(user_compare_fci_cache); \
808
0
  ARRAYG(compare_deprecation_thrown) = 0; \
809
0
  BG(user_compare_fci_cache) = empty_fcall_info_cache; \
810
811
#define PHP_ARRAY_CMP_FUNC_RESTORE() \
812
0
  BG(user_compare_fci) = old_user_compare_fci; \
813
0
  BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
814
815
static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, bool renumber) /* {{{ */
816
0
{
817
0
  zval *array;
818
0
  zend_array *arr;
819
0
  PHP_ARRAY_CMP_FUNC_VARS;
820
821
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
822
823
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
824
0
    Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
825
0
    Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
826
0
  ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
827
828
0
  arr = Z_ARR_P(array);
829
0
  if (zend_hash_num_elements(arr) == 0)  {
830
0
    PHP_ARRAY_CMP_FUNC_RESTORE();
831
0
    RETURN_TRUE;
832
0
  }
833
834
  /* Copy array, so the in-place modifications will not be visible to the callback function */
835
0
  arr = zend_array_dup(arr);
836
837
0
  zend_array_sort(arr, compare_func, renumber);
838
839
0
  zval garbage;
840
0
  ZVAL_COPY_VALUE(&garbage, array);
841
0
  ZVAL_ARR(array, arr);
842
0
  zval_ptr_dtor(&garbage);
843
844
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
845
0
  RETURN_TRUE;
846
0
}
847
/* }}} */
848
849
/* {{{ Sort an array by values using a user-defined comparison function */
850
PHP_FUNCTION(usort)
851
0
{
852
0
  php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, true);
853
0
}
854
/* }}} */
855
856
/* {{{ Sort an array with a user-defined comparison function and maintain index association */
857
PHP_FUNCTION(uasort)
858
0
{
859
0
  php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, false);
860
0
}
861
/* }}} */
862
863
static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
864
0
{
865
0
  zval args[2];
866
0
  zval retval;
867
868
0
  if (f->key == NULL) {
869
0
    ZVAL_LONG(&args[0], f->h);
870
0
  } else {
871
0
    ZVAL_STR(&args[0], f->key);
872
0
  }
873
0
  if (s->key == NULL) {
874
0
    ZVAL_LONG(&args[1], s->h);
875
0
  } else {
876
0
    ZVAL_STR(&args[1], s->key);
877
0
  }
878
879
0
  BG(user_compare_fci).param_count = 2;
880
0
  BG(user_compare_fci).params = args;
881
0
  BG(user_compare_fci).retval = &retval;
882
0
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
883
884
0
  if (UNEXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) {
885
0
    if (!ARRAYG(compare_deprecation_thrown)) {
886
0
      php_error_docref(NULL, E_DEPRECATED,
887
0
        "Returning bool from comparison function is deprecated, "
888
0
        "return an integer less than, equal to, or greater than zero");
889
0
      ARRAYG(compare_deprecation_thrown) = 1;
890
0
    }
891
892
0
    if (Z_TYPE(retval) == IS_FALSE) {
893
      /* Retry with swapped operands. */
894
0
      if (s->key == NULL) {
895
0
        ZVAL_LONG(&args[0], s->h);
896
0
      } else {
897
0
        ZVAL_STR(&args[0], s->key);
898
0
      }
899
0
      if (f->key == NULL) {
900
0
        ZVAL_LONG(&args[1], f->h);
901
0
      } else {
902
0
        ZVAL_STR(&args[1], f->key);
903
0
      }
904
905
0
      zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
906
907
0
      zend_long ret = php_get_long(&retval);
908
0
      return -ZEND_NORMALIZE_BOOL(ret);
909
0
    }
910
0
  }
911
912
0
  zend_long result = php_get_long(&retval);
913
0
  return ZEND_NORMALIZE_BOOL(result);
914
0
}
915
/* }}} */
916
917
static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */
918
0
{
919
0
  RETURN_STABLE_SORT(a, b, php_array_user_key_compare_unstable(a, b));
920
0
}
921
/* }}} */
922
923
/* {{{ Sort an array by keys using a user-defined comparison function */
924
PHP_FUNCTION(uksort)
925
0
{
926
0
  php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, false);
927
0
}
928
/* }}} */
929
930
18
static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
931
18
  if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
932
3
    return Z_ARRVAL_P(zv);
933
3
  }
934
935
15
  ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
936
15
  php_error_docref(NULL, E_DEPRECATED,
937
15
    "Calling %s() on an object is deprecated", get_active_function_name());
938
939
15
  zend_object *zobj = Z_OBJ_P(zv);
940
15
  if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
941
2
    if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
942
2
      GC_DELREF(zobj->properties);
943
2
    }
944
2
    zobj->properties = zend_array_dup(zobj->properties);
945
2
  }
946
15
  return zobj->handlers->get_properties(zobj);
947
15
}
948
949
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
950
17
{
951
17
  zval *entry;
952
953
17
  while (true) {
954
17
    if ((entry = zend_hash_get_current_data(array)) == NULL) {
955
4
      return NULL;
956
4
    }
957
958
13
    ZVAL_DEINDIRECT(entry);
959
960
    /* Possible with an uninitialized typed property */
961
13
    if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
962
0
      zend_result result;
963
0
      if (forward_direction) {
964
0
        result = zend_hash_move_forward(array);
965
0
      } else {
966
0
        result = zend_hash_move_backwards(array);
967
0
      }
968
0
      if (result != SUCCESS) {
969
0
        return NULL;
970
0
      }
971
13
    } else {
972
13
      break;
973
13
    }
974
13
  }
975
976
13
  return entry;
977
17
}
978
979
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
980
14
{
981
14
  zval *entry = php_array_iter_seek_current(array, forward_direction);
982
14
  if (EXPECTED(entry)) {
983
10
    RETURN_COPY_DEREF(entry);
984
10
  } else {
985
4
    RETURN_FALSE;
986
4
  }
987
14
}
988
989
/* {{{ Advances array argument's internal pointer to the last element and return it */
990
PHP_FUNCTION(end)
991
2
{
992
2
  zval *array_zv;
993
994
6
  ZEND_PARSE_PARAMETERS_START(1, 1)
995
8
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
996
2
  ZEND_PARSE_PARAMETERS_END();
997
998
2
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
999
2
  if (zend_hash_num_elements(array) == 0) {
1000
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1001
0
    RETURN_FALSE;
1002
0
  }
1003
2
  zend_hash_internal_pointer_end(array);
1004
1005
2
  if (USED_RET()) {
1006
2
    php_array_iter_return_current(return_value, array, false);
1007
2
  }
1008
2
}
1009
/* }}} */
1010
1011
/* {{{ Move array argument's internal pointer to the previous element and return it */
1012
PHP_FUNCTION(prev)
1013
2
{
1014
2
  zval *array_zv;
1015
1016
6
  ZEND_PARSE_PARAMETERS_START(1, 1)
1017
8
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1018
2
  ZEND_PARSE_PARAMETERS_END();
1019
1020
2
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1021
2
  if (zend_hash_num_elements(array) == 0) {
1022
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1023
0
    RETURN_FALSE;
1024
0
  }
1025
2
  zend_hash_move_backwards(array);
1026
1027
2
  if (USED_RET()) {
1028
2
    php_array_iter_return_current(return_value, array, false);
1029
2
  }
1030
2
}
1031
/* }}} */
1032
1033
/* {{{ Move array argument's internal pointer to the next element and return it */
1034
PHP_FUNCTION(next)
1035
2
{
1036
2
  zval *array_zv;
1037
1038
6
  ZEND_PARSE_PARAMETERS_START(1, 1)
1039
8
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1040
2
  ZEND_PARSE_PARAMETERS_END();
1041
1042
2
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1043
2
  if (zend_hash_num_elements(array) == 0) {
1044
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1045
0
    RETURN_FALSE;
1046
0
  }
1047
2
  zend_hash_move_forward(array);
1048
1049
2
  if (USED_RET()) {
1050
2
    php_array_iter_return_current(return_value, array, true);
1051
2
  }
1052
2
}
1053
/* }}} */
1054
1055
/* {{{ Set array argument's internal pointer to the first element and return it */
1056
PHP_FUNCTION(reset)
1057
4
{
1058
4
  zval *array_zv;
1059
1060
12
  ZEND_PARSE_PARAMETERS_START(1, 1)
1061
16
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1062
4
  ZEND_PARSE_PARAMETERS_END();
1063
1064
4
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1065
4
  if (zend_hash_num_elements(array) == 0) {
1066
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1067
0
    RETURN_FALSE;
1068
0
  }
1069
4
  zend_hash_internal_pointer_reset(array);
1070
1071
4
  if (USED_RET()) {
1072
3
    php_array_iter_return_current(return_value, array, true);
1073
3
  }
1074
4
}
1075
/* }}} */
1076
1077
/* {{{ Return the element currently pointed to by the internal array pointer */
1078
PHP_FUNCTION(current)
1079
5
{
1080
5
  zval *array_zv;
1081
1082
15
  ZEND_PARSE_PARAMETERS_START(1, 1)
1083
20
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1084
5
  ZEND_PARSE_PARAMETERS_END();
1085
1086
5
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1087
5
  php_array_iter_return_current(return_value, array, true);
1088
5
}
1089
/* }}} */
1090
1091
/* {{{ Return the key of the element currently pointed to by the internal array pointer */
1092
PHP_FUNCTION(key)
1093
3
{
1094
3
  zval *array_zv;
1095
1096
9
  ZEND_PARSE_PARAMETERS_START(1, 1)
1097
12
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1098
3
  ZEND_PARSE_PARAMETERS_END();
1099
1100
3
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1101
3
  zval *entry = php_array_iter_seek_current(array, true);
1102
3
  if (EXPECTED(entry)) {
1103
3
    zend_hash_get_current_key_zval(array, return_value);
1104
3
  }
1105
3
}
1106
/* }}} */
1107
1108
static zval *php_array_data_minmax(HashTable *array, bool max) /* {{{ */
1109
0
{
1110
0
  zval *entry, *result = NULL;
1111
1112
0
  ZEND_HASH_FOREACH_VAL(array, entry) {
1113
0
    if (!result) {
1114
0
      result = entry;
1115
0
      continue;
1116
0
    }
1117
1118
0
    int cmp = zend_compare(result, entry);
1119
0
    if (max ? cmp < 0 : cmp > 0) {
1120
0
      result = entry;
1121
0
    }
1122
0
  } ZEND_HASH_FOREACH_END();
1123
1124
0
  return result;
1125
0
}
1126
/* }}} */
1127
1128
/* {{{
1129
 * proto mixed min(array values)
1130
 * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
1131
   Return the lowest value in an array or a series of arguments */
1132
PHP_FUNCTION(min)
1133
0
{
1134
0
  uint32_t argc;
1135
0
  zval *args = NULL;
1136
1137
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
1138
0
    Z_PARAM_VARIADIC('+', args, argc)
1139
0
  ZEND_PARSE_PARAMETERS_END();
1140
1141
  /* mixed min ( array $values ) */
1142
0
  if (argc == 1) {
1143
0
    if (Z_TYPE(args[0]) != IS_ARRAY) {
1144
0
      zend_wrong_parameter_type_error(1, Z_EXPECTED_ARRAY, &args[0]);
1145
0
      RETURN_THROWS();
1146
0
    } else {
1147
0
      zval *result = php_array_data_minmax(Z_ARRVAL(args[0]), false);
1148
0
      if (result) {
1149
0
        RETURN_COPY_DEREF(result);
1150
0
      } else {
1151
0
        zend_argument_value_error(1, "must contain at least one element");
1152
0
        RETURN_THROWS();
1153
0
      }
1154
0
    }
1155
0
  } else {
1156
    /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1157
0
    zval *min;
1158
0
    uint32_t i;
1159
1160
0
    min = &args[0];
1161
0
    zend_long min_lval;
1162
0
    double min_dval;
1163
1164
0
    if (Z_TYPE_P(min) == IS_LONG) {
1165
0
      min_lval = Z_LVAL_P(min);
1166
1167
0
      for (i = 1; i < argc; i++) {
1168
0
        if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1169
0
          if (min_lval > Z_LVAL(args[i])) {
1170
0
            min_lval = Z_LVAL(args[i]);
1171
0
            min = &args[i];
1172
0
          }
1173
0
        } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval_silent((double) min_lval) == min_lval)) {
1174
          /* if min_lval can be exactly represented as a double, go to double dedicated code */
1175
0
          min_dval = (double) min_lval;
1176
0
          goto double_compare;
1177
0
        } else {
1178
0
          goto generic_compare;
1179
0
        }
1180
0
      }
1181
1182
0
      RETURN_LONG(min_lval);
1183
0
    } else if (Z_TYPE_P(min) == IS_DOUBLE) {
1184
0
      min_dval = Z_DVAL_P(min);
1185
1186
0
      for (i = 1; i < argc; i++) {
1187
0
        if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1188
0
          double_compare:
1189
0
          if (min_dval > Z_DVAL(args[i])) {
1190
0
            min_dval = Z_DVAL(args[i]);
1191
0
            min = &args[i];
1192
0
          }
1193
0
        } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval_silent((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1194
          /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1195
0
          if (min_dval > (double)Z_LVAL(args[i])) {
1196
0
            min_dval = (double)Z_LVAL(args[i]);
1197
0
            min = &args[i];
1198
0
          }
1199
0
        } else {
1200
0
          goto generic_compare;
1201
0
        }
1202
0
      }
1203
0
    } else {
1204
0
      for (i = 1; i < argc; i++) {
1205
0
        generic_compare:
1206
0
        if (zend_compare(&args[i], min) < 0) {
1207
0
          min = &args[i];
1208
0
        }
1209
0
      }
1210
0
    }
1211
1212
0
    RETURN_COPY(min);
1213
0
  }
1214
0
}
1215
/* }}} */
1216
1217
ZEND_FRAMELESS_FUNCTION(min, 2)
1218
0
{
1219
0
  zval *lhs, *rhs;
1220
1221
0
  Z_FLF_PARAM_ZVAL(1, lhs);
1222
0
  Z_FLF_PARAM_ZVAL(2, rhs);
1223
1224
0
  double lhs_dval;
1225
1226
0
  if (Z_TYPE_P(lhs) == IS_LONG) {
1227
0
    zend_long lhs_lval = Z_LVAL_P(lhs);
1228
1229
0
    if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
1230
0
      RETURN_COPY_VALUE(lhs_lval < Z_LVAL_P(rhs) ? lhs : rhs);
1231
0
    } else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval_silent((double) lhs_lval) == lhs_lval)) {
1232
      /* if lhs_lval can be exactly represented as a double, go to double dedicated code */
1233
0
      lhs_dval = (double) lhs_lval;
1234
0
      goto double_compare;
1235
0
    } else {
1236
0
      goto generic_compare;
1237
0
    }
1238
0
  } else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
1239
0
    lhs_dval = Z_DVAL_P(lhs);
1240
1241
0
    if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
1242
0
double_compare:
1243
0
      RETURN_COPY_VALUE(lhs_dval < Z_DVAL_P(rhs) ? lhs : rhs);
1244
0
    } else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval_silent((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
1245
      /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1246
0
      RETURN_COPY_VALUE(lhs_dval < (double)Z_LVAL_P(rhs) ? lhs : rhs);
1247
0
    } else {
1248
0
      goto generic_compare;
1249
0
    }
1250
0
  } else {
1251
0
generic_compare:
1252
0
    RETURN_COPY(zend_compare(lhs, rhs) < 0 ? lhs : rhs);
1253
0
  }
1254
0
}
1255
1256
/* {{{
1257
 * proto mixed max(array values)
1258
 * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
1259
   Return the highest value in an array or a series of arguments */
1260
PHP_FUNCTION(max)
1261
1
{
1262
1
  zval *args = NULL;
1263
1
  uint32_t argc;
1264
1265
3
  ZEND_PARSE_PARAMETERS_START(1, -1)
1266
3
    Z_PARAM_VARIADIC('+', args, argc)
1267
3
  ZEND_PARSE_PARAMETERS_END();
1268
1269
  /* mixed max ( array $values ) */
1270
1
  if (argc == 1) {
1271
0
    if (Z_TYPE(args[0]) != IS_ARRAY) {
1272
0
      zend_wrong_parameter_type_error(1, Z_EXPECTED_ARRAY, &args[0]);
1273
0
      RETURN_THROWS();
1274
0
    } else {
1275
0
      zval *result = php_array_data_minmax(Z_ARRVAL(args[0]), true);
1276
0
      if (result) {
1277
0
        RETURN_COPY_DEREF(result);
1278
0
      } else {
1279
0
        zend_argument_value_error(1, "must contain at least one element");
1280
0
        RETURN_THROWS();
1281
0
      }
1282
0
    }
1283
1
  } else {
1284
    /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1285
1
    zval *max;
1286
1
    uint32_t i;
1287
1288
1
    max = &args[0];
1289
1
    zend_long max_lval;
1290
1
    double max_dval;
1291
1292
1
    if (Z_TYPE_P(max) == IS_LONG) {
1293
1
      max_lval = Z_LVAL_P(max);
1294
1295
2
      for (i = 1; i < argc; i++) {
1296
1
        if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1297
1
          if (max_lval < Z_LVAL(args[i])) {
1298
0
            max_lval = Z_LVAL(args[i]);
1299
0
            max = &args[i];
1300
0
          }
1301
1
        } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval_silent((double) max_lval) == max_lval)) {
1302
          /* if max_lval can be exactly represented as a double, go to double dedicated code */
1303
0
          max_dval = (double) max_lval;
1304
0
          goto double_compare;
1305
0
        } else {
1306
0
          goto generic_compare;
1307
0
        }
1308
1
      }
1309
1310
1
      RETURN_LONG(max_lval);
1311
1
    } else if (Z_TYPE_P(max) == IS_DOUBLE) {
1312
0
      max_dval = Z_DVAL_P(max);
1313
1314
0
      for (i = 1; i < argc; i++) {
1315
0
        if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1316
0
          double_compare:
1317
0
          if (max_dval < Z_DVAL(args[i])) {
1318
0
            max_dval = Z_DVAL(args[i]);
1319
0
            max = &args[i];
1320
0
          }
1321
0
        } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval_silent((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1322
          /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1323
0
          if (max_dval < (double)Z_LVAL(args[i])) {
1324
0
            max_dval = (double)Z_LVAL(args[i]);
1325
0
            max = &args[i];
1326
0
          }
1327
0
        } else {
1328
0
          goto generic_compare;
1329
0
        }
1330
0
      }
1331
0
    } else {
1332
0
      for (i = 1; i < argc; i++) {
1333
0
        generic_compare:
1334
0
        if (zend_compare(&args[i], max) > 0) {
1335
0
          max = &args[i];
1336
0
        }
1337
0
      }
1338
0
    }
1339
1340
0
    RETURN_COPY(max);
1341
0
  }
1342
1
}
1343
/* }}} */
1344
1345
ZEND_FRAMELESS_FUNCTION(max, 2)
1346
0
{
1347
0
  zval *lhs, *rhs;
1348
1349
0
  Z_FLF_PARAM_ZVAL(1, lhs);
1350
0
  Z_FLF_PARAM_ZVAL(2, rhs);
1351
1352
0
  double lhs_dval;
1353
1354
0
  if (Z_TYPE_P(lhs) == IS_LONG) {
1355
0
    zend_long lhs_lval = Z_LVAL_P(lhs);
1356
1357
0
    if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
1358
0
      RETURN_COPY_VALUE(lhs_lval >= Z_LVAL_P(rhs) ? lhs : rhs);
1359
0
    } else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval_silent((double) lhs_lval) == lhs_lval)) {
1360
      /* if lhs_lval can be exactly represented as a double, go to double dedicated code */
1361
0
      lhs_dval = (double) lhs_lval;
1362
0
      goto double_compare;
1363
0
    } else {
1364
0
      goto generic_compare;
1365
0
    }
1366
0
  } else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
1367
0
    lhs_dval = Z_DVAL_P(lhs);
1368
1369
0
    if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
1370
0
double_compare:
1371
0
      RETURN_COPY_VALUE(lhs_dval >= Z_DVAL_P(rhs) ? lhs : rhs);
1372
0
    } else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval_silent((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
1373
      /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1374
0
      RETURN_COPY_VALUE(lhs_dval >= (double)Z_LVAL_P(rhs) ? lhs : rhs);
1375
0
    } else {
1376
0
      goto generic_compare;
1377
0
    }
1378
0
  } else {
1379
0
generic_compare:
1380
0
    RETURN_COPY(zend_compare(lhs, rhs) >= 0 ? lhs : rhs);
1381
0
  }
1382
0
}
1383
1384
typedef struct {
1385
  zend_fcall_info fci;
1386
  zend_fcall_info_cache fci_cache;
1387
} php_array_walk_context;
1388
1389
static zend_result php_array_walk(
1390
  php_array_walk_context *context, zval *array, zval *userdata, bool recursive)
1391
1
{
1392
1
  zval args[3],   /* Arguments to userland function */
1393
1
     retval,    /* Return value - unused */
1394
1
     *zv;
1395
1
  HashTable *target_hash = HASH_OF(array);
1396
1
  HashPosition pos;
1397
1
  uint32_t ht_iter;
1398
1
  zend_result result = SUCCESS;
1399
1400
  /* Create a local copy of fci, as we want to use different arguments at different
1401
   * levels of recursion. */
1402
1
  zend_fcall_info fci = context->fci;
1403
1404
1
#ifdef ZEND_CHECK_STACK_LIMIT
1405
1
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
1406
0
    zend_call_stack_size_error();
1407
0
    return FAILURE;
1408
0
  }
1409
1
#endif
1410
1411
1
  if (zend_hash_num_elements(target_hash) == 0) {
1412
0
    return result;
1413
0
  }
1414
1415
  /* Set up known arguments */
1416
1
  ZVAL_UNDEF(&args[1]);
1417
1
  if (userdata) {
1418
0
    ZVAL_COPY_VALUE(&args[2], userdata);
1419
0
  }
1420
1421
1
  fci.retval = &retval;
1422
1
  fci.param_count = userdata ? 3 : 2;
1423
1
  fci.params = args;
1424
1425
1
  zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1426
1
  ht_iter = zend_hash_iterator_add(target_hash, pos);
1427
1428
  /* Iterate through hash */
1429
2
  do {
1430
    /* Retrieve value */
1431
2
    zv = zend_hash_get_current_data_ex(target_hash, &pos);
1432
2
    if (zv == NULL) {
1433
1
      break;
1434
1
    }
1435
1436
    /* Skip undefined indirect elements */
1437
1
    if (Z_TYPE_P(zv) == IS_INDIRECT) {
1438
1
      zv = Z_INDIRECT_P(zv);
1439
1
      if (Z_TYPE_P(zv) == IS_UNDEF) {
1440
0
        zend_hash_move_forward_ex(target_hash, &pos);
1441
0
        continue;
1442
0
      }
1443
1444
      /* Add type source for property references. */
1445
1
      if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1446
1
        zend_property_info *prop_info =
1447
1
          zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1448
1
        if (prop_info) {
1449
0
          ZVAL_NEW_REF(zv, zv);
1450
0
          ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1451
0
        }
1452
1
      }
1453
1
    }
1454
1455
    /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1456
1
    ZVAL_MAKE_REF(zv);
1457
1458
    /* Retrieve key */
1459
1
    zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
1460
1461
    /* Move to next element already now -- this mirrors the approach used by foreach
1462
     * and ensures proper behavior with regard to modifications. */
1463
1
    zend_hash_move_forward_ex(target_hash, &pos);
1464
1465
    /* Back up hash position, as it may change */
1466
1
    EG(ht_iterators)[ht_iter].pos = pos;
1467
1468
1
    if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
1469
0
      HashTable *thash;
1470
0
      zval ref;
1471
0
      ZVAL_COPY_VALUE(&ref, zv);
1472
1473
0
      ZVAL_DEREF(zv);
1474
0
      SEPARATE_ARRAY(zv);
1475
0
      thash = Z_ARRVAL_P(zv);
1476
0
      if (GC_IS_RECURSIVE(thash)) {
1477
0
        zend_throw_error(NULL, "Recursion detected");
1478
0
        result = FAILURE;
1479
0
        break;
1480
0
      }
1481
1482
0
      Z_ADDREF(ref);
1483
0
      GC_PROTECT_RECURSION(thash);
1484
0
      result = php_array_walk(context, zv, userdata, recursive);
1485
0
      if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
1486
        /* If the hashtable changed in the meantime, we'll "leak" this apply count
1487
         * increment -- our reference to thash is no longer valid. */
1488
0
        GC_UNPROTECT_RECURSION(thash);
1489
0
      }
1490
0
      zval_ptr_dtor(&ref);
1491
1
    } else {
1492
1
      ZVAL_COPY_VALUE(&args[0], zv);
1493
1494
      /* Call the userland function */
1495
1
      result = zend_call_function(&fci, &context->fci_cache);
1496
1
      zval_ptr_dtor(&retval);
1497
1
    }
1498
1499
1
    zval_ptr_dtor_str(&args[1]);
1500
1501
1
    if (result == FAILURE) {
1502
0
      break;
1503
0
    }
1504
1505
    /* Reload array and position -- both may have changed */
1506
1
    if (Z_TYPE_P(array) == IS_ARRAY) {
1507
0
      pos = zend_hash_iterator_pos_ex(ht_iter, array);
1508
0
      target_hash = Z_ARRVAL_P(array);
1509
1
    } else if (Z_TYPE_P(array) == IS_OBJECT) {
1510
1
      target_hash = Z_OBJPROP_P(array);
1511
1
      pos = zend_hash_iterator_pos(ht_iter, target_hash);
1512
1
    } else {
1513
0
      zend_type_error("Iterated value is no longer an array or object");
1514
0
      result = FAILURE;
1515
0
      break;
1516
0
    }
1517
1
  } while (!EG(exception));
1518
1519
1
  zend_hash_iterator_del(ht_iter);
1520
1
  return result;
1521
1
}
1522
1523
/* {{{ Apply a user function to every member of an array */
1524
PHP_FUNCTION(array_walk)
1525
2
{
1526
2
  zval *array;
1527
2
  zval *userdata = NULL;
1528
2
  php_array_walk_context context;
1529
1530
6
  ZEND_PARSE_PARAMETERS_START(2, 3)
1531
8
    Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1532
10
    Z_PARAM_FUNC(context.fci, context.fci_cache)
1533
1
    Z_PARAM_OPTIONAL
1534
2
    Z_PARAM_ZVAL(userdata)
1535
2
  ZEND_PARSE_PARAMETERS_END();
1536
1537
1
  if (Z_TYPE_P(array) == IS_OBJECT) {
1538
1
    php_error_docref(NULL, E_DEPRECATED,
1539
1
      "Passing an object for argument #1 $array to array_walk() is deprecated, call get_object_vars() first instead");
1540
1
    if (UNEXPECTED(EG(exception))) {
1541
0
      RETURN_THROWS();
1542
0
    }
1543
1
  }
1544
1545
1
  php_array_walk(&context, array, userdata, /* recursive */ false);
1546
1
  RETURN_TRUE;
1547
1
}
1548
/* }}} */
1549
1550
/* {{{ Apply a user function recursively to every member of an array */
1551
PHP_FUNCTION(array_walk_recursive)
1552
0
{
1553
0
  zval *array;
1554
0
  zval *userdata = NULL;
1555
0
  php_array_walk_context context;
1556
1557
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
1558
0
    Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1559
0
    Z_PARAM_FUNC(context.fci, context.fci_cache)
1560
0
    Z_PARAM_OPTIONAL
1561
0
    Z_PARAM_ZVAL(userdata)
1562
0
  ZEND_PARSE_PARAMETERS_END();
1563
1564
1565
0
  if (Z_TYPE_P(array) == IS_OBJECT) {
1566
0
    php_error_docref(NULL, E_DEPRECATED,
1567
0
      "Passing an object for argument #1 $array to array_walk_recursive() is deprecated, call get_object_vars() first instead");
1568
0
    if (UNEXPECTED(EG(exception))) {
1569
0
      RETURN_THROWS();
1570
0
    }
1571
0
  }
1572
1573
0
  php_array_walk(&context, array, userdata, /* recursive */ true);
1574
0
  RETURN_TRUE;
1575
0
}
1576
/* }}} */
1577
1578
/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1579
 * 0 = return boolean
1580
 * 1 = return key
1581
 */
1582
static zend_always_inline void _php_search_array(zval *return_value, zval *value, zval *array, bool strict, int behavior) /* {{{ */
1583
19
{
1584
19
  zval *entry; /* pointer to array entry */
1585
19
  zend_ulong num_idx;
1586
19
  zend_string *str_idx;
1587
1588
19
  if (strict) {
1589
18
    if (Z_TYPE_P(value) == IS_LONG) {
1590
0
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1591
0
        ZVAL_DEREF(entry);
1592
0
        if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1593
0
          if (behavior == 0) {
1594
0
            RETURN_TRUE;
1595
0
          } else {
1596
0
            if (str_idx) {
1597
0
              RETURN_STR_COPY(str_idx);
1598
0
            } else {
1599
0
              RETURN_LONG(num_idx);
1600
0
            }
1601
0
          }
1602
0
        }
1603
0
      } ZEND_HASH_FOREACH_END();
1604
18
    } else {
1605
15.4k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1606
15.4k
        ZVAL_DEREF(entry);
1607
15.4k
        if (fast_is_identical_function(value, entry)) {
1608
5
          if (behavior == 0) {
1609
5
            RETURN_TRUE;
1610
5
          } else {
1611
0
            if (str_idx) {
1612
0
              RETURN_STR_COPY(str_idx);
1613
0
            } else {
1614
0
              RETURN_LONG(num_idx);
1615
0
            }
1616
0
          }
1617
5
        }
1618
15.4k
      } ZEND_HASH_FOREACH_END();
1619
18
    }
1620
18
  } else {
1621
1
    if (Z_TYPE_P(value) == IS_LONG) {
1622
0
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1623
0
        if (fast_equal_check_long(value, entry)) {
1624
0
          if (behavior == 0) {
1625
0
            RETURN_TRUE;
1626
0
          } else {
1627
0
            if (str_idx) {
1628
0
              RETURN_STR_COPY(str_idx);
1629
0
            } else {
1630
0
              RETURN_LONG(num_idx);
1631
0
            }
1632
0
          }
1633
0
        }
1634
0
      } ZEND_HASH_FOREACH_END();
1635
1
    } else if (Z_TYPE_P(value) == IS_STRING) {
1636
1.02k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1637
1.02k
        if (fast_equal_check_string(value, entry)) {
1638
0
          if (behavior == 0) {
1639
0
            RETURN_TRUE;
1640
0
          } else {
1641
0
            if (str_idx) {
1642
0
              RETURN_STR_COPY(str_idx);
1643
0
            } else {
1644
0
              RETURN_LONG(num_idx);
1645
0
            }
1646
0
          }
1647
0
        }
1648
1.02k
      } ZEND_HASH_FOREACH_END();
1649
1
    } else {
1650
0
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1651
0
        if (fast_equal_check_function(value, entry)) {
1652
0
          if (behavior == 0) {
1653
0
            RETURN_TRUE;
1654
0
          } else {
1655
0
            if (str_idx) {
1656
0
              RETURN_STR_COPY(str_idx);
1657
0
            } else {
1658
0
              RETURN_LONG(num_idx);
1659
0
            }
1660
0
          }
1661
0
        }
1662
0
      } ZEND_HASH_FOREACH_END();
1663
0
    }
1664
1
  }
1665
1666
14
  RETURN_FALSE;
1667
14
}
1668
/* }}} */
1669
1670
/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1671
 * 0 = return boolean
1672
 * 1 = return key
1673
 */
1674
static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1675
20
{
1676
20
  zval *value,    /* value to check for */
1677
20
     *array;    /* array to check in */
1678
20
  bool strict = 0;  /* strict comparison or not */
1679
1680
60
  ZEND_PARSE_PARAMETERS_START(2, 3)
1681
80
    Z_PARAM_ZVAL(value)
1682
80
    Z_PARAM_ARRAY(array)
1683
19
    Z_PARAM_OPTIONAL
1684
74
    Z_PARAM_BOOL(strict)
1685
20
  ZEND_PARSE_PARAMETERS_END();
1686
1687
19
  _php_search_array(return_value, value, array, strict, behavior);
1688
19
}
1689
1690
/* {{{ Checks if the given value exists in the array */
1691
PHP_FUNCTION(in_array)
1692
20
{
1693
20
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1694
20
}
1695
/* }}} */
1696
1697
ZEND_FRAMELESS_FUNCTION(in_array, 2)
1698
0
{
1699
0
  zval *value, *array;
1700
1701
0
  Z_FLF_PARAM_ZVAL(1, value);
1702
0
  Z_FLF_PARAM_ARRAY(2, array);
1703
1704
0
  _php_search_array(return_value, value, array, false, 0);
1705
1706
0
flf_clean:;
1707
0
}
1708
1709
ZEND_FRAMELESS_FUNCTION(in_array, 3)
1710
0
{
1711
0
  zval *value, *array;
1712
0
  bool strict;
1713
1714
0
  Z_FLF_PARAM_ZVAL(1, value);
1715
0
  Z_FLF_PARAM_ARRAY(2, array);
1716
0
  Z_FLF_PARAM_BOOL(3, strict);
1717
1718
0
  _php_search_array(return_value, value, array, strict, 0);
1719
1720
0
flf_clean:;
1721
0
}
1722
1723
/* {{{ Searches the array for a given value and returns the corresponding key if successful */
1724
PHP_FUNCTION(array_search)
1725
0
{
1726
0
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1727
0
}
1728
/* }}} */
1729
1730
static zend_always_inline bool php_valid_var_name(const zend_string *var_name) /* {{{ */
1731
9
{
1732
  /* first 256 bits for first character, and second 256 bits for the next */
1733
9
  static const uint32_t charset[8] = {
1734
       /*  31      0   63     32   95     64   127    96 */
1735
9
      0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1736
9
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1737
9
  static const uint32_t charset2[8] = {
1738
       /*  31      0   63     32   95     64   127    96 */
1739
9
      0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1740
9
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1741
1742
9
  if (UNEXPECTED(!ZSTR_LEN(var_name))) {
1743
0
    return false;
1744
0
  }
1745
1746
  /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1747
9
  uint32_t ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[0];
1748
9
  if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1749
3
    return false;
1750
3
  }
1751
1752
  /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1753
6
  if (ZSTR_LEN(var_name) > 1) {
1754
4
    size_t i = 1;
1755
26
    do {
1756
26
      ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[i];
1757
26
      if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1758
1
        return false;
1759
1
      }
1760
26
    } while (++i < ZSTR_LEN(var_name));
1761
4
  }
1762
5
  return true;
1763
6
}
1764
/* }}} */
1765
1766
static zend_string* php_prefix_varname(const zend_string *prefix, const zend_string *var_name)
1767
0
{
1768
0
  return zend_string_concat3(ZSTR_VAL(prefix), ZSTR_LEN(prefix), ZEND_STRL("_"), ZSTR_VAL(var_name), ZSTR_LEN(var_name));
1769
0
}
1770
1771
static zend_long php_extract_ref_if_exists(const zend_array *arr, const zend_array *symbol_table) /* {{{ */
1772
0
{
1773
0
  zend_long count = 0;
1774
0
  zend_string *var_name;
1775
0
  zval *entry, *orig_var;
1776
1777
0
  if (HT_IS_PACKED(arr)) {
1778
0
    return 0;
1779
0
  }
1780
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1781
0
    if (!var_name) {
1782
0
      continue;
1783
0
    }
1784
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1785
0
    if (orig_var) {
1786
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1787
0
        orig_var = Z_INDIRECT_P(orig_var);
1788
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1789
0
          continue;
1790
0
        }
1791
0
      }
1792
0
      if (!php_valid_var_name(var_name)) {
1793
0
        continue;
1794
0
      }
1795
0
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1796
0
        continue;
1797
0
      }
1798
0
      if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1799
0
        zend_throw_error(NULL, "Cannot re-assign $this");
1800
0
        return -1;
1801
0
      }
1802
0
      if (Z_ISREF_P(entry)) {
1803
0
        Z_ADDREF_P(entry);
1804
0
      } else {
1805
0
        ZVAL_MAKE_REF_EX(entry, 2);
1806
0
      }
1807
0
      zval_ptr_dtor(orig_var);
1808
0
      ZVAL_REF(orig_var, Z_REF_P(entry));
1809
0
      count++;
1810
0
    }
1811
0
  } ZEND_HASH_FOREACH_END();
1812
1813
0
  return count;
1814
0
}
1815
/* }}} */
1816
1817
static zend_long php_extract_if_exists(const zend_array *arr, const zend_array *symbol_table) /* {{{ */
1818
0
{
1819
0
  zend_long count = 0;
1820
0
  zend_string *var_name;
1821
0
  zval *entry, *orig_var;
1822
1823
0
  if (HT_IS_PACKED(arr)) {
1824
0
    return 0;
1825
0
  }
1826
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1827
0
    if (!var_name) {
1828
0
      continue;
1829
0
    }
1830
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1831
0
    if (orig_var) {
1832
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1833
0
        orig_var = Z_INDIRECT_P(orig_var);
1834
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1835
0
          continue;
1836
0
        }
1837
0
      }
1838
0
      if (!php_valid_var_name(var_name)) {
1839
0
        continue;
1840
0
      }
1841
0
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1842
0
        continue;
1843
0
      }
1844
0
      if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1845
0
        zend_throw_error(NULL, "Cannot re-assign $this");
1846
0
        return -1;
1847
0
      }
1848
0
      ZVAL_DEREF(entry);
1849
0
      ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1850
0
      if (UNEXPECTED(EG(exception))) {
1851
0
        return -1;
1852
0
      }
1853
0
      count++;
1854
0
    }
1855
0
  } ZEND_HASH_FOREACH_END();
1856
1857
0
  return count;
1858
0
}
1859
/* }}} */
1860
1861
static zend_long php_extract_ref_overwrite(const zend_array *arr, zend_array *symbol_table) /* {{{ */
1862
0
{
1863
0
  zend_long count = 0;
1864
0
  zend_string *var_name;
1865
0
  zval *entry, *orig_var;
1866
1867
0
  if (HT_IS_PACKED(arr)) {
1868
0
    return 0;
1869
0
  }
1870
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1871
0
    if (!var_name) {
1872
0
      continue;
1873
0
    }
1874
0
    if (!php_valid_var_name(var_name)) {
1875
0
      continue;
1876
0
    }
1877
0
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1878
0
      zend_throw_error(NULL, "Cannot re-assign $this");
1879
0
      return -1;
1880
0
    }
1881
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1882
0
    if (orig_var) {
1883
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1884
0
        orig_var = Z_INDIRECT_P(orig_var);
1885
0
      }
1886
0
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1887
0
        continue;
1888
0
      }
1889
0
      if (Z_ISREF_P(entry)) {
1890
0
        Z_ADDREF_P(entry);
1891
0
      } else {
1892
0
        ZVAL_MAKE_REF_EX(entry, 2);
1893
0
      }
1894
0
      zval garbage;
1895
0
      ZVAL_COPY_VALUE(&garbage, orig_var);
1896
0
      ZVAL_REF(orig_var, Z_REF_P(entry));
1897
0
      zval_ptr_dtor(&garbage);
1898
0
    } else {
1899
0
      if (Z_ISREF_P(entry)) {
1900
0
        Z_ADDREF_P(entry);
1901
0
      } else {
1902
0
        ZVAL_MAKE_REF_EX(entry, 2);
1903
0
      }
1904
0
      zend_hash_add_new(symbol_table, var_name, entry);
1905
0
    }
1906
0
    count++;
1907
0
  } ZEND_HASH_FOREACH_END();
1908
1909
0
  return count;
1910
0
}
1911
/* }}} */
1912
1913
static zend_long php_extract_overwrite(const zend_array *arr, zend_array *symbol_table) /* {{{ */
1914
5
{
1915
5
  zend_long count = 0;
1916
5
  zend_string *var_name;
1917
5
  zval *entry, *orig_var;
1918
1919
5
  if (HT_IS_PACKED(arr)) {
1920
0
    return 0;
1921
0
  }
1922
28
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1923
28
    if (!var_name) {
1924
0
      continue;
1925
0
    }
1926
9
    if (!php_valid_var_name(var_name)) {
1927
4
      continue;
1928
4
    }
1929
5
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1930
1
      zend_throw_error(NULL, "Cannot re-assign $this");
1931
1
      return -1;
1932
1
    }
1933
4
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1934
4
    if (orig_var) {
1935
2
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1936
2
        orig_var = Z_INDIRECT_P(orig_var);
1937
2
      }
1938
2
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1939
0
        continue;
1940
0
      }
1941
2
      ZVAL_DEREF(entry);
1942
2
      ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1943
2
      if (UNEXPECTED(EG(exception))) {
1944
0
        return -1;
1945
0
      }
1946
2
    } else {
1947
2
      ZVAL_DEREF(entry);
1948
2
      Z_TRY_ADDREF_P(entry);
1949
2
      zend_hash_add_new(symbol_table, var_name, entry);
1950
2
    }
1951
4
    count++;
1952
4
  } ZEND_HASH_FOREACH_END();
1953
1954
4
  return count;
1955
5
}
1956
/* }}} */
1957
1958
static zend_long php_extract_ref_prefix_if_exists(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
1959
0
{
1960
0
  zend_long count = 0;
1961
0
  zend_string *var_name;
1962
0
  zval *entry, *orig_var;
1963
1964
0
  if (HT_IS_PACKED(arr)) {
1965
0
    return 0;
1966
0
  }
1967
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1968
0
    if (!var_name) {
1969
0
      continue;
1970
0
    }
1971
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1972
0
    if (orig_var) {
1973
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1974
0
        orig_var = Z_INDIRECT_P(orig_var);
1975
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
1976
0
          if (Z_ISREF_P(entry)) {
1977
0
            Z_ADDREF_P(entry);
1978
0
          } else {
1979
0
            ZVAL_MAKE_REF_EX(entry, 2);
1980
0
          }
1981
0
          ZVAL_REF(orig_var, Z_REF_P(entry));
1982
0
          count++;
1983
0
          continue;
1984
0
        }
1985
0
      }
1986
0
      zend_string *final_name = php_prefix_varname(prefix, var_name);
1987
0
      if (php_valid_var_name(final_name)) {
1988
        /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
1989
0
        ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
1990
1991
0
        if (Z_ISREF_P(entry)) {
1992
0
          Z_ADDREF_P(entry);
1993
0
        } else {
1994
0
          ZVAL_MAKE_REF_EX(entry, 2);
1995
0
        }
1996
0
        if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
1997
0
          if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1998
0
            orig_var = Z_INDIRECT_P(orig_var);
1999
0
          }
2000
0
          zval_ptr_dtor(orig_var);
2001
0
          ZVAL_REF(orig_var, Z_REF_P(entry));
2002
0
        } else {
2003
0
          zend_hash_add_new(symbol_table, final_name, entry);
2004
0
        }
2005
0
        count++;
2006
0
      }
2007
0
      zend_string_release_ex(final_name, false);
2008
0
    }
2009
0
  } ZEND_HASH_FOREACH_END();
2010
2011
0
  return count;
2012
0
}
2013
/* }}} */
2014
2015
static zend_long php_extract_prefix_if_exists(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2016
0
{
2017
0
  zend_long count = 0;
2018
0
  zend_string *var_name;
2019
0
  zval *entry, *orig_var;
2020
2021
0
  if (HT_IS_PACKED(arr)) {
2022
0
    return 0;
2023
0
  }
2024
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2025
0
    if (!var_name) {
2026
0
      continue;
2027
0
    }
2028
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2029
0
    if (orig_var) {
2030
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2031
0
        orig_var = Z_INDIRECT_P(orig_var);
2032
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2033
0
          ZVAL_COPY_DEREF(orig_var, entry);
2034
0
          count++;
2035
0
          continue;
2036
0
        }
2037
0
      }
2038
0
      zend_string *final_name = php_prefix_varname(prefix, var_name);
2039
0
      if (php_valid_var_name(final_name)) {
2040
        /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
2041
0
        ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2042
2043
0
        ZVAL_DEREF(entry);
2044
0
        if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2045
0
          if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2046
0
            orig_var = Z_INDIRECT_P(orig_var);
2047
0
          }
2048
0
          ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2049
0
          if (UNEXPECTED(EG(exception))) {
2050
0
            zend_string_release_ex(final_name, 0);
2051
0
            return -1;
2052
0
          }
2053
0
        } else {
2054
0
          Z_TRY_ADDREF_P(entry);
2055
0
          zend_hash_add_new(symbol_table, final_name, entry);
2056
0
        }
2057
0
        count++;
2058
0
      }
2059
0
      zend_string_release_ex(final_name, false);
2060
0
    }
2061
0
  } ZEND_HASH_FOREACH_END();
2062
2063
0
  return count;
2064
0
}
2065
/* }}} */
2066
2067
static zend_long php_extract_ref_prefix_same(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2068
0
{
2069
0
  zend_long count = 0;
2070
0
  zend_string *var_name;
2071
0
  zval *entry, *orig_var;
2072
2073
0
  if (HT_IS_PACKED(arr)) {
2074
0
    return 0;
2075
0
  }
2076
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2077
0
    if (!var_name) {
2078
0
      continue;
2079
0
    }
2080
0
    if (ZSTR_LEN(var_name) == 0) {
2081
0
      continue;
2082
0
    }
2083
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2084
0
    if (orig_var) {
2085
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2086
0
        orig_var = Z_INDIRECT_P(orig_var);
2087
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2088
0
          if (Z_ISREF_P(entry)) {
2089
0
            Z_ADDREF_P(entry);
2090
0
          } else {
2091
0
            ZVAL_MAKE_REF_EX(entry, 2);
2092
0
          }
2093
0
          ZVAL_REF(orig_var, Z_REF_P(entry));
2094
0
          count++;
2095
0
          continue;
2096
0
        }
2097
0
      }
2098
0
prefix:;
2099
0
      zend_string *final_name = php_prefix_varname(prefix, var_name);
2100
0
      if (php_valid_var_name(final_name)) {
2101
        /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
2102
0
        ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2103
2104
0
        if (Z_ISREF_P(entry)) {
2105
0
          Z_ADDREF_P(entry);
2106
0
        } else {
2107
0
          ZVAL_MAKE_REF_EX(entry, 2);
2108
0
        }
2109
0
        if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2110
0
          if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2111
0
            orig_var = Z_INDIRECT_P(orig_var);
2112
0
          }
2113
0
          zval_ptr_dtor(orig_var);
2114
0
          ZVAL_REF(orig_var, Z_REF_P(entry));
2115
0
        } else {
2116
0
          zend_hash_add_new(symbol_table, final_name, entry);
2117
0
        }
2118
0
        count++;
2119
0
      }
2120
0
      zend_string_release_ex(final_name, false);
2121
0
    } else {
2122
0
      if (!php_valid_var_name(var_name)) {
2123
0
        continue;
2124
0
      }
2125
0
      if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2126
0
        goto prefix;
2127
0
      }
2128
0
      if (Z_ISREF_P(entry)) {
2129
0
        Z_ADDREF_P(entry);
2130
0
      } else {
2131
0
        ZVAL_MAKE_REF_EX(entry, 2);
2132
0
      }
2133
0
      zend_hash_add_new(symbol_table, var_name, entry);
2134
0
      count++;
2135
0
    }
2136
0
  } ZEND_HASH_FOREACH_END();
2137
2138
0
  return count;
2139
0
}
2140
/* }}} */
2141
2142
static zend_long php_extract_prefix_same(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2143
0
{
2144
0
  zend_long count = 0;
2145
0
  zend_string *var_name;
2146
0
  zval *entry, *orig_var;
2147
2148
0
  if (HT_IS_PACKED(arr)) {
2149
0
    return 0;
2150
0
  }
2151
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2152
0
    if (!var_name) {
2153
0
      continue;
2154
0
    }
2155
0
    if (ZSTR_LEN(var_name) == 0) {
2156
0
      continue;
2157
0
    }
2158
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2159
0
    if (orig_var) {
2160
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2161
0
        orig_var = Z_INDIRECT_P(orig_var);
2162
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2163
0
          ZVAL_COPY_DEREF(orig_var, entry);
2164
0
          count++;
2165
0
          continue;
2166
0
        }
2167
0
      }
2168
0
prefix:;
2169
0
      zend_string *final_name = php_prefix_varname(prefix, var_name);
2170
0
      if (php_valid_var_name(final_name)) {
2171
        /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
2172
0
        ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2173
2174
0
        ZVAL_DEREF(entry);
2175
0
        if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2176
0
          if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2177
0
            orig_var = Z_INDIRECT_P(orig_var);
2178
0
          }
2179
0
          ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2180
0
          if (UNEXPECTED(EG(exception))) {
2181
0
            zend_string_release_ex(final_name, false);
2182
0
            return -1;
2183
0
          }
2184
0
        } else {
2185
0
          Z_TRY_ADDREF_P(entry);
2186
0
          zend_hash_add_new(symbol_table, final_name, entry);
2187
0
        }
2188
0
        count++;
2189
0
      }
2190
0
      zend_string_release_ex(final_name, false);
2191
0
    } else {
2192
0
      if (!php_valid_var_name(var_name)) {
2193
0
        continue;
2194
0
      }
2195
0
      if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2196
0
        goto prefix;
2197
0
      }
2198
0
      ZVAL_DEREF(entry);
2199
0
      Z_TRY_ADDREF_P(entry);
2200
0
      zend_hash_add_new(symbol_table, var_name, entry);
2201
0
      count++;
2202
0
    }
2203
0
  } ZEND_HASH_FOREACH_END();
2204
2205
0
  return count;
2206
0
}
2207
/* }}} */
2208
2209
static zend_long php_extract_ref_prefix_all(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2210
0
{
2211
0
  zend_long count = 0;
2212
0
  zend_string *var_name;
2213
0
  zend_ulong num_key;
2214
0
  zval *entry, *orig_var;
2215
2216
0
  ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2217
0
    zend_string *final_name;
2218
0
    if (var_name) {
2219
0
      if (ZSTR_LEN(var_name) == 0) {
2220
0
        continue;
2221
0
      }
2222
0
      final_name = php_prefix_varname(prefix, var_name);
2223
0
    } else {
2224
0
      zend_string *str = zend_long_to_str(num_key);
2225
0
      final_name = php_prefix_varname(prefix, str);
2226
0
      zend_string_release_ex(str, false);
2227
0
    }
2228
0
    if (php_valid_var_name(final_name)) {
2229
      /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
2230
0
      ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2231
2232
0
      if (Z_ISREF_P(entry)) {
2233
0
        Z_ADDREF_P(entry);
2234
0
      } else {
2235
0
        ZVAL_MAKE_REF_EX(entry, 2);
2236
0
      }
2237
0
      if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2238
0
        if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2239
0
          orig_var = Z_INDIRECT_P(orig_var);
2240
0
        }
2241
0
        zval_ptr_dtor(orig_var);
2242
0
        ZVAL_REF(orig_var, Z_REF_P(entry));
2243
0
      } else {
2244
0
        zend_hash_add_new(symbol_table, final_name, entry);
2245
0
      }
2246
0
      count++;
2247
0
    }
2248
0
    zend_string_release_ex(final_name, false);
2249
0
  } ZEND_HASH_FOREACH_END();
2250
2251
0
  return count;
2252
0
}
2253
/* }}} */
2254
2255
static zend_long php_extract_prefix_all(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2256
0
{
2257
0
  zend_long count = 0;
2258
0
  zend_string *var_name;
2259
0
  zend_ulong num_key;
2260
0
  zval *entry, *orig_var;
2261
2262
0
  ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2263
0
    zend_string *final_name;
2264
0
    if (var_name) {
2265
0
      if (ZSTR_LEN(var_name) == 0) {
2266
0
        continue;
2267
0
      }
2268
0
      final_name = php_prefix_varname(prefix, var_name);
2269
0
    } else {
2270
0
      zend_string *str = zend_long_to_str(num_key);
2271
0
      final_name = php_prefix_varname(prefix, str);
2272
0
      zend_string_release_ex(str, false);
2273
0
    }
2274
0
    if (php_valid_var_name(final_name)) {
2275
      /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */
2276
0
      ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2277
2278
0
      ZVAL_DEREF(entry);
2279
0
      if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2280
0
        if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2281
0
          orig_var = Z_INDIRECT_P(orig_var);
2282
0
        }
2283
0
        ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2284
0
        if (UNEXPECTED(EG(exception))) {
2285
0
          zend_string_release_ex(final_name, false);
2286
0
          return -1;
2287
0
        }
2288
0
      } else {
2289
0
        Z_TRY_ADDREF_P(entry);
2290
0
        zend_hash_add_new(symbol_table, final_name, entry);
2291
0
      }
2292
0
      count++;
2293
0
    }
2294
0
    zend_string_release_ex(final_name, false);
2295
0
  } ZEND_HASH_FOREACH_END();
2296
2297
0
  return count;
2298
0
}
2299
/* }}} */
2300
2301
static zend_long php_extract_ref_prefix_invalid(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2302
0
{
2303
0
  zend_long count = 0;
2304
0
  zend_string *var_name;
2305
0
  zend_ulong num_key;
2306
0
  zval *entry, *orig_var;
2307
2308
0
  ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2309
0
    zend_string *final_name;
2310
0
    if (var_name) {
2311
0
      if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2312
0
        final_name = php_prefix_varname(prefix, var_name);
2313
2314
0
        if (!php_valid_var_name(final_name)) {
2315
0
          zend_string_release_ex(final_name, false);
2316
0
          continue;
2317
0
        }
2318
0
      } else {
2319
0
        final_name = zend_string_copy(var_name);
2320
0
      }
2321
0
    } else {
2322
0
      zend_string *str = zend_long_to_str(num_key);
2323
0
      final_name = php_prefix_varname(prefix, str);
2324
0
      zend_string_release_ex(str, false);
2325
0
      if (!php_valid_var_name(final_name)) {
2326
0
        zend_string_release_ex(final_name, false);
2327
0
        continue;
2328
0
      }
2329
0
    }
2330
2331
    /* We previously checked if the var name is "this" to prefix it */
2332
0
    ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2333
2334
0
    if (Z_ISREF_P(entry)) {
2335
0
      Z_ADDREF_P(entry);
2336
0
    } else {
2337
0
      ZVAL_MAKE_REF_EX(entry, 2);
2338
0
    }
2339
0
    if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2340
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2341
0
        orig_var = Z_INDIRECT_P(orig_var);
2342
0
      }
2343
0
      zval_ptr_dtor(orig_var);
2344
0
      ZVAL_REF(orig_var, Z_REF_P(entry));
2345
0
    } else {
2346
0
      zend_hash_add_new(symbol_table, final_name, entry);
2347
0
    }
2348
0
    count++;
2349
2350
0
    zend_string_release_ex(final_name, false);
2351
0
  } ZEND_HASH_FOREACH_END();
2352
2353
0
  return count;
2354
0
}
2355
/* }}} */
2356
2357
static zend_long php_extract_prefix_invalid(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */
2358
0
{
2359
0
  zend_long count = 0;
2360
0
  zend_string *var_name;
2361
0
  zend_ulong num_key;
2362
0
  zval *entry, *orig_var;
2363
2364
0
  ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) {
2365
0
    zend_string *final_name;
2366
0
    if (var_name) {
2367
0
      if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2368
0
        final_name = php_prefix_varname(prefix, var_name);
2369
0
        if (!php_valid_var_name(final_name)) {
2370
0
          zend_string_release_ex(final_name, false);
2371
0
          continue;
2372
0
        }
2373
0
      } else {
2374
0
        final_name = zend_string_copy(var_name);
2375
0
      }
2376
0
    } else {
2377
0
      zend_string *str = zend_long_to_str(num_key);
2378
0
      final_name = php_prefix_varname(prefix, str);
2379
0
      zend_string_release_ex(str, 0);
2380
0
      if (!php_valid_var_name(final_name)) {
2381
0
        zend_string_release_ex(final_name, false);
2382
0
        continue;
2383
0
      }
2384
0
    }
2385
2386
    /* We previously checked if the var name is "this" to prefix it */
2387
0
    ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS)));
2388
2389
0
    ZVAL_DEREF(entry);
2390
0
    if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) {
2391
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2392
0
        orig_var = Z_INDIRECT_P(orig_var);
2393
0
      }
2394
0
      ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
2395
0
      if (UNEXPECTED(EG(exception))) {
2396
0
        zend_string_release_ex(final_name, false);
2397
0
        return -1;
2398
0
      }
2399
0
    } else {
2400
0
      Z_TRY_ADDREF_P(entry);
2401
0
      zend_hash_add_new(symbol_table, final_name, entry);
2402
0
    }
2403
0
    count++;
2404
2405
0
    zend_string_release_ex(final_name, false);
2406
0
  } ZEND_HASH_FOREACH_END();
2407
2408
0
  return count;
2409
0
}
2410
/* }}} */
2411
2412
static zend_long php_extract_ref_skip(const zend_array *arr, zend_array *symbol_table) /* {{{ */
2413
0
{
2414
0
  zend_long count = 0;
2415
0
  zend_string *var_name;
2416
0
  zval *entry, *orig_var;
2417
2418
0
  if (HT_IS_PACKED(arr)) {
2419
0
    return 0;
2420
0
  }
2421
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2422
0
    if (!var_name) {
2423
0
      continue;
2424
0
    }
2425
0
    if (!php_valid_var_name(var_name)) {
2426
0
      continue;
2427
0
    }
2428
0
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2429
0
      continue;
2430
0
    }
2431
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2432
0
    if (orig_var) {
2433
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2434
0
        orig_var = Z_INDIRECT_P(orig_var);
2435
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2436
0
          if (Z_ISREF_P(entry)) {
2437
0
            Z_ADDREF_P(entry);
2438
0
          } else {
2439
0
            ZVAL_MAKE_REF_EX(entry, 2);
2440
0
          }
2441
0
          ZVAL_REF(orig_var, Z_REF_P(entry));
2442
0
          count++;
2443
0
        }
2444
0
      }
2445
0
    } else {
2446
0
      if (Z_ISREF_P(entry)) {
2447
0
        Z_ADDREF_P(entry);
2448
0
      } else {
2449
0
        ZVAL_MAKE_REF_EX(entry, 2);
2450
0
      }
2451
0
      zend_hash_add_new(symbol_table, var_name, entry);
2452
0
      count++;
2453
0
    }
2454
0
  } ZEND_HASH_FOREACH_END();
2455
2456
0
  return count;
2457
0
}
2458
/* }}} */
2459
2460
static zend_long php_extract_skip(const zend_array *arr, zend_array *symbol_table) /* {{{ */
2461
0
{
2462
0
  zend_long count = 0;
2463
0
  zend_string *var_name;
2464
0
  zval *entry, *orig_var;
2465
2466
0
  if (HT_IS_PACKED(arr)) {
2467
0
    return 0;
2468
0
  }
2469
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2470
0
    if (!var_name) {
2471
0
      continue;
2472
0
    }
2473
0
    if (!php_valid_var_name(var_name)) {
2474
0
      continue;
2475
0
    }
2476
0
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2477
0
      continue;
2478
0
    }
2479
0
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2480
0
    if (orig_var) {
2481
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2482
0
        orig_var = Z_INDIRECT_P(orig_var);
2483
0
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2484
0
          ZVAL_COPY_DEREF(orig_var, entry);
2485
0
          count++;
2486
0
        }
2487
0
      }
2488
0
    } else {
2489
0
      ZVAL_DEREF(entry);
2490
0
      Z_TRY_ADDREF_P(entry);
2491
0
      zend_hash_add_new(symbol_table, var_name, entry);
2492
0
      count++;
2493
0
    }
2494
0
  } ZEND_HASH_FOREACH_END();
2495
2496
0
  return count;
2497
0
}
2498
/* }}} */
2499
2500
/* {{{ Imports variables into symbol table from an array */
2501
PHP_FUNCTION(extract)
2502
5
{
2503
5
  zval *var_array_param;
2504
5
  zend_long extract_refs;
2505
5
  zend_long extract_type = PHP_EXTR_OVERWRITE;
2506
5
  zend_string *prefix = NULL;
2507
5
  zend_long count;
2508
5
  zend_array *symbol_table;
2509
2510
15
  ZEND_PARSE_PARAMETERS_START(1, 3)
2511
20
    Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2512
5
    Z_PARAM_OPTIONAL
2513
10
    Z_PARAM_LONG(extract_type)
2514
0
    Z_PARAM_STR(prefix)
2515
5
  ZEND_PARSE_PARAMETERS_END();
2516
2517
5
  extract_refs = (extract_type & PHP_EXTR_REFS);
2518
5
  if (extract_refs) {
2519
0
    SEPARATE_ARRAY(var_array_param);
2520
0
  }
2521
5
  extract_type &= 0xff;
2522
2523
5
  if (extract_type < PHP_EXTR_OVERWRITE || extract_type > PHP_EXTR_IF_EXISTS) {
2524
0
    zend_argument_value_error(2, "must be a valid extract type");
2525
0
    RETURN_THROWS();
2526
0
  }
2527
2528
5
  if (extract_type > PHP_EXTR_SKIP && extract_type <= PHP_EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
2529
0
    zend_argument_value_error(3, "is required when using this extract type");
2530
0
    RETURN_THROWS();
2531
0
  }
2532
2533
5
  if (prefix) {
2534
0
    if (ZSTR_LEN(prefix) && !php_valid_var_name(prefix)) {
2535
0
      zend_argument_value_error(3, "must be a valid identifier");
2536
0
      RETURN_THROWS();
2537
0
    }
2538
0
  }
2539
2540
5
  if (zend_forbid_dynamic_call() == FAILURE) {
2541
0
    return;
2542
0
  }
2543
2544
5
  symbol_table = zend_rebuild_symbol_table();
2545
5
  ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2546
2547
5
  if (extract_refs) {
2548
0
    switch (extract_type) {
2549
0
      case PHP_EXTR_IF_EXISTS:
2550
0
        count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
2551
0
        break;
2552
0
      case PHP_EXTR_OVERWRITE:
2553
0
        count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
2554
0
        break;
2555
0
      case PHP_EXTR_PREFIX_IF_EXISTS:
2556
0
        count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2557
0
        break;
2558
0
      case PHP_EXTR_PREFIX_SAME:
2559
0
        count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2560
0
        break;
2561
0
      case PHP_EXTR_PREFIX_ALL:
2562
0
        count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2563
0
        break;
2564
0
      case PHP_EXTR_PREFIX_INVALID:
2565
0
        count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
2566
0
        break;
2567
0
      default:
2568
0
        count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
2569
0
        break;
2570
0
    }
2571
5
  } else {
2572
    /* The array might be stored in a local variable that will be overwritten */
2573
5
    zval array_copy;
2574
5
    ZVAL_COPY(&array_copy, var_array_param);
2575
5
    switch (extract_type) {
2576
0
      case PHP_EXTR_IF_EXISTS:
2577
0
        count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
2578
0
        break;
2579
5
      case PHP_EXTR_OVERWRITE:
2580
5
        count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2581
5
        break;
2582
0
      case PHP_EXTR_PREFIX_IF_EXISTS:
2583
0
        count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
2584
0
        break;
2585
0
      case PHP_EXTR_PREFIX_SAME:
2586
0
        count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
2587
0
        break;
2588
0
      case PHP_EXTR_PREFIX_ALL:
2589
0
        count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
2590
0
        break;
2591
0
      case PHP_EXTR_PREFIX_INVALID:
2592
0
        count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
2593
0
        break;
2594
0
      default:
2595
0
        count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2596
0
        break;
2597
5
    }
2598
5
    zval_ptr_dtor(&array_copy);
2599
5
  }
2600
2601
5
  RETURN_LONG(count);
2602
5
}
2603
/* }}} */
2604
2605
static zend_result php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2606
12
{
2607
12
  zval *value_ptr, data;
2608
2609
12
  ZVAL_DEREF(entry);
2610
12
  if (Z_TYPE_P(entry) == IS_STRING) {
2611
9
    if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
2612
0
      ZVAL_DEREF(value_ptr);
2613
0
      Z_TRY_ADDREF_P(value_ptr);
2614
0
      zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
2615
9
    } else if (zend_string_equals(Z_STR_P(entry), ZSTR_KNOWN(ZEND_STR_THIS))) {
2616
0
      zend_object *object = zend_get_this_object(EG(current_execute_data));
2617
0
      if (object) {
2618
0
        ZVAL_OBJ_COPY(&data, object);
2619
0
        zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
2620
0
      }
2621
9
    } else {
2622
9
      php_error_docref(NULL, E_WARNING, "Undefined variable $%pS", Z_STR_P(entry));
2623
      /* A user error handler may have thrown. */
2624
9
      return EG(exception) ? FAILURE : SUCCESS;
2625
9
    }
2626
9
  } else if (Z_TYPE_P(entry) == IS_ARRAY) {
2627
0
    zend_result result = SUCCESS;
2628
2629
0
#ifdef ZEND_CHECK_STACK_LIMIT
2630
0
    if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
2631
0
      zend_call_stack_size_error();
2632
0
      return FAILURE;
2633
0
    }
2634
0
#endif
2635
0
    if (Z_REFCOUNTED_P(entry)) {
2636
0
      if (Z_IS_RECURSIVE_P(entry)) {
2637
0
        zend_throw_error(NULL, "Recursion detected");
2638
0
        return FAILURE;
2639
0
      }
2640
0
      Z_PROTECT_RECURSION_P(entry);
2641
0
    }
2642
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
2643
0
      if (UNEXPECTED(php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos) == FAILURE)) {
2644
0
        result = FAILURE;
2645
0
        break;
2646
0
      }
2647
0
    } ZEND_HASH_FOREACH_END();
2648
0
    if (Z_REFCOUNTED_P(entry)) {
2649
0
      Z_UNPROTECT_RECURSION_P(entry);
2650
0
    }
2651
2652
0
    return result;
2653
3
  } else {
2654
3
    php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2655
    /* A user error handler may have thrown. */
2656
3
    return EG(exception) ? FAILURE : SUCCESS;
2657
3
  }
2658
2659
0
  return SUCCESS;
2660
12
}
2661
/* }}} */
2662
2663
/* {{{ Creates a hash containing variables and their values */
2664
PHP_FUNCTION(compact)
2665
9
{
2666
9
  zval *args = NULL; /* function arguments array */
2667
9
  uint32_t num_args, i;
2668
9
  zend_array *symbol_table;
2669
2670
27
  ZEND_PARSE_PARAMETERS_START(1, -1)
2671
27
    Z_PARAM_VARIADIC('+', args, num_args)
2672
27
  ZEND_PARSE_PARAMETERS_END();
2673
2674
9
  if (zend_forbid_dynamic_call() == FAILURE) {
2675
0
    return;
2676
0
  }
2677
2678
9
  symbol_table = zend_rebuild_symbol_table();
2679
9
  ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2680
2681
  /* compact() is probably most used with a single array of var_names
2682
     or multiple string names, rather than a combination of both.
2683
     So quickly guess a minimum result size based on that */
2684
9
  if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
2685
0
    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
2686
9
  } else {
2687
9
    array_init_size(return_value, num_args);
2688
9
  }
2689
2690
21
  for (i = 0; i < num_args; i++) {
2691
12
    if (UNEXPECTED(php_compact_var(symbol_table, return_value, &args[i], i + 1) == FAILURE)) {
2692
0
      RETURN_THROWS();
2693
0
    }
2694
12
  }
2695
9
}
2696
/* }}} */
2697
2698
/* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2699
PHP_FUNCTION(array_fill)
2700
3
{
2701
3
  zval *val;
2702
3
  zend_long start_key, num;
2703
2704
9
  ZEND_PARSE_PARAMETERS_START(3, 3)
2705
12
    Z_PARAM_LONG(start_key)
2706
15
    Z_PARAM_LONG(num)
2707
10
    Z_PARAM_ZVAL(val)
2708
10
  ZEND_PARSE_PARAMETERS_END();
2709
2710
2
  if (EXPECTED(num > 0)) {
2711
2
    if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2712
0
      zend_argument_value_error(2, "is too large");
2713
0
      RETURN_THROWS();
2714
2
    } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
2715
0
      zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
2716
0
      RETURN_THROWS();
2717
2
    } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2718
      /* create packed array */
2719
2
      zval *zv;
2720
2721
2
      if (UNEXPECTED(!array_finit_size_packed(return_value, (uint32_t)(start_key + num)))) {
2722
0
        RETURN_OOM();
2723
0
      }
2724
2
      Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2725
2
      Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2726
2
      Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2727
2728
2
      if (Z_REFCOUNTED_P(val)) {
2729
0
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2730
0
      }
2731
2732
2
      zv = Z_ARRVAL_P(return_value)->arPacked;
2733
2734
6
      while (start_key--) {
2735
4
        ZVAL_UNDEF(zv);
2736
4
        zv++;
2737
4
      }
2738
196k
      while (num--) {
2739
196k
        ZVAL_COPY_VALUE(zv, val);
2740
196k
        zv++;
2741
196k
      }
2742
2
    } else {
2743
      /* create hash */
2744
0
      if (UNEXPECTED(!array_finit_size_mixed(return_value, (uint32_t)num))) {
2745
0
        RETURN_OOM();
2746
0
      }
2747
0
      if (Z_REFCOUNTED_P(val)) {
2748
0
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2749
0
      }
2750
0
      zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2751
0
      while (--num) {
2752
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2753
0
        start_key++;
2754
0
      }
2755
0
    }
2756
2
  } else if (EXPECTED(num == 0)) {
2757
0
    RETURN_EMPTY_ARRAY();
2758
0
  } else {
2759
0
    zend_argument_value_error(2, "must be greater than or equal to 0");
2760
0
    RETURN_THROWS();
2761
0
  }
2762
2
}
2763
/* }}} */
2764
2765
/* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2766
PHP_FUNCTION(array_fill_keys)
2767
2
{
2768
2
  zval *keys, *val, *entry;
2769
2770
6
  ZEND_PARSE_PARAMETERS_START(2, 2)
2771
8
    Z_PARAM_ARRAY(keys)
2772
10
    Z_PARAM_ZVAL(val)
2773
10
  ZEND_PARSE_PARAMETERS_END();
2774
2775
  /* Initialize return array */
2776
2
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2777
2778
22
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2779
22
    ZVAL_DEREF(entry);
2780
22
    if (Z_TYPE_P(entry) == IS_LONG) {
2781
4
      zend_hash_index_add(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2782
6
    } else {
2783
6
      zend_string *tmp_key;
2784
6
      zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2785
6
      zend_symtable_add(Z_ARRVAL_P(return_value), key, val);
2786
6
      zend_tmp_string_release(tmp_key);
2787
6
    }
2788
22
  } ZEND_HASH_FOREACH_END();
2789
2790
2
  if (Z_REFCOUNTED_P(val)) {
2791
0
    GC_ADDREF_EX(Z_COUNTED_P(val), zend_hash_num_elements(Z_ARRVAL_P(return_value)));
2792
0
  }
2793
2
}
2794
/* }}} */
2795
2796
44
#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2797
44
    double __calc_size = ((start - end) / (_step)) + 1; \
2798
44
    if (__calc_size >= (double)HT_MAX_SIZE) { \
2799
2
      double __exceed_by = __calc_size - (double)HT_MAX_SIZE; \
2800
2
      zend_value_error(\
2801
2
        "The supplied range exceeds the maximum array size by %.1f elements: " \
2802
2
        "start=%.1f, end=%.1f, step=%.1f. Max size: %.0f", \
2803
2
        __exceed_by, end, start, (_step), (double)HT_MAX_SIZE); \
2804
2
      RETURN_THROWS(); \
2805
2
    } \
2806
44
    size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2807
42
    array_init_size(return_value, size); \
2808
42
    if (UNEXPECTED(zend_hash_real_finit_packed(Z_ARRVAL_P(return_value), /* fallible */ true) == FAILURE)) { \
2809
0
      RETURN_OOM(); \
2810
0
    } \
2811
42
  } while (0)
2812
2813
779
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2814
779
    zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2815
779
    if (__calc_size >= HT_MAX_SIZE - 1) { \
2816
0
      uint64_t __excess = __calc_size - (HT_MAX_SIZE - 1); \
2817
0
      zend_value_error(\
2818
0
        "The supplied range exceeds the maximum array size by %" PRIu64 " elements: " \
2819
0
        "start=" ZEND_LONG_FMT ", end=" ZEND_LONG_FMT ", step=" ZEND_LONG_FMT ". " \
2820
0
        "Calculated size: %" PRIu64 ". Maximum size: %" PRIu64 ".", \
2821
0
        __excess, end, start, (_step), (uint64_t)__calc_size, (uint64_t)HT_MAX_SIZE); \
2822
0
      RETURN_THROWS(); \
2823
0
    } \
2824
779
    size = (uint32_t)(__calc_size + 1); \
2825
779
    array_init_size(return_value, size); \
2826
779
    if (UNEXPECTED(zend_hash_real_finit_packed(Z_ARRVAL_P(return_value), /* fallible */ true) == FAILURE)) { \
2827
761
      RETURN_OOM(); \
2828
761
    } \
2829
779
  } while (0)
2830
2831
/* Process input for the range() function
2832
 * 0 on exceptions
2833
 * IS_LONG if only interpretable as int
2834
 * IS_DOUBLE if only interpretable as float
2835
 * IS_STRING if only interpretable as string
2836
 * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
2837
static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2838
1.66k
{
2839
1.66k
  switch (Z_TYPE_P(input)) {
2840
1.60k
    case IS_LONG:
2841
1.60k
      *lval = Z_LVAL_P(input);
2842
1.60k
      *dval = (double) Z_LVAL_P(input);
2843
1.60k
      return IS_LONG;
2844
58
    case IS_DOUBLE:
2845
58
      *dval = Z_DVAL_P(input);
2846
58
      check_dval_value:
2847
58
      if (zend_isinf(*dval)) {
2848
1
        zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2849
1
        return 0;
2850
1
      }
2851
57
      if (zend_isnan(*dval)) {
2852
0
        zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2853
0
        return 0;
2854
0
      }
2855
57
      return IS_DOUBLE;
2856
1
    case IS_STRING: {
2857
      /* Process strings:
2858
       * - Empty strings are converted to 0 with a diagnostic
2859
       * - Check if string is numeric and store the values in passed pointer
2860
       * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2861
       * - If numeric int, check it is one byte or not
2862
       *   - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2863
       *   - If not should only be interpreted as int, return IS_LONG;
2864
       * - Otherwise is a string and return IS_STRING */
2865
1
      if (Z_STRLEN_P(input) == 0) {
2866
0
        const char *arg_name = get_active_function_arg_name(arg_num);
2867
0
        php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2868
0
        if (UNEXPECTED(EG(exception))) {
2869
0
          return 0;
2870
0
        }
2871
0
        *lval = 0;
2872
0
        *dval = 0.0;
2873
0
        return IS_LONG;
2874
0
      }
2875
1
      uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2876
1
      if (type == IS_DOUBLE) {
2877
0
        goto check_dval_value;
2878
0
      }
2879
1
      if (type == IS_LONG) {
2880
0
        *dval = (double) *lval;
2881
0
        if (Z_STRLEN_P(input) == 1) {
2882
0
          return IS_ARRAY;
2883
0
        } else {
2884
0
          return IS_LONG;
2885
0
        }
2886
0
      }
2887
1
      if (Z_STRLEN_P(input) != 1) {
2888
1
        const char *arg_name = get_active_function_arg_name(arg_num);
2889
1
        php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2890
1
        if (UNEXPECTED(EG(exception))) {
2891
0
          return 0;
2892
0
        }
2893
1
      }
2894
      /* Set fall back values to 0 in case the other argument is not a string */
2895
1
      *lval = 0;
2896
1
      *dval = 0.0;
2897
1
      return IS_STRING;
2898
1
    }
2899
0
    default: ZEND_UNREACHABLE();
2900
1.66k
  }
2901
1.66k
}
2902
2903
/* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2904
PHP_FUNCTION(range)
2905
842
{
2906
842
  zval *user_start, *user_end, *user_step = NULL, tmp;
2907
842
  bool is_step_double = false;
2908
842
  bool is_step_negative = false;
2909
842
  double step_double = 1.0;
2910
842
  zend_long step = 1;
2911
2912
2.51k
  ZEND_PARSE_PARAMETERS_START(2, 3)
2913
3.32k
    Z_PARAM_NUMBER_OR_STR(user_start)
2914
4.16k
    Z_PARAM_NUMBER_OR_STR(user_end)
2915
832
    Z_PARAM_OPTIONAL
2916
1.68k
    Z_PARAM_NUMBER(user_step)
2917
842
  ZEND_PARSE_PARAMETERS_END();
2918
2919
832
  if (user_step) {
2920
11
    if (UNEXPECTED(Z_TYPE_P(user_step) == IS_DOUBLE)) {
2921
10
      step_double = Z_DVAL_P(user_step);
2922
2923
10
      if (zend_isinf(step_double)) {
2924
0
        zend_argument_value_error(3, "must be a finite number, INF provided");
2925
0
        RETURN_THROWS();
2926
0
      }
2927
10
      if (zend_isnan(step_double)) {
2928
0
        zend_argument_value_error(3, "must be a finite number, NAN provided");
2929
0
        RETURN_THROWS();
2930
0
      }
2931
2932
      /* We only want positive step values. */
2933
10
      if (step_double < 0.0) {
2934
0
        is_step_negative = true;
2935
0
        step_double *= -1;
2936
0
      }
2937
10
      step = zend_dval_to_lval_silent(step_double);
2938
10
      if (!zend_is_long_compatible(step_double, step)) {
2939
10
        is_step_double = true;
2940
10
      }
2941
10
    } else {
2942
1
      step = Z_LVAL_P(user_step);
2943
      /* We only want positive step values. */
2944
1
      if (step < 0) {
2945
0
        if (UNEXPECTED(step == ZEND_LONG_MIN)) {
2946
0
          zend_argument_value_error(3, "must be greater than " ZEND_LONG_FMT, step);
2947
0
          RETURN_THROWS();
2948
0
        }
2949
0
        is_step_negative = true;
2950
0
        step *= -1;
2951
0
      }
2952
1
      step_double = (double) step;
2953
1
    }
2954
11
    if (step_double == 0.0) {
2955
0
      zend_argument_value_error(3, "cannot be 0");
2956
0
      RETURN_THROWS();
2957
0
    }
2958
11
  }
2959
2960
832
  uint8_t start_type;
2961
832
  double start_double;
2962
832
  zend_long start_long;
2963
832
  uint8_t end_type;
2964
832
  double end_double;
2965
832
  zend_long end_long;
2966
2967
832
  start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2968
832
  if (start_type == 0) {
2969
1
    RETURN_THROWS();
2970
1
  }
2971
831
  end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2972
831
  if (end_type == 0) {
2973
0
    RETURN_THROWS();
2974
0
  }
2975
2976
  /* If the range is given as strings, generate an array of characters. */
2977
831
  if (start_type >= IS_STRING || end_type >= IS_STRING) {
2978
    /* If one of the inputs is NOT a string nor single-byte string */
2979
1
    if (UNEXPECTED(start_type < IS_STRING || end_type < IS_STRING)) {
2980
1
      if (start_type < IS_STRING) {
2981
0
        if (end_type != IS_ARRAY) {
2982
0
          php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if"
2983
0
            " argument #2 ($end) is a single byte string, argument #2 ($end) converted to 0");
2984
0
        }
2985
0
        end_type = IS_LONG;
2986
1
      } else if (end_type < IS_STRING) {
2987
1
        if (start_type != IS_ARRAY) {
2988
1
          php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a single byte string if"
2989
1
            " argument #1 ($start) is a single byte string, argument #1 ($start) converted to 0");
2990
1
        }
2991
1
        start_type = IS_LONG;
2992
1
      }
2993
1
      if (UNEXPECTED(EG(exception))) {
2994
0
        RETURN_THROWS();
2995
0
      }
2996
1
      goto handle_numeric_inputs;
2997
1
    }
2998
2999
0
    if (is_step_double) {
3000
      /* Only emit warning if one of the input is not a numeric digit */
3001
0
      if (start_type == IS_STRING || end_type == IS_STRING) {
3002
0
        php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
3003
0
          " of characters, inputs converted to 0");
3004
0
        if (UNEXPECTED(EG(exception))) {
3005
0
          RETURN_THROWS();
3006
0
        }
3007
0
      }
3008
0
      end_type = IS_LONG;
3009
0
      start_type = IS_LONG;
3010
0
      goto handle_numeric_inputs;
3011
0
    }
3012
3013
    /* Generate array of characters, as zero-extended ints to make bounds checking possible in the loop condition */
3014
0
    int low = (unsigned char) Z_STRVAL_P(user_start)[0];
3015
0
    int high = (unsigned char) Z_STRVAL_P(user_end)[0];
3016
3017
    /* Decreasing char range */
3018
0
    if (low > high) {
3019
0
      if (low - high < step) {
3020
0
        goto boundary_error;
3021
0
      }
3022
      /* Initialize the return_value as an array. */
3023
0
      array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
3024
0
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3025
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3026
0
        for (; low >= high; low -= step) {
3027
0
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3028
0
          ZEND_HASH_FILL_NEXT();
3029
0
        }
3030
0
      } ZEND_HASH_FILL_END();
3031
0
    } else if (high > low) { /* Increasing char range */
3032
0
      if (is_step_negative) {
3033
0
        goto negative_step_error;
3034
0
      }
3035
0
      if (high - low < step) {
3036
0
        goto boundary_error;
3037
0
      }
3038
0
      array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
3039
0
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3040
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3041
0
        for (; low <= high; low += step) {
3042
0
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3043
0
          ZEND_HASH_FILL_NEXT();
3044
0
        }
3045
0
      } ZEND_HASH_FILL_END();
3046
0
    } else {
3047
0
      array_init(return_value);
3048
0
      ZVAL_CHAR(&tmp, low);
3049
0
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3050
0
    }
3051
0
    return;
3052
0
  }
3053
3054
831
  handle_numeric_inputs:
3055
831
  if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
3056
52
    double element;
3057
52
    uint32_t i, size;
3058
3059
    /* Decreasing float range */
3060
52
    if (start_double > end_double) {
3061
26
      if (start_double - end_double < step_double) {
3062
4
        goto boundary_error;
3063
4
      }
3064
3065
22
      RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
3066
3067
20
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3068
1.53M
        for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
3069
1.53M
          ZEND_HASH_FILL_SET_DOUBLE(element);
3070
1.53M
          ZEND_HASH_FILL_NEXT();
3071
1.53M
        }
3072
20
      } ZEND_HASH_FILL_END();
3073
26
    } else if (end_double > start_double) { /* Increasing float range */
3074
24
      if (is_step_negative) {
3075
0
        goto negative_step_error;
3076
0
      }
3077
24
      if (end_double - start_double < step_double) {
3078
2
        goto boundary_error;
3079
2
      }
3080
3081
22
      RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
3082
3083
22
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3084
4.91k
        for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
3085
4.88k
          ZEND_HASH_FILL_SET_DOUBLE(element);
3086
4.88k
          ZEND_HASH_FILL_NEXT();
3087
4.88k
        }
3088
22
      } ZEND_HASH_FILL_END();
3089
22
    } else {
3090
2
      array_init(return_value);
3091
2
      ZVAL_DOUBLE(&tmp, start_double);
3092
2
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3093
2
    }
3094
779
  } else {
3095
779
    ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
3096
    /* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
3097
779
    zend_ulong unsigned_step= (zend_ulong)step;
3098
779
    uint32_t i, size;
3099
3100
    /* Decreasing int range */
3101
779
    if (start_long > end_long) {
3102
765
      if ((zend_ulong)start_long - end_long < unsigned_step) {
3103
0
        goto boundary_error;
3104
0
      }
3105
3106
765
      RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3107
3108
6
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3109
284
        for (i = 0; i < size; ++i) {
3110
278
          ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3111
278
          ZEND_HASH_FILL_NEXT();
3112
278
        }
3113
6
      } ZEND_HASH_FILL_END();
3114
14
    } else if (end_long > start_long) { /* Increasing int range */
3115
14
      if (is_step_negative) {
3116
0
        goto negative_step_error;
3117
0
      }
3118
14
      if ((zend_ulong)end_long - start_long < unsigned_step) {
3119
0
        goto boundary_error;
3120
0
      }
3121
3122
14
      RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3123
3124
12
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3125
68.3k
        for (i = 0; i < size; ++i) {
3126
68.3k
          ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3127
68.3k
          ZEND_HASH_FILL_NEXT();
3128
68.3k
        }
3129
12
      } ZEND_HASH_FILL_END();
3130
12
    } else {
3131
0
      array_init(return_value);
3132
0
      ZVAL_LONG(&tmp, start_long);
3133
0
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3134
0
    }
3135
779
  }
3136
62
  return;
3137
3138
62
negative_step_error:
3139
0
  zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3140
0
  RETURN_THROWS();
3141
3142
6
boundary_error:
3143
6
  zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3144
6
  RETURN_THROWS();
3145
6
}
3146
/* }}} */
3147
3148
#undef RANGE_CHECK_DOUBLE_INIT_ARRAY
3149
#undef RANGE_CHECK_LONG_INIT_ARRAY
3150
3151
/* {{{ php_array_data_shuffle */
3152
PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *array) /* {{{ */
3153
0
{
3154
0
  const php_random_algo *algo = engine.algo;
3155
0
  void *state = engine.state;
3156
3157
0
  int64_t idx, j, n_elems, rnd_idx, n_left;
3158
0
  zval *zv, temp;
3159
0
  HashTable *hash;
3160
3161
0
  n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3162
3163
0
  if (n_elems < 1) {
3164
0
    return true;
3165
0
  }
3166
3167
0
  hash = Z_ARRVAL_P(array);
3168
0
  n_left = n_elems;
3169
3170
0
  if (!HT_IS_PACKED(hash)) {
3171
0
    if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3172
0
      Bucket *p = hash->arData;
3173
0
      zend_long i = hash->nNumUsed;
3174
3175
0
      for (; i > 0; p++, i--) {
3176
0
        if (p->key) {
3177
0
          zend_string_release(p->key);
3178
0
          p->key = NULL;
3179
0
        }
3180
0
      }
3181
0
    }
3182
0
    zend_hash_to_packed(hash);
3183
0
  }
3184
3185
0
  if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3186
0
    if (hash->nNumUsed != hash->nNumOfElements) {
3187
0
      for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3188
0
        zv = hash->arPacked + idx;
3189
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3190
0
        if (j != idx) {
3191
0
          ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3192
0
        }
3193
0
        j++;
3194
0
      }
3195
0
    }
3196
0
    while (--n_left) {
3197
0
      rnd_idx = algo->range(state, 0, n_left);
3198
0
      if (EG(exception)) {
3199
0
        return false;
3200
0
      }
3201
0
      if (rnd_idx != n_left) {
3202
0
        ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3203
0
        ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3204
0
        ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3205
0
      }
3206
0
    }
3207
0
  } else {
3208
0
    zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3209
3210
0
    if (hash->nNumUsed != hash->nNumOfElements) {
3211
0
      for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3212
0
        zv = hash->arPacked + idx;
3213
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3214
0
        if (j != idx) {
3215
0
          ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3216
0
          if (idx == iter_pos) {
3217
0
            zend_hash_iterators_update(hash, idx, j);
3218
0
            iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3219
0
          }
3220
0
        }
3221
0
        j++;
3222
0
      }
3223
0
    }
3224
0
    while (--n_left) {
3225
0
      rnd_idx = algo->range(state, 0, n_left);
3226
0
      if (EG(exception)) {
3227
0
        return false;
3228
0
      }
3229
0
      if (rnd_idx != n_left) {
3230
0
        ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3231
0
        ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3232
0
        ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3233
0
        zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
3234
0
      }
3235
0
    }
3236
0
  }
3237
0
  hash->nNumUsed = n_elems;
3238
0
  hash->nInternalPointer = 0;
3239
0
  hash->nNextFreeElement = n_elems;
3240
3241
0
  return true;
3242
0
}
3243
/* }}} */
3244
3245
/* {{{ Randomly shuffle the contents of an array */
3246
PHP_FUNCTION(shuffle)
3247
0
{
3248
0
  zval *array;
3249
3250
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3251
0
    Z_PARAM_ARRAY_EX(array, 0, 1)
3252
0
  ZEND_PARSE_PARAMETERS_END();
3253
3254
0
  php_array_data_shuffle(php_random_default_engine(), array);
3255
3256
0
  RETURN_TRUE;
3257
0
}
3258
/* }}} */
3259
3260
static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
3261
0
{
3262
0
  HashTable    out_hash;      /* Output hashtable */
3263
0
  zend_long  num_in;      /* Number of entries in the input hashtable */
3264
0
  zend_long  pos;       /* Current position in the hashtable */
3265
0
  uint32_t     idx;
3266
0
  zval    *entry;       /* Hash entry */
3267
0
  uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3268
3269
0
  GC_ADDREF(in_hash);
3270
0
  HT_ALLOW_COW_VIOLATION(in_hash); /* Will be reset when setting the flags for in_hash */
3271
3272
  /* Get number of entries in the input hash */
3273
0
  num_in = zend_hash_num_elements(in_hash);
3274
3275
  /* Clamp the offset.. */
3276
0
  if (offset > num_in) {
3277
0
    offset = num_in;
3278
0
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3279
0
    offset = 0;
3280
0
  }
3281
3282
  /* ..and the length */
3283
0
  if (length < 0) {
3284
0
    length = num_in - offset + length;
3285
0
  } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3286
0
    length = num_in - offset;
3287
0
  }
3288
3289
  /* Number of entries in the output hash: the input entries that are kept
3290
   * plus the replacement entries. After clamping, a non-positive length
3291
   * removes nothing, so all input entries are kept. */
3292
0
  uint32_t num_out = num_in - MAX(length, 0) + (replace ? zend_hash_num_elements(replace) : 0);
3293
3294
  /* Create and initialize output hash */
3295
0
  zend_hash_init(&out_hash, num_out, NULL, ZVAL_PTR_DTOR, 0);
3296
3297
0
  if (HT_IS_PACKED(in_hash)) {
3298
    /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3299
0
    entry = in_hash->arPacked;
3300
0
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3301
0
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3302
3303
0
      zend_hash_next_index_insert_new(&out_hash, entry);
3304
0
      if (idx == iter_pos) {
3305
0
        if ((zend_long)idx != pos) {
3306
0
          zend_hash_iterators_update(in_hash, idx, pos);
3307
0
        }
3308
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3309
0
      }
3310
0
      pos++;
3311
0
    }
3312
3313
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3314
0
    if (removed != NULL) {
3315
0
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3316
0
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3317
0
        pos++;
3318
0
        Z_TRY_ADDREF_P(entry);
3319
0
        zend_hash_next_index_insert_new(removed, entry);
3320
0
        zend_hash_packed_del_val(in_hash, entry);
3321
        /* Bump iterator positions to the element after replacement. */
3322
0
        if (idx == iter_pos) {
3323
0
          zend_hash_iterators_update(in_hash, idx, offset + length);
3324
0
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3325
0
        }
3326
0
      }
3327
0
    } else { /* otherwise just skip those entries */
3328
0
      zend_long pos2 = pos;
3329
3330
0
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3331
0
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3332
0
        pos2++;
3333
0
        zend_hash_packed_del_val(in_hash, entry);
3334
        /* Bump iterator positions to the element after replacement. */
3335
0
        if (idx == iter_pos) {
3336
0
          zend_hash_iterators_update(in_hash, idx, offset + length);
3337
0
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3338
0
        }
3339
0
      }
3340
0
    }
3341
3342
    /* If there are entries to insert.. */
3343
0
    if (replace) {
3344
0
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3345
0
        Z_TRY_ADDREF_P(entry);
3346
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3347
0
        pos++;
3348
0
      } ZEND_HASH_FOREACH_END();
3349
0
    }
3350
3351
    /* Copy the remaining input hash entries to the output hash */
3352
0
    entry = in_hash->arPacked + idx;
3353
0
    for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3354
0
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3355
0
      zend_hash_next_index_insert_new(&out_hash, entry);
3356
0
      if (idx == iter_pos) {
3357
0
        if ((zend_long)idx != pos) {
3358
0
          zend_hash_iterators_update(in_hash, idx, pos);
3359
0
        }
3360
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3361
0
      }
3362
0
      pos++;
3363
0
    }
3364
0
  } else {
3365
0
    Bucket *p = in_hash->arData;
3366
3367
    /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3368
0
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, p++) {
3369
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3370
0
      entry = &p->val;
3371
3372
      /* Update output hash depending on key type */
3373
0
      if (p->key == NULL) {
3374
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3375
0
      } else {
3376
0
        zend_hash_add_new(&out_hash, p->key, entry);
3377
0
      }
3378
0
      if (idx == iter_pos) {
3379
0
        if ((zend_long)idx != pos) {
3380
0
          zend_hash_iterators_update(in_hash, idx, pos);
3381
0
        }
3382
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3383
0
      }
3384
0
      pos++;
3385
0
    }
3386
3387
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3388
0
    if (removed != NULL) {
3389
0
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3390
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
3391
0
        pos++;
3392
0
        entry = &p->val;
3393
0
        Z_TRY_ADDREF_P(entry);
3394
0
        if (p->key == NULL) {
3395
0
          zend_hash_next_index_insert_new(removed, entry);
3396
0
        } else {
3397
0
          zend_hash_add_new(removed, p->key, entry);
3398
0
        }
3399
0
        zend_hash_del_bucket(in_hash, p);
3400
0
      }
3401
0
    } else { /* otherwise just skip those entries */
3402
0
      zend_long pos2 = pos;
3403
3404
0
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3405
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
3406
0
        pos2++;
3407
0
        zend_hash_del_bucket(in_hash, p);
3408
0
      }
3409
0
    }
3410
0
    iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3411
3412
    /* If there are entries to insert.. */
3413
0
    if (replace) {
3414
0
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3415
0
        Z_TRY_ADDREF_P(entry);
3416
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3417
0
        pos++;
3418
0
      } ZEND_HASH_FOREACH_END();
3419
0
    }
3420
3421
    /* Copy the remaining input hash entries to the output hash */
3422
0
    for ( ; idx < in_hash->nNumUsed ; idx++, p++) {
3423
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3424
0
      entry = &p->val;
3425
0
      if (p->key == NULL) {
3426
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3427
0
      } else {
3428
0
        zend_hash_add_new(&out_hash, p->key, entry);
3429
0
      }
3430
0
      if (idx == iter_pos) {
3431
0
        if ((zend_long)idx != pos) {
3432
0
          zend_hash_iterators_update(in_hash, idx, pos);
3433
0
        }
3434
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3435
0
      }
3436
0
      pos++;
3437
0
    }
3438
0
  }
3439
3440
  /* replace HashTable data */
3441
0
  HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3442
0
  HT_SET_ITERATORS_COUNT(in_hash, 0);
3443
0
  in_hash->pDestructor = NULL;
3444
3445
  /* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset().
3446
   * This avoids the COW violation assertion and delays advancing to the first valid position
3447
   * until after we've switched to the new array structure (out_hash). The iterator will be
3448
   * advanced when actually accessed, at which point it will find valid indexes in the new array. */
3449
0
  in_hash->nInternalPointer = 0;
3450
3451
0
  if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
3452
    /* Array was completely deallocated during the operation */
3453
0
    zend_array_destroy(in_hash);
3454
0
    zend_hash_destroy(&out_hash);
3455
0
    zend_throw_error(NULL, "Array was modified during array_splice operation");
3456
0
    return;
3457
0
  }
3458
3459
0
  uint32_t type_info = GC_TYPE_INFO(in_hash);
3460
0
  GC_TYPE_INFO(in_hash) = GC_ARRAY;
3461
3462
0
  zend_hash_destroy(in_hash);
3463
0
  GC_TYPE_INFO(in_hash) = type_info;
3464
3465
0
  HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
3466
0
  in_hash->nTableSize        = out_hash.nTableSize;
3467
0
  in_hash->nTableMask        = out_hash.nTableMask;
3468
0
  in_hash->nNumUsed          = out_hash.nNumUsed;
3469
0
  in_hash->nNumOfElements    = out_hash.nNumOfElements;
3470
0
  in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
3471
0
  in_hash->arData            = out_hash.arData;
3472
0
  in_hash->pDestructor       = out_hash.pDestructor;
3473
0
}
3474
/* }}} */
3475
3476
/* {{{ Pushes elements onto the end of the array */
3477
PHP_FUNCTION(array_push)
3478
0
{
3479
0
  zval   *args,   /* Function arguments array */
3480
0
       *stack;    /* Input array */
3481
0
  uint32_t argc;    /* Number of function arguments */
3482
3483
3484
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
3485
0
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3486
0
    Z_PARAM_VARIADIC('+', args, argc)
3487
0
  ZEND_PARSE_PARAMETERS_END();
3488
3489
  /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3490
0
  for (uint32_t i = 0; i < argc; i++) {
3491
0
    Z_TRY_ADDREF(args[i]);
3492
3493
0
    if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &args[i]) == NULL) {
3494
0
      Z_TRY_DELREF(args[i]);
3495
0
      zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3496
0
      RETURN_THROWS();
3497
0
    }
3498
0
  }
3499
3500
  /* Clean up and return the number of values in the stack */
3501
0
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3502
0
}
3503
/* }}} */
3504
3505
/* {{{ Pops an element off the end of the array */
3506
PHP_FUNCTION(array_pop)
3507
0
{
3508
0
  zval *stack,  /* Input stack */
3509
0
     *val;    /* Value to be popped */
3510
0
  uint32_t idx;
3511
3512
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3513
0
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3514
0
  ZEND_PARSE_PARAMETERS_END();
3515
3516
0
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3517
0
    return;
3518
0
  }
3519
3520
0
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3521
    /* Get the last value and copy it into the return value */
3522
0
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3523
0
    while (1) {
3524
0
      if (idx == 0) {
3525
0
        return;
3526
0
      }
3527
0
      idx--;
3528
0
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3529
0
      if (Z_TYPE_P(val) != IS_UNDEF) {
3530
0
        break;
3531
0
      }
3532
0
    }
3533
0
    RETVAL_COPY_VALUE(val);
3534
0
    ZVAL_UNDEF(val);
3535
3536
0
    if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3537
0
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3538
0
    }
3539
3540
    /* Delete the last value */
3541
0
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3542
0
  } else {
3543
0
    Bucket *p;
3544
3545
    /* Get the last value and copy it into the return value */
3546
0
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3547
0
    while (1) {
3548
0
      if (idx == 0) {
3549
0
        return;
3550
0
      }
3551
0
      idx--;
3552
0
      p = Z_ARRVAL_P(stack)->arData + idx;
3553
0
      val = &p->val;
3554
0
      if (Z_TYPE_P(val) != IS_UNDEF) {
3555
0
        break;
3556
0
      }
3557
0
    }
3558
0
    RETVAL_COPY_VALUE(val);
3559
0
    ZVAL_UNDEF(val);
3560
3561
0
    if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3562
0
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3563
0
    }
3564
3565
    /* Delete the last value */
3566
0
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3567
0
  }
3568
0
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3569
3570
0
  if (Z_ISREF_P(return_value)) {
3571
0
    zend_unwrap_reference(return_value);
3572
0
  }
3573
0
}
3574
/* }}} */
3575
3576
/* {{{ Pops an element off the beginning of the array */
3577
PHP_FUNCTION(array_shift)
3578
0
{
3579
0
  zval *stack,  /* Input stack */
3580
0
     *val;    /* Value to be popped */
3581
0
  uint32_t idx;
3582
3583
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3584
0
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3585
0
  ZEND_PARSE_PARAMETERS_END();
3586
3587
0
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3588
0
    return;
3589
0
  }
3590
3591
  /* re-index like it did before */
3592
0
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3593
0
    uint32_t k = 0;
3594
3595
    /* Get the first value and copy it into the return value */
3596
0
    idx = 0;
3597
0
    while (1) {
3598
0
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3599
0
        return;
3600
0
      }
3601
0
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3602
0
      if (Z_TYPE_P(val) != IS_UNDEF) {
3603
0
        break;
3604
0
      }
3605
0
      idx++;
3606
0
    }
3607
0
    RETVAL_COPY_VALUE(val);
3608
0
    ZVAL_UNDEF(val);
3609
3610
    /* Delete the first value */
3611
0
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3612
3613
0
    if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3614
0
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3615
0
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3616
0
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3617
0
        if (idx != k) {
3618
0
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3619
0
          ZVAL_COPY_VALUE(q, val);
3620
0
          ZVAL_UNDEF(val);
3621
0
        }
3622
0
        k++;
3623
0
      }
3624
0
    } else {
3625
0
      uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3626
3627
0
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3628
0
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3629
0
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3630
0
        if (idx != k) {
3631
0
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3632
0
          ZVAL_COPY_VALUE(q, val);
3633
0
          ZVAL_UNDEF(val);
3634
0
          if (idx == iter_pos) {
3635
0
            zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3636
0
            iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3637
0
          }
3638
0
        }
3639
0
        k++;
3640
0
      }
3641
0
    }
3642
0
    Z_ARRVAL_P(stack)->nNumUsed = k;
3643
0
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3644
0
  } else {
3645
0
    uint32_t k = 0;
3646
0
    int should_rehash = 0;
3647
0
    Bucket *p;
3648
3649
    /* Get the first value and copy it into the return value */
3650
0
    idx = 0;
3651
0
    while (1) {
3652
0
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3653
0
        return;
3654
0
      }
3655
0
      p = Z_ARRVAL_P(stack)->arData + idx;
3656
0
      val = &p->val;
3657
0
      if (Z_TYPE_P(val) != IS_UNDEF) {
3658
0
        break;
3659
0
      }
3660
0
      idx++;
3661
0
    }
3662
0
    RETVAL_COPY_VALUE(val);
3663
0
    ZVAL_UNDEF(val);
3664
3665
    /* Delete the first value */
3666
0
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3667
3668
0
    for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3669
0
      p = Z_ARRVAL_P(stack)->arData + idx;
3670
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3671
0
      if (p->key == NULL) {
3672
0
        if (p->h != k) {
3673
0
          p->h = k++;
3674
0
          should_rehash = 1;
3675
0
        } else {
3676
0
          k++;
3677
0
        }
3678
0
      }
3679
0
    }
3680
0
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3681
0
    if (should_rehash) {
3682
0
      zend_hash_rehash(Z_ARRVAL_P(stack));
3683
0
    }
3684
0
  }
3685
3686
0
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3687
3688
0
  if (Z_ISREF_P(return_value)) {
3689
0
    zend_unwrap_reference(return_value);
3690
0
  }
3691
0
}
3692
/* }}} */
3693
3694
/* {{{ Pushes elements onto the beginning of the array */
3695
PHP_FUNCTION(array_unshift)
3696
2
{
3697
2
  zval   *args,     /* Function arguments array */
3698
2
       *stack;      /* Input stack */
3699
2
  HashTable new_hash;   /* New hashtable for the stack */
3700
2
  uint32_t argc;      /* Number of function arguments */
3701
2
  zend_string *key;
3702
2
  zval *value;
3703
3704
6
  ZEND_PARSE_PARAMETERS_START(1, -1)
3705
8
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3706
2
    Z_PARAM_VARIADIC('+', args, argc)
3707
2
  ZEND_PARSE_PARAMETERS_END();
3708
3709
2
  zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3710
3711
2
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3712
2
    zend_hash_real_init_packed(&new_hash);
3713
3714
2
    ZEND_HASH_FILL_PACKED(&new_hash) {
3715
16
      for (uint32_t i = 0; i < argc; i++) {
3716
14
        Z_TRY_ADDREF(args[i]);
3717
14
        ZEND_HASH_FILL_ADD(&args[i]);
3718
14
      }
3719
3720
26
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(stack), value) {
3721
26
        ZEND_HASH_FILL_ADD(value);
3722
26
      } ZEND_HASH_FOREACH_END();
3723
2
    } ZEND_HASH_FILL_END();
3724
2
  } else {
3725
0
    for (uint32_t i = 0; i < argc; i++) {
3726
0
      Z_TRY_ADDREF(args[i]);
3727
0
      zend_hash_next_index_insert_new(&new_hash, &args[i]);
3728
0
    }
3729
3730
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3731
0
      if (key) {
3732
0
        zend_hash_add_new(&new_hash, key, value);
3733
0
      } else {
3734
0
        zend_hash_next_index_insert_new(&new_hash, value);
3735
0
      }
3736
0
    } ZEND_HASH_FOREACH_END();
3737
0
  }
3738
3739
2
  if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3740
2
    zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3741
2
    HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3742
2
    HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3743
2
  }
3744
3745
  /* replace HashTable data */
3746
2
  Z_ARRVAL_P(stack)->pDestructor = NULL;
3747
2
  uint32_t type_info = GC_TYPE_INFO(Z_ARRVAL_P(stack));
3748
2
  GC_TYPE_INFO(Z_ARRVAL_P(stack)) = GC_ARRAY;
3749
2
  zend_hash_destroy(Z_ARRVAL_P(stack));
3750
3751
2
  GC_TYPE_INFO(Z_ARRVAL_P(stack)) = type_info;
3752
2
  HT_FLAGS(Z_ARRVAL_P(stack))          = HT_FLAGS(&new_hash);
3753
2
  Z_ARRVAL_P(stack)->nTableSize        = new_hash.nTableSize;
3754
2
  Z_ARRVAL_P(stack)->nTableMask        = new_hash.nTableMask;
3755
2
  Z_ARRVAL_P(stack)->nNumUsed          = new_hash.nNumUsed;
3756
2
  Z_ARRVAL_P(stack)->nNumOfElements    = new_hash.nNumOfElements;
3757
2
  Z_ARRVAL_P(stack)->nNextFreeElement  = new_hash.nNextFreeElement;
3758
2
  Z_ARRVAL_P(stack)->arData            = new_hash.arData;
3759
2
  Z_ARRVAL_P(stack)->pDestructor       = new_hash.pDestructor;
3760
3761
2
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3762
3763
  /* Clean up and return the number of elements in the stack */
3764
2
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3765
2
}
3766
/* }}} */
3767
3768
/* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3769
PHP_FUNCTION(array_splice)
3770
0
{
3771
0
  zval *array,        /* Input array */
3772
0
     *repl_array = NULL; /* Replacement array */
3773
0
  HashTable  *rem_hash = NULL;
3774
0
  zend_long offset,
3775
0
      length = 0;
3776
0
  bool length_is_null = 1;
3777
0
  int   num_in;       /* Number of elements in the input array */
3778
3779
0
  ZEND_PARSE_PARAMETERS_START(2, 4)
3780
0
    Z_PARAM_ARRAY_EX(array, 0, 1)
3781
0
    Z_PARAM_LONG(offset)
3782
0
    Z_PARAM_OPTIONAL
3783
0
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3784
0
    Z_PARAM_ZVAL(repl_array)
3785
0
  ZEND_PARSE_PARAMETERS_END();
3786
3787
0
  num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3788
3789
0
  if (length_is_null) {
3790
0
    length = num_in;
3791
0
  }
3792
3793
0
  if (ZEND_NUM_ARGS() == 4) {
3794
    /* Make sure the last argument, if passed, is an array */
3795
0
    convert_to_array(repl_array);
3796
0
  }
3797
3798
  /* Don't create the array of removed elements if it's not going
3799
   * to be used; e.g. only removing and/or replacing elements */
3800
0
  if (USED_RET()) {
3801
0
    zend_long size = length;
3802
3803
    /* Clamp the offset.. */
3804
0
    if (offset > num_in) {
3805
0
      offset = num_in;
3806
0
    } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3807
0
      offset = 0;
3808
0
    }
3809
3810
    /* ..and the length */
3811
0
    if (length < 0) {
3812
0
      size = num_in - offset + length;
3813
0
    } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
3814
0
      size = num_in - offset;
3815
0
    }
3816
3817
    /* Initialize return value */
3818
0
    array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3819
0
    rem_hash = Z_ARRVAL_P(return_value);
3820
0
  } else {
3821
    /* The return value will not be used, but make sure it still has the correct type. */
3822
0
    RETVAL_EMPTY_ARRAY();
3823
0
  }
3824
3825
  /* Perform splice */
3826
0
  php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3827
0
}
3828
/* }}} */
3829
3830
/* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3831
   Finds the bucket at the given valid offset */
3832
static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3833
0
{
3834
0
  zend_long pos;
3835
0
  Bucket *bucket;
3836
0
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3837
0
  if (HT_IS_WITHOUT_HOLES(ht)) {
3838
    /* There's no need to iterate over the array to filter out holes if there are no holes */
3839
0
    return ht->arData + offset;
3840
0
  }
3841
  /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3842
0
  pos = 0;
3843
0
  ZEND_HASH_MAP_FOREACH_BUCKET(ht, bucket) {
3844
0
    if (pos >= offset) {
3845
      /* This is the bucket of the array element at the requested offset */
3846
0
      return bucket;
3847
0
    }
3848
0
    ++pos;
3849
0
  } ZEND_HASH_FOREACH_END();
3850
3851
  /* Return a pointer to the end of the bucket array. */
3852
0
  return ht->arData + ht->nNumUsed;
3853
0
}
3854
/* }}} */
3855
3856
/* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3857
   Finds the bucket at the given valid offset */
3858
static inline zval* find_packed_val_at_offset(HashTable* ht, zend_long offset)
3859
0
{
3860
0
  zend_long pos;
3861
0
  zval *zv;
3862
0
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3863
0
  if (HT_IS_WITHOUT_HOLES(ht)) {
3864
    /* There's no need to iterate over the array to filter out holes if there are no holes */
3865
0
    return ht->arPacked + offset;
3866
0
  }
3867
  /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3868
0
  pos = 0;
3869
0
  ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) {
3870
0
    if (pos >= offset) {
3871
      /* This is the bucket of the array element at the requested offset */
3872
0
      return zv;
3873
0
    }
3874
0
    ++pos;
3875
0
  } ZEND_HASH_FOREACH_END();
3876
3877
  /* Return a pointer to the end of the bucket array. */
3878
0
  return ht->arPacked + ht->nNumUsed;
3879
0
}
3880
/* }}} */
3881
3882
/* {{{ Returns elements specified by offset and length */
3883
PHP_FUNCTION(array_slice)
3884
0
{
3885
0
  zval *input;                  /* Input array */
3886
0
  zval *entry;                  /* An array entry */
3887
0
  zend_long offset;             /* Offset to get elements from */
3888
0
  zend_long length = 0;         /* How many elements to get */
3889
0
  bool length_is_null = 1; /* Whether an explicit length has been omitted */
3890
0
  bool preserve_keys = 0;  /* Whether to preserve keys while copying to the new array */
3891
0
  uint32_t num_in;              /* Number of elements in the input array */
3892
0
  zend_string *string_key;
3893
0
  zend_ulong num_key;
3894
3895
0
  ZEND_PARSE_PARAMETERS_START(2, 4)
3896
0
    Z_PARAM_ARRAY(input)
3897
0
    Z_PARAM_LONG(offset)
3898
0
    Z_PARAM_OPTIONAL
3899
0
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3900
0
    Z_PARAM_BOOL(preserve_keys)
3901
0
  ZEND_PARSE_PARAMETERS_END();
3902
3903
  /* Get number of entries in the input hash */
3904
0
  num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
3905
3906
  /* We want all entries from offset to the end if length is not passed or is null */
3907
0
  if (length_is_null) {
3908
0
    length = num_in;
3909
0
  }
3910
3911
  /* Clamp the offset.. */
3912
0
  if (offset > (zend_long) num_in) {
3913
0
    RETURN_EMPTY_ARRAY();
3914
0
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3915
0
    offset = 0;
3916
0
  }
3917
3918
  /* ..and the length */
3919
0
  if (length < 0) {
3920
0
    length = num_in - offset + length;
3921
0
  } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3922
0
    length = num_in - offset;
3923
0
  }
3924
3925
0
  if (length <= 0) {
3926
0
    RETURN_EMPTY_ARRAY();
3927
0
  }
3928
3929
  /* Initialize returned array */
3930
0
  array_init_size(return_value, (uint32_t)length);
3931
3932
  // Contains modified variants of ZEND_HASH_FOREACH_VAL
3933
0
  {
3934
0
    HashTable *ht = Z_ARRVAL_P(input);
3935
3936
    /* Start at the beginning and go until we hit offset */
3937
0
    if (HT_IS_PACKED(ht)) {
3938
0
      zval *zv = find_packed_val_at_offset(ht, offset);
3939
0
      zval *end = ht->arPacked + ht->nNumUsed;
3940
3941
0
      if (!preserve_keys
3942
0
       || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3943
0
        zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3944
0
        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3945
0
          for (; zv != end; zv++) {
3946
0
            if (__fill_idx >= length) {
3947
0
              break;
3948
0
            }
3949
0
            if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3950
0
              continue;
3951
0
            }
3952
0
            entry = zv;
3953
0
            if (UNEXPECTED(Z_ISREF_P(entry)) &&
3954
0
              UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3955
0
              entry = Z_REFVAL_P(entry);
3956
0
            }
3957
0
            Z_TRY_ADDREF_P(entry);
3958
0
            ZEND_HASH_FILL_ADD(entry);
3959
0
          }
3960
0
        } ZEND_HASH_FILL_END();
3961
0
      } else {
3962
0
        zend_long n = 0;  /* Current number of elements */
3963
0
        zend_long idx = zv - ht->arPacked;
3964
3965
0
        for (; zv != end; zv++, idx++) {
3966
0
          if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3967
0
            continue;
3968
0
          }
3969
0
          if (n >= length) {
3970
0
            break;
3971
0
          }
3972
0
          n++;
3973
0
          entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, zv);
3974
0
          zval_add_ref(entry);
3975
0
        }
3976
0
      }
3977
0
    } else {
3978
0
      zend_long n = 0;  /* Current number of elements */
3979
0
      Bucket *p = find_bucket_at_offset(ht, offset);
3980
0
      Bucket *end = ht->arData + ht->nNumUsed;
3981
3982
0
      for (; p != end; p++) {
3983
0
        entry = &p->val;
3984
0
        if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3985
0
          continue;
3986
0
        }
3987
0
        if (n >= length) {
3988
0
          break;
3989
0
        }
3990
0
        n++;
3991
0
        num_key = p->h;
3992
0
        string_key = p->key;
3993
3994
0
        if (string_key) {
3995
0
          entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3996
0
        } else {
3997
0
          if (preserve_keys) {
3998
0
            entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3999
0
          } else {
4000
0
            entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4001
0
          }
4002
0
        }
4003
0
        zval_add_ref(entry);
4004
0
      }
4005
0
    }
4006
0
  }
4007
0
}
4008
/* }}} */
4009
4010
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
4011
0
{
4012
0
  zval *src_entry, *dest_entry;
4013
0
  zend_string *string_key;
4014
4015
0
  ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4016
0
    if (string_key) {
4017
0
      if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
4018
0
        zval *src_zval = src_entry;
4019
0
        zval *dest_zval = dest_entry;
4020
0
        HashTable *thash;
4021
0
        zval tmp;
4022
0
        int ret;
4023
4024
0
        ZVAL_DEREF(src_zval);
4025
0
        ZVAL_DEREF(dest_zval);
4026
0
        thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
4027
0
        if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
4028
0
          zend_throw_error(NULL, "Recursion detected");
4029
0
          return 0;
4030
0
        }
4031
4032
0
        ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4033
0
        dest_zval = dest_entry;
4034
4035
0
        if (Z_TYPE_P(dest_zval) == IS_NULL) {
4036
0
          convert_to_array(dest_zval);
4037
0
          add_next_index_null(dest_zval);
4038
0
        } else {
4039
0
          convert_to_array(dest_zval);
4040
0
        }
4041
0
        SEPARATE_ZVAL(dest_zval);
4042
4043
0
        ZVAL_UNDEF(&tmp);
4044
0
        if (Z_TYPE_P(src_zval) == IS_OBJECT) {
4045
0
          ZVAL_COPY(&tmp, src_zval);
4046
0
          convert_to_array(&tmp);
4047
0
          src_zval = &tmp;
4048
0
        }
4049
0
        if (Z_TYPE_P(src_zval) == IS_ARRAY) {
4050
0
          if (thash) {
4051
0
            GC_TRY_PROTECT_RECURSION(thash);
4052
0
          }
4053
0
          ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4054
0
          if (thash) {
4055
0
            GC_TRY_UNPROTECT_RECURSION(thash);
4056
0
          }
4057
0
          if (!ret) {
4058
0
            zval_ptr_dtor(&tmp);
4059
0
            return 0;
4060
0
          }
4061
0
        } else {
4062
0
          Z_TRY_ADDREF_P(src_zval);
4063
0
          zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
4064
0
          if (UNEXPECTED(!zv)) {
4065
0
            Z_TRY_DELREF_P(src_zval);
4066
0
            zend_cannot_add_element();
4067
0
            return 0;
4068
0
          }
4069
0
        }
4070
0
        zval_ptr_dtor(&tmp);
4071
0
      } else {
4072
0
        zval *zv = zend_hash_add_new(dest, string_key, src_entry);
4073
0
        zval_add_ref(zv);
4074
0
      }
4075
0
    } else {
4076
0
      zval *zv = zend_hash_next_index_insert(dest, src_entry);
4077
0
      if (UNEXPECTED(!zv)) {
4078
0
        zend_cannot_add_element();
4079
0
        return 0;
4080
0
      }
4081
0
      zval_add_ref(zv);
4082
0
    }
4083
0
  } ZEND_HASH_FOREACH_END();
4084
0
  return 1;
4085
0
}
4086
/* }}} */
4087
4088
PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
4089
0
{
4090
0
  zval *src_entry;
4091
0
  zend_string *string_key;
4092
4093
0
  if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
4094
0
    zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
4095
0
    ZEND_HASH_FILL_PACKED(dest) {
4096
0
      ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4097
0
        if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
4098
0
          UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
4099
0
          src_entry = Z_REFVAL_P(src_entry);
4100
0
        }
4101
0
        Z_TRY_ADDREF_P(src_entry);
4102
0
        ZEND_HASH_FILL_ADD(src_entry);
4103
0
      } ZEND_HASH_FOREACH_END();
4104
0
    } ZEND_HASH_FILL_END();
4105
0
  } else {
4106
0
    ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4107
0
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4108
0
        Z_REFCOUNT_P(src_entry) == 1)) {
4109
0
        src_entry = Z_REFVAL_P(src_entry);
4110
0
      }
4111
0
      Z_TRY_ADDREF_P(src_entry);
4112
0
      if (string_key) {
4113
0
        zend_hash_update(dest, string_key, src_entry);
4114
0
      } else {
4115
0
        zend_hash_next_index_insert_new(dest, src_entry);
4116
0
      }
4117
0
    } ZEND_HASH_FOREACH_END();
4118
0
  }
4119
0
  return 1;
4120
0
}
4121
/* }}} */
4122
4123
PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4124
0
{
4125
0
  zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4126
0
  zend_string *string_key;
4127
0
  zend_ulong num_key;
4128
0
  int ret;
4129
4130
0
#ifdef ZEND_CHECK_STACK_LIMIT
4131
0
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
4132
0
    zend_call_stack_size_error();
4133
0
    return 0;
4134
0
  }
4135
0
#endif
4136
4137
0
  ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4138
0
    src_zval = src_entry;
4139
0
    ZVAL_DEREF(src_zval);
4140
0
    if (string_key) {
4141
0
      if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4142
0
        (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4143
0
        (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4144
0
         (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4145
4146
0
        zval *zv = zend_hash_update(dest, string_key, src_entry);
4147
0
        zval_add_ref(zv);
4148
0
        continue;
4149
0
      }
4150
0
    } else {
4151
0
      if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4152
0
        (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4153
0
        (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4154
0
         (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4155
4156
0
        zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4157
0
        zval_add_ref(zv);
4158
0
        continue;
4159
0
      }
4160
0
    }
4161
4162
0
    dest_zval = dest_entry;
4163
0
    ZVAL_DEREF(dest_zval);
4164
0
    if (Z_IS_RECURSIVE_P(dest_zval) ||
4165
0
      Z_IS_RECURSIVE_P(src_zval) ||
4166
0
      (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
4167
0
      zend_throw_error(NULL, "Recursion detected");
4168
0
      return 0;
4169
0
    }
4170
4171
0
    ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4172
0
    SEPARATE_ZVAL(dest_entry);
4173
0
    dest_zval = dest_entry;
4174
4175
0
    if (Z_REFCOUNTED_P(dest_zval)) {
4176
0
      Z_PROTECT_RECURSION_P(dest_zval);
4177
0
    }
4178
0
    if (Z_REFCOUNTED_P(src_zval)) {
4179
0
      Z_PROTECT_RECURSION_P(src_zval);
4180
0
    }
4181
4182
0
    ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4183
4184
0
    if (Z_REFCOUNTED_P(dest_zval)) {
4185
0
      Z_UNPROTECT_RECURSION_P(dest_zval);
4186
0
    }
4187
0
    if (Z_REFCOUNTED_P(src_zval)) {
4188
0
      Z_UNPROTECT_RECURSION_P(src_zval);
4189
0
    }
4190
4191
0
    if (!ret) {
4192
0
      return 0;
4193
0
    }
4194
0
  } ZEND_HASH_FOREACH_END();
4195
4196
0
  return 1;
4197
0
}
4198
/* }}} */
4199
4200
static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4201
0
{
4202
0
  zval *args = NULL;
4203
0
  zval *arg;
4204
0
  uint32_t argc, i;
4205
0
  HashTable *dest;
4206
4207
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
4208
0
    Z_PARAM_VARIADIC('+', args, argc)
4209
0
  ZEND_PARSE_PARAMETERS_END();
4210
4211
4212
0
  for (i = 0; i < argc; i++) {
4213
0
    zval *arg = args + i;
4214
4215
0
    if (Z_TYPE_P(arg) != IS_ARRAY) {
4216
0
      zend_wrong_parameter_type_error(i + 1, Z_EXPECTED_ARRAY, arg);
4217
0
      RETURN_THROWS();
4218
0
    }
4219
0
  }
4220
4221
  /* copy first array if necessary */
4222
0
  arg = args;
4223
0
  bool in_place = zend_may_modify_arg_in_place(arg);
4224
0
  if (in_place) {
4225
0
    dest = Z_ARRVAL_P(arg);
4226
0
  } else {
4227
0
    dest = zend_array_dup(Z_ARRVAL_P(arg));
4228
0
  }
4229
4230
0
  ZVAL_ARR(return_value, dest);
4231
4232
0
  if (recursive) {
4233
0
    for (i = 1; i < argc; i++) {
4234
0
      arg = args + i;
4235
0
      php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4236
0
    }
4237
0
  } else {
4238
0
    for (i = 1; i < argc; i++) {
4239
0
      arg = args + i;
4240
0
      zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4241
0
    }
4242
0
  }
4243
4244
0
  if (in_place) {
4245
0
    GC_ADDREF(dest);
4246
0
  }
4247
0
}
4248
/* }}} */
4249
4250
static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4251
0
{
4252
0
  zval *args = NULL;
4253
0
  zval *arg;
4254
0
  uint32_t argc, i;
4255
0
  zval *src_entry;
4256
0
  HashTable *src, *dest;
4257
0
  uint64_t count = 0;
4258
4259
0
  ZEND_PARSE_PARAMETERS_START(0, -1)
4260
0
    Z_PARAM_VARIADIC('+', args, argc)
4261
0
  ZEND_PARSE_PARAMETERS_END();
4262
4263
0
  if (argc == 0) {
4264
0
    RETURN_EMPTY_ARRAY();
4265
0
  }
4266
4267
0
  for (i = 0; i < argc; i++) {
4268
0
    zval *arg = args + i;
4269
4270
0
    if (Z_TYPE_P(arg) != IS_ARRAY) {
4271
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4272
0
      RETURN_THROWS();
4273
0
    }
4274
0
    count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4275
0
  }
4276
4277
0
  if (UNEXPECTED(count >= HT_MAX_SIZE)) {
4278
0
    zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
4279
0
    RETURN_THROWS();
4280
0
  }
4281
4282
0
  if (argc == 2) {
4283
0
    zval *ret = NULL;
4284
4285
0
    if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4286
0
      ret = &args[1];
4287
0
    } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4288
0
      ret = &args[0];
4289
0
    }
4290
0
    if (ret) {
4291
0
      if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4292
0
        if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4293
0
          RETURN_COPY(ret);
4294
0
        }
4295
0
      } else {
4296
0
        bool copy = true;
4297
0
        zend_string *string_key;
4298
4299
0
        ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4300
0
          if (!string_key) {
4301
0
            copy = false;
4302
0
            break;
4303
0
          }
4304
0
        } ZEND_HASH_FOREACH_END();
4305
0
        if (copy) {
4306
0
          RETURN_COPY(ret);
4307
0
        }
4308
0
      }
4309
0
    }
4310
0
  }
4311
4312
0
  arg = args;
4313
0
  src  = Z_ARRVAL_P(arg);
4314
  /* copy first array if necessary */
4315
0
  bool in_place = false;
4316
0
  if (HT_IS_PACKED(src)) {
4317
    /* Note: If it has holes, it might get sequentialized */
4318
0
    if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4319
0
      dest = src;
4320
0
      in_place = true;
4321
0
      RETVAL_ARR(dest);
4322
0
    } else {
4323
0
      array_init_size(return_value, count);
4324
0
      dest = Z_ARRVAL_P(return_value);
4325
4326
0
      zend_hash_real_init_packed(dest);
4327
0
      ZEND_HASH_FILL_PACKED(dest) {
4328
0
        ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4329
0
          if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4330
0
            Z_REFCOUNT_P(src_entry) == 1)) {
4331
0
            src_entry = Z_REFVAL_P(src_entry);
4332
0
          }
4333
0
          Z_TRY_ADDREF_P(src_entry);
4334
0
          ZEND_HASH_FILL_ADD(src_entry);
4335
0
        } ZEND_HASH_FOREACH_END();
4336
0
      } ZEND_HASH_FILL_END();
4337
0
    }
4338
0
  } else {
4339
0
    array_init_size(return_value, count);
4340
0
    dest = Z_ARRVAL_P(return_value);
4341
4342
0
    zend_string *string_key;
4343
0
    zend_hash_real_init_mixed(dest);
4344
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4345
0
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4346
0
        Z_REFCOUNT_P(src_entry) == 1)) {
4347
0
        src_entry = Z_REFVAL_P(src_entry);
4348
0
      }
4349
0
      Z_TRY_ADDREF_P(src_entry);
4350
0
      if (EXPECTED(string_key)) {
4351
0
        _zend_hash_append(dest, string_key, src_entry);
4352
0
      } else {
4353
0
        zend_hash_next_index_insert_new(dest, src_entry);
4354
0
      }
4355
0
    } ZEND_HASH_FOREACH_END();
4356
0
  }
4357
0
  if (recursive) {
4358
0
    for (i = 1; i < argc; i++) {
4359
0
      arg = args + i;
4360
0
      php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4361
0
    }
4362
0
  } else {
4363
0
    for (i = 1; i < argc; i++) {
4364
0
      arg = args + i;
4365
0
      php_array_merge(dest, Z_ARRVAL_P(arg));
4366
0
    }
4367
0
  }
4368
4369
0
  if (in_place) {
4370
0
    GC_ADDREF(dest);
4371
0
  }
4372
0
}
4373
/* }}} */
4374
4375
/* {{{ Merges elements from passed arrays into one array */
4376
PHP_FUNCTION(array_merge)
4377
0
{
4378
0
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4379
0
}
4380
/* }}} */
4381
4382
/* {{{ Recursively merges elements from passed arrays into one array */
4383
PHP_FUNCTION(array_merge_recursive)
4384
0
{
4385
0
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4386
0
}
4387
/* }}} */
4388
4389
/* {{{ Replaces elements from passed arrays into one array */
4390
PHP_FUNCTION(array_replace)
4391
0
{
4392
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4393
0
}
4394
/* }}} */
4395
4396
/* {{{ Recursively replaces elements from passed arrays into one array */
4397
PHP_FUNCTION(array_replace_recursive)
4398
0
{
4399
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4400
0
}
4401
/* }}} */
4402
4403
/* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4404
PHP_FUNCTION(array_keys)
4405
0
{
4406
0
  zval *input,        /* Input array */
4407
0
       *search_value = NULL, /* Value to search for */
4408
0
       *entry,        /* An entry in the input array */
4409
0
         new_val;       /* New value */
4410
0
  bool strict = 0;    /* do strict comparison */
4411
0
  zend_ulong num_idx;
4412
0
  zend_string *str_idx;
4413
0
  zend_array *arrval;
4414
0
  zend_ulong elem_count;
4415
4416
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
4417
0
    Z_PARAM_ARRAY(input)
4418
0
    Z_PARAM_OPTIONAL
4419
0
    Z_PARAM_ZVAL(search_value)
4420
0
    Z_PARAM_BOOL(strict)
4421
0
  ZEND_PARSE_PARAMETERS_END();
4422
0
  arrval = Z_ARRVAL_P(input);
4423
0
  elem_count = zend_hash_num_elements(arrval);
4424
4425
  /* Base case: empty input */
4426
0
  if (!elem_count) {
4427
0
    RETURN_EMPTY_ARRAY();
4428
0
  }
4429
4430
  /* Initialize return array */
4431
0
  if (search_value != NULL) {
4432
0
    array_init(return_value);
4433
4434
0
    if (strict) {
4435
0
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4436
0
        ZVAL_DEREF(entry);
4437
0
        if (fast_is_identical_function(search_value, entry)) {
4438
0
          if (str_idx) {
4439
0
            ZVAL_STR_COPY(&new_val, str_idx);
4440
0
          } else {
4441
0
            ZVAL_LONG(&new_val, num_idx);
4442
0
          }
4443
0
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4444
0
        }
4445
0
      } ZEND_HASH_FOREACH_END();
4446
0
    } else {
4447
0
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4448
0
        if (fast_equal_check_function(search_value, entry)) {
4449
0
          if (str_idx) {
4450
0
            ZVAL_STR_COPY(&new_val, str_idx);
4451
0
          } else {
4452
0
            ZVAL_LONG(&new_val, num_idx);
4453
0
          }
4454
0
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4455
0
        }
4456
0
      } ZEND_HASH_FOREACH_END();
4457
0
    }
4458
0
  } else {
4459
0
    array_init_size(return_value, elem_count);
4460
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4461
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4462
0
      if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4463
        /* Optimistic case: range(0..n-1) for vector-like packed array */
4464
0
        zend_ulong lval = 0;
4465
4466
0
        for (; lval < elem_count; ++lval) {
4467
0
          ZEND_HASH_FILL_SET_LONG(lval);
4468
0
          ZEND_HASH_FILL_NEXT();
4469
0
        }
4470
0
      } else {
4471
        /* Go through input array and add keys to the return array */
4472
0
        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4473
0
          if (str_idx) {
4474
0
            ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4475
0
          } else {
4476
0
            ZEND_HASH_FILL_SET_LONG(num_idx);
4477
0
          }
4478
0
          ZEND_HASH_FILL_NEXT();
4479
0
        } ZEND_HASH_FOREACH_END();
4480
0
      }
4481
0
    } ZEND_HASH_FILL_END();
4482
0
  }
4483
0
}
4484
/* }}} */
4485
4486
/* {{{ Get the key of the first element of the array */
4487
PHP_FUNCTION(array_key_first)
4488
0
{
4489
0
  zval *stack;    /* Input stack */
4490
4491
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4492
0
    Z_PARAM_ARRAY(stack)
4493
0
  ZEND_PARSE_PARAMETERS_END();
4494
4495
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4496
0
  HashPosition pos = 0;
4497
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4498
0
}
4499
/* }}} */
4500
4501
/* {{{ Get the key of the last element of the array */
4502
PHP_FUNCTION(array_key_last)
4503
0
{
4504
0
  zval *stack;    /* Input stack */
4505
0
  HashPosition pos;
4506
4507
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4508
0
    Z_PARAM_ARRAY(stack)
4509
0
  ZEND_PARSE_PARAMETERS_END();
4510
4511
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4512
0
  zend_hash_internal_pointer_end_ex(target_hash, &pos);
4513
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4514
0
}
4515
/* }}} */
4516
4517
PHP_FUNCTION(array_first)
4518
0
{
4519
0
  HashTable *array;
4520
4521
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4522
0
    Z_PARAM_ARRAY_HT(array)
4523
0
  ZEND_PARSE_PARAMETERS_END();
4524
4525
0
  ZEND_HASH_FOREACH_VAL(array, zval *zv) {
4526
0
    RETURN_COPY_DEREF(zv);
4527
0
  } ZEND_HASH_FOREACH_END();
4528
0
}
4529
4530
PHP_FUNCTION(array_last)
4531
0
{
4532
0
  HashTable *array;
4533
4534
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4535
0
    Z_PARAM_ARRAY_HT(array)
4536
0
  ZEND_PARSE_PARAMETERS_END();
4537
4538
0
  ZEND_HASH_REVERSE_FOREACH_VAL(array, zval *zv) {
4539
0
    RETURN_COPY_DEREF(zv);
4540
0
  } ZEND_HASH_FOREACH_END();
4541
0
}
4542
4543
/* {{{ Return just the values from the input array */
4544
PHP_FUNCTION(array_values)
4545
7
{
4546
7
  zval   *input;    /* Input array */
4547
7
  zend_array *arrval;
4548
7
  zend_long arrlen;
4549
4550
21
  ZEND_PARSE_PARAMETERS_START(1, 1)
4551
28
    Z_PARAM_ARRAY(input)
4552
7
  ZEND_PARSE_PARAMETERS_END();
4553
4554
6
  arrval = Z_ARRVAL_P(input);
4555
4556
  /* Return empty input as is */
4557
6
  arrlen = zend_hash_num_elements(arrval);
4558
6
  if (!arrlen) {
4559
0
    RETURN_EMPTY_ARRAY();
4560
0
  }
4561
4562
  /* Return vector-like packed arrays as-is */
4563
6
  if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4564
6
    arrval->nNextFreeElement == arrlen) {
4565
6
    RETURN_COPY(input);
4566
6
  }
4567
4568
0
  RETURN_ARR(zend_array_to_list(arrval));
4569
0
}
4570
/* }}} */
4571
4572
/* {{{ Return the value as key and the frequency of that value in input as value */
4573
PHP_FUNCTION(array_count_values)
4574
0
{
4575
0
  zval  *input,   /* Input array */
4576
0
      *entry,   /* An entry in the input array */
4577
0
      *tmp;
4578
0
  HashTable *myht;
4579
4580
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4581
0
    Z_PARAM_ARRAY(input)
4582
0
  ZEND_PARSE_PARAMETERS_END();
4583
4584
  /* Initialize return array */
4585
0
  array_init(return_value);
4586
4587
  /* Go through input array and add values to the return array */
4588
0
  myht = Z_ARRVAL_P(input);
4589
0
  ZEND_HASH_FOREACH_VAL(myht, entry) {
4590
0
    ZVAL_DEREF(entry);
4591
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4592
0
      if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4593
0
        zval data;
4594
0
        ZVAL_LONG(&data, 1);
4595
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4596
0
      } else {
4597
0
        Z_LVAL_P(tmp)++;
4598
0
      }
4599
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4600
0
      if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4601
0
        zval data;
4602
0
        ZVAL_LONG(&data, 1);
4603
0
        zend_symtable_add_new(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4604
0
      } else {
4605
0
        Z_LVAL_P(tmp)++;
4606
0
      }
4607
0
    } else {
4608
0
      php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4609
0
    }
4610
0
  } ZEND_HASH_FOREACH_END();
4611
0
}
4612
/* }}} */
4613
4614
static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, bool is_string_key, void **cache_slot, zval *rv) /* {{{ */
4615
0
{
4616
0
  zval *prop = NULL;
4617
4618
0
  if (Z_TYPE_P(data) == IS_OBJECT) {
4619
0
    zend_string *tmp_str;
4620
    /* If name is an integer convert integer to string */
4621
0
    if (name_str == NULL) {
4622
0
      tmp_str = zend_long_to_str(name_long);
4623
0
    } else {
4624
0
      tmp_str = zend_string_copy(name_str);
4625
0
    }
4626
    /* The has_property check is first performed in "exists" mode (which returns true for
4627
     * properties that are null but exist) and then in "has" mode to handle objects that
4628
     * implement __isset (which is not called in "exists" mode). */
4629
0
    if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4630
0
        || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4631
0
      prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4632
0
      if (prop) {
4633
0
        ZVAL_DEREF(prop);
4634
0
        if (prop != rv) {
4635
0
          Z_TRY_ADDREF_P(prop);
4636
0
        }
4637
0
      }
4638
0
    }
4639
0
    zend_string_release(tmp_str);
4640
0
  } else if (Z_TYPE_P(data) == IS_ARRAY) {
4641
0
    if (is_string_key) {
4642
0
      prop = zend_hash_find(Z_ARRVAL_P(data), name_str);
4643
0
    } else {
4644
0
      prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4645
0
    }
4646
0
    if (prop) {
4647
0
      ZVAL_DEREF(prop);
4648
0
      Z_TRY_ADDREF_P(prop);
4649
0
    }
4650
0
  }
4651
4652
0
  return prop;
4653
0
}
4654
/* }}} */
4655
4656
/* {{{ Return the values from a single column in the input array, identified by the
4657
   value_key and optionally indexed by the index_key */
4658
PHP_FUNCTION(array_column)
4659
0
{
4660
0
  HashTable *input;
4661
0
  zval *colval, *data, rv;
4662
0
  zend_string *column_str = NULL;
4663
0
  zend_long column_long = 0;
4664
0
  bool column_is_null = 0;
4665
0
  zend_string *index_str = NULL;
4666
0
  zend_long index_long = 0;
4667
0
  bool index_is_null = 1;
4668
4669
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
4670
0
    Z_PARAM_ARRAY_HT(input)
4671
0
    Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4672
0
    Z_PARAM_OPTIONAL
4673
0
    Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4674
0
  ZEND_PARSE_PARAMETERS_END();
4675
4676
0
  uint32_t num_elements = zend_hash_num_elements(input);
4677
0
  if (num_elements == 0) {
4678
0
    RETURN_EMPTY_ARRAY();
4679
0
  }
4680
4681
0
  if (column_is_null && index_is_null) {
4682
0
    array_init_size(return_value, num_elements);
4683
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4684
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4685
0
      ZEND_HASH_FOREACH_VAL(input, data) {
4686
0
        ZVAL_DEREF(data);
4687
0
        Z_TRY_ADDREF_P(data);
4688
0
        ZEND_HASH_FILL_ADD(data);
4689
0
      } ZEND_HASH_FOREACH_END();
4690
0
    } ZEND_HASH_FILL_END();
4691
0
    return;
4692
0
  }
4693
4694
  /* Normalize array keys once, retaining the original names for object properties. */
4695
0
  zend_ulong column_index = (zend_ulong) column_long;
4696
0
  bool column_is_string_key = column_str && !ZEND_HANDLE_NUMERIC(column_str, column_index);
4697
0
  column_long = (zend_long) column_index;
4698
0
  void *cache_slot_column[3] = { NULL, NULL, NULL };
4699
4700
  /* Index param is not passed */
4701
0
  if (index_is_null) {
4702
0
    array_init_size(return_value, num_elements);
4703
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4704
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4705
0
      ZEND_HASH_FOREACH_VAL(input, data) {
4706
0
        ZVAL_DEREF(data);
4707
0
        if ((colval = array_column_fetch_prop(data, column_str, column_long, column_is_string_key, cache_slot_column, &rv)) == NULL) {
4708
0
          continue;
4709
0
        }
4710
0
        ZEND_HASH_FILL_ADD(colval);
4711
0
      } ZEND_HASH_FOREACH_END();
4712
0
    } ZEND_HASH_FILL_END();
4713
0
  } else {
4714
0
    zend_ulong index = (zend_ulong) index_long;
4715
0
    bool index_is_string_key = index_str && !ZEND_HANDLE_NUMERIC(index_str, index);
4716
0
    index_long = (zend_long) index;
4717
0
    void *cache_slot_index[3] = { NULL, NULL, NULL };
4718
4719
0
    array_init_size(return_value, num_elements);
4720
0
    ZEND_HASH_FOREACH_VAL(input, data) {
4721
0
      ZVAL_DEREF(data);
4722
4723
0
      if (column_is_null) {
4724
0
        Z_TRY_ADDREF_P(data);
4725
0
        colval = data;
4726
0
      } else if ((colval = array_column_fetch_prop(data, column_str, column_long, column_is_string_key, cache_slot_column, &rv)) == NULL) {
4727
0
        continue;
4728
0
      }
4729
4730
0
      zval rv;
4731
0
      zval *keyval = array_column_fetch_prop(data, index_str, index_long, index_is_string_key, cache_slot_index, &rv);
4732
0
      if (keyval) {
4733
0
        array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4734
0
        zval_ptr_dtor(colval);
4735
0
        zval_ptr_dtor(keyval);
4736
0
      } else {
4737
0
        zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4738
0
      }
4739
0
    } ZEND_HASH_FOREACH_END();
4740
0
  }
4741
0
}
4742
/* }}} */
4743
4744
/* {{{ Return input as a new array with the order of the entries reversed */
4745
PHP_FUNCTION(array_reverse)
4746
6
{
4747
6
  zval   *input,        /* Input array */
4748
6
       *entry;        /* An entry in the input array */
4749
6
  zend_string *string_key;
4750
6
  zend_ulong    num_key;
4751
6
  bool preserve_keys = 0; /* whether to preserve keys */
4752
4753
18
  ZEND_PARSE_PARAMETERS_START(1, 2)
4754
24
    Z_PARAM_ARRAY(input)
4755
5
    Z_PARAM_OPTIONAL
4756
10
    Z_PARAM_BOOL(preserve_keys)
4757
6
  ZEND_PARSE_PARAMETERS_END();
4758
4759
  /* Initialize return array */
4760
5
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4761
5
  if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4762
5
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4763
5
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4764
50
      ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4765
50
        if (UNEXPECTED(Z_ISREF_P(entry) &&
4766
50
          Z_REFCOUNT_P(entry) == 1)) {
4767
12
          entry = Z_REFVAL_P(entry);
4768
12
        }
4769
50
        Z_TRY_ADDREF_P(entry);
4770
50
        ZEND_HASH_FILL_ADD(entry);
4771
50
      } ZEND_HASH_FOREACH_END();
4772
5
    } ZEND_HASH_FILL_END();
4773
5
  } else {
4774
0
    ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4775
0
      if (string_key) {
4776
0
        entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4777
0
      } else {
4778
0
        if (preserve_keys) {
4779
0
          entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4780
0
        } else {
4781
0
          entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4782
0
        }
4783
0
      }
4784
0
      zval_add_ref(entry);
4785
0
    } ZEND_HASH_FOREACH_END();
4786
0
  }
4787
5
}
4788
/* }}} */
4789
4790
/* Releases references taken up-front for pad values that were not inserted. */
4791
static zend_always_inline void php_array_pad_release(zval *pad_value, zend_long num_pads)
4792
0
{
4793
0
  if (Z_REFCOUNTED_P(pad_value)) {
4794
0
    GC_DELREF_EX(Z_COUNTED_P(pad_value), num_pads);
4795
0
  }
4796
0
}
4797
4798
/* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4799
PHP_FUNCTION(array_pad)
4800
0
{
4801
0
  zval  *input;   /* Input array */
4802
0
  zval  *pad_value; /* Padding value obviously */
4803
0
  zend_long pad_size;   /* Size to pad to */
4804
0
  zend_long pad_size_abs; /* Absolute value of pad_size */
4805
0
  zend_long input_size;   /* Size of the input array */
4806
0
  zend_long num_pads;   /* How many pads do we need */
4807
0
  zend_long i;
4808
0
  zend_string *key;
4809
0
  zval *value;
4810
4811
0
  ZEND_PARSE_PARAMETERS_START(3, 3)
4812
0
    Z_PARAM_ARRAY(input)
4813
0
    Z_PARAM_LONG(pad_size)
4814
0
    Z_PARAM_ZVAL(pad_value)
4815
0
  ZEND_PARSE_PARAMETERS_END();
4816
4817
0
  if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4818
0
    zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4819
0
    RETURN_THROWS();
4820
0
  }
4821
4822
  /* Do some initial calculations */
4823
0
  input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4824
0
  pad_size_abs = ZEND_ABS(pad_size);
4825
4826
0
  if (input_size >= pad_size_abs) {
4827
    /* Copy the original array */
4828
0
    ZVAL_COPY(return_value, input);
4829
0
    return;
4830
0
  }
4831
4832
0
  num_pads = pad_size_abs - input_size;
4833
0
  if (Z_REFCOUNTED_P(pad_value)) {
4834
0
    GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4835
0
  }
4836
4837
0
  array_init_size(return_value, pad_size_abs);
4838
0
  if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4839
0
    if (UNEXPECTED(zend_hash_real_finit_packed(Z_ARRVAL_P(return_value), /* fallible */ true) == FAILURE)) {
4840
0
      php_array_pad_release(pad_value, num_pads);
4841
0
      RETURN_OOM();
4842
0
    }
4843
4844
0
    if (pad_size < 0) {
4845
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4846
0
        for (i = 0; i < num_pads; i++) {
4847
0
          ZEND_HASH_FILL_ADD(pad_value);
4848
0
        }
4849
0
      } ZEND_HASH_FILL_END();
4850
0
    }
4851
4852
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4853
0
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4854
0
        Z_TRY_ADDREF_P(value);
4855
0
        ZEND_HASH_FILL_ADD(value);
4856
0
      } ZEND_HASH_FOREACH_END();
4857
0
    } ZEND_HASH_FILL_END();
4858
4859
0
    if (pad_size > 0) {
4860
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4861
0
        for (i = 0; i < num_pads; i++) {
4862
0
          ZEND_HASH_FILL_ADD(pad_value);
4863
0
        }
4864
0
      } ZEND_HASH_FILL_END();
4865
0
    }
4866
0
  } else {
4867
0
    if (pad_size < 0) {
4868
0
      for (i = 0; i < num_pads; i++) {
4869
0
        if (UNEXPECTED(zend_hash_next_index_finsert_new(Z_ARRVAL_P(return_value), pad_value) == HASH_NOMEM)) {
4870
0
          php_array_pad_release(pad_value, num_pads - i);
4871
0
          RETURN_OOM();
4872
0
        }
4873
0
      }
4874
0
    }
4875
4876
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4877
0
      Z_TRY_ADDREF_P(value);
4878
0
      zval *result;
4879
0
      if (key) {
4880
0
        result = zend_hash_fadd_new(Z_ARRVAL_P(return_value), key, value);
4881
0
      } else {
4882
0
        result = zend_hash_next_index_finsert_new(Z_ARRVAL_P(return_value), value);
4883
0
      }
4884
0
      if (UNEXPECTED(result == HASH_NOMEM)) {
4885
0
        Z_TRY_DELREF_P(value);
4886
0
        if (pad_size > 0) {
4887
0
          php_array_pad_release(pad_value, num_pads);
4888
0
        }
4889
0
        RETURN_OOM();
4890
0
      }
4891
0
    } ZEND_HASH_FOREACH_END();
4892
4893
0
    if (pad_size > 0) {
4894
0
      for (i = 0; i < num_pads; i++) {
4895
0
        if (UNEXPECTED(zend_hash_next_index_finsert_new(Z_ARRVAL_P(return_value), pad_value) == HASH_NOMEM)) {
4896
0
          php_array_pad_release(pad_value, num_pads - i);
4897
0
          RETURN_OOM();
4898
0
        }
4899
0
      }
4900
0
    }
4901
0
  }
4902
0
}
4903
/* }}} */
4904
4905
/* {{{ Return array with key <-> value flipped */
4906
PHP_FUNCTION(array_flip)
4907
0
{
4908
0
  zval *array, *entry, data;
4909
0
  zend_ulong num_idx;
4910
0
  zend_string *str_idx;
4911
4912
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4913
0
    Z_PARAM_ARRAY(array)
4914
0
  ZEND_PARSE_PARAMETERS_END();
4915
4916
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4917
4918
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4919
0
    ZVAL_DEREF(entry);
4920
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4921
0
      if (str_idx) {
4922
0
        ZVAL_STR_COPY(&data, str_idx);
4923
0
      } else {
4924
0
        ZVAL_LONG(&data, num_idx);
4925
0
      }
4926
0
      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4927
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4928
0
      if (str_idx) {
4929
0
        ZVAL_STR_COPY(&data, str_idx);
4930
0
      } else {
4931
0
        ZVAL_LONG(&data, num_idx);
4932
0
      }
4933
0
      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4934
0
    } else {
4935
0
      php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4936
0
    }
4937
0
  } ZEND_HASH_FOREACH_END();
4938
0
}
4939
/* }}} */
4940
4941
/* {{{ Returns an array with all string keys lowercased [or uppercased] */
4942
PHP_FUNCTION(array_change_key_case)
4943
0
{
4944
0
  zval *array, *entry;
4945
0
  zend_string *string_key;
4946
0
  zend_string *new_key;
4947
0
  zend_ulong num_key;
4948
0
  zend_long change_to_upper = PHP_CASE_LOWER;
4949
4950
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
4951
0
    Z_PARAM_ARRAY(array)
4952
0
    Z_PARAM_OPTIONAL
4953
0
    Z_PARAM_LONG(change_to_upper)
4954
0
  ZEND_PARSE_PARAMETERS_END();
4955
4956
0
  if (change_to_upper != PHP_CASE_LOWER && change_to_upper != PHP_CASE_UPPER) {
4957
0
    zend_argument_value_error(2, "must be either CASE_LOWER or CASE_UPPER");
4958
0
    RETURN_THROWS();
4959
0
  }
4960
4961
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4962
4963
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4964
0
    if (!string_key) {
4965
0
      entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4966
0
    } else {
4967
0
      if (change_to_upper == PHP_CASE_UPPER) {
4968
0
        new_key = zend_string_toupper(string_key);
4969
0
      } else {
4970
0
        new_key = zend_string_tolower(string_key);
4971
0
      }
4972
0
      entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4973
0
      zend_string_release_ex(new_key, 0);
4974
0
    }
4975
4976
0
    zval_add_ref(entry);
4977
0
  } ZEND_HASH_FOREACH_END();
4978
0
}
4979
/* }}} */
4980
4981
struct bucketindex {
4982
  Bucket b;
4983
  unsigned int i;
4984
};
4985
4986
static void array_bucketindex_swap(void *p, void *q)
4987
0
{
4988
0
  struct bucketindex *f = (struct bucketindex *)p;
4989
0
  struct bucketindex *g = (struct bucketindex *)q;
4990
0
  struct bucketindex t;
4991
0
  t = *f;
4992
0
  *f = *g;
4993
0
  *g = t;
4994
0
}
4995
4996
/* {{{ Removes duplicate values from array */
4997
PHP_FUNCTION(array_unique)
4998
0
{
4999
0
  zval *array;
5000
0
  Bucket *p;
5001
0
  zend_long sort_type = PHP_SORT_STRING;
5002
0
  bucket_compare_func_t cmp;
5003
0
  struct bucketindex *arTmp, *cmpdata, *lastkept;
5004
0
  uint32_t i, idx;
5005
5006
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
5007
0
    Z_PARAM_ARRAY(array)
5008
0
    Z_PARAM_OPTIONAL
5009
0
    Z_PARAM_LONG(sort_type)
5010
0
  ZEND_PARSE_PARAMETERS_END();
5011
5012
0
  if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
5013
0
    ZVAL_COPY(return_value, array);
5014
0
    return;
5015
0
  }
5016
5017
0
  if (sort_type == PHP_SORT_STRING) {
5018
0
    HashTable seen;
5019
0
    zend_long num_key;
5020
0
    zend_string *str_key;
5021
0
    zval *val;
5022
5023
0
    zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
5024
0
    array_init(return_value);
5025
5026
0
    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
5027
0
      zval *retval;
5028
0
      if (Z_TYPE_P(val) == IS_STRING) {
5029
0
        retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
5030
0
      } else {
5031
0
        zend_string *tmp_str_val;
5032
0
        zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
5033
0
        retval = zend_hash_add_empty_element(&seen, str_val);
5034
0
        zend_tmp_string_release(tmp_str_val);
5035
0
      }
5036
5037
0
      if (retval) {
5038
        /* First occurrence of the value */
5039
0
        if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
5040
0
          ZVAL_DEREF(val);
5041
0
        }
5042
0
        Z_TRY_ADDREF_P(val);
5043
5044
0
        if (str_key) {
5045
0
          zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
5046
0
        } else {
5047
0
          zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
5048
0
        }
5049
0
      }
5050
0
    } ZEND_HASH_FOREACH_END();
5051
5052
0
    zend_hash_destroy(&seen);
5053
0
    return;
5054
0
  }
5055
5056
0
  cmp = php_get_data_compare_func_unstable(sort_type, false);
5057
5058
0
  bool in_place = zend_may_modify_arg_in_place(array);
5059
0
  if (in_place) {
5060
0
    RETVAL_ARR(Z_ARRVAL_P(array));
5061
0
  } else {
5062
0
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
5063
0
  }
5064
5065
  /* create and sort array with pointers to the target_hash buckets */
5066
0
  arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5067
0
  if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
5068
0
    zval *zv = Z_ARRVAL_P(array)->arPacked;
5069
0
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
5070
0
      if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5071
0
      ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
5072
0
      arTmp[i].b.h = idx;
5073
0
      arTmp[i].b.key = NULL;
5074
0
      arTmp[i].i = i;
5075
0
      i++;
5076
0
    }
5077
0
  } else {
5078
0
    p = Z_ARRVAL_P(array)->arData;
5079
0
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
5080
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
5081
0
      arTmp[i].b = *p;
5082
0
      arTmp[i].i = i;
5083
0
      i++;
5084
0
    }
5085
0
  }
5086
0
  ZVAL_UNDEF(&arTmp[i].b.val);
5087
0
  zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
5088
0
      (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
5089
5090
0
  if (UNEXPECTED(EG(exception))) {
5091
0
    goto out;
5092
0
  }
5093
5094
  /* go through the sorted array and delete duplicates from the copy */
5095
0
  lastkept = arTmp;
5096
0
  for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
5097
0
    if (cmp(&lastkept->b, &cmpdata->b)) {
5098
0
      lastkept = cmpdata;
5099
0
    } else {
5100
0
      if (lastkept->i > cmpdata->i) {
5101
0
        p = &lastkept->b;
5102
0
        lastkept = cmpdata;
5103
0
      } else {
5104
0
        p = &cmpdata->b;
5105
0
      }
5106
0
      if (p->key == NULL) {
5107
0
        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5108
0
      } else {
5109
0
        zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5110
0
      }
5111
0
    }
5112
0
  }
5113
5114
0
out:
5115
0
  pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5116
5117
0
  if (in_place) {
5118
0
    Z_ADDREF_P(return_value);
5119
0
  }
5120
0
}
5121
/* }}} */
5122
5123
static int zval_compare(zval *first, zval *second) /* {{{ */
5124
0
{
5125
0
  return string_compare_function(first, second);
5126
0
}
5127
/* }}} */
5128
5129
static int zval_user_compare(zval *a, zval *b) /* {{{ */
5130
0
{
5131
0
  zval args[2];
5132
0
  zval retval;
5133
5134
0
  ZVAL_COPY_VALUE(&args[0], a);
5135
0
  ZVAL_COPY_VALUE(&args[1], b);
5136
5137
0
  BG(user_compare_fci).param_count = 2;
5138
0
  BG(user_compare_fci).params = args;
5139
0
  BG(user_compare_fci).retval = &retval;
5140
5141
0
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
5142
0
  zend_long ret = php_get_long(&retval);
5143
0
  return ZEND_NORMALIZE_BOOL(ret);
5144
0
}
5145
/* }}} */
5146
5147
static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5148
0
{
5149
0
  uint32_t argc, i;
5150
0
  zval *args;
5151
0
  int (*intersect_data_compare_func)(zval *, zval *) = NULL;
5152
0
  bool ok;
5153
0
  zval *val, *data;
5154
0
  char *param_spec;
5155
0
  zend_string *key;
5156
0
  zend_ulong h;
5157
5158
  /* Get the argument count */
5159
0
  argc = ZEND_NUM_ARGS();
5160
0
  if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5161
    /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
5162
0
    param_spec = "+f";
5163
0
    intersect_data_compare_func = zval_user_compare;
5164
0
  } else {
5165
    /*  INTERSECT_COMP_DATA_NONE - array_intersect_key()
5166
      INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
5167
0
    param_spec = "+";
5168
5169
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5170
0
      intersect_data_compare_func = zval_compare;
5171
0
    }
5172
0
  }
5173
5174
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5175
0
    RETURN_THROWS();
5176
0
  }
5177
5178
0
  for (i = 0; i < argc; i++) {
5179
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5180
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5181
0
      RETURN_THROWS();
5182
0
    }
5183
0
  }
5184
5185
0
  array_init(return_value);
5186
5187
  /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
5188
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5189
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5190
0
      val = Z_REFVAL_P(val);
5191
0
    }
5192
0
    if (key == NULL) {
5193
0
      ok = true;
5194
0
      for (i = 1; i < argc; i++) {
5195
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
5196
0
          (intersect_data_compare_func &&
5197
0
          intersect_data_compare_func(val, data) != 0)
5198
0
        ) {
5199
0
          ok = false;
5200
0
          break;
5201
0
        }
5202
0
      }
5203
0
      if (ok) {
5204
0
        Z_TRY_ADDREF_P(val);
5205
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5206
0
      }
5207
0
    } else {
5208
0
      ok = true;
5209
0
      for (i = 1; i < argc; i++) {
5210
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5211
0
          (intersect_data_compare_func &&
5212
0
          intersect_data_compare_func(val, data) != 0)
5213
0
        ) {
5214
0
          ok = false;
5215
0
          break;
5216
0
        }
5217
0
      }
5218
0
      if (ok) {
5219
0
        Z_TRY_ADDREF_P(val);
5220
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5221
0
      }
5222
0
    }
5223
0
  } ZEND_HASH_FOREACH_END();
5224
0
}
5225
/* }}} */
5226
5227
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5228
0
{
5229
0
  zval *args = NULL;
5230
0
  HashTable *hash;
5231
0
  uint32_t arr_argc, i;
5232
0
  int c = 0;
5233
0
  uint32_t idx;
5234
0
  Bucket **lists, *list, **ptrs, *p;
5235
0
  char *param_spec;
5236
0
  zend_fcall_info fci1, fci2;
5237
0
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5238
0
  zend_fcall_info *fci_key = NULL, *fci_data;
5239
0
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5240
0
  PHP_ARRAY_CMP_FUNC_VARS;
5241
0
  bool in_place = false;
5242
5243
0
  bucket_compare_func_t intersect_key_compare_func;
5244
0
  bucket_compare_func_t intersect_data_compare_func;
5245
5246
0
  if (behavior == INTERSECT_NORMAL) {
5247
0
    intersect_key_compare_func = php_array_key_compare_string;
5248
5249
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5250
      /* array_intersect() */
5251
0
      param_spec = "+";
5252
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5253
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5254
      /* array_uintersect() */
5255
0
      param_spec = "+f";
5256
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5257
0
    } else {
5258
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5259
0
      return;
5260
0
    }
5261
5262
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5263
0
      RETURN_THROWS();
5264
0
    }
5265
0
    fci_data = &fci1;
5266
0
    fci_data_cache = &fci1_cache;
5267
5268
0
  } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5269
    /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5270
     * no comparison of the data is done (part of INTERSECT_ASSOC) */
5271
5272
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5273
      /* array_intersect_assoc() or array_intersect_key() */
5274
0
      param_spec = "+";
5275
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5276
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5277
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5278
      /* array_uintersect_assoc() */
5279
0
      param_spec = "+f";
5280
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5281
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5282
0
      fci_data = &fci1;
5283
0
      fci_data_cache = &fci1_cache;
5284
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5285
      /* array_intersect_uassoc() or array_intersect_ukey() */
5286
0
      param_spec = "+f";
5287
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5288
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5289
0
      fci_key = &fci1;
5290
0
      fci_key_cache = &fci1_cache;
5291
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5292
      /* array_uintersect_uassoc() */
5293
0
      param_spec = "+ff";
5294
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5295
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5296
0
      fci_data = &fci1;
5297
0
      fci_data_cache = &fci1_cache;
5298
0
      fci_key = &fci2;
5299
0
      fci_key_cache = &fci2_cache;
5300
0
    } else {
5301
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5302
0
      return;
5303
0
    }
5304
5305
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5306
0
      RETURN_THROWS();
5307
0
    }
5308
5309
0
  } else {
5310
0
    ZEND_ASSERT(0 && "Invalid behavior");
5311
0
    return;
5312
0
  }
5313
5314
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
5315
5316
  /* for each argument, create and sort list with pointers to the hash buckets */
5317
0
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5318
0
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5319
5320
0
  if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5321
0
    BG(user_compare_fci) = *fci_data;
5322
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5323
0
  } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5324
0
    BG(user_compare_fci) = *fci_key;
5325
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5326
0
  }
5327
5328
0
  for (i = 0; i < arr_argc; i++) {
5329
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5330
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5331
0
      arr_argc = i; /* only free up to i - 1 */
5332
0
      goto out;
5333
0
    }
5334
0
    hash = Z_ARRVAL(args[i]);
5335
0
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5336
0
    lists[i] = list;
5337
0
    ptrs[i] = list;
5338
0
    if (HT_IS_PACKED(hash)) {
5339
0
      zval *zv = hash->arPacked;
5340
0
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5341
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5342
0
        ZVAL_COPY_VALUE(&list->val, zv);
5343
0
        list->h = idx;
5344
0
        list->key = NULL;
5345
0
        list++;
5346
0
      }
5347
0
    } else {
5348
0
      p = hash->arData;
5349
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5350
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5351
0
        *list++ = *p;
5352
0
      }
5353
0
    }
5354
0
    ZVAL_UNDEF(&list->val);
5355
0
    if (hash->nNumOfElements > 1) {
5356
0
      if (behavior == INTERSECT_NORMAL) {
5357
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5358
0
            sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5359
0
            (swap_func_t)zend_hash_bucket_swap);
5360
0
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5361
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5362
0
            sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5363
0
            (swap_func_t)zend_hash_bucket_swap);
5364
0
      }
5365
0
    }
5366
0
  }
5367
5368
  /* copy the argument array if necessary */
5369
0
  in_place = zend_may_modify_arg_in_place(&args[0]);
5370
0
  if (in_place) {
5371
0
    RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5372
0
  } else {
5373
0
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5374
0
  }
5375
5376
  /* go through the lists and look for common values */
5377
0
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5378
0
    if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5379
0
      && key_compare_type == INTERSECT_COMP_KEY_USER) {
5380
0
      BG(user_compare_fci) = *fci_key;
5381
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5382
0
    }
5383
5384
0
    for (i = 1; i < arr_argc; i++) {
5385
0
      if (behavior & INTERSECT_NORMAL) {
5386
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5387
0
          ptrs[i]++;
5388
0
        }
5389
0
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5390
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5391
0
          ptrs[i]++;
5392
0
        }
5393
0
        if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5394
          /* this means that ptrs[i] is not NULL so we can compare
5395
           * and "c==0" is from last operation
5396
           * in this branch of code we enter only when INTERSECT_ASSOC
5397
           * since when we have INTERSECT_KEY compare of data is not wanted. */
5398
0
          if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5399
0
            BG(user_compare_fci) = *fci_data;
5400
0
            BG(user_compare_fci_cache) = *fci_data_cache;
5401
0
          }
5402
0
          if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5403
0
            c = 1;
5404
0
            if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5405
0
              BG(user_compare_fci) = *fci_key;
5406
0
              BG(user_compare_fci_cache) = *fci_key_cache;
5407
              /* When KEY_USER, the last parameter is always the callback */
5408
0
            }
5409
            /* we are going to the break */
5410
0
          } else {
5411
            /* continue looping */
5412
0
          }
5413
0
        }
5414
0
      }
5415
0
      if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5416
        /* delete any values corresponding to remains of ptrs[0] */
5417
        /* and exit because they do not present in at least one of */
5418
        /* the other arguments */
5419
0
        for (;;) {
5420
0
          p = ptrs[0]++;
5421
0
          if (Z_TYPE(p->val) == IS_UNDEF) {
5422
0
            goto out;
5423
0
          }
5424
0
          if (p->key == NULL) {
5425
0
            zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5426
0
          } else {
5427
0
            zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5428
0
          }
5429
0
        }
5430
0
      }
5431
0
      if (c) /* here we get if not all are equal */
5432
0
        break;
5433
0
      ptrs[i]++;
5434
0
    }
5435
0
    if (c) {
5436
      /* Value of ptrs[0] not in all arguments, delete all entries */
5437
      /* with value < value of ptrs[i] */
5438
0
      for (;;) {
5439
0
        p = ptrs[0];
5440
0
        if (p->key == NULL) {
5441
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5442
0
        } else {
5443
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5444
0
        }
5445
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5446
0
          goto out;
5447
0
        }
5448
0
        if (behavior == INTERSECT_NORMAL) {
5449
0
          if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5450
0
            break;
5451
0
          }
5452
0
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5453
          /* no need of looping because indexes are unique */
5454
0
          break;
5455
0
        }
5456
0
      }
5457
0
    } else {
5458
      /* ptrs[0] is present in all the arguments */
5459
      /* Skip all entries with same value as ptrs[0] */
5460
0
      for (;;) {
5461
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5462
0
          goto out;
5463
0
        }
5464
0
        if (behavior == INTERSECT_NORMAL) {
5465
0
          if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5466
0
            break;
5467
0
          }
5468
0
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5469
          /* no need of looping because indexes are unique */
5470
0
          break;
5471
0
        }
5472
0
      }
5473
0
    }
5474
0
  }
5475
0
out:
5476
0
  for (i = 0; i < arr_argc; i++) {
5477
0
    hash = Z_ARRVAL(args[i]);
5478
0
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5479
0
  }
5480
5481
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
5482
5483
0
  efree(ptrs);
5484
0
  efree(lists);
5485
5486
0
  if (in_place) {
5487
0
    Z_ADDREF_P(return_value);
5488
0
  }
5489
0
}
5490
/* }}} */
5491
5492
/* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data. */
5493
PHP_FUNCTION(array_intersect_key)
5494
0
{
5495
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5496
0
}
5497
/* }}} */
5498
5499
/* {{{ Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data. */
5500
PHP_FUNCTION(array_intersect_ukey)
5501
0
{
5502
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5503
0
}
5504
/* }}} */
5505
5506
static zend_always_inline bool php_array_intersect_get_key(
5507
    zval *value, zend_ulong *num_key, zend_string **str_key, zend_string **tmp_key)
5508
0
{
5509
0
  ZVAL_DEREF(value);
5510
0
  *tmp_key = NULL;
5511
5512
0
  if (Z_TYPE_P(value) == IS_LONG) {
5513
0
    *num_key = (zend_ulong) Z_LVAL_P(value);
5514
0
    *str_key = NULL;
5515
0
    return true;
5516
0
  }
5517
5518
0
  if (Z_TYPE_P(value) == IS_STRING) {
5519
0
    *str_key = Z_STR_P(value);
5520
0
    return true;
5521
0
  }
5522
5523
0
  *str_key = zval_try_get_tmp_string(value, tmp_key);
5524
0
  return *str_key != NULL;
5525
0
}
5526
5527
static zend_always_inline void php_array_intersect_empty_result(zval *first, zval *return_value)
5528
0
{
5529
0
  HashTable *result;
5530
0
  bool in_place = zend_may_modify_arg_in_place(first);
5531
5532
0
  if (in_place) {
5533
0
    result = Z_ARRVAL_P(first);
5534
0
    ZVAL_ARR(return_value, result);
5535
0
  } else {
5536
0
    result = zend_array_dup(Z_ARRVAL_P(first));
5537
0
    ZVAL_ARR(return_value, result);
5538
0
  }
5539
5540
0
  ZEND_HASH_FOREACH_KEY(result, zend_ulong num_key, zend_string *key) {
5541
0
    if (key) {
5542
0
      zend_hash_del(result, key);
5543
0
    } else {
5544
0
      zend_hash_index_del(result, num_key);
5545
0
    }
5546
0
  } ZEND_HASH_FOREACH_END();
5547
5548
0
  if (in_place) {
5549
0
    Z_ADDREF_P(return_value);
5550
0
  }
5551
0
}
5552
5553
/* {{{ Hash-based implementation of array_intersect(). Values are compared
5554
 * using their string representation. On the long|string domain, this is
5555
 * exactly key equality under symtable normalization: a long and a string
5556
 * compare equal iff the string is the canonical decimal representation of the
5557
 * long, which is precisely when ZEND_HANDLE_NUMERIC converts it to that long
5558
 * key. Other values are converted to string before the same normalization. */
5559
static zend_never_inline void php_array_intersect_hash(zval *args, uint32_t argc, zval *return_value)
5560
0
{
5561
0
  for (uint32_t i = 0; i < argc; i++) {
5562
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5563
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5564
0
      return;
5565
0
    }
5566
0
  }
5567
5568
  /* An empty argument makes the intersection empty, so no values need to be
5569
   * converted to string. */
5570
0
  for (uint32_t i = 0; i < argc; i++) {
5571
0
    if (zend_hash_num_elements(Z_ARRVAL(args[i])) == 0) {
5572
0
      php_array_intersect_empty_result(&args[0], return_value);
5573
0
      return;
5574
0
    }
5575
0
  }
5576
5577
  /* Map each value of args[1] to the number of consecutive arguments,
5578
   * starting from args[1], the value has been seen in. */
5579
0
  zval one;
5580
0
  ZVAL_LONG(&one, 1);
5581
0
  HashTable set;
5582
0
  zend_hash_init(&set, zend_hash_num_elements(Z_ARRVAL(args[1])), NULL, NULL, 0);
5583
0
  zend_bitset delete_bitset = NULL;
5584
0
  ALLOCA_FLAG(use_heap);
5585
0
  bool in_place = false;
5586
5587
0
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[1]), zval *value) {
5588
0
    zend_ulong value_num_key = 0;
5589
0
    zend_string *value_str_key, *tmp_key;
5590
0
    if (!php_array_intersect_get_key(value, &value_num_key, &value_str_key, &tmp_key)) {
5591
0
      goto cleanup;
5592
0
    }
5593
0
    if (value_str_key) {
5594
0
      zend_symtable_update(&set, value_str_key, &one);
5595
0
    } else {
5596
0
      zend_hash_index_update(&set, value_num_key, &one);
5597
0
    }
5598
0
    zend_tmp_string_release(tmp_key);
5599
0
  } ZEND_HASH_FOREACH_END();
5600
5601
0
  for (uint32_t i = 2; i < argc; i++) {
5602
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), zval *value) {
5603
0
      zend_ulong value_num_key = 0;
5604
0
      zend_string *value_str_key, *tmp_key;
5605
0
      if (!php_array_intersect_get_key(value, &value_num_key, &value_str_key, &tmp_key)) {
5606
0
        goto cleanup;
5607
0
      }
5608
0
      zval *count;
5609
0
      if (value_str_key) {
5610
0
        count = zend_symtable_find(&set, value_str_key);
5611
0
      } else {
5612
0
        count = zend_hash_index_find(&set, value_num_key);
5613
0
      }
5614
0
      zend_tmp_string_release(tmp_key);
5615
0
      if (count && Z_LVAL_P(count) == (zend_long) i - 1) {
5616
0
        ZVAL_LONG(count, i);
5617
0
      }
5618
0
    } ZEND_HASH_FOREACH_END();
5619
0
  }
5620
5621
  /* Match the generic path by filtering the first argument in place if
5622
   * possible and duplicating it otherwise. In particular, duplication keeps
5623
   * bucket holes whose positions are observable through array_rand(). */
5624
0
  HashTable *result;
5625
0
  in_place = zend_may_modify_arg_in_place(&args[0]);
5626
0
  if (in_place) {
5627
0
    result = Z_ARRVAL(args[0]);
5628
0
    ZVAL_ARR(return_value, result);
5629
0
  } else {
5630
0
    result = zend_array_dup(Z_ARRVAL(args[0]));
5631
0
    ZVAL_ARR(return_value, result);
5632
0
  }
5633
5634
  /* Determine all entries to remove before deleting any. Deleting an entry may
5635
   * invoke a user destructor that changes subsequent string conversions. */
5636
0
  HashTable *scanned_result = result;
5637
0
  uint32_t scanned_num_used = result->nNumUsed;
5638
0
  uint32_t delete_bitset_len = zend_bitset_len(scanned_num_used);
5639
0
  delete_bitset = ZEND_BITSET_ALLOCA(delete_bitset_len, use_heap);
5640
0
  zend_bitset_clear(delete_bitset, delete_bitset_len);
5641
5642
0
  size_t scanned_element_size = ZEND_HASH_ELEMENT_SIZE(scanned_result);
5643
0
  for (uint32_t result_idx = 0; result_idx < scanned_num_used; result_idx++) {
5644
0
    zval *entry = ZEND_HASH_ELEMENT_EX(scanned_result, result_idx, scanned_element_size);
5645
0
    if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
5646
0
      continue;
5647
0
    }
5648
0
    zend_ulong value_num_key = 0;
5649
0
    zend_string *value_str_key, *tmp_key;
5650
0
    if (!php_array_intersect_get_key(entry, &value_num_key, &value_str_key, &tmp_key)) {
5651
0
      goto cleanup;
5652
0
    }
5653
0
    zval *count;
5654
0
    if (value_str_key) {
5655
0
      count = zend_symtable_find(&set, value_str_key);
5656
0
    } else {
5657
0
      count = zend_hash_index_find(&set, value_num_key);
5658
0
    }
5659
0
    zend_tmp_string_release(tmp_key);
5660
0
    if (!count || Z_LVAL_P(count) != (zend_long) argc - 1) {
5661
0
      zend_bitset_incl(delete_bitset, result_idx);
5662
0
    }
5663
0
  }
5664
5665
  /* A conversion may retain the first argument through reentrant user code,
5666
   * so it may no longer be safe to modify the original array in place. */
5667
0
  if (in_place && !zend_may_modify_arg_in_place(&args[0])) {
5668
0
    result = zend_array_dup(Z_ARRVAL(args[0]));
5669
0
    ZVAL_ARR(return_value, result);
5670
0
    in_place = false;
5671
0
  }
5672
5673
  /* The late duplication may compact holes, so read keys from the table whose
5674
   * bucket indexes are stored in the bitset. */
5675
0
  uint32_t result_idx;
5676
0
  ZEND_BITSET_FOREACH(delete_bitset, delete_bitset_len, result_idx) {
5677
0
    if (HT_IS_PACKED(scanned_result)) {
5678
0
      zend_hash_index_del(result, result_idx);
5679
0
    } else {
5680
0
      zval *entry = ZEND_HASH_ELEMENT_EX(scanned_result, result_idx, scanned_element_size);
5681
0
      Bucket *bucket = (Bucket *) entry;
5682
0
      if (bucket->key) {
5683
0
        zend_hash_del(result, bucket->key);
5684
0
      } else {
5685
0
        zend_hash_index_del(result, bucket->h);
5686
0
      }
5687
0
    }
5688
0
  } ZEND_BITSET_FOREACH_END();
5689
5690
0
cleanup:
5691
0
  if (delete_bitset) {
5692
0
    free_alloca(delete_bitset, use_heap);
5693
0
  }
5694
0
  zend_hash_destroy(&set);
5695
0
  if (in_place) {
5696
0
    Z_ADDREF_P(return_value);
5697
0
  }
5698
0
}
5699
/* }}} */
5700
5701
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5702
PHP_FUNCTION(array_intersect)
5703
0
{
5704
0
  zval *args;
5705
0
  uint32_t argc;
5706
5707
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5708
0
    RETURN_THROWS();
5709
0
  }
5710
5711
0
  if (argc >= 2) {
5712
0
    php_array_intersect_hash(args, argc, return_value);
5713
0
    return;
5714
0
  }
5715
5716
  /* Preserve the generic path and its conversion side effects for calls with
5717
   * a single array. */
5718
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5719
0
}
5720
/* }}} */
5721
5722
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */
5723
PHP_FUNCTION(array_uintersect)
5724
0
{
5725
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5726
0
}
5727
/* }}} */
5728
5729
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5730
PHP_FUNCTION(array_intersect_assoc)
5731
0
{
5732
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5733
0
}
5734
/* }}} */
5735
5736
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using a user-supplied callback. */
5737
PHP_FUNCTION(array_intersect_uassoc)
5738
0
{
5739
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5740
0
}
5741
/* }}} */
5742
5743
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using a user-supplied callback. */
5744
PHP_FUNCTION(array_uintersect_assoc)
5745
0
{
5746
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5747
0
}
5748
/* }}} */
5749
5750
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks. */
5751
PHP_FUNCTION(array_uintersect_uassoc)
5752
0
{
5753
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5754
0
}
5755
/* }}} */
5756
5757
static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5758
0
{
5759
0
  uint32_t argc, i;
5760
0
  zval *args;
5761
0
  int (*diff_data_compare_func)(zval *, zval *) = NULL;
5762
0
  bool ok;
5763
0
  zval *val, *data;
5764
0
  zend_string *key;
5765
0
  zend_ulong h;
5766
5767
  /* Get the argument count */
5768
0
  argc = ZEND_NUM_ARGS();
5769
0
  if (data_compare_type == DIFF_COMP_DATA_USER) {
5770
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5771
0
      RETURN_THROWS();
5772
0
    }
5773
0
    diff_data_compare_func = zval_user_compare;
5774
0
  } else {
5775
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5776
0
      RETURN_THROWS();
5777
0
    }
5778
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5779
0
      diff_data_compare_func = zval_compare;
5780
0
    }
5781
0
  }
5782
5783
0
  for (i = 0; i < argc; i++) {
5784
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5785
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5786
0
      RETURN_THROWS();
5787
0
    }
5788
0
  }
5789
5790
0
  array_init(return_value);
5791
5792
  /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5793
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5794
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5795
0
      val = Z_REFVAL_P(val);
5796
0
    }
5797
0
    if (key == NULL) {
5798
0
      ok = true;
5799
0
      for (i = 1; i < argc; i++) {
5800
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5801
0
          (!diff_data_compare_func ||
5802
0
          diff_data_compare_func(val, data) == 0)
5803
0
        ) {
5804
0
          ok = false;
5805
0
          break;
5806
0
        }
5807
0
      }
5808
0
      if (ok) {
5809
0
        Z_TRY_ADDREF_P(val);
5810
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5811
0
      }
5812
0
    } else {
5813
0
      ok = true;
5814
0
      for (i = 1; i < argc; i++) {
5815
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5816
0
          (!diff_data_compare_func ||
5817
0
          diff_data_compare_func(val, data) == 0)
5818
0
        ) {
5819
0
          ok = false;
5820
0
          break;
5821
0
        }
5822
0
      }
5823
0
      if (ok) {
5824
0
        Z_TRY_ADDREF_P(val);
5825
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5826
0
      }
5827
0
    }
5828
0
  } ZEND_HASH_FOREACH_END();
5829
0
}
5830
/* }}} */
5831
5832
static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5833
0
{
5834
0
  zval *args = NULL;
5835
0
  HashTable *hash;
5836
0
  uint32_t arr_argc, i;
5837
0
  int c;
5838
0
  uint32_t idx;
5839
0
  Bucket **lists, *list, **ptrs, *p;
5840
0
  char *param_spec;
5841
0
  zend_fcall_info fci1, fci2;
5842
0
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5843
0
  zend_fcall_info *fci_key = NULL, *fci_data;
5844
0
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5845
0
  PHP_ARRAY_CMP_FUNC_VARS;
5846
5847
0
  bucket_compare_func_t diff_key_compare_func;
5848
0
  bucket_compare_func_t diff_data_compare_func;
5849
5850
0
  if (behavior == DIFF_NORMAL) {
5851
0
    diff_key_compare_func = php_array_key_compare_string_unstable;
5852
5853
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5854
      /* array_diff */
5855
0
      param_spec = "+";
5856
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5857
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5858
      /* array_udiff */
5859
0
      param_spec = "+f";
5860
0
      diff_data_compare_func = php_array_user_compare_unstable;
5861
0
    } else {
5862
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5863
0
      return;
5864
0
    }
5865
5866
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5867
0
      RETURN_THROWS();
5868
0
    }
5869
0
    fci_data = &fci1;
5870
0
    fci_data_cache = &fci1_cache;
5871
5872
0
  } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5873
    /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5874
     * no comparison of the data is done (part of DIFF_ASSOC) */
5875
5876
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5877
      /* array_diff_assoc() or array_diff_key() */
5878
0
      param_spec = "+";
5879
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5880
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5881
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5882
      /* array_udiff_assoc() */
5883
0
      param_spec = "+f";
5884
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5885
0
      diff_data_compare_func = php_array_user_compare_unstable;
5886
0
      fci_data = &fci1;
5887
0
      fci_data_cache = &fci1_cache;
5888
0
    } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5889
      /* array_diff_uassoc() or array_diff_ukey() */
5890
0
      param_spec = "+f";
5891
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5892
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5893
0
      fci_key = &fci1;
5894
0
      fci_key_cache = &fci1_cache;
5895
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5896
      /* array_udiff_uassoc() */
5897
0
      param_spec = "+ff";
5898
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5899
0
      diff_data_compare_func = php_array_user_compare_unstable;
5900
0
      fci_data = &fci1;
5901
0
      fci_data_cache = &fci1_cache;
5902
0
      fci_key = &fci2;
5903
0
      fci_key_cache = &fci2_cache;
5904
0
    } else {
5905
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5906
0
      return;
5907
0
    }
5908
5909
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5910
0
      RETURN_THROWS();
5911
0
    }
5912
5913
0
  } else {
5914
0
    ZEND_ASSERT(0 && "Invalid behavior");
5915
0
    return;
5916
0
  }
5917
5918
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
5919
5920
  /* for each argument, create and sort list with pointers to the hash buckets */
5921
0
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5922
0
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5923
5924
0
  if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5925
0
    BG(user_compare_fci) = *fci_data;
5926
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5927
0
  } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5928
0
    BG(user_compare_fci) = *fci_key;
5929
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5930
0
  }
5931
5932
0
  for (i = 0; i < arr_argc; i++) {
5933
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5934
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5935
0
      arr_argc = i; /* only free up to i - 1 */
5936
0
      goto out;
5937
0
    }
5938
0
    hash = Z_ARRVAL(args[i]);
5939
0
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5940
0
    lists[i] = list;
5941
0
    ptrs[i] = list;
5942
0
    if (HT_IS_PACKED(hash)) {
5943
0
      zval *zv = hash->arPacked;
5944
0
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5945
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5946
0
        ZVAL_COPY_VALUE(&list->val, zv);
5947
0
        list->h = idx;
5948
0
        list->key = NULL;
5949
0
        list++;
5950
0
      }
5951
0
    } else {
5952
0
      p = hash->arData;
5953
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5954
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5955
0
        *list++ = *p;
5956
0
      }
5957
0
    }
5958
0
    ZVAL_UNDEF(&list->val);
5959
0
    if (hash->nNumOfElements > 1) {
5960
0
      if (behavior == DIFF_NORMAL) {
5961
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5962
0
            sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5963
0
            (swap_func_t)zend_hash_bucket_swap);
5964
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5965
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5966
0
            sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5967
0
            (swap_func_t)zend_hash_bucket_swap);
5968
0
      }
5969
0
    }
5970
0
  }
5971
5972
  /* copy the argument array */
5973
0
  RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5974
5975
  /* go through the lists and look for values of ptr[0] that are not in the others */
5976
0
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5977
0
    if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5978
0
      &&
5979
0
      key_compare_type == DIFF_COMP_KEY_USER
5980
0
    ) {
5981
0
      BG(user_compare_fci) = *fci_key;
5982
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5983
0
    }
5984
0
    c = 1;
5985
0
    for (i = 1; i < arr_argc; i++) {
5986
0
      Bucket *ptr = ptrs[i];
5987
0
      if (behavior == DIFF_NORMAL) {
5988
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5989
0
          ptrs[i]++;
5990
0
        }
5991
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5992
0
        while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5993
0
          ptr++;
5994
0
        }
5995
0
      }
5996
0
      if (!c) {
5997
0
        if (behavior == DIFF_NORMAL) {
5998
0
          if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5999
0
            ptrs[i]++;
6000
0
          }
6001
0
          break;
6002
0
        } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
6003
          /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
6004
           * data comparison is not needed - skipped. */
6005
0
          if (Z_TYPE(ptr->val) != IS_UNDEF) {
6006
0
            if (data_compare_type == DIFF_COMP_DATA_USER) {
6007
0
              BG(user_compare_fci) = *fci_data;
6008
0
              BG(user_compare_fci_cache) = *fci_data_cache;
6009
0
            }
6010
0
            if (diff_data_compare_func(ptrs[0], ptr) != 0) {
6011
              /* the data is not the same */
6012
0
              c = -1;
6013
0
              if (key_compare_type == DIFF_COMP_KEY_USER) {
6014
0
                BG(user_compare_fci) = *fci_key;
6015
0
                BG(user_compare_fci_cache) = *fci_key_cache;
6016
0
              }
6017
0
            } else {
6018
0
              break;
6019
              /* we have found the element in other arrays thus we don't want it
6020
               * in the return_value -> delete from there */
6021
0
            }
6022
0
          }
6023
0
        } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
6024
          /* the behavior here differs from INTERSECT_KEY in php_intersect
6025
           * since in the "diff" case we have to remove the entry from
6026
           * return_value while when doing intersection the entry must not
6027
           * be deleted. */
6028
0
          break; /* remove the key */
6029
0
        }
6030
0
      }
6031
0
    }
6032
0
    if (!c) {
6033
      /* ptrs[0] in one of the other arguments */
6034
      /* delete all entries with value as ptrs[0] */
6035
0
      for (;;) {
6036
0
        p = ptrs[0];
6037
0
        if (p->key == NULL) {
6038
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
6039
0
        } else {
6040
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
6041
0
        }
6042
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
6043
0
          goto out;
6044
0
        }
6045
0
        if (behavior == DIFF_NORMAL) {
6046
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
6047
0
            break;
6048
0
          }
6049
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
6050
          /* in this case no array_key_compare is needed */
6051
0
          break;
6052
0
        }
6053
0
      }
6054
0
    } else {
6055
      /* ptrs[0] in none of the other arguments */
6056
      /* skip all entries with value as ptrs[0] */
6057
0
      for (;;) {
6058
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
6059
0
          goto out;
6060
0
        }
6061
0
        if (behavior == DIFF_NORMAL) {
6062
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
6063
0
            break;
6064
0
          }
6065
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
6066
          /* in this case no array_key_compare is needed */
6067
0
          break;
6068
0
        }
6069
0
      }
6070
0
    }
6071
0
  }
6072
0
out:
6073
0
  for (i = 0; i < arr_argc; i++) {
6074
0
    hash = Z_ARRVAL(args[i]);
6075
0
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
6076
0
  }
6077
6078
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
6079
6080
0
  efree(ptrs);
6081
0
  efree(lists);
6082
0
}
6083
/* }}} */
6084
6085
/* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved. */
6086
PHP_FUNCTION(array_diff_key)
6087
0
{
6088
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
6089
0
}
6090
/* }}} */
6091
6092
/* {{{ Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved. */
6093
PHP_FUNCTION(array_diff_ukey)
6094
0
{
6095
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
6096
0
}
6097
/* }}} */
6098
6099
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
6100
PHP_FUNCTION(array_diff)
6101
0
{
6102
0
  zval *args;
6103
0
  uint32_t argc, i;
6104
0
  uint64_t num;
6105
0
  HashTable exclude;
6106
0
  zval *value;
6107
0
  zend_string *str, *tmp_str, *key;
6108
0
  zend_long idx;
6109
0
  zval dummy;
6110
6111
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
6112
0
    Z_PARAM_VARIADIC('+', args, argc)
6113
0
  ZEND_PARSE_PARAMETERS_END();
6114
6115
0
  if (Z_TYPE(args[0]) != IS_ARRAY) {
6116
0
    zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
6117
0
    RETURN_THROWS();
6118
0
  }
6119
6120
0
  num = zend_hash_num_elements(Z_ARRVAL(args[0]));
6121
0
  if (num == 0) {
6122
0
    for (i = 1; i < argc; i++) {
6123
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
6124
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6125
0
        RETURN_THROWS();
6126
0
      }
6127
0
    }
6128
0
    RETURN_EMPTY_ARRAY();
6129
0
  } else if (num == 1) {
6130
0
    int found = 0;
6131
0
    zend_string *search_str, *tmp_search_str;
6132
6133
0
    value = NULL;
6134
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
6135
0
      break;
6136
0
    } ZEND_HASH_FOREACH_END();
6137
6138
0
    if (!value) {
6139
0
      for (i = 1; i < argc; i++) {
6140
0
        if (Z_TYPE(args[i]) != IS_ARRAY) {
6141
0
          zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6142
0
          RETURN_THROWS();
6143
0
        }
6144
0
      }
6145
0
      RETURN_EMPTY_ARRAY();
6146
0
    }
6147
6148
0
    search_str = zval_get_tmp_string(value, &tmp_search_str);
6149
6150
0
    for (i = 1; i < argc; i++) {
6151
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
6152
0
        zend_tmp_string_release(tmp_search_str);
6153
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6154
0
        RETURN_THROWS();
6155
0
      }
6156
0
      if (!found) {
6157
0
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
6158
0
          str = zval_get_tmp_string(value, &tmp_str);
6159
0
          if (zend_string_equals(search_str, str)) {
6160
0
            zend_tmp_string_release(tmp_str);
6161
0
            found = 1;
6162
0
            break;
6163
0
          }
6164
0
          zend_tmp_string_release(tmp_str);
6165
0
        } ZEND_HASH_FOREACH_END();
6166
0
      }
6167
0
    }
6168
6169
0
    zend_tmp_string_release(tmp_search_str);
6170
6171
0
    if (found) {
6172
0
      RETVAL_EMPTY_ARRAY();
6173
0
    } else {
6174
0
      ZVAL_COPY(return_value, &args[0]);
6175
0
    }
6176
0
    return;
6177
0
  }
6178
6179
  /* count number of elements */
6180
0
  num = 0;
6181
0
  for (i = 1; i < argc; i++) {
6182
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
6183
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6184
0
      RETURN_THROWS();
6185
0
    }
6186
0
    num += zend_hash_num_elements(Z_ARRVAL(args[i]));
6187
0
  }
6188
6189
0
  if (num == 0) {
6190
0
    ZVAL_COPY(return_value, &args[0]);
6191
0
    return;
6192
0
  }
6193
6194
0
  if (UNEXPECTED(num >= HT_MAX_SIZE)) {
6195
0
    zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
6196
0
    RETURN_THROWS();
6197
0
  }
6198
6199
0
  ZVAL_NULL(&dummy);
6200
  /* create exclude map */
6201
0
  zend_hash_init(&exclude, num, NULL, NULL, 0);
6202
0
  for (i = 1; i < argc; i++) {
6203
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
6204
0
      str = zval_get_tmp_string(value, &tmp_str);
6205
0
      zend_hash_add(&exclude, str, &dummy);
6206
0
      zend_tmp_string_release(tmp_str);
6207
0
    } ZEND_HASH_FOREACH_END();
6208
0
  }
6209
6210
  /* copy all elements of first array that are not in exclude set */
6211
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
6212
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
6213
0
    str = zval_get_tmp_string(value, &tmp_str);
6214
0
    if (!zend_hash_exists(&exclude, str)) {
6215
0
      if (key) {
6216
0
        value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
6217
0
      } else {
6218
0
        value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
6219
0
      }
6220
0
      zval_add_ref(value);
6221
0
    }
6222
0
    zend_tmp_string_release(tmp_str);
6223
0
  } ZEND_HASH_FOREACH_END();
6224
6225
0
  zend_hash_destroy(&exclude);
6226
0
}
6227
/* }}} */
6228
6229
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function. */
6230
PHP_FUNCTION(array_udiff)
6231
0
{
6232
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
6233
0
}
6234
/* }}} */
6235
6236
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal */
6237
PHP_FUNCTION(array_diff_assoc)
6238
0
{
6239
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
6240
0
}
6241
/* }}} */
6242
6243
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function. */
6244
PHP_FUNCTION(array_diff_uassoc)
6245
0
{
6246
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
6247
0
}
6248
/* }}} */
6249
6250
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function. */
6251
PHP_FUNCTION(array_udiff_assoc)
6252
0
{
6253
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
6254
0
}
6255
/* }}} */
6256
6257
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions. */
6258
PHP_FUNCTION(array_udiff_uassoc)
6259
0
{
6260
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
6261
0
}
6262
/* }}} */
6263
6264
8
#define MULTISORT_ORDER 0
6265
0
#define MULTISORT_TYPE  1
6266
21
#define MULTISORT_LAST  2
6267
6268
PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
6269
0
{
6270
0
  Bucket *ab = *(Bucket **)a;
6271
0
  Bucket *bb = *(Bucket **)b;
6272
0
  int r;
6273
0
  zend_long result;
6274
6275
0
  r = 0;
6276
0
  do {
6277
0
    result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
6278
0
    if (result != 0) {
6279
0
      return result > 0 ? 1 : -1;
6280
0
    }
6281
0
    r++;
6282
0
  } while (Z_TYPE(ab[r].val) != IS_UNDEF);
6283
6284
0
  return stable_sort_fallback(&ab[r], &bb[r]);
6285
0
}
6286
/* }}} */
6287
6288
#define MULTISORT_ABORT       \
6289
1
  efree(func); \
6290
1
  efree(arrays);         \
6291
0
  return;
6292
6293
0
static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
6294
0
  Bucket *t;
6295
0
  Bucket **f = (Bucket **)p;
6296
0
  Bucket **g = (Bucket **)q;
6297
6298
0
  t = *f;
6299
0
  *f = *g;
6300
0
  *g = t;
6301
0
}
6302
/* }}} */
6303
6304
/* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
6305
PHP_FUNCTION(array_multisort)
6306
2
{
6307
2
  zval*     args;
6308
2
  zval**      arrays;
6309
2
  HashTable**   hashes;
6310
2
  Bucket**    indirect;
6311
2
  uint32_t    idx;
6312
2
  HashTable*    hash;
6313
2
  uint32_t    argc;
6314
2
  uint32_t    array_size;
6315
2
  uint32_t    num_arrays = 0;
6316
2
  int       parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
6317
2
  int       sort_order = PHP_SORT_ASC;
6318
2
  int       sort_type  = PHP_SORT_REGULAR;
6319
2
  uint32_t    i, k, n;
6320
2
  bucket_compare_func_t *func;
6321
6322
6
  ZEND_PARSE_PARAMETERS_START(1, -1)
6323
6
    Z_PARAM_VARIADIC('+', args, argc)
6324
6
  ZEND_PARSE_PARAMETERS_END();
6325
6326
  /* Allocate space for storing pointers to input arrays and sort flags. */
6327
2
  arrays = (zval **)ecalloc(argc, sizeof(zval *));
6328
6
  for (i = 0; i < MULTISORT_LAST; i++) {
6329
4
    parse_state[i] = 0;
6330
4
  }
6331
2
  func = ecalloc(argc, sizeof(bucket_compare_func_t));
6332
6333
  /* Here we go through the input arguments and parse them. Each one can
6334
   * be either an array or a sort flag which follows an array. If not
6335
   * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
6336
   * accordingly. There can't be two sort flags of the same type after an
6337
   * array, and the very first argument has to be an array. */
6338
11
  for (i = 0; i < argc; i++) {
6339
10
    zval *arg = &args[i];
6340
6341
10
    ZVAL_DEREF(arg);
6342
10
    if (Z_TYPE_P(arg) == IS_ARRAY) {
6343
5
      SEPARATE_ARRAY(arg);
6344
      /* We see the next array, so we update the sort flags of
6345
       * the previous array and reset the sort flags. */
6346
5
      if (i > 0) {
6347
3
        func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6348
3
        sort_order = PHP_SORT_ASC;
6349
3
        sort_type = PHP_SORT_REGULAR;
6350
3
      }
6351
5
      arrays[num_arrays++] = arg;
6352
6353
      /* Next one may be an array or a list of sort flags. */
6354
15
      for (k = 0; k < MULTISORT_LAST; k++) {
6355
10
        parse_state[k] = 1;
6356
10
      }
6357
5
    } else if (Z_TYPE_P(arg) == IS_LONG) {
6358
4
      switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
6359
4
        case PHP_SORT_ASC:
6360
4
        case PHP_SORT_DESC:
6361
          /* flag allowed here */
6362
4
          if (parse_state[MULTISORT_ORDER] == 1) {
6363
            /* Save the flag and make sure then next arg is not the current flag. */
6364
4
            sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
6365
4
            parse_state[MULTISORT_ORDER] = 0;
6366
4
          } else {
6367
0
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6368
0
            MULTISORT_ABORT;
6369
0
          }
6370
4
          break;
6371
6372
4
        case PHP_SORT_REGULAR:
6373
0
        case PHP_SORT_NUMERIC:
6374
0
        case PHP_SORT_STRING:
6375
0
        case PHP_SORT_NATURAL:
6376
0
        case PHP_SORT_LOCALE_STRING:
6377
          /* flag allowed here */
6378
0
          if (parse_state[MULTISORT_TYPE] == 1) {
6379
            /* Save the flag and make sure then next arg is not the current flag. */
6380
0
            sort_type = (int)Z_LVAL_P(arg);
6381
0
            parse_state[MULTISORT_TYPE] = 0;
6382
0
          } else {
6383
0
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6384
0
            MULTISORT_ABORT;
6385
0
          }
6386
0
          break;
6387
6388
0
        default:
6389
0
          zend_argument_value_error(i + 1, "must be a valid sort flag");
6390
0
          MULTISORT_ABORT;
6391
0
          break;
6392
6393
4
      }
6394
4
    } else {
6395
1
      zend_argument_type_error(i + 1, "must be an array or a sort flag");
6396
1
      MULTISORT_ABORT;
6397
0
    }
6398
10
  }
6399
6400
  /* Make sure the arrays are of the same size. */
6401
1
  array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6402
3
  for (i = 1; i < num_arrays; i++) {
6403
2
    if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
6404
0
      zend_value_error("Array sizes are inconsistent");
6405
0
      MULTISORT_ABORT;
6406
0
    }
6407
2
  }
6408
6409
  /* If all arrays are empty we don't need to do anything. */
6410
1
  if (array_size < 1) {
6411
1
    efree(func);
6412
1
    efree(arrays);
6413
1
    RETURN_TRUE;
6414
1
  }
6415
6416
  /* Take care of the last array sort flags. */
6417
0
  func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6418
0
  bucket_compare_func_t *old_multisort_func = ARRAYG(multisort_func);
6419
0
  ARRAYG(multisort_func) = func;
6420
6421
  /* Create the indirection array. This array is of size MxN, where
6422
   * M is the number of entries in each input array and N is the number
6423
   * of the input arrays + 1. The last column is UNDEF to indicate the end
6424
   * of the row. It also stores the original position for stable sorting. */
6425
0
  indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6426
  /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6427
0
  Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6428
0
  for (i = 0; i < array_size; i++) {
6429
0
    indirect[i] = indirects + (i * (num_arrays + 1));
6430
0
  }
6431
0
  hashes = safe_emalloc(num_arrays, sizeof(HashTable *), 0);
6432
0
  for (i = 0; i < num_arrays; i++) {
6433
0
    hashes[i] = Z_ARRVAL_P(arrays[i]);
6434
0
    GC_ADDREF(hashes[i]);
6435
0
    HT_ALLOW_COW_VIOLATION(hashes[i]);
6436
0
  }
6437
0
  for (i = 0; i < num_arrays; i++) {
6438
0
    k = 0;
6439
0
    if (HT_IS_PACKED(hashes[i])) {
6440
0
      zval *zv = hashes[i]->arPacked;
6441
0
      for (idx = 0; idx < hashes[i]->nNumUsed; idx++, zv++) {
6442
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6443
0
        ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6444
0
        indirect[k][i].h = idx;
6445
0
        indirect[k][i].key = NULL;
6446
0
        k++;
6447
0
      }
6448
0
    } else {
6449
0
      Bucket *p = hashes[i]->arData;
6450
0
      for (idx = 0; idx < hashes[i]->nNumUsed; idx++, p++) {
6451
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
6452
0
        indirect[k][i] = *p;
6453
0
        k++;
6454
0
      }
6455
0
    }
6456
0
  }
6457
0
  for (k = 0; k < array_size; k++) {
6458
0
    ZVAL_UNDEF(&indirect[k][num_arrays].val);
6459
0
    Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6460
0
  }
6461
6462
  /* Do the actual sort magic - bada-bim, bada-boom. */
6463
0
  zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6464
0
  if (EG(exception)) {
6465
0
    goto clean_up;
6466
0
  }
6467
6468
  /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6469
0
  for (i = 0; i < num_arrays; i++) {
6470
0
    hash = hashes[i];
6471
0
    hash->nNumUsed = array_size;
6472
0
    hash->nNextFreeElement = array_size;
6473
0
    hash->nInternalPointer = 0;
6474
0
    if (HT_IS_PACKED(hash)) {
6475
0
      for (k = 0; k < array_size; k++) {
6476
0
        ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6477
0
      }
6478
0
    } else {
6479
0
      bool repack = true;
6480
6481
0
      for (n = 0, k = 0; k < array_size; k++) {
6482
0
        hash->arData[k] = indirect[k][i];
6483
0
        if (hash->arData[k].key == NULL) {
6484
0
          hash->arData[k].h = n++;
6485
0
        } else {
6486
0
          repack = false;
6487
0
        }
6488
0
      }
6489
0
      if (repack) {
6490
0
        zend_hash_to_packed(hash);
6491
0
      } else {
6492
0
        zend_hash_rehash(hash);
6493
0
      }
6494
0
    }
6495
0
  }
6496
0
  RETVAL_TRUE;
6497
6498
0
clean_up:
6499
0
  for (i = 0; i < num_arrays; i++) {
6500
0
    if (UNEXPECTED(GC_DELREF(hashes[i]) == 0)) {
6501
0
      zend_array_destroy(hashes[i]);
6502
0
    } else {
6503
0
      gc_check_possible_root((zend_refcounted *)hashes[i]);
6504
0
    }
6505
0
  }
6506
0
  efree(hashes);
6507
0
  efree(indirects);
6508
0
  efree(indirect);
6509
0
  efree(func);
6510
0
  efree(arrays);
6511
0
  ARRAYG(multisort_func) = old_multisort_func;
6512
0
}
6513
/* }}} */
6514
6515
/* {{{ php_array_pick_keys */
6516
PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent)
6517
0
{
6518
0
  const php_random_algo *algo = engine.algo;
6519
0
  void *state = engine.state;
6520
6521
0
  HashTable *ht = Z_ARRVAL_P(input);
6522
0
  uint32_t num_avail = zend_hash_num_elements(ht);
6523
0
  zend_long i, randval;
6524
0
  zend_string *string_key;
6525
0
  zend_ulong num_key;
6526
0
  zval *zv;
6527
0
  Bucket *b;
6528
0
  zend_bitset bitset;
6529
0
  int negative_bitset = 0;
6530
0
  uint32_t bitset_len;
6531
0
  ALLOCA_FLAG(use_heap);
6532
6533
0
  if (num_avail == 0) {
6534
0
    if (!silent) {
6535
0
      zend_argument_must_not_be_empty_error(1);
6536
0
    }
6537
0
    return false;
6538
0
  }
6539
6540
0
  if (num_req == 1) {
6541
0
    if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6542
      /* If less than 1/2 of elements are used, don't sample. Instead search for a
6543
       * specific offset using linear scan. */
6544
0
      i = 0;
6545
0
      randval = algo->range(state, 0, num_avail - 1);
6546
0
      if (EG(exception)) {
6547
0
        return false;
6548
0
      }
6549
0
      ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6550
0
        if (i == randval) {
6551
0
          if (string_key) {
6552
0
            ZVAL_STR_COPY(retval, string_key);
6553
0
          } else {
6554
0
            ZVAL_LONG(retval, num_key);
6555
0
          }
6556
0
          return true;
6557
0
        }
6558
0
        i++;
6559
0
      } ZEND_HASH_FOREACH_END();
6560
0
    }
6561
6562
    /* Sample random buckets until we hit one that is not empty.
6563
     * The worst case probability of hitting an empty element is 1-1/2. The worst case
6564
     * probability of hitting N empty elements in a row is (1-1/2)**N.
6565
     * For N=10 this becomes smaller than 0.1%. */
6566
0
    if (HT_IS_PACKED(ht)) {
6567
0
      do {
6568
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6569
0
        if (EG(exception)) {
6570
0
          return false;
6571
0
        }
6572
0
        zv = &ht->arPacked[randval];
6573
0
        if (!Z_ISUNDEF_P(zv)) {
6574
0
          ZVAL_LONG(retval, randval);
6575
0
          return true;
6576
0
        }
6577
0
      } while (true);
6578
0
    } else {
6579
0
      do {
6580
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6581
0
        if (EG(exception)) {
6582
0
          return false;
6583
0
        }
6584
0
        b = &ht->arData[randval];
6585
0
        if (!Z_ISUNDEF(b->val)) {
6586
0
          if (b->key) {
6587
0
            ZVAL_STR_COPY(retval, b->key);
6588
0
          } else {
6589
0
            ZVAL_LONG(retval, b->h);
6590
0
          }
6591
0
          return true;
6592
0
        }
6593
0
      } while (true);
6594
0
    }
6595
0
  }
6596
6597
0
  if (num_req <= 0 || num_req > num_avail) {
6598
0
    if (!silent) {
6599
0
      zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6600
0
    }
6601
0
    return false;
6602
0
  }
6603
6604
  /* Make the return value an array only if we need to pass back more than one result. */
6605
0
  array_init_size(retval, (uint32_t) num_req);
6606
0
  if (num_req > (num_avail >> 1)) {
6607
0
    negative_bitset = 1;
6608
0
    num_req = num_avail - num_req;
6609
0
  }
6610
6611
0
  bitset_len = zend_bitset_len(num_avail);
6612
0
  bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6613
0
  zend_bitset_clear(bitset, bitset_len);
6614
6615
0
  i = num_req;
6616
0
  int failures = 0;
6617
0
  while (i) {
6618
0
    randval = algo->range(state, 0, num_avail - 1);
6619
0
    if (EG(exception)) {
6620
0
      goto fail;
6621
0
    }
6622
0
    if (zend_bitset_in(bitset, randval)) {
6623
0
      if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6624
0
        if (!silent) {
6625
0
          zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6626
0
        }
6627
6628
0
        goto fail;
6629
0
      }
6630
0
    } else {
6631
0
      zend_bitset_incl(bitset, randval);
6632
0
      i--;
6633
0
      failures = 0;
6634
0
    }
6635
0
  }
6636
6637
0
  zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6638
0
  ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6639
    /* We can't use zend_hash_index_find()
6640
     * because the array may have string keys or gaps. */
6641
0
    ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6642
0
      if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6643
0
        if (string_key) {
6644
0
          ZEND_HASH_FILL_SET_STR_COPY(string_key);
6645
0
        } else {
6646
0
          ZEND_HASH_FILL_SET_LONG(num_key);
6647
0
        }
6648
0
        ZEND_HASH_FILL_NEXT();
6649
0
      }
6650
0
      i++;
6651
0
    } ZEND_HASH_FOREACH_END();
6652
0
  } ZEND_HASH_FILL_END();
6653
6654
0
  free_alloca(bitset, use_heap);
6655
6656
0
  return true;
6657
6658
0
 fail:
6659
0
  free_alloca(bitset, use_heap);
6660
6661
0
  return false;
6662
0
}
6663
/* }}} */
6664
6665
/* {{{ Return key/keys for random entry/entries in the array */
6666
PHP_FUNCTION(array_rand)
6667
0
{
6668
0
  zval *input;
6669
0
  zend_long num_req = 1;
6670
6671
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
6672
0
    Z_PARAM_ARRAY(input)
6673
0
    Z_PARAM_OPTIONAL
6674
0
    Z_PARAM_LONG(num_req)
6675
0
  ZEND_PARSE_PARAMETERS_END();
6676
6677
0
  if (!php_array_pick_keys(
6678
0
      php_random_default_engine(),
6679
0
      input,
6680
0
      num_req,
6681
0
      return_value,
6682
0
      false)
6683
0
  ) {
6684
0
    RETURN_THROWS();
6685
0
  }
6686
0
}
6687
/* }}} */
6688
6689
/* Apply a single array_sum/array_product step to return_value. */
6690
static zend_always_inline void php_array_binop_apply(
6691
    zval *return_value, zval *entry, const char *op_name, binary_op_type op)
6692
0
{
6693
  /* For objects we try to cast them to a numeric type */
6694
0
  if (Z_TYPE_P(entry) == IS_OBJECT) {
6695
0
    zval dst;
6696
0
    zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6697
6698
    /* Do not type error for BC */
6699
0
    if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6700
0
      php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6701
0
        op_name, zend_zval_type_name(entry));
6702
0
      return;
6703
0
    }
6704
0
    op(return_value, return_value, &dst);
6705
0
    return;
6706
0
  }
6707
6708
0
  zend_result status = op(return_value, return_value, entry);
6709
0
  if (status == FAILURE) {
6710
0
    ZEND_ASSERT(EG(exception));
6711
0
    zend_clear_exception();
6712
    /* BC resources: previously resources were cast to int */
6713
0
    if (Z_TYPE_P(entry) == IS_RESOURCE) {
6714
0
      zval tmp;
6715
0
      ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6716
0
      op(return_value, return_value, &tmp);
6717
0
    }
6718
    /* BC non numeric strings: previously were cast to 0 */
6719
0
    else if (Z_TYPE_P(entry) == IS_STRING) {
6720
0
      zval tmp;
6721
0
      ZVAL_LONG(&tmp, 0);
6722
0
      op(return_value, return_value, &tmp);
6723
0
    }
6724
0
    php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6725
0
      op_name, zend_zval_type_name(entry));
6726
0
  }
6727
0
}
6728
6729
/* Wrapper for array_sum and array_product */
6730
static zend_always_inline void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6731
0
{
6732
0
  HashTable *input;
6733
6734
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
6735
0
    Z_PARAM_ARRAY_HT(input)
6736
0
  ZEND_PARSE_PARAMETERS_END();
6737
6738
0
  if (zend_hash_num_elements(input) == 0) {
6739
0
    RETURN_LONG(initial);
6740
0
  }
6741
6742
0
  ZVAL_LONG(return_value, initial);
6743
6744
0
  if (op == add_function) {
6745
0
    zval *entry;
6746
0
    ZEND_HASH_FOREACH_VAL(input, entry) {
6747
0
      if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) {
6748
0
        fast_long_add_function(return_value, return_value, entry);
6749
0
        continue;
6750
0
      }
6751
0
      php_array_binop_apply(return_value, entry, op_name, op);
6752
0
    } ZEND_HASH_FOREACH_END();
6753
0
  } else if (op == mul_function) {
6754
0
    zval *entry;
6755
0
    ZEND_HASH_FOREACH_VAL(input, entry) {
6756
0
      if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) {
6757
0
        zend_long lval;
6758
0
        double dval;
6759
0
        int overflow;
6760
0
        ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(return_value), Z_LVAL_P(entry), lval, dval, overflow);
6761
0
        if (UNEXPECTED(overflow)) {
6762
0
          ZVAL_DOUBLE(return_value, dval);
6763
0
        } else {
6764
0
          Z_LVAL_P(return_value) = lval;
6765
0
        }
6766
0
        continue;
6767
0
      }
6768
0
      php_array_binop_apply(return_value, entry, op_name, op);
6769
0
    } ZEND_HASH_FOREACH_END();
6770
0
  } else {
6771
0
    zval *entry;
6772
0
    ZEND_HASH_FOREACH_VAL(input, entry) {
6773
0
      php_array_binop_apply(return_value, entry, op_name, op);
6774
0
    } ZEND_HASH_FOREACH_END();
6775
0
  }
6776
0
}
6777
6778
/* {{{ Returns the sum of the array entries */
6779
PHP_FUNCTION(array_sum)
6780
0
{
6781
0
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6782
0
}
6783
/* }}} */
6784
6785
/* {{{ Returns the product of the array entries */
6786
PHP_FUNCTION(array_product)
6787
0
{
6788
0
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6789
0
}
6790
/* }}} */
6791
6792
/* {{{ Iteratively reduce the array to a single value via the callback. */
6793
PHP_FUNCTION(array_reduce)
6794
0
{
6795
0
  zval *input;
6796
0
  zval args[2];
6797
0
  zval *operand;
6798
0
  zend_fcall_info fci;
6799
0
  zend_fcall_info_cache fci_cache;
6800
0
  zval *initial = NULL;
6801
0
  HashTable *htbl;
6802
6803
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
6804
0
    Z_PARAM_ARRAY(input)
6805
0
    Z_PARAM_FUNC(fci, fci_cache)
6806
0
    Z_PARAM_OPTIONAL
6807
0
    Z_PARAM_ZVAL(initial)
6808
0
  ZEND_PARSE_PARAMETERS_END();
6809
6810
0
  if (initial) {
6811
0
    ZVAL_COPY(return_value, initial);
6812
0
  } else {
6813
0
    ZVAL_NULL(return_value);
6814
0
  }
6815
6816
  /* (zval **)input points to an element of argument stack
6817
   * the base pointer of which is subject to change.
6818
   * thus we need to keep the pointer to the hashtable for safety */
6819
0
  htbl = Z_ARRVAL_P(input);
6820
6821
0
  if (zend_hash_num_elements(htbl) == 0) {
6822
0
    return;
6823
0
  }
6824
6825
0
  fci.retval = return_value;
6826
0
  fci.param_count = 2;
6827
0
  fci.params = args;
6828
0
  fci.consumed_args = zend_fci_consumed_arg(0);
6829
6830
0
  ZEND_HASH_FOREACH_VAL(htbl, operand) {
6831
0
    ZVAL_COPY_VALUE(&args[0], return_value);
6832
0
    ZVAL_COPY_VALUE(&args[1], operand);
6833
6834
0
    zend_call_function(&fci, &fci_cache);
6835
0
    zval_ptr_dtor(&args[0]);
6836
6837
0
    if (EXPECTED(!Z_ISUNDEF_P(return_value))) {
6838
0
      if (UNEXPECTED(Z_ISREF_P(return_value))) {
6839
0
        zend_unwrap_reference(return_value);
6840
0
      }
6841
0
    } else {
6842
0
      RETURN_NULL();
6843
0
    }
6844
0
  } ZEND_HASH_FOREACH_END();
6845
0
}
6846
/* }}} */
6847
6848
/* Consumes `zv` */
6849
static bool php_is_true(zval *zv)
6850
0
{
6851
0
  switch (Z_TYPE_P(zv)) {
6852
0
    case IS_TRUE:
6853
0
      return true;
6854
0
    case IS_FALSE:
6855
0
      return false;
6856
0
    default: {
6857
0
      bool rv = zend_is_true(zv);
6858
0
      zval_ptr_dtor(zv);
6859
0
      return rv;
6860
0
    }
6861
0
  }
6862
0
}
6863
6864
/* {{{ Filters elements from the array via the callback. */
6865
PHP_FUNCTION(array_filter)
6866
0
{
6867
0
  zval *array;
6868
0
  zval *operand;
6869
0
  zval *key;
6870
0
  zval args[2];
6871
0
  zval retval;
6872
0
  bool have_callback = 0;
6873
0
  zend_long use_type = ARRAY_FILTER_USE_VALUE;
6874
0
  zend_string *string_key;
6875
0
  zend_fcall_info fci = empty_fcall_info;
6876
0
  zend_fcall_info_cache fci_cache;
6877
0
  zend_ulong num_key;
6878
6879
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
6880
0
    Z_PARAM_ARRAY(array)
6881
0
    Z_PARAM_OPTIONAL
6882
0
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6883
0
    Z_PARAM_LONG(use_type)
6884
0
  ZEND_PARSE_PARAMETERS_END();
6885
6886
0
  switch (use_type) {
6887
0
    case ARRAY_FILTER_USE_VALUE:
6888
0
    case ARRAY_FILTER_USE_BOTH:
6889
0
    case ARRAY_FILTER_USE_KEY:
6890
0
      break;
6891
0
    default:
6892
0
      zend_argument_value_error(3, "must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH");
6893
0
    RETURN_THROWS();
6894
0
  }
6895
6896
0
  if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6897
0
    RETURN_EMPTY_ARRAY();
6898
0
  }
6899
0
  array_init(return_value);
6900
6901
0
  if (ZEND_FCI_INITIALIZED(fci)) {
6902
0
    have_callback = 1;
6903
0
    fci.retval = &retval;
6904
0
    if (use_type == ARRAY_FILTER_USE_BOTH) {
6905
0
      fci.param_count = 2;
6906
0
      key = &args[1];
6907
0
    } else {
6908
0
      fci.param_count = 1;
6909
0
      key = &args[0];
6910
0
    }
6911
0
  }
6912
6913
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6914
0
    if (have_callback) {
6915
0
      if (use_type != ARRAY_FILTER_USE_VALUE) {
6916
        /* Set up the key */
6917
0
        if (!string_key) {
6918
0
          ZVAL_LONG(key, num_key);
6919
0
        } else {
6920
0
          ZVAL_STR(key, string_key);
6921
0
        }
6922
0
      }
6923
0
      if (use_type != ARRAY_FILTER_USE_KEY) {
6924
0
        ZVAL_COPY_VALUE(&args[0], operand);
6925
0
      }
6926
0
      fci.params = args;
6927
6928
0
      zend_result result = zend_call_function(&fci, &fci_cache);
6929
0
      ZEND_ASSERT(result == SUCCESS);
6930
6931
0
      if (UNEXPECTED(EG(exception))) {
6932
0
        RETURN_THROWS();
6933
0
      }
6934
6935
0
      if (!php_is_true(&retval)) {
6936
0
        continue;
6937
0
      }
6938
0
    } else if (!zend_is_true(operand)) {
6939
0
      continue;
6940
0
    }
6941
6942
0
    if (string_key) {
6943
0
      operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6944
0
    } else {
6945
0
      operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6946
0
    }
6947
0
    zval_add_ref(operand);
6948
0
  } ZEND_HASH_FOREACH_END();
6949
0
}
6950
/* }}} */
6951
6952
/* {{{ Internal function to find an array element for a user closure. */
6953
enum php_array_find_result {
6954
  PHP_ARRAY_FIND_EXCEPTION = -1,
6955
  PHP_ARRAY_FIND_NONE = 0,
6956
  PHP_ARRAY_FIND_SOME = 1,
6957
};
6958
6959
static enum php_array_find_result php_array_find(const HashTable *array, const zend_fcall_info_cache *fcc, zval *result_key, zval *result_value, bool negate_condition)
6960
0
{
6961
0
  zval retval;
6962
0
  zval args[2];
6963
6964
0
  if (zend_hash_num_elements(array) == 0) {
6965
0
    return PHP_ARRAY_FIND_NONE;
6966
0
  }
6967
6968
0
  ZEND_HASH_FOREACH_KEY_VAL(array, zend_ulong num_key, zend_string *str_key, zval *operand) {
6969
    /* Set up the key */
6970
0
    if (!str_key) {
6971
0
      ZVAL_LONG(&args[1], num_key);
6972
0
    } else {
6973
      /* Allows copying the numeric branch, without this branch, into the iteration code
6974
       * that checks for the packed array flag. */
6975
0
      ZEND_ASSUME(!HT_IS_PACKED(array));
6976
0
      ZVAL_STR(&args[1], str_key);
6977
0
    }
6978
6979
0
    ZVAL_COPY_VALUE(&args[0], operand);
6980
6981
0
    zend_call_known_fcc(fcc, &retval, 2, args, NULL);
6982
6983
0
    if (UNEXPECTED(Z_ISUNDEF(retval))) {
6984
0
      return PHP_ARRAY_FIND_EXCEPTION;
6985
0
    }
6986
6987
0
    if (php_is_true(&retval) ^ negate_condition) {
6988
0
      if (result_value != NULL) {
6989
0
        ZVAL_COPY_DEREF(result_value, &args[0]);
6990
0
      }
6991
6992
0
      if (result_key != NULL) {
6993
0
        ZVAL_COPY(result_key, &args[1]);
6994
0
      }
6995
6996
0
      return PHP_ARRAY_FIND_SOME;
6997
0
    }
6998
0
  } ZEND_HASH_FOREACH_END();
6999
7000
0
  return PHP_ARRAY_FIND_NONE;
7001
0
}
7002
/* }}} */
7003
7004
/* {{{ Search within an array and returns the first found element value. */
7005
PHP_FUNCTION(array_find)
7006
0
{
7007
0
  HashTable *array;
7008
0
  zend_fcall_info fci;
7009
0
  zend_fcall_info_cache fcc;
7010
7011
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7012
0
    Z_PARAM_ARRAY_HT(array)
7013
0
    Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
7014
0
  ZEND_PARSE_PARAMETERS_END();
7015
7016
0
  php_array_find(array, &fcc, NULL, return_value, false);
7017
0
  zend_release_fcall_info_cache(&fcc);
7018
0
}
7019
/* }}} */
7020
7021
/* {{{ Search within an array and returns the first found element key. */
7022
PHP_FUNCTION(array_find_key)
7023
0
{
7024
0
  HashTable *array;
7025
0
  zend_fcall_info fci;
7026
0
  zend_fcall_info_cache fcc;
7027
7028
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7029
0
    Z_PARAM_ARRAY_HT(array)
7030
0
    Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
7031
0
  ZEND_PARSE_PARAMETERS_END();
7032
7033
0
  php_array_find(array, &fcc, return_value, NULL, false);
7034
0
  zend_release_fcall_info_cache(&fcc);
7035
0
}
7036
/* }}} */
7037
7038
/* {{{ Checks if at least one array element satisfies a callback function. */
7039
PHP_FUNCTION(array_any)
7040
0
{
7041
0
  HashTable *array;
7042
0
  zend_fcall_info fci;
7043
0
  zend_fcall_info_cache fcc;
7044
7045
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7046
0
    Z_PARAM_ARRAY_HT(array)
7047
0
    Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
7048
0
  ZEND_PARSE_PARAMETERS_END();
7049
7050
0
  RETVAL_BOOL(php_array_find(array, &fcc, NULL, NULL, false) == PHP_ARRAY_FIND_SOME);
7051
0
  zend_release_fcall_info_cache(&fcc);
7052
0
}
7053
/* }}} */
7054
7055
/* {{{ Checks if all array elements satisfy a callback function. */
7056
PHP_FUNCTION(array_all)
7057
0
{
7058
0
  HashTable *array;
7059
0
  zend_fcall_info fci;
7060
0
  zend_fcall_info_cache fcc;
7061
7062
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7063
0
    Z_PARAM_ARRAY_HT(array)
7064
0
    Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
7065
0
  ZEND_PARSE_PARAMETERS_END();
7066
7067
0
  RETVAL_BOOL(php_array_find(array, &fcc, NULL, NULL, true) == PHP_ARRAY_FIND_NONE);
7068
0
  zend_release_fcall_info_cache(&fcc);
7069
0
}
7070
/* }}} */
7071
7072
/* {{{ Applies the callback to the elements in given arrays. */
7073
PHP_FUNCTION(array_map)
7074
2
{
7075
2
  zval *arrays = NULL;
7076
2
  uint32_t n_arrays = 0;
7077
2
  zval result;
7078
2
  zend_fcall_info fci;
7079
2
  zend_fcall_info_cache fci_cache;
7080
2
  uint32_t i;
7081
2
  uint32_t k, maxlen = 0;
7082
7083
6
  ZEND_PARSE_PARAMETERS_START(2, -1)
7084
8
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
7085
0
    Z_PARAM_VARIADIC('+', arrays, n_arrays)
7086
2
  ZEND_PARSE_PARAMETERS_END();
7087
7088
0
  if (n_arrays == 1) {
7089
0
    if (Z_TYPE(arrays[0]) != IS_ARRAY) {
7090
0
      zend_wrong_parameter_type_error(2, Z_EXPECTED_ARRAY, &arrays[0]);
7091
0
      RETURN_THROWS();
7092
0
    }
7093
0
    const HashTable *input = Z_ARRVAL(arrays[0]);
7094
0
    maxlen = zend_hash_num_elements(input);
7095
7096
    /* Short-circuit: if no callback and only one array, just return it. */
7097
0
    if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
7098
0
      ZVAL_COPY(return_value, &arrays[0]);
7099
0
      return;
7100
0
    }
7101
7102
0
    fci.retval = &result;
7103
0
    fci.param_count = 1;
7104
7105
0
    if (HT_IS_PACKED(input)) {
7106
0
      array_init_size(return_value, input->nNumUsed);
7107
0
      HashTable *output = Z_ARRVAL_P(return_value);
7108
0
      zend_hash_real_init_packed(output);
7109
7110
0
      uint32_t undefs = 0;
7111
0
      ZEND_HASH_FILL_PACKED(output) {
7112
        /* Can't use ZEND_HASH_PACKED_FOREACH_VAL() because we need to also account for the UNDEF values
7113
         * so the keys in the output array will match those of the input array. */
7114
0
        for (zval *cur = input->arPacked, *end = input->arPacked + input->nNumUsed; cur != end; cur++) {
7115
0
          if (EXPECTED(!Z_ISUNDEF_P(cur))) {
7116
0
            fci.params = cur;
7117
0
            zend_result ret = zend_call_function(&fci, &fci_cache);
7118
0
            ZEND_ASSERT(ret == SUCCESS);
7119
0
            ZEND_IGNORE_VALUE(ret);
7120
0
            if (UNEXPECTED(Z_ISUNDEF(result))) {
7121
0
              ZEND_HASH_FILL_FINISH();
7122
0
              RETURN_THROWS();
7123
0
            }
7124
0
          } else {
7125
0
            ZVAL_UNDEF(&result);
7126
0
            undefs++;
7127
0
          }
7128
0
          ZEND_HASH_FILL_ADD(&result);
7129
0
        }
7130
0
      } ZEND_HASH_FILL_END();
7131
0
      output->nNumOfElements -= undefs;
7132
0
    } else {
7133
0
      zend_ulong num_key;
7134
0
      zend_string *str_key;
7135
7136
0
      array_init_size(return_value, maxlen);
7137
0
      HashTable *output = Z_ARRVAL_P(return_value);
7138
0
      zend_hash_real_init_mixed(output);
7139
7140
0
      ZEND_HASH_MAP_FOREACH_KEY_VAL(input, num_key, str_key, fci.params) {
7141
0
        zend_result ret = zend_call_function(&fci, &fci_cache);
7142
0
        ZEND_ASSERT(ret == SUCCESS);
7143
0
        ZEND_IGNORE_VALUE(ret);
7144
0
        if (UNEXPECTED(Z_ISUNDEF(result))) {
7145
0
          RETURN_THROWS();
7146
0
        }
7147
0
        if (str_key) {
7148
0
          _zend_hash_append(output, str_key, &result);
7149
0
        } else {
7150
0
          zend_hash_index_add_new(output, num_key, &result);
7151
0
        }
7152
0
      } ZEND_HASH_FOREACH_END();
7153
0
    }
7154
0
  } else {
7155
0
    for (i = 0; i < n_arrays; i++) {
7156
0
      if (Z_TYPE(arrays[i]) != IS_ARRAY) {
7157
0
        zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
7158
0
        RETURN_THROWS();
7159
0
      }
7160
0
      if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
7161
0
        maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
7162
0
      }
7163
0
    }
7164
7165
0
    if (!maxlen) {
7166
0
      RETURN_EMPTY_ARRAY();
7167
0
    }
7168
7169
0
    if (UNEXPECTED(!array_finit_size_packed(return_value, maxlen))) {
7170
0
      RETURN_OOM();
7171
0
    }
7172
7173
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
7174
0
      if (!ZEND_FCI_INITIALIZED(fci)) {
7175
0
        uint32_t *array_pos = ecalloc(n_arrays, sizeof(HashPosition));
7176
0
        zval zv;
7177
7178
        /* We iterate through all the arrays at once. */
7179
0
        for (k = 0; k < maxlen; k++) {
7180
7181
          /* If no callback, the result will be an array, consisting of current
7182
           * entries from all arrays. */
7183
0
          if (ZEND_HAS_OOM()) {
7184
0
            efree(array_pos);
7185
0
            RETURN_OOM();
7186
0
          }
7187
0
          array_init_size(&result, n_arrays);
7188
7189
0
          zend_hash_real_init_packed(Z_ARRVAL(result));
7190
0
          ZEND_HASH_FILL_PACKED(Z_ARRVAL(result)) {
7191
0
            for (i = 0; i < n_arrays; i++) {
7192
              /* If this array still has elements, add the current one to the
7193
               * parameter list, otherwise use null value. */
7194
0
              uint32_t pos = array_pos[i];
7195
0
              if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
7196
0
                while (1) {
7197
0
                  if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7198
0
                    ZEND_HASH_FILL_SET_NULL();
7199
0
                    break;
7200
0
                  } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
7201
0
                    ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
7202
0
                    ZEND_HASH_FILL_SET(&zv);
7203
0
                    array_pos[i] = pos + 1;
7204
0
                    break;
7205
0
                  }
7206
0
                  pos++;
7207
0
                }
7208
0
              } else {
7209
0
                while (1) {
7210
0
                  if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7211
0
                    ZEND_HASH_FILL_SET_NULL();
7212
0
                    break;
7213
0
                  } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
7214
0
                    ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
7215
0
                    ZEND_HASH_FILL_SET(&zv);
7216
0
                    array_pos[i] = pos + 1;
7217
0
                    break;
7218
0
                  }
7219
0
                  pos++;
7220
0
                }
7221
0
              }
7222
0
              ZEND_HASH_FILL_NEXT();
7223
0
            }
7224
0
          } ZEND_HASH_FILL_END();
7225
7226
0
          ZEND_HASH_FILL_ADD(&result);
7227
0
        }
7228
7229
0
        efree(array_pos);
7230
0
      } else {
7231
0
        zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
7232
7233
        /* Remember next starting point in the array, initialize those as zeros. */
7234
0
        for (i = 0; i < n_arrays; i++) {
7235
0
          Z_EXTRA(params[i]) = 0;
7236
0
        }
7237
7238
0
        fci.retval = &result;
7239
0
        fci.param_count = n_arrays;
7240
0
        fci.params = params;
7241
7242
        /* We iterate through all the arrays at once. */
7243
0
        for (k = 0; k < maxlen; k++) {
7244
0
          for (i = 0; i < n_arrays; i++) {
7245
            /* If this array still has elements, add the current one to the
7246
             * parameter list, otherwise use null value. */
7247
0
            uint32_t pos = Z_EXTRA(params[i]);
7248
0
            if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
7249
0
              while (1) {
7250
0
                if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7251
0
                  ZVAL_NULL(&params[i]);
7252
0
                  break;
7253
0
                } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
7254
0
                  ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
7255
0
                  Z_EXTRA(params[i]) = pos + 1;
7256
0
                  break;
7257
0
                }
7258
0
                pos++;
7259
0
              }
7260
0
            } else {
7261
0
              while (1) {
7262
0
                if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7263
0
                  ZVAL_NULL(&params[i]);
7264
0
                  break;
7265
0
                } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
7266
0
                  ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
7267
0
                  Z_EXTRA(params[i]) = pos + 1;
7268
0
                  break;
7269
0
                }
7270
0
                pos++;
7271
0
              }
7272
0
            }
7273
0
          }
7274
7275
0
          zend_result ret = zend_call_function(&fci, &fci_cache);
7276
0
          ZEND_ASSERT(ret == SUCCESS);
7277
0
          ZEND_IGNORE_VALUE(ret);
7278
7279
0
          if (Z_TYPE(result) == IS_UNDEF) {
7280
0
            ZEND_HASH_FILL_FINISH();
7281
0
            efree(params);
7282
0
            RETURN_THROWS();
7283
0
          }
7284
7285
0
          ZEND_HASH_FILL_ADD(&result);
7286
0
        }
7287
7288
0
        efree(params);
7289
0
      }
7290
0
    } ZEND_HASH_FILL_END();
7291
0
  }
7292
0
}
7293
/* }}} */
7294
7295
/* {{{ Checks if the given key or index exists in the array */
7296
PHP_FUNCTION(array_key_exists)
7297
0
{
7298
0
  zval *key;
7299
0
  HashTable *ht;
7300
7301
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7302
0
    Z_PARAM_ZVAL(key)
7303
0
    Z_PARAM_ARRAY_HT(ht)
7304
0
  ZEND_PARSE_PARAMETERS_END();
7305
7306
0
  switch (Z_TYPE_P(key)) {
7307
0
    case IS_STRING:
7308
0
      RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
7309
0
      break;
7310
0
    case IS_LONG:
7311
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
7312
0
      break;
7313
0
    case IS_NULL:
7314
0
      zend_error(E_DEPRECATED, "Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead");
7315
0
      RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
7316
0
      break;
7317
0
    case IS_DOUBLE:
7318
0
      RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
7319
0
      break;
7320
0
    case IS_FALSE:
7321
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 0));
7322
0
      break;
7323
0
    case IS_TRUE:
7324
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 1));
7325
0
      break;
7326
0
    case IS_RESOURCE:
7327
0
      zend_use_resource_as_offset(key);
7328
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
7329
0
      break;
7330
0
    default:
7331
0
      zend_argument_type_error(1, "must be a valid array offset type");
7332
0
      break;
7333
0
  }
7334
0
}
7335
/* }}} */
7336
7337
/* {{{ Split array into chunks */
7338
PHP_FUNCTION(array_chunk)
7339
0
{
7340
0
  int num_in;
7341
0
  zend_long size, current = 0;
7342
0
  zend_string *str_key;
7343
0
  zend_ulong num_key;
7344
0
  bool preserve_keys = 0;
7345
0
  zval *input = NULL;
7346
0
  zval chunk;
7347
0
  zval *entry;
7348
7349
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
7350
0
    Z_PARAM_ARRAY(input)
7351
0
    Z_PARAM_LONG(size)
7352
0
    Z_PARAM_OPTIONAL
7353
0
    Z_PARAM_BOOL(preserve_keys)
7354
0
  ZEND_PARSE_PARAMETERS_END();
7355
7356
  /* Do bounds checking for size parameter. */
7357
0
  if (size < 1) {
7358
0
    zend_argument_value_error(2, "must be greater than 0");
7359
0
    RETURN_THROWS();
7360
0
  }
7361
7362
0
  num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
7363
7364
0
  if (size > num_in) {
7365
0
    if (num_in == 0) {
7366
0
      RETURN_EMPTY_ARRAY();
7367
0
    }
7368
0
    size = num_in;
7369
0
  }
7370
7371
0
  if (UNEXPECTED(!array_finit_size_packed(return_value, (uint32_t)(((num_in - 1) / size) + 1)))) {
7372
0
    RETURN_OOM();
7373
0
  }
7374
7375
0
  if (!preserve_keys) {
7376
    /* Every chunk is a list of exactly `size` elements (the last one possibly
7377
     * shorter), so each one can be filled directly. The input is walked by
7378
     * element pointer, so the packed/hash stride is computed only once. */
7379
0
    HashTable *ht = Z_ARRVAL_P(input);
7380
0
    uint32_t elem_size = ZEND_HASH_ELEMENT_SIZE(ht);
7381
0
    zval *zv = ht->arPacked;
7382
0
    uint32_t remaining = (uint32_t)num_in;
7383
7384
0
    while (remaining > 0) {
7385
0
      uint32_t chunk_size = MIN((uint32_t)size, remaining);
7386
7387
0
      array_init_size(&chunk, chunk_size);
7388
0
      zend_hash_real_init_packed(Z_ARRVAL(chunk));
7389
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL(chunk)) {
7390
0
        uint32_t n = 0;
7391
0
        while (n < chunk_size) {
7392
0
          entry = zv;
7393
0
          zv = ZEND_HASH_NEXT_ELEMENT(zv, elem_size);
7394
0
          if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
7395
0
            continue;
7396
0
          }
7397
0
          if (UNEXPECTED(Z_ISREF_P(entry)) && Z_REFCOUNT_P(entry) == 1) {
7398
0
            entry = Z_REFVAL_P(entry);
7399
0
          }
7400
0
          Z_TRY_ADDREF_P(entry);
7401
0
          ZEND_HASH_FILL_ADD(entry);
7402
0
          n++;
7403
0
        }
7404
0
      } ZEND_HASH_FILL_END();
7405
0
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &chunk);
7406
0
      remaining -= chunk_size;
7407
0
    }
7408
0
    return;
7409
0
  }
7410
7411
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
7412
    /* If new chunk, create and initialize it. */
7413
0
    if (current == 0) {
7414
0
      if (ZEND_HAS_OOM()) {
7415
0
        RETURN_OOM();
7416
0
      }
7417
0
      array_init_size(&chunk, (uint32_t)size);
7418
0
      add_next_index_zval(return_value, &chunk);
7419
0
    }
7420
7421
    /* Add entry to the chunk, preserving keys. */
7422
0
    if (str_key) {
7423
0
      entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
7424
0
    } else {
7425
0
      entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
7426
0
    }
7427
0
    zval_add_ref(entry);
7428
7429
0
    if (++current == size) {
7430
0
      current = 0;
7431
0
    }
7432
0
  } ZEND_HASH_FOREACH_END();
7433
0
}
7434
/* }}} */
7435
7436
/* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
7437
PHP_FUNCTION(array_combine)
7438
0
{
7439
0
  HashTable *values, *keys;
7440
0
  uint32_t pos_values = 0;
7441
0
  zval *entry_keys, *entry_values;
7442
0
  int num_keys, num_values;
7443
7444
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7445
0
    Z_PARAM_ARRAY_HT(keys)
7446
0
    Z_PARAM_ARRAY_HT(values)
7447
0
  ZEND_PARSE_PARAMETERS_END();
7448
7449
0
  num_keys = zend_hash_num_elements(keys);
7450
0
  num_values = zend_hash_num_elements(values);
7451
7452
0
  if (num_keys != num_values) {
7453
0
    zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
7454
0
    RETURN_THROWS();
7455
0
  }
7456
7457
0
  if (!num_keys) {
7458
0
    RETURN_EMPTY_ARRAY();
7459
0
  }
7460
7461
0
  array_init_size(return_value, num_keys);
7462
0
  ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
7463
0
    while (1) {
7464
0
      if (pos_values >= values->nNumUsed) {
7465
0
        break;
7466
0
      }
7467
0
      entry_values = ZEND_HASH_ELEMENT(values, pos_values);
7468
0
      if (Z_TYPE_P(entry_values) != IS_UNDEF) {
7469
0
        if (Z_TYPE_P(entry_keys) == IS_LONG) {
7470
0
          entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
7471
0
            Z_LVAL_P(entry_keys), entry_values);
7472
0
        } else {
7473
0
          zend_string *tmp_key;
7474
0
          zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
7475
0
          entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
7476
0
            key, entry_values);
7477
0
          zend_tmp_string_release(tmp_key);
7478
0
        }
7479
0
        zval_add_ref(entry_values);
7480
0
        pos_values++;
7481
0
        break;
7482
0
      }
7483
0
      pos_values++;
7484
0
    }
7485
0
  } ZEND_HASH_FOREACH_END();
7486
0
}
7487
/* }}} */