Coverage Report

Created: 2025-12-14 06:10

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
5
#define DIFF_COMP_DATA_NONE    -1
47
0
#define DIFF_COMP_DATA_INTERNAL 0
48
5
#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
60.1k
static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
90
60.1k
  if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
91
34.8k
    return 1;
92
34.8k
  } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
93
25.3k
    return -1;
94
25.3k
  } else {
95
0
    return 0;
96
0
  }
97
60.1k
}
98
99
203k
#define RETURN_STABLE_SORT(a, b, result) do { \
100
203k
  int _result = (result); \
101
203k
  if (EXPECTED(_result)) { \
102
143k
    return _result; \
103
143k
  } \
104
203k
  return stable_sort_fallback((a), (b)); \
105
203k
} while (0)
106
107
/* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
108
#define DEFINE_SORT_VARIANTS(name) \
109
3.28k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
3.28k
    return php_array_##name##_unstable_i(a, b); \
111
3.28k
  } \
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
9
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
9
    return php_array_##name##_unstable_i(a, b); \
111
9
  } \
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
3.27k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
3.27k
    return php_array_##name##_unstable_i(a, b); \
111
3.27k
  } \
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
200k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
200k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
200k
  } \
array.c:php_array_natural_compare
Line
Count
Source
112
27.4k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
27.4k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
27.4k
  } \
array.c:php_array_natural_case_compare
Line
Count
Source
112
50
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
50
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
50
  } \
array.c:php_array_data_compare_numeric
Line
Count
Source
112
4.91k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
4.91k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
4.91k
  } \
array.c:php_array_data_compare_string_case
Line
Count
Source
112
38
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
38
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
38
  } \
array.c:php_array_data_compare_string
Line
Count
Source
112
108k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
108k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
108k
  } \
array.c:php_array_data_compare_string_locale
Line
Count
Source
112
1.70k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
1.70k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
1.70k
  } \
array.c:php_array_data_compare
Line
Count
Source
112
57.6k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
57.6k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
57.6k
  } \
array.c:php_array_key_compare_numeric
Line
Count
Source
112
5
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
5
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
5
  } \
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
2.53k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
116
2.53k
    return php_array_##name##_unstable(a, b) * -1; \
117
2.53k
  } \
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
2.53k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
116
2.53k
    return php_array_##name##_unstable(a, b) * -1; \
117
2.53k
  } \
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
2.53k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
119
2.53k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
120
2.53k
  } \
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
2.53k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
119
2.53k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
120
2.53k
  } \
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
5
{
148
5
  if (f->key == NULL && s->key == NULL) {
149
5
    return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
150
5
  } 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
5
}
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
60.9k
{
288
60.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
60.9k
  zval *rhs = &s->val;
292
60.9k
  ZVAL_DEREF(rhs);
293
60.9k
  if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
294
600
   && result == ZEND_UNCOMPARABLE
295
527
   && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
296
527
    zval *lhs = &f->val;
297
527
    ZVAL_DEREF(lhs);
298
527
    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
527
      uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
301
527
      uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
302
527
      return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
303
527
    } else {
304
      // Shift enums to the end of the array
305
0
      return -1;
306
0
    }
307
527
  }
308
60.4k
  return result;
309
60.9k
}
310
/* }}} */
311
312
static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
313
4.91k
{
314
4.91k
  return numeric_compare_function(&f->val, &s->val);
315
4.91k
}
316
/* }}} */
317
318
static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
319
38
{
320
38
  return string_case_compare_function(&f->val, &s->val);
321
38
}
322
/* }}} */
323
324
static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
325
108k
{
326
108k
  return string_compare_function(&f->val, &s->val);
327
108k
}
328
/* }}} */
329
330
static int php_array_natural_general_compare(Bucket *f, Bucket *s, bool fold_case) /* {{{ */
331
27.4k
{
332
27.4k
  zend_string *tmp_str1, *tmp_str2;
333
27.4k
  zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
334
27.4k
  zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
335
336
27.4k
  int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
337
338
27.4k
  zend_tmp_string_release(tmp_str1);
339
27.4k
  zend_tmp_string_release(tmp_str2);
340
27.4k
  return result;
341
27.4k
}
342
/* }}} */
343
344
static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
345
27.4k
{
346
27.4k
  return php_array_natural_general_compare(a, b, false);
347
27.4k
}
348
/* }}} */
349
350
static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
351
50
{
352
50
  return php_array_natural_general_compare(a, b, true);
353
50
}
354
/* }}} */
355
356
static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) /* {{{ */
357
1.70k
{
358
1.70k
  return string_locale_compare_function(&f->val, &s->val);
359
1.70k
}
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
4
{
377
4
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
378
2
    case PHP_SORT_NUMERIC:
379
2
      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
4
  }
402
0
  return NULL;
403
4
}
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
620
{
437
620
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
438
24
    case PHP_SORT_NUMERIC:
439
24
      return php_array_data_compare_numeric;
440
441
214
    case PHP_SORT_STRING:
442
214
      if (sort_type & PHP_SORT_FLAG_CASE) {
443
7
        return php_array_data_compare_string_case;
444
207
      } else {
445
207
        return php_array_data_compare_string;
446
207
      }
447
448
228
    case PHP_SORT_NATURAL:
449
228
      if (sort_type & PHP_SORT_FLAG_CASE) {
450
2
        return php_array_natural_case_compare;
451
226
      } else {
452
226
        return php_array_natural_compare;
453
226
      }
454
455
24
    case PHP_SORT_LOCALE_STRING:
456
24
      return php_array_data_compare_string_locale;
457
458
65
    case PHP_SORT_REGULAR:
459
130
    default:
460
130
      return php_array_data_compare;
461
620
  }
462
0
  return NULL;
463
620
}
464
465
static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_type) /* {{{ */
466
80
{
467
80
  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
80
    case PHP_SORT_REGULAR:
489
80
    default:
490
80
      return php_array_reverse_data_compare;
491
80
  }
492
0
  return NULL;
493
80
}
494
495
static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, bool reverse) /* {{{ */
496
124
{
497
124
  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
11
    case PHP_SORT_STRING:
507
11
      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
11
      } else {
514
11
        if (reverse) {
515
0
          return php_array_reverse_data_compare_string_unstable;
516
11
        } else {
517
11
          return php_array_data_compare_string_unstable;
518
11
        }
519
11
      }
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
113
    case PHP_SORT_REGULAR:
547
113
    default:
548
113
      if (reverse) {
549
0
        return php_array_reverse_data_compare_unstable;
550
113
      } else {
551
113
        return php_array_data_compare_unstable;
552
113
      }
553
0
      break;
554
124
  }
555
0
  return NULL;
556
124
}
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
717
static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) {
685
717
  HashTable *array;
686
717
  zend_long sort_type = PHP_SORT_REGULAR;
687
717
  bucket_compare_func_t cmp;
688
689
2.14k
  ZEND_PARSE_PARAMETERS_START(1, 2)
690
2.84k
    Z_PARAM_ARRAY_HT_EX(array, 0, 1)
691
704
    Z_PARAM_OPTIONAL
692
2.54k
    Z_PARAM_LONG(sort_type)
693
717
  ZEND_PARSE_PARAMETERS_END();
694
695
704
  cmp = get_cmp(sort_type);
696
697
704
  zend_array_sort(array, cmp, renumber);
698
699
704
  RETURN_TRUE;
700
704
}
701
702
/* {{{ Sort an array and maintain index association */
703
PHP_FUNCTION(asort)
704
8
{
705
8
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, false);
706
8
}
707
/* }}} */
708
709
/* {{{ Sort an array in reverse order and maintain index association */
710
PHP_FUNCTION(arsort)
711
68
{
712
68
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, false);
713
68
}
714
/* }}} */
715
716
/* {{{ Sort an array */
717
PHP_FUNCTION(sort)
718
623
{
719
623
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, true);
720
623
}
721
/* }}} */
722
723
/* {{{ Sort an array in reverse order */
724
PHP_FUNCTION(rsort)
725
14
{
726
14
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, true);
727
14
}
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
4
{
740
4
  php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_compare_func, false);
741
4
}
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.27k
static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
917
1.27k
  if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
918
1.22k
    return Z_ARRVAL_P(zv);
919
1.22k
  }
920
921
49
  ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
922
49
  php_error_docref(NULL, E_DEPRECATED,
923
49
    "Calling %s() on an object is deprecated", get_active_function_name());
924
925
49
  zend_object *zobj = Z_OBJ_P(zv);
926
49
  if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
927
7
    if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
928
7
      GC_DELREF(zobj->properties);
929
7
    }
930
7
    zobj->properties = zend_array_dup(zobj->properties);
931
7
  }
932
49
  return zobj->handlers->get_properties(zobj);
933
49
}
934
935
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
936
326
{
937
326
  zval *entry;
938
939
326
  while (true) {
940
326
    if ((entry = zend_hash_get_current_data(array)) == NULL) {
941
27
      return NULL;
942
27
    }
943
944
299
    ZVAL_DEINDIRECT(entry);
945
946
    /* Possible with an uninitialized typed property */
947
299
    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
299
    } else {
958
299
      break;
959
299
    }
960
299
  }
961
962
299
  return entry;
963
326
}
964
965
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
966
147
{
967
147
  zval *entry = php_array_iter_seek_current(array, forward_direction);
968
147
  if (EXPECTED(entry)) {
969
129
    RETURN_COPY_DEREF(entry);
970
129
  } else {
971
18
    RETURN_FALSE;
972
18
  }
973
147
}
974
975
/* {{{ Advances array argument's internal pointer to the last element and return it */
976
PHP_FUNCTION(end)
977
42
{
978
42
  zval *array_zv;
979
980
126
  ZEND_PARSE_PARAMETERS_START(1, 1)
981
168
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
982
42
  ZEND_PARSE_PARAMETERS_END();
983
984
39
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
985
39
  if (zend_hash_num_elements(array) == 0) {
986
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
987
0
    RETURN_FALSE;
988
0
  }
989
39
  zend_hash_internal_pointer_end(array);
990
991
39
  if (USED_RET()) {
992
17
    php_array_iter_return_current(return_value, array, false);
993
17
  }
994
39
}
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
22
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1007
22
  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
22
  zend_hash_move_backwards(array);
1012
1013
22
  if (USED_RET()) {
1014
12
    php_array_iter_return_current(return_value, array, false);
1015
12
  }
1016
22
}
1017
/* }}} */
1018
1019
/* {{{ Move array argument's internal pointer to the next element and return it */
1020
PHP_FUNCTION(next)
1021
365
{
1022
365
  zval *array_zv;
1023
1024
1.09k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1025
1.45k
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1026
365
  ZEND_PARSE_PARAMETERS_END();
1027
1028
360
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1029
360
  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
360
  zend_hash_move_forward(array);
1034
1035
360
  if (USED_RET()) {
1036
22
    php_array_iter_return_current(return_value, array, true);
1037
22
  }
1038
360
}
1039
/* }}} */
1040
1041
/* {{{ Set array argument's internal pointer to the first element and return it */
1042
PHP_FUNCTION(reset)
1043
602
{
1044
602
  zval *array_zv;
1045
1046
1.80k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1047
2.40k
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1048
602
  ZEND_PARSE_PARAMETERS_END();
1049
1050
601
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1051
601
  if (zend_hash_num_elements(array) == 0) {
1052
    /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
1053
5
    RETURN_FALSE;
1054
5
  }
1055
596
  zend_hash_internal_pointer_reset(array);
1056
1057
596
  if (USED_RET()) {
1058
19
    php_array_iter_return_current(return_value, array, true);
1059
19
  }
1060
596
}
1061
/* }}} */
1062
1063
/* {{{ Return the element currently pointed to by the internal array pointer */
1064
PHP_FUNCTION(current)
1065
81
{
1066
81
  zval *array_zv;
1067
1068
241
  ZEND_PARSE_PARAMETERS_START(1, 1)
1069
316
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1070
81
  ZEND_PARSE_PARAMETERS_END();
1071
1072
77
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1073
77
  php_array_iter_return_current(return_value, array, true);
1074
77
}
1075
/* }}} */
1076
1077
/* {{{ Return the key of the element currently pointed to by the internal array pointer */
1078
PHP_FUNCTION(key)
1079
185
{
1080
185
  zval *array_zv;
1081
1082
554
  ZEND_PARSE_PARAMETERS_START(1, 1)
1083
736
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1084
185
  ZEND_PARSE_PARAMETERS_END();
1085
1086
179
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1087
179
  zval *entry = php_array_iter_seek_current(array, true);
1088
179
  if (EXPECTED(entry)) {
1089
170
    zend_hash_get_current_key_zval(array, return_value);
1090
170
  }
1091
179
}
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
5
{
1106
5
  uint32_t argc;
1107
5
  zval *args = NULL;
1108
1109
15
  ZEND_PARSE_PARAMETERS_START(1, -1)
1110
15
    Z_PARAM_VARIADIC('+', args, argc)
1111
15
  ZEND_PARSE_PARAMETERS_END();
1112
1113
  /* mixed min ( array $values ) */
1114
5
  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
5
  } else {
1128
    /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1129
5
    zval *min;
1130
5
    uint32_t i;
1131
1132
5
    min = &args[0];
1133
5
    zend_long min_lval;
1134
5
    double min_dval;
1135
1136
5
    if (Z_TYPE_P(min) == IS_LONG) {
1137
5
      min_lval = Z_LVAL_P(min);
1138
1139
5
      for (i = 1; i < argc; i++) {
1140
5
        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
5
        } 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
5
        } else {
1150
5
          goto generic_compare;
1151
5
        }
1152
5
      }
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
5
      for (i = 1; i < argc; i++) {
1177
5
        generic_compare:
1178
5
        if (zend_compare(&args[i], min) < 0) {
1179
5
          min = &args[i];
1180
5
        }
1181
5
      }
1182
0
    }
1183
1184
5
    RETURN_COPY(min);
1185
5
  }
1186
5
}
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
33
{
1364
33
  zval args[3],   /* Arguments to userland function */
1365
33
     retval,    /* Return value - unused */
1366
33
     *zv;
1367
33
  HashTable *target_hash = HASH_OF(array);
1368
33
  HashPosition pos;
1369
33
  uint32_t ht_iter;
1370
33
  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
33
  zend_fcall_info fci = context->fci;
1375
1376
33
  if (zend_hash_num_elements(target_hash) == 0) {
1377
0
    return result;
1378
0
  }
1379
1380
  /* Set up known arguments */
1381
33
  ZVAL_UNDEF(&args[1]);
1382
33
  if (userdata) {
1383
0
    ZVAL_COPY_VALUE(&args[2], userdata);
1384
0
  }
1385
1386
33
  fci.retval = &retval;
1387
33
  fci.param_count = userdata ? 3 : 2;
1388
33
  fci.params = args;
1389
1390
33
  zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1391
33
  ht_iter = zend_hash_iterator_add(target_hash, pos);
1392
1393
  /* Iterate through hash */
1394
149
  do {
1395
    /* Retrieve value */
1396
149
    zv = zend_hash_get_current_data_ex(target_hash, &pos);
1397
149
    if (zv == NULL) {
1398
28
      break;
1399
28
    }
1400
1401
    /* Skip undefined indirect elements */
1402
121
    if (Z_TYPE_P(zv) == IS_INDIRECT) {
1403
86
      zv = Z_INDIRECT_P(zv);
1404
86
      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
86
      if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1411
86
        zend_property_info *prop_info =
1412
86
          zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1413
86
        if (prop_info) {
1414
58
          ZVAL_NEW_REF(zv, zv);
1415
58
          ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1416
58
        }
1417
86
      }
1418
86
    }
1419
1420
    /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1421
121
    ZVAL_MAKE_REF(zv);
1422
1423
    /* Retrieve key */
1424
121
    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
121
    zend_hash_move_forward_ex(target_hash, &pos);
1429
1430
    /* Back up hash position, as it may change */
1431
121
    EG(ht_iterators)[ht_iter].pos = pos;
1432
1433
121
    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
121
    } else {
1457
121
      ZVAL_COPY_VALUE(&args[0], zv);
1458
1459
      /* Call the userland function */
1460
121
      result = zend_call_function(&fci, &context->fci_cache);
1461
121
      zval_ptr_dtor(&retval);
1462
121
    }
1463
1464
121
    zval_ptr_dtor_str(&args[1]);
1465
1466
121
    if (result == FAILURE) {
1467
0
      break;
1468
0
    }
1469
1470
    /* Reload array and position -- both may have changed */
1471
121
    if (Z_TYPE_P(array) == IS_ARRAY) {
1472
35
      pos = zend_hash_iterator_pos_ex(ht_iter, array);
1473
35
      target_hash = Z_ARRVAL_P(array);
1474
86
    } else if (Z_TYPE_P(array) == IS_OBJECT) {
1475
86
      target_hash = Z_OBJPROP_P(array);
1476
86
      pos = zend_hash_iterator_pos(ht_iter, target_hash);
1477
86
    } 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
121
  } while (!EG(exception));
1483
1484
33
  zend_hash_iterator_del(ht_iter);
1485
33
  return result;
1486
33
}
1487
1488
/* {{{ Apply a user function to every member of an array */
1489
PHP_FUNCTION(array_walk)
1490
41
{
1491
41
  zval *array;
1492
41
  zval *userdata = NULL;
1493
41
  php_array_walk_context context;
1494
1495
123
  ZEND_PARSE_PARAMETERS_START(2, 3)
1496
164
    Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1497
195
    Z_PARAM_FUNC(context.fci, context.fci_cache)
1498
33
    Z_PARAM_OPTIONAL
1499
66
    Z_PARAM_ZVAL(userdata)
1500
66
  ZEND_PARSE_PARAMETERS_END();
1501
1502
33
  php_array_walk(&context, array, userdata, /* recursive */ false);
1503
33
  RETURN_TRUE;
1504
33
}
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
153
{
1532
153
  zval *entry; /* pointer to array entry */
1533
153
  zend_ulong num_idx;
1534
153
  zend_string *str_idx;
1535
1536
153
  if (strict) {
1537
105
    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
105
    } else {
1553
73.5k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1554
73.5k
        ZVAL_DEREF(entry);
1555
73.5k
        if (fast_is_identical_function(value, entry)) {
1556
57
          if (behavior == 0) {
1557
57
            RETURN_TRUE;
1558
57
          } 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
57
        }
1566
73.5k
      } ZEND_HASH_FOREACH_END();
1567
105
    }
1568
105
  } else {
1569
48
    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
48
    } else if (Z_TYPE_P(value) == IS_STRING) {
1584
15.7k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1585
15.7k
        if (fast_equal_check_string(value, entry)) {
1586
26
          if (behavior == 0) {
1587
26
            RETURN_TRUE;
1588
26
          } 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
26
        }
1596
15.7k
      } ZEND_HASH_FOREACH_END();
1597
35
    } else {
1598
44
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1599
44
        if (fast_equal_check_function(value, entry)) {
1600
13
          if (behavior == 0) {
1601
0
            RETURN_TRUE;
1602
13
          } else {
1603
13
            if (str_idx) {
1604
0
              RETURN_STR_COPY(str_idx);
1605
13
            } else {
1606
13
              RETURN_LONG(num_idx);
1607
13
            }
1608
13
          }
1609
13
        }
1610
44
      } ZEND_HASH_FOREACH_END();
1611
13
    }
