Coverage Report

Created: 2026-09-14 06:25

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
16
#define DIFF_COMP_DATA_NONE    -1
45
0
#define DIFF_COMP_DATA_INTERNAL 0
46
16
#define DIFF_COMP_DATA_USER     1
47
0
#define DIFF_COMP_KEY_INTERNAL  0
48
0
#define DIFF_COMP_KEY_USER      1
49
50
30
#define INTERSECT_NORMAL    1
51
0
#define INTERSECT_KEY     2
52
15
#define INTERSECT_ASSOC     6
53
0
#define INTERSECT_COMP_DATA_NONE    -1
54
10
#define INTERSECT_COMP_DATA_INTERNAL 0
55
5
#define INTERSECT_COMP_DATA_USER     1
56
5
#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
63.5k
static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
89
63.5k
  if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
90
34.1k
    return 1;
91
34.1k
  } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
92
29.4k
    return -1;
93
29.4k
  } else {
94
0
    return 0;
95
0
  }
96
63.5k
}
97
98
220k
#define RETURN_STABLE_SORT(a, b, result) do { \
99
220k
  int _result = (result); \
100
220k
  if (EXPECTED(_result)) { \
101
157k
    return _result; \
102
157k
  } \
103
220k
  return stable_sort_fallback((a), (b)); \
104
220k
} while (0)
105
106
/* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
107
#define DEFINE_SORT_VARIANTS(name) \
108
18.1k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
109
18.1k
    return php_array_##name##_unstable_i(a, b); \
110
18.1k
  } \
Unexecuted instantiation: array.c:php_array_data_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_data_compare_string_case_unstable
array.c:php_array_data_compare_string_unstable
Line
Count
Source
108
17
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
109
17
    return php_array_##name##_unstable_i(a, b); \
110
17
  } \
Unexecuted instantiation: array.c:php_array_natural_case_compare_unstable
array.c:php_array_natural_compare_unstable
Line
Count
Source
108
7
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
109
7
    return php_array_##name##_unstable_i(a, b); \
110
7
  } \
Unexecuted instantiation: array.c:php_array_data_compare_string_locale_unstable
array.c:php_array_data_compare_unstable
Line
Count
Source
108
18.1k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
109
18.1k
    return php_array_##name##_unstable_i(a, b); \
110
18.1k
  } \
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
203k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
203k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
203k
  } \
array.c:php_array_natural_compare
Line
Count
Source
111
29.6k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
29.6k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
29.6k
  } \
array.c:php_array_natural_case_compare
Line
Count
Source
111
100
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
100
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
100
  } \
array.c:php_array_data_compare_numeric
Line
Count
Source
111
13.0k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
13.0k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
13.0k
  } \
array.c:php_array_data_compare_string_case
Line
Count
Source
111
173
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
173
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
173
  } \
array.c:php_array_data_compare_string
Line
Count
Source
111
78.3k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
78.3k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
78.3k
  } \
array.c:php_array_data_compare_string_locale
Line
Count
Source
111
3.56k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
3.56k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
3.56k
  } \
array.c:php_array_data_compare
Line
Count
Source
111
78.3k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
78.3k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
78.3k
  } \
array.c:php_array_key_compare_numeric
Line
Count
Source
111
31
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
31
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
31
  } \
array.c:php_array_key_compare_string_case
Line
Count
Source
111
2
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
2
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
2
  } \
array.c:php_array_key_compare_string
Line
Count
Source
111
25
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
25
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
25
  } \
Unexecuted instantiation: array.c:php_array_key_compare_string_locale
array.c:php_array_key_compare
Line
Count
Source
111
33
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
112
33
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
113
33
  } \
114
17.3k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
115
17.3k
    return php_array_##name##_unstable(a, b) * -1; \
116
17.3k
  } \
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
array.c:php_array_reverse_natural_compare_unstable
Line
Count
Source
114
7
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
115
7
    return php_array_##name##_unstable(a, b) * -1; \
116
7
  } \
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_locale_unstable
array.c:php_array_reverse_data_compare_unstable
Line
Count
Source
114
17.3k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
115
17.3k
    return php_array_##name##_unstable(a, b) * -1; \
116
17.3k
  } \
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
17.3k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
118
17.3k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
119
17.3k
  } \
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
array.c:php_array_reverse_natural_compare
Line
Count
Source
117
7
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
118
7
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
119
7
  } \
Unexecuted instantiation: array.c:php_array_reverse_data_compare_string_locale
array.c:php_array_reverse_data_compare
Line
Count
Source
117
17.3k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
118
17.3k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
119
17.3k
  } \
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
33
{
123
33
  zval first;
124
33
  zval second;
125
126
33
  if (f->key == NULL && s->key == NULL) {
127
9
    return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
128
24
  } else if (f->key && s->key) {
129
13
    return zendi_smart_strcmp(f->key, s->key);
130
13
  }
131
11
  if (f->key) {
132
11
    ZVAL_STR(&first, f->key);
133
11
  } else {
134
0
    ZVAL_LONG(&first, f->h);
135
0
  }
136
11
  if (s->key) {
137
0
    ZVAL_STR(&second, s->key);
138
11
  } else {
139
11
    ZVAL_LONG(&second, s->h);
140
11
  }
141
11
  return zend_compare(&first, &second);
142
33
}
143
/* }}} */
144
145
static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
146
31
{
147
31
  if (f->key == NULL && s->key == NULL) {
148
16
    return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
149
16
  } else {
150
15
    double d1, d2;
151
15
    if (f->key) {
152
9
      d1 = zend_strtod(f->key->val, NULL);
153
9
    } else {
154
6
      d1 = (double)(zend_long)f->h;
155
6
    }
156
15
    if (s->key) {
157
7
      d2 = zend_strtod(s->key->val, NULL);
158
8
    } else {
159
8
      d2 = (double)(zend_long)s->h;
160
8
    }
161
15
    return ZEND_THREEWAY_COMPARE(d1, d2);
162
15
  }
163
31
}
164
/* }}} */
165
166
static zend_always_inline int php_array_key_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
167
2
{
168
2
  const char *s1, *s2;
169
2
  size_t l1, l2;
170
2
  char buf1[MAX_LENGTH_OF_LONG + 1];
171
2
  char buf2[MAX_LENGTH_OF_LONG + 1];
172
173
2
  if (f->key) {
174
0
    s1 = f->key->val;
175
0
    l1 = f->key->len;
176
2
  } else {
177
2
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
178
2
    l1 = buf1 + sizeof(buf1) - 1 - s1;
179
2
  }
180
2
  if (s->key) {
181
0
    s2 = s->key->val;
182
0
    l2 = s->key->len;
183
2
  } else {
184
2
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
185
2
    l2 = buf2 + sizeof(buf2) - 1 - s2;
186
2
  }
187
2
  return zend_binary_strcasecmp_l(s1, l1, s2, l2);
188
2
}
189
/* }}} */
190
191
static zend_always_inline int php_array_key_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
192
25
{
193
25
  const char *s1, *s2;
194
25
  size_t l1, l2;
195
25
  char buf1[MAX_LENGTH_OF_LONG + 1];
196
25
  char buf2[MAX_LENGTH_OF_LONG + 1];
197
198
25
  if (f->key) {
199
11
    s1 = f->key->val;
200
11
    l1 = f->key->len;
201
14
  } else {
202
14
    s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
203
14
    l1 = buf1 + sizeof(buf1) - 1 - s1;
204
14
  }
205
25
  if (s->key) {
206
5
    s2 = s->key->val;
207
5
    l2 = s->key->len;
208
20
  } else {
209
20
    s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
210
20
    l2 = buf2 + sizeof(buf2) - 1 - s2;
211
20
  }
212
25
  return zend_binary_strcmp(s1, l1, s2, l2);
213
25
}
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
96.5k
{
287
96.5k
  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
96.5k
  zval *rhs = &s->val;
291
96.5k
  ZVAL_DEREF(rhs);
292
96.5k
  if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
293
590
   && result == ZEND_UNCOMPARABLE
294
493
   && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
295
493
    zval *lhs = &f->val;
296
493
    ZVAL_DEREF(lhs);
297
493
    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
493
      uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
300
493
      uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
301
493
      return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
302
493
    } else {
303
      // Shift enums to the end of the array
304
0
      return -1;
305
0
    }
306
493
  }
307
96.0k
  return result;
308
96.5k
}
309
/* }}} */
310
311
static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
312
13.0k
{
313
13.0k
  return numeric_compare_function(&f->val, &s->val);
314
13.0k
}
315
/* }}} */
316
317
static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
318
173
{
319
173
  return string_case_compare_function(&f->val, &s->val);
320
173
}
321
/* }}} */
322
323
static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
324
78.3k
{
325
78.3k
  return string_compare_function(&f->val, &s->val);
326
78.3k
}
327
/* }}} */
328
329
static int php_array_natural_general_compare(Bucket *f, Bucket *s, bool fold_case) /* {{{ */
330
29.7k
{
331
29.7k
  zend_string *tmp_str1, *tmp_str2;
332
29.7k
  zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
333
29.7k
  zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
334
335
29.7k
  int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
336
337
29.7k
  zend_tmp_string_release(tmp_str1);
338
29.7k
  zend_tmp_string_release(tmp_str2);
339
29.7k
  return result;
340
29.7k
}
341
/* }}} */
342
343
static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
344
29.6k
{
345
29.6k
  return php_array_natural_general_compare(a, b, false);
346
29.6k
}
347
/* }}} */
348
349
static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
350
100
{
351
100
  return php_array_natural_general_compare(a, b, true);
352
100
}
353
/* }}} */
354
355
static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
356
3.56k
{
357
3.56k
  return string_locale_compare_function(&f->val, &s->val);
358
3.56k
}
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
28
{
376
28
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
377
11
    case PHP_SORT_NUMERIC:
378
11
      return php_array_key_compare_numeric;
379
380
10
    case PHP_SORT_STRING:
381
10
      if (sort_type & PHP_SORT_FLAG_CASE) {
382
2
        return php_array_key_compare_string_case;
383
8
      } else {
384
8
        return php_array_key_compare_string;
385
8
      }
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
5
    case PHP_SORT_REGULAR:
398
7
    default:
399
7
      return php_array_key_compare;
400
28
  }
401
0
  return NULL;
402
28
}
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
869
{
436
869
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
437
60
    case PHP_SORT_NUMERIC:
438
60
      return php_array_data_compare_numeric;
439
440
158
    case PHP_SORT_STRING:
441
158
      if (sort_type & PHP_SORT_FLAG_CASE) {
442
14
        return php_array_data_compare_string_case;
443
144
      } else {
444
144
        return php_array_data_compare_string;
445
144
      }
446
447
222
    case PHP_SORT_NATURAL:
448
222
      if (sort_type & PHP_SORT_FLAG_CASE) {
449
5
        return php_array_natural_case_compare;
450
217
      } else {
451
217
        return php_array_natural_compare;
452
217
      }
453
454
60
    case PHP_SORT_LOCALE_STRING:
455
60
      return php_array_data_compare_string_locale;
456
457
249
    case PHP_SORT_REGULAR:
458
369
    default:
459
369
      return php_array_data_compare;
460
869
  }
461
0
  return NULL;
462
869
}
463
464
static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_type) /* {{{ */
465
711
{
466
711
  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
2
    case PHP_SORT_NATURAL:
478
2
      if (sort_type & PHP_SORT_FLAG_CASE) {
479
0
        return php_array_reverse_natural_case_compare;
480
2
      } else {
481
2
        return php_array_reverse_natural_compare;
482
2
      }
483
484
0
    case PHP_SORT_LOCALE_STRING:
485
0
      return php_array_reverse_data_compare_string_locale;
486
487
709
    case PHP_SORT_REGULAR:
488
709
    default:
489
709
      return php_array_reverse_data_compare;
490
711
  }
491
0
  return NULL;
492
711
}
493
494
static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, bool reverse) /* {{{ */
495
182
{
496
182
  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
12
    case PHP_SORT_STRING:
506
12
      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
12
      } else {
513
12
        if (reverse) {
514
0
          return php_array_reverse_data_compare_string_unstable;
515
12
        } else {
516
12
          return php_array_data_compare_string_unstable;
517
12
        }
518
12
      }
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
170
    case PHP_SORT_REGULAR:
546
170
    default:
547
170
      if (reverse) {
548
0
        return php_array_reverse_data_compare_unstable;
549
170
      } else {
550
170
        return php_array_data_compare_unstable;
551
170
      }
552
0
      break;
553
182
  }
554
0
  return NULL;
555
182
}
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
6
{
612
6
  zval *array;
613
6
  zend_long mode = PHP_COUNT_NORMAL;
614
6
  zend_long cnt;
615
616
15
  ZEND_PARSE_PARAMETERS_START(1, 2)
617
15
    Z_PARAM_ZVAL(array)
618
12
    Z_PARAM_OPTIONAL
619
12
    Z_PARAM_LONG(mode)
620
6
  ZEND_PARSE_PARAMETERS_END();
621
622
3
  if (mode != PHP_COUNT_NORMAL && mode != PHP_COUNT_RECURSIVE) {
623
1
    zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
624
1
    RETURN_THROWS();
625
1
  }
626
627
2
  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
2
    default:
663
2
      zend_argument_type_error(1, "must be of type Countable|array, %s given", zend_zval_value_name(array));
664
2
      RETURN_THROWS();
665
2
  }
666
2
}
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
1.61k
static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) {
699
1.61k
  HashTable *array;
700
1.61k
  zend_long sort_type = PHP_SORT_REGULAR;
701
1.61k
  bucket_compare_func_t cmp;
702
703
4.84k
  ZEND_PARSE_PARAMETERS_START(1, 2)
704
6.45k
    Z_PARAM_ARRAY_HT_EX(array, 0, 1)
705
1.60k
    Z_PARAM_OPTIONAL
706
4.57k
    Z_PARAM_LONG(sort_type)
707
1.61k
  ZEND_PARSE_PARAMETERS_END();
708
709
1.60k
  cmp = get_cmp(sort_type);
710
711
1.60k
  zend_array_sort(array, cmp, renumber);
712
713
1.60k
  RETURN_TRUE;
714
1.60k
}
715
716
/* {{{ Sort an array and maintain index association */
717
PHP_FUNCTION(asort)
718
40
{
719
40
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, false);
720
40
}
721
/* }}} */
722
723
/* {{{ Sort an array in reverse order and maintain index association */
724
PHP_FUNCTION(arsort)
725
599
{
726
599
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, false);
727
599
}
728
/* }}} */
729
730
/* {{{ Sort an array */
731
PHP_FUNCTION(sort)
732
836
{
733
836
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, true);
734
836
}
735
/* }}} */
736
737
/* {{{ Sort an array in reverse order */
738
PHP_FUNCTION(rsort)
739
112
{
740
112
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, true);
741
112
}
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
28
{
754
28
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_compare_func, false);
755
28
}
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
5
  zend_fcall_info old_user_compare_fci; \
803
5
  zend_fcall_info_cache old_user_compare_fci_cache \
804
805
#define PHP_ARRAY_CMP_FUNC_BACKUP() \
806
5
  old_user_compare_fci = BG(user_compare_fci); \
807
5
  old_user_compare_fci_cache = BG(user_compare_fci_cache); \
808
5
  ARRAYG(compare_deprecation_thrown) = 0; \
809
5
  BG(user_compare_fci_cache) = empty_fcall_info_cache; \
810
811
#define PHP_ARRAY_CMP_FUNC_RESTORE() \
812
5
  BG(user_compare_fci) = old_user_compare_fci; \
813
5
  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
3.12k
static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
931
3.12k
  if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
932
3.02k
    return Z_ARRVAL_P(zv);
933
3.02k
  }
934
935
108
  ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
936
108
  php_error_docref(NULL, E_DEPRECATED,
937
108
    "Calling %s() on an object is deprecated", get_active_function_name());
938
939
108
  zend_object *zobj = Z_OBJ_P(zv);
940
108
  if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
941
22
    if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
942
22
      GC_DELREF(zobj->properties);
943
22
    }
944
22
    zobj->properties = zend_array_dup(zobj->properties);
945
22
  }
946
108
  return zobj->handlers->get_properties(zobj);
947
108
}
948
949
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
950
1.02k
{
951
1.02k
  zval *entry;
952
953
1.02k
  while (true) {
954
1.02k
    if ((entry = zend_hash_get_current_data(array)) == NULL) {
955
120
      return NULL;
956
120
    }
957
958
905
    ZVAL_DEINDIRECT(entry);
959
960
    /* Possible with an uninitialized typed property */
961
905
    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
905
    } else {
972
905
      break;
973
905
    }
974
905
  }
975
976
905
  return entry;
977
1.02k
}
978
979
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
980
275
{
981
275
  zval *entry = php_array_iter_seek_current(array, forward_direction);
982
275
  if (EXPECTED(entry)) {
983
241
    RETURN_COPY_DEREF(entry);
984
241
  } else {
985
34
    RETURN_FALSE;
986
34
  }
987
275
}
988
989
/* {{{ Advances array argument's internal pointer to the last element and return it */
990
PHP_FUNCTION(end)
991
59
{
992
59
  zval *array_zv;
993
994
177
  ZEND_PARSE_PARAMETERS_START(1, 1)
995
236
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
996
59
  ZEND_PARSE_PARAMETERS_END();
997
998
55
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
999
55
  if (zend_hash_num_elements(array) == 0) {
1000
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1001
3
    RETURN_FALSE;
1002
3
  }
1003
52
  zend_hash_internal_pointer_end(array);
1004
1005
52
  if (USED_RET()) {
1006
21
    php_array_iter_return_current(return_value, array, false);
1007
21
  }
1008
52
}
1009
/* }}} */
1010
1011
/* {{{ Move array argument's internal pointer to the previous element and return it */
1012
PHP_FUNCTION(prev)
1013
44
{
1014
44
  zval *array_zv;
1015
1016
132
  ZEND_PARSE_PARAMETERS_START(1, 1)
1017
176
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1018
44
  ZEND_PARSE_PARAMETERS_END();
1019
1020
43
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1021
43
  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
43
  zend_hash_move_backwards(array);
1026
1027
43
  if (USED_RET()) {
1028
16
    php_array_iter_return_current(return_value, array, false);
1029
16
  }
1030
43
}
1031
/* }}} */
1032
1033
/* {{{ Move array argument's internal pointer to the next element and return it */
1034
PHP_FUNCTION(next)
1035
1.31k
{
1036
1.31k
  zval *array_zv;
1037
1038
3.93k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1039
5.23k
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1040
1.31k
  ZEND_PARSE_PARAMETERS_END();
1041
1042
1.30k
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1043
1.30k
  if (zend_hash_num_elements(array) == 0) {
1044
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1045
59
    RETURN_FALSE;
1046
59
  }
1047
1.24k
  zend_hash_move_forward(array);
1048
1049
1.24k
  if (USED_RET()) {
1050
35
    php_array_iter_return_current(return_value, array, true);
1051
35
  }
1052
1.24k
}
1053
/* }}} */
1054
1055
/* {{{ Set array argument's internal pointer to the first element and return it */
1056
PHP_FUNCTION(reset)
1057
809
{
1058
809
  zval *array_zv;
1059
1060
2.42k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1061
3.23k
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1062
809
  ZEND_PARSE_PARAMETERS_END();
1063
1064
803
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1065
803
  if (zend_hash_num_elements(array) == 0) {
1066
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1067
71
    RETURN_FALSE;
1068
71
  }
1069
732
  zend_hash_internal_pointer_reset(array);
1070
1071
732
  if (USED_RET()) {
1072
27
    php_array_iter_return_current(return_value, array, true);
1073
27
  }
1074
732
}
1075
/* }}} */
1076
1077
/* {{{ Return the element currently pointed to by the internal array pointer */
1078
PHP_FUNCTION(current)
1079
189
{
1080
189
  zval *array_zv;
1081
1082
561
  ZEND_PARSE_PARAMETERS_START(1, 1)
1083
732
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1084
189
  ZEND_PARSE_PARAMETERS_END();
1085
1086
176
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1087
176
  php_array_iter_return_current(return_value, array, true);
1088
176
}
1089
/* }}} */
1090
1091
/* {{{ Return the key of the element currently pointed to by the internal array pointer */
1092
PHP_FUNCTION(key)
1093
758
{
1094
758
  zval *array_zv;
1095
1096
2.27k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1097
3.02k
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1098
758
  ZEND_PARSE_PARAMETERS_END();
1099
1100
750
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1101
750
  zval *entry = php_array_iter_seek_current(array, true);
1102
750
  if (EXPECTED(entry)) {
1103
664
    zend_hash_get_current_key_zval(array, return_value);
1104
664
  }
1105
750
}
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
3
{
1134
3
  uint32_t argc;
1135
3
  zval *args = NULL;
1136
1137
9
  ZEND_PARSE_PARAMETERS_START(1, -1)
1138
9
    Z_PARAM_VARIADIC('+', args, argc)
1139
9
  ZEND_PARSE_PARAMETERS_END();
1140
1141
  /* mixed min ( array $values ) */
1142
3
  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
3
  } else {
1156
    /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1157
3
    zval *min;
1158
3
    uint32_t i;
1159
1160
3
    min = &args[0];
1161
3
    zend_long min_lval;
1162
3
    double min_dval;
1163
1164
3
    if (Z_TYPE_P(min) == IS_LONG) {
1165
3
      min_lval = Z_LVAL_P(min);
1166
1167
3
      for (i = 1; i < argc; i++) {
1168
3
        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
3
        } 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
3
        } else {
1178
3
          goto generic_compare;
1179
3
        }
1180
3
      }
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
3
      for (i = 1; i < argc; i++) {
1205
3
        generic_compare:
1206
3
        if (zend_compare(&args[i], min) < 0) {
1207
3
          min = &args[i];
1208
3
        }
1209
3
      }
1210
0
    }
1211
1212
3
    RETURN_COPY(min);
1213
3
  }
1214
3
}
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
38
{
1262
38
  zval *args = NULL;
1263
38
  uint32_t argc;
1264
1265
114
  ZEND_PARSE_PARAMETERS_START(1, -1)
1266
114
    Z_PARAM_VARIADIC('+', args, argc)
1267
114
  ZEND_PARSE_PARAMETERS_END();
1268
1269
  /* mixed max ( array $values ) */
1270
38
  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
38
  } else {
1284
    /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1285
38
    zval *max;
1286
38
    uint32_t i;
1287
1288
38
    max = &args[0];
1289
38
    zend_long max_lval;
1290
38
    double max_dval;
1291
1292
38
    if (Z_TYPE_P(max) == IS_LONG) {
1293
38
      max_lval = Z_LVAL_P(max);
1294
1295
86
      for (i = 1; i < argc; i++) {
1296
48
        if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1297
48
          if (max_lval < Z_LVAL(args[i])) {
1298
43
            max_lval = Z_LVAL(args[i]);
1299
43
            max = &args[i];
1300
43
          }
1301
48
        } 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
48
      }
1309
1310
38
      RETURN_LONG(max_lval);
1311
38
    } 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
38
}
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
68
{
1392
68
  zval args[3],   /* Arguments to userland function */
1393
68
     retval,    /* Return value - unused */
1394
68
     *zv;
1395
68
  HashTable *target_hash = HASH_OF(array);
1396
68
  HashPosition pos;
1397
68
  uint32_t ht_iter;
1398
68
  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
68
  zend_fcall_info fci = context->fci;
1403
1404
68
#ifdef ZEND_CHECK_STACK_LIMIT
1405
68
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
1406
0
    zend_call_stack_size_error();
1407
0
    return FAILURE;
1408
0
  }
1409
68
#endif
1410
1411
68
  if (zend_hash_num_elements(target_hash) == 0) {
1412
0
    return result;
1413
0
  }
1414
1415
  /* Set up known arguments */
1416
68
  ZVAL_UNDEF(&args[1]);
1417
68
  if (userdata) {
1418
0
    ZVAL_COPY_VALUE(&args[2], userdata);
1419
0
  }
1420
1421
68
  fci.retval = &retval;
1422
68
  fci.param_count = userdata ? 3 : 2;
1423
68
  fci.params = args;
1424
1425
68
  zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1426
68
  ht_iter = zend_hash_iterator_add(target_hash, pos);
1427
1428
  /* Iterate through hash */
1429
391
  do {
1430
    /* Retrieve value */
1431
391
    zv = zend_hash_get_current_data_ex(target_hash, &pos);
1432
391
    if (zv == NULL) {
1433
57
      break;
1434
57
    }
1435
1436
    /* Skip undefined indirect elements */
1437
334
    if (Z_TYPE_P(zv) == IS_INDIRECT) {
1438
263
      zv = Z_INDIRECT_P(zv);
1439
263
      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
263
      if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1446
263
        zend_property_info *prop_info =
1447
263
          zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1448
263
        if (prop_info) {
1449
183
          ZVAL_NEW_REF(zv, zv);
1450
183
          ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1451
183
        }
1452
263
      }
1453
263
    }
1454
1455
    /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1456
334
    ZVAL_MAKE_REF(zv);
1457
1458
    /* Retrieve key */
1459
334
    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
334
    zend_hash_move_forward_ex(target_hash, &pos);
1464
1465
    /* Back up hash position, as it may change */
1466
334
    EG(ht_iterators)[ht_iter].pos = pos;
1467
1468
334
    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
334
    } else {
1492
334
      ZVAL_COPY_VALUE(&args[0], zv);
1493
1494
      /* Call the userland function */
1495
334
      result = zend_call_function(&fci, &context->fci_cache);
1496
334
      zval_ptr_dtor(&retval);
1497
334
    }
1498
1499
334
    zval_ptr_dtor_str(&args[1]);
1500
1501
334
    if (result == FAILURE) {
1502
0
      break;
1503
0
    }
