Coverage Report

Created: 2026-02-14 06:52

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