1612
48
  }
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
176
{
1624
176
  zval *value,    /* value to check for */
1625
176
     *array;    /* array to check in */
1626
176
  bool strict = 0;  /* strict comparison or not */
1627
1628
512
  ZEND_PARSE_PARAMETERS_START(2, 3)
1629
640
    Z_PARAM_ZVAL(value)
1630
640
    Z_PARAM_ARRAY(array)
1631
153
    Z_PARAM_OPTIONAL
1632
516
    Z_PARAM_BOOL(strict)
1633
176
  ZEND_PARSE_PARAMETERS_END();
1634
1635
153
  _php_search_array(return_value, value, array, strict, behavior);
1636
153
}
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
15
{
1674
15
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1675
15
}
1676
/* }}} */
1677
1678
static zend_always_inline bool php_valid_var_name(const zend_string *var_name) /* {{{ */
1679
17
{
1680
  /* first 256 bits for first character, and second 256 bits for the next */
1681
17
  static const uint32_t charset[8] = {
1682
       /*  31      0   63     32   95     64   127    96 */
1683
17
      0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
1684
17
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1685
17
  static const uint32_t charset2[8] = {
1686
       /*  31      0   63     32   95     64   127    96 */
1687
17
      0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
1688
17
      0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
1689
1690
17
  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
17
  uint32_t ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[0];
1696
17
  if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
1697
0
    return false;
1698
0
  }
1699
1700
  /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1701
17
  if (ZSTR_LEN(var_name) > 1) {
1702
6
    size_t i = 1;
1703
18
    do {
1704
18
      ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[i];
1705
18
      if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
1706
0
        return false;
1707
0
      }
1708
18
    } while (++i < ZSTR_LEN(var_name));
1709
6
  }
1710
17
  return true;
1711
17
}
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
58
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(arr, var_name, entry) {
1871
58
    if (!var_name) {
1872
0
      continue;
1873
0
    }
1874
12
    if (!php_valid_var_name(var_name)) {
1875
0
      continue;
1876
0
    }
1877
12
    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
6
    orig_var = zend_hash_find_known_hash(symbol_table, var_name);
1882
6
    if (orig_var) {
1883
0
      if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
1884
0
        orig_var = Z_INDIRECT_P(orig_var);
1885
0
      }
1886
0
      if (zend_string_equals_literal(var_name, "GLOBALS")) {
1887
0
        continue;
1888
0
      }
1889
0
      ZVAL_DEREF(entry);
1890
0
      ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
1891
0
      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
6
    count++;
1900
6
  } 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
63
{
2451
63
  zval *var_array_param;
2452
63
  zend_long extract_refs;
2453
63
  zend_long extract_type = PHP_EXTR_OVERWRITE;
2454
63
  zend_string *prefix = NULL;
2455
63
  zend_long count;
2456
63
  zend_array *symbol_table;
2457
2458
189
  ZEND_PARSE_PARAMETERS_START(1, 3)
2459
252
    Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
2460
62
    Z_PARAM_OPTIONAL
2461
134
    Z_PARAM_LONG(extract_type)
2462
15
    Z_PARAM_STR(prefix)
2463
63
  ZEND_PARSE_PARAMETERS_END();
2464
2465
62
  extract_refs = (extract_type & PHP_EXTR_REFS);
2466
62
  if (extract_refs) {
2467
0
    SEPARATE_ARRAY(var_array_param);
2468
0
  }
2469
62
  extract_type &= 0xff;
2470
2471
62
  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
62
  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
62
  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
62
  if (zend_forbid_dynamic_call() == FAILURE) {
2489
40
    return;
2490
40
  }
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
38
{
2555
38
  zval *value_ptr, data;
2556
2557
38
  ZVAL_DEREF(entry);
2558
38
  if (Z_TYPE_P(entry) == IS_STRING) {
2559
35
    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
35
    } 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
35
    } else {
2570
35
      php_error_docref_unchecked(NULL, E_WARNING, "Undefined variable $%S", Z_STR_P(entry));
2571
35
    }
2572
35
  } 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
3
  } else {
2587
3
    php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2588
3
    return;
2589
3
  }
2590
38
}
2591
/* }}} */
2592
2593
/* {{{ Creates a hash containing variables and their values */
2594
PHP_FUNCTION(compact)
2595
43
{
2596
43
  zval *args = NULL; /* function arguments array */
2597
43
  uint32_t num_args, i;
2598
43
  zend_array *symbol_table;
2599
2600
129
  ZEND_PARSE_PARAMETERS_START(1, -1)
2601
129
    Z_PARAM_VARIADIC('+', args, num_args)
2602
129
  ZEND_PARSE_PARAMETERS_END();
2603
2604
43
  if (zend_forbid_dynamic_call() == FAILURE) {
2605
8
    return;
2606
8
  }
2607
2608
35
  symbol_table = zend_rebuild_symbol_table();
2609
35
  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
35
  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
35
  } else {
2617
35
    array_init_size(return_value, num_args);
2618
35
  }
2619
2620
73
  for (i = 0; i < num_args; i++) {
2621
38
    php_compact_var(symbol_table, return_value, &args[i], i + 1);
2622
38
  }
2623
35
}
2624
/* }}} */
2625
2626
/* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2627
PHP_FUNCTION(array_fill)
2628
95
{
2629
95
  zval *val;
2630
95
  zend_long start_key, num;
2631
2632
285
  ZEND_PARSE_PARAMETERS_START(3, 3)
2633
380
    Z_PARAM_LONG(start_key)
2634
475
    Z_PARAM_LONG(num)
2635
475
    Z_PARAM_ZVAL(val)
2636
475
  ZEND_PARSE_PARAMETERS_END();
2637
2638
95
  if (EXPECTED(num > 0)) {
2639
88
    if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2640
0
      zend_argument_value_error(2, "is too large");
2641
0
      RETURN_THROWS();
2642
88
    } 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
88
    } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2646
      /* create packed array */
2647
88
      zval *zv;
2648
2649
88
      array_init_size(return_value, (uint32_t)(start_key + num));
2650
88
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2651
88
      Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2652
88
      Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2653
88
      Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2654
2655
88
      if (Z_REFCOUNTED_P(val)) {
2656
14
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2657
14
      }
2658
2659
88
      zv = Z_ARRVAL_P(return_value)->arPacked;
2660
2661
122
      while (start_key--) {
2662
34
        ZVAL_UNDEF(zv);
2663
34
        zv++;
2664
34
      }
2665
791k
      while (num--) {
2666
791k
        ZVAL_COPY_VALUE(zv, val);
2667
791k
        zv++;
2668
791k
      }
2669
88
    } 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
88
  } 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
95
}
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
22
{
2694
22
  zval *keys, *val, *entry;
2695
2696
66
  ZEND_PARSE_PARAMETERS_START(2, 2)
2697
88
    Z_PARAM_ARRAY(keys)
2698
80
    Z_PARAM_ZVAL(val)
2699
80
  ZEND_PARSE_PARAMETERS_END();
2700
2701
  /* Initialize return array */
2702
16
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2703
2704
162
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2705
162
    ZVAL_DEREF(entry);
2706
162
    if (Z_TYPE_P(entry) == IS_LONG) {
2707
26
      zend_hash_index_add(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2708
47
    } else {
2709
47
      zend_string *tmp_key;
2710
47
      zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2711
47
      zend_symtable_add(Z_ARRVAL_P(return_value), key, val);
2712
47
      zend_tmp_string_release(tmp_key);
2713
47
    }
2714
162
  } ZEND_HASH_FOREACH_END();
2715
2716
16
  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
16
}
2720
/* }}} */
2721
2722
2
#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end, _step) do { \
2723
2
    double __calc_size = ((start - end) / (_step)) + 1; \
2724
2
    if (__calc_size >= (double)HT_MAX_SIZE) { \
2725
2
      double __exceed_by = __calc_size - (double)HT_MAX_SIZE; \
2726
2
      zend_value_error(\
2727
2
        "The supplied range exceeds the maximum array size by %.1f elements: " \
2728
2
        "start=%.1f, end=%.1f, step=%.1f. Max size: %.0f", \
2729
2
        __exceed_by, end, start, (_step), (double)HT_MAX_SIZE); \
2730
2
      RETURN_THROWS(); \
2731
2
    } \
2732
2
    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
417
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2738
417
    zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2739
417
    if (__calc_size >= HT_MAX_SIZE - 1) { \
2740
0
      uint64_t __excess = __calc_size - (HT_MAX_SIZE - 1); \
2741
0
      zend_value_error(\
2742
0
        "The supplied range exceeds the maximum array size by %" PRIu64 " elements: " \
2743
0
        "start=" ZEND_LONG_FMT ", end=" ZEND_LONG_FMT ", step=" ZEND_LONG_FMT ". " \
2744
0
        "Calculated size: %" PRIu64 ". Maximum size: %" PRIu64 ".", \
2745
0
        __excess, end, start, (_step), (uint64_t)__calc_size, (uint64_t)HT_MAX_SIZE); \
2746
0
      RETURN_THROWS(); \
2747
0
    } \
2748
417
    size = (uint32_t)(__calc_size + 1); \
2749
417
    array_init_size(return_value, size); \
2750
417
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2751
417
  } 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
882
{
2761
882
  switch (Z_TYPE_P(input)) {
2762
842
    case IS_LONG:
2763
842
      *lval = Z_LVAL_P(input);
2764
842
      *dval = (double) Z_LVAL_P(input);
2765
842
      return IS_LONG;
2766
2
    case IS_DOUBLE:
2767
2
      *dval = Z_DVAL_P(input);
2768
2
      check_dval_value:
2769
2
      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
2
      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
2
      return IS_DOUBLE;
2778
38
    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
38
      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
38
      uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2798
38
      if (type == IS_DOUBLE) {
2799
0
        goto check_dval_value;
2800
0
      }
2801
38
      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
38
      if (Z_STRLEN_P(input) != 1) {
2810
0
        const char *arg_name = get_active_function_arg_name(arg_num);
2811
0
        php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
2812
0
        if (UNEXPECTED(EG(exception))) {
2813
0
          return 0;
2814
0
        }
2815
0
      }
2816
      /* Set fall back values to 0 in case the other argument is not a string */
2817
38
      *lval = 0;
2818
38
      *dval = 0.0;
2819
38
      return IS_STRING;
2820
38
    }
2821
0
    EMPTY_SWITCH_DEFAULT_CASE();
2822
882
  }
2823
882
}
2824
2825
/* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2826
PHP_FUNCTION(range)
2827
441
{
2828
441
  zval *user_start, *user_end, *user_step = NULL, tmp;
2829
441
  bool is_step_double = false;
2830
441
  bool is_step_negative = false;
2831
441
  double step_double = 1.0;
2832
441
  zend_long step = 1;
2833
2834
1.32k
  ZEND_PARSE_PARAMETERS_START(2, 3)
2835
1.76k
    Z_PARAM_NUMBER_OR_STR(user_start)
2836
2.20k
    Z_PARAM_NUMBER_OR_STR(user_end)
2837
441
    Z_PARAM_OPTIONAL
2838
920
    Z_PARAM_NUMBER(user_step)
2839
441
  ZEND_PARSE_PARAMETERS_END();
2840
2841
441
  if (user_step) {
2842
19
    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
19
    } else {
2864
19
      step = Z_LVAL_P(user_step);
2865
      /* We only want positive step values. */
2866
19
      if (step < 0) {
2867
0
        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
0
        is_step_negative = true;
2872
0
        step *= -1;
2873
0
      }
2874
19
      step_double = (double) step;
2875
19
    }
2876
19
    if (step_double == 0.0) {
2877
0
      zend_argument_value_error(3, "cannot be 0");
2878
0
      RETURN_THROWS();
2879
0
    }
2880
19
  }
2881
2882
441
  uint8_t start_type;
2883
441
  double start_double;
2884
441
  zend_long start_long;
2885
441
  uint8_t end_type;
2886
441
  double end_double;
2887
441
  zend_long end_long;
2888
2889
441
  start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
2890
441
  if (start_type == 0) {
2891
0
    RETURN_THROWS();
2892
0
  }
2893
441
  end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
2894
441
  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
441
  if (start_type >= IS_STRING || end_type >= IS_STRING) {
2900
    /* If one of the inputs is NOT a string nor single-byte string */
2901
19
    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
19
    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
19
    int low = (unsigned char) Z_STRVAL_P(user_start)[0];
2937
19
    int high = (unsigned char) Z_STRVAL_P(user_end)[0];
2938
2939
    /* Decreasing char range */
2940
19
    if (low > high) {
2941
3
      if (low - high < step) {
2942
0
        goto boundary_error;
2943
0
      }
2944
      /* Initialize the return_value as an array. */
2945
3
      array_init_size(return_value, (uint32_t)(((low - high) / step) + 1));
2946
3
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2947
3
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2948
165
        for (; low >= high; low -= step) {
2949
162
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2950
162
          ZEND_HASH_FILL_NEXT();
2951
162
        }
2952
3
      } ZEND_HASH_FILL_END();
2953
16
    } else if (high > low) { /* Increasing char range */
2954
16
      if (is_step_negative) {
2955
0
        goto negative_step_error;
2956
0
      }
2957
16
      if (high - low < step) {
2958
0
        goto boundary_error;
2959
0
      }
2960
16
      array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
2961
16
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2962
16
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
2963
432
        for (; low <= high; low += step) {
2964
416
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
2965
416
          ZEND_HASH_FILL_NEXT();
2966
416
        }
2967
16
      } ZEND_HASH_FILL_END();
2968
16
    } 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
19
    return;
2974
19
  }
2975
2976
422
  handle_numeric_inputs:
2977
422
  if (start_type == IS_DOUBLE || end_type == IS_DOUBLE || is_step_double) {
2978
2
    double element;
2979
2
    uint32_t i, size;
2980
2981
    /* Decreasing float range */
2982
2
    if (start_double > end_double) {
2983
0
      if (start_double - end_double < step_double) {
2984
0
        goto boundary_error;
2985
0
      }
2986
2987
0
      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
420
  } else {
3017
420
    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
420
    zend_ulong unsigned_step= (zend_ulong)step;
3020
420
    uint32_t i, size;
3021
3022
    /* Decreasing int range */
3023
420
    if (start_long > end_long) {
3024
1
      if ((zend_ulong)start_long - end_long < unsigned_step) {
3025
0
        goto boundary_error;
3026
0
      }
3027
3028
1
      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
419
    } else if (end_long > start_long) { /* Increasing int range */
3037
416
      if (is_step_negative) {
3038
0
        goto negative_step_error;
3039
0
      }
3040
416
      if ((zend_ulong)end_long - start_long < unsigned_step) {
3041
0
        goto boundary_error;
3042
0
      }
3043
3044
416
      RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3045
3046
416
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3047
546k
        for (i = 0; i < size; ++i) {
3048
545k
          ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3049
545k
          ZEND_HASH_FILL_NEXT();
3050
545k
        }
3051
416
      } ZEND_HASH_FILL_END();
3052
416
    } 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
420
  }
3058
420
  return;
3059
3060
420
negative_step_error:
3061
0
  zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3062
0
  RETURN_THROWS();
3063
3064
0
boundary_error:
3065
0
  zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
3066
0
  RETURN_THROWS();
3067
0
}
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
830
{
3184
830
  HashTable    out_hash;      /* Output hashtable */
3185
830
  zend_long  num_in;      /* Number of entries in the input hashtable */
3186
830
  zend_long  pos;       /* Current position in the hashtable */
3187
830
  uint32_t     idx;
3188
830
  zval    *entry;       /* Hash entry */
3189
830
  uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3190
3191
830
  GC_ADDREF(in_hash);
3192
830
  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
830
  num_in = zend_hash_num_elements(in_hash);
3196
3197
  /* Clamp the offset.. */
3198
830
  if (offset > num_in) {
3199
22
    offset = num_in;
3200
808
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3201
0
    offset = 0;
3202
0
  }
3203
3204
  /* ..and the length */
3205
830
  if (length < 0) {
3206
6
    length = num_in - offset + length;
3207
824
  } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3208
46
    length = num_in - offset;
3209
46
  }
3210
3211
  /* Create and initialize output hash */
3212
830
  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
830
  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
769
    entry = in_hash->arPacked;
3217
970
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3218
201
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3219
3220
201
      zend_hash_next_index_insert_new(&out_hash, entry);
3221
201
      if (idx == iter_pos) {
3222
34
        if ((zend_long)idx != pos) {
3223
0
          zend_hash_iterators_update(in_hash, idx, pos);
3224
0
        }
3225
34
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3226
34
      }
3227
201
      pos++;
3228
201
    }
3229
3230
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3231
769
    if (removed != NULL) {
3232
721
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3233
357
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3234
357
        pos++;
3235
357
        Z_TRY_ADDREF_P(entry);
3236
357
        zend_hash_next_index_insert_new(removed, entry);
3237
357
        zend_hash_packed_del_val(in_hash, entry);
3238
        /* Bump iterator positions to the element after replacement. */
3239
357
        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
357
      }
3244
405
    } else { /* otherwise just skip those entries */
3245
405
      zend_long pos2 = pos;
3246
3247
1.21k
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3248
812
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3249
812
        pos2++;
3250
812
        zend_hash_packed_del_val(in_hash, entry);
3251
        /* Bump iterator positions to the element after replacement. */
3252
812
        if (idx == iter_pos) {
3253
44
          zend_hash_iterators_update(in_hash, idx, offset + length);
3254
44
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3255
44
        }
3256
812
      }
3257
405
    }
3258
3259
    /* If there are entries to insert.. */
3260
769
    if (replace) {
3261
295
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3262
295
        Z_TRY_ADDREF_P(entry);
3263
295
        zend_hash_next_index_insert_new(&out_hash, entry);
3264
295
        pos++;
3265
295
      } ZEND_HASH_FOREACH_END();
3266
63
    }
3267
3268
    /* Copy the remaining input hash entries to the output hash */
3269
769
    entry = in_hash->arPacked + idx;
3270
946
    for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3271
177
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3272
177
      zend_hash_next_index_insert_new(&out_hash, entry);
3273
177
      if (idx == iter_pos) {
3274
83
        if ((zend_long)idx != pos) {
3275
72
          zend_hash_iterators_update(in_hash, idx, pos);
3276
72
        }
3277
83
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3278
83
      }
3279
177
      pos++;
3280
177
    }
3281
769
  } else {
3282
61
    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
61
    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
61
    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
61
    } else { /* otherwise just skip those entries */
3319
61
      zend_long pos2 = pos;
3320
3321
61
      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
61
    }
3327
61
    iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
3328
3329
    /* If there are entries to insert.. */
3330
61
    if (replace) {
3331
229
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3332
229
        Z_TRY_ADDREF_P(entry);
3333
229
        zend_hash_next_index_insert_new(&out_hash, entry);
3334
229
        pos++;
3335
229
      } ZEND_HASH_FOREACH_END();
3336
55
    }
3337
3338
    /* Copy the remaining input hash entries to the output hash */
3339
61
    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
61
  }
3356
3357
  /* replace HashTable data */
3358
830
  HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3359
830
  HT_SET_ITERATORS_COUNT(in_hash, 0);
3360
830
  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
830
  in_hash->nInternalPointer = 0;
3367
3368
830
  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
830
  zend_hash_destroy(in_hash);
3377
3378
830
  HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
3379
830
  in_hash->nTableSize        = out_hash.nTableSize;
3380
830
  in_hash->nTableMask        = out_hash.nTableMask;
3381
830
  in_hash->nNumUsed          = out_hash.nNumUsed;
3382
830
  in_hash->nNumOfElements    = out_hash.nNumOfElements;
3383
830
  in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
3384
830
  in_hash->arData            = out_hash.arData;
3385
830
  in_hash->pDestructor       = out_hash.pDestructor;
3386
830
}
3387
/* }}} */
3388
3389
/* {{{ Pushes elements onto the end of the array */
3390
PHP_FUNCTION(array_push)
3391
209
{
3392
209
  zval   *args,   /* Function arguments array */
3393
209
       *stack;    /* Input array */
3394
209
  uint32_t argc;    /* Number of function arguments */
3395
3396
3397
627
  ZEND_PARSE_PARAMETERS_START(1, -1)
3398
836
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3399
208
    Z_PARAM_VARIADIC('+', args, argc)
3400
209
  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
416
  for (uint32_t i = 0; i < argc; i++) {
3404
208
    Z_TRY_ADDREF(args[i]);
3405
3406
208
    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
208
  }
3412
3413
  /* Clean up and return the number of values in the stack */
3414
208
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3415
208
}
3416
/* }}} */
3417
3418
/* {{{ Pops an element off the end of the array */
3419
PHP_FUNCTION(array_pop)
3420
245
{
3421
245
  zval *stack,  /* Input stack */
3422
245
     *val;    /* Value to be popped */
3423
245
  uint32_t idx;
3424
3425
735
  ZEND_PARSE_PARAMETERS_START(1, 1)
3426
980
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3427
245
  ZEND_PARSE_PARAMETERS_END();
3428
3429
240
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3430
0
    return;
3431
0
  }