1504
1505
    /* Reload array and position -- both may have changed */
1506
334
    if (Z_TYPE_P(array) == IS_ARRAY) {
1507
71
      pos = zend_hash_iterator_pos_ex(ht_iter, array);
1508
71
      target_hash = Z_ARRVAL_P(array);
1509
263
    } else if (Z_TYPE_P(array) == IS_OBJECT) {
1510
263
      target_hash = Z_OBJPROP_P(array);
1511
263
      pos = zend_hash_iterator_pos(ht_iter, target_hash);
1512
263
    } 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
334
  } while (!EG(exception));
1518
1519
68
  zend_hash_iterator_del(ht_iter);
1520
68
  return result;
1521
68
}
1522
1523
/* {{{ Apply a user function to every member of an array */
1524
PHP_FUNCTION(array_walk)
1525
83
{
1526
83
  zval *array;
1527
83
  zval *userdata = NULL;
1528
83
  php_array_walk_context context;
1529
1530
246
  ZEND_PARSE_PARAMETERS_START(2, 3)
1531
320
    Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1532
370
    Z_PARAM_FUNC(context.fci, context.fci_cache)
1533
68
    Z_PARAM_OPTIONAL
1534
136
    Z_PARAM_ZVAL(userdata)
1535
136
  ZEND_PARSE_PARAMETERS_END();
1536
1537
68
  if (Z_TYPE_P(array) == IS_OBJECT) {
1538
53
    php_error_docref(NULL, E_DEPRECATED,
1539
53
      "Passing an object for argument #1 $array to array_walk() is deprecated, call get_object_vars() first instead");
1540
53
    if (UNEXPECTED(EG(exception))) {
1541
0
      RETURN_THROWS();
1542
0
    }
1543
53
  }
1544
1545
68
  php_array_walk(&context, array, userdata, /* recursive */ false);
1546
68
  RETURN_TRUE;
1547
68
}
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
214
{
1584
214
  zval *entry; /* pointer to array entry */
1585
214
  zend_ulong num_idx;
1586
214
  zend_string *str_idx;
1587
1588
214
  if (strict) {
1589
143
    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
143
    } else {
1605
122k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1606
122k
        ZVAL_DEREF(entry);
1607
122k
        if (fast_is_identical_function(value, entry)) {
1608
64
          if (behavior == 0) {
1609
64
            RETURN_TRUE;
1610
64
          } 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
64
        }
1618
122k
      } ZEND_HASH_FOREACH_END();
1619
143
    }
1620
143
  } else {
1621
71
    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
71
    } else if (Z_TYPE_P(value) == IS_STRING) {
1636
32.0k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1637
32.0k
        if (fast_equal_check_string(value, entry)) {
1638
34
          if (behavior == 0) {
1639
34
            RETURN_TRUE;
1640
34
          } 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
34
        }
1648
32.0k
      } ZEND_HASH_FOREACH_END();
1649
53
    } else {
1650
5.19k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1651
5.19k
        if (fast_equal_check_function(value, entry)) {
1652
13
          if (behavior == 0) {
1653
0
            RETURN_TRUE;
1654
13
          } else {
1655
13
            if (str_idx) {
1656
0
              RETURN_STR_COPY(str_idx);
1657
13
            } else {
1658
13
              RETURN_LONG(num_idx);
1659
13
            }
1660
13
          }
1661
13
        }
1662
5.19k
      } ZEND_HASH_FOREACH_END();
1663
18
    }
1664
71
  }
1665
1666
103
  RETURN_FALSE;
1667
103
}
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
230
{
1676
230
  zval *value,    /* value to check for */
1677
230
     *array;    /* array to check in */
1678
230
  bool strict = 0;  /* strict comparison or not */
1679
1680
681
  ZEND_PARSE_PARAMETERS_START(2, 3)
1681
884
    Z_PARAM_ZVAL(value)
1682
884
    Z_PARAM_ARRAY(array)
1683
214
    Z_PARAM_OPTIONAL
1684
714
    Z_PARAM_BOOL(strict)
1685
230
  ZEND_PARSE_PARAMETERS_END();
1686
1687
214
  _php_search_array(return_value, value, array, strict, behavior);
1688
214
}
1689
1690
/* {{{ Checks if the given value exists in the array */
1691
PHP_FUNCTION(in_array)
1692
214
{
1693
214
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1694
214
}
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
16
{
1726
16
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1727
16
}
1728
/* }}} */
1729
1730
static zend_always_inline bool php_valid_var_name(const zend_string *var_name) /* {{{ */
1731
36
{
1732
  /* first 256 bits for first character, and second 256 bits for the next */
1733
36
  static const uint32_t charset[8] = {
1734
       /*  31      0   63     32   95     64   127    96 */
1735
36
      0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1736
36
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1737
36
  static const uint32_t charset2[8] = {
1738
       /*  31      0   63     32   95     64   127    96 */
1739
36
      0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1740
36
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1741
1742
36
  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
36
  uint32_t ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[0];
1748
36
  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
33
  if (ZSTR_LEN(var_name) > 1) {
1754
16
    size_t i = 1;
1755
271
    do {
1756
271
      ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[i];
1757
271
      if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1758
7
        return false;
1759
7
      }
1760
271
    } while (++i < ZSTR_LEN(var_name));
1761
16
  }
1762
26
  return true;
1763
33
}
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
28
{
1915
28
  zend_long count = 0;
1916
28
  zend_string *var_name;
1917
28
  zval *entry, *orig_var;
1918
1919
28
  if (HT_IS_PACKED(arr)) {
1920
0
    return 0;
1921
0
  }
1922
124
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1923
124
    if (!var_name) {
1924
3
      continue;
1925
3
    }
1926
31
    if (!php_valid_var_name(var_name)) {
1927
10
      continue;
1928
10
    }
1929
21
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
1930
6
      zend_throw_error(NULL, "Cannot re-assign $this");
1931
6
      return -1;
1932
6
    }
1933
15
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1934
15
    if (orig_var) {
1935
7
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1936
7
        orig_var = Z_INDIRECT_P(orig_var);
1937
7
      }
1938
7
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1939
0
        continue;
1940
0
      }
1941
7
      ZVAL_DEREF(entry);
1942
7
      ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1943
7
      if (UNEXPECTED(EG(exception))) {
1944
0
        return -1;
1945
0
      }
1946
8
    } else {
1947
8
      ZVAL_DEREF(entry);
1948
8
      Z_TRY_ADDREF_P(entry);
1949
8
      zend_hash_add_new(symbol_table, var_name, entry);
1950
8
    }
1951
15
    count++;
1952
15
  } ZEND_HASH_FOREACH_END();
1953
1954
22
  return count;
1955
28
}
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
5
{
2462
5
  zend_long count = 0;
2463
5
  zend_string *var_name;
2464
5
  zval *entry, *orig_var;
2465
2466
5
  if (HT_IS_PACKED(arr)) {
2467
0
    return 0;
2468
0
  }
2469
20
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
2470
20
    if (!var_name) {
2471
0
      continue;
2472
0
    }
2473
5
    if (!php_valid_var_name(var_name)) {
2474
0
      continue;
2475
0
    }
2476
5
    if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) {
2477
0
      continue;
2478
0
    }
2479
5
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
2480
5
    if (orig_var) {
2481
5
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
2482
5
        orig_var = Z_INDIRECT_P(orig_var);
2483
5
        if (Z_TYPE_P(orig_var) == IS_UNDEF) {
2484
5
          ZVAL_COPY_DEREF(orig_var, entry);
2485
5
          count++;
2486
5
        }
2487
5
      }
2488
5
    } 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
5
  } ZEND_HASH_FOREACH_END();
2495
2496
5
  return count;
2497
5
}
2498
/* }}} */
2499
2500
/* {{{ Imports variables into symbol table from an array */
2501
PHP_FUNCTION(extract)
2502
80
{
2503
80
  zval *var_array_param;
2504
80
  zend_long extract_refs;
2505
80
  zend_long extract_type = PHP_EXTR_OVERWRITE;
2506
80
  zend_string *prefix = NULL;
2507
80
  zend_long count;
2508
80
  zend_array *symbol_table;
2509
2510
240
  ZEND_PARSE_PARAMETERS_START(1, 3)
2511
320
    Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2512
74
    Z_PARAM_OPTIONAL
2513
158
    Z_PARAM_LONG(extract_type)
2514
15
    Z_PARAM_STR(prefix)
2515
80
  ZEND_PARSE_PARAMETERS_END();
2516
2517
74
  extract_refs = (extract_type & PHP_EXTR_REFS);
2518
74
  if (extract_refs) {
2519
0
    SEPARATE_ARRAY(var_array_param);
2520
0
  }
2521
74
  extract_type &= 0xff;
2522
2523
74
  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
74
  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
74
  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
74
  if (zend_forbid_dynamic_call() == FAILURE) {
2541
41
    return;
2542
41
  }
2543
2544
33
  symbol_table = zend_rebuild_symbol_table();
2545
33
  ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
2546
2547
33
  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
33
  } else {
2572
    /* The array might be stored in a local variable that will be overwritten */
2573
33
    zval array_copy;
2574
33
    ZVAL_COPY(&array_copy, var_array_param);
2575
33
    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
28
      case PHP_EXTR_OVERWRITE:
2580
28
        count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
2581
28
        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
5
      default:
2595
5
        count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
2596
5
        break;
2597
33
    }
2598
33
    zval_ptr_dtor(&array_copy);
2599
33
  }
2600
2601
33
  RETURN_LONG(count);
2602
33
}
2603
/* }}} */
2604
2605
static zend_result php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
2606
71
{
2607
71
  zval *value_ptr, data;
2608
2609
71
  ZVAL_DEREF(entry);
2610
71
  if (Z_TYPE_P(entry) == IS_STRING) {
2611
71
    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
71
    } 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
71
    } else {
2622
71
      php_error_docref(NULL, E_WARNING, "Undefined variable $%pS", Z_STR_P(entry));
2623
      /* A user error handler may have thrown. */
2624
71
      return EG(exception) ? FAILURE : SUCCESS;
2625
71
    }
2626
71
  } 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
0
  } else {
2654
0
    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
0
    return EG(exception) ? FAILURE : SUCCESS;
2657
0
  }
2658
2659
0
  return SUCCESS;
2660
71
}
2661
/* }}} */
2662
2663
/* {{{ Creates a hash containing variables and their values */
2664
PHP_FUNCTION(compact)
2665
79
{
2666
79
  zval *args = NULL; /* function arguments array */
2667
79
  uint32_t num_args, i;
2668
79
  zend_array *symbol_table;
2669
2670
237
  ZEND_PARSE_PARAMETERS_START(1, -1)
2671
237
    Z_PARAM_VARIADIC('+', args, num_args)
2672
237
  ZEND_PARSE_PARAMETERS_END();
2673
2674
79
  if (zend_forbid_dynamic_call() == FAILURE) {
2675
8
    return;
2676
8
  }
2677
2678
71
  symbol_table = zend_rebuild_symbol_table();
2679
71
  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
71
  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
71
  } else {
2687
71
    array_init_size(return_value, num_args);
2688
71
  }
2689
2690
142
  for (i = 0; i < num_args; i++) {
2691
71
    if (UNEXPECTED(php_compact_var(symbol_table, return_value, &args[i], i + 1) == FAILURE)) {
2692
0
      RETURN_THROWS();
2693
0
    }
2694
71
  }
2695
71
}
2696
/* }}} */
2697
2698
/* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2699
PHP_FUNCTION(array_fill)
2700
255
{
2701
255
  zval *val;
2702
255
  zend_long start_key, num;
2703
2704
765
  ZEND_PARSE_PARAMETERS_START(3, 3)
2705
1.02k
    Z_PARAM_LONG(start_key)
2706
1.27k
    Z_PARAM_LONG(num)
2707
1.27k
    Z_PARAM_ZVAL(val)
2708
1.27k
  ZEND_PARSE_PARAMETERS_END();
2709
2710
255
  if (EXPECTED(num > 0)) {
2711
222
    if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2712
0
      zend_argument_value_error(2, "is too large");
2713
0
      RETURN_THROWS();
2714
222
    } 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
222
    } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2718
      /* create packed array */
2719
220
      zval *zv;
2720
2721
220
      array_init_size(return_value, (uint32_t)(start_key + num));
2722
220
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2723
220
      Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2724
220
      Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2725
220
      Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2726
2727
220
      if (Z_REFCOUNTED_P(val)) {
2728
26
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2729
26
      }
2730
2731
220
      zv = Z_ARRVAL_P(return_value)->arPacked;
2732
2733
250
      while (start_key--) {
2734
30
        ZVAL_UNDEF(zv);
2735
30
        zv++;
2736
30
      }
2737
1.77M
      while (num--) {
2738
1.77M
        ZVAL_COPY_VALUE(zv, val);
2739
1.77M
        zv++;
2740
1.77M
      }
2741
220
    } else {
2742
      /* create hash */
2743
2
      array_init_size(return_value, (uint32_t)num);
2744
2
      zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
2745
2
      if (Z_REFCOUNTED_P(val)) {
2746
2
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2747
2
      }
2748
2
      zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
2749
2
      while (--num) {
2750
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
2751
0
        start_key++;
2752
0
      }
2753
2
    }
2754
222
  } else if (EXPECTED(num == 0)) {
2755
32
    RETURN_EMPTY_ARRAY();
2756
32
  } else {
2757
1
    zend_argument_value_error(2, "must be greater than or equal to 0");
2758
1
    RETURN_THROWS();
2759
1
  }
2760
255
}
2761
/* }}} */
2762
2763
/* {{{ Create an array using the elements of the first parameter as keys each initialized to val */
2764
PHP_FUNCTION(array_fill_keys)
2765
30
{
2766
30
  zval *keys, *val, *entry;
2767
2768
87
  ZEND_PARSE_PARAMETERS_START(2, 2)
2769
108
    Z_PARAM_ARRAY(keys)
2770
105
    Z_PARAM_ZVAL(val)
2771
105
  ZEND_PARSE_PARAMETERS_END();
2772
2773
  /* Initialize return array */
2774
21
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2775
2776
211
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2777
211
    ZVAL_DEREF(entry);
2778
211
    if (Z_TYPE_P(entry) == IS_LONG) {
2779
29
      zend_hash_index_add(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2780
66
    } else {
2781
66
      zend_string *tmp_key;
2782
66
      zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2783
66
      zend_symtable_add(Z_ARRVAL_P(return_value), key, val);
2784
66
      zend_tmp_string_release(tmp_key);
2785
66
    }
2786
211
  } ZEND_HASH_FOREACH_END();
2787
2788
21
  if (Z_REFCOUNTED_P(val)) {
2789
0
    GC_ADDREF_EX(Z_COUNTED_P(val), zend_hash_num_elements(Z_ARRVAL_P(return_value)));
2790
0
  }
2791
21
}
2792
/* }}} */
2793
2794
23
#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2795
23
    double __calc_size = ((start - end) / (_step)) + 1; \
2796
23
    if (__calc_size >= (double)HT_MAX_SIZE) { \
2797
17
      double __exceed_by = __calc_size - (double)HT_MAX_SIZE; \
2798
17
      zend_value_error(\
2799
17
        "The supplied range exceeds the maximum array size by %.1f elements: " \
2800
17
        "start=%.1f, end=%.1f, step=%.1f. Max size: %.0f", \
2801
17
        __exceed_by, end, start, (_step), (double)HT_MAX_SIZE); \
2802
17
      RETURN_THROWS(); \
2803
17
    } \
2804
23
    size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
2805
6
    array_init_size(return_value, size); \
2806
6
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2807
6
  } while (0)
2808
2809
811
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2810
811
    zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2811
811
    if (__calc_size >= HT_MAX_SIZE - 1) { \
2812
3
      uint64_t __excess = __calc_size - (HT_MAX_SIZE - 1); \
2813
3
      zend_value_error(\
2814
3
        "The supplied range exceeds the maximum array size by %" PRIu64 " elements: " \
2815
3
        "start=" ZEND_LONG_FMT ", end=" ZEND_LONG_FMT ", step=" ZEND_LONG_FMT ". " \
2816
3
        "Calculated size: %" PRIu64 ". Maximum size: %" PRIu64 ".", \
2817
3
        __excess, end, start, (_step), (uint64_t)__calc_size, (uint64_t)HT_MAX_SIZE); \
2818
3
      RETURN_THROWS(); \
2819
3
    } \
2820
811
    size = (uint32_t)(__calc_size + 1); \
2821
808
    array_init_size(return_value, size); \
2822
808
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2823
808
  } while (0)
2824
2825
/* Process input for the range() function
2826
 * 0 on exceptions
2827
 * IS_LONG if only interpretable as int
2828
 * IS_DOUBLE if only interpretable as float
2829
 * IS_STRING if only interpretable as string
2830
 * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
2831
static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
2832
1.75k
{
2833
1.75k
  switch (Z_TYPE_P(input)) {
2834
1.64k
    case IS_LONG:
2835
1.64k
      *lval = Z_LVAL_P(input);
2836
1.64k
      *dval = (double) Z_LVAL_P(input);
2837
1.64k
      return IS_LONG;
2838
35
    case IS_DOUBLE:
2839
35
      *dval = Z_DVAL_P(input);
2840
35
      check_dval_value:
2841
35
      if (zend_isinf(*dval)) {
2842
3
        zend_argument_value_error(arg_num, "must be a finite number, INF provided");
2843
3
        return 0;
2844
3
      }
2845
32
      if (zend_isnan(*dval)) {
2846
0
        zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
2847
0
        return 0;
2848
0
      }
2849
32
      return IS_DOUBLE;
2850
70
    case IS_STRING: {
2851
      /* Process strings:
2852
       * - Empty strings are converted to 0 with a diagnostic
2853
       * - Check if string is numeric and store the values in passed pointer
2854
       * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2855
       * - If numeric int, check it is one byte or not
2856
       *   - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2857
       *   - If not should only be interpreted as int, return IS_LONG;
2858
       * - Otherwise is a string and return IS_STRING */
2859
70
      if (Z_STRLEN_P(input) == 0) {
2860
1
        const char *arg_name = get_active_function_arg_name(arg_num);
2861
1
        php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
2862
1
        if (UNEXPECTED(EG(exception))) {
2863
0
          return 0;
2864
0
        }
2865
1
        *lval = 0;
2866
1
        *dval = 0.0;
2867
1
        return IS_LONG;
2868
1
      }
2869
69
      uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2870
69
      if (type == IS_DOUBLE) {
2871
0
        goto check_dval_value;
2872
0
      }
2873
69
      if (type == IS_LONG) {
2874
4
        *dval = (double) *lval;
2875
4
        if (Z_STRLEN_P(input) == 1) {
2876
3
          return IS_ARRAY;
2877
3
        } else {
2878
1
          return IS_LONG;
2879
1
        }
2880
4
      }
2881
65
      if (Z_STRLEN_P(input) != 1) {
2882
26
        const char *arg_name = get_active_function_arg_name(arg_num);
2883
26
        php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2884
26
        if (UNEXPECTED(EG(exception))) {
2885
0
          return 0;
2886
0
        }
2887
26
      }
2888
      /* Set fall back values to 0 in case the other argument is not a string */
2889
65
      *lval = 0;
2890
65
      *dval = 0.0;
2891
65
      return IS_STRING;
2892
65
    }
2893
0
    default: ZEND_UNREACHABLE();
2894
1.75k
  }
2895
1.75k
}
2896
2897
/* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2898
PHP_FUNCTION(range)
2899
882
{
2900
882
  zval *user_start, *user_end, *user_step = NULL, tmp;
2901
882
  bool is_step_double = false;
2902
882
  bool is_step_negative = false;
2903
882
  double step_double = 1.0;
2904
882
  zend_long step = 1;
2905
2906
2.64k
  ZEND_PARSE_PARAMETERS_START(2, 3)
2907
3.51k
    Z_PARAM_NUMBER_OR_STR(user_start)
2908
4.39k
    Z_PARAM_NUMBER_OR_STR(user_end)
2909
878
    Z_PARAM_OPTIONAL
2910
1.82k
    Z_PARAM_NUMBER(user_step)
2911
882
  ZEND_PARSE_PARAMETERS_END();
2912
2913
877
  if (user_step) {
2914
34
    if (UNEXPECTED(Z_TYPE_P(user_step) == IS_DOUBLE)) {
2915
4
      step_double = Z_DVAL_P(user_step);
2916
2917
4
      if (zend_isinf(step_double)) {
2918
0
        zend_argument_value_error(3, "must be a finite number, INF provided");
2919
0
        RETURN_THROWS();
2920
0
      }
2921
4
      if (zend_isnan(step_double)) {
2922
0
        zend_argument_value_error(3, "must be a finite number, NAN provided");
2923
0
        RETURN_THROWS();
2924
0
      }
2925
2926
      /* We only want positive step values. */
2927
4
      if (step_double < 0.0) {
2928
1
        is_step_negative = true;
2929
1
        step_double *= -1;
2930
1
      }
2931
4
      step = zend_dval_to_lval_silent(step_double);
2932
4
      if (!zend_is_long_compatible(step_double, step)) {
2933
4
        is_step_double = true;
2934
4
      }
2935
30
    } else {
2936
30
      step = Z_LVAL_P(user_step);
2937
      /* We only want positive step values. */
2938
30
      if (step < 0) {
2939
6
        if (UNEXPECTED(step == ZEND_LONG_MIN)) {
2940
0
          zend_argument_value_error(3, "must be greater than " ZEND_LONG_FMT, step);
2941
0
          RETURN_THROWS();
2942
0
        }
2943
6
        is_step_negative = true;
2944
6
        step *= -1;
2945
6
      }
2946
30
      step_double = (double) step;
2947
30
    }
2948
34
    if (step_double == 0.0) {
2949
0
      zend_argument_value_error(3, "cannot be 0");
2950
0
      RETURN_THROWS();
2951
0
    }
2952
34
  }
2953
2954
877
  uint8_t start_type;
2955
877
  double start_double;
2956
877
  zend_long start_long;
2957
877
  uint8_t end_type;
2958
877
  double end_double;
2959
877
  zend_long end_long;
2960
2961
877
  start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2962
877
  if (start_type == 0) {
2963
2
    RETURN_THROWS();
2964
2
  }
2965
875
  end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2966
875
  if (end_type == 0) {
2967
1
    RETURN_THROWS();
2968
1
  }
2969
2970
  /* If the range is given as strings, generate an array of characters. */
2971
874
  if (start_type >= IS_STRING || end_type >= IS_STRING) {
2972
    /* If one of the inputs is NOT a string nor single-byte string */
2973
38
    if (UNEXPECTED(start_type < IS_STRING || end_type < IS_STRING)) {
2974
9
      if (start_type < IS_STRING) {
2975
2
        if (end_type != IS_ARRAY) {
2976
1
          php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if"
2977
1
            " argument #2 ($end) is a single byte string, argument #2 ($end) converted to 0");
2978
1
        }
2979
2
        end_type = IS_LONG;
2980
7
      } else if (end_type < IS_STRING) {
2981
7
        if (start_type != IS_ARRAY) {
2982
7
          php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a single byte string if"
2983
7
            " argument #1 ($start) is a single byte string, argument #1 ($start) converted to 0");
2984
7
        }
2985
7
        start_type = IS_LONG;
2986
7
      }
2987
9
      if (UNEXPECTED(EG(exception))) {
2988
0
        RETURN_THROWS();
2989
0
      }
2990
9
      goto handle_numeric_inputs;
2991
9
    }
2992
2993
29
    if (is_step_double) {
2994
      /* Only emit warning if one of the input is not a numeric digit */
2995
1
      if (start_type == IS_STRING || end_type == IS_STRING) {
2996
1
        php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2997
1
          " of characters, inputs converted to 0");
2998
1
        if (UNEXPECTED(EG(exception))) {
2999
0
          RETURN_THROWS();
3000
0
        }
3001
1
      }
3002
1
      end_type = IS_LONG;
3003
1
      start_type = IS_LONG;
3004
1
      goto handle_numeric_inputs;
3005
1
    }
3006
3007
    /* Generate array of characters, as zero-extended ints to make bounds checking possible in the loop condition */
3008
28
    int low = (unsigned char) Z_STRVAL_P(user_start)[0];
3009
28
    int high = (unsigned char) Z_STRVAL_P(user_end)[0];
3010
3011
    /* Decreasing char range */
3012
28
    if (low > high) {
3013
3
      if (low - high < step) {
3014
0
        goto boundary_error;
3015
0
      }
3016
      /* Initialize the return_value as an array. */
3017
3
      array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
3018
3
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3019
3
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3020
312
        for (; low >= high; low -= step) {
3021
309
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3022
309
          ZEND_HASH_FILL_NEXT();
3023
309
        }
3024
3
      } ZEND_HASH_FILL_END();
3025
25
    } else if (high > low) { /* Increasing char range */
3026
22
      if (is_step_negative) {
3027
1
        goto negative_step_error;
3028
1
      }
3029
21
      if (high - low < step) {
3030
0
        goto boundary_error;
3031
0
      }
3032
21
      array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
3033
21
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3034
21
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3035
503
        for (; low <= high; low += step) {
3036
482
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3037
482
          ZEND_HASH_FILL_NEXT();
3038
482
        }
3039
21
      } ZEND_HASH_FILL_END();
3040
21
    } else {
3041
3
      array_init(return_value);
3042
3
      ZVAL_CHAR(&tmp, low);
3043
3
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3044
3
    }
3045
27
    return;
3046
28
  }
3047
3048
846
  handle_numeric_inputs:
3049
846
  if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
3050
29
    double element;
3051
29
    uint32_t i, size;
3052
3053
    /* Decreasing float range */
