Coverage Report

Created: 2026-04-01 06:49

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