3432
3433
240
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3434
    /* Get the last value and copy it into the return value */
3435
74
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3436
74
    while (1) {
3437
74
      if (idx == 0) {
3438
0
        return;
3439
0
      }
3440
74
      idx--;
3441
74
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3442
74
      if (Z_TYPE_P(val) != IS_UNDEF) {
3443
74
        break;
3444
74
      }
3445
74
    }
3446
74
    RETVAL_COPY_VALUE(val);
3447
74
    ZVAL_UNDEF(val);
3448
3449
74
    if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3450
74
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3451
74
    }
3452
3453
    /* Delete the last value */
3454
74
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3455
166
  } else {
3456
166
    Bucket *p;
3457
3458
    /* Get the last value and copy it into the return value */
3459
166
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3460
166
    while (1) {
3461
166
      if (idx == 0) {
3462
0
        return;
3463
0
      }
3464
166
      idx--;
3465
166
      p = Z_ARRVAL_P(stack)->arData + idx;
3466
166
      val = &p->val;
3467
166
      if (Z_TYPE_P(val) != IS_UNDEF) {
3468
166
        break;
3469
166
      }
3470
166
    }
3471
166
    RETVAL_COPY_VALUE(val);
3472
166
    ZVAL_UNDEF(val);
3473
3474
166
    if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3475
166
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3476
166
    }
3477
3478
    /* Delete the last value */
3479
166
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3480
166
  }
3481
240
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3482
3483
240
  if (Z_ISREF_P(return_value)) {
3484
0
    zend_unwrap_reference(return_value);
3485
0
  }
3486
240
}
3487
/* }}} */
3488
3489
/* {{{ Pops an element off the beginning of the array */
3490
PHP_FUNCTION(array_shift)
3491
212
{
3492
212
  zval *stack,  /* Input stack */
3493
212
     *val;    /* Value to be popped */
3494
212
  uint32_t idx;
3495
3496
636
  ZEND_PARSE_PARAMETERS_START(1, 1)
3497
848
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3498
212
  ZEND_PARSE_PARAMETERS_END();
3499
3500
207
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3501
7
    return;
3502
7
  }
3503
3504
  /* re-index like it did before */
3505
200
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3506
200
    uint32_t k = 0;
3507
3508
    /* Get the first value and copy it into the return value */
3509
200
    idx = 0;
3510
200
    while (1) {
3511
200
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3512
0
        return;
3513
0
      }
3514
200
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3515
200
      if (Z_TYPE_P(val) != IS_UNDEF) {
3516
200
        break;
3517
200
      }
3518
0
      idx++;
3519
0
    }
3520
200
    RETVAL_COPY_VALUE(val);
3521
200
    ZVAL_UNDEF(val);
3522
3523
    /* Delete the first value */
3524
200
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3525
3526
200
    if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3527
386
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3528
210
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3529
210
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3530
123
        if (idx != k) {
3531
123
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3532
123
          ZVAL_COPY_VALUE(q, val);
3533
123
          ZVAL_UNDEF(val);
3534
123
        }
3535
123
        k++;
3536
123
      }
3537
176
    } else {
3538
24
      uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
3539
3540
78
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3541
54
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3542
54
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3543
36
        if (idx != k) {
3544
36
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3545
36
          ZVAL_COPY_VALUE(q, val);
3546
36
          ZVAL_UNDEF(val);
3547
36
          if (idx == iter_pos) {
3548
18
            zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
3549
18
            iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
3550
18
          }
3551
36
        }
3552
36
        k++;
3553
36
      }
3554
24
    }
3555
200
    Z_ARRVAL_P(stack)->nNumUsed = k;
3556
200
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3557
200
  } 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
200
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3600
3601
200
  if (Z_ISREF_P(return_value)) {
3602
24
    zend_unwrap_reference(return_value);
3603
24
  }
3604
200
}
3605
/* }}} */
3606
3607
/* {{{ Pushes elements onto the beginning of the array */
3608
PHP_FUNCTION(array_unshift)
3609
18
{
3610
18
  zval   *args,     /* Function arguments array */
3611
18
       *stack;      /* Input stack */
3612
18
  HashTable new_hash;   /* New hashtable for the stack */
3613
18
  uint32_t argc;      /* Number of function arguments */
3614
18
  zend_string *key;
3615
18
  zval *value;
3616
3617
54
  ZEND_PARSE_PARAMETERS_START(1, -1)
3618
72
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3619
13
    Z_PARAM_VARIADIC('+', args, argc)
3620
18
  ZEND_PARSE_PARAMETERS_END();
3621
3622
13
  zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3623
3624
13
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3625
7
    zend_hash_real_init_packed(&new_hash);
3626
3627
7
    ZEND_HASH_FILL_PACKED(&new_hash) {
3628
58
      for (uint32_t i = 0; i < argc; i++) {
3629
51
        Z_TRY_ADDREF(args[i]);
3630
51
        ZEND_HASH_FILL_ADD(&args[i]);
3631
51
      }
3632
3633
56
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(stack), value) {
3634
56
        ZEND_HASH_FILL_ADD(value);
3635
56
      } ZEND_HASH_FOREACH_END();
3636
7
    } ZEND_HASH_FILL_END();
3637
7
  } else {
3638
12
    for (uint32_t i = 0; i < argc; i++) {
3639
6
      Z_TRY_ADDREF(args[i]);
3640
6
      zend_hash_next_index_insert_new(&new_hash, &args[i]);
3641
6
    }
3642
3643
12
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3644
12
      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
12
    } ZEND_HASH_FOREACH_END();
3650
6
  }
3651
3652
13
  if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3653
7
    zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3654
7
    HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3655
7
    HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3656
7
  }
3657
3658
  /* replace HashTable data */
3659
13
  Z_ARRVAL_P(stack)->pDestructor = NULL;
3660
13
  zend_hash_destroy(Z_ARRVAL_P(stack));
3661
3662
13
  HT_FLAGS(Z_ARRVAL_P(stack))          = HT_FLAGS(&new_hash);
3663
13
  Z_ARRVAL_P(stack)->nTableSize        = new_hash.nTableSize;
3664
13
  Z_ARRVAL_P(stack)->nTableMask        = new_hash.nTableMask;
3665
13
  Z_ARRVAL_P(stack)->nNumUsed          = new_hash.nNumUsed;
3666
13
  Z_ARRVAL_P(stack)->nNumOfElements    = new_hash.nNumOfElements;
3667
13
  Z_ARRVAL_P(stack)->nNextFreeElement  = new_hash.nNextFreeElement;
3668
13
  Z_ARRVAL_P(stack)->arData            = new_hash.arData;
3669
13
  Z_ARRVAL_P(stack)->pDestructor       = new_hash.pDestructor;
3670
3671
13
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3672
3673
  /* Clean up and return the number of elements in the stack */
3674
13
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3675
13
}
3676
/* }}} */
3677
3678
/* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3679
PHP_FUNCTION(array_splice)
3680
840
{
3681
840
  zval *array,        /* Input array */
3682
840
     *repl_array = NULL; /* Replacement array */
3683
840
  HashTable  *rem_hash = NULL;
3684
840
  zend_long offset,
3685
840
      length = 0;
3686
840
  bool length_is_null = 1;
3687
840
  int   num_in;       /* Number of elements in the input array */
3688
3689
2.51k
  ZEND_PARSE_PARAMETERS_START(2, 4)
3690
3.35k
    Z_PARAM_ARRAY_EX(array, 0, 1)
3691
4.16k
    Z_PARAM_LONG(offset)
3692
830
    Z_PARAM_OPTIONAL
3693
2.59k
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3694
1.63k
    Z_PARAM_ZVAL(repl_array)
3695
1.63k
  ZEND_PARSE_PARAMETERS_END();
3696
3697
830
  num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3698
3699
830
  if (length_is_null) {
3700
364
    length = num_in;
3701
364
  }
3702
3703
830
  if (ZEND_NUM_ARGS() == 4) {
3704
    /* Make sure the last argument, if passed, is an array */
3705
118
    convert_to_array(repl_array);
3706
118
  }
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
830
  if (USED_RET()) {
3711
364
    zend_long size = length;
3712
3713
    /* Clamp the offset.. */
3714
364
    if (offset > num_in) {
3715
4
      offset = num_in;
3716
360
    } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3717
1
      offset = 0;
3718
1
    }
3719
3720
    /* ..and the length */
3721
364
    if (length < 0) {
3722
0
      size = num_in - offset + length;
3723
364
    } 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
364
    array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3729
364
    rem_hash = Z_ARRVAL_P(return_value);
3730
466
  } else {
3731
    /* The return value will not be used, but make sure it still has the correct type. */
3732
466
    RETVAL_EMPTY_ARRAY();
3733
466
  }
3734
3735
  /* Perform splice */
3736
830
  php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3737
830
}
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
22
{
3744
22
  zend_long pos;
3745
22
  Bucket *bucket;
3746
22
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3747
22
  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
22
    return ht->arData + offset;
3750
22
  }
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
167
{
3770
167
  zend_long pos;
3771
167
  zval *zv;
3772
167
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3773
167
  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
167
    return ht->arPacked + offset;
3776
167
  }
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
210
{
3795
210
  zval *input;                  /* Input array */
3796
210
  zval *entry;                  /* An array entry */
3797
210
  zend_long offset;             /* Offset to get elements from */
3798
210
  zend_long length = 0;         /* How many elements to get */
3799
210
  bool length_is_null = 1; /* Whether an explicit length has been omitted */
3800
210
  bool preserve_keys = 0;  /* Whether to preserve keys while copying to the new array */
3801
210
  uint32_t num_in;              /* Number of elements in the input array */
3802
210
  zend_string *string_key;
3803
210
  zend_ulong num_key;
3804
3805
630
  ZEND_PARSE_PARAMETERS_START(2, 4)
3806
840
    Z_PARAM_ARRAY(input)
3807
1.00k
    Z_PARAM_LONG(offset)
3808
201
    Z_PARAM_OPTIONAL
3809
794
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3810
632
    Z_PARAM_BOOL(preserve_keys)
3811
210
  ZEND_PARSE_PARAMETERS_END();
3812
3813
  /* Get number of entries in the input hash */
3814
201
  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
201
  if (length_is_null) {
3818
41
    length = num_in;
3819
41
  }
3820
3821
  /* Clamp the offset.. */
3822
201
  if (offset > (zend_long) num_in) {
3823
2
    RETURN_EMPTY_ARRAY();
3824
199
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3825
0
    offset = 0;
3826
0
  }
3827
3828
  /* ..and the length */
3829
199
  if (length < 0) {
3830
0
    length = num_in - offset + length;
3831
199
  } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3832
25
    length = num_in - offset;
3833
25
  }
3834
3835
199
  if (length <= 0) {
3836
10
    RETURN_EMPTY_ARRAY();
3837
10
  }
3838
3839
  /* Initialize returned array */
3840
189
  array_init_size(return_value, (uint32_t)length);
3841
3842
  // Contains modified variants of ZEND_HASH_FOREACH_VAL
3843
189
  {
3844
189
    HashTable *ht = Z_ARRVAL_P(input);
3845
3846
    /* Start at the beginning and go until we hit offset */
3847
189
    if (HT_IS_PACKED(ht)) {
3848
167
      zval *zv = find_packed_val_at_offset(ht, offset);
3849
167
      zval *end = ht->arPacked + ht->nNumUsed;
3850
3851
167
      if (!preserve_keys
3852
167
       || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3853
167
        zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3854
167
        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3855
356
          for (; zv != end; zv++) {
3856
199
            if (__fill_idx >= length) {
3857
10
              break;
3858
10
            }
3859
189
            if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3860
0
              continue;
3861
0
            }
3862
189
            entry = zv;
3863
189
            if (UNEXPECTED(Z_ISREF_P(entry)) &&
3864
139
              UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3865
78
              entry = Z_REFVAL_P(entry);
3866
78
            }
3867
189
            Z_TRY_ADDREF_P(entry);
3868
189
            ZEND_HASH_FILL_ADD(entry);
3869
189
          }
3870
167
        } ZEND_HASH_FILL_END();
3871
167
      } 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
167
    } else {
3888
22
      zend_long n = 0;  /* Current number of elements */
3889
22
      Bucket *p = find_bucket_at_offset(ht, offset);
3890
22
      Bucket *end = ht->arData + ht->nNumUsed;
3891
3892
56
      for (; p != end; p++) {
3893
34
        entry = &p->val;
3894
34
        if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3895
0
          continue;
3896
0
        }
3897
34
        if (n >= length) {
3898
0
          break;
3899
0
        }
3900
34
        n++;
3901
34
        num_key = p->h;
3902
34
        string_key = p->key;
3903
3904
34
        if (string_key) {
3905
22
          entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
3906
22
        } else {
3907
12
          if (preserve_keys) {
3908
12
            entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
3909
12
          } else {
3910
0
            entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
3911
0
          }
3912
12
        }
3913
34
        zval_add_ref(entry);
3914
34
      }
3915
22
    }
3916
189
  }
3917
189
}
3918
/* }}} */
3919
3920
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
3921
31
{
3922
31
  zval *src_entry, *dest_entry;
3923
31
  zend_string *string_key;
3924
3925
93
  ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
3926
93
    if (string_key) {
3927
20
      if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
3928
20
        zval *src_zval = src_entry;
3929
20
        zval *dest_zval = dest_entry;
3930
20
        HashTable *thash;
3931
20
        zval tmp;
3932
20
        int ret;
3933
3934
20
        ZVAL_DEREF(src_zval);
3935
20
        ZVAL_DEREF(dest_zval);
3936
20
        thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
3937
20
        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
20
        ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
3943
20
        dest_zval = dest_entry;
3944
3945
20
        if (Z_TYPE_P(dest_zval) == IS_NULL) {
3946
0
          convert_to_array(dest_zval);
3947
0
          add_next_index_null(dest_zval);
3948
20
        } else {
3949
20
          convert_to_array(dest_zval);
3950
20
        }
3951
20
        SEPARATE_ZVAL(dest_zval);
3952
3953
20
        ZVAL_UNDEF(&tmp);
3954
20
        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
20
        if (Z_TYPE_P(src_zval) == IS_ARRAY) {
3960
11
          if (thash) {
3961
11
            GC_TRY_PROTECT_RECURSION(thash);
3962
11
          }
3963
11
          ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
3964
11
          if (thash) {
3965
11
            GC_TRY_UNPROTECT_RECURSION(thash);
3966
11
          }
3967
11
          if (!ret) {
3968
11
            return 0;
3969
11
          }
3970
11
        } 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
0
      } else {
3981
0
        zval *zv = zend_hash_add_new(dest, string_key, src_entry);
3982
0
        zval_add_ref(zv);
3983
0
      }
3984
20
    } else {
3985
11
      zval *zv = zend_hash_next_index_insert(dest, src_entry);
3986
11
      if (UNEXPECTED(!zv)) {
3987
11
        zend_cannot_add_element();
3988
11
        return 0;
3989
11
      }
3990
0
      zval_add_ref(zv);
3991
0
    }
3992
93
  } ZEND_HASH_FOREACH_END();
3993
0
  return 1;
3994
31
}
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
39
{
4154
39
  zval *args = NULL;
4155
39
  zval *arg;
4156
39
  uint32_t argc, i;
4157
39
  zval *src_entry;
4158
39
  HashTable *src, *dest;
4159
39
  uint32_t count = 0;
4160
4161
117
  ZEND_PARSE_PARAMETERS_START(0, -1)
4162
117
    Z_PARAM_VARIADIC('+', args, argc)
4163
117
  ZEND_PARSE_PARAMETERS_END();
4164
4165
34
  if (argc == 0) {
4166
0
    RETURN_EMPTY_ARRAY();
4167
0
  }
4168
4169
96
  for (i = 0; i < argc; i++) {
4170
64
    zval *arg = args + i;
4171
4172
64
    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
62
    count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4177
62
  }
4178
4179
32
  if (argc == 2) {
4180
30
    zval *ret = NULL;
4181
4182
30
    if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4183
10
      ret = &args[1];
4184
20
    } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4185
0
      ret = &args[0];
4186
0
    }
4187
30
    if (ret) {
4188
10
      if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4189
10
        if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4190
10
          RETURN_COPY(ret);
4191
10
        }
4192
10
      } else {
4193
0
        bool copy = true;
4194
0
        zend_string *string_key;
4195
4196
0
        ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
4197
0
          if (!string_key) {
4198
0
            copy = false;
4199
0
            break;
4200
0
          }
4201
0
        } ZEND_HASH_FOREACH_END();
4202
0
        if (copy) {
4203
0
          RETURN_COPY(ret);
4204
0
        }
4205
0
      }
4206
10
    }
4207
30
  }
4208
4209
22
  arg = args;
4210
22
  src  = Z_ARRVAL_P(arg);
4211
  /* copy first array if necessary */
4212
22
  bool in_place = false;
4213
22
  if (HT_IS_PACKED(src)) {
4214
    /* Note: If it has holes, it might get sequentialized */
4215
0
    if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) {
4216
0
      dest = src;
4217
0
      in_place = true;
4218
0
      RETVAL_ARR(dest);
4219
0
    } else {
4220
0
      array_init_size(return_value, count);
4221
0
      dest = Z_ARRVAL_P(return_value);
4222
4223
0
      zend_hash_real_init_packed(dest);
4224
0
      ZEND_HASH_FILL_PACKED(dest) {
4225
0
        ZEND_HASH_PACKED_FOREACH_VAL(src, src_entry) {
4226
0
          if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4227
0
            Z_REFCOUNT_P(src_entry) == 1)) {
4228
0
            src_entry = Z_REFVAL_P(src_entry);
4229
0
          }
4230
0
          Z_TRY_ADDREF_P(src_entry);
4231
0
          ZEND_HASH_FILL_ADD(src_entry);
4232
0
        } ZEND_HASH_FOREACH_END();
4233
0
      } ZEND_HASH_FILL_END();
4234
0
    }
4235
22
  } else {
4236
22
    array_init_size(return_value, count);
4237
22
    dest = Z_ARRVAL_P(return_value);
4238
4239
22
    zend_string *string_key;
4240
22
    zend_hash_real_init_mixed(dest);
4241
92
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4242
92
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4243
92
        Z_REFCOUNT_P(src_entry) == 1)) {
4244
0
        src_entry = Z_REFVAL_P(src_entry);
4245
0
      }
4246
92
      Z_TRY_ADDREF_P(src_entry);
4247
92
      if (EXPECTED(string_key)) {
4248
22
        _zend_hash_append(dest, string_key, src_entry);
4249
22
      } else {
4250
2
        zend_hash_next_index_insert_new(dest, src_entry);
4251
2
      }
4252
92
    } ZEND_HASH_FOREACH_END();
4253
22
  }
4254
22
  if (recursive) {
4255
42
    for (i = 1; i < argc; i++) {
4256
20
      arg = args + i;
4257
20
      php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4258
20
    }
4259
22
  } else {
4260
0
    for (i = 1; i < argc; i++) {
4261
0
      arg = args + i;
4262
0
      php_array_merge(dest, Z_ARRVAL_P(arg));
4263
0
    }
4264
0
  }
4265
4266
22
  if (in_place) {
4267
0
    GC_ADDREF(dest);
4268
0
  }
