Coverage Report

Created: 2025-11-16 06:23

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