3054
29
    if (start_double > end_double) {
3055
22
      if (start_double - end_double < step_double) {
3056
4
        goto boundary_error;
3057
4
      }
3058
3059
18
      RANGE_CHECK_DOUBLE_INIT_ARRAY(start_double, end_double, step_double);
3060
3061
2
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3062
24
        for (i = 0, element = start_double; i < size && element >= end_double; ++i, element = start_double - (i * step_double)) {
3063
22
          ZEND_HASH_FILL_SET_DOUBLE(element);
3064
22
          ZEND_HASH_FILL_NEXT();
3065
22
        }
3066
2
      } ZEND_HASH_FILL_END();
3067
7
    } else if (end_double > start_double) { /* Increasing float range */
3068
6
      if (is_step_negative) {
3069
0
        goto negative_step_error;
3070
0
      }
3071
6
      if (end_double - start_double < step_double) {
3072
1
        goto boundary_error;
3073
1
      }
3074
3075
5
      RANGE_CHECK_DOUBLE_INIT_ARRAY(end_double, start_double, step_double);
3076
3077
4
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3078
56
        for (i = 0, element = start_double; i < size && element <= end_double; ++i, element = start_double + (i * step_double)) {
3079
52
          ZEND_HASH_FILL_SET_DOUBLE(element);
3080
52
          ZEND_HASH_FILL_NEXT();
3081
52
        }
3082
4
      } ZEND_HASH_FILL_END();
3083
4
    } else {
3084
1
      array_init(return_value);
3085
1
      ZVAL_DOUBLE(&tmp, start_double);
3086
1
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3087
1
    }
3088
817
  } else {
3089
817
    ZEND_ASSERT(start_type == IS_LONG && end_type == IS_LONG && !is_step_double);
3090
    /* unsigned_step is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
3091
817
    zend_ulong unsigned_step= (zend_ulong)step;
3092
817
    uint32_t i, size;
3093
3094
    /* Decreasing int range */
3095
817
    if (start_long > end_long) {
3096
128
      if ((zend_ulong)start_long - end_long < unsigned_step) {
3097
0
        goto boundary_error;
3098
0
      }
3099
3100
128
      RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3101
3102
126
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3103
407
        for (i = 0; i < size; ++i) {
3104
281
          ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3105
281
          ZEND_HASH_FILL_NEXT();
3106
281
        }
3107
126
      } ZEND_HASH_FILL_END();
3108
689
    } else if (end_long > start_long) { /* Increasing int range */
3109
684
      if (is_step_negative) {
3110
1
        goto negative_step_error;
3111
1
      }
3112
683
      if ((zend_ulong)end_long - start_long < unsigned_step) {
3113
0
        goto boundary_error;
3114
0
      }
3115
3116
683
      RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3117
3118
682
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3119
794k
        for (i = 0; i < size; ++i) {
3120
794k
          ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3121
794k
          ZEND_HASH_FILL_NEXT();
3122
794k
        }
3123
680
      } ZEND_HASH_FILL_END();
3124
682
    } else {
3125
5
      array_init(return_value);
3126
5
      ZVAL_LONG(&tmp, start_long);
3127
5
      zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
3128
5
    }
3129
817
  }
3130
818
  return;
3131
3132
818
negative_step_error:
3133
2
  zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3134
2
  RETURN_THROWS();
3135
3136
5
boundary_error:
3137
5
  zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3138
5
  RETURN_THROWS();
3139
5
}
3140
/* }}} */
3141
3142
#undef RANGE_CHECK_DOUBLE_INIT_ARRAY
3143
#undef RANGE_CHECK_LONG_INIT_ARRAY
3144
3145
/* {{{ php_array_data_shuffle */
3146
PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *array) /* {{{ */
3147
0
{
3148
0
  const php_random_algo *algo = engine.algo;
3149
0
  void *state = engine.state;
3150
3151
0
  int64_t idx, j, n_elems, rnd_idx, n_left;
3152
0
  zval *zv, temp;
3153
0
  HashTable *hash;
3154
3155
0
  n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
3156
3157
0
  if (n_elems < 1) {
3158
0
    return true;
3159
0
  }
3160
3161
0
  hash = Z_ARRVAL_P(array);
3162
0
  n_left = n_elems;
3163
3164
0
  if (!HT_IS_PACKED(hash)) {
3165
0
    if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
3166
0
      Bucket *p = hash->arData;
3167
0
      zend_long i = hash->nNumUsed;
3168
3169
0
      for (; i > 0; p++, i--) {
3170
0
        if (p->key) {
3171
0
          zend_string_release(p->key);
3172
0
          p->key = NULL;
3173
0
        }
3174
0
      }
3175
0
    }
3176
0
    zend_hash_to_packed(hash);
3177
0
  }
3178
3179
0
  if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
3180
0
    if (hash->nNumUsed != hash->nNumOfElements) {
3181
0
      for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3182
0
        zv = hash->arPacked + idx;
3183
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3184
0
        if (j != idx) {
3185
0
          ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3186
0
        }
3187
0
        j++;
3188
0
      }
3189
0
    }
3190
0
    while (--n_left) {
3191
0
      rnd_idx = algo->range(state, 0, n_left);
3192
0
      if (EG(exception)) {
3193
0
        return false;
3194
0
      }
3195
0
      if (rnd_idx != n_left) {
3196
0
        ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3197
0
        ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3198
0
        ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3199
0
      }
3200
0
    }
3201
0
  } else {
3202
0
    zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
3203
3204
0
    if (hash->nNumUsed != hash->nNumOfElements) {
3205
0
      for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
3206
0
        zv = hash->arPacked + idx;
3207
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
3208
0
        if (j != idx) {
3209
0
          ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
3210
0
          if (idx == iter_pos) {
3211
0
            zend_hash_iterators_update(hash, idx, j);
3212
0
            iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
3213
0
          }
3214
0
        }
3215
0
        j++;
3216
0
      }
3217
0
    }
3218
0
    while (--n_left) {
3219
0
      rnd_idx = algo->range(state, 0, n_left);
3220
0
      if (EG(exception)) {
3221
0
        return false;
3222
0
      }
3223
0
      if (rnd_idx != n_left) {
3224
0
        ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
3225
0
        ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
3226
0
        ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
3227
0
        zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
3228
0
      }
3229
0
    }
3230
0
  }
3231
0
  hash->nNumUsed = n_elems;
3232
0
  hash->nInternalPointer = 0;
3233
0
  hash->nNextFreeElement = n_elems;
3234
3235
0
  return true;
3236
0
}
3237
/* }}} */
3238
3239
/* {{{ Randomly shuffle the contents of an array */
3240
PHP_FUNCTION(shuffle)
3241
0
{
3242
0
  zval *array;
3243
3244
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3245
0
    Z_PARAM_ARRAY_EX(array, 0, 1)
3246
0
  ZEND_PARSE_PARAMETERS_END();
3247
3248
0
  php_array_data_shuffle(php_random_default_engine(), array);
3249
3250
0
  RETURN_TRUE;
3251
0
}
3252
/* }}} */
3253
3254
static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
3255
776
{
3256
776
  HashTable    out_hash;      /* Output hashtable */
3257
776
  zend_long  num_in;      /* Number of entries in the input hashtable */
3258
776
  zend_long  pos;       /* Current position in the hashtable */
3259
776
  uint32_t     idx;
3260
776
  zval    *entry;       /* Hash entry */
3261
776
  uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3262
3263
776
  GC_ADDREF(in_hash);
3264
776
  HT_ALLOW_COW_VIOLATION(in_hash); /* Will be reset when setting the flags for in_hash */
3265
3266
  /* Get number of entries in the input hash */
3267
776
  num_in = zend_hash_num_elements(in_hash);
3268
3269
  /* Clamp the offset.. */
3270
776
  if (offset > num_in) {
3271
30
    offset = num_in;
3272
746
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3273
0
    offset = 0;
3274
0
  }
3275
3276
  /* ..and the length */
3277
776
  if (length < 0) {
3278
12
    length = num_in - offset + length;
3279
764
  } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3280
14
    length = num_in - offset;
3281
14
  }
3282
3283
  /* Create and initialize output hash */
3284
776
  zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
3285
3286
776
  if (HT_IS_PACKED(in_hash)) {
3287
    /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3288
707
    entry = in_hash->arPacked;
3289
884
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3290
177
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3291
3292
177
      zend_hash_next_index_insert_new(&out_hash, entry);
3293
177
      if (idx == iter_pos) {
3294
39
        if ((zend_long)idx != pos) {
3295
0
          zend_hash_iterators_update(in_hash, idx, pos);
3296
0
        }
3297
39
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3298
39
      }
3299
177
      pos++;
3300
177
    }
3301
3302
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3303
707
    if (removed != NULL) {
3304
684
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3305
339
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3306
339
        pos++;
3307
339
        Z_TRY_ADDREF_P(entry);
3308
339
        zend_hash_next_index_insert_new(removed, entry);
3309
339
        zend_hash_packed_del_val(in_hash, entry);
3310
        /* Bump iterator positions to the element after replacement. */
3311
339
        if (idx == iter_pos) {
3312
0
          zend_hash_iterators_update(in_hash, idx, offset + length);
3313
0
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3314
0
        }
3315
339
      }
3316
362
    } else { /* otherwise just skip those entries */
3317
362
      zend_long pos2 = pos;
3318
3319
1.11k
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3320
753
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3321
753
        pos2++;
3322
753
        zend_hash_packed_del_val(in_hash, entry);
3323
        /* Bump iterator positions to the element after replacement. */
3324
753
        if (idx == iter_pos) {
3325
43
          zend_hash_iterators_update(in_hash, idx, offset + length);
3326
43
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3327
43
        }
3328
753
      }
3329
362
    }
3330
3331
    /* If there are entries to insert.. */
3332
707
    if (replace) {
3333
340
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3334
340
        Z_TRY_ADDREF_P(entry);
3335
340
        zend_hash_next_index_insert_new(&out_hash, entry);
3336
340
        pos++;
3337
340
      } ZEND_HASH_FOREACH_END();
3338
66
    }
3339
3340
    /* Copy the remaining input hash entries to the output hash */
3341
707
    entry = in_hash->arPacked + idx;
3342
875
    for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3343
168
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3344
168
      zend_hash_next_index_insert_new(&out_hash, entry);
3345
168
      if (idx == iter_pos) {
3346
75
        if ((zend_long)idx != pos) {
3347
61
          zend_hash_iterators_update(in_hash, idx, pos);
3348
61
        }
3349
75
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3350
75
      }
3351
168
      pos++;
3352
168
    }
3353
707
  } else {
3354
69
    Bucket *p = in_hash->arData;
3355
3356
    /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
3357
69
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, p++) {
3358
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3359
0
      entry = &p->val;
3360
3361
      /* Update output hash depending on key type */
3362
0
      if (p->key == NULL) {
3363
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3364
0
      } else {
3365
0
        zend_hash_add_new(&out_hash, p->key, entry);
3366
0
      }
3367
0
      if (idx == iter_pos) {
3368
0
        if ((zend_long)idx != pos) {
3369
0
          zend_hash_iterators_update(in_hash, idx, pos);
3370
0
        }
3371
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3372
0
      }
3373
0
      pos++;
3374
0
    }
3375
3376
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3377
69
    if (removed != NULL) {
3378
6
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3379
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
3380
0
        pos++;
3381
0
        entry = &p->val;
3382
0
        Z_TRY_ADDREF_P(entry);
3383
0
        if (p->key == NULL) {
3384
0
          zend_hash_next_index_insert_new(removed, entry);
3385
0
        } else {
3386
0
          zend_hash_add_new(removed, p->key, entry);
3387
0
        }
3388
0
        zend_hash_del_bucket(in_hash, p);
3389
0
      }
3390
63
    } else { /* otherwise just skip those entries */
3391
63
      zend_long pos2 = pos;
3392
3393
63
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3394
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
3395
0
        pos2++;
3396
0
        zend_hash_del_bucket(in_hash, p);
3397
0
      }
3398
63
    }
3399
69
    iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3400
3401
    /* If there are entries to insert.. */
3402
69
    if (replace) {
3403
228
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3404
228
        Z_TRY_ADDREF_P(entry);
3405
228
        zend_hash_next_index_insert_new(&out_hash, entry);
3406
228
        pos++;
3407
228
      } ZEND_HASH_FOREACH_END();
3408
60
    }
3409
3410
    /* Copy the remaining input hash entries to the output hash */
3411
69
    for ( ; idx < in_hash->nNumUsed ; idx++, p++) {
3412
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3413
0
      entry = &p->val;
3414
0
      if (p->key == NULL) {
3415
0
        zend_hash_next_index_insert_new(&out_hash, entry);
3416
0
      } else {
3417
0
        zend_hash_add_new(&out_hash, p->key, entry);
3418
0
      }
3419
0
      if (idx == iter_pos) {
3420
0
        if ((zend_long)idx != pos) {
3421
0
          zend_hash_iterators_update(in_hash, idx, pos);
3422
0
        }
3423
0
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3424
0
      }
3425
0
      pos++;
3426
0
    }
3427
69
  }
3428
3429
  /* replace HashTable data */
3430
776
  HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3431
776
  HT_SET_ITERATORS_COUNT(in_hash, 0);
3432
776
  in_hash->pDestructor = NULL;
3433
3434
  /* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset().
3435
   * This avoids the COW violation assertion and delays advancing to the first valid position
3436
   * until after we've switched to the new array structure (out_hash). The iterator will be
3437
   * advanced when actually accessed, at which point it will find valid indexes in the new array. */
3438
776
  in_hash->nInternalPointer = 0;
3439
3440
776
  if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
3441
    /* Array was completely deallocated during the operation */
3442
0
    zend_array_destroy(in_hash);
3443
0
    zend_hash_destroy(&out_hash);
3444
0
    zend_throw_error(NULL, "Array was modified during array_splice operation");
3445
0
    return;
3446
0
  }
3447
3448
776
  zend_hash_destroy(in_hash);
3449
3450
776
  HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
3451
776
  in_hash->nTableSize        = out_hash.nTableSize;
3452
776
  in_hash->nTableMask        = out_hash.nTableMask;
3453
776
  in_hash->nNumUsed          = out_hash.nNumUsed;
3454
776
  in_hash->nNumOfElements    = out_hash.nNumOfElements;
3455
776
  in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
3456
776
  in_hash->arData            = out_hash.arData;
3457
776
  in_hash->pDestructor       = out_hash.pDestructor;
3458
776
}
3459
/* }}} */
3460
3461
/* {{{ Pushes elements onto the end of the array */
3462
PHP_FUNCTION(array_push)
3463
27
{
3464
27
  zval   *args,   /* Function arguments array */
3465
27
       *stack;    /* Input array */
3466
27
  uint32_t argc;    /* Number of function arguments */
3467
3468
3469
81
  ZEND_PARSE_PARAMETERS_START(1, -1)
3470
108
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3471
24
    Z_PARAM_VARIADIC('+', args, argc)
3472
27
  ZEND_PARSE_PARAMETERS_END();
3473
3474
  /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
3475
28
  for (uint32_t i = 0; i < argc; i++) {
3476
14
    Z_TRY_ADDREF(args[i]);
3477
3478
14
    if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &args[i]) == NULL) {
3479
0
      Z_TRY_DELREF(args[i]);
3480
0
      zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
3481
0
      RETURN_THROWS();
3482
0
    }
3483
14
  }
3484
3485
  /* Clean up and return the number of values in the stack */
3486
14
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3487
14
}
3488
/* }}} */
3489
3490
/* {{{ Pops an element off the end of the array */
3491
PHP_FUNCTION(array_pop)
3492
759
{
3493
759
  zval *stack,  /* Input stack */
3494
759
     *val;    /* Value to be popped */
3495
759
  uint32_t idx;
3496
3497
2.27k
  ZEND_PARSE_PARAMETERS_START(1, 1)
3498
3.03k
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3499
759
  ZEND_PARSE_PARAMETERS_END();
3500
3501
746
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3502
263
    return;
3503
263
  }
3504
3505
483
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3506
    /* Get the last value and copy it into the return value */
3507
354
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3508
354
    while (1) {
3509
354
      if (idx == 0) {
3510
0
        return;
3511
0
      }
3512
354
      idx--;
3513
354
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3514
354
      if (Z_TYPE_P(val) != IS_UNDEF) {
3515
354
        break;
3516
354
      }
3517
354
    }
3518
354
    RETVAL_COPY_VALUE(val);
3519
354
    ZVAL_UNDEF(val);
3520
3521
354
    if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3522
354
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3523
354
    }
3524
3525
    /* Delete the last value */
3526
354
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3527
354
  } else {
3528
129
    Bucket *p;
3529
3530
    /* Get the last value and copy it into the return value */
3531
129
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3532
129
    while (1) {
3533
129
      if (idx == 0) {
3534
0
        return;
3535
0
      }
3536
129
      idx--;
3537
129
      p = Z_ARRVAL_P(stack)->arData + idx;
3538
129
      val = &p->val;
3539
129
      if (Z_TYPE_P(val) != IS_UNDEF) {
3540
129
        break;
3541
129
      }
3542
129
    }
3543
129
    RETVAL_COPY_VALUE(val);
3544
129
    ZVAL_UNDEF(val);
3545
3546
129
    if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3547
128
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3548
128
    }
3549
3550
    /* Delete the last value */
3551
129
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3552
129
  }
3553
483
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3554
3555
483
  if (Z_ISREF_P(return_value)) {
3556
0
    zend_unwrap_reference(return_value);
3557
0
  }
3558
483
}
3559
/* }}} */
3560
3561
/* {{{ Pops an element off the beginning of the array */
3562
PHP_FUNCTION(array_shift)
3563
672
{
3564
672
  zval *stack,  /* Input stack */
3565
672
     *val;    /* Value to be popped */
3566
672
  uint32_t idx;
3567
3568
2.01k
  ZEND_PARSE_PARAMETERS_START(1, 1)
3569
2.68k
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3570
672
  ZEND_PARSE_PARAMETERS_END();
3571
3572
657
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3573
181
    return;
3574
181
  }
3575
3576
  /* re-index like it did before */
3577
476
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3578
476
    uint32_t k = 0;
3579
3580
    /* Get the first value and copy it into the return value */
3581
476
    idx = 0;
3582
476
    while (1) {
3583
476
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3584
0
        return;
3585
0
      }
3586
476
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3587
476
      if (Z_TYPE_P(val) != IS_UNDEF) {
3588
476
        break;
3589
476
      }
3590
0
      idx++;
3591
0
    }
3592
476
    RETVAL_COPY_VALUE(val);
3593
476
    ZVAL_UNDEF(val);
3594
3595
    /* Delete the first value */
3596
476
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3597
3598
476
    if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3599
1.14k
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3600
725
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3601
725
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3602
515
        if (idx != k) {
3603
515
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3604
515
          ZVAL_COPY_VALUE(q, val);
3605
515
          ZVAL_UNDEF(val);
3606
515
        }
3607
515
        k++;
3608
515
      }
3609
424
    } else {
3610
52
      uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3611
3612
169
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3613
117
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3614
117
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3615
78
        if (idx != k) {
3616
78
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3617
78
          ZVAL_COPY_VALUE(q, val);
3618
78
          ZVAL_UNDEF(val);
3619
78
          if (idx == iter_pos) {
3620
39
            zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3621
39
            iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3622
39
          }
3623
78
        }
3624
78
        k++;
3625
78
      }
3626
52
    }
3627
476
    Z_ARRVAL_P(stack)->nNumUsed = k;
3628
476
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3629
476
  } else {
3630
0
    uint32_t k = 0;
3631
0
    int should_rehash = 0;
3632
0
    Bucket *p;
3633
3634
    /* Get the first value and copy it into the return value */
3635
0
    idx = 0;
3636
0
    while (1) {
3637
0
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3638
0
        return;
3639
0
      }
3640
0
      p = Z_ARRVAL_P(stack)->arData + idx;
3641
0
      val = &p->val;
3642
0
      if (Z_TYPE_P(val) != IS_UNDEF) {
3643
0
        break;
3644
0
      }
3645
0
      idx++;
3646
0
    }
3647
0
    RETVAL_COPY_VALUE(val);
3648
0
    ZVAL_UNDEF(val);
3649
3650
    /* Delete the first value */
3651
0
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3652
3653
0
    for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3654
0
      p = Z_ARRVAL_P(stack)->arData + idx;
3655
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
3656
0
      if (p->key == NULL) {
3657
0
        if (p->h != k) {
3658
0
          p->h = k++;
3659
0
          should_rehash = 1;
3660
0
        } else {
3661
0
          k++;
3662
0
        }
3663
0
      }
3664
0
    }
3665
0
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3666
0
    if (should_rehash) {
3667
0
      zend_hash_rehash(Z_ARRVAL_P(stack));
3668
0
    }
3669
0
  }
3670
3671
476
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3672
3673
476
  if (Z_ISREF_P(return_value)) {
3674
52
    zend_unwrap_reference(return_value);
3675
52
  }
3676
476
}
3677
/* }}} */
3678
3679
/* {{{ Pushes elements onto the beginning of the array */
3680
PHP_FUNCTION(array_unshift)
3681
35
{
3682
35
  zval   *args,     /* Function arguments array */
3683
35
       *stack;      /* Input stack */
3684
35
  HashTable new_hash;   /* New hashtable for the stack */
3685
35
  uint32_t argc;      /* Number of function arguments */
3686
35
  zend_string *key;
3687
35
  zval *value;
3688
3689
105
  ZEND_PARSE_PARAMETERS_START(1, -1)
3690
140
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3691
26
    Z_PARAM_VARIADIC('+', args, argc)
3692
35
  ZEND_PARSE_PARAMETERS_END();
3693
3694
26
  zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3695
3696
26
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3697
16
    zend_hash_real_init_packed(&new_hash);
3698
3699
16
    ZEND_HASH_FILL_PACKED(&new_hash) {
3700
121
      for (uint32_t i = 0; i < argc; i++) {
3701
105
        Z_TRY_ADDREF(args[i]);
3702
105
        ZEND_HASH_FILL_ADD(&args[i]);
3703
105
      }
3704
3705
138
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(stack), value) {
3706
138
        ZEND_HASH_FILL_ADD(value);
3707
138
      } ZEND_HASH_FOREACH_END();
3708
16
    } ZEND_HASH_FILL_END();
3709
16
  } else {
3710
20
    for (uint32_t i = 0; i < argc; i++) {
3711
10
      Z_TRY_ADDREF(args[i]);
3712
10
      zend_hash_next_index_insert_new(&new_hash, &args[i]);
3713
10
    }
3714
3715
20
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3716
20
      if (key) {
3717
0
        zend_hash_add_new(&new_hash, key, value);
3718
0
      } else {
3719
0
        zend_hash_next_index_insert_new(&new_hash, value);
3720
0
      }
3721
20
    } ZEND_HASH_FOREACH_END();
3722
10
  }
3723
3724
26
  if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3725
11
    zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3726
11
    HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3727
11
    HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3728
11
  }
3729
3730
  /* replace HashTable data */
3731
26
  Z_ARRVAL_P(stack)->pDestructor = NULL;
3732
26
  zend_hash_destroy(Z_ARRVAL_P(stack));
3733
3734
26
  HT_FLAGS(Z_ARRVAL_P(stack))          = HT_FLAGS(&new_hash);
3735
26
  Z_ARRVAL_P(stack)->nTableSize        = new_hash.nTableSize;
3736
26
  Z_ARRVAL_P(stack)->nTableMask        = new_hash.nTableMask;
3737
26
  Z_ARRVAL_P(stack)->nNumUsed          = new_hash.nNumUsed;
3738
26
  Z_ARRVAL_P(stack)->nNumOfElements    = new_hash.nNumOfElements;
3739
26
  Z_ARRVAL_P(stack)->nNextFreeElement  = new_hash.nNextFreeElement;
3740
26
  Z_ARRVAL_P(stack)->arData            = new_hash.arData;
3741
26
  Z_ARRVAL_P(stack)->pDestructor       = new_hash.pDestructor;
3742
3743
26
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3744
3745
  /* Clean up and return the number of elements in the stack */
3746
26
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3747
26
}
3748
/* }}} */
3749
3750
/* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3751
PHP_FUNCTION(array_splice)
3752
791
{
3753
791
  zval *array,        /* Input array */
3754
791
     *repl_array = NULL; /* Replacement array */
3755
791
  HashTable  *rem_hash = NULL;
3756
791
  zend_long offset,
3757
791
      length = 0;
3758
791
  bool length_is_null = 1;
3759
791
  int   num_in;       /* Number of elements in the input array */
3760
3761
2.37k
  ZEND_PARSE_PARAMETERS_START(2, 4)
3762
3.15k
    Z_PARAM_ARRAY_EX(array, 0, 1)
3763
3.89k
    Z_PARAM_LONG(offset)
3764
776
    Z_PARAM_OPTIONAL
3765
2.39k
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3766
1.51k
    Z_PARAM_ZVAL(repl_array)
3767
1.51k
  ZEND_PARSE_PARAMETERS_END();
3768
3769
776
  num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3770
3771
776
  if (length_is_null) {
3772
354
    length = num_in;
3773
354
  }
3774
3775
776
  if (ZEND_NUM_ARGS() == 4) {
3776
    /* Make sure the last argument, if passed, is an array */
3777
126
    convert_to_array(repl_array);
3778
126
  }
3779
3780
  /* Don't create the array of removed elements if it's not going
3781
   * to be used; e.g. only removing and/or replacing elements */
3782
776
  if (USED_RET()) {
3783
351
    zend_long size = length;
3784
3785
    /* Clamp the offset.. */
3786
351
    if (offset > num_in) {
3787
12
      offset = num_in;
3788
339
    } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3789
1
      offset = 0;
3790
1
    }