4269
22
}
4270
/* }}} */
4271
4272
/* {{{ Merges elements from passed arrays into one array */
4273
PHP_FUNCTION(array_merge)
4274
17
{
4275
17
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4276
17
}
4277
/* }}} */
4278
4279
/* {{{ Recursively merges elements from passed arrays into one array */
4280
PHP_FUNCTION(array_merge_recursive)
4281
22
{
4282
22
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4283
22
}
4284
/* }}} */
4285
4286
/* {{{ Replaces elements from passed arrays into one array */
4287
PHP_FUNCTION(array_replace)
4288
0
{
4289
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4290
0
}
4291
/* }}} */
4292
4293
/* {{{ Recursively replaces elements from passed arrays into one array */
4294
PHP_FUNCTION(array_replace_recursive)
4295
0
{
4296
0
  php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4297
0
}
4298
/* }}} */
4299
4300
/* {{{ Return just the keys from the input array, optionally only for the specified search_value */
4301
PHP_FUNCTION(array_keys)
4302
20
{
4303
20
  zval *input,        /* Input array */
4304
20
       *search_value = NULL, /* Value to search for */
4305
20
       *entry,        /* An entry in the input array */
4306
20
         new_val;       /* New value */
4307
20
  bool strict = 0;    /* do strict comparison */
4308
20
  zend_ulong num_idx;
4309
20
  zend_string *str_idx;
4310
20
  zend_array *arrval;
4311
20
  zend_ulong elem_count;
4312
4313
60
  ZEND_PARSE_PARAMETERS_START(1, 3)
4314
80
    Z_PARAM_ARRAY(input)
4315
20
    Z_PARAM_OPTIONAL
4316
50
    Z_PARAM_ZVAL(search_value)
4317
50
    Z_PARAM_BOOL(strict)
4318
20
  ZEND_PARSE_PARAMETERS_END();
4319
20
  arrval = Z_ARRVAL_P(input);
4320
20
  elem_count = zend_hash_num_elements(arrval);
4321
4322
  /* Base case: empty input */
4323
20
  if (!elem_count) {
4324
0
    RETURN_COPY(input);
4325
0
  }
4326
4327
  /* Initialize return array */
4328
20
  if (search_value != NULL) {
4329
5
    array_init(return_value);
4330
4331
5
    if (strict) {
4332
25
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4333
25
        ZVAL_DEREF(entry);
4334
25
        if (fast_is_identical_function(search_value, entry)) {
4335
5
          if (str_idx) {
4336
0
            ZVAL_STR_COPY(&new_val, str_idx);
4337
5
          } else {
4338
5
            ZVAL_LONG(&new_val, num_idx);
4339
5
          }
4340
5
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4341
5
        }
4342
25
      } ZEND_HASH_FOREACH_END();
4343
5
    } else {
4344
0
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4345
0
        if (fast_equal_check_function(search_value, entry)) {
4346
0
          if (str_idx) {
4347
0
            ZVAL_STR_COPY(&new_val, str_idx);
4348
0
          } else {
4349
0
            ZVAL_LONG(&new_val, num_idx);
4350
0
          }
4351
0
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4352
0
        }
4353
0
      } ZEND_HASH_FOREACH_END();
4354
0
    }
4355
15
  } else {
4356
15
    array_init_size(return_value, elem_count);
4357
15
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4358
15
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4359
15
      if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4360
        /* Optimistic case: range(0..n-1) for vector-like packed array */
4361
8
        zend_ulong lval = 0;
4362
4363
36
        for (; lval < elem_count; ++lval) {
4364
28
          ZEND_HASH_FILL_SET_LONG(lval);
4365
28
          ZEND_HASH_FILL_NEXT();
4366
28
        }
4367
8
      } else {
4368
        /* Go through input array and add keys to the return array */
4369
30
        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4370
30
          if (str_idx) {
4371
9
            ZEND_HASH_FILL_SET_STR_COPY(str_idx);
4372
9
          } else {
4373
0
            ZEND_HASH_FILL_SET_LONG(num_idx);
4374
0
          }
4375
30
          ZEND_HASH_FILL_NEXT();
4376
30
        } ZEND_HASH_FOREACH_END();
4377
7
      }
4378
15
    } ZEND_HASH_FILL_END();
4379
15
  }
4380
20
}
4381
/* }}} */
4382
4383
/* {{{ Get the key of the first element of the array */
4384
PHP_FUNCTION(array_key_first)
4385
0
{
4386
0
  zval *stack;    /* Input stack */
4387
4388
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4389
0
    Z_PARAM_ARRAY(stack)
4390
0
  ZEND_PARSE_PARAMETERS_END();
4391
4392
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4393
0
  HashPosition pos = 0;
4394
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4395
0
}
4396
/* }}} */
4397
4398
/* {{{ Get the key of the last element of the array */
4399
PHP_FUNCTION(array_key_last)
4400
0
{
4401
0
  zval *stack;    /* Input stack */
4402
0
  HashPosition pos;
4403
4404
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4405
0
    Z_PARAM_ARRAY(stack)
4406
0
  ZEND_PARSE_PARAMETERS_END();
4407
4408
0
  HashTable *target_hash = Z_ARRVAL_P (stack);
4409
0
  zend_hash_internal_pointer_end_ex(target_hash, &pos);
4410
0
  zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
4411
0
}
4412
/* }}} */
4413
4414
PHP_FUNCTION(array_first)
4415
0
{
4416
0
  HashTable *array;
4417
4418
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4419
0
    Z_PARAM_ARRAY_HT(array)
4420
0
  ZEND_PARSE_PARAMETERS_END();
4421
4422
0
  ZEND_HASH_FOREACH_VAL(array, zval *zv) {
4423
0
    RETURN_COPY_DEREF(zv);
4424
0
  } ZEND_HASH_FOREACH_END();
4425
0
}
4426
4427
PHP_FUNCTION(array_last)
4428
0
{
4429
0
  HashTable *array;
4430
4431
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4432
0
    Z_PARAM_ARRAY_HT(array)
4433
0
  ZEND_PARSE_PARAMETERS_END();
4434
4435
0
  ZEND_HASH_REVERSE_FOREACH_VAL(array, zval *zv) {
4436
0
    RETURN_COPY_DEREF(zv);
4437
0
  } ZEND_HASH_FOREACH_END();
4438
0
}
4439
4440
/* {{{ Return just the values from the input array */
4441
PHP_FUNCTION(array_values)
4442
12
{
4443
12
  zval   *input;    /* Input array */
4444
12
  zend_array *arrval;
4445
12
  zend_long arrlen;
4446
4447
36
  ZEND_PARSE_PARAMETERS_START(1, 1)
4448
48
    Z_PARAM_ARRAY(input)
4449
12
  ZEND_PARSE_PARAMETERS_END();
4450
4451
9
  arrval = Z_ARRVAL_P(input);
4452
4453
  /* Return empty input as is */
4454
9
  arrlen = zend_hash_num_elements(arrval);
4455
9
  if (!arrlen) {
4456
0
    RETURN_EMPTY_ARRAY();
4457
0
  }
4458
4459
  /* Return vector-like packed arrays as-is */
4460
9
  if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
4461
9
    arrval->nNextFreeElement == arrlen) {
4462
9
    RETURN_COPY(input);
4463
9
  }
4464
4465
0
  RETURN_ARR(zend_array_to_list(arrval));
4466
0
}
4467
/* }}} */
4468
4469
/* {{{ Return the value as key and the frequency of that value in input as value */
4470
PHP_FUNCTION(array_count_values)
4471
0
{
4472
0
  zval  *input,   /* Input array */
4473
0
      *entry,   /* An entry in the input array */
4474
0
      *tmp;
4475
0
  HashTable *myht;
4476
4477
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4478
0
    Z_PARAM_ARRAY(input)
4479
0
  ZEND_PARSE_PARAMETERS_END();
4480
4481
  /* Initialize return array */
4482
0
  array_init(return_value);
4483
4484
  /* Go through input array and add values to the return array */
4485
0
  myht = Z_ARRVAL_P(input);
4486
0
  ZEND_HASH_FOREACH_VAL(myht, entry) {
4487
0
    ZVAL_DEREF(entry);
4488
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4489
0
      if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
4490
0
        zval data;
4491
0
        ZVAL_LONG(&data, 1);
4492
0
        zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4493
0
      } else {
4494
0
        Z_LVAL_P(tmp)++;
4495
0
      }
4496
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4497
0
      if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
4498
0
        zval data;
4499
0
        ZVAL_LONG(&data, 1);
4500
0
        zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4501
0
      } else {
4502
0
        Z_LVAL_P(tmp)++;
4503
0
      }
4504
0
    } else {
4505
0
      php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
4506
0
    }
4507
0
  } ZEND_HASH_FOREACH_END();
4508
0
}
4509
/* }}} */
4510
4511
static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, void **cache_slot, zval *rv) /* {{{ */
4512
0
{
4513
0
  zval *prop = NULL;
4514
4515
0
  if (Z_TYPE_P(data) == IS_OBJECT) {
4516
0
    zend_string *tmp_str;
4517
    /* If name is an integer convert integer to string */
4518
0
    if (name_str == NULL) {
4519
0
      tmp_str = zend_long_to_str(name_long);
4520
0
    } else {
4521
0
      tmp_str = zend_string_copy(name_str);
4522
0
    }
4523
    /* The has_property check is first performed in "exists" mode (which returns true for
4524
     * properties that are null but exist) and then in "has" mode to handle objects that
4525
     * implement __isset (which is not called in "exists" mode). */
4526
0
    if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, cache_slot)
4527
0
        || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, cache_slot)) {
4528
0
      prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, cache_slot, rv);
4529
0
      if (prop) {
4530
0
        ZVAL_DEREF(prop);
4531
0
        if (prop != rv) {
4532
0
          Z_TRY_ADDREF_P(prop);
4533
0
        }
4534
0
      }
4535
0
    }
4536
0
    zend_string_release(tmp_str);
4537
0
  } else if (Z_TYPE_P(data) == IS_ARRAY) {
4538
    /* Name is a string */
4539
0
    if (name_str != NULL) {
4540
0
      prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
4541
0
    } else {
4542
0
      prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
4543
0
    }
4544
0
    if (prop) {
4545
0
      ZVAL_DEREF(prop);
4546
0
      Z_TRY_ADDREF_P(prop);
4547
0
    }
4548
0
  }
4549
4550
0
  return prop;
4551
0
}
4552
/* }}} */
4553
4554
/* {{{ Return the values from a single column in the input array, identified by the
4555
   value_key and optionally indexed by the index_key */
4556
PHP_FUNCTION(array_column)
4557
0
{
4558
0
  HashTable *input;
4559
0
  zval *colval, *data, rv;
4560
0
  zend_string *column_str = NULL;
4561
0
  zend_long column_long = 0;
4562
0
  bool column_is_null = 0;
4563
0
  zend_string *index_str = NULL;
4564
0
  zend_long index_long = 0;
4565
0
  bool index_is_null = 1;
4566
4567
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
4568
0
    Z_PARAM_ARRAY_HT(input)
4569
0
    Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
4570
0
    Z_PARAM_OPTIONAL
4571
0
    Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
4572
0
  ZEND_PARSE_PARAMETERS_END();
4573
4574
0
  void* cache_slot_column[3] = { NULL, NULL, NULL };
4575
0
  void* cache_slot_index[3] = { NULL, NULL, NULL };
4576
4577
0
  array_init_size(return_value, zend_hash_num_elements(input));
4578
  /* Index param is not passed */
4579
0
  if (index_is_null) {
4580
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4581
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4582
0
      ZEND_HASH_FOREACH_VAL(input, data) {
4583
0
        ZVAL_DEREF(data);
4584
0
        if (column_is_null) {
4585
0
          Z_TRY_ADDREF_P(data);
4586
0
          colval = data;
4587
0
        } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4588
0
          continue;
4589
0
        }
4590
0
        ZEND_HASH_FILL_ADD(colval);
4591
0
      } ZEND_HASH_FOREACH_END();
4592
0
    } ZEND_HASH_FILL_END();
4593
0
  } else {
4594
0
    ZEND_HASH_FOREACH_VAL(input, data) {
4595
0
      ZVAL_DEREF(data);
4596
4597
0
      if (column_is_null) {
4598
0
        Z_TRY_ADDREF_P(data);
4599
0
        colval = data;
4600
0
      } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) {
4601
0
        continue;
4602
0
      }
4603
4604
0
      zval rv;
4605
0
      zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv);
4606
0
      if (keyval) {
4607
0
        array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
4608
0
        zval_ptr_dtor(colval);
4609
0
        zval_ptr_dtor(keyval);
4610
0
      } else {
4611
0
        zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
4612
0
      }
4613
0
    } ZEND_HASH_FOREACH_END();
4614
0
  }
4615
0
}
4616
/* }}} */
4617
4618
/* {{{ Return input as a new array with the order of the entries reversed */
4619
PHP_FUNCTION(array_reverse)
4620
41
{
4621
41
  zval   *input,        /* Input array */
4622
41
       *entry;        /* An entry in the input array */
4623
41
  zend_string *string_key;
4624
41
  zend_ulong    num_key;
4625
41
  bool preserve_keys = 0; /* whether to preserve keys */
4626
4627
123
  ZEND_PARSE_PARAMETERS_START(1, 2)
4628
164
    Z_PARAM_ARRAY(input)
4629
38
    Z_PARAM_OPTIONAL
4630
76
    Z_PARAM_BOOL(preserve_keys)
4631
41
  ZEND_PARSE_PARAMETERS_END();
4632
4633
  /* Initialize return array */
4634
38
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4635
38
  if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4636
38
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4637
38
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4638
1.23k
      ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4639
1.23k
        if (UNEXPECTED(Z_ISREF_P(entry) &&
4640
1.23k
          Z_REFCOUNT_P(entry) == 1)) {
4641
18
          entry = Z_REFVAL_P(entry);
4642
18
        }
4643
1.23k
        Z_TRY_ADDREF_P(entry);
4644
1.23k
        ZEND_HASH_FILL_ADD(entry);
4645
1.23k
      } ZEND_HASH_FOREACH_END();
4646
38
    } ZEND_HASH_FILL_END();
4647
38
  } else {
4648
0
    ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
4649
0
      if (string_key) {
4650
0
        entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4651
0
      } else {
4652
0
        if (preserve_keys) {
4653
0
          entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4654
0
        } else {
4655
0
          entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4656
0
        }
4657
0
      }
4658
0
      zval_add_ref(entry);
4659
0
    } ZEND_HASH_FOREACH_END();
4660
0
  }
4661
38
}
4662
/* }}} */
4663
4664
/* {{{ Returns a copy of input array padded with pad_value to size pad_size */
4665
PHP_FUNCTION(array_pad)
4666
0
{
4667
0
  zval  *input;   /* Input array */
4668
0
  zval  *pad_value; /* Padding value obviously */
4669
0
  zend_long pad_size;   /* Size to pad to */
4670
0
  zend_long pad_size_abs; /* Absolute value of pad_size */
4671
0
  zend_long input_size;   /* Size of the input array */
4672
0
  zend_long num_pads;   /* How many pads do we need */
4673
0
  zend_long i;
4674
0
  zend_string *key;
4675
0
  zval *value;
4676
4677
0
  ZEND_PARSE_PARAMETERS_START(3, 3)
4678
0
    Z_PARAM_ARRAY(input)
4679
0
    Z_PARAM_LONG(pad_size)
4680
0
    Z_PARAM_ZVAL(pad_value)
4681
0
  ZEND_PARSE_PARAMETERS_END();
4682
4683
0
  if (pad_size < Z_L(-HT_MAX_SIZE) || pad_size > Z_L(HT_MAX_SIZE)) {
4684
0
    zend_argument_value_error(2, "must not exceed the maximum allowed array size");
4685
0
    RETURN_THROWS();
4686
0
  }
4687
4688
  /* Do some initial calculations */
4689
0
  input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
4690
0
  pad_size_abs = ZEND_ABS(pad_size);
4691
4692
0
  if (input_size >= pad_size_abs) {
4693
    /* Copy the original array */
4694
0
    ZVAL_COPY(return_value, input);
4695
0
    return;
4696
0
  }
4697
4698
0
  num_pads = pad_size_abs - input_size;
4699
0
  if (Z_REFCOUNTED_P(pad_value)) {
4700
0
    GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
4701
0
  }
4702
4703
0
  array_init_size(return_value, pad_size_abs);
4704
0
  if (HT_IS_PACKED(Z_ARRVAL_P(input))) {
4705
0
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4706
4707
0
    if (pad_size < 0) {
4708
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4709
0
        for (i = 0; i < num_pads; i++) {
4710
0
          ZEND_HASH_FILL_ADD(pad_value);
4711
0
        }
4712
0
      } ZEND_HASH_FILL_END();
4713
0
    }
4714
4715
0
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4716
0
      ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(input), value) {
4717
0
        Z_TRY_ADDREF_P(value);
4718
0
        ZEND_HASH_FILL_ADD(value);
4719
0
      } ZEND_HASH_FOREACH_END();
4720
0
    } ZEND_HASH_FILL_END();
4721
4722
0
    if (pad_size > 0) {
4723
0
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4724
0
        for (i = 0; i < num_pads; i++) {
4725
0
          ZEND_HASH_FILL_ADD(pad_value);
4726
0
        }
4727
0
      } ZEND_HASH_FILL_END();
4728
0
    }
4729
0
  } else {
4730
0
    if (pad_size < 0) {
4731
0
      for (i = 0; i < num_pads; i++) {
4732
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4733
0
      }
4734
0
    }
4735
4736
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(input), key, value) {
4737
0
      Z_TRY_ADDREF_P(value);
4738
0
      if (key) {
4739
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
4740
0
      } else {
4741
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
4742
0
      }
4743
0
    } ZEND_HASH_FOREACH_END();
4744
4745
0
    if (pad_size > 0) {
4746
0
      for (i = 0; i < num_pads; i++) {
4747
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
4748
0
      }
4749
0
    }
4750
0
  }
4751
0
}
4752
/* }}} */
4753
4754
/* {{{ Return array with key <-> value flipped */
4755
PHP_FUNCTION(array_flip)
4756
0
{
4757
0
  zval *array, *entry, data;
4758
0
  zend_ulong num_idx;
4759
0
  zend_string *str_idx;
4760
4761
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
4762
0
    Z_PARAM_ARRAY(array)
4763
0
  ZEND_PARSE_PARAMETERS_END();
4764
4765
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4766
4767
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
4768
0
    ZVAL_DEREF(entry);
4769
0
    if (Z_TYPE_P(entry) == IS_LONG) {
4770
0
      if (str_idx) {
4771
0
        ZVAL_STR_COPY(&data, str_idx);
4772
0
      } else {
4773
0
        ZVAL_LONG(&data, num_idx);
4774
0
      }
4775
0
      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
4776
0
    } else if (Z_TYPE_P(entry) == IS_STRING) {
4777
0
      if (str_idx) {
4778
0
        ZVAL_STR_COPY(&data, str_idx);
4779
0
      } else {
4780
0
        ZVAL_LONG(&data, num_idx);
4781
0
      }
4782
0
      zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
4783
0
    } else {
4784
0
      php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
4785
0
    }
4786
0
  } ZEND_HASH_FOREACH_END();
4787
0
}
4788
/* }}} */
4789
4790
/* {{{ Returns an array with all string keys lowercased [or uppercased] */
4791
PHP_FUNCTION(array_change_key_case)
4792
0
{
4793
0
  zval *array, *entry;
4794
0
  zend_string *string_key;
4795
0
  zend_string *new_key;
4796
0
  zend_ulong num_key;
4797
0
  zend_long change_to_upper=0;
4798
4799
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
4800
0
    Z_PARAM_ARRAY(array)
4801
0
    Z_PARAM_OPTIONAL
4802
0
    Z_PARAM_LONG(change_to_upper)
4803
0
  ZEND_PARSE_PARAMETERS_END();
4804
4805
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
4806
4807
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
4808
0
    if (!string_key) {
4809
0
      entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
4810
0
    } else {
4811
0
      if (change_to_upper) {
4812
0
        new_key = zend_string_toupper(string_key);
4813
0
      } else {
4814
0
        new_key = zend_string_tolower(string_key);
4815
0
      }
4816
0
      entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
4817
0
      zend_string_release_ex(new_key, 0);
4818
0
    }
4819
4820
0
    zval_add_ref(entry);
4821
0
  } ZEND_HASH_FOREACH_END();
