Coverage Report

Created: 2025-07-23 06:33

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