3791
3792
    /* ..and the length */
3793
351
    if (length < 0) {
3794
0
      size = num_in - offset + length;
3795
351
    } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
3796
8
      size = num_in - offset;
3797
8
    }
3798
3799
    /* Initialize return value */
3800
351
    array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3801
351
    rem_hash = Z_ARRVAL_P(return_value);
3802
425
  } else {
3803
    /* The return value will not be used, but make sure it still has the correct type. */
3804
425
    RETVAL_EMPTY_ARRAY();
3805
425
  }
3806
3807
  /* Perform splice */
3808
776
  php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3809
776
}
3810
/* }}} */
3811
3812
/* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3813
   Finds the bucket at the given valid offset */
3814
static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
3815
29
{
3816
29
  zend_long pos;
3817
29
  Bucket *bucket;
3818
29
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3819
29
  if (HT_IS_WITHOUT_HOLES(ht)) {
3820
    /* There's no need to iterate over the array to filter out holes if there are no holes */
3821
29
    return ht->arData + offset;
3822
29
  }
3823
  /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3824
0
  pos = 0;
3825
0
  ZEND_HASH_MAP_FOREACH_BUCKET(ht, bucket) {
3826
0
    if (pos >= offset) {
3827
      /* This is the bucket of the array element at the requested offset */
3828
0
      return bucket;
3829
0
    }
3830
0
    ++pos;
3831
0
  } ZEND_HASH_FOREACH_END();
3832
3833
  /* Return a pointer to the end of the bucket array. */
3834
0
  return ht->arData + ht->nNumUsed;
3835
0
}
3836
/* }}} */
3837
3838
/* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
3839
   Finds the bucket at the given valid offset */
3840
static inline zval* find_packed_val_at_offset(HashTable* ht, zend_long offset)
3841
202
{
3842
202
  zend_long pos;
3843
202
  zval *zv;
3844
202
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3845
202
  if (HT_IS_WITHOUT_HOLES(ht)) {
3846
    /* There's no need to iterate over the array to filter out holes if there are no holes */
3847
202
    return ht->arPacked + offset;
3848
202
  }
3849
  /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
3850
0
  pos = 0;
3851
0
  ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) {
3852
0
    if (pos >= offset) {
3853
      /* This is the bucket of the array element at the requested offset */
3854
0
      return zv;
3855
0
    }
3856
0
    ++pos;
3857
0
  } ZEND_HASH_FOREACH_END();
3858
3859
  /* Return a pointer to the end of the bucket array. */
3860
0
  return ht->arPacked + ht->nNumUsed;
3861
0
}
3862
/* }}} */
3863
3864
/* {{{ Returns elements specified by offset and length */
3865
PHP_FUNCTION(array_slice)
3866
268
{
3867
268
  zval *input;                  /* Input array */
3868
268
  zval *entry;                  /* An array entry */
3869
268
  zend_long offset;             /* Offset to get elements from */
3870
268
  zend_long length = 0;         /* How many elements to get */
3871
268
  bool length_is_null = 1; /* Whether an explicit length has been omitted */
3872
268
  bool preserve_keys = 0;  /* Whether to preserve keys while copying to the new array */
3873
268
  uint32_t num_in;              /* Number of elements in the input array */
3874
268
  zend_string *string_key;
3875
268
  zend_ulong num_key;
3876
3877
803
  ZEND_PARSE_PARAMETERS_START(2, 4)
3878
1.06k
    Z_PARAM_ARRAY(input)
3879
1.27k
    Z_PARAM_LONG(offset)
3880
251
    Z_PARAM_OPTIONAL
3881
986
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3882
774
    Z_PARAM_BOOL(preserve_keys)
3883
268
  ZEND_PARSE_PARAMETERS_END();
3884
3885
  /* Get number of entries in the input hash */
3886
245
  num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
3887
3888
  /* We want all entries from offset to the end if length is not passed or is null */
3889
245
  if (length_is_null) {
3890
64
    length = num_in;
3891
64
  }
3892
3893
  /* Clamp the offset.. */
3894
245
  if (offset > (zend_long) num_in) {
3895
1
    RETURN_EMPTY_ARRAY();
3896
244
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3897
0
    offset = 0;
3898
0
  }
3899
3900
  /* ..and the length */
3901
244
  if (length < 0) {
3902
0
    length = num_in - offset + length;
3903
244
  } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3904
35
    length = num_in - offset;
3905
35
  }
3906
3907
244
  if (length <= 0) {
3908
13
    RETURN_EMPTY_ARRAY();
3909
13
  }
3910
3911
  /* Initialize returned array */
3912
231
  array_init_size(return_value, (uint32_t)length);
3913
3914
  // Contains modified variants of ZEND_HASH_FOREACH_VAL
3915
231
  {
3916
231
    HashTable *ht = Z_ARRVAL_P(input);
3917
3918
    /* Start at the beginning and go until we hit offset */
3919
231
    if (HT_IS_PACKED(ht)) {
3920
202
      zval *zv = find_packed_val_at_offset(ht, offset);
3921
202
      zval *end = ht->arPacked + ht->nNumUsed;
3922
3923
202
      if (!preserve_keys
3924
200
       || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3925
200
        zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3926
200
        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3927
435
          for (; zv != end; zv++) {
3928
257
            if (__fill_idx >= length) {
3929
22
              break;
3930
22
            }
3931
235
            if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3932
0
              continue;
3933
0
            }
3934
235
            entry = zv;
3935
235
            if (UNEXPECTED(Z_ISREF_P(entry)) &&
3936
153
              UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3937
82
              entry = Z_REFVAL_P(entry);
3938
82
            }
3939
235
            Z_TRY_ADDREF_P(entry);
3940
235
            ZEND_HASH_FILL_ADD(entry);
3941
235
          }
3942
200
        } ZEND_HASH_FILL_END();
3943
200
      } else {
3944
2
        zend_long n = 0;  /* Current number of elements */
3945
2
        zend_long idx = zv - ht->arPacked;
3946
3947
6
        for (; zv != end; zv++, idx++) {
3948
4
          if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3949
0
            continue;
3950
0
          }
3951
4
          if (n >= length) {
3952
0
            break;
3953
0
          }
3954
4
          n++;
3955
4
          entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, zv);
3956
4
          zval_add_ref(entry);
3957
4
        }
3958
2
      }
3959
202
    } else {
3960
29
      zend_long n = 0;  /* Current number of elements */
3961
29
      Bucket *p = find_bucket_at_offset(ht, offset);
3962
29
      Bucket *end = ht->arData + ht->nNumUsed;
3963
3964
73
      for (; p != end; p++) {
3965
44
        entry = &p->val;
3966
44
        if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3967
0
          continue;
3968
0
        }
3969
44
        if (n >= length) {
3970
0
          break;
3971
0
        }
3972
44
        n++;
3973
44
        num_key = p->h;
3974
44
        string_key = p->key;
3975
3976
44
        if (string_key) {
3977
29
          entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3978
29
        } else {
3979
15
          if (preserve_keys) {
3980
15
            entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3981
15
          } else {
3982
0
            entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3983
0
          }
3984
15
        }
3985
44
        zval_add_ref(entry);
3986
44
      }
3987
29
    }
3988
231
  }
3989
231
}
3990
/* }}} */
3991
3992
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
3993
25
{
3994
25
  zval *src_entry, *dest_entry;
3995
25
  zend_string *string_key;
3996
3997
75
  ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3998
75
    if (string_key) {
3999
18
      if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
4000
15
        zval *src_zval = src_entry;
4001
15
        zval *dest_zval = dest_entry;
4002
15
        HashTable *thash;
4003
15
        zval tmp;
4004
15
        int ret;
4005
4006
15
        ZVAL_DEREF(src_zval);
4007
15
        ZVAL_DEREF(dest_zval);
4008
15
        thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
4009
15
        if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
4010
0
          zend_throw_error(NULL, "Recursion detected");
4011
0
          return 0;
4012
0
        }
4013
4014
15
        ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4015
15
        dest_zval = dest_entry;
4016
4017
15
        if (Z_TYPE_P(dest_zval) == IS_NULL) {
4018
0
          convert_to_array(dest_zval);
4019
0
          add_next_index_null(dest_zval);
4020
15
        } else {
4021
15
          convert_to_array(dest_zval);
4022
15
        }
4023
15
        SEPARATE_ZVAL(dest_zval);
4024
4025
15
        ZVAL_UNDEF(&tmp);
4026
15
        if (Z_TYPE_P(src_zval) == IS_OBJECT) {
4027
0
          ZVAL_COPY(&tmp, src_zval);
4028
0
          convert_to_array(&tmp);
4029
0
          src_zval = &tmp;
4030
0
        }
4031
15
        if (Z_TYPE_P(src_zval) == IS_ARRAY) {
4032
5
          if (thash) {
4033
5
            GC_TRY_PROTECT_RECURSION(thash);
4034
5
          }
4035
5
          ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4036
5
          if (thash) {
4037
5
            GC_TRY_UNPROTECT_RECURSION(thash);
4038
5
          }
4039
5
          if (!ret) {
4040
5
            zval_ptr_dtor(&tmp);
4041
5
            return 0;
4042
5
          }
4043
10
        } else {
4044
10
          Z_TRY_ADDREF_P(src_zval);
4045
10
          zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
4046
10
          if (UNEXPECTED(!zv)) {
4047
10
            Z_TRY_DELREF_P(src_zval);
4048
10
            zend_cannot_add_element();
4049
10
            return 0;
4050
10
          }
4051
10
        }
4052
0
        zval_ptr_dtor(&tmp);
4053
3
      } else {
4054
3
        zval *zv = zend_hash_add_new(dest, string_key, src_entry);
4055
3
        zval_add_ref(zv);
4056
3
      }
4057
18
    } else {
4058
7
      zval *zv = zend_hash_next_index_insert(dest, src_entry);
4059
7
      if (UNEXPECTED(!zv)) {
4060
5
        zend_cannot_add_element();
4061
5
        return 0;
4062
5
      }
4063
2
      zval_add_ref(zv);
4064
2
    }
4065
75
  } ZEND_HASH_FOREACH_END();
4066
5
  return 1;
4067
25
}
4068
/* }}} */
4069
4070
PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
4071
0
{
4072
0
  zval *src_entry;
4073
0
  zend_string *string_key;
4074
4075
0
  if (HT_IS_PACKED(dest) && HT_IS_PACKED(src)) {
4076
0
    zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
4077
0
    ZEND_HASH_FILL_PACKED(dest) {
4078
0
      ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4079
0
        if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
4080
0
          UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
4081
0
          src_entry = Z_REFVAL_P(src_entry);
4082
0
        }
4083
0
        Z_TRY_ADDREF_P(src_entry);
4084
0
        ZEND_HASH_FILL_ADD(src_entry);
4085
0
      } ZEND_HASH_FOREACH_END();
4086
0
    } ZEND_HASH_FILL_END();
4087
0
  } else {
4088
0
    ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4089
0
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4090
0
        Z_REFCOUNT_P(src_entry) == 1)) {
4091
0
        src_entry = Z_REFVAL_P(src_entry);
4092
0
      }
4093
0
      Z_TRY_ADDREF_P(src_entry);
4094
0
      if (string_key) {
4095
0
        zend_hash_update(dest, string_key, src_entry);
4096
0
      } else {
4097
0
        zend_hash_next_index_insert_new(dest, src_entry);
4098
0
      }
4099
0
    } ZEND_HASH_FOREACH_END();
4100
0
  }
4101
0
  return 1;
4102
0
}
4103
/* }}} */
4104
4105
PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
4106
0
{
4107
0
  zval *src_entry, *dest_entry, *src_zval, *dest_zval;
4108
0
  zend_string *string_key;
4109
0
  zend_ulong num_key;
4110
0
  int ret;
4111
4112
0
#ifdef ZEND_CHECK_STACK_LIMIT
4113
0
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
4114
0
    zend_call_stack_size_error();
4115
0
    return 0;
4116
0
  }
4117
0
#endif
4118
4119
0
  ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
4120
0
    src_zval = src_entry;
4121
0
    ZVAL_DEREF(src_zval);
4122
0
    if (string_key) {
4123
0
      if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4124
0
        (dest_entry = zend_hash_find_known_hash(dest, string_key)) == NULL ||
4125
0
        (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4126
0
         (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4127
4128
0
        zval *zv = zend_hash_update(dest, string_key, src_entry);
4129
0
        zval_add_ref(zv);
4130
0
        continue;
4131
0
      }
4132
0
    } else {
4133
0
      if (Z_TYPE_P(src_zval) != IS_ARRAY ||
4134
0
        (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
4135
0
        (Z_TYPE_P(dest_entry) != IS_ARRAY &&
4136
0
         (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
4137
4138
0
        zval *zv = zend_hash_index_update(dest, num_key, src_entry);
4139
0
        zval_add_ref(zv);
4140
0
        continue;
4141
0
      }
4142
0
    }
4143
4144
0
    dest_zval = dest_entry;
4145
0
    ZVAL_DEREF(dest_zval);
4146
0
    if (Z_IS_RECURSIVE_P(dest_zval) ||
4147
0
      Z_IS_RECURSIVE_P(src_zval) ||
4148
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))) {
4149
0
      zend_throw_error(NULL, "Recursion detected");
4150
0
      return 0;
4151
0
    }
4152
4153
0
    ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4154
0
    SEPARATE_ZVAL(dest_entry);
4155
0
    dest_zval = dest_entry;
4156
4157
0
    if (Z_REFCOUNTED_P(dest_zval)) {
4158
0
      Z_PROTECT_RECURSION_P(dest_zval);
4159
0
    }
4160
0
    if (Z_REFCOUNTED_P(src_zval)) {
4161
0
      Z_PROTECT_RECURSION_P(src_zval);
4162
0
    }
4163
4164
0
    ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4165
4166
0
    if (Z_REFCOUNTED_P(dest_zval)) {
4167
0
      Z_UNPROTECT_RECURSION_P(dest_zval);
4168
0
    }
4169
0
    if (Z_REFCOUNTED_P(src_zval)) {
4170
0
      Z_UNPROTECT_RECURSION_P(src_zval);
4171
0
    }
4172
4173
0
    if (!ret) {
4174
0
      return 0;
4175
0
    }
4176
0
  } ZEND_HASH_FOREACH_END();
4177
4178
0
  return 1;
4179
0
}
4180
/* }}} */
4181
4182
static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4183
0
{
4184
0
  zval *args = NULL;
4185
0
  zval *arg;
4186
0
  uint32_t argc, i;
4187
0
  HashTable *dest;
4188
4189
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
4190
0
    Z_PARAM_VARIADIC('+', args, argc)
4191
0
  ZEND_PARSE_PARAMETERS_END();
4192
4193
4194
0
  for (i = 0; i < argc; i++) {
4195
0
    zval *arg = args + i;
4196
4197
0
    if (Z_TYPE_P(arg) != IS_ARRAY) {
4198
0
      zend_wrong_parameter_type_error(i + 1, Z_EXPECTED_ARRAY, arg);
4199
0
      RETURN_THROWS();
4200
0
    }
4201
0
  }
4202
4203
  /* copy first array if necessary */
4204
0
  arg = args;
4205
0
  bool in_place = zend_may_modify_arg_in_place(arg);
4206
0
  if (in_place) {
4207
0
    dest = Z_ARRVAL_P(arg);
4208
0
  } else {
4209
0
    dest = zend_array_dup(Z_ARRVAL_P(arg));
4210
0
  }
4211
4212
0
  ZVAL_ARR(return_value, dest);
4213
4214
0
  if (recursive) {
4215
0
    for (i = 1; i < argc; i++) {
4216
0
      arg = args + i;
4217
0
      php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
4218
0
    }
4219
0
  } else {
4220
0
    for (i = 1; i < argc; i++) {
4221
0
      arg = args + i;
4222
0
      zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
4223
0
    }
4224
0
  }
4225
4226
0
  if (in_place) {
4227
0
    GC_ADDREF(dest);
4228
0
  }
4229
0
}
4230
/* }}} */
4231
4232
static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
4233
68
{
4234
68
  zval *args = NULL;
4235
68
  zval *arg;
4236
68
  uint32_t argc, i;
4237
68
  zval *src_entry;
4238
68
  HashTable *src, *dest;
4239
68
  uint64_t count = 0;
4240
4241
204
  ZEND_PARSE_PARAMETERS_START(0, -1)
4242
204
    Z_PARAM_VARIADIC('+', args, argc)
4243
204
  ZEND_PARSE_PARAMETERS_END();
4244
4245
52
  if (argc == 0) {
4246
0
    RETURN_EMPTY_ARRAY();
4247
0
  }
4248
4249
136
  for (i = 0; i < argc; i++) {
4250
91
    zval *arg = args + i;
4251
4252
91
    if (Z_TYPE_P(arg) != IS_ARRAY) {
4253
7
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(arg));
4254
7
      RETURN_THROWS();
4255
7
    }
4256
84
    count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4257
84
  }
4258
4259
45
  if (UNEXPECTED(count >= HT_MAX_SIZE)) {
4260
0
    zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
4261
0
    RETURN_THROWS();
4262
0
  }
4263
4264
45
  if (argc == 2) {
4265
37
    zval *ret = NULL;
4266
4267
37
    if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4268
17
      ret = &args[1];
4269
20
    } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4270
0
      ret = &args[0];
4271
0
    }
4272
37
    if (ret) {
4273
17
      if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4274
17
        if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4275
17
          RETURN_COPY(ret);
4276
17
        }
4277
17
      } else {
4278
0
        bool copy = true;
4279
0
        zend_string *string_key;
4280
4281
0
        ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4282
0
          if (!string_key) {
4283
0
            copy = false;
4284
0
            break;
4285
0
          }
4286
0
        } ZEND_HASH_FOREACH_END();
4287
0
        if (copy) {
4288
0
          RETURN_COPY(ret);
4289
0
        }
4290
0
      }
4291
17
    }
4292
37
  }
4293
4294
28
  arg = args;
4295
28
  src  = Z_ARRVAL_P(arg);
4296
  /* copy first array if necessary */
4297
28
  bool in_place = false;
4298
28
  if (HT_IS_PACKED(src)) {
4299
    /* Note: If it has holes, it might get sequentialized */
4300
0
    if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4301
0
      dest = src;
4302
0
      in_place = true;
4303
0
      RETVAL_ARR(dest);
4304
0
    } else {
4305
0
      array_init_size(return_value, count);
4306
0
      dest = Z_ARRVAL_P(return_value);
4307
4308
0
      zend_hash_real_init_packed(dest);
4309
0
      ZEND_HASH_FILL_PACKED(dest) {
4310
0
        ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4311
0
          if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4312
0
            Z_REFCOUNT_P(src_entry) == 1)) {
4313
0
            src_entry = Z_REFVAL_P(src_entry);
4314
0
          }
4315
0
          Z_TRY_ADDREF_P(src_entry);
4316
0
          ZEND_HASH_FILL_ADD(src_entry);
4317
0
        } ZEND_HASH_FOREACH_END();
4318
0
      } ZEND_HASH_FILL_END();
4319
0
    }
4320
28
  } else {
4321
28
    array_init_size(return_value, count);
4322
28
    dest = Z_ARRVAL_P(return_value);
4323
4324
28
    zend_string *string_key;
4325
28
    zend_hash_real_init_mixed(dest);
4326
116
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4327
116
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4328
116
        Z_REFCOUNT_P(src_entry) == 1)) {
4329
0
        src_entry = Z_REFVAL_P(src_entry);
4330
0
      }
4331
116
      Z_TRY_ADDREF_P(src_entry);
4332
116
      if (EXPECTED(string_key)) {
4333
28
        _zend_hash_append(dest, string_key, src_entry);
4334
28
      } else {
4335
2
        zend_hash_next_index_insert_new(dest, src_entry);
4336
2
      }
4337
116
    } ZEND_HASH_FOREACH_END();
4338
28
  }
4339
28
  if (recursive) {
4340
48
    for (i = 1; i < argc; i++) {
4341
20
      arg = args + i;
4342
20
      php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4343
20
    }
4344
28
  } else {
4345
0
    for (i = 1; i < argc; i++) {
4346
0
      arg = args + i;
4347
0
      php_array_merge(dest, Z_ARRVAL_P(arg));
4348
0
    }
4349
0
  }
4350
4351
28
  if (in_place) {
4352
0
    GC_ADDREF(dest);
4353
0
  }
4354
28
}
4355
/* }}} */
4356
4357
/* {{{ Merges elements from passed arrays into one array */
4358
PHP_FUNCTION(array_merge)
4359
38
{
4360
38
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4361
38
}
4362
/* }}} */
4363
4364
/* {{{ Recursively merges elements from passed arrays into one array */
4365
PHP_FUNCTION(array_merge_recursive)
4366
30
{
4367
30
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4368
30
}
4369
/* }}} */
4370
4371
/* {{{ Replaces elements from passed arrays into one array */
4372
PHP_FUNCTION(array_replace)
4373
0
{
4374
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4375
0
}
4376
/* }}} */
4377
4378
/* {{{ Recursively replaces elements from passed arrays into one array */
4379
PHP_FUNCTION(array_replace_recursive)
4380
0
{
4381
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4382
0
}
4383
/* }}} */
4384
4385
/* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4386
PHP_FUNCTION(array_keys)
4387
115
{
4388
115
  zval *input,        /* Input array */
4389
115
       *search_value = NULL, /* Value to search for */
4390
115
       *entry,        /* An entry in the input array */
4391
115
         new_val;       /* New value */
4392
115
  bool strict = 0;    /* do strict comparison */
4393
115
  zend_ulong num_idx;
4394
115
  zend_string *str_idx;
4395
115
  zend_array *arrval;
4396
115
  zend_ulong elem_count;
4397
4398
345
  ZEND_PARSE_PARAMETERS_START(1, 3)
4399
460
    Z_PARAM_ARRAY(input)
4400
108
    Z_PARAM_OPTIONAL
4401
290
    Z_PARAM_ZVAL(search_value)
4402
290
    Z_PARAM_BOOL(strict)
4403
115
  ZEND_PARSE_PARAMETERS_END();
4404
108
  arrval = Z_ARRVAL_P(input);
4405
108
  elem_count = zend_hash_num_elements(arrval);
4406
4407
  /* Base case: empty input */
4408
108
  if (!elem_count) {
4409
0
    RETURN_EMPTY_ARRAY();
4410
0
  }
4411
4412
  /* Initialize return array */
4413
108
  if (search_value != NULL) {
4414
37
    array_init(return_value);
4415
4416
37
    if (strict) {
4417
119
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4418
119
        ZVAL_DEREF(entry);
4419
119
        if (fast_is_identical_function(search_value, entry)) {
4420
7
          if (str_idx) {
4421
0
            ZVAL_STR_COPY(&new_val, str_idx);
4422
7
          } else {
4423
7
            ZVAL_LONG(&new_val, num_idx);
4424
7
          }
4425
7
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4426
7
        }
4427
119
      } ZEND_HASH_FOREACH_END();
4428
19
    } else {
4429
126
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4430
126
        if (fast_equal_check_function(search_value, entry)) {
4431
15
          if (str_idx) {
4432
0
            ZVAL_STR_COPY(&new_val, str_idx);
4433
15
          } else {
4434
15
            ZVAL_LONG(&new_val, num_idx);
4435
15
          }
4436
15
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4437
15
        }
4438
126
      } ZEND_HASH_FOREACH_END();
4439
18
    }
4440
71
  } else {
4441
71
    array_init_size(return_value, elem_count);
4442
71
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4443
71
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4444
71
      if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4445
        /* Optimistic case: range(0..n-1) for vector-like packed array */
4446
8
        zend_ulong lval = 0;
4447
4448
36
        for (; lval < elem_count; ++lval) {
4449
28
          ZEND_HASH_FILL_SET_LONG(lval);
4450
28
          ZEND_HASH_FILL_NEXT();
4451
28
        }
4452
63
      } else {
4453
        /* Go through input array and add keys to the return array */
4454
500
        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4455
500
          if (str_idx) {
4456
171
            ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4457
171
          } else {
4458
0
            ZEND_HASH_FILL_SET_LONG(num_idx);
4459
0
          }
4460
500
          ZEND_HASH_FILL_NEXT();
4461
500
        } ZEND_HASH_FOREACH_END();
4462
63
      }
4463
71
    } ZEND_HASH_FILL_END();
4464
71
  }