4822
0
}
4823
/* }}} */
4824
4825
struct bucketindex {
4826
  Bucket b;
4827
  unsigned int i;
4828
};
4829
4830
static void array_bucketindex_swap(void *p, void *q)
4831
437
{
4832
437
  struct bucketindex *f = (struct bucketindex *)p;
4833
437
  struct bucketindex *g = (struct bucketindex *)q;
4834
437
  struct bucketindex t;
4835
437
  t = *f;
4836
437
  *f = *g;
4837
437
  *g = t;
4838
437
}
4839
4840
/* {{{ Removes duplicate values from array */
4841
PHP_FUNCTION(array_unique)
4842
39
{
4843
39
  zval *array;
4844
39
  Bucket *p;
4845
39
  zend_long sort_type = PHP_SORT_STRING;
4846
39
  bucket_compare_func_t cmp;
4847
39
  struct bucketindex *arTmp, *cmpdata, *lastkept;
4848
39
  uint32_t i, idx;
4849
4850
117
  ZEND_PARSE_PARAMETERS_START(1, 2)
4851
156
    Z_PARAM_ARRAY(array)
4852
38
    Z_PARAM_OPTIONAL
4853
116
    Z_PARAM_LONG(sort_type)
4854
39
  ZEND_PARSE_PARAMETERS_END();
4855
4856
38
  if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4857
0
    ZVAL_COPY(return_value, array);
4858
0
    return;
4859
0
  }
4860
4861
38
  if (sort_type == PHP_SORT_STRING) {
4862
18
    HashTable seen;
4863
18
    zend_long num_key;
4864
18
    zend_string *str_key;
4865
18
    zval *val;
4866
4867
18
    zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
4868
18
    array_init(return_value);
4869
4870
218
    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, str_key, val) {
4871
218
      zval *retval;
4872
218
      if (Z_TYPE_P(val) == IS_STRING) {
4873
28
        retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
4874
72
      } else {
4875
72
        zend_string *tmp_str_val;
4876
72
        zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
4877
72
        retval = zend_hash_add_empty_element(&seen, str_val);
4878
72
        zend_tmp_string_release(tmp_str_val);
4879
72
      }
4880
4881
218
      if (retval) {
4882
        /* First occurrence of the value */
4883
38
        if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
4884
0
          ZVAL_DEREF(val);
4885
0
        }
4886
38
        Z_TRY_ADDREF_P(val);
4887
4888
38
        if (str_key) {
4889
38
          zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
4890
38
        } else {
4891
0
          zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
4892
0
        }
4893
38
      }
4894
218
    } ZEND_HASH_FOREACH_END();
4895
4896
18
    zend_hash_destroy(&seen);
4897
18
    return;
4898
18
  }
4899
4900
20
  cmp = php_get_data_compare_func_unstable(sort_type, false);
4901
4902
20
  bool in_place = zend_may_modify_arg_in_place(array);
4903
20
  if (in_place) {
4904
0
    RETVAL_ARR(Z_ARRVAL_P(array));
4905
20
  } else {
4906
20
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
4907
20
  }
4908
4909
  /* create and sort array with pointers to the target_hash buckets */
4910
20
  arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4911
20
  if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
4912
20
    zval *zv = Z_ARRVAL_P(array)->arPacked;
4913
202
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
4914
182
      if (Z_TYPE_P(zv) == IS_UNDEF) continue;
4915
182
      ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
4916
182
      arTmp[i].b.h = idx;
4917
182
      arTmp[i].b.key = NULL;
4918
182
      arTmp[i].i = i;
4919
182
      i++;
4920
182
    }
4921
20
  } else {
4922
0
    p = Z_ARRVAL_P(array)->arData;
4923
0
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, p++) {
4924
0
      if (Z_TYPE(p->val) == IS_UNDEF) continue;
4925
0
      arTmp[i].b = *p;
4926
0
      arTmp[i].i = i;
4927
0
      i++;
4928
0
    }
4929
0
  }
4930
20
  ZVAL_UNDEF(&arTmp[i].b.val);
4931
20
  zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
4932
20
      (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
4933
4934
20
  if (UNEXPECTED(EG(exception))) {
4935
0
    goto out;
4936
0
  }
4937
4938
  /* go through the sorted array and delete duplicates from the copy */
4939
20
  lastkept = arTmp;
4940
182
  for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
4941
162
    if (cmp(&lastkept->b, &cmpdata->b)) {
4942
133
      lastkept = cmpdata;
4943
133
    } else {
4944
29
      if (lastkept->i > cmpdata->i) {
4945
0
        p = &lastkept->b;
4946
0
        lastkept = cmpdata;
4947
29
      } else {
4948
29
        p = &cmpdata->b;
4949
29
      }
4950
29
      if (p->key == NULL) {
4951
29
        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
4952
29
      } else {
4953
0
        zend_hash_del(Z_ARRVAL_P(return_value), p->key);
4954
0
      }
4955
29
    }
4956
162
  }
4957
4958
20
out:
4959
20
  pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
4960
4961
20
  if (in_place) {
4962
0
    Z_ADDREF_P(return_value);
4963
0
  }
4964
20
}
4965
/* }}} */
4966
4967
static int zval_compare(zval *first, zval *second) /* {{{ */
4968
0
{
4969
0
  return string_compare_function(first, second);
4970
0
}
4971
/* }}} */
4972
4973
static int zval_user_compare(zval *a, zval *b) /* {{{ */
4974
0
{
4975
0
  zval args[2];
4976
0
  zval retval;
4977
4978
0
  ZVAL_COPY_VALUE(&args[0], a);
4979
0
  ZVAL_COPY_VALUE(&args[1], b);
4980
4981
0
  BG(user_compare_fci).param_count = 2;
4982
0
  BG(user_compare_fci).params = args;
4983
0
  BG(user_compare_fci).retval = &retval;
4984
4985
0
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
4986
0
  zend_long ret = php_get_long(&retval);
4987
0
  return ZEND_NORMALIZE_BOOL(ret);
4988
0
}
4989
/* }}} */
4990
4991
static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
4992
0
{
4993
0
  uint32_t argc, i;
4994
0
  zval *args;
4995
0
  int (*intersect_data_compare_func)(zval *, zval *) = NULL;
4996
0
  bool ok;
4997
0
  zval *val, *data;
4998
0
  char *param_spec;
4999
0
  zend_string *key;
5000
0
  zend_ulong h;
5001
5002
  /* Get the argument count */
5003
0
  argc = ZEND_NUM_ARGS();
5004
0
  if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5005
    /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
5006
0
    param_spec = "+f";
5007
0
    intersect_data_compare_func = zval_user_compare;
5008
0
  } else {
5009
    /*  INTERSECT_COMP_DATA_NONE - array_intersect_key()
5010
      INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
5011
0
    param_spec = "+";
5012
5013
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5014
0
      intersect_data_compare_func = zval_compare;
5015
0
    }
5016
0
  }
5017
5018
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5019
0
    RETURN_THROWS();
5020
0
  }
5021
5022
0
  for (i = 0; i < argc; i++) {
5023
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5024
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5025
0
      RETURN_THROWS();
5026
0
    }
5027
0
  }
5028
5029
0
  array_init(return_value);
5030
5031
  /* Iterate over keys of the first array, to compute keys that are in all of the other arrays. */
5032
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5033
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5034
0
      val = Z_REFVAL_P(val);
5035
0
    }
5036
0
    if (key == NULL) {
5037
0
      ok = true;
5038
0
      for (i = 1; i < argc; i++) {
5039
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
5040
0
          (intersect_data_compare_func &&
5041
0
          intersect_data_compare_func(val, data) != 0)
5042
0
        ) {
5043
0
          ok = false;
5044
0
          break;
5045
0
        }
5046
0
      }
5047
0
      if (ok) {
5048
0
        Z_TRY_ADDREF_P(val);
5049
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5050
0
      }
5051
0
    } else {
5052
0
      ok = true;
5053
0
      for (i = 1; i < argc; i++) {
5054
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) == NULL ||
5055
0
          (intersect_data_compare_func &&
5056
0
          intersect_data_compare_func(val, data) != 0)
5057
0
        ) {
5058
0
          ok = false;
5059
0
          break;
5060
0
        }
5061
0
      }
5062
0
      if (ok) {
5063
0
        Z_TRY_ADDREF_P(val);
5064
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5065
0
      }
5066
0
    }
5067
0
  } ZEND_HASH_FOREACH_END();
5068
0
}
5069
/* }}} */
5070
5071
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5072
0
{
5073
0
  zval *args = NULL;
5074
0
  HashTable *hash;
5075
0
  uint32_t arr_argc, i;
5076
0
  int c = 0;
5077
0
  uint32_t idx;
5078
0
  Bucket **lists, *list, **ptrs, *p;
5079
0
  char *param_spec;
5080
0
  zend_fcall_info fci1, fci2;
5081
0
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5082
0
  zend_fcall_info *fci_key = NULL, *fci_data;
5083
0
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5084
0
  PHP_ARRAY_CMP_FUNC_VARS;
5085
0
  bool in_place = false;
5086
5087
0
  bucket_compare_func_t intersect_key_compare_func;
5088
0
  bucket_compare_func_t intersect_data_compare_func;
5089
5090
0
  if (behavior == INTERSECT_NORMAL) {
5091
0
    intersect_key_compare_func = php_array_key_compare_string;
5092
5093
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
5094
      /* array_intersect() */
5095
0
      param_spec = "+";
5096
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5097
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5098
      /* array_uintersect() */
5099
0
      param_spec = "+f";
5100
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5101
0
    } else {
5102
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5103
0
      return;
5104
0
    }
5105
5106
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5107
0
      RETURN_THROWS();
5108
0
    }
5109
0
    fci_data = &fci1;
5110
0
    fci_data_cache = &fci1_cache;
5111
5112
0
  } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5113
    /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
5114
     * no comparison of the data is done (part of INTERSECT_ASSOC) */
5115
5116
0
    if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5117
      /* array_intersect_assoc() or array_intersect_key() */
5118
0
      param_spec = "+";
5119
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5120
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5121
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
5122
      /* array_uintersect_assoc() */
5123
0
      param_spec = "+f";
5124
0
      intersect_key_compare_func = php_array_key_compare_string_unstable;
5125
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5126
0
      fci_data = &fci1;
5127
0
      fci_data_cache = &fci1_cache;
5128
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
5129
      /* array_intersect_uassoc() or array_intersect_ukey() */
5130
0
      param_spec = "+f";
5131
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5132
0
      intersect_data_compare_func = php_array_data_compare_string_unstable;
5133
0
      fci_key = &fci1;
5134
0
      fci_key_cache = &fci1_cache;
5135
0
    } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
5136
      /* array_uintersect_uassoc() */
5137
0
      param_spec = "+ff";
5138
0
      intersect_key_compare_func = php_array_user_key_compare_unstable;
5139
0
      intersect_data_compare_func = php_array_user_compare_unstable;
5140
0
      fci_data = &fci1;
5141
0
      fci_data_cache = &fci1_cache;
5142
0
      fci_key = &fci2;
5143
0
      fci_key_cache = &fci2_cache;
5144
0
    } else {
5145
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5146
0
      return;
5147
0
    }
5148
5149
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5150
0
      RETURN_THROWS();
5151
0
    }
5152
5153
0
  } else {
5154
0
    ZEND_ASSERT(0 && "Invalid behavior");
5155
0
    return;
5156
0
  }
5157
5158
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
5159
5160
  /* for each argument, create and sort list with pointers to the hash buckets */
5161
0
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5162
0
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5163
5164
0
  if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
5165
0
    BG(user_compare_fci) = *fci_data;
5166
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5167
0
  } else if ((behavior & INTERSECT_ASSOC) && key_compare_type == INTERSECT_COMP_KEY_USER) {
5168
0
    BG(user_compare_fci) = *fci_key;
5169
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5170
0
  }
5171
5172
0
  for (i = 0; i < arr_argc; i++) {
5173
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5174
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5175
0
      arr_argc = i; /* only free up to i - 1 */
5176
0
      goto out;
5177
0
    }
5178
0
    hash = Z_ARRVAL(args[i]);
5179
0
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5180
0
    lists[i] = list;
5181
0
    ptrs[i] = list;
5182
0
    if (HT_IS_PACKED(hash)) {
5183
0
      zval *zv = hash->arPacked;
5184
0
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5185
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5186
0
        ZVAL_COPY_VALUE(&list->val, zv);
5187
0
        list->h = idx;
5188
0
        list->key = NULL;
5189
0
        list++;
5190
0
      }
5191
0
    } else {
5192
0
      p = hash->arData;
5193
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5194
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5195
0
        *list++ = *p;
5196
0
      }
5197
0
    }
5198
0
    ZVAL_UNDEF(&list->val);
5199
0
    if (hash->nNumOfElements > 1) {
5200
0
      if (behavior == INTERSECT_NORMAL) {
5201
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5202
0
            sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
5203
0
            (swap_func_t)zend_hash_bucket_swap);
5204
0
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5205
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5206
0
            sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
5207
0
            (swap_func_t)zend_hash_bucket_swap);
5208
0
      }
5209
0
    }
5210
0
  }
5211
5212
  /* copy the argument array if necessary */
5213
0
  in_place = zend_may_modify_arg_in_place(&args[0]);
5214
0
  if (in_place) {
5215
0
    RETVAL_ARR(Z_ARRVAL_P(&args[0]));
5216
0
  } else {
5217
0
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(&args[0])));
5218
0
  }
5219
5220
  /* go through the lists and look for common values */
5221
0
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5222
0
    if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
5223
0
      && key_compare_type == INTERSECT_COMP_KEY_USER) {
5224
0
      BG(user_compare_fci) = *fci_key;
5225
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5226
0
    }
5227
5228
0
    for (i = 1; i < arr_argc; i++) {
5229
0
      if (behavior & INTERSECT_NORMAL) {
5230
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
5231
0
          ptrs[i]++;
5232
0
        }
5233
0
      } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5234
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
5235
0
          ptrs[i]++;
5236
0
        }
5237
0
        if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
5238
          /* this means that ptrs[i] is not NULL so we can compare
5239
           * and "c==0" is from last operation
5240
           * in this branch of code we enter only when INTERSECT_ASSOC
5241
           * since when we have INTERSECT_KEY compare of data is not wanted. */
5242
0
          if (data_compare_type == INTERSECT_COMP_DATA_USER) {
5243
0
            BG(user_compare_fci) = *fci_data;
5244
0
            BG(user_compare_fci_cache) = *fci_data_cache;
5245
0
          }
5246
0
          if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
5247
0
            c = 1;
5248
0
            if (key_compare_type == INTERSECT_COMP_KEY_USER) {
5249
0
              BG(user_compare_fci) = *fci_key;
5250
0
              BG(user_compare_fci_cache) = *fci_key_cache;
5251
              /* When KEY_USER, the last parameter is always the callback */
5252
0
            }
5253
            /* we are going to the break */
5254
0
          } else {
5255
            /* continue looping */
5256
0
          }
5257
0
        }
5258
0
      }
5259
0
      if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
5260
        /* delete any values corresponding to remains of ptrs[0] */
5261
        /* and exit because they do not present in at least one of */
5262
        /* the other arguments */
5263
0
        for (;;) {
5264
0
          p = ptrs[0]++;
5265
0
          if (Z_TYPE(p->val) == IS_UNDEF) {
5266
0
            goto out;
5267
0
          }
5268
0
          if (p->key == NULL) {
5269
0
            zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5270
0
          } else {
5271
0
            zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5272
0
          }
5273
0
        }
5274
0
      }
5275
0
      if (c) /* here we get if not all are equal */
5276
0
        break;
5277
0
      ptrs[i]++;
5278
0
    }
5279
0
    if (c) {
5280
      /* Value of ptrs[0] not in all arguments, delete all entries */
5281
      /* with value < value of ptrs[i] */
5282
0
      for (;;) {
5283
0
        p = ptrs[0];
5284
0
        if (p->key == NULL) {
5285
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5286
0
        } else {
5287
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5288
0
        }
5289
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5290
0
          goto out;
5291
0
        }
5292
0
        if (behavior == INTERSECT_NORMAL) {
5293
0
          if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
5294
0
            break;
5295
0
          }
5296
0
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5297
          /* no need of looping because indexes are unique */
5298
0
          break;
5299
0
        }
5300
0
      }
5301
0
    } else {
5302
      /* ptrs[0] is present in all the arguments */
5303
      /* Skip all entries with same value as ptrs[0] */
5304
0
      for (;;) {
5305
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5306
0
          goto out;
5307
0
        }
5308
0
        if (behavior == INTERSECT_NORMAL) {
5309
0
          if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5310
0
            break;
5311
0
          }
5312
0
        } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
5313
          /* no need of looping because indexes are unique */
5314
0
          break;
5315
0
        }
5316
0
      }
5317
0
    }
5318
0
  }
5319
0
out:
5320
0
  for (i = 0; i < arr_argc; i++) {
5321
0
    hash = Z_ARRVAL(args[i]);
5322
0
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5323
0
  }
5324
5325
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
5326
5327
0
  efree(ptrs);
5328
0
  efree(lists);
5329
5330
0
  if (in_place) {
5331
0
    Z_ADDREF_P(return_value);
5332
0
  }
5333
0
}
5334
/* }}} */
5335
5336
/* {{{ 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. */
5337
PHP_FUNCTION(array_intersect_key)
5338
0
{
5339
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
5340
0
}
5341
/* }}} */
5342
5343
/* {{{ 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. */
5344
PHP_FUNCTION(array_intersect_ukey)
5345
0
{
5346
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5347
0
}
5348
/* }}} */
5349
5350
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments */
5351
PHP_FUNCTION(array_intersect)
5352
0
{
5353
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
5354
0
}
5355
/* }}} */
5356
5357
/* {{{ 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. */
5358
PHP_FUNCTION(array_uintersect)
5359
0
{
5360
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
5361
0
}
5362
/* }}} */
5363
5364
/* {{{ Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
5365
PHP_FUNCTION(array_intersect_assoc)
5366
0
{
5367
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
5368
0
}
5369
/* }}} */
5370
5371
/* {{{ 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. */
5372
PHP_FUNCTION(array_intersect_uassoc)
5373
0
{
5374
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
5375
0
}
5376
/* }}} */
5377
5378
/* {{{ 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. */
5379
PHP_FUNCTION(array_uintersect_assoc)
5380
0
{
5381
0
  php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
5382
0
}
5383
/* }}} */
5384
5385
/* {{{ 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. */
5386
PHP_FUNCTION(array_uintersect_uassoc)
5387
0
{
5388
0
  php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
5389
0
}
5390
/* }}} */
5391
5392
static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
5393
5
{
5394
5
  uint32_t argc, i;
5395
5
  zval *args;
5396
5
  int (*diff_data_compare_func)(zval *, zval *) = NULL;
5397
5
  bool ok;
5398
5
  zval *val, *data;
5399
5
  zend_string *key;
5400
5
  zend_ulong h;
5401
5402
  /* Get the argument count */
5403
5
  argc = ZEND_NUM_ARGS();
5404
5
  if (data_compare_type == DIFF_COMP_DATA_USER) {
5405
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
5406
0
      RETURN_THROWS();
5407
0
    }
5408
0
    diff_data_compare_func = zval_user_compare;
5409
5
  } else {
5410
5
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
5411
5
      RETURN_THROWS();
5412
5
    }
