Coverage Report

Created: 2026-07-25 06:39

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