4465
108
}
4466
/* }}} */
4467
4468
/* {{{ Get the key of the first element of the array */
4469
PHP_FUNCTION(array_key_first)
4470
0
{
4471
0
  zval *stack;    /* Input stack */
4472
4473
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4474
0
    Z_PARAM_ARRAY(stack)
4475
0
  ZEND_PARSE_PARAMETERS_END();
4476
4477
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4478
0
  HashPosition pos = 0;
4479
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4480
0
}
4481
/* }}} */
4482
4483
/* {{{ Get the key of the last element of the array */
4484
PHP_FUNCTION(array_key_last)
4485
0
{
4486
0
  zval *stack;    /* Input stack */
4487
0
  HashPosition pos;
4488
4489
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4490
0
    Z_PARAM_ARRAY(stack)
4491
0
  ZEND_PARSE_PARAMETERS_END();
4492
4493
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4494
0
  zend_hash_internal_pointer_end_ex(target_hash, &pos);
4495
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4496
0
}
4497
/* }}} */
4498
4499
PHP_FUNCTION(array_first)
4500
0
{
4501
0
  HashTable *array;
4502
4503
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4504
0
    Z_PARAM_ARRAY_HT(array)
4505
0
  ZEND_PARSE_PARAMETERS_END();
4506
4507
0
  ZEND_HASH_FOREACH_VAL(array, zval *zv) {
4508
0
    RETURN_COPY_DEREF(zv);
4509
0
  } ZEND_HASH_FOREACH_END();
4510
0
}
4511
4512
PHP_FUNCTION(array_last)
4513
0
{
4514
0
  HashTable *array;
4515
4516
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4517
0
    Z_PARAM_ARRAY_HT(array)
4518
0
  ZEND_PARSE_PARAMETERS_END();
4519
4520
0
  ZEND_HASH_REVERSE_FOREACH_VAL(array, zval *zv) {
4521
0
    RETURN_COPY_DEREF(zv);
4522
0
  } ZEND_HASH_FOREACH_END();
4523
0
}
4524
4525
/* {{{ Return just the values from the input array */
4526
PHP_FUNCTION(array_values)
4527
101
{
4528
101
  zval   *input;    /* Input array */
4529
101
  zend_array *arrval;
4530
101
  zend_long arrlen;
4531
4532
303
  ZEND_PARSE_PARAMETERS_START(1, 1)
4533
404
    Z_PARAM_ARRAY(input)
4534
101
  ZEND_PARSE_PARAMETERS_END();
4535
4536
95
  arrval = Z_ARRVAL_P(input);
4537
4538
  /* Return empty input as is */
4539
95
  arrlen = zend_hash_num_elements(arrval);
4540
95
  if (!arrlen) {
4541
0
    RETURN_EMPTY_ARRAY();
4542
0
  }
4543
4544
  /* Return vector-like packed arrays as-is */
4545
95
  if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4546
92
    arrval->nNextFreeElement == arrlen) {
4547
92
    RETURN_COPY(input);
4548
92
  }
4549
4550
3
  RETURN_ARR(zend_array_to_list(arrval));
4551
3
}
4552
/* }}} */
4553
4554
/* {{{ Return the value as key and the frequency of that value in input as value */
4555
PHP_FUNCTION(array_count_values)
4556
0
{
4557
0
  zval  *input,   /* Input array */
4558
0
      *entry,   /* An entry in the input array */
4559
0
      *tmp;
4560
0
  HashTable *myht;
4561
4562
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4563
0
    Z_PARAM_ARRAY(input)
4564
0
  ZEND_PARSE_PARAMETERS_END();
4565
4566
  /* Initialize return array */
4567
0
  array_init(return_value);
4568
4569
  /* Go through input array and add values to the return array */
4570
0
  myht = Z_ARRVAL_P(input);
4571
0
  ZEND_HASH_FOREACH_VAL(myht, entry) {
4572
0
    ZVAL_DEREF(entry);
4573
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4574
0
      if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4575
0
        zval data;
4576
0
        ZVAL_LONG(&data, 1);
4577
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4578
0
      } else {
4579
0
        Z_LVAL_P(tmp)++;
4580
0
      }
4581
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4582
0
      if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4583
0
        zval data;
4584
0
        ZVAL_LONG(&data, 1);
4585
0
        zend_symtable_add_new(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4586
0
      } else {
4587
0
        Z_LVAL_P(tmp)++;
4588
0
      }
4589
0
    } else {
4590
0
      php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4591
0
    }
4592
0
  } ZEND_HASH_FOREACH_END();
4593
0
}
4594
/* }}} */
4595
4596
static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4597
0
{
4598
0
  zval *prop = NULL;
4599
4600
0
  if (Z_TYPE_P(data) == IS_OBJECT) {
4601
0
    zend_string *tmp_str;
4602
    /* If name is an integer convert integer to string */
4603
0
    if (name_str == NULL) {
4604
0
      tmp_str = zend_long_to_str(name_long);
4605
0
    } else {
4606
0
      tmp_str = zend_string_copy(name_str);
4607
0
    }
4608
    /* The has_property check is first performed in "exists" mode (which returns true for
4609
     * properties that are null but exist) and then in "has" mode to handle objects that
4610
     * implement __isset (which is not called in "exists" mode). */
4611
0
    if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4612
0
        || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4613
0
      prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4614
0
      if (prop) {
4615
0
        ZVAL_DEREF(prop);
4616
0
        if (prop != rv) {
4617
0
          Z_TRY_ADDREF_P(prop);
4618
0
        }
4619
0
      }
4620
0
    }
4621
0
    zend_string_release(tmp_str);
4622
0
  } else if (Z_TYPE_P(data) == IS_ARRAY) {
4623
    /* Name is a string */
4624
0
    if (name_str != NULL) {
4625
0
      prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4626
0
    } else {
4627
0
      prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4628
0
    }
4629
0
    if (prop) {
4630
0
      ZVAL_DEREF(prop);
4631
0
      Z_TRY_ADDREF_P(prop);
4632
0
    }
4633
0
  }
4634
4635
0
  return prop;
4636
0
}
4637
/* }}} */
4638
4639
/* {{{ Return the values from a single column in the input array, identified by the
4640
   value_key and optionally indexed by the index_key */
4641
PHP_FUNCTION(array_column)
4642
0
{
4643
0
  HashTable *input;
4644
0
  zval *colval, *data, rv;
4645
0
  zend_string *column_str = NULL;
4646
0
  zend_long column_long = 0;
4647
0
  bool column_is_null = 0;
4648
0
  zend_string *index_str = NULL;
4649
0
  zend_long index_long = 0;
4650
0
  bool index_is_null = 1;
4651
4652
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
4653
0
    Z_PARAM_ARRAY_HT(input)
4654
0
    Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4655
0
    Z_PARAM_OPTIONAL
4656
0
    Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4657
0
  ZEND_PARSE_PARAMETERS_END();
4658
4659
0
  void* cache_slot_column[3] = { NULL, NULL, NULL };
4660
0
  void* cache_slot_index[3] = { NULL, NULL, NULL };
4661
4662
0
  array_init_size(return_value, zend_hash_num_elements(input));
4663
  /* Index param is not passed */
4664
0
  if (index_is_null) {
4665
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4666
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4667
0
      ZEND_HASH_FOREACH_VAL(input, data) {
4668
0
        ZVAL_DEREF(data);
4669
0
        if (column_is_null) {
4670
0
          Z_TRY_ADDREF_P(data);
4671
0
          colval = data;
4672
0
        } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4673
0
          continue;
4674
0
        }
4675
0
        ZEND_HASH_FILL_ADD(colval);
4676
0
      } ZEND_HASH_FOREACH_END();
4677
0
    } ZEND_HASH_FILL_END();
4678
0
  } else {
4679
0
    ZEND_HASH_FOREACH_VAL(input, data) {
4680
0
      ZVAL_DEREF(data);
4681
4682
0
      if (column_is_null) {
4683
0
        Z_TRY_ADDREF_P(data);
4684
0
        colval = data;
4685
0
      } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4686
0
        continue;
4687
0
      }
4688
4689
0
      zval rv;
4690
0
      zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4691
0
      if (keyval) {
4692
0
        array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4693
0
        zval_ptr_dtor(colval);
4694
0
        zval_ptr_dtor(keyval);
4695
0
      } else {
4696
0
        zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4697
0
      }
4698
0
    } ZEND_HASH_FOREACH_END();
4699
0
  }
4700
0
}
4701
/* }}} */
4702
4703
/* {{{ Return input as a new array with the order of the entries reversed */
4704
PHP_FUNCTION(array_reverse)
4705
110
{
4706
110
  zval   *input,        /* Input array */
4707
110
       *entry;        /* An entry in the input array */
4708
110
  zend_string *string_key;
4709
110
  zend_ulong    num_key;
4710
110
  bool preserve_keys = 0; /* whether to preserve keys */
4711
4712
330
  ZEND_PARSE_PARAMETERS_START(1, 2)
4713
440
    Z_PARAM_ARRAY(input)
4714
106
    Z_PARAM_OPTIONAL
4715
212
    Z_PARAM_BOOL(preserve_keys)
4716
110
  ZEND_PARSE_PARAMETERS_END();
4717
4718
  /* Initialize return array */
4719
106
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4720
106
  if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4721
103
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4722
103
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4723
2.94k
      ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4724
2.94k
        if (UNEXPECTED(Z_ISREF_P(entry) &&
4725
2.94k
          Z_REFCOUNT_P(entry) == 1)) {
4726
24
          entry = Z_REFVAL_P(entry);
4727
24
        }
4728
2.94k
        Z_TRY_ADDREF_P(entry);
4729
2.94k
        ZEND_HASH_FILL_ADD(entry);
4730
2.94k
      } ZEND_HASH_FOREACH_END();
4731
103
    } ZEND_HASH_FILL_END();
4732
103
  } else {
4733
97
    ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4734
97
      if (string_key) {
4735
3
        entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4736
44
      } else {
4737
44
        if (preserve_keys) {
4738
0
          entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4739
44
        } else {
4740
44
          entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4741
44
        }
4742
44
      }
4743
97
      zval_add_ref(entry);
4744
97
    } ZEND_HASH_FOREACH_END();
4745
3
  }
4746
106
}
4747
/* }}} */
4748
4749
/* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4750
PHP_FUNCTION(array_pad)
4751
0
{
4752
0
  zval  *input;   /* Input array */
4753
0
  zval  *pad_value; /* Padding value obviously */
4754
0
  zend_long pad_size;   /* Size to pad to */
4755
0
  zend_long pad_size_abs; /* Absolute value of pad_size */
4756
0
  zend_long input_size;   /* Size of the input array */
4757
0
  zend_long num_pads;   /* How many pads do we need */
4758
0
  zend_long i;
4759
0
  zend_string *key;
4760
0
  zval *value;
4761
4762
0
  ZEND_PARSE_PARAMETERS_START(3, 3)
4763
0
    Z_PARAM_ARRAY(input)
4764
0
    Z_PARAM_LONG(pad_size)
4765
0
    Z_PARAM_ZVAL(pad_value)
4766
0
  ZEND_PARSE_PARAMETERS_END();
4767
4768
0
  if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4769
0
    zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4770
0
    RETURN_THROWS();
4771
0
  }
4772
4773
  /* Do some initial calculations */
4774
0
  input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4775
0
  pad_size_abs = ZEND_ABS(pad_size);
4776
4777
0
  if (input_size >= pad_size_abs) {
4778
    /* Copy the original array */
4779
0
    ZVAL_COPY(return_value, input);
4780
0
    return;
4781
0
  }
4782
4783
0
  num_pads = pad_size_abs - input_size;
4784
0
  if (Z_REFCOUNTED_P(pad_value)) {
4785
0
    GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4786
0
  }
4787
4788
0
  array_init_size(return_value, pad_size_abs);
4789
0
  if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4790
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4791
4792
0
    if (pad_size < 0) {
4793
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4794
0
        for (i = 0; i < num_pads; i++) {
4795
0
          ZEND_HASH_FILL_ADD(pad_value);
4796
0
        }
4797
0
      } ZEND_HASH_FILL_END();
4798
0
    }
4799
4800
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4801
0
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4802
0
        Z_TRY_ADDREF_P(value);
4803
0
        ZEND_HASH_FILL_ADD(value);
4804
0
      } ZEND_HASH_FOREACH_END();
4805
0
    } ZEND_HASH_FILL_END();
4806
4807
0
    if (pad_size > 0) {
4808
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4809
0
        for (i = 0; i < num_pads; i++) {
4810
0
          ZEND_HASH_FILL_ADD(pad_value);
4811
0
        }
4812
0
      } ZEND_HASH_FILL_END();
4813
0
    }
4814
0
  } else {
4815
0
    if (pad_size < 0) {
4816
0
      for (i = 0; i < num_pads; i++) {
4817
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4818
0
      }
4819
0
    }
4820
4821
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4822
0
      Z_TRY_ADDREF_P(value);
4823
0
      if (key) {
4824
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4825
0
      } else {
4826
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4827
0
      }
4828
0
    } ZEND_HASH_FOREACH_END();
4829
4830
0
    if (pad_size > 0) {
4831
0
      for (i = 0; i < num_pads; i++) {
4832
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4833
0
      }
4834
0
    }
4835
0
  }
4836
0
}
4837
/* }}} */
4838
4839
/* {{{ Return array with key <-> value flipped */
4840
PHP_FUNCTION(array_flip)
4841
0
{
4842
0
  zval *array, *entry, data;
4843
0
  zend_ulong num_idx;
4844
0
  zend_string *str_idx;
4845
4846
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4847
0
    Z_PARAM_ARRAY(array)
4848
0
  ZEND_PARSE_PARAMETERS_END();
4849
4850
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4851
4852
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4853
0
    ZVAL_DEREF(entry);
4854
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4855
0
      if (str_idx) {
4856
0
        ZVAL_STR_COPY(&data, str_idx);
4857
0
      } else {
4858
0
        ZVAL_LONG(&data, num_idx);
4859
0
      }
4860
0
      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4861
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4862
0
      if (str_idx) {
4863
0
        ZVAL_STR_COPY(&data, str_idx);
4864
0
      } else {
4865
0
        ZVAL_LONG(&data, num_idx);
4866
0
      }
4867
0
      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4868
0
    } else {
4869
0
      php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4870
0
    }
4871
0
  } ZEND_HASH_FOREACH_END();
4872
0
}
4873
/* }}} */
4874
4875
/* {{{ Returns an array with all string keys lowercased [or uppercased] */
4876
PHP_FUNCTION(array_change_key_case)
4877
0
{
4878
0
  zval *array, *entry;
4879
0
  zend_string *string_key;
4880
0
  zend_string *new_key;
4881
0
  zend_ulong num_key;
4882
0
  zend_long change_to_upper = PHP_CASE_LOWER;
4883
4884
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
4885
0
    Z_PARAM_ARRAY(array)
4886
0
    Z_PARAM_OPTIONAL
4887
0
    Z_PARAM_LONG(change_to_upper)
4888
0
  ZEND_PARSE_PARAMETERS_END();
4889
4890
0
  if (change_to_upper != PHP_CASE_LOWER && change_to_upper != PHP_CASE_UPPER) {
4891
0
    zend_argument_value_error(2, "must be either CASE_LOWER or CASE_UPPER");
4892
0
    RETURN_THROWS();
4893
0
  }
4894
4895
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4896
4897
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4898
0
    if (!string_key) {
4899
0
      entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4900
0
    } else {
4901
0
      if (change_to_upper == PHP_CASE_UPPER) {
4902
0
        new_key = zend_string_toupper(string_key);
4903
0
      } else {
4904
0
        new_key = zend_string_tolower(string_key);
4905
0
      }
4906
0
      entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4907
0
      zend_string_release_ex(new_key, 0);
4908
0
    }
4909
4910
0
    zval_add_ref(entry);
4911
0
  } ZEND_HASH_FOREACH_END();
4912
0
}
4913
/* }}} */
4914
4915
struct bucketindex {
4916
  Bucket b;
4917
  unsigned int i;
4918
};
4919
4920
static void array_bucketindex_swap(void *p, void *q)
4921
376
{
4922
376
  struct bucketindex *f = (struct bucketindex *)p;
4923
376
  struct bucketindex *g = (struct bucketindex *)q;
4924
376
  struct bucketindex t;
4925
376
  t = *f;
4926
376
  *f = *g;
4927
376
  *g = t;
4928
376
}
4929
4930
/* {{{ Removes duplicate values from array */
4931
PHP_FUNCTION(array_unique)
4932
56
{
4933
56
  zval *array;
4934
56
  Bucket *p;
4935
56
  zend_long sort_type = PHP_SORT_STRING;
4936
56
  bucket_compare_func_t cmp;
4937
56
  struct bucketindex *arTmp, *cmpdata, *lastkept;
4938
56
  uint32_t i, idx;
4939
4940
168
  ZEND_PARSE_PARAMETERS_START(1, 2)
4941
224
    Z_PARAM_ARRAY(array)
4942
50
    Z_PARAM_OPTIONAL
4943
140
    Z_PARAM_LONG(sort_type)
4944
56
  ZEND_PARSE_PARAMETERS_END();
4945
4946
50
  if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4947
0
    ZVAL_COPY(return_value, array);
4948
0
    return;
4949
0
  }
4950
4951
50
  if (sort_type == PHP_SORT_STRING) {
4952
30
    HashTable seen;
4953
30
    zend_long num_key;
4954
30
    zend_string *str_key;
4955
30
    zval *val;
4956
4957
30
    zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4958
30
    array_init(return_value);
4959
4960
374
    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4961
374
      zval *retval;
4962
374
      if (Z_TYPE_P(val) == IS_STRING) {
4963
47
        retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4964
125
      } else {
4965
125
        zend_string *tmp_str_val;
4966
125
        zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4967
125
        retval = zend_hash_add_empty_element(&seen, str_val);
4968
125
        zend_tmp_string_release(tmp_str_val);
4969
125
      }
4970
4971
374
      if (retval) {
4972
        /* First occurrence of the value */
4973
73
        if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4974
0
          ZVAL_DEREF(val);
4975
0
        }
4976
73
        Z_TRY_ADDREF_P(val);
4977
4978
73
        if (str_key) {
4979
73
          zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4980
73
        } else {
4981
0
          zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4982
0
        }
4983
73
      }
4984
374
    } ZEND_HASH_FOREACH_END();
4985
4986
30
    zend_hash_destroy(&seen);
4987
30
    return;
4988
30
  }
4989
4990
20
  cmp = php_get_data_compare_func_unstable(sort_type, false);
4991
4992
20
  bool in_place = zend_may_modify_arg_in_place(array);
4993
20
  if (in_place) {
4994
0
    RETVAL_ARR(Z_ARRVAL_P(array));
4995
20
  } else {
4996
20
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4997
20
  }
4998
4999
  /* create and sort array with pointers to the target_hash buckets */
5000
20
  arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5001
20
  if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
5002
20
    zval *zv = Z_ARRVAL_P(array)->arPacked;
5003
202
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
5004
182
      if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5005
182
      ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
5006
182
      arTmp[i].b.h = idx;
5007
182
      arTmp[i].b.key = NULL;
5008
182
      arTmp[i].i = i;
5009
182
      i++;
5010
182
    }
5011
20
  } else {
5012
0
    p = Z_ARRVAL_P(array)->arData;
5013
0
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
5014
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
5015
0
      arTmp[i].b = *p;
5016
0
      arTmp[i].i = i;
5017
0
      i++;
5018
0
    }
5019
0
  }
5020
20
  ZVAL_UNDEF(&arTmp[i].b.val);
5021
20
  zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
5022
20
      (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
5023
5024
20
  if (UNEXPECTED(EG(exception))) {
5025
0
    goto out;
5026
0
  }
5027
5028
  /* go through the sorted array and delete duplicates from the copy */
5029
20
  lastkept = arTmp;
5030
182
  for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
5031
162
    if (cmp(&lastkept->b, &cmpdata->b)) {
5032
133
      lastkept = cmpdata;
5033
133
    } else {
5034
29
      if (lastkept->i > cmpdata->i) {
5035
0
        p = &lastkept->b;
5036
0
        lastkept = cmpdata;
5037
29
      } else {
5038
29
        p = &cmpdata->b;
5039
29
      }
5040
29
      if (p->key == NULL) {
5041
29
        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5042
29
      } else {
5043
0
        zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5044
0
      }
5045
29
    }
5046
162
  }
5047
5048
20
out:
5049
20
  pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5050
5051
20
  if (in_place) {
5052
0
    Z_ADDREF_P(return_value);
5053
0
  }
5054
20
}
5055
/* }}} */
5056
5057
static int zval_compare(zval *first, zval *second) /* {{{ */
5058
0
{
5059
0
  return string_compare_function(first, second);
5060
0
}
5061
/* }}} */
5062
5063
static int zval_user_compare(zval *a, zval *b) /* {{{ */
5064
0
{
5065
0
  zval args[2];
5066
0
  zval retval;
5067
5068
0
  ZVAL_COPY_VALUE(&args[0], a);
5069
0
  ZVAL_COPY_VALUE(&args[1], b);
5070
5071
0
  BG(user_compare_fci).param_count = 2;
5072
0
  BG(user_compare_fci).params = args;
5073
0
  BG(user_compare_fci).retval = &retval;
5074
5075
0
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
5076
0
  zend_long ret = php_get_long(&retval);
5077
0
  return ZEND_NORMALIZE_BOOL(ret);
5078
0
}
5079
/* }}} */
5080
5081
static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5082
0
{
5083
0
  uint32_t argc, i;
5084
0
  zval *args;
5085
0
  int (*intersect_data_compare_func)(zval *, zval *) = NULL;
5086
0
  bool ok;
5087
0
  zval *val, *data;
5088
0
  char *param_spec;
5089
0
  zend_string *key;
5090
0
  zend_ulong h;
5091
5092
  /* Get the argument count */
5093
0
  argc = ZEND_NUM_ARGS();
5094
0
  if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5095
    /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
5096
0
    param_spec = "+f";
5097
0
    intersect_data_compare_func = zval_user_compare;
5098
0
  } else {
5099
    /*  INTERSECT_COMP_DATA_NONE - array_intersect_key()
5100
      INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
5101
0
    param_spec = "+";
5102
5103
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5104
0
      intersect_data_compare_func = zval_compare;
5105
0
    }
5106
0
  }
5107
5108
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5109
0
    RETURN_THROWS();
5110
0
  }
5111
5112
0
  for (i = 0; i < argc; i++) {
5113
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5114
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5115
0
      RETURN_THROWS();
5116
0
    }
5117
0
  }
5118
5119
0
  array_init(return_value);
5120
5121
  /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
5122
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5123
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5124
0
      val = Z_REFVAL_P(val);
5125
0
    }
5126
0
    if (key == NULL) {
5127
0
      ok = true;
5128
0
      for (i = 1; i < argc; i++) {
5129
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
5130
0
          (intersect_data_compare_func &&
5131
0
          intersect_data_compare_func(val, data) != 0)
5132
0
        ) {
5133
0
          ok = false;
5134
0
          break;
5135
0
        }
5136
0
      }
5137
0
      if (ok) {
5138
0
        Z_TRY_ADDREF_P(val);
5139
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5140
0
      }
5141
0
    } else {
5142
0
      ok = true;
5143
0
      for (i = 1; i < argc; i++) {
5144
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5145
0
          (intersect_data_compare_func &&
5146
0
          intersect_data_compare_func(val, data) != 0)
5147
0
        ) {
5148
0
          ok = false;
5149
0
          break;
5150
0
        }
5151
0
      }
5152
0
      if (ok) {
5153
0
        Z_TRY_ADDREF_P(val);
5154
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5155
0
      }
5156
0
    }
5157
0
  } ZEND_HASH_FOREACH_END();
5158
0
}
5159
/* }}} */
5160
5161
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5162
5
{
5163
5
  zval *args = NULL;
5164
5
  HashTable *hash;
5165
5
  uint32_t arr_argc, i;
5166
5
  int c = 0;
5167
5
  uint32_t idx;
5168
5
  Bucket **lists, *list, **ptrs, *p;
5169
5
  char *param_spec;
5170
5
  zend_fcall_info fci1, fci2;
5171
5
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5172
5
  zend_fcall_info *fci_key = NULL, *fci_data;
5173
5
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5174
5
  PHP_ARRAY_CMP_FUNC_VARS;
5175
5
  bool in_place = false;
5176
5177
5
  bucket_compare_func_t intersect_key_compare_func;
5178
5
  bucket_compare_func_t intersect_data_compare_func;
5179
5180
5
  if (behavior == INTERSECT_NORMAL) {
5181
5
    intersect_key_compare_func = php_array_key_compare_string;
5182
5183
5
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5184
      /* array_intersect() */
5185
5
      param_spec = "+";
5186
5
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5187
5
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5188
      /* array_uintersect() */
5189
0
      param_spec = "+f";
5190
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5191
0
    } else {
5192
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5193
0
      return;
5194
0
    }
5195
5196
5
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5197
0
      RETURN_THROWS();
5198
0
    }
5199
5
    fci_data = &fci1;
5200
5
    fci_data_cache = &fci1_cache;
5201
5202
5
  } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5203
    /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5204
     * no comparison of the data is done (part of INTERSECT_ASSOC) */
5205
5206
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5207
      /* array_intersect_assoc() or array_intersect_key() */
5208
0
      param_spec = "+";
5209
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5210
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5211
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5212
      /* array_uintersect_assoc() */
5213
0
      param_spec = "+f";
5214
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5215
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5216
0
      fci_data = &fci1;
5217
0
      fci_data_cache = &fci1_cache;
5218
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5219
      /* array_intersect_uassoc() or array_intersect_ukey() */
5220
0
      param_spec = "+f";
5221
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5222
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5223
0
      fci_key = &fci1;
5224
0
      fci_key_cache = &fci1_cache;
5225
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5226
      /* array_uintersect_uassoc() */
5227
0
      param_spec = "+ff";
5228
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5229
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5230
0
      fci_data = &fci1;
5231
0
      fci_data_cache = &fci1_cache;
5232
0
      fci_key = &fci2;
5233
0
      fci_key_cache = &fci2_cache;
5234
0
    } else {
5235
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5236
0
      return;
5237
0
    }
5238
5239
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5240
0
      RETURN_THROWS();
5241
0
    }
5242
5243
0
  } else {
5244
0
    ZEND_ASSERT(0 && "Invalid behavior");
5245
0
    return;
5246
0
  }
5247
5248
5
  PHP_ARRAY_CMP_FUNC_BACKUP();
5249
5250
  /* for each argument, create and sort list with pointers to the hash buckets */
5251
5
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5252
5
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5253
5254
5
  if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5255
0
    BG(user_compare_fci) = *fci_data;
5256
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5257
5
  } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5258
0
    BG(user_compare_fci) = *fci_key;
5259
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5260
0
  }
5261
5262
10
  for (i = 0; i < arr_argc; i++) {
5263
5
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5264
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5265
0
      arr_argc = i; /* only free up to i - 1 */
5266
0
      goto out;
5267
0
    }