5413
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5414
0
      diff_data_compare_func = zval_compare;
5415
0
    }
5416
0
  }
5417
5418
0
  for (i = 0; i < argc; i++) {
5419
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5420
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5421
0
      RETURN_THROWS();
5422
0
    }
5423
0
  }
5424
5425
0
  array_init(return_value);
5426
5427
  /* Iterate over keys of the first array, to compute keys that aren't in the other arrays. */
5428
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), h, key, val) {
5429
0
    if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
5430
0
      val = Z_REFVAL_P(val);
5431
0
    }
5432
0
    if (key == NULL) {
5433
0
      ok = true;
5434
0
      for (i = 1; i < argc; i++) {
5435
0
        if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
5436
0
          (!diff_data_compare_func ||
5437
0
          diff_data_compare_func(val, data) == 0)
5438
0
        ) {
5439
0
          ok = false;
5440
0
          break;
5441
0
        }
5442
0
      }
5443
0
      if (ok) {
5444
0
        Z_TRY_ADDREF_P(val);
5445
0
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
5446
0
      }
5447
0
    } else {
5448
0
      ok = true;
5449
0
      for (i = 1; i < argc; i++) {
5450
0
        if ((data = zend_hash_find_known_hash(Z_ARRVAL(args[i]), key)) != NULL &&
5451
0
          (!diff_data_compare_func ||
5452
0
          diff_data_compare_func(val, data) == 0)
5453
0
        ) {
5454
0
          ok = false;
5455
0
          break;
5456
0
        }
5457
0
      }
5458
0
      if (ok) {
5459
0
        Z_TRY_ADDREF_P(val);
5460
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
5461
0
      }
5462
0
    }
5463
0
  } ZEND_HASH_FOREACH_END();
5464
0
}
5465
/* }}} */
5466
5467
static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
5468
0
{
5469
0
  zval *args = NULL;
5470
0
  HashTable *hash;
5471
0
  uint32_t arr_argc, i;
5472
0
  int c;
5473
0
  uint32_t idx;
5474
0
  Bucket **lists, *list, **ptrs, *p;
5475
0
  char *param_spec;
5476
0
  zend_fcall_info fci1, fci2;
5477
0
  zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
5478
0
  zend_fcall_info *fci_key = NULL, *fci_data;
5479
0
  zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
5480
0
  PHP_ARRAY_CMP_FUNC_VARS;
5481
5482
0
  bucket_compare_func_t diff_key_compare_func;
5483
0
  bucket_compare_func_t diff_data_compare_func;
5484
5485
0
  if (behavior == DIFF_NORMAL) {
5486
0
    diff_key_compare_func = php_array_key_compare_string_unstable;
5487
5488
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
5489
      /* array_diff */
5490
0
      param_spec = "+";
5491
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5492
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER) {
5493
      /* array_udiff */
5494
0
      param_spec = "+f";
5495
0
      diff_data_compare_func = php_array_user_compare_unstable;
5496
0
    } else {
5497
0
      ZEND_ASSERT(0 && "Invalid data_compare_type");
5498
0
      return;
5499
0
    }
5500
5501
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
5502
0
      RETURN_THROWS();
5503
0
    }
5504
0
    fci_data = &fci1;
5505
0
    fci_data_cache = &fci1_cache;
5506
5507
0
  } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
5508
    /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
5509
     * no comparison of the data is done (part of DIFF_ASSOC) */
5510
5511
0
    if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5512
      /* array_diff_assoc() or array_diff_key() */
5513
0
      param_spec = "+";
5514
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5515
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5516
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
5517
      /* array_udiff_assoc() */
5518
0
      param_spec = "+f";
5519
0
      diff_key_compare_func = php_array_key_compare_string_unstable;
5520
0
      diff_data_compare_func = php_array_user_compare_unstable;
5521
0
      fci_data = &fci1;
5522
0
      fci_data_cache = &fci1_cache;
5523
0
    } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
5524
      /* array_diff_uassoc() or array_diff_ukey() */
5525
0
      param_spec = "+f";
5526
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5527
0
      diff_data_compare_func = php_array_data_compare_string_unstable;
5528
0
      fci_key = &fci1;
5529
0
      fci_key_cache = &fci1_cache;
5530
0
    } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
5531
      /* array_udiff_uassoc() */
5532
0
      param_spec = "+ff";
5533
0
      diff_key_compare_func = php_array_user_key_compare_unstable;
5534
0
      diff_data_compare_func = php_array_user_compare_unstable;
5535
0
      fci_data = &fci1;
5536
0
      fci_data_cache = &fci1_cache;
5537
0
      fci_key = &fci2;
5538
0
      fci_key_cache = &fci2_cache;
5539
0
    } else {
5540
0
      ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
5541
0
      return;
5542
0
    }
5543
5544
0
    if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
5545
0
      RETURN_THROWS();
5546
0
    }
5547
5548
0
  } else {
5549
0
    ZEND_ASSERT(0 && "Invalid behavior");
5550
0
    return;
5551
0
  }
5552
5553
0
  PHP_ARRAY_CMP_FUNC_BACKUP();
5554
5555
  /* for each argument, create and sort list with pointers to the hash buckets */
5556
0
  lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5557
0
  ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
5558
5559
0
  if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
5560
0
    BG(user_compare_fci) = *fci_data;
5561
0
    BG(user_compare_fci_cache) = *fci_data_cache;
5562
0
  } else if ((behavior & DIFF_ASSOC) && key_compare_type == DIFF_COMP_KEY_USER) {
5563
0
    BG(user_compare_fci) = *fci_key;
5564
0
    BG(user_compare_fci_cache) = *fci_key_cache;
5565
0
  }
5566
5567
0
  for (i = 0; i < arr_argc; i++) {
5568
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5569
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5570
0
      arr_argc = i; /* only free up to i - 1 */
5571
0
      goto out;
5572
0
    }
5573
0
    hash = Z_ARRVAL(args[i]);
5574
0
    list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5575
0
    lists[i] = list;
5576
0
    ptrs[i] = list;
5577
0
    if (HT_IS_PACKED(hash)) {
5578
0
      zval *zv = hash->arPacked;
5579
0
      for (idx = 0; idx < hash->nNumUsed; idx++, zv++) {
5580
0
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5581
0
        ZVAL_COPY_VALUE(&list->val, zv);
5582
0
        list->h = idx;
5583
0
        list->key = NULL;
5584
0
        list++;
5585
0
      }
5586
0
    } else {
5587
0
      p = hash->arData;
5588
0
      for (idx = 0; idx < hash->nNumUsed; idx++, p++) {
5589
0
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
5590
0
        *list++ = *p;
5591
0
      }
5592
0
    }
5593
0
    ZVAL_UNDEF(&list->val);
5594
0
    if (hash->nNumOfElements > 1) {
5595
0
      if (behavior == DIFF_NORMAL) {
5596
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5597
0
            sizeof(Bucket), (compare_func_t) diff_data_compare_func,
5598
0
            (swap_func_t)zend_hash_bucket_swap);
5599
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5600
0
        zend_sort((void *) lists[i], hash->nNumOfElements,
5601
0
            sizeof(Bucket), (compare_func_t) diff_key_compare_func,
5602
0
            (swap_func_t)zend_hash_bucket_swap);
5603
0
      }
5604
0
    }
5605
0
  }
5606
5607
  /* copy the argument array */
5608
0
  RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
5609
5610
  /* go through the lists and look for values of ptr[0] that are not in the others */
5611
0
  while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
5612
0
    if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
5613
0
      &&
5614
0
      key_compare_type == DIFF_COMP_KEY_USER
5615
0
    ) {
5616
0
      BG(user_compare_fci) = *fci_key;
5617
0
      BG(user_compare_fci_cache) = *fci_key_cache;
5618
0
    }
5619
0
    c = 1;
5620
0
    for (i = 1; i < arr_argc; i++) {
5621
0
      Bucket *ptr = ptrs[i];
5622
0
      if (behavior == DIFF_NORMAL) {
5623
0
        while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
5624
0
          ptrs[i]++;
5625
0
        }
5626
0
      } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5627
0
        while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
5628
0
          ptr++;
5629
0
        }
5630
0
      }
5631
0
      if (!c) {
5632
0
        if (behavior == DIFF_NORMAL) {
5633
0
          if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
5634
0
            ptrs[i]++;
5635
0
          }
5636
0
          break;
5637
0
        } else if (behavior == DIFF_ASSOC) {  /* only when DIFF_ASSOC */
5638
          /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
5639
           * data comparison is not needed - skipped. */
5640
0
          if (Z_TYPE(ptr->val) != IS_UNDEF) {
5641
0
            if (data_compare_type == DIFF_COMP_DATA_USER) {
5642
0
              BG(user_compare_fci) = *fci_data;
5643
0
              BG(user_compare_fci_cache) = *fci_data_cache;
5644
0
            }
5645
0
            if (diff_data_compare_func(ptrs[0], ptr) != 0) {
5646
              /* the data is not the same */
5647
0
              c = -1;
5648
0
              if (key_compare_type == DIFF_COMP_KEY_USER) {
5649
0
                BG(user_compare_fci) = *fci_key;
5650
0
                BG(user_compare_fci_cache) = *fci_key_cache;
5651
0
              }
5652
0
            } else {
5653
0
              break;
5654
              /* we have found the element in other arrays thus we don't want it
5655
               * in the return_value -> delete from there */
5656
0
            }
5657
0
          }
5658
0
        } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
5659
          /* the behavior here differs from INTERSECT_KEY in php_intersect
5660
           * since in the "diff" case we have to remove the entry from
5661
           * return_value while when doing intersection the entry must not
5662
           * be deleted. */
5663
0
          break; /* remove the key */
5664
0
        }
5665
0
      }
5666
0
    }
5667
0
    if (!c) {
5668
      /* ptrs[0] in one of the other arguments */
5669
      /* delete all entries with value as ptrs[0] */
5670
0
      for (;;) {
5671
0
        p = ptrs[0];
5672
0
        if (p->key == NULL) {
5673
0
          zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5674
0
        } else {
5675
0
          zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5676
0
        }
5677
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5678
0
          goto out;
5679
0
        }
5680
0
        if (behavior == DIFF_NORMAL) {
5681
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5682
0
            break;
5683
0
          }
5684
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5685
          /* in this case no array_key_compare is needed */
5686
0
          break;
5687
0
        }
5688
0
      }
5689
0
    } else {
5690
      /* ptrs[0] in none of the other arguments */
5691
      /* skip all entries with value as ptrs[0] */
5692
0
      for (;;) {
5693
0
        if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
5694
0
          goto out;
5695
0
        }
5696
0
        if (behavior == DIFF_NORMAL) {
5697
0
          if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
5698
0
            break;
5699
0
          }
5700
0
        } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
5701
          /* in this case no array_key_compare is needed */
5702
0
          break;
5703
0
        }
5704
0
      }
5705
0
    }
5706
0
  }
5707
0
out:
5708
0
  for (i = 0; i < arr_argc; i++) {
5709
0
    hash = Z_ARRVAL(args[i]);
5710
0
    pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
5711
0
  }
5712
5713
0
  PHP_ARRAY_CMP_FUNC_RESTORE();
5714
5715
0
  efree(ptrs);
5716
0
  efree(lists);
5717
0
}
5718
/* }}} */
5719
5720
/* {{{ 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. */
5721
PHP_FUNCTION(array_diff_key)
5722
5
{
5723
5
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
5724
5
}
5725
/* }}} */
5726
5727
/* {{{ 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. */
5728
PHP_FUNCTION(array_diff_ukey)
5729
0
{
5730
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5731
0
}
5732
/* }}} */
5733
5734
/* {{{ Returns the entries of arr1 that have values which are not present in any of the others arguments. */
5735
PHP_FUNCTION(array_diff)
5736
17
{
5737
17
  zval *args;
5738
17
  uint32_t argc, i;
5739
17
  uint32_t num;
5740
17
  HashTable exclude;
5741
17
  zval *value;
5742
17
  zend_string *str, *tmp_str, *key;
5743
17
  zend_long idx;
5744
17
  zval dummy;
5745
5746
34
  ZEND_PARSE_PARAMETERS_START(1, -1)
5747
34
    Z_PARAM_VARIADIC('+', args, argc)
5748
34
  ZEND_PARSE_PARAMETERS_END();
5749
5750
0
  if (Z_TYPE(args[0]) != IS_ARRAY) {
5751
0
    zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
5752
0
    RETURN_THROWS();
5753
0
  }
5754
5755
0
  num = zend_hash_num_elements(Z_ARRVAL(args[0]));
5756
0
  if (num == 0) {
5757
0
    for (i = 1; i < argc; i++) {
5758
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
5759
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5760
0
        RETURN_THROWS();
5761
0
      }
5762
0
    }
5763
0
    RETURN_EMPTY_ARRAY();
5764
0
  } else if (num == 1) {
5765
0
    int found = 0;
5766
0
    zend_string *search_str, *tmp_search_str;
5767
5768
0
    value = NULL;
5769
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[0]), value) {
5770
0
      break;
5771
0
    } ZEND_HASH_FOREACH_END();
5772
5773
0
    if (!value) {
5774
0
      for (i = 1; i < argc; i++) {
5775
0
        if (Z_TYPE(args[i]) != IS_ARRAY) {
5776
0
          zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5777
0
          RETURN_THROWS();
5778
0
        }
5779
0
      }
5780
0
      RETURN_EMPTY_ARRAY();
5781
0
    }
5782
5783
0
    search_str = zval_get_tmp_string(value, &tmp_search_str);
5784
5785
0
    for (i = 1; i < argc; i++) {
5786
0
      if (Z_TYPE(args[i]) != IS_ARRAY) {
5787
0
        zend_tmp_string_release(tmp_search_str);
5788
0
        zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5789
0
        RETURN_THROWS();
5790
0
      }
5791
0
      if (!found) {
5792
0
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5793
0
          str = zval_get_tmp_string(value, &tmp_str);
5794
0
          if (zend_string_equals(search_str, str)) {
5795
0
            zend_tmp_string_release(tmp_str);
5796
0
            found = 1;
5797
0
            break;
5798
0
          }
5799
0
          zend_tmp_string_release(tmp_str);
5800
0
        } ZEND_HASH_FOREACH_END();
5801
0
      }
5802
0
    }
5803
5804
0
    zend_tmp_string_release(tmp_search_str);
5805
5806
0
    if (found) {
5807
0
      RETVAL_EMPTY_ARRAY();
5808
0
    } else {
5809
0
      ZVAL_COPY(return_value, &args[0]);
5810
0
    }
5811
0
    return;
5812
0
  }
5813
5814
  /* count number of elements */
5815
0
  num = 0;
5816
0
  for (i = 1; i < argc; i++) {
5817
0
    if (Z_TYPE(args[i]) != IS_ARRAY) {
5818
0
      zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_value_name(&args[i]));
5819
0
      RETURN_THROWS();
5820
0
    }
5821
0
    num += zend_hash_num_elements(Z_ARRVAL(args[i]));
5822
0
  }
5823
5824
0
  if (num == 0) {
5825
0
    ZVAL_COPY(return_value, &args[0]);
5826
0
    return;
5827
0
  }
5828
5829
0
  ZVAL_NULL(&dummy);
5830
  /* create exclude map */
5831
0
  zend_hash_init(&exclude, num, NULL, NULL, 0);
5832
0
  for (i = 1; i < argc; i++) {
5833
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), value) {
5834
0
      str = zval_get_tmp_string(value, &tmp_str);
5835
0
      zend_hash_add(&exclude, str, &dummy);
5836
0
      zend_tmp_string_release(tmp_str);
5837
0
    } ZEND_HASH_FOREACH_END();
5838
0
  }
5839
5840
  /* copy all elements of first array that are not in exclude set */
5841
0
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
5842
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(args[0]), idx, key, value) {
5843
0
    str = zval_get_tmp_string(value, &tmp_str);
5844
0
    if (!zend_hash_exists(&exclude, str)) {
5845
0
      if (key) {
5846
0
        value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
5847
0
      } else {
5848
0
        value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
5849
0
      }
5850
0
      zval_add_ref(value);
5851
0
    }
5852
0
    zend_tmp_string_release(tmp_str);
5853
0
  } ZEND_HASH_FOREACH_END();
5854
5855
0
  zend_hash_destroy(&exclude);
5856
0
}
5857
/* }}} */
5858
5859
/* {{{ 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. */
5860
PHP_FUNCTION(array_udiff)
5861
0
{
5862
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
5863
0
}
5864
/* }}} */
5865
5866
/* {{{ 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 */
5867
PHP_FUNCTION(array_diff_assoc)
5868
0
{
5869
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
5870
0
}
5871
/* }}} */
5872
5873
/* {{{ 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. */
5874
PHP_FUNCTION(array_diff_uassoc)
5875
0
{
5876
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
5877
0
}
5878
/* }}} */
5879
5880
/* {{{ 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. */
5881
PHP_FUNCTION(array_udiff_assoc)
5882
0
{
5883
0
  php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
5884
0
}
5885
/* }}} */
5886
5887
/* {{{ 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. */
5888
PHP_FUNCTION(array_udiff_uassoc)
5889
0
{
5890
0
  php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
5891
0
}
5892
/* }}} */
5893
5894
45
#define MULTISORT_ORDER 0
5895
22
#define MULTISORT_TYPE  1
5896
657
#define MULTISORT_LAST  2
5897
5898
PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5899
120
{
5900
120
  Bucket *ab = *(Bucket **)a;
5901
120
  Bucket *bb = *(Bucket **)b;
5902
120
  int r;
5903
120
  zend_long result;
5904
5905
120
  r = 0;
5906
120
  do {
5907
120
    result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
5908
120
    if (result != 0) {
5909
111
      return result > 0 ? 1 : -1;
5910
111
    }
5911
9
    r++;
5912
9
  } while (Z_TYPE(ab[r].val) != IS_UNDEF);
5913
5914
9
  return stable_sort_fallback(&ab[r], &bb[r]);
5915
120
}
5916
/* }}} */
5917
5918
#define MULTISORT_ABORT       \
5919
7
  efree(func); \
5920
7
  efree(arrays);         \
5921
0
  return;
5922
5923
63
static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
5924
63
  Bucket *t;
5925
63
  Bucket **f = (Bucket **)p;
5926
63
  Bucket **g = (Bucket **)q;
5927
5928
63
  t = *f;
5929
63
  *f = *g;
5930
63
  *g = t;
5931
63
}
5932
/* }}} */
5933
5934
/* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
5935
PHP_FUNCTION(array_multisort)
5936
110
{
5937
110
  zval*     args;
5938
110
  zval**      arrays;
5939
110
  Bucket**    indirect;
5940
110
  uint32_t    idx;
5941
110
  HashTable*    hash;
5942
110
  uint32_t    argc;
5943
110
  uint32_t    array_size;
5944
110
  uint32_t    num_arrays = 0;
5945
110
  int       parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
5946
110
  int       sort_order = PHP_SORT_ASC;
5947
110
  int       sort_type  = PHP_SORT_REGULAR;
5948
110
  uint32_t    i, k, n;
5949
110
  bucket_compare_func_t *func;
5950
5951
322
  ZEND_PARSE_PARAMETERS_START(1, -1)
5952
322
    Z_PARAM_VARIADIC('+', args, argc)
5953
322
  ZEND_PARSE_PARAMETERS_END();
5954
5955
  /* Allocate space for storing pointers to input arrays and sort flags. */
5956
102
  arrays = (zval **)ecalloc(argc, sizeof(zval *));
5957
306
  for (i = 0; i < MULTISORT_LAST; i++) {
5958
204
    parse_state[i] = 0;
5959
204
  }
5960
102
  func = ecalloc(argc, sizeof(bucket_compare_func_t));
