Coverage Report

Created: 2026-06-13 07:01

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