5268
5
    hash = Z_ARRVAL(args[i]);
5269
5
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5270
5
    lists[i] = list;
5271
5
    ptrs[i] = list;
5272
5
    if (HT_IS_PACKED(hash)) {
5273
5
      zval *zv = hash->arPacked;
5274
15
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5275
10
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5276
10
        ZVAL_COPY_VALUE(&list->val, zv);
5277
10
        list->h = idx;
5278
10
        list->key = NULL;
5279
10
        list++;
5280
10
      }
5281
5
    } else {
5282
0
      p = hash->arData;
5283
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5284
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5285
0
        *list++ = *p;
5286
0
      }
5287
0
    }
5288
5
    ZVAL_UNDEF(&list->val);
5289
5
    if (hash->nNumOfElements > 1) {
5290
5
      if (behavior == INTERSECT_NORMAL) {
5291
5
        zend_sort((void *) lists[i], hash->nNumOfElements,
5292
5
            sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5293
5
            (swap_func_t)zend_hash_bucket_swap);
5294
5
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5295
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5296
0
            sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5297
0
            (swap_func_t)zend_hash_bucket_swap);
5298
0
      }
5299
5
    }
5300
5
  }
5301
5302
  /* copy the argument array if necessary */
5303
5
  in_place = zend_may_modify_arg_in_place(&args[0]);
5304
5
  if (in_place) {
5305
0
    RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5306
5
  } else {
5307
5
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5308
5
  }
5309
5310
  /* go through the lists and look for common values */
5311
10
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5312
10
    if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5313
0
      && key_compare_type == INTERSECT_COMP_KEY_USER) {
5314
0
      BG(user_compare_fci) = *fci_key;
5315
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5316
0
    }
5317
5318
10
    for (i = 1; i < arr_argc; i++) {
5319
0
      if (behavior & INTERSECT_NORMAL) {
5320
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5321
0
          ptrs[i]++;
5322
0
        }
5323
0
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5324
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5325
0
          ptrs[i]++;
5326
0
        }
5327
0
        if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5328
          /* this means that ptrs[i] is not NULL so we can compare
5329
           * and "c==0" is from last operation
5330
           * in this branch of code we enter only when INTERSECT_ASSOC
5331
           * since when we have INTERSECT_KEY compare of data is not wanted. */
5332
0
          if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5333
0
            BG(user_compare_fci) = *fci_data;
5334
0
            BG(user_compare_fci_cache) = *fci_data_cache;
5335
0
          }
5336
0
          if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5337
0
            c = 1;
5338
0
            if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5339
0
              BG(user_compare_fci) = *fci_key;
5340
0
              BG(user_compare_fci_cache) = *fci_key_cache;
5341
              /* When KEY_USER, the last parameter is always the callback */
5342
0
            }
5343
            /* we are going to the break */
5344
0
          } else {
5345
            /* continue looping */
5346
0
          }
5347
0
        }
5348
0
      }
5349
0
      if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5350
        /* delete any values corresponding to remains of ptrs[0] */
5351
        /* and exit because they do not present in at least one of */
5352
        /* the other arguments */
5353
0
        for (;;) {
5354
0
          p = ptrs[0]++;
5355
0
          if (Z_TYPE(p->val) == IS_UNDEF) {
5356
0
            goto out;
5357
0
          }
5358
0
          if (p->key == NULL) {
5359
0
            zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5360
0
          } else {
5361
0
            zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5362
0
          }
5363
0
        }
5364
0
      }
5365
0
      if (c) /* here we get if not all are equal */
5366
0
        break;
5367
0
      ptrs[i]++;
5368
0
    }
5369
10
    if (c) {
5370
      /* Value of ptrs[0] not in all arguments, delete all entries */
5371
      /* with value < value of ptrs[i] */
5372
0
      for (;;) {
5373
0
        p = ptrs[0];
5374
0
        if (p->key == NULL) {
5375
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5376
0
        } else {
5377
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5378
0
        }
5379
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5380
0
          goto out;
5381
0
        }
5382
0
        if (behavior == INTERSECT_NORMAL) {
5383
0
          if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5384
0
            break;
5385
0
          }
5386
0
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5387
          /* no need of looping because indexes are unique */
5388
0
          break;
5389
0
        }
5390
0
      }
5391
10
    } else {
5392
      /* ptrs[0] is present in all the arguments */
5393
      /* Skip all entries with same value as ptrs[0] */
5394
10
      for (;;) {
5395
10
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5396
5
          goto out;
5397
5
        }
5398
5
        if (behavior == INTERSECT_NORMAL) {
5399
5
          if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5400
5
            break;
5401
5
          }
5402
5
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5403
          /* no need of looping because indexes are unique */
5404
0
          break;
5405
0
        }
5406
5
      }
5407
10
    }
5408
10
  }
5409
5
out:
5410
10
  for (i = 0; i < arr_argc; i++) {
5411
5
    hash = Z_ARRVAL(args[i]);
5412
5
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5413
5
  }
5414
5415
5
  PHP_ARRAY_CMP_FUNC_RESTORE();
5416
5417
5
  efree(ptrs);
5418
5
  efree(lists);
5419
5420
5
  if (in_place) {
5421
0
    Z_ADDREF_P(return_value);
5422
0
  }
5423
5
}
5424
/* }}} */
5425
5426
/* {{{ 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. */
5427
PHP_FUNCTION(array_intersect_key)
5428
0
{
5429
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5430
0
}
5431
/* }}} */
5432
5433
/* {{{ 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. */
5434
PHP_FUNCTION(array_intersect_ukey)
5435
0
{
5436
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5437
0
}
5438
/* }}} */
5439
5440
static zend_always_inline bool php_array_intersect_get_key(
5441
    zval *value, zend_ulong *num_key, zend_string **str_key, zend_string **tmp_key)
5442
0
{
5443
0
  ZVAL_DEREF(value);
5444
0
  *tmp_key = NULL;
5445
5446
0
  if (Z_TYPE_P(value) == IS_LONG) {
5447
0
    *num_key = (zend_ulong) Z_LVAL_P(value);
5448
0
    *str_key = NULL;
5449
0
    return true;
5450
0
  }
5451
5452
0
  if (Z_TYPE_P(value) == IS_STRING) {
5453
0
    *str_key = Z_STR_P(value);
5454
0
    return true;
5455
0
  }
5456
5457
0
  *str_key = zval_try_get_tmp_string(value, tmp_key);
5458
0
  return *str_key != NULL;
5459
0
}
5460
5461
static zend_always_inline void php_array_intersect_empty_result(zval *first, zval *return_value)
5462
0
{
5463
0
  HashTable *result;
5464
0
  bool in_place = zend_may_modify_arg_in_place(first);
5465
5466
0
  if (in_place) {
5467
0
    result = Z_ARRVAL_P(first);
5468
0
    ZVAL_ARR(return_value, result);
5469
0
  } else {
5470
0
    result = zend_array_dup(Z_ARRVAL_P(first));
5471
0
    ZVAL_ARR(return_value, result);
5472
0
  }
5473
5474
0
  ZEND_HASH_FOREACH_KEY(result, zend_ulong num_key, zend_string *key) {
5475
0
    if (key) {
5476
0
      zend_hash_del(result, key);
5477
0
    } else {
5478
0
      zend_hash_index_del(result, num_key);
5479
0
    }
5480
0
  } ZEND_HASH_FOREACH_END();
5481
5482
0
  if (in_place) {
5483
0
    Z_ADDREF_P(return_value);
5484
0
  }
5485
0
}
5486
5487
/* {{{ Hash-based implementation of array_intersect(). Values are compared
5488
 * using their string representation. On the long|string domain, this is
5489
 * exactly key equality under symtable normalization: a long and a string
5490
 * compare equal iff the string is the canonical decimal representation of the
5491
 * long, which is precisely when ZEND_HANDLE_NUMERIC converts it to that long
5492
 * key. Other values are converted to string before the same normalization. */
5493
static zend_never_inline void php_array_intersect_hash(zval *args, uint32_t argc, zval *return_value)
5494
0
{
5495
0
  for (uint32_t i = 0; i < argc; i++) {
5496
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5497
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5498
0
      return;
5499
0
    }
5500
0
  }
5501
5502
  /* An empty argument makes the intersection empty, so no values need to be
5503
   * converted to string. */
5504
0
  for (uint32_t i = 0; i < argc; i++) {
5505
0
    if (zend_hash_num_elements(Z_ARRVAL(args[i])) == 0) {
5506
0
      php_array_intersect_empty_result(&args[0], return_value);
5507
0
      return;
5508
0
    }
5509
0
  }
5510
5511
  /* Map each value of args[1] to the number of consecutive arguments,
5512
   * starting from args[1], the value has been seen in. */
5513
0
  zval one;
5514
0
  ZVAL_LONG(&one, 1);
5515
0
  HashTable set;
5516
0
  zend_hash_init(&set, zend_hash_num_elements(Z_ARRVAL(args[1])), NULL, NULL, 0);
5517
0
  zend_bitset delete_bitset = NULL;
5518
0
  ALLOCA_FLAG(use_heap);
5519
0
  bool in_place = false;
5520
5521
0
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[1]), zval *value) {
5522
0
    zend_ulong value_num_key = 0;
5523
0
    zend_string *value_str_key, *tmp_key;
5524
0
    if (!php_array_intersect_get_key(value, &value_num_key, &value_str_key, &tmp_key)) {
5525
0
      goto cleanup;
5526
0
    }
5527
0
    if (value_str_key) {
5528
0
      zend_symtable_update(&set, value_str_key, &one);
5529
0
    } else {
5530
0
      zend_hash_index_update(&set, value_num_key, &one);
5531
0
    }
5532
0
    zend_tmp_string_release(tmp_key);
5533
0
  } ZEND_HASH_FOREACH_END();
5534
5535
0
  for (uint32_t i = 2; i < argc; i++) {
5536
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), zval *value) {
5537
0
      zend_ulong value_num_key = 0;
5538
0
      zend_string *value_str_key, *tmp_key;
5539
0
      if (!php_array_intersect_get_key(value, &value_num_key, &value_str_key, &tmp_key)) {
5540
0
        goto cleanup;
5541
0
      }
5542
0
      zval *count;
5543
0
      if (value_str_key) {
5544
0
        count = zend_symtable_find(&set, value_str_key);
5545
0
      } else {
5546
0
        count = zend_hash_index_find(&set, value_num_key);
5547
0
      }
5548
0
      zend_tmp_string_release(tmp_key);
5549
0
      if (count && Z_LVAL_P(count) == (zend_long) i - 1) {
5550
0
        ZVAL_LONG(count, i);
5551
0
      }
5552
0
    } ZEND_HASH_FOREACH_END();
5553
0
  }
5554
5555
  /* Match the generic path by filtering the first argument in place if
5556
   * possible and duplicating it otherwise. In particular, duplication keeps
5557
   * bucket holes whose positions are observable through array_rand(). */
5558
0
  HashTable *result;
5559
0
  in_place = zend_may_modify_arg_in_place(&args[0]);
5560
0
  if (in_place) {
5561
0
    result = Z_ARRVAL(args[0]);
5562
0
    ZVAL_ARR(return_value, result);
5563
0
  } else {
5564
0
    result = zend_array_dup(Z_ARRVAL(args[0]));
5565
0
    ZVAL_ARR(return_value, result);
5566
0
  }
5567
5568
  /* Determine all entries to remove before deleting any. Deleting an entry may
5569
   * invoke a user destructor that changes subsequent string conversions. */
5570
0
  HashTable *scanned_result = result;
5571
0
  uint32_t scanned_num_used = result->nNumUsed;
5572
0
  uint32_t delete_bitset_len = zend_bitset_len(scanned_num_used);
5573
0
  delete_bitset = ZEND_BITSET_ALLOCA(delete_bitset_len, use_heap);
5574
0
  zend_bitset_clear(delete_bitset, delete_bitset_len);
5575
5576
0
  size_t scanned_element_size = ZEND_HASH_ELEMENT_SIZE(scanned_result);
5577
0
  for (uint32_t result_idx = 0; result_idx < scanned_num_used; result_idx++) {
5578
0
    zval *entry = ZEND_HASH_ELEMENT_EX(scanned_result, result_idx, scanned_element_size);
5579
0
    if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
5580
0
      continue;
5581
0
    }
5582
0
    zend_ulong value_num_key = 0;
5583
0
    zend_string *value_str_key, *tmp_key;
5584
0
    if (!php_array_intersect_get_key(entry, &value_num_key, &value_str_key, &tmp_key)) {
5585
0
      goto cleanup;
5586
0
    }
5587
0
    zval *count;
5588
0
    if (value_str_key) {
5589
0
      count = zend_symtable_find(&set, value_str_key);
5590
0
    } else {
5591
0
      count = zend_hash_index_find(&set, value_num_key);
5592
0
    }
5593
0
    zend_tmp_string_release(tmp_key);
5594
0
    if (!count || Z_LVAL_P(count) != (zend_long) argc - 1) {
5595
0
      zend_bitset_incl(delete_bitset, result_idx);
5596
0
    }
5597
0
  }
5598
5599
  /* A conversion may retain the first argument through reentrant user code,
5600
   * so it may no longer be safe to modify the original array in place. */
5601
0
  if (in_place && !zend_may_modify_arg_in_place(&args[0])) {
5602
0
    result = zend_array_dup(Z_ARRVAL(args[0]));
5603
0
    ZVAL_ARR(return_value, result);
5604
0
    in_place = false;
5605
0
  }
5606
5607
  /* The late duplication may compact holes, so read keys from the table whose
5608
   * bucket indexes are stored in the bitset. */
5609
0
  uint32_t result_idx;
5610
0
  ZEND_BITSET_FOREACH(delete_bitset, delete_bitset_len, result_idx) {
5611
0
    if (HT_IS_PACKED(scanned_result)) {
5612
0
      zend_hash_index_del(result, result_idx);
5613
0
    } else {
5614
0
      zval *entry = ZEND_HASH_ELEMENT_EX(scanned_result, result_idx, scanned_element_size);
5615
0
      Bucket *bucket = (Bucket *) entry;
5616
0
      if (bucket->key) {
5617
0
        zend_hash_del(result, bucket->key);
5618
0
      } else {
5619
0
        zend_hash_index_del(result, bucket->h);
5620
0
      }
5621
0
    }
5622
0
  } ZEND_BITSET_FOREACH_END();
5623
5624
0
cleanup:
5625
0
  if (delete_bitset) {
5626
0
    free_alloca(delete_bitset, use_heap);
5627
0
  }
5628
0
  zend_hash_destroy(&set);
5629
0
  if (in_place) {
5630
0
    Z_ADDREF_P(return_value);
5631
0
  }
5632
0
}
5633
/* }}} */
5634
5635
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5636
PHP_FUNCTION(array_intersect)
5637
10
{
5638
10
  zval *args;
5639
10
  uint32_t argc;
5640
5641
10
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5642
5
    RETURN_THROWS();
5643
5
  }
5644
5645
5
  if (argc >= 2) {
5646
0
    php_array_intersect_hash(args, argc, return_value);
5647
0
    return;
5648
0
  }
5649
5650
  /* Preserve the generic path and its conversion side effects for calls with
5651
   * a single array. */
5652
5
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5653
5
}
5654
/* }}} */
5655
5656
/* {{{ 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. */
5657
PHP_FUNCTION(array_uintersect)
5658
0
{
5659
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5660
0
}
5661
/* }}} */
5662
5663
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5664
PHP_FUNCTION(array_intersect_assoc)
5665
0
{
5666
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5667
0
}
5668
/* }}} */
5669
5670
/* {{{ 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. */
5671
PHP_FUNCTION(array_intersect_uassoc)
5672
0
{
5673
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5674
0
}
5675
/* }}} */
5676
5677
/* {{{ 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. */
5678
PHP_FUNCTION(array_uintersect_assoc)
5679
0
{
5680
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5681
0
}
5682
/* }}} */
5683
5684
/* {{{ 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. */
5685
PHP_FUNCTION(array_uintersect_uassoc)
5686
0
{
5687
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5688
0
}
5689
/* }}} */
5690
5691
static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5692
16
{
5693
16
  uint32_t argc, i;
5694
16
  zval *args;
5695
16
  int (*diff_data_compare_func)(zval *, zval *) = NULL;
5696
16
  bool ok;
5697
16
  zval *val, *data;
5698
16
  zend_string *key;
5699
16
  zend_ulong h;
5700
5701
  /* Get the argument count */
5702
16
  argc = ZEND_NUM_ARGS();
5703
16
  if (data_compare_type == DIFF_COMP_DATA_USER) {
5704
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5705
0
      RETURN_THROWS();
5706
0
    }
5707
0
    diff_data_compare_func = zval_user_compare;
5708
16
  } else {
5709
16
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5710
16
      RETURN_THROWS();
5711
16
    }
5712
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5713
0
      diff_data_compare_func = zval_compare;
5714
0
    }
5715
0
  }
5716
5717
0
  for (i = 0; i < argc; i++) {
5718
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5719
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5720
0
      RETURN_THROWS();
5721
0
    }
5722
0
  }
5723
5724
0
  array_init(return_value);
5725
5726
  /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5727
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5728
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5729
0
      val = Z_REFVAL_P(val);
5730
0
    }
5731
0
    if (key == NULL) {
5732
0
      ok = true;
5733
0
      for (i = 1; i < argc; i++) {
5734
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5735
0
          (!diff_data_compare_func ||
5736
0
          diff_data_compare_func(val, data) == 0)
5737
0
        ) {
5738
0
          ok = false;
5739
0
          break;
5740
0
        }
5741
0
      }
5742
0
      if (ok) {
5743
0
        Z_TRY_ADDREF_P(val);
5744
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5745
0
      }
5746
0
    } else {
5747
0
      ok = true;
5748
0
      for (i = 1; i < argc; i++) {
5749
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5750
0
          (!diff_data_compare_func ||
5751
0
          diff_data_compare_func(val, data) == 0)
5752
0
        ) {
5753
0
          ok = false;
5754
0
          break;
5755
0
        }
5756
0
      }
5757
0
      if (ok) {
5758
0
        Z_TRY_ADDREF_P(val);
5759
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5760
0
      }
5761
0
    }
5762
0
  } ZEND_HASH_FOREACH_END();
5763
0
}
5764
/* }}} */
5765
5766
static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5767
0
{
5768
0
  zval *args = NULL;
5769
0
  HashTable *hash;
5770
0
  uint32_t arr_argc, i;
5771
0
  int c;
5772
0
  uint32_t idx;
5773
0
  Bucket **lists, *list, **ptrs, *p;
5774
0
  char *param_spec;
5775
0
  zend_fcall_info fci1, fci2;
5776
0
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5777
0
  zend_fcall_info *fci_key = NULL, *fci_data;
5778
0
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5779
0
  PHP_ARRAY_CMP_FUNC_VARS;
5780
5781
0
  bucket_compare_func_t diff_key_compare_func;
5782
0
  bucket_compare_func_t diff_data_compare_func;
5783
5784
0
  if (behavior == DIFF_NORMAL) {
5785
0
    diff_key_compare_func = php_array_key_compare_string_unstable;
5786
5787
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5788
      /* array_diff */
5789
0
      param_spec = "+";
5790
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5791
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5792
      /* array_udiff */
5793
0
      param_spec = "+f";
5794
0
      diff_data_compare_func = php_array_user_compare_unstable;
5795
0
    } else {
5796
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5797
0
      return;
5798
0
    }
5799
5800
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5801
0
      RETURN_THROWS();
5802
0
    }
5803
0
    fci_data = &fci1;
5804
0
    fci_data_cache = &fci1_cache;
5805
5806
0
  } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5807
    /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5808
     * no comparison of the data is done (part of DIFF_ASSOC) */
5809
5810
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5811
      /* array_diff_assoc() or array_diff_key() */
5812
0
      param_spec = "+";
5813
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5814
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5815
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5816
      /* array_udiff_assoc() */
5817
0
      param_spec = "+f";
5818
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5819
0
      diff_data_compare_func = php_array_user_compare_unstable;
5820
0
      fci_data = &fci1;
5821
0
      fci_data_cache = &fci1_cache;
5822
0
    } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5823
      /* array_diff_uassoc() or array_diff_ukey() */
5824
0
      param_spec = "+f";
5825
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5826
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5827
0
      fci_key = &fci1;
5828
0
      fci_key_cache = &fci1_cache;
5829
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5830
      /* array_udiff_uassoc() */
5831
0
      param_spec = "+ff";
5832
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5833
0
      diff_data_compare_func = php_array_user_compare_unstable;
5834
0
      fci_data = &fci1;
5835
0
      fci_data_cache = &fci1_cache;
5836
0
      fci_key = &fci2;
5837
0
      fci_key_cache = &fci2_cache;
5838
0
    } else {
5839
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5840
0
      return;
5841
0
    }
5842
5843
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5844
0
      RETURN_THROWS();
5845
0
    }
5846
5847
0
  } else {
5848
0
    ZEND_ASSERT(0 && "Invalid behavior");
5849
0
    return;
5850
0
  }
5851
5852
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
5853
5854
  /* for each argument, create and sort list with pointers to the hash buckets */
5855
0
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5856
0
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5857
5858
0
  if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5859
0
    BG(user_compare_fci) = *fci_data;
5860
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5861
0
  } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5862
0
    BG(user_compare_fci) = *fci_key;
5863
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5864
0
  }
5865
5866
0
  for (i = 0; i < arr_argc; i++) {
5867
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5868
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5869
0
      arr_argc = i; /* only free up to i - 1 */
5870
0
      goto out;
5871
0
    }
5872
0
    hash = Z_ARRVAL(args[i]);
5873
0
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5874
0
    lists[i] = list;
5875
0
    ptrs[i] = list;
5876
0
    if (HT_IS_PACKED(hash)) {
5877
0
      zval *zv = hash->arPacked;
5878
0
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5879
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5880
0
        ZVAL_COPY_VALUE(&list->val, zv);
5881
0
        list->h = idx;
5882
0
        list->key = NULL;
5883
0
        list++;
5884
0
      }
5885
0
    } else {
5886
0
      p = hash->arData;
5887
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5888
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5889
0
        *list++ = *p;
5890
0
      }
5891
0
    }
5892
0
    ZVAL_UNDEF(&list->val);
5893
0
    if (hash->nNumOfElements > 1) {
5894
0
      if (behavior == DIFF_NORMAL) {
5895
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5896
0
            sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5897
0
            (swap_func_t)zend_hash_bucket_swap);
5898
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5899
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5900
0
            sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5901
0
            (swap_func_t)zend_hash_bucket_swap);
5902
0
      }
5903
0
    }
5904
0
  }
5905
5906
  /* copy the argument array */
5907
0
  RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5908
5909
  /* go through the lists and look for values of ptr[0] that are not in the others */
5910
0
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5911
0
    if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5912
0
      &&
5913
0
      key_compare_type == DIFF_COMP_KEY_USER
5914
0
    ) {
5915
0
      BG(user_compare_fci) = *fci_key;
5916
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5917
0
    }
5918
0
    c = 1;
5919
0
    for (i = 1; i < arr_argc; i++) {
5920
0
      Bucket *ptr = ptrs[i];
5921
0
      if (behavior == DIFF_NORMAL) {
5922
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5923
0
          ptrs[i]++;
5924
0
        }
5925
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5926
0
        while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5927
0
          ptr++;
5928
0
        }
5929
0
      }
5930
0
      if (!c) {
5931
0
        if (behavior == DIFF_NORMAL) {
5932
0
          if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5933
0
            ptrs[i]++;
5934
0
          }
5935
0
          break;
5936
0
        } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
5937
          /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5938
           * data comparison is not needed - skipped. */
5939
0
          if (Z_TYPE(ptr->val) != IS_UNDEF) {
5940
0
            if (data_compare_type == DIFF_COMP_DATA_USER) {
5941
0
              BG(user_compare_fci) = *fci_data;
5942
0
              BG(user_compare_fci_cache) = *fci_data_cache;
5943
0
            }
5944
0
            if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5945
              /* the data is not the same */
5946
0
              c = -1;
5947
0
              if (key_compare_type == DIFF_COMP_KEY_USER) {
5948
0
                BG(user_compare_fci) = *fci_key;
5949
0
                BG(user_compare_fci_cache) = *fci_key_cache;
5950
0
              }
5951
0
            } else {
5952
0
              break;
5953
              /* we have found the element in other arrays thus we don't want it
5954
               * in the return_value -> delete from there */
5955
0
            }
5956
0
          }
5957
0
        } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5958
          /* the behavior here differs from INTERSECT_KEY in php_intersect
5959
           * since in the "diff" case we have to remove the entry from
5960
           * return_value while when doing intersection the entry must not
5961
           * be deleted. */
5962
0
          break; /* remove the key */
5963
0
        }
5964
0
      }
5965
0
    }
5966
0
    if (!c) {
5967
      /* ptrs[0] in one of the other arguments */
5968
      /* delete all entries with value as ptrs[0] */
5969
0
      for (;;) {
5970
0
        p = ptrs[0];
5971
0
        if (p->key == NULL) {
5972
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5973
0
        } else {
5974
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5975
0
        }
5976
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5977
0
          goto out;
5978
0
        }
5979
0
        if (behavior == DIFF_NORMAL) {
5980
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5981
0
            break;
5982
0
          }
5983
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5984
          /* in this case no array_key_compare is needed */
5985
0
          break;
5986
0
        }
5987
0
      }
5988
0
    } else {
5989
      /* ptrs[0] in none of the other arguments */
5990
      /* skip all entries with value as ptrs[0] */
5991
0
      for (;;) {
5992
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5993
0
          goto out;
5994
0
        }
5995
0
        if (behavior == DIFF_NORMAL) {
5996
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5997
0
            break;
5998
0
          }
5999
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
6000
          /* in this case no array_key_compare is needed */
6001
0
          break;
6002
0
        }