5961
5962
  /* Here we go through the input arguments and parse them. Each one can
5963
   * be either an array or a sort flag which follows an array. If not
5964
   * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
5965
   * accordingly. There can't be two sort flags of the same type after an
5966
   * array, and the very first argument has to be an array. */
5967
252
  for (i = 0; i < argc; i++) {
5968
157
    zval *arg = &args[i];
5969
5970
157
    ZVAL_DEREF(arg);
5971
157
    if (Z_TYPE_P(arg) == IS_ARRAY) {
5972
117
      SEPARATE_ARRAY(arg);
5973
      /* We see the next array, so we update the sort flags of
5974
       * the previous array and reset the sort flags. */
5975
117
      if (i > 0) {
5976
15
        func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
5977
15
        sort_order = PHP_SORT_ASC;
5978
15
        sort_type = PHP_SORT_REGULAR;
5979
15
      }
5980
117
      arrays[num_arrays++] = arg;
5981
5982
      /* Next one may be an array or a list of sort flags. */
5983
351
      for (k = 0; k < MULTISORT_LAST; k++) {
5984
234
        parse_state[k] = 1;
5985
234
      }
5986
117
    } else if (Z_TYPE_P(arg) == IS_LONG) {
5987
34
      switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
5988
23
        case PHP_SORT_ASC:
5989
23
        case PHP_SORT_DESC:
5990
          /* flag allowed here */
5991
23
          if (parse_state[MULTISORT_ORDER] == 1) {
5992
            /* Save the flag and make sure then next arg is not the current flag. */
5993
22
            sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
5994
22
            parse_state[MULTISORT_ORDER] = 0;
5995
22
          } else {
5996
1
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
5997
1
            MULTISORT_ABORT;
5998
0
          }
5999
22
          break;
6000
6001
22
        case PHP_SORT_REGULAR:
6002
0
        case PHP_SORT_NUMERIC:
6003
11
        case PHP_SORT_STRING:
6004
11
        case PHP_SORT_NATURAL:
6005
11
        case PHP_SORT_LOCALE_STRING:
6006
          /* flag allowed here */
6007
11
          if (parse_state[MULTISORT_TYPE] == 1) {
6008
            /* Save the flag and make sure then next arg is not the current flag. */
6009
11
            sort_type = (int)Z_LVAL_P(arg);
6010
11
            parse_state[MULTISORT_TYPE] = 0;
6011
11
          } else {
6012
0
            zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
6013
0
            MULTISORT_ABORT;
6014
0
          }
6015
11
          break;
6016
6017
11
        default:
6018
0
          zend_argument_value_error(i + 1, "must be a valid sort flag");
6019
0
          MULTISORT_ABORT;
6020
0
          break;
6021
6022
34
      }
6023
34
    } else {
6024
6
      zend_argument_type_error(i + 1, "must be an array or a sort flag");
6025
6
      MULTISORT_ABORT;
6026
0
    }
6027
157
  }
6028
6029
  /* Make sure the arrays are of the same size. */
6030
95
  array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6031
107
  for (i = 1; i < num_arrays; i++) {
6032
12
    if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) {
6033
0
      zend_value_error("Array sizes are inconsistent");
6034
0
      MULTISORT_ABORT;
6035
0
    }
6036
12
  }
6037
6038
  /* If all arrays are empty we don't need to do anything. */
6039
95
  if (array_size < 1) {
6040
6
    efree(func);
6041
6
    efree(arrays);
6042
6
    RETURN_TRUE;
6043
6
  }
6044
6045
  /* Take care of the last array sort flags. */
6046
89
  func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6047
89
  bucket_compare_func_t *old_multisort_func = ARRAYG(multisort_func);
6048
89
  ARRAYG(multisort_func) = func;
6049
6050
  /* Create the indirection array. This array is of size MxN, where
6051
   * M is the number of entries in each input array and N is the number
6052
   * of the input arrays + 1. The last column is UNDEF to indicate the end
6053
   * of the row. It also stores the original position for stable sorting. */
6054
89
  indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6055
  /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6056
89
  Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6057
298
  for (i = 0; i < array_size; i++) {
6058
209
    indirect[i] = indirects + (i * (num_arrays + 1));
6059
209
  }
6060
178
  for (i = 0; i < num_arrays; i++) {
6061
89
    k = 0;
6062
89
    if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6063
53
      zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6064
217
      for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6065
164
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6066
146
        ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6067
146
        indirect[k][i].h = idx;
6068
146
        indirect[k][i].key = NULL;
6069
146
        k++;
6070
146
      }
6071
53
    } else {
6072
36
      Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6073
99
      for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6074
63
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
6075
63
        indirect[k][i] = *p;
6076
63
        k++;
6077
63
      }
6078
36
    }
6079
89
  }
6080
298
  for (k = 0; k < array_size; k++) {
6081
209
    ZVAL_UNDEF(&indirect[k][num_arrays].val);
6082
209
    Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6083
209
  }
6084
6085
  /* Do the actual sort magic - bada-bim, bada-boom. */
6086
89
  zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6087
89
  if (EG(exception)) {
6088
9
    goto clean_up;
6089
9
  }
6090
6091
  /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6092
160
  for (i = 0; i < num_arrays; i++) {
6093
80
    hash = Z_ARRVAL_P(arrays[i]);
6094
80
    hash->nNumUsed = array_size;
6095
80
    hash->nNextFreeElement = array_size;
6096
80
    hash->nInternalPointer = 0;
6097
80
    if (HT_IS_PACKED(hash)) {
6098
190
      for (k = 0; k < array_size; k++) {
6099
140
        ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6100
140
      }
6101
50
    } else {
6102
30
      bool repack = true;
6103
6104
81
      for (n = 0, k = 0; k < array_size; k++) {
6105
51
        hash->arData[k] = indirect[k][i];
6106
51
        if (hash->arData[k].key == NULL) {
6107
0
          hash->arData[k].h = n++;
6108
51
        } else {
6109
51
          repack = false;
6110
51
        }
6111
51
      }
6112
30
      if (repack) {
6113
0
        zend_hash_to_packed(hash);
6114
30
      } else {
6115
30
        zend_hash_rehash(hash);
6116
30
      }
6117
30
    }
6118
80
  }
6119
80
  RETVAL_TRUE;
6120
6121
89
clean_up:
6122
89
  efree(indirects);
6123
89
  efree(indirect);
6124
89
  efree(func);
6125
89
  efree(arrays);
6126
89
  ARRAYG(multisort_func) = old_multisort_func;
6127
89
}
6128
/* }}} */
6129
6130
/* {{{ php_array_pick_keys */
6131
PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent)
6132
0
{
6133
0
  const php_random_algo *algo = engine.algo;
6134
0
  void *state = engine.state;
6135
6136
0
  HashTable *ht = Z_ARRVAL_P(input);
6137
0
  uint32_t num_avail = zend_hash_num_elements(ht);
6138
0
  zend_long i, randval;
6139
0
  zend_string *string_key;
6140
0
  zend_ulong num_key;
6141
0
  zval *zv;
6142
0
  Bucket *b;
6143
0
  zend_bitset bitset;
6144
0
  int negative_bitset = 0;
6145
0
  uint32_t bitset_len;
6146
0
  ALLOCA_FLAG(use_heap);
6147
6148
0
  if (num_avail == 0) {
6149
0
    if (!silent) {
6150
0
      zend_argument_must_not_be_empty_error(1);
6151
0
    }
6152
0
    return false;
6153
0
  }
6154
6155
0
  if (num_req == 1) {
6156
0
    if (num_avail < ht->nNumUsed - (ht->nNumUsed >> 1)) {
6157
      /* If less than 1/2 of elements are used, don't sample. Instead search for a
6158
       * specific offset using linear scan. */
6159
0
      i = 0;
6160
0
      randval = algo->range(state, 0, num_avail - 1);
6161
0
      if (EG(exception)) {
6162
0
        return false;
6163
0
      }
6164
0
      ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6165
0
        if (i == randval) {
6166
0
          if (string_key) {
6167
0
            ZVAL_STR_COPY(retval, string_key);
6168
0
          } else {
6169
0
            ZVAL_LONG(retval, num_key);
6170
0
          }
6171
0
          return true;
6172
0
        }
6173
0
        i++;
6174
0
      } ZEND_HASH_FOREACH_END();
6175
0
    }
6176
6177
    /* Sample random buckets until we hit one that is not empty.
6178
     * The worst case probability of hitting an empty element is 1-1/2. The worst case
6179
     * probability of hitting N empty elements in a row is (1-1/2)**N.
6180
     * For N=10 this becomes smaller than 0.1%. */
6181
0
    if (HT_IS_PACKED(ht)) {
6182
0
      do {
6183
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6184
0
        if (EG(exception)) {
6185
0
          return false;
6186
0
        }
6187
0
        zv = &ht->arPacked[randval];
6188
0
        if (!Z_ISUNDEF_P(zv)) {
6189
0
          ZVAL_LONG(retval, randval);
6190
0
          return true;
6191
0
        }
6192
0
      } while (true);
6193
0
    } else {
6194
0
      do {
6195
0
        randval = algo->range(state, 0, ht->nNumUsed - 1);
6196
0
        if (EG(exception)) {
6197
0
          return false;
6198
0
        }
6199
0
        b = &ht->arData[randval];
6200
0
        if (!Z_ISUNDEF(b->val)) {
6201
0
          if (b->key) {
6202
0
            ZVAL_STR_COPY(retval, b->key);
6203
0
          } else {
6204
0
            ZVAL_LONG(retval, b->h);
6205
0
          }
6206
0
          return true;
6207
0
        }
6208
0
      } while (true);
6209
0
    }
6210
0
  }
6211
6212
0
  if (num_req <= 0 || num_req > num_avail) {
6213
0
    if (!silent) {
6214
0
      zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
6215
0
    }
6216
0
    return false;
6217
0
  }
6218
6219
  /* Make the return value an array only if we need to pass back more than one result. */
6220
0
  array_init_size(retval, (uint32_t) num_req);
6221
0
  if (num_req > (num_avail >> 1)) {
6222
0
    negative_bitset = 1;
6223
0
    num_req = num_avail - num_req;
6224
0
  }
6225
6226
0
  bitset_len = zend_bitset_len(num_avail);
6227
0
  bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
6228
0
  zend_bitset_clear(bitset, bitset_len);
6229
6230
0
  i = num_req;
6231
0
  int failures = 0;
6232
0
  while (i) {
6233
0
    randval = algo->range(state, 0, num_avail - 1);
6234
0
    if (EG(exception)) {
6235
0
      goto fail;
6236
0
    }
6237
0
    if (zend_bitset_in(bitset, randval)) {
6238
0
      if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
6239
0
        if (!silent) {
6240
0
          zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
6241
0
        }
6242
6243
0
        goto fail;
6244
0
      }
6245
0
    } else {
6246
0
      zend_bitset_incl(bitset, randval);
6247
0
      i--;
6248
0
      failures = 0;
6249
0
    }
6250
0
  }
6251
6252
0
  zend_hash_real_init_packed(Z_ARRVAL_P(retval));
6253
0
  ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
6254
    /* We can't use zend_hash_index_find()
6255
     * because the array may have string keys or gaps. */
6256
0
    ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) {
6257
0
      if (zend_bitset_in(bitset, i) ^ negative_bitset) {
6258
0
        if (string_key) {
6259
0
          ZEND_HASH_FILL_SET_STR_COPY(string_key);
6260
0
        } else {
6261
0
          ZEND_HASH_FILL_SET_LONG(num_key);
6262
0
        }
6263
0
        ZEND_HASH_FILL_NEXT();
6264
0
      }
6265
0
      i++;
6266
0
    } ZEND_HASH_FOREACH_END();
6267
0
  } ZEND_HASH_FILL_END();
6268
6269
0
  free_alloca(bitset, use_heap);
6270
6271
0
  return true;
6272
6273
0
 fail:
6274
0
  free_alloca(bitset, use_heap);
6275
6276
0
  return false;
6277
0
}
6278
/* }}} */
6279
6280
/* {{{ Return key/keys for random entry/entries in the array */
6281
PHP_FUNCTION(array_rand)
6282
0
{
6283
0
  zval *input;
6284
0
  zend_long num_req = 1;
6285
6286
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
6287
0
    Z_PARAM_ARRAY(input)
6288
0
    Z_PARAM_OPTIONAL
6289
0
    Z_PARAM_LONG(num_req)
6290
0
  ZEND_PARSE_PARAMETERS_END();
6291
6292
0
  if (!php_array_pick_keys(
6293
0
      php_random_default_engine(),
6294
0
      input,
6295
0
      num_req,
6296
0
      return_value,
6297
0
      false)
6298
0
  ) {
6299
0
    RETURN_THROWS();
6300
0
  }
6301
0
}
6302
/* }}} */
6303
6304
/* Wrapper for array_sum and array_product */
6305
static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial)
6306
0
{
6307
0
  HashTable *input;
6308
0
  zval *entry;
6309
6310
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
6311
0
    Z_PARAM_ARRAY_HT(input)
6312
0
  ZEND_PARSE_PARAMETERS_END();
6313
6314
0
  if (zend_hash_num_elements(input) == 0) {
6315
0
    RETURN_LONG(initial);
6316
0
  }
6317
6318
0
  ZVAL_LONG(return_value, initial);
6319
0
  ZEND_HASH_FOREACH_VAL(input, entry) {
6320
    /* For objects we try to cast them to a numeric type */
6321
0
    if (Z_TYPE_P(entry) == IS_OBJECT) {
6322
0
      zval dst;
6323
0
      zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER);
6324
6325
      /* Do not type error for BC */
6326
0
      if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) {
6327
0
        php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6328
0
          op_name, zend_zval_type_name(entry));
6329
0
        continue;
6330
0
      }
6331
0
      op(return_value, return_value, &dst);
6332
0
      continue;
6333
0
    }
6334
6335
0
    zend_result status = op(return_value, return_value, entry);
6336
0
    if (status == FAILURE) {
6337
0
      ZEND_ASSERT(EG(exception));
6338
0
      zend_clear_exception();
6339
      /* BC resources: previously resources were cast to int */
6340
0
      if (Z_TYPE_P(entry) == IS_RESOURCE) {
6341
0
        zval tmp;
6342
0
        ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry));
6343
0
        op(return_value, return_value, &tmp);
6344
0
      }
6345
      /* BC non numeric strings: previously were cast to 0 */
6346
0
      else if (Z_TYPE_P(entry) == IS_STRING) {
6347
0
        zval tmp;
6348
0
        ZVAL_LONG(&tmp, 0);
6349
0
        op(return_value, return_value, &tmp);
6350
0
      }
6351
0
      php_error_docref(NULL, E_WARNING, "%s is not supported on type %s",
6352
0
        op_name, zend_zval_type_name(entry));
6353
0
    }
6354
0
  } ZEND_HASH_FOREACH_END();
6355
0
}
6356
6357
/* {{{ Returns the sum of the array entries */
6358
PHP_FUNCTION(array_sum)
6359
0
{
6360
0
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0);
6361
0
}
6362
/* }}} */
6363
6364
/* {{{ Returns the product of the array entries */
6365
PHP_FUNCTION(array_product)
6366
0
{
6367
0
  php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1);
6368
0
}
6369
/* }}} */
6370
6371
/* {{{ Iteratively reduce the array to a single value via the callback. */
6372
PHP_FUNCTION(array_reduce)
6373
12
{
6374
12
  zval *input;
6375
12
  zval args[2];
6376
12
  zval *operand;
6377
12
  zend_fcall_info fci;
6378
12
  zend_fcall_info_cache fci_cache;
6379
12
  zval *initial = NULL;
6380
12
  HashTable *htbl;
6381
6382
36
  ZEND_PARSE_PARAMETERS_START(2, 3)
6383
48
    Z_PARAM_ARRAY(input)
6384
55
    Z_PARAM_FUNC(fci, fci_cache)
6385
11
    Z_PARAM_OPTIONAL
6386
42
    Z_PARAM_ZVAL(initial)
6387
42
  ZEND_PARSE_PARAMETERS_END();
6388
6389
11
  if (initial) {
6390
10
    ZVAL_COPY(return_value, initial);
6391
10
  } else {
6392
1
    ZVAL_NULL(return_value);
6393
1
  }
6394
6395
  /* (zval **)input points to an element of argument stack
6396
   * the base pointer of which is subject to change.
6397
   * thus we need to keep the pointer to the hashtable for safety */
6398
11
  htbl = Z_ARRVAL_P(input);
6399
6400
11
  if (zend_hash_num_elements(htbl) == 0) {
6401
0
    return;
6402
0
  }
6403
6404
11
  fci.retval = return_value;
6405
11
  fci.param_count = 2;
6406
11
  fci.params = args;
6407
6408
73
  ZEND_HASH_FOREACH_VAL(htbl, operand) {
6409
73
    ZVAL_COPY_VALUE(&args[0], return_value);
6410
73
    ZVAL_COPY_VALUE(&args[1], operand);
6411
6412
73
    zend_call_function(&fci, &fci_cache);
6413
73
    zval_ptr_dtor(&args[0]);
6414
6415
73
    if (EXPECTED(!Z_ISUNDEF_P(return_value))) {
6416
30
      if (UNEXPECTED(Z_ISREF_P(return_value))) {
6417
6
        zend_unwrap_reference(return_value);
6418
6
      }
6419
30
    } else {
6420
1
      RETURN_NULL();
6421
1
    }
6422
73
  } ZEND_HASH_FOREACH_END();
6423
11
}
6424
/* }}} */
6425
6426
/* Consumes `zv` */
6427
static bool php_is_true(zval *zv)
6428
495
{
6429
495
  switch (Z_TYPE_P(zv)) {
6430
6
    case IS_TRUE:
6431
6
      return true;
6432
0
    case IS_FALSE:
6433
0
      return false;
6434
489
    default: {
6435
489
      bool rv = zend_is_true(zv);
6436
489
      zval_ptr_dtor(zv);
6437
489
      return rv;
6438
0
    }
6439
495
  }
6440
495
}
6441
6442
/* {{{ Filters elements from the array via the callback. */
6443
PHP_FUNCTION(array_filter)
6444
17
{
6445
17
  zval *array;
6446
17
  zval *operand;
6447
17
  zval *key;
6448
17
  zval args[2];
6449
17
  zval retval;
6450
17
  bool have_callback = 0;
6451
17
  zend_long use_type = 0;
6452
17
  zend_string *string_key;
6453
17
  zend_fcall_info fci = empty_fcall_info;
6454
17
  zend_fcall_info_cache fci_cache;
6455
17
  zend_ulong num_key;
6456
6457
51
  ZEND_PARSE_PARAMETERS_START(1, 3)
6458
68
    Z_PARAM_ARRAY(array)
6459
17
    Z_PARAM_OPTIONAL
6460
68
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6461
51
    Z_PARAM_LONG(use_type)
6462
17
  ZEND_PARSE_PARAMETERS_END();
6463
6464
17
  if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6465
4
    RETVAL_EMPTY_ARRAY();
6466
4
    return;
6467
4
  }
6468
13
  array_init(return_value);
6469
6470
13
  if (ZEND_FCI_INITIALIZED(fci)) {
6471
11
    have_callback = 1;
6472
11
    fci.retval = &retval;
6473
11
    if (use_type == ARRAY_FILTER_USE_BOTH) {
6474
0
      fci.param_count = 2;
6475
0
      key = &args[1];
6476
11
    } else {
6477
11
      fci.param_count = 1;
6478
11
      key = &args[0];
6479
11
    }
6480
11
  }
6481
6482
1.01k
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6483
1.01k
    if (have_callback) {
6484
500
      if (use_type) {
6485
        /* Set up the key */
6486
0
        if (!string_key) {
6487
0
          ZVAL_LONG(key, num_key);
6488
0
        } else {
6489
0
          ZVAL_STR(key, string_key);
6490
0
        }
6491
0
      }
6492
500
      if (use_type != ARRAY_FILTER_USE_KEY) {
6493
500
        ZVAL_COPY_VALUE(&args[0], operand);
6494
500
      }
6495
500
      fci.params = args;
6496
6497
500
      zend_result result = zend_call_function(&fci, &fci_cache);
6498
500
      ZEND_ASSERT(result == SUCCESS);
6499
6500
500
      if (UNEXPECTED(EG(exception))) {
6501
2
        RETURN_THROWS();
6502
2
      }
6503
6504
495
      if (!php_is_true(&retval)) {
6505
489
        continue;
6506
489
      }
6507
495
    } else if (!zend_is_true(operand)) {
6508
2
      continue;
6509
2
    }
6510
6511
6
    if (string_key) {
6512
0
      operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
6513
6
    } else {
6514
6
      operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
6515
6
    }
6516
6
    zval_add_ref(operand);
6517
6
  } ZEND_HASH_FOREACH_END();
6518
13
}
6519
/* }}} */
6520
6521
/* {{{ Internal function to find an array element for a user closure. */
6522
enum php_array_find_result {
6523
  PHP_ARRAY_FIND_EXCEPTION = -1,
6524
  PHP_ARRAY_FIND_NONE = 0,
6525
  PHP_ARRAY_FIND_SOME = 1,
6526
};
6527
6528
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)
6529
0
{
6530
0
  zend_ulong num_key;
6531
0
  zend_string *str_key;
6532
0
  zval retval;
6533
0
  zval args[2];
6534
0
  zval *operand;
6535
6536
0
  if (zend_hash_num_elements(array) == 0) {
6537
0
    return PHP_ARRAY_FIND_NONE;
6538
0
  }
6539
6540
0
  ZEND_ASSERT(ZEND_FCI_INITIALIZED(fci));
6541
6542
0
  fci.retval = &retval;
6543
0
  fci.param_count = 2;
6544
0
  fci.params = args;
6545
6546
0
  ZEND_HASH_FOREACH_KEY_VAL(array, num_key, str_key, operand) {
6547
    /* Set up the key */
6548
0
    if (!str_key) {
6549
0
      ZVAL_LONG(&args[1], num_key);
6550
0
    } else {
6551
      /* Allows copying the numeric branch, without this branch, into the iteration code
6552
       * that checks for the packed array flag. */
6553
0
      ZEND_ASSUME(!HT_IS_PACKED(array));
6554
0
      ZVAL_STR(&args[1], str_key);
6555
0
    }
6556
6557
0
    ZVAL_COPY_VALUE(&args[0], operand);
6558
6559
0
    zend_result result = zend_call_function(&fci, fci_cache);
6560
0
    ZEND_ASSERT(result == SUCCESS);
6561
6562
0
    if (UNEXPECTED(Z_ISUNDEF(retval))) {
6563
0
      return PHP_ARRAY_FIND_EXCEPTION;
6564
0
    }
6565
6566
0
    if (php_is_true(&retval) ^ negate_condition) {
6567
0
      if (result_value != NULL) {
6568
0
        ZVAL_COPY_DEREF(result_value, &args[0]);
6569
0
      }
6570
6571
0
      if (result_key != NULL) {
6572
0
        ZVAL_COPY(result_key, &args[1]);
6573
0
      }
6574
6575
0
      return PHP_ARRAY_FIND_SOME;
6576
0
    }
6577
0
  } ZEND_HASH_FOREACH_END();
6578
6579
0
  return PHP_ARRAY_FIND_NONE;
6580
0
}
6581
/* }}} */
6582
6583
/* {{{ Search within an array and returns the first found element value. */
6584
PHP_FUNCTION(array_find)
6585
0
{
6586
0
  HashTable *array;
6587
0
  zend_fcall_info fci;
6588
0
  zend_fcall_info_cache fci_cache;
6589
6590
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6591
0
    Z_PARAM_ARRAY_HT(array)
6592
0
    Z_PARAM_FUNC(fci, fci_cache)
6593
0
  ZEND_PARSE_PARAMETERS_END();
6594
6595
0
  php_array_find(array, fci, &fci_cache, NULL, return_value, false);
6596
0
}
6597
/* }}} */
6598
6599
/* {{{ Search within an array and returns the first found element key. */
6600
PHP_FUNCTION(array_find_key)
6601
0
{
6602
0
  HashTable *array;
6603
0
  zend_fcall_info fci;
6604
0
  zend_fcall_info_cache fci_cache;
6605
6606
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6607
0
    Z_PARAM_ARRAY_HT(array)
6608
0
    Z_PARAM_FUNC(fci, fci_cache)
6609
0
  ZEND_PARSE_PARAMETERS_END();
6610
6611
0
  php_array_find(array, fci, &fci_cache, return_value, NULL, false);
6612
0
}
6613
/* }}} */
6614
6615
/* {{{ Checks if at least one array element satisfies a callback function. */
6616
PHP_FUNCTION(array_any)
6617
0
{
6618
0
  HashTable *array;
6619
0
  zend_fcall_info fci;
6620
0
  zend_fcall_info_cache fci_cache;
6621
6622
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6623
0
    Z_PARAM_ARRAY_HT(array)
6624
0
    Z_PARAM_FUNC(fci, fci_cache)
6625
0
  ZEND_PARSE_PARAMETERS_END();
6626
6627
0
  RETURN_BOOL(php_array_find(array, fci, &fci_cache, NULL, NULL, false) == PHP_ARRAY_FIND_SOME);
6628
0
}
6629
/* }}} */
6630
6631
/* {{{ Checks if all array elements satisfy a callback function. */
6632
PHP_FUNCTION(array_all)
6633
0
{
6634
0
  HashTable *array;
6635
0
  zend_fcall_info fci;
6636
0
  zend_fcall_info_cache fci_cache;
6637
6638
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6639
0
    Z_PARAM_ARRAY_HT(array)
6640
0
    Z_PARAM_FUNC(fci, fci_cache)
6641
0
  ZEND_PARSE_PARAMETERS_END();
6642
6643
0
  RETURN_BOOL(php_array_find(array, fci, &fci_cache, NULL, NULL, true) == PHP_ARRAY_FIND_NONE);
6644
0
}
6645
/* }}} */
6646
6647
/* {{{ Applies the callback to the elements in given arrays. */
6648
PHP_FUNCTION(array_map)
6649
1.38k
{
6650
1.38k
  zval *arrays = NULL;
6651
1.38k
  uint32_t n_arrays = 0;
6652
1.38k
  zval result;
6653
1.38k
  zend_fcall_info fci;
6654
1.38k
  zend_fcall_info_cache fci_cache;
6655
1.38k
  uint32_t i;
6656
1.38k
  uint32_t k, maxlen = 0;
6657
6658
4.14k
  ZEND_PARSE_PARAMETERS_START(2, -1)
6659
5.53k
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6660
1.34k
    Z_PARAM_VARIADIC('+', arrays, n_arrays)
6661
1.38k
  ZEND_PARSE_PARAMETERS_END();
6662
6663
1.34k
  if (n_arrays == 1) {
6664
1.32k
    if (Z_TYPE(arrays[0]) != IS_ARRAY) {
6665
6
      zend_argument_type_error(2, "must be of type array, %s given", zend_zval_value_name(&arrays[0]));
6666
6
      RETURN_THROWS();
6667
6
    }
6668
1.31k
    const HashTable *input = Z_ARRVAL(arrays[0]);
6669
1.31k
    maxlen = zend_hash_num_elements(input);
6670
6671
    /* Short-circuit: if no callback and only one array, just return it. */
6672
1.31k
    if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6673
51
      ZVAL_COPY(return_value, &arrays[0]);
6674
51
      return;
6675
51
    }
6676
6677
1.26k
    fci.retval = &result;
6678
1.26k
    fci.param_count = 1;
6679
6680
1.26k
    if (HT_IS_PACKED(input)) {
6681
1.25k
      array_init_size(return_value, input->nNumUsed);
6682
1.25k
      HashTable *output = Z_ARRVAL_P(return_value);
6683
1.25k
      zend_hash_real_init_packed(output);
6684
6685
1.25k
      uint32_t undefs = 0;
6686
1.25k
      ZEND_HASH_FILL_PACKED(output) {
6687
        /* Can't use ZEND_HASH_PACKED_FOREACH_VAL() because we need to also account for the UNDEF values
6688
         * so the keys in the output array will match those of the input array. */
6689
1.79k
        for (zval *cur = input->arPacked, *end = input->arPacked + input->nNumUsed; cur != end; cur++) {
6690
1.56k
          if (EXPECTED(!Z_ISUNDEF_P(cur))) {
6691
1.56k
            fci.params = cur;
6692
1.56k
            zend_result ret = zend_call_function(&fci, &fci_cache);
6693
1.56k
            ZEND_ASSERT(ret == SUCCESS);
6694
1.56k
            ZEND_IGNORE_VALUE(ret);
6695
572
            if (UNEXPECTED(Z_ISUNDEF(result))) {
6696
35
              ZEND_HASH_FILL_FINISH();
6697
35
              RETURN_THROWS();
6698
35
            }
6699
572
          } else {
6700
0
            ZVAL_UNDEF(&result);
6701
0
            undefs++;
6702
0
          }
6703
537
          ZEND_HASH_FILL_ADD(&result);
6704
537
        }
6705
1.25k
      } ZEND_HASH_FILL_END();
6706
226
      output->nNumOfElements -= undefs;
6707
226
    } else {
6708
11
      zend_ulong num_key;
6709
11
      zend_string *str_key;
6710
6711
11
      array_init_size(return_value, maxlen);
6712
11
      HashTable *output = Z_ARRVAL_P(return_value);
6713
11
      zend_hash_real_init_mixed(output);
6714
6715
154
      ZEND_HASH_MAP_FOREACH_KEY_VAL(input, num_key, str_key, fci.params) {
6716
154
        zend_result ret = zend_call_function(&fci, &fci_cache);
6717
154
        ZEND_ASSERT(ret == SUCCESS);
6718
154
        ZEND_IGNORE_VALUE(ret);
6719
66
        if (UNEXPECTED(Z_ISUNDEF(result))) {
6720
0
          RETURN_THROWS();
6721
0
        }
6722
66
        if (str_key) {
6723
66
          _zend_hash_append(output, str_key, &result);
6724
66
        } else {
6725
0
          zend_hash_index_add_new(output, num_key, &result);
6726
0
        }
6727
66
      } ZEND_HASH_FOREACH_END();
6728
11
    }
6729
1.26k
  } else {
6730
65
    for (i = 0; i < n_arrays; i++) {
6731
50
      if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6732
4
        zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6733
4
        RETURN_THROWS();
6734
4
      }
6735
46
      if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6736
20
        maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6737
20
      }
6738
46
    }
6739
6740
15
    array_init_size(return_value, maxlen);
6741
6742
15
    if (!ZEND_FCI_INITIALIZED(fci)) {
6743
15
      uint32_t *array_pos = ecalloc(n_arrays, sizeof(HashPosition));
6744
15
      zval zv;
6745
6746
      /* We iterate through all the arrays at once. */
6747
61
      for (k = 0; k < maxlen; k++) {
6748
6749
        /* If no callback, the result will be an array, consisting of current
6750
         * entries from all arrays. */
6751
46
        array_init_size(&result, n_arrays);
6752
6753
183
        for (i = 0; i < n_arrays; i++) {
6754
          /* If this array still has elements, add the current one to the
6755
           * parameter list, otherwise use null value. */
6756
137
          uint32_t pos = array_pos[i];
6757
137
          if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6758
137
            while (1) {
6759
137
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6760
20
                ZVAL_NULL(&zv);
6761
20
                break;
6762
117
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6763
117
                ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6764
117
                array_pos[i] = pos + 1;
6765
117
                break;
6766
117
              }
6767
0
              pos++;
6768
0
            }
6769
137
          } else {
6770
0
            while (1) {
6771
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6772
0
                ZVAL_NULL(&zv);
6773
0
                break;
6774
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6775
0
                ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
6776
0
                array_pos[i] = pos + 1;
6777
0
                break;
6778
0
              }
6779
0
              pos++;
6780
0
            }
6781
0
          }
6782
137
          zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
6783
137
        }
6784
6785
46
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6786
46
      }
6787
6788
15
      efree(array_pos);
6789
15
    } else {
6790
0
      zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
6791
6792
      /* Remember next starting point in the array, initialize those as zeros. */
6793
0
      for (i = 0; i < n_arrays; i++) {
6794
0
        Z_EXTRA(params[i]) = 0;
6795
0
      }
6796
6797
0
      fci.retval = &result;
6798
0
      fci.param_count = n_arrays;
6799
0
      fci.params = params;
6800
6801
      /* We iterate through all the arrays at once. */
6802
0
      for (k = 0; k < maxlen; k++) {
6803
0
        for (i = 0; i < n_arrays; i++) {
6804
          /* If this array still has elements, add the current one to the
6805
           * parameter list, otherwise use null value. */
6806
0
          uint32_t pos = Z_EXTRA(params[i]);
6807
0
          if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
6808
0
            while (1) {
6809
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6810
0
                ZVAL_NULL(&params[i]);
6811
0
                break;
6812
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6813
0
                ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
6814
0
                Z_EXTRA(params[i]) = pos + 1;
6815
0
                break;
6816
0
              }
6817
0
              pos++;
6818
0
            }
6819
0
          } else {
6820
0
            while (1) {
6821
0
              if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
6822
0
                ZVAL_NULL(&params[i]);
6823
0
                break;
6824
0
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
6825
0
                ZVAL_COPY_VALUE(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
6826
0
                Z_EXTRA(params[i]) = pos + 1;
6827
0
                break;
6828
0
              }
6829
0
              pos++;
6830
0
            }
6831
0
          }
6832
0
        }
6833
6834
0
        zend_result ret = zend_call_function(&fci, &fci_cache);
6835
0
        ZEND_ASSERT(ret == SUCCESS);
6836
0
        ZEND_IGNORE_VALUE(ret);
6837
6838
0
        if (Z_TYPE(result) == IS_UNDEF) {
6839
0
          efree(params);
6840
0
          RETURN_THROWS();
6841
0
        }
6842
6843
0
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
6844
0
      }
6845
6846
0
      efree(params);
6847
0
    }
6848
15
  }
6849
1.34k
}
6850
/* }}} */
6851
6852
/* {{{ Checks if the given key or index exists in the array */
6853
PHP_FUNCTION(array_key_exists)
6854
9
{
6855
9
  zval *key;
6856
9
  HashTable *ht;
6857
6858
18
  ZEND_PARSE_PARAMETERS_START(2, 2)
6859
18
    Z_PARAM_ZVAL(key)
6860
0
    Z_PARAM_ARRAY_HT(ht)
6861
9
  ZEND_PARSE_PARAMETERS_END();
6862
6863
0
  switch (Z_TYPE_P(key)) {
6864
0
    case IS_STRING:
6865
0
      RETVAL_BOOL(zend_symtable_exists(ht, Z_STR_P(key)));
6866
0
      break;
6867
0
    case IS_LONG:
6868
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
6869
0
      break;
6870
0
    case IS_NULL:
6871
0
      zend_error(E_DEPRECATED, "Using null as the key parameter for array_key_exists() is deprecated, use an empty string instead");
6872
0
      RETVAL_BOOL(zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()));
6873
0
      break;
6874
0
    case IS_DOUBLE:
6875
0
      RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval_safe(Z_DVAL_P(key))));
6876
0
      break;
6877
0
    case IS_FALSE:
6878
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 0));
6879
0
      break;
6880
0
    case IS_TRUE:
6881
0
      RETVAL_BOOL(zend_hash_index_exists(ht, 1));
6882
0
      break;
6883
0
    case IS_RESOURCE:
6884
0
      zend_use_resource_as_offset(key);
6885
0
      RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
6886
0
      break;
6887
0
    default:
6888
0
      zend_argument_type_error(1, "must be a valid array offset type");
6889
0
      break;
6890
0
  }
6891
0
}
6892
/* }}} */
6893
6894
/* {{{ Split array into chunks */
6895
PHP_FUNCTION(array_chunk)
6896
0
{
6897
0
  int num_in;
6898
0
  zend_long size, current = 0;
6899
0
  zend_string *str_key;
6900
0
  zend_ulong num_key;
6901
0
  bool preserve_keys = 0;
6902
0
  zval *input = NULL;
6903
0
  zval chunk;
6904
0
  zval *entry;
6905
6906
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
6907
0
    Z_PARAM_ARRAY(input)
6908
0
    Z_PARAM_LONG(size)
6909
0
    Z_PARAM_OPTIONAL
6910
0
    Z_PARAM_BOOL(preserve_keys)
6911
0
  ZEND_PARSE_PARAMETERS_END();
6912
6913
  /* Do bounds checking for size parameter. */
6914
0
  if (size < 1) {
6915
0
    zend_argument_value_error(2, "must be greater than 0");
6916
0
    RETURN_THROWS();
6917
0
  }
6918
6919
0
  num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
6920
6921
0
  if (size > num_in) {
6922
0
    if (num_in == 0) {
6923
0
      RETURN_EMPTY_ARRAY();
6924
0
    }
6925
0
    size = num_in;
6926
0
  }
6927
6928
0
  array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
6929
0
  zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
6930
6931
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
6932
    /* If new chunk, create and initialize it. */
6933
0
    if (current == 0) {
6934
0
      array_init_size(&chunk, (uint32_t)size);
6935
0
      add_next_index_zval(return_value, &chunk);
6936
0
    }
6937
6938
    /* Add entry to the chunk, preserving keys if necessary. */
6939
0
    if (preserve_keys) {
6940
0
      if (str_key) {
6941
0
        entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
6942
0
      } else {
6943
0
        entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
6944
0
      }
6945
0
    } else {
6946
0
      entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
6947
0
    }
6948
0
    zval_add_ref(entry);
6949
6950
0
    if (++current == size) {
6951
0
      current = 0;
6952
0
    }
6953
0
  } ZEND_HASH_FOREACH_END();
6954
0
}
6955
/* }}} */
6956
6957
/* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
6958
PHP_FUNCTION(array_combine)
6959
0
{
6960
0
  HashTable *values, *keys;
6961
0
  uint32_t pos_values = 0;
6962
0
  zval *entry_keys, *entry_values;
6963
0
  int num_keys, num_values;
6964
6965
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6966
0
    Z_PARAM_ARRAY_HT(keys)
6967
0
    Z_PARAM_ARRAY_HT(values)
6968
0
  ZEND_PARSE_PARAMETERS_END();
6969
6970
0
  num_keys = zend_hash_num_elements(keys);
6971
0
  num_values = zend_hash_num_elements(values);
6972
6973
0
  if (num_keys != num_values) {
6974
0
    zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
6975
0
    RETURN_THROWS();
6976
0
  }
6977
6978
0
  if (!num_keys) {
6979
0
    RETURN_EMPTY_ARRAY();
6980
0
  }
6981
6982
0
  array_init_size(return_value, num_keys);
6983
0
  ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
6984
0
    while (1) {
6985
0
      if (pos_values >= values->nNumUsed) {
6986
0
        break;
6987
0
      }
6988
0
      entry_values = ZEND_HASH_ELEMENT(values, pos_values);
6989
0
      if (Z_TYPE_P(entry_values) != IS_UNDEF) {
6990
0
        if (Z_TYPE_P(entry_keys) == IS_LONG) {
6991
0
          entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
6992
0
            Z_LVAL_P(entry_keys), entry_values);
6993
0
        } else {
6994
0
          zend_string *tmp_key;
6995
0
          zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
6996
0
          entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
6997
0
            key, entry_values);
6998
0
          zend_tmp_string_release(tmp_key);
6999
0
        }
7000
0
        zval_add_ref(entry_values);
7001
0
        pos_values++;
7002
0
        break;
7003
0
      }
7004
0
      pos_values++;
7005
0
    }
7006
0
  } ZEND_HASH_FOREACH_END();
7007
0
}
7008
/* }}} */