6003
0
      }
6004
0
    }
6005
0
  }
6006
0
out:
6007
0
  for (i = 0; i < arr_argc; i++) {
6008
0
    hash = Z_ARRVAL(args[i]);
6009
0
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
6010
0
  }
6011
6012
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
6013
6014
0
  efree(ptrs);
6015
0
  efree(lists);
6016
0
}
6017
/* }}} */
6018
6019
/* {{{ 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. */
6020
PHP_FUNCTION(array_diff_key)
6021
16
{
6022
16
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
6023
16
}
6024
/* }}} */
6025
6026
/* {{{ 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. */
6027
PHP_FUNCTION(array_diff_ukey)
6028
0
{
6029
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
6030
0
}
6031
/* }}} */
6032
6033
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
6034
PHP_FUNCTION(array_diff)
6035
16
{
6036
16
  zval *args;
6037
16
  uint32_t argc, i;
6038
16
  uint64_t num;
6039
16
  HashTable exclude;
6040
16
  zval *value;
6041
16
  zend_string *str, *tmp_str, *key;
6042
16
  zend_long idx;
6043
16
  zval dummy;
6044
6045
32
  ZEND_PARSE_PARAMETERS_START(1, -1)
6046
32
    Z_PARAM_VARIADIC('+', args, argc)
6047
32
  ZEND_PARSE_PARAMETERS_END();
6048
6049
0
  if (Z_TYPE(args[0]) != IS_ARRAY) {
6050
0
    zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
6051
0
    RETURN_THROWS();
6052
0
  }
6053
6054
0
  num = zend_hash_num_elements(Z_ARRVAL(args[0]));
6055
0
  if (num == 0) {
6056
0
    for (i = 1; i < argc; i++) {
6057
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
6058
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6059
0
        RETURN_THROWS();
6060
0
      }
6061
0
    }
6062
0
    RETURN_EMPTY_ARRAY();
6063
0
  } else if (num == 1) {
6064
0
    int found = 0;
6065
0
    zend_string *search_str, *tmp_search_str;
6066
6067
0
    value = NULL;
6068
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
6069
0
      break;
6070
0
    } ZEND_HASH_FOREACH_END();
6071
6072
0
    if (!value) {
6073
0
      for (i = 1; i < argc; i++) {
6074
0
        if (Z_TYPE(args[i]) != IS_ARRAY) {
6075
0
          zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6076
0
          RETURN_THROWS();
6077
0
        }
6078
0
      }
6079
0
      RETURN_EMPTY_ARRAY();
6080
0
    }
6081
6082
0
    search_str = zval_get_tmp_string(value, &tmp_search_str);
6083
6084
0
    for (i = 1; i < argc; i++) {
6085
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
6086
0
        zend_tmp_string_release(tmp_search_str);
6087
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6088
0
        RETURN_THROWS();
6089
0
      }
6090
0
      if (!found) {
6091
0
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
6092
0
          str = zval_get_tmp_string(value, &tmp_str);
6093
0
          if (zend_string_equals(search_str, str)) {
6094
0
            zend_tmp_string_release(tmp_str);
6095
0
            found = 1;
6096
0
            break;
6097
0
          }
6098
0
          zend_tmp_string_release(tmp_str);
6099
0
        } ZEND_HASH_FOREACH_END();
6100
0
      }
6101
0
    }
6102
6103
0
    zend_tmp_string_release(tmp_search_str);
6104
6105
0
    if (found) {
6106
0
      RETVAL_EMPTY_ARRAY();
6107
0
    } else {
6108
0
      ZVAL_COPY(return_value, &args[0]);
6109
0
    }
6110
0
    return;
6111
0
  }
6112
6113
  /* count number of elements */
6114
0
  num = 0;
6115
0
  for (i = 1; i < argc; i++) {
6116
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
6117
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
6118
0
      RETURN_THROWS();
6119
0
    }
6120
0
    num += zend_hash_num_elements(Z_ARRVAL(args[i]));
6121
0
  }
6122
6123
0
  if (num == 0) {
6124
0
    ZVAL_COPY(return_value, &args[0]);
6125
0
    return;
6126
0
  }
6127
6128
0
  if (UNEXPECTED(num >= HT_MAX_SIZE)) {
6129
0
    zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
6130
0
    RETURN_THROWS();
6131
0
  }
6132
6133
0
  ZVAL_NULL(&dummy);
6134
  /* create exclude map */
6135
0
  zend_hash_init(&exclude, num, NULL, NULL, 0);
6136
0
  for (i = 1; i < argc; i++) {
6137
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
6138
0
      str = zval_get_tmp_string(value, &tmp_str);
6139
0
      zend_hash_add(&exclude, str, &dummy);
6140
0
      zend_tmp_string_release(tmp_str);
6141
0
    } ZEND_HASH_FOREACH_END();
6142
0
  }
6143
6144
  /* copy all elements of first array that are not in exclude set */
6145
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
6146
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
6147
0
    str = zval_get_tmp_string(value, &tmp_str);
6148
0
    if (!zend_hash_exists(&exclude, str)) {
6149
0
      if (key) {
6150
0
        value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
6151
0
      } else {
6152
0
        value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
6153
0
      }
6154
0
      zval_add_ref(value);
6155
0
    }
6156
0
    zend_tmp_string_release(tmp_str);
6157
0
  } ZEND_HASH_FOREACH_END();
6158
6159
0
  zend_hash_destroy(&exclude);
6160
0
}
6161
/* }}} */
6162
6163
/* {{{ 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. */
6164
PHP_FUNCTION(array_udiff)
6165
0
{
6166
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
6167
0
}
6168
/* }}} */
6169
6170
/* {{{ 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 */
6171
PHP_FUNCTION(array_diff_assoc)
6172
0
{
6173
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
6174
0
}
6175
/* }}} */
6176
6177
/* {{{ 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. */
6178
PHP_FUNCTION(array_diff_uassoc)
6179
0
{
6180
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
6181
0
}
6182
/* }}} */
6183
6184
/* {{{ 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. */
6185
PHP_FUNCTION(array_udiff_assoc)
6186
0
{
6187
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
6188
0
}
6189
/* }}} */
6190
6191
/* {{{ 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. */
6192
PHP_FUNCTION(array_udiff_uassoc)
6193
0
{
6194
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
6195
0
}
6196
/* }}} */
6197
6198
51
#define MULTISORT_ORDER 0
6199
27
#define MULTISORT_TYPE  1
6200
1.03k
#define MULTISORT_LAST  2
6201
6202
PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
6203
201
{
6204
201
  Bucket *ab = *(Bucket **)a;
6205
201
  Bucket *bb = *(Bucket **)b;
6206
201
  int r;
6207
201
  zend_long result;
6208
6209
201
  r = 0;
6210
201
  do {
6211
201
    result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
6212
201
    if (result != 0) {
6213
188
      return result > 0 ? 1 : -1;
6214
188
    }
6215
13
    r++;
6216
13
  } while (Z_TYPE(ab[r].val) != IS_UNDEF);
6217
6218
13
  return stable_sort_fallback(&ab[r], &bb[r]);
6219
201
}
6220
/* }}} */
6221
6222
#define MULTISORT_ABORT       \
6223
12
  efree(func); \
6224
12
  efree(arrays);         \
6225
0
  return;
6226
6227
114
static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
6228
114
  Bucket *t;
6229
114
  Bucket **f = (Bucket **)p;
6230
114
  Bucket **g = (Bucket **)q;
6231
6232
114
  t = *f;
6233
114
  *f = *g;
6234
114
  *g = t;
6235
114
}
6236
/* }}} */
6237
6238
/* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
6239
PHP_FUNCTION(array_multisort)
6240
174
{
6241
174
  zval*     args;
6242
174
  zval**      arrays;
6243
174
  HashTable**   hashes;
6244
174
  Bucket**    indirect;
6245
174
  uint32_t    idx;
6246
174
  HashTable*    hash;
6247
174
  uint32_t    argc;
6248
174
  uint32_t    array_size;
6249
174
  uint32_t    num_arrays = 0;
6250
174
  int       parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
6251
174
  int       sort_order = PHP_SORT_ASC;
6252
174
  int       sort_type  = PHP_SORT_REGULAR;
6253
174
  uint32_t    i, k, n;
6254
174
  bucket_compare_func_t *func;
6255
6256
515
  ZEND_PARSE_PARAMETERS_START(1, -1)
6257
515
    Z_PARAM_VARIADIC('+', args, argc)
6258
515
  ZEND_PARSE_PARAMETERS_END();
6259
6260
  /* Allocate space for storing pointers to input arrays and sort flags. */
6261
167
  arrays = (zval **)ecalloc(argc, sizeof(zval *));
6262
501
  for (i = 0; i < MULTISORT_LAST; i++) {
6263
334
    parse_state[i] = 0;
6264
334
  }
6265
167
  func = ecalloc(argc, sizeof(bucket_compare_func_t));
6266
6267
  /* Here we go through the input arguments and parse them. Each one can
6268
   * be either an array or a sort flag which follows an array. If not
6269
   * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
6270
   * accordingly. There can't be two sort flags of the same type after an
6271
   * array, and the very first argument has to be an array. */
6272
381
  for (i = 0; i < argc; i++) {
6273
226
    zval *arg = &args[i];
6274
6275
226
    ZVAL_DEREF(arg);
6276
226
    if (Z_TYPE_P(arg) == IS_ARRAY) {
6277
178
      SEPARATE_ARRAY(arg);
6278
      /* We see the next array, so we update the sort flags of
6279
       * the previous array and reset the sort flags. */
6280
178
      if (i > 0) {
6281
15
        func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6282
15
        sort_order = PHP_SORT_ASC;
6283
15
        sort_type = PHP_SORT_REGULAR;
6284
15
      }
6285
178
      arrays[num_arrays++] = arg;
6286
6287
      /* Next one may be an array or a list of sort flags. */
6288
534
      for (k = 0; k < MULTISORT_LAST; k++) {
6289
356
        parse_state[k] = 1;
6290
356
      }
6291
178
    } else if (Z_TYPE_P(arg) == IS_LONG) {
6292
42
      switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
6293
27
        case PHP_SORT_ASC:
6294
27
        case PHP_SORT_DESC:
6295
          /* flag allowed here */
6296
27
          if (parse_state[MULTISORT_ORDER] == 1) {
6297
            /* Save the flag and make sure then next arg is not the current flag. */
6298
24
            sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
6299
24
            parse_state[MULTISORT_ORDER] = 0;
6300
24
          } else {
6301
3
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6302
3
            MULTISORT_ABORT;
6303
0
          }
6304
24
          break;
6305
6306
24
        case PHP_SORT_REGULAR:
6307
0
        case PHP_SORT_NUMERIC:
6308
15
        case PHP_SORT_STRING:
6309
15
        case PHP_SORT_NATURAL:
6310
15
        case PHP_SORT_LOCALE_STRING:
6311
          /* flag allowed here */
6312
15
          if (parse_state[MULTISORT_TYPE] == 1) {
6313
            /* Save the flag and make sure then next arg is not the current flag. */
6314
12
            sort_type = (int)Z_LVAL_P(arg);
6315
12
            parse_state[MULTISORT_TYPE] = 0;
6316
12
          } else {
6317
3
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6318
3
            MULTISORT_ABORT;
6319
0
          }
6320
12
          break;
6321
6322
12
        default:
6323
0
          zend_argument_value_error(i + 1, "must be a valid sort flag");
6324
0
          MULTISORT_ABORT;
6325
0
          break;
6326
6327
42
      }
6328
42
    } else {
6329
6
      zend_argument_type_error(i + 1, "must be an array or a sort flag");
6330
6
      MULTISORT_ABORT;
6331
0
    }
6332
226
  }
6333
6334
  /* Make sure the arrays are of the same size. */
6335
155
  array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6336
169
  for (i = 1; i < num_arrays; i++) {
6337
14
    if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
6338
0
      zend_value_error("Array sizes are inconsistent");
6339
0
      MULTISORT_ABORT;
6340
0
    }
6341
14
  }
6342
6343
  /* If all arrays are empty we don't need to do anything. */
6344
155
  if (array_size < 1) {
6345
8
    efree(func);
6346
8
    efree(arrays);
6347
8
    RETURN_TRUE;
6348
8
  }
6349
6350
  /* Take care of the last array sort flags. */
6351
147
  func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6352
147
  bucket_compare_func_t *old_multisort_func = ARRAYG(multisort_func);
6353
147
  ARRAYG(multisort_func) = func;
6354
6355
  /* Create the indirection array. This array is of size MxN, where
6356
   * M is the number of entries in each input array and N is the number
6357
   * of the input arrays + 1. The last column is UNDEF to indicate the end
6358
   * of the row. It also stores the original position for stable sorting. */
6359
147
  indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6360
  /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6361
147
  Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6362
491
  for (i = 0; i < array_size; i++) {
6363
344
    indirect[i] = indirects + (i * (num_arrays + 1));
6364
344
  }
6365
147
  hashes = safe_emalloc(num_arrays, sizeof(HashTable *), 0);
6366
294
  for (i = 0; i < num_arrays; i++) {
6367
147
    hashes[i] = Z_ARRVAL_P(arrays[i]);
6368
147
    GC_ADDREF(hashes[i]);
6369
147
    HT_ALLOW_COW_VIOLATION(hashes[i]);
6370
147
  }
6371
294
  for (i = 0; i < num_arrays; i++) {
6372
147
    k = 0;
6373
147
    if (HT_IS_PACKED(hashes[i])) {
6374
82
      zval *zv = hashes[i]->arPacked;
6375
307
      for (idx = 0; idx < hashes[i]->nNumUsed; idx++, zv++) {
6376
225
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6377
224
        ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6378
224
        indirect[k][i].h = idx;
6379
224
        indirect[k][i].key = NULL;
6380
224
        k++;
6381
224
      }
6382
82
    } else {
6383
65
      Bucket *p = hashes[i]->arData;
6384
185
      for (idx = 0; idx < hashes[i]->nNumUsed; idx++, p++) {
6385
120
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
6386
120
        indirect[k][i] = *p;
6387
120
        k++;
6388
120
      }
6389
65
    }
6390
147
  }
6391
491
  for (k = 0; k < array_size; k++) {
6392
344
    ZVAL_UNDEF(&indirect[k][num_arrays].val);
6393
344
    Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6394
344
  }
6395
6396
  /* Do the actual sort magic - bada-bim, bada-boom. */
6397
147
  zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6398
147
  if (EG(exception)) {
6399
4
    goto clean_up;
6400
4
  }
6401
6402
  /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6403
286
  for (i = 0; i < num_arrays; i++) {
6404
143
    hash = hashes[i];
6405
143
    hash->nNumUsed = array_size;
6406
143
    hash->nNextFreeElement = array_size;
6407
143
    hash->nInternalPointer = 0;
6408
143
    if (HT_IS_PACKED(hash)) {
6409
306
      for (k = 0; k < array_size; k++) {
6410
224
        ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6411
224
      }
6412
82
    } else {
6413
61
      bool repack = true;
6414
6415
173
      for (n = 0, k = 0; k < array_size; k++) {
6416
112
        hash->arData[k] = indirect[k][i];
6417
112
        if (hash->arData[k].key == NULL) {
6418
14
          hash->arData[k].h = n++;
6419
98
        } else {
6420
98
          repack = false;
6421
98
        }
6422
112
      }
6423
61
      if (repack) {
6424
2
        zend_hash_to_packed(hash);
6425
59
      } else {
6426
59
        zend_hash_rehash(hash);
6427
59
      }
6428
61
    }
6429
143
  }
6430
143
  RETVAL_TRUE;
6431
6432
147
clean_up:
6433
294
  for (i = 0; i < num_arrays; i++) {
6434
147
    if (UNEXPECTED(GC_DELREF(hashes[i]) == 0)) {
6435
0
      zend_array_destroy(hashes[i]);
6436
147
    } else {
6437
147
      gc_check_possible_root((zend_refcounted *)hashes[i]);
6438
147
    }
6439
147
  }
6440
147
  efree(hashes);
6441
147
  efree(indirects);
6442
147
  efree(indirect);
6443
147
  efree(func);
6444
147
  efree(arrays);
6445
147
  ARRAYG(multisort_func) = old_multisort_func;
6446
147
}
6447
/* }}} */
6448
6449
/* {{{ php_array_pick_keys */
6450
PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent)
6451
0
{
6452
0
  const php_random_algo *algo = engine.algo;
6453
0
  void *state = engine.state;
6454
6455
0
  HashTable *ht = Z_ARRVAL_P(input);
6456
0
  uint32_t num_avail = zend_hash_num_elements(ht);
6457
0
  zend_long i, randval;
6458
0
  zend_string *string_key;
6459
0
  zend_ulong num_key;
6460
0
  zval *zv;
6461
0
  Bucket *b;
6462
0
  zend_bitset bitset;
6463
0
  int negative_bitset = 0;
6464
0
  uint32_t bitset_len;
6465
0
  ALLOCA_FLAG(use_heap);
6466
6467
0
  if (num_avail == 0) {
6468
0
    if (!silent) {
6469
0
      zend_argument_must_not_be_empty_error(1);
6470
0
    }
6471
0
    return false;
6472
0
  }
6473
6474
0
  if (num_req == 1) {
6475
0
    if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6476
      /* If less than 1/2 of elements are used, don't sample. Instead search for a
6477
       * specific offset using linear scan. */
6478
0
      i = 0;
6479
0
      randval = algo->range(state, 0, num_avail - 1);
6480
0
      if (EG(exception)) {
6481
0
        return false;
6482
0
      }
6483
0
      ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6484
0
        if (i == randval) {
6485
0
          if (string_key) {
6486
0
            ZVAL_STR_COPY(retval, string_key);
6487
0
          } else {
6488
0
            ZVAL_LONG(retval, num_key);
6489
0
          }
6490
0
          return true;
6491
0
        }
6492
0
        i++;
6493
0
      } ZEND_HASH_FOREACH_END();
6494
0
    }
6495
6496
    /* Sample random buckets until we hit one that is not empty.
6497
     * The worst case probability of hitting an empty element is 1-1/2. The worst case
6498
     * probability of hitting N empty elements in a row is (1-1/2)**N.
6499
     * For N=10 this becomes smaller than 0.1%. */
6500
0
    if (HT_IS_PACKED(ht)) {
6501
0
      do {
6502
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6503
0
        if (EG(exception)) {
6504
0
          return false;
6505
0
        }
6506
0
        zv = &ht->arPacked[randval];
6507
0
        if (!Z_ISUNDEF_P(zv)) {
6508
0
          ZVAL_LONG(retval, randval);
6509
0
          return true;
6510
0
        }
6511
0
      } while (true);
6512
0
    } else {
6513
0
      do {
6514
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6515
0
        if (EG(exception)) {
6516
0
          return false;
6517
0
        }
6518
0
        b = &ht->arData[randval];
6519
0
        if (!Z_ISUNDEF(b->val)) {
6520
0
          if (b->key) {
6521
0
            ZVAL_STR_COPY(retval, b->key);
6522
0
          } else {
6523
0
            ZVAL_LONG(retval, b->h);
6524
0
          }
6525
0
          return true;
6526
0
        }
6527
0
      } while (true);
6528
0
    }
6529
0
  }
6530
6531
0
  if (num_req <= 0 || num_req > num_avail) {
6532
0
    if (!silent) {
6533
0
      zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6534
0
    }
6535
0
    return false;
6536
0
  }
6537
6538
  /* Make the return value an array only if we need to pass back more than one result. */
6539
0
  array_init_size(retval, (uint32_t) num_req);
6540
0
  if (num_req > (num_avail >> 1)) {
6541
0
    negative_bitset = 1;
6542
0
    num_req = num_avail - num_req;
6543
0
  }
6544
6545
0
  bitset_len = zend_bitset_len(num_avail);
6546
0
  bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6547
0
  zend_bitset_clear(bitset, bitset_len);
6548
6549
0
  i = num_req;
6550
0
  int failures = 0;
6551
0
  while (i) {
6552
0
    randval = algo->range(state, 0, num_avail - 1);
6553
0
    if (EG(exception)) {
6554
0
      goto fail;
6555
0
    }
6556
0
    if (zend_bitset_in(bitset, randval)) {
6557
0
      if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6558
0
        if (!silent) {
6559
0
          zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6560
0
        }
6561
6562
0
        goto fail;
6563
0
      }
6564
0
    } else {
6565
0
      zend_bitset_incl(bitset, randval);
6566
0
      i--;
6567
0
      failures = 0;
6568
0
    }
6569
0
  }
6570
6571
0
  zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6572
0
  ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6573
    /* We can't use zend_hash_index_find()
6574
     * because the array may have string keys or gaps. */
6575
0
    ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6576
0
      if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6577
0
        if (string_key) {
6578
0
          ZEND_HASH_FILL_SET_STR_COPY(string_key);
6579
0
        } else {
6580
0
          ZEND_HASH_FILL_SET_LONG(num_key);
6581
0
        }
6582
0
        ZEND_HASH_FILL_NEXT();
6583
0
      }
6584
0
      i++;
6585
0
    } ZEND_HASH_FOREACH_END();
6586
0
  } ZEND_HASH_FILL_END();
6587
6588
0
  free_alloca(bitset, use_heap);
6589
6590
0
  return true;
6591
6592
0
 fail:
6593
0
  free_alloca(bitset, use_heap);
6594
6595
0
  return false;
6596
0
}
6597
/* }}} */
6598
6599
/* {{{ Return key/keys for random entry/entries in the array */
6600
PHP_FUNCTION(array_rand)
6601
0
{
6602
0
  zval *input;
6603
0
  zend_long num_req = 1;
6604
6605
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
6606
0
    Z_PARAM_ARRAY(input)
6607
0
    Z_PARAM_OPTIONAL
6608
0
    Z_PARAM_LONG(num_req)
6609
0
  ZEND_PARSE_PARAMETERS_END();
6610
6611
0
  if (!php_array_pick_keys(
6612
0
      php_random_default_engine(),
6613
0
      input,
6614
0
      num_req,
6615
0
      return_value,
6616
0
      false)
6617
0
  ) {
6618
0
    RETURN_THROWS();
6619
0
  }
6620
0
}
6621
/* }}} */
6622
6623
/* Apply a single array_sum/array_product step to return_value. */
6624
static zend_always_inline void php_array_binop_apply(
6625
    zval *return_value, zval *entry, const char *op_name, binary_op_type op)
6626
0
{
6627
  /* For objects we try to cast them to a numeric type */
6628
0
  if (Z_TYPE_P(entry) == IS_OBJECT) {
6629
0
    zval dst;
6630
0
    zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6631
6632
    /* Do not type error for BC */
6633
0
    if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6634
0
      php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6635
0
        op_name, zend_zval_type_name(entry));
6636
0
      return;
6637
0
    }
6638
0
    op(return_value, return_value, &dst);
6639
0
    return;
6640
0
  }
6641
6642
0
  zend_result status = op(return_value, return_value, entry);
6643
0
  if (status == FAILURE) {
6644
0
    ZEND_ASSERT(EG(exception));
6645
0
    zend_clear_exception();
6646
    /* BC resources: previously resources were cast to int */
6647
0
    if (Z_TYPE_P(entry) == IS_RESOURCE) {
6648
0
      zval tmp;
6649
0
      ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6650
0
      op(return_value, return_value, &tmp);
6651
0
    }
6652
    /* BC non numeric strings: previously were cast to 0 */
6653
0
    else if (Z_TYPE_P(entry) == IS_STRING) {
6654
0
      zval tmp;
6655
0
      ZVAL_LONG(&tmp, 0);
6656
0
      op(return_value, return_value, &tmp);
6657
0
    }
6658
0
    php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6659
0
      op_name, zend_zval_type_name(entry));
6660
0
  }
6661
0
}
6662
6663
/* Wrapper for array_sum and array_product */
6664
static zend_always_inline void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6665
99
{
6666
99
  HashTable *input;
6667
6668
297
  ZEND_PARSE_PARAMETERS_START(1, 1)
6669
396
    Z_PARAM_ARRAY_HT(input)
6670
99
  ZEND_PARSE_PARAMETERS_END();
6671
6672
99
  if (zend_hash_num_elements(input) == 0) {
6673
0
    RETURN_LONG(initial);
6674
0
  }
6675
6676
99
  ZVAL_LONG(return_value, initial);
6677
6678
99
  if (op == add_function) {
6679
99
    zval *entry;
6680
535
    ZEND_HASH_FOREACH_VAL(input, entry) {
6681
535
      if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) {
6682
218
        fast_long_add_function(return_value, return_value, entry);
6683
218
        continue;
6684
218
      }
6685
0
      php_array_binop_apply(return_value, entry, op_name, op);
6686
0
    } ZEND_HASH_FOREACH_END();
6687
99
  } else if (op == mul_function) {
6688
0
    zval *entry;
6689
0
    ZEND_HASH_FOREACH_VAL(input, entry) {
6690
0
      if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) {
6691
0
        zend_long lval;
6692
0
        double dval;
6693
0
        int overflow;
6694
0
        ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(return_value), Z_LVAL_P(entry), lval, dval, overflow);
6695
0
        if (UNEXPECTED(overflow)) {
6696
0
          ZVAL_DOUBLE(return_value, dval);
6697
0
        } else {
6698
0
          Z_LVAL_P(return_value) = lval;
6699
0
        }
6700
0
        continue;
6701
0
      }
6702
0
      php_array_binop_apply(return_value, entry, op_name, op);
6703
0
    } ZEND_HASH_FOREACH_END();
6704
0
  } else {
6705
0
    zval *entry;
6706
0
    ZEND_HASH_FOREACH_VAL(input, entry) {
6707
0
      php_array_binop_apply(return_value, entry, op_name, op);
6708
0
    } ZEND_HASH_FOREACH_END();
6709
0
  }
6710
99
}
6711
6712
/* {{{ Returns the sum of the array entries */
6713
PHP_FUNCTION(array_sum)
6714
99
{
6715
99
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6716
99
}
6717
/* }}} */
6718
6719
/* {{{ Returns the product of the array entries */
6720
PHP_FUNCTION(array_product)
6721
0
{
6722
0
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6723
0
}
6724
/* }}} */
6725
6726
/* {{{ Iteratively reduce the array to a single value via the callback. */
6727
PHP_FUNCTION(array_reduce)
6728
21
{
6729
21
  zval *input;
6730
21
  zval args[2];
6731
21
  zval *operand;
6732
21
  zend_fcall_info fci;
6733
21
  zend_fcall_info_cache fci_cache;
6734
21
  zval *initial = NULL;
6735
21
  HashTable *htbl;
6736
6737
63
  ZEND_PARSE_PARAMETERS_START(2, 3)
6738
84
    Z_PARAM_ARRAY(input)
6739
85
    Z_PARAM_FUNC(fci, fci_cache)
6740
16
    Z_PARAM_OPTIONAL
6741
62
    Z_PARAM_ZVAL(initial)
6742
62
  ZEND_PARSE_PARAMETERS_END();
6743
6744
16
  if (initial) {
6745
15
    ZVAL_COPY(return_value, initial);
6746
15
  } else {
6747
1
    ZVAL_NULL(return_value);
6748
1
  }
6749
6750
  /* (zval **)input points to an element of argument stack
6751
   * the base pointer of which is subject to change.
6752
   * thus we need to keep the pointer to the hashtable for safety */
6753
16
  htbl = Z_ARRVAL_P(input);
6754
6755
16
  if (zend_hash_num_elements(htbl) == 0) {
6756
0
    return;
6757
0
  }
6758
6759
16
  fci.retval = return_value;
6760
16
  fci.param_count = 2;
6761
16
  fci.params = args;
6762
16
  fci.consumed_args = zend_fci_consumed_arg(0);
6763
6764
90
  ZEND_HASH_FOREACH_VAL(htbl, operand) {
6765
90
    ZVAL_COPY_VALUE(&args[0], return_value);
6766
90
    ZVAL_COPY_VALUE(&args[1], operand);
6767
6768
90
    zend_call_function(&fci, &fci_cache);
6769
90
    zval_ptr_dtor(&args[0]);
6770
6771
90
    if (EXPECTED(!Z_ISUNDEF_P(return_value))) {
6772
35
      if (UNEXPECTED(Z_ISREF_P(return_value))) {
6773
8
        zend_unwrap_reference(return_value);
6774
8
      }
6775
35
    } else {
6776
2
      RETURN_NULL();
6777
2
    }
6778
90
  } ZEND_HASH_FOREACH_END();
6779
16
}
6780
/* }}} */
6781
6782
/* Consumes `zv` */
6783
static bool php_is_true(zval *zv)
6784
819
{
6785
819
  switch (Z_TYPE_P(zv)) {
6786
4
    case IS_TRUE:
6787
4
      return true;
6788
0
    case IS_FALSE:
6789
0
      return false;
6790
815
    default: {
6791
815
      bool rv = zend_is_true(zv);
6792
815
      zval_ptr_dtor(zv);
6793
815
      return rv;
6794
0
    }
6795
819
  }
6796
819
}
6797
6798
/* {{{ Filters elements from the array via the callback. */
6799
PHP_FUNCTION(array_filter)
6800
32
{
6801
32
  zval *array;
6802
32
  zval *operand;
6803
32
  zval *key;
6804
32
  zval args[2];
6805
32
  zval retval;
6806
32
  bool have_callback = 0;
6807
32
  zend_long use_type = ARRAY_FILTER_USE_VALUE;
6808
32
  zend_string *string_key;
6809
32
  zend_fcall_info fci = empty_fcall_info;
6810
32
  zend_fcall_info_cache fci_cache;
6811
32
  zend_ulong num_key;
6812
6813
96
  ZEND_PARSE_PARAMETERS_START(1, 3)
6814
128
    Z_PARAM_ARRAY(array)
6815
30
    Z_PARAM_OPTIONAL
6816
120
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6817
78
    Z_PARAM_LONG(use_type)
6818
32
  ZEND_PARSE_PARAMETERS_END();
6819
6820
26
  switch (use_type) {
6821
26
    case ARRAY_FILTER_USE_VALUE:
6822
26
    case ARRAY_FILTER_USE_BOTH:
6823
26
    case ARRAY_FILTER_USE_KEY:
6824
26
      break;
6825
0
    default:
6826
0
      zend_argument_value_error(3, "must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH");
6827
0
    RETURN_THROWS();
6828
26
  }
6829
6830
26
  if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6831
5
    RETURN_EMPTY_ARRAY();
6832
5
  }
6833
21
  array_init(return_value);
6834
6835
21
  if (ZEND_FCI_INITIALIZED(fci)) {
6836
14
    have_callback = 1;
6837
14
    fci.retval = &retval;
6838
14
    if (use_type == ARRAY_FILTER_USE_BOTH) {
6839
0
      fci.param_count = 2;
6840
0
      key = &args[1];
6841
14
    } else {
6842
14
      fci.param_count = 1;
6843
14
      key = &args[0];
6844
14
    }
6845
14
  }
6846
6847
1.69k
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6848
1.69k
    if (have_callback) {
6849
829
      if (use_type != ARRAY_FILTER_USE_VALUE) {
6850
        /* Set up the key */
6851
0
        if (!string_key) {
6852
0
          ZVAL_LONG(key, num_key);
6853
0
        } else {
6854
0
          ZVAL_STR(key, string_key);
6855
0
        }
6856
0
      }
6857
829
      if (use_type != ARRAY_FILTER_USE_KEY) {
6858
829
        ZVAL_COPY_VALUE(&args[0], operand);
6859
829
      }
6860
829
      fci.params = args;
6861
6862
829
      zend_result result = zend_call_function(&fci, &fci_cache);
6863
829
      ZEND_ASSERT(result == SUCCESS);
6864
6865
829
      if (UNEXPECTED(EG(exception))) {
6866
5
        RETURN_THROWS();
6867
5
      }
6868
6869
819
      if (!php_is_true(&retval)) {
6870
815
        continue;
6871
815
      }
6872
819
    } else if (!zend_is_true(operand)) {
6873
7
      continue;
6874
7
    }
6875
6876
4
    if (string_key) {
6877
0
      operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6878
4
    } else {
6879
4
      operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6880
4
    }
6881
4
    zval_add_ref(operand);
6882
4
  } ZEND_HASH_FOREACH_END();
6883
21
}
6884
/* }}} */
6885
6886
/* {{{ Internal function to find an array element for a user closure. */
6887
enum php_array_find_result {
6888
  PHP_ARRAY_FIND_EXCEPTION = -1,
6889
  PHP_ARRAY_FIND_NONE = 0,
6890
  PHP_ARRAY_FIND_SOME = 1,
6891
};
6892
6893
static enum php_array_find_result php_array_find(const HashTable *array, zend_fcall_info fci, zend_fcall_info_cache *fci_cache, zval *result_key, zval *result_value, bool negate_condition)
6894
0
{
6895
0
  zend_ulong num_key;
6896
0
  zend_string *str_key;
6897
0
  zval retval;
6898
0
  zval args[2];
6899
0
  zval *operand;
6900
6901
0
  if (zend_hash_num_elements(array) == 0) {
6902
0
    return PHP_ARRAY_FIND_NONE;
6903
0
  }
6904
6905
0
  ZEND_ASSERT(ZEND_FCI_INITIALIZED(fci));
6906
6907
0
  fci.retval = &retval;
6908
0
  fci.param_count = 2;
6909
0
  fci.params = args;
6910
6911
0
  ZEND_HASH_FOREACH_KEY_VAL(array, num_key, str_key, operand) {
6912
    /* Set up the key */
6913
0
    if (!str_key) {
6914
0
      ZVAL_LONG(&args[1], num_key);
6915
0
    } else {
6916
      /* Allows copying the numeric branch, without this branch, into the iteration code
6917
       * that checks for the packed array flag. */
6918
0
      ZEND_ASSUME(!HT_IS_PACKED(array));
6919
0
      ZVAL_STR(&args[1], str_key);
6920
0
    }
6921
6922
0
    ZVAL_COPY_VALUE(&args[0], operand);
6923
6924
0
    zend_result result = zend_call_function(&fci, fci_cache);
6925
0
    ZEND_ASSERT(result == SUCCESS);
6926
6927
0
    if (UNEXPECTED(Z_ISUNDEF(retval))) {
6928
0
      return PHP_ARRAY_FIND_EXCEPTION;
6929
0
    }
6930
6931
0
    if (php_is_true(&retval) ^ negate_condition) {
6932
0
      if (result_value != NULL) {
6933
0
        ZVAL_COPY_DEREF(result_value, &args[0]);
6934
0
      }
6935
6936
0
      if (result_key != NULL) {
6937
0
        ZVAL_COPY(result_key, &args[1]);
6938
0
      }
6939
6940
0
      return PHP_ARRAY_FIND_SOME;
6941
0
    }
6942
0
  } ZEND_HASH_FOREACH_END();
6943
6944
0
  return PHP_ARRAY_FIND_NONE;
6945
0
}
6946
/* }}} */
6947
6948
/* {{{ Search within an array and returns the first found element value. */
6949
PHP_FUNCTION(array_find)
6950
0
{
6951
0
  HashTable *array;
6952
0
  zend_fcall_info fci;
6953
0
  zend_fcall_info_cache fci_cache;
6954
6955
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6956
0
    Z_PARAM_ARRAY_HT(array)
6957
0
    Z_PARAM_FUNC(fci, fci_cache)
6958
0
  ZEND_PARSE_PARAMETERS_END();
6959
6960
0
  php_array_find(array, fci, &fci_cache, NULL, return_value, false);
6961
0
}
6962
/* }}} */
6963
6964
/* {{{ Search within an array and returns the first found element key. */
6965
PHP_FUNCTION(array_find_key)
6966
0
{
6967
0
  HashTable *array;
6968
0
  zend_fcall_info fci;
6969
0
  zend_fcall_info_cache fci_cache;
6970
6971
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6972
0
    Z_PARAM_ARRAY_HT(array)
6973
0
    Z_PARAM_FUNC(fci, fci_cache)
6974
0
  ZEND_PARSE_PARAMETERS_END();
6975
6976
0
  php_array_find(array, fci, &fci_cache, return_value, NULL, false);
6977
0
}
6978
/* }}} */
6979
6980
/* {{{ Checks if at least one array element satisfies a callback function. */
6981
PHP_FUNCTION(array_any)
6982
0
{
6983
0
  HashTable *array;
6984
0
  zend_fcall_info fci;
6985
0
  zend_fcall_info_cache fci_cache;
6986
6987
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6988
0
    Z_PARAM_ARRAY_HT(array)
6989
0
    Z_PARAM_FUNC(fci, fci_cache)
6990
0
  ZEND_PARSE_PARAMETERS_END();
6991
6992
0
  RETURN_BOOL(php_array_find(array, fci, &fci_cache, NULL, NULL, false) == PHP_ARRAY_FIND_SOME);
6993
0
}
6994
/* }}} */
6995
6996
/* {{{ Checks if all array elements satisfy a callback function. */
6997
PHP_FUNCTION(array_all)
6998
0
{
6999
0
  HashTable *array;
7000
0
  zend_fcall_info fci;
7001
0
  zend_fcall_info_cache fci_cache;
7002
7003
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7004
0
    Z_PARAM_ARRAY_HT(array)
7005
0
    Z_PARAM_FUNC(fci, fci_cache)
7006
0
  ZEND_PARSE_PARAMETERS_END();
7007
7008
0
  RETURN_BOOL(php_array_find(array, fci, &fci_cache, NULL, NULL, true) == PHP_ARRAY_FIND_NONE);
7009
0
}
7010
/* }}} */
7011
7012
/* {{{ Applies the callback to the elements in given arrays. */
7013
PHP_FUNCTION(array_map)
7014
1.46k
{
7015
1.46k
  zval *arrays = NULL;
7016
1.46k
  uint32_t n_arrays = 0;
7017
1.46k
  zval result;
7018
1.46k
  zend_fcall_info fci;
7019
1.46k
  zend_fcall_info_cache fci_cache;
7020
1.46k
  uint32_t i;
7021
1.46k
  uint32_t k, maxlen = 0;
7022
7023
4.39k
  ZEND_PARSE_PARAMETERS_START(2, -1)
7024
5.86k
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
7025
1.40k
    Z_PARAM_VARIADIC('+', arrays, n_arrays)
7026
1.46k
  ZEND_PARSE_PARAMETERS_END();
7027
7028
1.40k
  if (n_arrays == 1) {
7029
1.38k
    if (Z_TYPE(arrays[0]) != IS_ARRAY) {
7030
10
      zend_wrong_parameter_type_error(2, Z_EXPECTED_ARRAY, &arrays[0]);
7031
10
      RETURN_THROWS();
7032
10
    }
7033
1.37k
    const HashTable *input = Z_ARRVAL(arrays[0]);
7034
1.37k
    maxlen = zend_hash_num_elements(input);
7035
7036
    /* Short-circuit: if no callback and only one array, just return it. */
7037
1.37k
    if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
7038
43
      ZVAL_COPY(return_value, &arrays[0]);
7039
43
      return;
7040
43
    }
7041
7042
1.33k
    fci.retval = &result;
7043
1.33k
    fci.param_count = 1;
7044
7045
1.33k
    if (HT_IS_PACKED(input)) {
7046
1.29k
      array_init_size(return_value, input->nNumUsed);
7047
1.29k
      HashTable *output = Z_ARRVAL_P(return_value);
7048
1.29k
      zend_hash_real_init_packed(output);
7049
7050
1.29k
      uint32_t undefs = 0;
7051
1.29k
      ZEND_HASH_FILL_PACKED(output) {
7052
        /* Can't use ZEND_HASH_PACKED_FOREACH_VAL() because we need to also account for the UNDEF values
7053
         * so the keys in the output array will match those of the input array. */
7054
1.92k
        for (zval *cur = input->arPacked, *end = input->arPacked + input->nNumUsed; cur != end; cur++) {
7055
1.66k
          if (EXPECTED(!Z_ISUNDEF_P(cur))) {
7056
1.66k
            fci.params = cur;
7057
1.66k
            zend_result ret = zend_call_function(&fci, &fci_cache);
7058
1.66k
            ZEND_ASSERT(ret == SUCCESS);
7059
1.66k
            ZEND_IGNORE_VALUE(ret);
7060
665
            if (UNEXPECTED(Z_ISUNDEF(result))) {
7061
38
              ZEND_HASH_FILL_FINISH();
7062
38
              RETURN_THROWS();
7063
38
            }
7064
665
          } else {
7065
0
            ZVAL_UNDEF(&result);
7066
0
            undefs++;
7067
0
          }
7068
627
          ZEND_HASH_FILL_ADD(&result);
7069
627
        }
7070
1.29k
      } ZEND_HASH_FILL_END();
7071
263
      output->nNumOfElements -= undefs;
7072
263
    } else {
7073
35
      zend_ulong num_key;
7074
35
      zend_string *str_key;
7075
7076
35
      array_init_size(return_value, maxlen);
7077
35
      HashTable *output = Z_ARRVAL_P(return_value);
7078
35
      zend_hash_real_init_mixed(output);
7079
7080
450
      ZEND_HASH_MAP_FOREACH_KEY_VAL(input, num_key, str_key, fci.params) {
7081
450
        zend_result ret = zend_call_function(&fci, &fci_cache);
7082
450
        ZEND_ASSERT(ret == SUCCESS);
7083
450
        ZEND_IGNORE_VALUE(ret);
7084
190
        if (UNEXPECTED(Z_ISUNDEF(result))) {
7085
4
          RETURN_THROWS();
7086
4
        }
7087
186
        if (str_key) {
7088
186
          _zend_hash_append(output, str_key, &result);
7089
186
        } else {
7090
0
          zend_hash_index_add_new(output, num_key, &result);
7091
0
        }
7092
186
      } ZEND_HASH_FOREACH_END();
7093
35
    }
7094
1.33k
  } else {
7095
54
    for (i = 0; i < n_arrays; i++) {
7096
42
      if (Z_TYPE(arrays[i]) != IS_ARRAY) {
7097
6
        zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
7098
6
        RETURN_THROWS();
7099
6
      }
7100
36
      if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
7101
14
        maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
7102
14
      }
7103
36
    }
7104
7105
12
    array_init_size(return_value, maxlen);
7106
7107
12
    if (!ZEND_FCI_INITIALIZED(fci)) {
7108
12
      uint32_t *array_pos = ecalloc(n_arrays, sizeof(HashPosition));
7109
12
      zval zv;
7110
7111
      /* We iterate through all the arrays at once. */
7112
50
      for (k = 0; k < maxlen; k++) {
7113
7114
        /* If no callback, the result will be an array, consisting of current
7115
         * entries from all arrays. */
7116
38
        array_init_size(&result, n_arrays);
7117
7118
155
        for (i = 0; i < n_arrays; i++) {
7119
          /* If this array still has elements, add the current one to the
7120
           * parameter list, otherwise use null value. */
7121
117
          uint32_t pos = array_pos[i];
7122
117
          if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
7123
117
            while (1) {
7124
117
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7125
19
                ZVAL_NULL(&zv);
7126
19
                break;
7127
98
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
7128
98
                ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
7129
98
                array_pos[i] = pos + 1;
7130
98
                break;
7131
98
              }
7132
0
              pos++;
7133
0
            }
7134
117
          } else {
7135
0
            while (1) {
7136
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7137
0
                ZVAL_NULL(&zv);
7138
0
                break;
7139
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
7140
0
                ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
7141
0
                array_pos[i] = pos + 1;
7142
0
                break;
7143
0
              }
7144
0
              pos++;
7145
0
            }
7146
0
          }
7147
117
          zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
7148
117
        }
7149
7150
38
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
7151
38
      }
7152
7153
12
      efree(array_pos);
7154
12
    } else {
7155
0
      zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
7156
7157
      /* Remember next starting point in the array, initialize those as zeros. */
7158
0
      for (i = 0; i < n_arrays; i++) {
7159
0
        Z_EXTRA(params[i]) = 0;
7160
0
      }
7161
7162
0
      fci.retval = &result;
7163
0
      fci.param_count = n_arrays;
7164
0
      fci.params = params;
7165
7166
      /* We iterate through all the arrays at once. */
7167
0
      for (k = 0; k < maxlen; k++) {
7168
0
        for (i = 0; i < n_arrays; i++) {
7169
          /* If this array still has elements, add the current one to the
7170
           * parameter list, otherwise use null value. */
7171
0
          uint32_t pos = Z_EXTRA(params[i]);
7172
0
          if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
7173
0
            while (1) {
7174
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7175
0
                ZVAL_NULL(&params[i]);
7176
0
                break;
7177
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
7178
0
                ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
7179
0
                Z_EXTRA(params[i]) = pos + 1;
7180
0
                break;
7181
0
              }
7182
0
              pos++;
7183
0
            }
7184
0
          } else {
7185
0
            while (1) {
7186
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
7187
0
                ZVAL_NULL(&params[i]);
7188
0
                break;
7189
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
7190
0
                ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
7191
0
                Z_EXTRA(params[i]) = pos + 1;
7192
0
                break;
7193
0
              }
7194
0
              pos++;
7195
0
            }
7196
0
          }
7197
0
        }
7198
7199
0
        zend_result ret = zend_call_function(&fci, &fci_cache);
7200
0
        ZEND_ASSERT(ret == SUCCESS);
7201
0
        ZEND_IGNORE_VALUE(ret);
7202
7203
0
        if (Z_TYPE(result) == IS_UNDEF) {
7204
0
          efree(params);
7205
0
          RETURN_THROWS();
7206
0
        }
7207
7208
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
7209
0
      }
7210
7211
0
      efree(params);
7212
0
    }
7213
12
  }
7214
1.40k
}
7215
/* }}} */
7216
7217
/* {{{ Checks if the given key or index exists in the array */
7218
PHP_FUNCTION(array_key_exists)
7219
6
{
7220
6
  zval *key;
7221
6
  HashTable *ht;
7222
7223
12
  ZEND_PARSE_PARAMETERS_START(2, 2)
7224
12
    Z_PARAM_ZVAL(key)
7225
0
    Z_PARAM_ARRAY_HT(ht)
7226
6
  ZEND_PARSE_PARAMETERS_END();
7227
7228
0
  switch (Z_TYPE_P(key)) {
7229
0
    case IS_STRING:
7230
0
      RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
7231
0
      break;
7232
0
    case IS_LONG:
7233
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
7234
0
      break;
7235
0
    case IS_NULL:
7236
0
      zend_error(E_DEPRECATED, "Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead");
7237
0
      RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
7238
0
      break;
7239
0
    case IS_DOUBLE:
7240
0
      RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
7241
0
      break;
7242
0
    case IS_FALSE:
7243
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 0));
7244
0
      break;
7245
0
    case IS_TRUE:
7246
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 1));
7247
0
      break;
7248
0
    case IS_RESOURCE:
7249
0
      zend_use_resource_as_offset(key);
7250
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
7251
0
      break;
7252
0
    default:
7253
0
      zend_argument_type_error(1, "must be a valid array offset type");
7254
0
      break;
7255
0
  }
7256
0
}
7257
/* }}} */
7258
7259
/* {{{ Split array into chunks */
7260
PHP_FUNCTION(array_chunk)
7261
0
{
7262
0
  int num_in;
7263
0
  zend_long size, current = 0;
7264
0
  zend_string *str_key;
7265
0
  zend_ulong num_key;
7266
0
  bool preserve_keys = 0;
7267
0
  zval *input = NULL;
7268
0
  zval chunk;
7269
0
  zval *entry;
7270
7271
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
7272
0
    Z_PARAM_ARRAY(input)
7273
0
    Z_PARAM_LONG(size)
7274
0
    Z_PARAM_OPTIONAL
7275
0
    Z_PARAM_BOOL(preserve_keys)
7276
0
  ZEND_PARSE_PARAMETERS_END();
7277
7278
  /* Do bounds checking for size parameter. */
7279
0
  if (size < 1) {
7280
0
    zend_argument_value_error(2, "must be greater than 0");
7281
0
    RETURN_THROWS();
7282
0
  }
7283
7284
0
  num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
7285
7286
0
  if (size > num_in) {
7287
0
    if (num_in == 0) {
7288
0
      RETURN_EMPTY_ARRAY();
7289
0
    }
7290
0
    size = num_in;
7291
0
  }
7292
7293
0
  array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
7294
0
  zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
7295
7296
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
7297
    /* If new chunk, create and initialize it. */
7298
0
    if (current == 0) {
7299
0
      array_init_size(&chunk, (uint32_t)size);
7300
0
      add_next_index_zval(return_value, &chunk);
7301
0
    }
7302
7303
    /* Add entry to the chunk, preserving keys if necessary. */
7304
0
    if (preserve_keys) {
7305
0
      if (str_key) {
7306
0
        entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
7307
0
      } else {
7308
0
        entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
7309
0
      }
7310
0
    } else {
7311
0
      entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
7312
0
    }
7313
0
    zval_add_ref(entry);
7314
7315
0
    if (++current == size) {
7316
0
      current = 0;
7317
0
    }
7318
0
  } ZEND_HASH_FOREACH_END();
7319
0
}
7320
/* }}} */
7321
7322
/* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
7323
PHP_FUNCTION(array_combine)
7324
0
{
7325
0
  HashTable *values, *keys;
7326
0
  uint32_t pos_values = 0;
7327
0
  zval *entry_keys, *entry_values;
7328
0
  int num_keys, num_values;
7329
7330
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7331
0
    Z_PARAM_ARRAY_HT(keys)
7332
0
    Z_PARAM_ARRAY_HT(values)
7333
0
  ZEND_PARSE_PARAMETERS_END();
7334
7335
0
  num_keys = zend_hash_num_elements(keys);
7336
0
  num_values = zend_hash_num_elements(values);
7337
7338
0
  if (num_keys != num_values) {
7339
0
    zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
7340
0
    RETURN_THROWS();
7341
0
  }
7342
7343
0
  if (!num_keys) {
7344
0
    RETURN_EMPTY_ARRAY();
7345
0
  }
7346
7347
0
  array_init_size(return_value, num_keys);
7348
0
  ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
7349
0
    while (1) {
7350
0
      if (pos_values >= values->nNumUsed) {
7351
0
        break;
7352
0
      }
7353
0
      entry_values = ZEND_HASH_ELEMENT(values, pos_values);
7354
0
      if (Z_TYPE_P(entry_values) != IS_UNDEF) {
7355
0
        if (Z_TYPE_P(entry_keys) == IS_LONG) {
7356
0
          entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
7357
0
            Z_LVAL_P(entry_keys), entry_values);
7358
0
        } else {
7359
0
          zend_string *tmp_key;
7360
0
          zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
7361
0
          entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
7362
0
            key, entry_values);
7363
0
          zend_tmp_string_release(tmp_key);
7364
0
        }
7365
0
        zval_add_ref(entry_values);
7366
0
        pos_values++;
7367
0
        break;
7368
0
      }
7369
0
      pos_values++;
7370
0
    }
7371
0
  } ZEND_HASH_FOREACH_END();
7372
0
}
7373
/* }}} */