Coverage Report

Created: 2025-06-13 06:43

/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
107k
static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b) {
90
107k
  if (Z_EXTRA(a->val) > Z_EXTRA(b->val)) {
91
57.7k
    return 1;
92
57.7k
  } else if (Z_EXTRA(a->val) < Z_EXTRA(b->val)) {
93
49.2k
    return -1;
94
49.2k
  } else {
95
0
    return 0;
96
0
  }
97
107k
}
98
99
345k
#define RETURN_STABLE_SORT(a, b, result) do { \
100
345k
  int _result = (result); \
101
345k
  if (EXPECTED(_result)) { \
102
238k
    return _result; \
103
238k
  } \
104
345k
  return stable_sort_fallback((a), (b)); \
105
345k
} while (0)
106
107
/* Generate inlined unstable and stable variants, and non-inlined reversed variants. */
108
#define DEFINE_SORT_VARIANTS(name) \
109
27.9k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
27.9k
    return php_array_##name##_unstable_i(a, b); \
111
27.9k
  } \
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
5
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
5
    return php_array_##name##_unstable_i(a, b); \
111
5
  } \
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
27.9k
  static zend_never_inline int php_array_##name##_unstable(Bucket *a, Bucket *b) { \
110
27.9k
    return php_array_##name##_unstable_i(a, b); \
111
27.9k
  } \
112
318k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
318k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
318k
  } \
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
35.5k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
35.5k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
35.5k
  } \
array.c:php_array_data_compare_numeric
Line
Count
Source
112
6.23k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
6.23k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
6.23k
  } \
array.c:php_array_data_compare_string_case
Line
Count
Source
112
7.49k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
7.49k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
7.49k
  } \
array.c:php_array_data_compare_string
Line
Count
Source
112
156k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
156k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
156k
  } \
array.c:php_array_data_compare_string_locale
Line
Count
Source
112
8.51k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
8.51k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
8.51k
  } \
array.c:php_array_data_compare
Line
Count
Source
112
103k
  static zend_never_inline int php_array_##name(Bucket *a, Bucket *b) { \
113
103k
    RETURN_STABLE_SORT(a, b, php_array_##name##_unstable_i(a, b)); \
114
103k
  } \
115
27.2k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
116
27.2k
    return php_array_##name##_unstable(a, b) * -1; \
117
27.2k
  } \
Unexecuted instantiation: array.c:php_array_reverse_key_compare_numeric_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_case_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_locale_unstable
Unexecuted instantiation: array.c:php_array_reverse_key_compare_unstable
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
27.2k
  static zend_never_inline int php_array_reverse_##name##_unstable(Bucket *a, Bucket *b) { \
116
27.2k
    return php_array_##name##_unstable(a, b) * -1; \
117
27.2k
  } \
118
27.2k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
119
27.2k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
120
27.2k
  } \
Unexecuted instantiation: array.c:php_array_reverse_key_compare_numeric
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_case
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string
Unexecuted instantiation: array.c:php_array_reverse_key_compare_string_locale
Unexecuted instantiation: array.c:php_array_reverse_key_compare
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
27.2k
  static zend_never_inline int php_array_reverse_##name(Bucket *a, Bucket *b) { \
119
27.2k
    RETURN_STABLE_SORT(a, b, php_array_reverse_##name##_unstable(a, b)); \
120
27.2k
  } \
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
131k
{
288
131k
  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
131k
  zval *rhs = &s->val;
292
131k
  ZVAL_DEREF(rhs);
293
131k
  if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
294
131k
   && result == ZEND_UNCOMPARABLE
295
131k
   && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
296
497
    zval *lhs = &f->val;
297
497
    ZVAL_DEREF(lhs);
298
497
    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
497
      uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
301
497
      uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
302
497
      return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
303
497
    } else {
304
      // Shift enums to the end of the array
305
0
      return -1;
306
0
    }
307
497
  }
308
130k
  return result;
309
131k
}
310
/* }}} */
311
312
static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */
313
6.23k
{
314
6.23k
  return numeric_compare_function(&f->val, &s->val);
315
6.23k
}
316
/* }}} */
317
318
static zend_always_inline int php_array_data_compare_string_case_unstable_i(Bucket *f, Bucket *s) /* {{{ */
319
7.49k
{
320
7.49k
  return string_case_compare_function(&f->val, &s->val);
321
7.49k
}
322
/* }}} */
323
324
static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f, Bucket *s) /* {{{ */
325
156k
{
326
156k
  return string_compare_function(&f->val, &s->val);
327
156k
}
328
/* }}} */
329
330
static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */
331
35.5k
{
332
35.5k
  zend_string *tmp_str1, *tmp_str2;
333
35.5k
  zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
334
35.5k
  zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
335
336
35.5k
  int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
337
338
35.5k
  zend_tmp_string_release(tmp_str1);
339
35.5k
  zend_tmp_string_release(tmp_str2);
340
35.5k
  return result;
341
35.5k
}
342
/* }}} */
343
344
static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */
345
35.5k
{
346
35.5k
  return php_array_natural_general_compare(a, b, 0);
347
35.5k
}
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
8.51k
{
358
8.51k
  return string_locale_compare_function(&f->val, &s->val);
359
8.51k
}
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.83k
{
441
1.83k
  switch (sort_type & ~PHP_SORT_FLAG_CASE) {
442
21
    case PHP_SORT_NUMERIC:
443
21
      if (reverse) {
444
0
        return php_array_reverse_data_compare_numeric;
445
21
      } else {
446
21
        return php_array_data_compare_numeric;
447
21
      }
448
0
      break;
449
450
249
    case PHP_SORT_STRING:
451
249
      if (sort_type & PHP_SORT_FLAG_CASE) {
452
8
        if (reverse) {
453
0
          return php_array_reverse_data_compare_string_case;
454
8
        } else {
455
8
          return php_array_data_compare_string_case;
456
8
        }
457
241
      } else {
458
241
        if (reverse) {
459
0
          return php_array_reverse_data_compare_string;
460
241
        } else {
461
241
          return php_array_data_compare_string;
462
241
        }
463
241
      }
464
0
      break;
465
466
233
    case PHP_SORT_NATURAL:
467
233
      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
233
      } else {
474
233
        if (reverse) {
475
0
          return php_array_reverse_natural_compare;
476
233
        } else {
477
233
          return php_array_natural_compare;
478
233
        }
479
233
      }
480
0
      break;
481
482
23
    case PHP_SORT_LOCALE_STRING:
483
23
      if (reverse) {
484
0
        return php_array_reverse_data_compare_string_locale;
485
23
      } else {
486
23
        return php_array_data_compare_string_locale;
487
23
      }
488
0
      break;
489
490
1.22k
    case PHP_SORT_REGULAR:
491
1.30k
    default:
492
1.30k
      if (reverse) {
493
632
        return php_array_reverse_data_compare;
494
677
      } else {
495
677
        return php_array_data_compare;
496
677
      }
497
0
      break;
498
1.83k
  }
499
0
  return NULL;
500
1.83k
}
501
/* }}} */
502
503
static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, int reverse) /* {{{ */
504
137
{
505
137
  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
7
    case PHP_SORT_STRING:
515
7
      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
7
      } else {
522
7
        if (reverse) {
523
0
          return php_array_reverse_data_compare_string_unstable;
524
7
        } else {
525
7
          return php_array_data_compare_string_unstable;
526
7
        }
527
7
      }
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
130
    case PHP_SORT_REGULAR:
555
130
    default:
556
130
      if (reverse) {
557
0
        return php_array_reverse_data_compare_unstable;
558
130
      } else {
559
130
        return php_array_data_compare_unstable;
560
130
      }
561
0
      break;
562
137
  }
563
0
  return NULL;
564
137
}
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
88
{
638
88
  if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) {
639
68
    return Z_LVAL_P(zv);
640
68
  } else {
641
20
    zend_long ret = zval_get_long_func(zv, false);
642
20
    zval_ptr_dtor(zv);
643
20
    return ret;
644
20
  }
645
88
}
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
611
{
761
611
  zval *array;
762
611
  zend_long sort_type = PHP_SORT_REGULAR;
763
611
  bucket_compare_func_t cmp;
764
765
1.83k
  ZEND_PARSE_PARAMETERS_START(1, 2)
766
2.44k
    Z_PARAM_ARRAY_EX(array, 0, 1)
767
610
    Z_PARAM_OPTIONAL
768
1.22k
    Z_PARAM_LONG(sort_type)
769
611
  ZEND_PARSE_PARAMETERS_END();
770
771
610
  cmp = php_get_data_compare_func(sort_type, 1);
772
773
610
  zend_array_sort(Z_ARRVAL_P(array), cmp, 0);
774
775
610
  RETURN_TRUE;
776
610
}
777
/* }}} */
778
779
/* {{{ Sort an array */
780
PHP_FUNCTION(sort)
781
1.20k
{
782
1.20k
  zval *array;
783
1.20k
  zend_long sort_type = PHP_SORT_REGULAR;
784
1.20k
  bucket_compare_func_t cmp;
785
786
3.59k
  ZEND_PARSE_PARAMETERS_START(1, 2)
787
4.79k
    Z_PARAM_ARRAY_EX(array, 0, 1)
788
1.19k
    Z_PARAM_OPTIONAL
789
3.64k
    Z_PARAM_LONG(sort_type)
790
1.20k
  ZEND_PARSE_PARAMETERS_END();
791
792
1.19k
  cmp = php_get_data_compare_func(sort_type, 0);
793
794
1.19k
  zend_array_sort(Z_ARRVAL_P(array), cmp, 1);
795
796
1.19k
  RETURN_TRUE;
797
1.19k
}
798
/* }}} */
799
800
/* {{{ Sort an array in reverse order */
801
PHP_FUNCTION(rsort)
802
24
{
803
24
  zval *array;
804
24
  zend_long sort_type = PHP_SORT_REGULAR;
805
24
  bucket_compare_func_t cmp;
806
807
72
  ZEND_PARSE_PARAMETERS_START(1, 2)
808
96
    Z_PARAM_ARRAY_EX(array, 0, 1)
809
22
    Z_PARAM_OPTIONAL
810
44
    Z_PARAM_LONG(sort_type)
811
24
  ZEND_PARSE_PARAMETERS_END();
812
813
22
  cmp = php_get_data_compare_func(sort_type, 1);
814
815
22
  zend_array_sort(Z_ARRVAL_P(array), cmp, 1);
816
817
22
  RETURN_TRUE;
818
22
}
819
/* }}} */
820
821
static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ */
822
88
{
823
88
  zval args[2];
824
88
  zval retval;
825
826
88
  ZVAL_COPY_VALUE(&args[0], &f->val);
827
88
  ZVAL_COPY_VALUE(&args[1], &s->val);
828
829
88
  BG(user_compare_fci).param_count = 2;
830
88
  BG(user_compare_fci).params = args;
831
88
  BG(user_compare_fci).retval = &retval;
832
88
  zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache));
833
834
88
  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
88
  zend_long ret = php_get_long(&retval);
854
88
  return ZEND_NORMALIZE_BOOL(ret);
855
88
}
856
/* }}} */
857
858
static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
859
88
{
860
88
  RETURN_STABLE_SORT(a, b, php_array_user_compare_unstable(a, b));
861
88
}
862
/* }}} */
863
864
#define PHP_ARRAY_CMP_FUNC_VARS \
865
16
  zend_fcall_info old_user_compare_fci; \
866
16
  zend_fcall_info_cache old_user_compare_fci_cache \
867
868
#define PHP_ARRAY_CMP_FUNC_BACKUP() \
869
16
  old_user_compare_fci = BG(user_compare_fci); \
870
16
  old_user_compare_fci_cache = BG(user_compare_fci_cache); \
871
16
  ARRAYG(compare_deprecation_thrown) = 0; \
872
16
  BG(user_compare_fci_cache) = empty_fcall_info_cache; \
873
874
#define PHP_ARRAY_CMP_FUNC_RESTORE() \
875
16
  BG(user_compare_fci) = old_user_compare_fci; \
876
16
  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
16
{
880
16
  zval *array;
881
16
  zend_array *arr;
882
16
  PHP_ARRAY_CMP_FUNC_VARS;
883
884
16
  PHP_ARRAY_CMP_FUNC_BACKUP();
885
886
48
  ZEND_PARSE_PARAMETERS_START(2, 2)
887
64
    Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
888
80
    Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
889
16
  ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
890
891
16
  arr = Z_ARR_P(array);
892
16
  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
16
  arr = zend_array_dup(arr);
899
900
16
  zend_array_sort(arr, compare_func, renumber);
901
902
16
  zval garbage;
903
16
  ZVAL_COPY_VALUE(&garbage, array);
904
16
  ZVAL_ARR(array, arr);
905
16
  zval_ptr_dtor(&garbage);
906
907
16
  PHP_ARRAY_CMP_FUNC_RESTORE();
908
16
  RETURN_TRUE;
909
16
}
910
/* }}} */
911
912
/* {{{ Sort an array by values using a user-defined comparison function */
913
PHP_FUNCTION(usort)
914
16
{
915
16
  php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
916
16
}
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.25k
static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
994
1.25k
  if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
995
1.20k
    return Z_ARRVAL_P(zv);
996
1.20k
  }
997
998
53
  ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
999
53
  php_error_docref(NULL, E_DEPRECATED,
1000
53
    "Calling %s() on an object is deprecated", get_active_function_name());
1001
1002
53
  zend_object *zobj = Z_OBJ_P(zv);
1003
53
  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
53
  return zobj->handlers->get_properties(zobj);
1010
53
}
1011
1012
static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction)
1013
371
{
1014
371
  zval *entry;
1015
1016
371
  while (true) {
1017
371
    if ((entry = zend_hash_get_current_data(array)) == NULL) {
1018
26
      return NULL;
1019
26
    }
1020
1021
345
    ZVAL_DEINDIRECT(entry);
1022
1023
    /* Possible with an uninitialized typed property */
1024
345
    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
345
    } else {
1035
345
      break;
1036
345
    }
1037
345
  }
1038
1039
345
  return entry;
1040
371
}
1041
1042
static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction)
1043
144
{
1044
144
  zval *entry = php_array_iter_seek_current(array, forward_direction);
1045
144
  if (EXPECTED(entry)) {
1046
123
    RETURN_COPY_DEREF(entry);
1047
123
  } else {
1048
21
    RETURN_FALSE;
1049
21
  }
1050
144
}
1051
1052
/* {{{ Advances array argument's internal pointer to the last element and return it */
1053
PHP_FUNCTION(end)
1054
37
{
1055
37
  zval *array_zv;
1056
1057
109
  ZEND_PARSE_PARAMETERS_START(1, 1)
1058
140
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1059
37
  ZEND_PARSE_PARAMETERS_END();
1060
1061
30
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1062
30
  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
30
  zend_hash_internal_pointer_end(array);
1067
1068
30
  if (USED_RET()) {
1069
12
    php_array_iter_return_current(return_value, array, false);
1070
12
  }
1071
30
}
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
244
{
1099
244
  zval *array_zv;
1100
1101
729
  ZEND_PARSE_PARAMETERS_START(1, 1)
1102
964
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1103
244
  ZEND_PARSE_PARAMETERS_END();
1104
1105
233
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1106
233
  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
233
  zend_hash_move_forward(array);
1111
1112
233
  if (USED_RET()) {
1113
23
    php_array_iter_return_current(return_value, array, true);
1114
23
  }
1115
233
}
1116
/* }}} */
1117
1118
/* {{{ Set array argument's internal pointer to the first element and return it */
1119
PHP_FUNCTION(reset)
1120
675
{
1121
675
  zval *array_zv;
1122
1123
2.02k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1124
2.70k
    Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1)
1125
675
  ZEND_PARSE_PARAMETERS_END();
1126
1127
673
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1128
673
  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
668
  zend_hash_internal_pointer_reset(array);
1133
1134
668
  if (USED_RET()) {
1135
24
    php_array_iter_return_current(return_value, array, true);
1136
24
  }
1137
668
}
1138
/* }}} */
1139
1140
/* {{{ Return the element currently pointed to by the internal array pointer */
1141
PHP_FUNCTION(current)
1142
82
{
1143
82
  zval *array_zv;
1144
1145
241
  ZEND_PARSE_PARAMETERS_START(1, 1)
1146
308
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1147
82
  ZEND_PARSE_PARAMETERS_END();
1148
1149
75
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1150
75
  php_array_iter_return_current(return_value, array, true);
1151
75
}
1152
/* }}} */
1153
1154
/* {{{ Return the key of the element currently pointed to by the internal array pointer */
1155
PHP_FUNCTION(key)
1156
234
{
1157
234
  zval *array_zv;
1158
1159
702
  ZEND_PARSE_PARAMETERS_START(1, 1)
1160
936
    Z_PARAM_ARRAY_OR_OBJECT(array_zv)
1161
234
  ZEND_PARSE_PARAMETERS_END();
1162
1163
227
  HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1164
227
  zval *entry = php_array_iter_seek_current(array, true);
1165
227
  if (EXPECTED(entry)) {
1166
222
    zend_hash_get_current_key_zval(array, return_value);
1167
222
  }
1168
227
}
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
13
{
1311
13
  zval *args = NULL;
1312
13
  uint32_t argc;
1313
1314
39
  ZEND_PARSE_PARAMETERS_START(1, -1)
1315
39
    Z_PARAM_VARIADIC('+', args, argc)
1316
13
  ZEND_PARSE_PARAMETERS_END();
1317
1318
  /* mixed max ( array $values ) */
1319
13
  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
13
  } else {
1333
    /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1334
13
    zval *max;
1335
13
    uint32_t i;
1336
1337
13
    max = &args[0];
1338
13
    zend_long max_lval;
1339
13
    double max_dval;
1340
1341
13
    if (Z_TYPE_P(max) == IS_LONG) {
1342
13
      max_lval = Z_LVAL_P(max);
1343
1344
26
      for (i = 1; i < argc; i++) {
1345
13
        if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1346
13
          if (max_lval < Z_LVAL(args[i])) {
1347
13
            max_lval = Z_LVAL(args[i]);
1348
13
            max = &args[i];
1349
13
          }
1350
13
        } 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
13
      }
1358
1359
13
      RETURN_LONG(max_lval);
1360
13
    } 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
13
}
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
29
{
1441
29
  zval args[3],   /* Arguments to userland function */
1442
29
     retval,    /* Return value - unused */
1443
29
     *zv;
1444
29
  HashTable *target_hash = HASH_OF(array);
1445
29
  HashPosition pos;
1446
29
  uint32_t ht_iter;
1447
29
  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
29
  zend_fcall_info fci = context->fci;
1452
1453
29
  if (zend_hash_num_elements(target_hash) == 0) {
1454
0
    return result;
1455
0
  }
1456
1457
  /* Set up known arguments */
1458
29
  ZVAL_UNDEF(&args[1]);
1459
29
  if (userdata) {
1460
0
    ZVAL_COPY(&args[2], userdata);
1461
0
  }
1462
1463
29
  fci.retval = &retval;
1464
29
  fci.param_count = userdata ? 3 : 2;
1465
29
  fci.params = args;
1466
1467
29
  zend_hash_internal_pointer_reset_ex(target_hash, &pos);
1468
29
  ht_iter = zend_hash_iterator_add(target_hash, pos);
1469
1470
  /* Iterate through hash */
1471
141
  do {
1472
    /* Retrieve value */
1473
141
    zv = zend_hash_get_current_data_ex(target_hash, &pos);
1474
141
    if (zv == NULL) {
1475
26
      break;
1476
26
    }
1477
1478
    /* Skip undefined indirect elements */
1479
115
    if (Z_TYPE_P(zv) == IS_INDIRECT) {
1480
90
      zv = Z_INDIRECT_P(zv);
1481
90
      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
90
      if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) {
1488
90
        zend_property_info *prop_info =
1489
90
          zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv);
1490
90
        if (prop_info) {
1491
63
          ZVAL_NEW_REF(zv, zv);
1492
63
          ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info);
1493
63
        }
1494
90
      }
1495
90
    }
1496
1497
    /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
1498
115
    ZVAL_MAKE_REF(zv);
1499
1500
    /* Retrieve key */
1501
115
    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
115
    zend_hash_move_forward_ex(target_hash, &pos);
1506
1507
    /* Back up hash position, as it may change */
1508
115
    EG(ht_iterators)[ht_iter].pos = pos;
1509
1510
115
    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
115
    } else {
1534
115
      ZVAL_COPY(&args[0], zv);
1535
1536
      /* Call the userland function */
1537
115
      result = zend_call_function(&fci, &context->fci_cache);
1538
115
      if (result == SUCCESS) {
1539
115
        zval_ptr_dtor(&retval);
1540
115
      }
1541
1542
115
      zval_ptr_dtor(&args[0]);
1543
115
    }
1544
1545
115
    if (Z_TYPE(args[1]) != IS_UNDEF) {
1546
115
      zval_ptr_dtor(&args[1]);
1547
115
      ZVAL_UNDEF(&args[1]);
1548
115
    }
1549
1550
115
    if (result == FAILURE) {
1551
0
      break;
1552
0
    }
1553
1554
    /* Reload array and position -- both may have changed */
1555
115
    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
90
    } else if (Z_TYPE_P(array) == IS_OBJECT) {
1559
90
      target_hash = Z_OBJPROP_P(array);
1560
90
      pos = zend_hash_iterator_pos(ht_iter, target_hash);
1561
90
    } 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
115
  } while (!EG(exception));
1567
1568
29
  if (userdata) {
1569
0
    zval_ptr_dtor(&args[2]);
1570
0
  }
1571
29
  zend_hash_iterator_del(ht_iter);
1572
29
  return result;
1573
29
}
1574
1575
/* {{{ Apply a user function to every member of an array */
1576
PHP_FUNCTION(array_walk)
1577
36
{
1578
36
  zval *array;
1579
36
  zval *userdata = NULL;
1580
36
  php_array_walk_context context;
1581
1582
108
  ZEND_PARSE_PARAMETERS_START(2, 3)
1583
144
    Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
1584
170
    Z_PARAM_FUNC(context.fci, context.fci_cache)
1585
29
    Z_PARAM_OPTIONAL
1586
58
    Z_PARAM_ZVAL(userdata)
1587
58
  ZEND_PARSE_PARAMETERS_END();
1588
1589
29
  php_array_walk(&context, array, userdata, /* recursive */ false);
1590
29
  RETURN_TRUE;
1591
29
}
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
102
{
1619
102
  zval *entry; /* pointer to array entry */
1620
102
  zend_ulong num_idx;
1621
102
  zend_string *str_idx;
1622
1623
102
  if (strict) {
1624
60
    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
60
    } else {
1640
28.5k
      ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1641
28.5k
        ZVAL_DEREF(entry);
1642
28.5k
        if (fast_is_identical_function(value, entry)) {
1643
40
          if (behavior == 0) {
1644
40
            RETURN_TRUE;
1645
40
          } 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
40
        }
1653
28.5k
      } ZEND_HASH_FOREACH_END();
1654
60
    }
1655
60
  } else {
1656
42
    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
42
    } 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
24
          if (behavior == 0) {
1674
24
            RETURN_TRUE;
1675
24
          } 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
24
        }
1683
10.5k
      } ZEND_HASH_FOREACH_END();
1684
29
    } 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
42
  }
1700
1701
25
  RETURN_FALSE;
1702
25
}
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
119
{
1711
119
  zval *value,    /* value to check for */
1712
119
     *array;    /* array to check in */
1713
119
  bool strict = 0;  /* strict comparison or not */
1714
1715
351
  ZEND_PARSE_PARAMETERS_START(2, 3)
1716
452
    Z_PARAM_ZVAL(value)
1717
452
    Z_PARAM_ARRAY(array)
1718
102
    Z_PARAM_OPTIONAL
1719
324
    Z_PARAM_BOOL(strict)
1720
119
  ZEND_PARSE_PARAMETERS_END();
1721
1722
102
  _php_search_array(return_value, value, array, strict, behavior);
1723
102
}
1724
1725
/* {{{ Checks if the given value exists in the array */
1726
PHP_FUNCTION(in_array)
1727
102
{
1728
102
  php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1729
102
}
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
65
{
2578
65
  zval *var_array_param;
2579
65
  zend_long extract_refs;
2580
65
  zend_long extract_type = PHP_EXTR_OVERWRITE;
2581
65
  zend_string *prefix = NULL;
2582
65
  zend_long count;
2583
65
  zend_array *symbol_table;
2584
2585
192
  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
65
  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
33
{
2682
33
  zval *value_ptr, data;
2683
2684
33
  ZVAL_DEREF(entry);
2685
33
  if (Z_TYPE_P(entry) == IS_STRING) {
2686
33
    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
33
    } 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
33
    } else {
2697
33
      php_error_docref_unchecked(NULL, E_WARNING, "Undefined variable $%S", Z_STR_P(entry));
2698
33
    }
2699
33
  } 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
0
  } else {
2714
0
    php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
2715
0
    return;
2716
0
  }
2717
33
}
2718
/* }}} */
2719
2720
/* {{{ Creates a hash containing variables and their values */
2721
PHP_FUNCTION(compact)
2722
41
{
2723
41
  zval *args = NULL;  /* function arguments array */
2724
41
  uint32_t num_args, i;
2725
41
  zend_array *symbol_table;
2726
2727
123
  ZEND_PARSE_PARAMETERS_START(1, -1)
2728
123
    Z_PARAM_VARIADIC('+', args, num_args)
2729
41
  ZEND_PARSE_PARAMETERS_END();
2730
2731
41
  if (zend_forbid_dynamic_call() == FAILURE) {
2732
8
    return;
2733
8
  }
2734
2735
33
  symbol_table = zend_rebuild_symbol_table();
2736
33
  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
33
  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
33
  } else {
2744
33
    array_init_size(return_value, num_args);
2745
33
  }
2746
2747
66
  for (i = 0; i < num_args; i++) {
2748
33
    php_compact_var(symbol_table, return_value, &args[i], i + 1);
2749
33
  }
2750
33
}
2751
/* }}} */
2752
2753
/* {{{ Create an array containing num elements starting with index start_key each initialized to val */
2754
PHP_FUNCTION(array_fill)
2755
175
{
2756
175
  zval *val;
2757
175
  zend_long start_key, num;
2758
2759
525
  ZEND_PARSE_PARAMETERS_START(3, 3)
2760
700
    Z_PARAM_LONG(start_key)
2761
875
    Z_PARAM_LONG(num)
2762
875
    Z_PARAM_ZVAL(val)
2763
875
  ZEND_PARSE_PARAMETERS_END();
2764
2765
175
  if (EXPECTED(num > 0)) {
2766
170
    if (sizeof(num) > 4 && UNEXPECTED(num > INT_MAX)) {
2767
0
      zend_argument_value_error(2, "is too large");
2768
0
      RETURN_THROWS();
2769
170
    } 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
170
    } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
2773
      /* create packed array */
2774
170
      zval *zv;
2775
2776
170
      array_init_size(return_value, (uint32_t)(start_key + num));
2777
170
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
2778
170
      Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
2779
170
      Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
2780
170
      Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
2781
2782
170
      if (Z_REFCOUNTED_P(val)) {
2783
10
        GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
2784
10
      }
2785
2786
170
      zv = Z_ARRVAL_P(return_value)->arPacked;
2787
2788
176
      while (start_key--) {
2789
6
        ZVAL_UNDEF(zv);
2790
6
        zv++;
2791
6
      }
2792
1.59M
      while (num--) {
2793
1.59M
        ZVAL_COPY_VALUE(zv, val);
2794
1.59M
        zv++;
2795
1.59M
      }
2796
170
    } 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
170
  } else if (EXPECTED(num == 0)) {
2810
5
    RETURN_EMPTY_ARRAY();
2811
5
  } else {
2812
0
    zend_argument_value_error(2, "must be greater than or equal to 0");
2813
0
    RETURN_THROWS();
2814
0
  }
2815
175
}
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
17
{
2821
17
  zval *keys, *val, *entry;
2822
2823
51
  ZEND_PARSE_PARAMETERS_START(2, 2)
2824
68
    Z_PARAM_ARRAY(keys)
2825
60
    Z_PARAM_ZVAL(val)
2826
60
  ZEND_PARSE_PARAMETERS_END();
2827
2828
  /* Initialize return array */
2829
12
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
2830
2831
124
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
2832
124
    ZVAL_DEREF(entry);
2833
124
    Z_TRY_ADDREF_P(val);
2834
124
    if (Z_TYPE_P(entry) == IS_LONG) {
2835
20
      zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
2836
36
    } else {
2837
36
      zend_string *tmp_key;
2838
36
      zend_string *key = zval_get_tmp_string(entry, &tmp_key);
2839
36
      zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
2840
36
      zend_tmp_string_release(tmp_key);
2841
36
    }
2842
124
  } ZEND_HASH_FOREACH_END();
2843
12
}
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
484
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
2862
484
    zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
2863
484
    if (__calc_size >= HT_MAX_SIZE - 1) { \
2864
4
      uint64_t __excess = __calc_size - (HT_MAX_SIZE - 1); \
2865
4
      zend_value_error(\
2866
4
        "The supplied range exceeds the maximum array size by %" PRIu64 " elements: " \
2867
4
        "start=" ZEND_LONG_FMT ", end=" ZEND_LONG_FMT ", step=" ZEND_LONG_FMT ". " \
2868
4
        "Calculated size: %" PRIu64 ". Maximum size: %" PRIu64 ".", \
2869
4
        __excess, end, start, (_step), (uint64_t)__calc_size, (uint64_t)HT_MAX_SIZE); \
2870
4
      RETURN_THROWS(); \
2871
4
    } \
2872
484
    size = (uint32_t)(__calc_size + 1); \
2873
480
    array_init_size(return_value, size); \
2874
480
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
2875
480
  } 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
1.02k
{
2885
1.02k
  switch (Z_TYPE_P(input)) {
2886
974
    case IS_LONG:
2887
974
      *lval = Z_LVAL_P(input);
2888
974
      *dval = (double) Z_LVAL_P(input);
2889
974
      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
46
    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
46
      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
46
      uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
2922
46
      if (type == IS_DOUBLE) {
2923
0
        goto check_dval_value;
2924
0
      }
2925
46
      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
46
      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
46
      *lval = 0;
2942
46
      *dval = 0.0;
2943
46
      return IS_STRING;
2944
46
    }
2945
0
    EMPTY_SWITCH_DEFAULT_CASE();
2946
1.02k
  }
2947
1.02k
}
2948
2949
/* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
2950
PHP_FUNCTION(range)
2951
510
{
2952
510
  zval *user_start, *user_end, *user_step = NULL, tmp;
2953
510
  bool is_step_double = false;
2954
510
  bool is_step_negative = false;
2955
510
  double step_double = 1.0;
2956
510
  zend_long step = 1;
2957
2958
1.53k
  ZEND_PARSE_PARAMETERS_START(2, 3)
2959
2.04k
    Z_PARAM_NUMBER_OR_STR(user_start)
2960
2.55k
    Z_PARAM_NUMBER_OR_STR(user_end)
2961
510
    Z_PARAM_OPTIONAL
2962
1.06k
    Z_PARAM_NUMBER(user_step)
2963
510
  ZEND_PARSE_PARAMETERS_END();
2964
2965
510
  if (user_step) {
2966
23
    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
23
    } else {
2988
23
      step = Z_LVAL_P(user_step);
2989
      /* We only want positive step values. */
2990
23
      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
23
      step_double = (double) step;
2999
23
    }
3000
23
    if (step_double == 0.0) {
3001
0
      zend_argument_value_error(3, "cannot be 0");
3002
0
      RETURN_THROWS();
3003
0
    }
3004
23
  }
3005
3006
510
  uint8_t start_type;
3007
510
  double start_double;
3008
510
  zend_long start_long;
3009
510
  uint8_t end_type;
3010
510
  double end_double;
3011
510
  zend_long end_long;
3012
3013
510
  start_type = php_range_process_input(user_start, 1, &start_long, &start_double);
3014
510
  if (start_type == 0) {
3015
0
    RETURN_THROWS();
3016
0
  }
3017
510
  end_type = php_range_process_input(user_end, 2, &end_long, &end_double);
3018
510
  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
510
  if (start_type >= IS_STRING || end_type >= IS_STRING) {
3024
    /* If one of the inputs is NOT a string nor single-byte string */
3025
23
    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
23
    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
23
    unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
3061
23
    unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
3062
3063
    /* Decreasing char range */
3064
23
    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
20
    } else if (high > low) { /* Increasing char range */
3081
20
      if (is_step_negative) {
3082
0
        goto negative_step_error;
3083
0
      }
3084
20
      if (high - low < step) {
3085
0
        goto boundary_error;
3086
0
      }
3087
20
      array_init_size(return_value, (uint32_t)(((high - low) / step) + 1));
3088
20
      zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3089
20
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3090
540
        for (; low <= high; low += (unsigned int)step) {
3091
520
          ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
3092
520
          ZEND_HASH_FILL_NEXT();
3093
520
          if (((signed int)low + step) > 255) {
3094
0
            break;
3095
0
          }
3096
520
        }
3097
20
      } ZEND_HASH_FILL_END();
3098
20
    } 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
23
    return;
3104
23
  }
3105
3106
487
  handle_numeric_inputs:
3107
487
  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
487
  } else {
3147
487
    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
487
    zend_ulong unsigned_step= (zend_ulong)step;
3150
487
    uint32_t i, size;
3151
3152
    /* Decreasing int range */
3153
487
    if (start_long > end_long) {
3154
3
      if ((zend_ulong)start_long - end_long < unsigned_step) {
3155
0
        goto boundary_error;
3156
0
      }
3157
3158
3
      RANGE_CHECK_LONG_INIT_ARRAY(start_long, end_long, unsigned_step);
3159
3160
3
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3161
12
        for (i = 0; i < size; ++i) {
3162
9
          ZEND_HASH_FILL_SET_LONG(start_long - (i * unsigned_step));
3163
9
          ZEND_HASH_FILL_NEXT();
3164
9
        }
3165
3
      } ZEND_HASH_FILL_END();
3166
484
    } else if (end_long > start_long) { /* Increasing int range */
3167
481
      if (is_step_negative) {
3168
0
        goto negative_step_error;
3169
0
      }
3170
481
      if ((zend_ulong)end_long - start_long < unsigned_step) {
3171
0
        goto boundary_error;
3172
0
      }
3173
3174
481
      RANGE_CHECK_LONG_INIT_ARRAY(end_long, start_long, unsigned_step);
3175
3176
477
      ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3177
740k
        for (i = 0; i < size; ++i) {
3178
739k
          ZEND_HASH_FILL_SET_LONG(start_long + (i * unsigned_step));
3179
739k
          ZEND_HASH_FILL_NEXT();
3180
739k
        }
3181
477
      } ZEND_HASH_FILL_END();
3182
477
    } 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
487
  }
3188
483
  return;
3189
3190
483
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
702
{
3314
702
  HashTable    out_hash;      /* Output hashtable */
3315
702
  zend_long  num_in;      /* Number of entries in the input hashtable */
3316
702
  zend_long  pos;       /* Current position in the hashtable */
3317
702
  uint32_t     idx;
3318
702
  zval    *entry;       /* Hash entry */
3319
702
  uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
3320
3321
  /* Get number of entries in the input hash */
3322
702
  num_in = zend_hash_num_elements(in_hash);
3323
3324
  /* Clamp the offset.. */
3325
702
  if (offset > num_in) {
3326
23
    offset = num_in;
3327
679
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3328
0
    offset = 0;
3329
0
  }
3330
3331
  /* ..and the length */
3332
702
  if (length < 0) {
3333
6
    length = num_in - offset + length;
3334
696
  } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
3335
52
    length = num_in - offset;
3336
52
  }
3337
3338
  /* Create and initialize output hash */
3339
702
  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
702
  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
633
    entry = in_hash->arPacked;
3344
885
    for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++, entry++) {
3345
252
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3346
3347
252
      zend_hash_next_index_insert_new(&out_hash, entry);
3348
252
      if (idx == iter_pos) {
3349
47
        if ((zend_long)idx != pos) {
3350
0
          zend_hash_iterators_update(in_hash, idx, pos);
3351
0
        }
3352
47
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3353
47
      }
3354
252
      pos++;
3355
252
    }
3356
3357
    /* If hash for removed entries exists, go until offset+length and copy the entries to it */
3358
633
    if (removed != NULL) {
3359
200
      for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3360
97
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3361
97
        pos++;
3362
97
        Z_TRY_ADDREF_P(entry);
3363
97
        zend_hash_next_index_insert_new(removed, entry);
3364
97
        zend_hash_packed_del_val(in_hash, entry);
3365
        /* Bump iterator positions to the element after replacement. */
3366
97
        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
97
      }
3371
530
    } else { /* otherwise just skip those entries */
3372
530
      zend_long pos2 = pos;
3373
3374
1.60k
      for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3375
1.07k
        if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3376
1.07k
        pos2++;
3377
1.07k
        zend_hash_packed_del_val(in_hash, entry);
3378
        /* Bump iterator positions to the element after replacement. */
3379
1.07k
        if (idx == iter_pos) {
3380
51
          zend_hash_iterators_update(in_hash, idx, offset + length);
3381
51
          iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3382
51
        }
3383
1.07k
      }
3384
530
    }
3385
3386
    /* If there are entries to insert.. */
3387
633
    if (replace) {
3388
432
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3389
432
        Z_TRY_ADDREF_P(entry);
3390
432
        zend_hash_next_index_insert_new(&out_hash, entry);
3391
432
        pos++;
3392
432
      } ZEND_HASH_FOREACH_END();
3393
74
    }
3394
3395
    /* Copy the remaining input hash entries to the output hash */
3396
633
    entry = in_hash->arPacked + idx;
3397
849
    for ( ; idx < in_hash->nNumUsed ; idx++, entry++) {
3398
216
      if (Z_TYPE_P(entry) == IS_UNDEF) continue;
3399
216
      zend_hash_next_index_insert_new(&out_hash, entry);
3400
216
      if (idx == iter_pos) {
3401
105
        if ((zend_long)idx != pos) {
3402
91
          zend_hash_iterators_update(in_hash, idx, pos);
3403
91
        }
3404
105
        iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
3405
105
      }
3406
216
      pos++;
3407
216
    }
3408
633
  } 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
261
      ZEND_HASH_FOREACH_VAL(replace, entry) {
3459
261
        Z_TRY_ADDREF_P(entry);
3460
261
        zend_hash_next_index_insert_new(&out_hash, entry);
3461
261
        pos++;
3462
261
      } 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
702
  HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
3486
702
  HT_SET_ITERATORS_COUNT(in_hash, 0);
3487
702
  in_hash->pDestructor = NULL;
3488
702
  zend_hash_destroy(in_hash);
3489
3490
702
  HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
3491
702
  in_hash->nTableSize        = out_hash.nTableSize;
3492
702
  in_hash->nTableMask        = out_hash.nTableMask;
3493
702
  in_hash->nNumUsed          = out_hash.nNumUsed;
3494
702
  in_hash->nNumOfElements    = out_hash.nNumOfElements;
3495
702
  in_hash->nNextFreeElement  = out_hash.nNextFreeElement;
3496
702
  in_hash->arData            = out_hash.arData;
3497
702
  in_hash->pDestructor       = out_hash.pDestructor;
3498
3499
702
  zend_hash_internal_pointer_reset(in_hash);
3500
702
}
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
82
{
3535
82
  zval *stack,  /* Input stack */
3536
82
     *val;    /* Value to be popped */
3537
82
  uint32_t idx;
3538
3539
246
  ZEND_PARSE_PARAMETERS_START(1, 1)
3540
328
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3541
82
  ZEND_PARSE_PARAMETERS_END();
3542
3543
82
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3544
0
    return;
3545
0
  }
3546
3547
82
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3548
    /* Get the last value and copy it into the return value */
3549
62
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3550
62
    while (1) {
3551
62
      if (idx == 0) {
3552
0
        return;
3553
0
      }
3554
62
      idx--;
3555
62
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3556
62
      if (Z_TYPE_P(val) != IS_UNDEF) {
3557
62
        break;
3558
62
      }
3559
62
    }
3560
62
    RETVAL_COPY_VALUE(val);
3561
62
    ZVAL_UNDEF(val);
3562
3563
62
    if (idx == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3564
62
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3565
62
    }
3566
3567
    /* Delete the last value */
3568
62
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3569
62
  } else {
3570
20
    Bucket *p;
3571
3572
    /* Get the last value and copy it into the return value */
3573
20
    idx = Z_ARRVAL_P(stack)->nNumUsed;
3574
20
    while (1) {
3575
20
      if (idx == 0) {
3576
0
        return;
3577
0
      }
3578
20
      idx--;
3579
20
      p = Z_ARRVAL_P(stack)->arData + idx;
3580
20
      val = &p->val;
3581
20
      if (Z_TYPE_P(val) != IS_UNDEF) {
3582
20
        break;
3583
20
      }
3584
20
    }
3585
20
    RETVAL_COPY_VALUE(val);
3586
20
    ZVAL_UNDEF(val);
3587
3588
20
    if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
3589
20
      Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
3590
20
    }
3591
3592
    /* Delete the last value */
3593
20
    zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
3594
20
  }
3595
82
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3596
3597
82
  if (Z_ISREF_P(return_value)) {
3598
0
    zend_unwrap_reference(return_value);
3599
0
  }
3600
82
}
3601
/* }}} */
3602
3603
/* {{{ Pops an element off the beginning of the array */
3604
PHP_FUNCTION(array_shift)
3605
195
{
3606
195
  zval *stack,  /* Input stack */
3607
195
     *val;    /* Value to be popped */
3608
195
  uint32_t idx;
3609
3610
585
  ZEND_PARSE_PARAMETERS_START(1, 1)
3611
780
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3612
195
  ZEND_PARSE_PARAMETERS_END();
3613
3614
195
  if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
3615
7
    return;
3616
7
  }
3617
3618
  /* re-index like it did before */
3619
188
  if (HT_IS_PACKED(Z_ARRVAL_P(stack))) {
3620
188
    uint32_t k = 0;
3621
3622
    /* Get the first value and copy it into the return value */
3623
188
    idx = 0;
3624
188
    while (1) {
3625
188
      if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
3626
0
        return;
3627
0
      }
3628
188
      val = Z_ARRVAL_P(stack)->arPacked + idx;
3629
188
      if (Z_TYPE_P(val) != IS_UNDEF) {
3630
188
        break;
3631
188
      }
3632
0
      idx++;
3633
0
    }
3634
188
    RETVAL_COPY_VALUE(val);
3635
188
    ZVAL_UNDEF(val);
3636
3637
    /* Delete the first value */
3638
188
    zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
3639
3640
188
    if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3641
355
      for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
3642
187
        val = Z_ARRVAL_P(stack)->arPacked + idx;
3643
187
        if (Z_TYPE_P(val) == IS_UNDEF) continue;
3644
109
        if (idx != k) {
3645
109
          zval *q = Z_ARRVAL_P(stack)->arPacked + k;
3646
109
          ZVAL_COPY_VALUE(q, val);
3647
109
          ZVAL_UNDEF(val);
3648
109
        }
3649
109
        k++;
3650
109
      }
3651
168
    } 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
188
    Z_ARRVAL_P(stack)->nNumUsed = k;
3670
188
    Z_ARRVAL_P(stack)->nNextFreeElement = k;
3671
188
  } 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
188
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3714
3715
188
  if (Z_ISREF_P(return_value)) {
3716
20
    zend_unwrap_reference(return_value);
3717
20
  }
3718
188
}
3719
/* }}} */
3720
3721
/* {{{ Pushes elements onto the beginning of the array */
3722
PHP_FUNCTION(array_unshift)
3723
15
{
3724
15
  zval   *args,     /* Function arguments array */
3725
15
       *stack;      /* Input stack */
3726
15
  HashTable new_hash;   /* New hashtable for the stack */
3727
15
  uint32_t argc;      /* Number of function arguments */
3728
15
  zend_string *key;
3729
15
  zval *value;
3730
3731
45
  ZEND_PARSE_PARAMETERS_START(1, -1)
3732
60
    Z_PARAM_ARRAY_EX(stack, 0, 1)
3733
12
    Z_PARAM_VARIADIC('+', args, argc)
3734
15
  ZEND_PARSE_PARAMETERS_END();
3735
3736
12
  zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
3737
57
  for (uint32_t i = 0; i < argc; i++) {
3738
45
    Z_TRY_ADDREF(args[i]);
3739
45
    zend_hash_next_index_insert_new(&new_hash, &args[i]);
3740
45
  }
3741
3742
42
  ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
3743
42
    if (key) {
3744
0
      zend_hash_add_new(&new_hash, key, value);
3745
15
    } else {
3746
15
      zend_hash_next_index_insert_new(&new_hash, value);
3747
15
    }
3748
42
  } ZEND_HASH_FOREACH_END();
3749
3750
12
  if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
3751
5
    zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
3752
5
    HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
3753
5
    HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
3754
5
  }
3755
3756
  /* replace HashTable data */
3757
12
  Z_ARRVAL_P(stack)->pDestructor = NULL;
3758
12
  zend_hash_destroy(Z_ARRVAL_P(stack));
3759
3760
12
  HT_FLAGS(Z_ARRVAL_P(stack))          = HT_FLAGS(&new_hash);
3761
12
  Z_ARRVAL_P(stack)->nTableSize        = new_hash.nTableSize;
3762
12
  Z_ARRVAL_P(stack)->nTableMask        = new_hash.nTableMask;
3763
12
  Z_ARRVAL_P(stack)->nNumUsed          = new_hash.nNumUsed;
3764
12
  Z_ARRVAL_P(stack)->nNumOfElements    = new_hash.nNumOfElements;
3765
12
  Z_ARRVAL_P(stack)->nNextFreeElement  = new_hash.nNextFreeElement;
3766
12
  Z_ARRVAL_P(stack)->arData            = new_hash.arData;
3767
12
  Z_ARRVAL_P(stack)->pDestructor       = new_hash.pDestructor;
3768
3769
12
  zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3770
3771
  /* Clean up and return the number of elements in the stack */
3772
12
  RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
3773
12
}
3774
/* }}} */
3775
3776
/* {{{ Removes the elements designated by offset and length and replace them with supplied array */
3777
PHP_FUNCTION(array_splice)
3778
712
{
3779
712
  zval *array,        /* Input array */
3780
712
     *repl_array = NULL;  /* Replacement array */
3781
712
  HashTable  *rem_hash = NULL;
3782
712
  zend_long offset,
3783
712
      length = 0;
3784
712
  bool length_is_null = 1;
3785
712
  int   num_in;       /* Number of elements in the input array */
3786
3787
2.13k
  ZEND_PARSE_PARAMETERS_START(2, 4)
3788
2.84k
    Z_PARAM_ARRAY_EX(array, 0, 1)
3789
3.52k
    Z_PARAM_LONG(offset)
3790
702
    Z_PARAM_OPTIONAL
3791
2.60k
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3792
2.07k
    Z_PARAM_ZVAL(repl_array)
3793
2.07k
  ZEND_PARSE_PARAMETERS_END();
3794
3795
702
  num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
3796
3797
702
  if (length_is_null) {
3798
103
    length = num_in;
3799
103
  }
3800
3801
702
  if (ZEND_NUM_ARGS() == 4) {
3802
    /* Make sure the last argument, if passed, is an array */
3803
137
    convert_to_array(repl_array);
3804
137
  }
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
702
  if (USED_RET()) {
3809
103
    zend_long size = length;
3810
3811
    /* Clamp the offset.. */
3812
103
    if (offset > num_in) {
3813
3
      offset = num_in;
3814
100
    } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3815
0
      offset = 0;
3816
0
    }
3817
3818
    /* ..and the length */
3819
103
    if (length < 0) {
3820
0
      size = num_in - offset + length;
3821
103
    } 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
103
    array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
3827
103
    rem_hash = Z_ARRVAL_P(return_value);
3828
599
  } else {
3829
    /* The return value will not be used, but make sure it still has the correct type. */
3830
599
    RETVAL_EMPTY_ARRAY();
3831
599
  }
3832
3833
  /* Perform splice */
3834
702
  php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
3835
702
}
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
17
{
3842
17
  zend_long pos;
3843
17
  Bucket *bucket;
3844
17
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3845
17
  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
17
    return ht->arData + offset;
3849
17
  }
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
114
{
3869
114
  zend_long pos;
3870
114
  zval *zv;
3871
114
  ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
3872
114
  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
114
    return ht->arPacked + offset;
3876
114
  }
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
145
{
3895
145
  zval *input;                  /* Input array */
3896
145
  zval *entry;                  /* An array entry */
3897
145
  zend_long offset;             /* Offset to get elements from */
3898
145
  zend_long length = 0;         /* How many elements to get */
3899
145
  bool length_is_null = 1; /* Whether an explicit length has been omitted */
3900
145
  bool preserve_keys = 0;  /* Whether to preserve keys while copying to the new array */
3901
145
  uint32_t num_in;              /* Number of elements in the input array */
3902
145
  zend_string *string_key;
3903
145
  zend_ulong num_key;
3904
3905
435
  ZEND_PARSE_PARAMETERS_START(2, 4)
3906
580
    Z_PARAM_ARRAY(input)
3907
680
    Z_PARAM_LONG(offset)
3908
136
    Z_PARAM_OPTIONAL
3909
534
    Z_PARAM_LONG_OR_NULL(length, length_is_null)
3910
427
    Z_PARAM_BOOL(preserve_keys)
3911
145
  ZEND_PARSE_PARAMETERS_END();
3912
3913
  /* Get number of entries in the input hash */
3914
136
  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
136
  if (length_is_null) {
3918
29
    length = num_in;
3919
29
  }
3920
3921
  /* Clamp the offset.. */
3922
136
  if (offset > (zend_long) num_in) {
3923
2
    RETURN_EMPTY_ARRAY();
3924
134
  } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
3925
0
    offset = 0;
3926
0
  }
3927
3928
  /* ..and the length */
3929
134
  if (length < 0) {
3930
0
    length = num_in - offset + length;
3931
134
  } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
3932
20
    length = num_in - offset;
3933
20
  }
3934
3935
134
  if (length <= 0) {
3936
3
    RETURN_EMPTY_ARRAY();
3937
3
  }
3938
3939
  /* Initialize returned array */
3940
131
  array_init_size(return_value, (uint32_t)length);
3941
3942
  // Contains modified variants of ZEND_HASH_FOREACH_VAL
3943
131
  {
3944
131
    HashTable *ht = Z_ARRVAL_P(input);
3945
3946
    /* Start at the beginning and go until we hit offset */
3947
131
    if (HT_IS_PACKED(ht)) {
3948
114
      zval *zv = find_packed_val_at_offset(ht, offset);
3949
114
      zval *end = ht->arPacked + ht->nNumUsed;
3950
3951
114
      if (!preserve_keys
3952
114
       || (offset == 0 && HT_IS_WITHOUT_HOLES(ht))) {
3953
114
        zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3954
114
        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3955
249
          for (; zv != end; zv++) {
3956
152
            if (__fill_idx >= length) {
3957
17
              break;
3958
17
            }
3959
135
            if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) {
3960
0
              continue;
3961
0
            }
3962
135
            entry = zv;
3963
135
            if (UNEXPECTED(Z_ISREF_P(entry)) &&
3964
135
              UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
3965
22
              entry = Z_REFVAL_P(entry);
3966
22
            }
3967
135
            Z_TRY_ADDREF_P(entry);
3968
135
            ZEND_HASH_FILL_ADD(entry);
3969
135
          }
3970
114
        } ZEND_HASH_FILL_END();
3971
114
      } 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
114
    } else {
3988
17
      zend_long n = 0;  /* Current number of elements */
3989
17
      Bucket *p = find_bucket_at_offset(ht, offset);
3990
17
      Bucket *end = ht->arData + ht->nNumUsed;
3991
3992
41
      for (; p != end; p++) {
3993
24
        entry = &p->val;
3994
24
        if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
3995
0
          continue;
3996
0
        }
3997
24
        if (n >= length) {
3998
0
          break;
3999
0
        }
4000
24
        n++;
4001
24
        num_key = p->h;
4002
24
        string_key = p->key;
4003
4004
24
        if (string_key) {
4005
17
          entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
4006
17
        } else {
4007
7
          if (preserve_keys) {
4008
7
            entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
4009
7
          } else {
4010
0
            entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
4011
0
          }
4012
7
        }
4013
24
        zval_add_ref(entry);
4014
24
      }
4015
17
    }
4016
131
  }
4017
131
}
4018
/* }}} */
4019
4020
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
4021
17
{
4022
17
  zval *src_entry, *dest_entry;
4023
17
  zend_string *string_key;
4024
4025
51
  ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4026
51
    if (string_key) {
4027
12
      if ((dest_entry = zend_hash_find_known_hash(dest, string_key)) != NULL) {
4028
12
        zval *src_zval = src_entry;
4029
12
        zval *dest_zval = dest_entry;
4030
12
        HashTable *thash;
4031
12
        zval tmp;
4032
12
        int ret;
4033
4034
12
        ZVAL_DEREF(src_zval);
4035
12
        ZVAL_DEREF(dest_zval);
4036
12
        thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
4037
12
        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
12
        ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
4043
12
        dest_zval = dest_entry;
4044
4045
12
        if (Z_TYPE_P(dest_zval) == IS_NULL) {
4046
0
          convert_to_array(dest_zval);
4047
0
          add_next_index_null(dest_zval);
4048
12
        } else {
4049
12
          convert_to_array(dest_zval);
4050
12
        }
4051
12
        SEPARATE_ZVAL(dest_zval);
4052
4053
12
        ZVAL_UNDEF(&tmp);
4054
12
        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
12
        if (Z_TYPE_P(src_zval) == IS_ARRAY) {
4060
5
          if (thash) {
4061
5
            GC_TRY_PROTECT_RECURSION(thash);
4062
5
          }
4063
5
          ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
4064
5
          if (thash) {
4065
5
            GC_TRY_UNPROTECT_RECURSION(thash);
4066
5
          }
4067
5
          if (!ret) {
4068
5
            return 0;
4069
5
          }
4070
7
        } else {
4071
7
          Z_TRY_ADDREF_P(src_zval);
4072
7
          zval *zv = zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
4073
7
          if (EXPECTED(!zv)) {
4074
7
            Z_TRY_DELREF_P(src_zval);
4075
7
            zend_cannot_add_element();
4076
7
            return 0;
4077
7
          }
4078
7
        }
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
12
    } else {
4085
5
      zval *zv = zend_hash_next_index_insert(dest, src_entry);
4086
5
      if (UNEXPECTED(!zv)) {
4087
5
        zend_cannot_add_element();
4088
5
        return 0;
4089
5
      }
4090
0
      zval_add_ref(zv);
4091
0
    }
4092
51
  } ZEND_HASH_FOREACH_END();
4093
0
  return 1;
4094
17
}
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
31
{
4254
31
  zval *args = NULL;
4255
31
  zval *arg;
4256
31
  uint32_t argc, i;
4257
31
  zval *src_entry;
4258
31
  HashTable *src, *dest;
4259
31
  uint32_t count = 0;
4260
4261
93
  ZEND_PARSE_PARAMETERS_START(0, -1)
4262
93
    Z_PARAM_VARIADIC('+', args, argc)
4263
31
  ZEND_PARSE_PARAMETERS_END();
4264
4265
26
  if (argc == 0) {
4266
0
    RETURN_EMPTY_ARRAY();
4267
0
  }
4268
4269
68
  for (i = 0; i < argc; i++) {
4270
47
    zval *arg = args + i;
4271
4272
47
    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
42
    count += zend_hash_num_elements(Z_ARRVAL_P(arg));
4277
42
  }
4278
4279
21
  if (argc == 2) {
4280
21
    zval *ret = NULL;
4281
4282
21
    if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
4283
9
      ret = &args[1];
4284
12
    } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
4285
0
      ret = &args[0];
4286
0
    }
4287
21
    if (ret) {
4288
9
      if (HT_IS_PACKED(Z_ARRVAL_P(ret))) {
4289
9
        if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
4290
9
          ZVAL_COPY(return_value, ret);
4291
9
          return;
4292
9
        }
4293
9
      } 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
9
    }
4309
21
  }
4310
4311
12
  arg = args;
4312
12
  src  = Z_ARRVAL_P(arg);
4313
  /* copy first array if necessary */
4314
12
  bool in_place = false;
4315
12
  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
12
  } else {
4338
12
    array_init_size(return_value, count);
4339
12
    dest = Z_ARRVAL_P(return_value);
4340
4341
12
    zend_string *string_key;
4342
12
    zend_hash_real_init_mixed(dest);
4343
48
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
4344
48
      if (UNEXPECTED(Z_ISREF_P(src_entry) &&
4345
48
        Z_REFCOUNT_P(src_entry) == 1)) {
4346
0
        src_entry = Z_REFVAL_P(src_entry);
4347
0
      }
4348
48
      Z_TRY_ADDREF_P(src_entry);
4349
48
      if (EXPECTED(string_key)) {
4350
12
        _zend_hash_append(dest, string_key, src_entry);
4351
12
      } else {
4352
0
        zend_hash_next_index_insert_new(dest, src_entry);
4353
0
      }
4354
48
    } ZEND_HASH_FOREACH_END();
4355
12
  }
4356
12
  if (recursive) {
4357
24
    for (i = 1; i < argc; i++) {
4358
12
      arg = args + i;
4359
12
      php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
4360
12
    }
4361
12
  } 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
12
  if (in_place) {
4369
0
    GC_ADDREF(dest);
4370
0
  }
4371
12
}
4372
/* }}} */
4373
4374
/* {{{ Merges elements from passed arrays into one array */
4375
PHP_FUNCTION(array_merge)
4376
19
{
4377
19
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4378
19
}
4379
/* }}} */
4380
4381
/* {{{ Recursively merges elements from passed arrays into one array */
4382
PHP_FUNCTION(array_merge_recursive)
4383
12
{
4384
12
  php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4385
12
}
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
27
{
4405
27
  zval *input,        /* Input array */
4406
27
       *search_value = NULL,  /* Value to search for */
4407
27
       *entry,        /* An entry in the input array */
4408
27
         new_val;       /* New value */
4409
27
  bool strict = 0;    /* do strict comparison */
4410
27
  zend_ulong num_idx;
4411
27
  zend_string *str_idx;
4412
27
  zend_array *arrval;
4413
27
  zend_ulong elem_count;
4414
4415
81
  ZEND_PARSE_PARAMETERS_START(1, 3)
4416
108
    Z_PARAM_ARRAY(input)
4417
27
    Z_PARAM_OPTIONAL
4418
74
    Z_PARAM_ZVAL(search_value)
4419
74
    Z_PARAM_BOOL(strict)
4420
27
  ZEND_PARSE_PARAMETERS_END();
4421
27
  arrval = Z_ARRVAL_P(input);
4422
27
  elem_count = zend_hash_num_elements(arrval);
4423
4424
  /* Base case: empty input */
4425
27
  if (!elem_count) {
4426
0
    RETURN_COPY(input);
4427
0
  }
4428
4429
  /* Initialize return array */
4430
27
  if (search_value != NULL) {
4431
10
    array_init(return_value);
4432
4433
10
    if (strict) {
4434
50
      ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
4435
50
        ZVAL_DEREF(entry);
4436
50
        if (fast_is_identical_function(search_value, entry)) {
4437
15
          if (str_idx) {
4438
0
            ZVAL_STR_COPY(&new_val, str_idx);
4439
15
          } else {
4440
15
            ZVAL_LONG(&new_val, num_idx);
4441
15
          }
4442
15
          zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
4443
15
        }
4444
50
      } ZEND_HASH_FOREACH_END();
4445
10
    } 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
17
  } else {
4458
17
    array_init_size(return_value, elem_count);
4459
17
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4460
17
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4461
17
      if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4462
        /* Optimistic case: range(0..n-1) for vector-like packed array */
4463
10
        zend_ulong lval = 0;
4464
4465
46
        for (; lval < elem_count; ++lval) {
4466
36
          ZEND_HASH_FILL_SET_LONG(lval);
4467
36
          ZEND_HASH_FILL_NEXT();
4468
36
        }
4469
10
      } 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
17
    } ZEND_HASH_FILL_END();
4481
17
  }
4482
27
}
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
34
{
4723
34
  zval   *input,        /* Input array */
4724
34
       *entry;        /* An entry in the input array */
4725
34
  zend_string *string_key;
4726
34
  zend_ulong    num_key;
4727
34
  bool preserve_keys = 0; /* whether to preserve keys */
4728
4729
102
  ZEND_PARSE_PARAMETERS_START(1, 2)
4730
136
    Z_PARAM_ARRAY(input)
4731
31
    Z_PARAM_OPTIONAL
4732
62
    Z_PARAM_BOOL(preserve_keys)
4733
34
  ZEND_PARSE_PARAMETERS_END();
4734
4735
  /* Initialize return array */
4736
31
  array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4737
31
  if (HT_IS_PACKED(Z_ARRVAL_P(input)) && !preserve_keys) {
4738
31
    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
4739
31
    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4740
1.33k
      ZEND_HASH_PACKED_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4741
1.33k
        if (UNEXPECTED(Z_ISREF_P(entry) &&
4742
1.33k
          Z_REFCOUNT_P(entry) == 1)) {
4743
21
          entry = Z_REFVAL_P(entry);
4744
21
        }
4745
1.33k
        Z_TRY_ADDREF_P(entry);
4746
1.33k
        ZEND_HASH_FILL_ADD(entry);
4747
1.33k
      } ZEND_HASH_FOREACH_END();
4748
31
    } ZEND_HASH_FILL_END();
4749
31
  } 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
31
}
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
349
{
4934
349
  struct bucketindex *f = (struct bucketindex *)p;
4935
349
  struct bucketindex *g = (struct bucketindex *)q;
4936
349
  struct bucketindex t;
4937
349
  t = *f;
4938
349
  *f = *g;
4939
349
  *g = t;
4940
349
}
4941
4942
/* {{{ Removes duplicate values from array */
4943
PHP_FUNCTION(array_unique)
4944
35
{
4945
35
  zval *array;
4946
35
  Bucket *p;
4947
35
  zend_long sort_type = PHP_SORT_STRING;
4948
35
  bucket_compare_func_t cmp;
4949
35
  struct bucketindex *arTmp, *cmpdata, *lastkept;
4950
35
  uint32_t i, idx;
4951
4952
105
  ZEND_PARSE_PARAMETERS_START(1, 2)
4953
140
    Z_PARAM_ARRAY(array)
4954
35
    Z_PARAM_OPTIONAL
4955
108
    Z_PARAM_LONG(sort_type)
4956
35
  ZEND_PARSE_PARAMETERS_END();
4957
4958
35
  if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
4959
0
    ZVAL_COPY(return_value, array);
4960
0
    return;
4961
0
  }
4962
4963
35
  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
19
  cmp = php_get_data_compare_func_unstable(sort_type, 0);
5003
5004
19
  bool in_place = zend_may_modify_arg_in_place(array);
5005
19
  if (in_place) {
5006
0
    RETVAL_ARR(Z_ARRVAL_P(array));
5007
19
  } else {
5008
19
    RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
5009
19
  }
5010
5011
  /* create and sort array with pointers to the target_hash buckets */
5012
19
  arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5013
19
  if (HT_IS_PACKED(Z_ARRVAL_P(array))) {
5014
19
    zval *zv = Z_ARRVAL_P(array)->arPacked;
5015
189
    for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++, zv++) {
5016
170
      if (Z_TYPE_P(zv) == IS_UNDEF) continue;
5017
170
      ZVAL_COPY_VALUE(&arTmp[i].b.val, zv);
5018
170
      arTmp[i].b.h = idx;
5019
170
      arTmp[i].b.key = NULL;
5020
170
      arTmp[i].i = i;
5021
170
      i++;
5022
170
    }
5023
19
  } 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
19
  ZVAL_UNDEF(&arTmp[i].b.val);
5033
19
  zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
5034
19
      (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);
5035
  /* go through the sorted array and delete duplicates from the copy */
5036
19
  lastkept = arTmp;
5037
170
  for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
5038
151
    if (cmp(&lastkept->b, &cmpdata->b)) {
5039
121
      lastkept = cmpdata;
5040
121
    } else {
5041
30
      if (lastkept->i > cmpdata->i) {
5042
0
        p = &lastkept->b;
5043
0
        lastkept = cmpdata;
5044
30
      } else {
5045
30
        p = &cmpdata->b;
5046
30
      }
5047
30
      if (p->key == NULL) {
5048
30
        zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
5049
30
      } else {
5050
0
        zend_hash_del(Z_ARRVAL_P(return_value), p->key);
5051
0
      }
5052
30
    }
5053
151
  }
5054
19
  pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
5055
5056
19
  if (in_place) {
5057
0
    Z_ADDREF_P(return_value);
5058
0
  }
5059
19
}
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
14
#define MULTISORT_TYPE  1
5990
693
#define MULTISORT_LAST  2
5991
5992
PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
5993
160
{
5994
160
  Bucket *ab = *(Bucket **)a;
5995
160
  Bucket *bb = *(Bucket **)b;
5996
160
  int r;
5997
160
  zend_long result;
5998
5999
160
  r = 0;
6000
160
  do {
6001
160
    result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
6002
160
    if (result != 0) {
6003
149
      return result > 0 ? 1 : -1;
6004
149
    }
6005
11
    r++;
6006
11
  } while (Z_TYPE(ab[r].val) != IS_UNDEF);
6007
6008
11
  return stable_sort_fallback(&ab[r], &bb[r]);
6009
160
}
6010
/* }}} */
6011
6012
#define MULTISORT_ABORT       \
6013
5
  efree(func); \
6014
5
  efree(arrays);         \
6015
0
  return;
6016
6017
84
static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
6018
84
  Bucket *t;
6019
84
  Bucket **f = (Bucket **)p;
6020
84
  Bucket **g = (Bucket **)q;
6021
6022
84
  t = *f;
6023
84
  *f = *g;
6024
84
  *g = t;
6025
84
}
6026
/* }}} */
6027
6028
/* {{{ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
6029
PHP_FUNCTION(array_multisort)
6030
118
{
6031
118
  zval*     args;
6032
118
  zval**      arrays;
6033
118
  Bucket**    indirect;
6034
118
  uint32_t    idx;
6035
118
  HashTable*    hash;
6036
118
  uint32_t    argc;
6037
118
  uint32_t    array_size;
6038
118
  uint32_t    num_arrays = 0;
6039
118
  int       parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
6040
118
  int       sort_order = PHP_SORT_ASC;
6041
118
  int       sort_type  = PHP_SORT_REGULAR;
6042
118
  uint32_t    i, k, n;
6043
118
  bucket_compare_func_t *func;
6044
6045
347
  ZEND_PARSE_PARAMETERS_START(1, -1)
6046
347
    Z_PARAM_VARIADIC('+', args, argc)
6047
118
  ZEND_PARSE_PARAMETERS_END();
6048
6049
  /* Allocate space for storing pointers to input arrays and sort flags. */
6050
111
  arrays = (zval **)ecalloc(argc, sizeof(zval *));
6051
333
  for (i = 0; i < MULTISORT_LAST; i++) {
6052
222
    parse_state[i] = 0;
6053
222
  }
6054
111
  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
252
  for (i = 0; i < argc; i++) {
6062
146
    zval *arg = &args[i];
6063
6064
146
    ZVAL_DEREF(arg);
6065
146
    if (Z_TYPE_P(arg) == IS_ARRAY) {
6066
120
      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
120
      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
120
      arrays[num_arrays++] = arg;
6075
6076
      /* Next one may be an array or a list of sort flags. */
6077
360
      for (k = 0; k < MULTISORT_LAST; k++) {
6078
240
        parse_state[k] = 1;
6079
240
      }
6080
120
    } else if (Z_TYPE_P(arg) == IS_LONG) {
6081
21
      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
7
        case PHP_SORT_STRING:
6098
7
        case PHP_SORT_NATURAL:
6099
7
        case PHP_SORT_LOCALE_STRING:
6100
          /* flag allowed here */
6101
7
          if (parse_state[MULTISORT_TYPE] == 1) {
6102
            /* Save the flag and make sure then next arg is not the current flag. */
6103
7
            sort_type = (int)Z_LVAL_P(arg);
6104
7
            parse_state[MULTISORT_TYPE] = 0;
6105
7
          } 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
7
          break;
6110
6111
7
        default:
6112
0
          zend_argument_value_error(i + 1, "must be a valid sort flag");
6113
0
          MULTISORT_ABORT;
6114
0
          break;
6115
6116
21
      }
6117
21
    } else {
6118
5
      zend_argument_type_error(i + 1, "must be an array or a sort flag");
6119
5
      MULTISORT_ABORT;
6120
0
    }
6121
146
  }
6122
  /* Take care of the last array sort flags. */
6123
106
  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
106
  array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
6127
116
  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
106
  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
101
  indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
6146
  /* Move num_arrays multiplication to size because it's essentially impossible to overflow. */
6147
101
  Bucket *indirects = (Bucket *)safe_emalloc(array_size, sizeof(Bucket) * (num_arrays + 1), 0);
6148
360
  for (i = 0; i < array_size; i++) {
6149
259
    indirect[i] = indirects + (i * (num_arrays + 1));
6150
259
  }
6151
202
  for (i = 0; i < num_arrays; i++) {
6152
101
    k = 0;
6153
101
    if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
6154
69
      zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
6155
267
      for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
6156
198
        if (Z_TYPE_P(zv) == IS_UNDEF) continue;
6157
198
        ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
6158
198
        indirect[k][i].h = idx;
6159
198
        indirect[k][i].key = NULL;
6160
198
        k++;
6161
198
      }
6162
69
    } else {
6163
32
      Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
6164
93
      for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
6165
61
        if (Z_TYPE(p->val) == IS_UNDEF) continue;
6166
61
        indirect[k][i] = *p;
6167
61
        k++;
6168
61
      }
6169
32
    }
6170
101
  }
6171
360
  for (k = 0; k < array_size; k++) {
6172
259
    ZVAL_UNDEF(&indirect[k][num_arrays].val);
6173
259
    Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
6174
259
  }
6175
6176
  /* Do the actual sort magic - bada-bim, bada-boom. */
6177
101
  zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
6178
101
  if (EG(exception)) {
6179
5
    goto clean_up;
6180
5
  }
6181
6182
  /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
6183
192
  for (i = 0; i < num_arrays; i++) {
6184
96
    hash = Z_ARRVAL_P(arrays[i]);
6185
96
    hash->nNumUsed = array_size;
6186
96
    hash->nNextFreeElement = array_size;
6187
96
    hash->nInternalPointer = 0;
6188
96
    if (HT_IS_PACKED(hash)) {
6189
267
      for (k = 0; k < array_size; k++) {
6190
198
        ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
6191
198
      }
6192
69
    } else {
6193
27
      bool repack = true;
6194
6195
78
      for (n = 0, k = 0; k < array_size; k++) {
6196
51
        hash->arData[k] = indirect[k][i];
6197
51
        if (hash->arData[k].key == NULL) {
6198
0
          hash->arData[k].h = n++;
6199
51
        } else {
6200
51
          repack = false;
6201
51
        }
6202
51
      }
6203
27
      if (repack) {
6204
0
        zend_hash_to_packed(hash);
6205
27
      } else {
6206
27
        zend_hash_rehash(hash);
6207
27
      }
6208
27
    }
6209
96
  }
6210
96
  RETVAL_TRUE;
6211
6212
101
clean_up:
6213
101
  efree(indirects);
6214
101
  efree(indirect);
6215
101
  efree(func);
6216
101
  efree(arrays);
6217
101
}
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
331
{
6519
331
  switch (Z_TYPE_P(zv)) {
6520
5
    case IS_TRUE:
6521
5
      return true;
6522
0
    case IS_FALSE:
6523
0
      return false;
6524
326
    default: {
6525
326
      bool rv = zend_is_true(zv);
6526
326
      zval_ptr_dtor(zv);
6527
326
      return rv;
6528
0
    }
6529
331
  }
6530
331
}
6531
6532
/* {{{ Filters elements from the array via the callback. */
6533
PHP_FUNCTION(array_filter)
6534
14
{
6535
14
  zval *array;
6536
14
  zval *operand;
6537
14
  zval *key;
6538
14
  zval args[2];
6539
14
  zval retval;
6540
14
  bool have_callback = 0;
6541
14
  zend_long use_type = 0;
6542
14
  zend_string *string_key;
6543
14
  zend_fcall_info fci = empty_fcall_info;
6544
14
  zend_fcall_info_cache fci_cache;
6545
14
  zend_ulong num_key;
6546
6547
42
  ZEND_PARSE_PARAMETERS_START(1, 3)
6548
56
    Z_PARAM_ARRAY(array)
6549
14
    Z_PARAM_OPTIONAL
6550
56
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6551
42
    Z_PARAM_LONG(use_type)
6552
14
  ZEND_PARSE_PARAMETERS_END();
6553
6554
14
  if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
6555
5
    RETVAL_EMPTY_ARRAY();
6556
5
    return;
6557
5
  }
6558
9
  array_init(return_value);
6559
6560
9
  if (ZEND_FCI_INITIALIZED(fci)) {
6561
9
    have_callback = 1;
6562
9
    fci.retval = &retval;
6563
9
    if (use_type == ARRAY_FILTER_USE_BOTH) {
6564
0
      fci.param_count = 2;
6565
0
      key = &args[1];
6566
9
    } else {
6567
9
      fci.param_count = 1;
6568
9
      key = &args[0];
6569
9
    }
6570
9
  }
6571
6572
679
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, operand) {
6573
679
    if (have_callback) {
6574
335
      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
335
      if (use_type != ARRAY_FILTER_USE_KEY) {
6583
335
        ZVAL_COPY_VALUE(&args[0], operand);
6584
335
      }
6585
335
      fci.params = args;
6586
6587
335
      zend_result result = zend_call_function(&fci, &fci_cache);
6588
335
      ZEND_ASSERT(result == SUCCESS);
6589
6590
333
      if (UNEXPECTED(EG(exception))) {
6591
2
        RETURN_THROWS();
6592
2
      }
6593
6594
331
      if (!php_is_true(&retval)) {
6595
326
        continue;
6596
326
      }
6597
331
    } 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
9
}
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
975
{
6740
975
  zval *arrays = NULL;
6741
975
  int n_arrays = 0;
6742
975
  zval result;
6743
975
  zend_fcall_info fci;
6744
975
  zend_fcall_info_cache fci_cache;
6745
975
  int i;
6746
975
  uint32_t k, maxlen = 0;
6747
6748
2.92k
  ZEND_PARSE_PARAMETERS_START(2, -1)
6749
3.90k
    Z_PARAM_FUNC_OR_NULL(fci, fci_cache)
6750
936
    Z_PARAM_VARIADIC('+', arrays, n_arrays)
6751
975
  ZEND_PARSE_PARAMETERS_END();
6752
6753
936
  if (n_arrays == 1) {
6754
921
    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
916
    const HashTable *input = Z_ARRVAL(arrays[0]);
6759
916
    maxlen = zend_hash_num_elements(input);
6760
6761
    /* Short-circuit: if no callback and only one array, just return it. */
6762
916
    if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
6763
47
      ZVAL_COPY(return_value, &arrays[0]);
6764
47
      return;
6765
47
    }
6766
6767
869
    fci.retval = &result;
6768
869
    fci.param_count = 1;
6769
6770
869
    if (HT_IS_PACKED(input)) {
6771
860
      array_init_size(return_value, input->nNumUsed);
6772
860
      HashTable *output = Z_ARRVAL_P(return_value);
6773
860
      zend_hash_real_init_packed(output);
6774
6775
860
      uint32_t undefs = 0;
6776
860
      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.27k
        for (zval *cur = input->arPacked, *end = input->arPacked + input->nNumUsed; cur != end; cur++) {
6780
1.04k
          if (EXPECTED(!Z_ISUNDEF_P(cur))) {
6781
1.04k
            fci.params = cur;
6782
1.04k
            zend_result ret = zend_call_function(&fci, &fci_cache);
6783
1.04k
            ZEND_ASSERT(ret == SUCCESS);
6784
451
            ZEND_IGNORE_VALUE(ret);
6785
451
            if (UNEXPECTED(Z_ISUNDEF(result))) {
6786
33
              ZEND_HASH_FILL_FINISH();
6787
33
              zend_array_destroy(output);
6788
33
              RETURN_NULL();
6789
33
            }
6790
451
          } else {
6791
0
            ZVAL_UNDEF(&result);
6792
0
            undefs++;
6793
0
          }
6794
418
          ZEND_HASH_FILL_ADD(&result);
6795
418
        }
6796
860
      } ZEND_HASH_FILL_END();
6797
230
      output->nNumOfElements -= undefs;
6798
230
    } 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
869
  } else {
6822
15
    uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
6823
6824
57
    for (i = 0; i < n_arrays; i++) {
6825
42
      if (Z_TYPE(arrays[i]) != IS_ARRAY) {
6826
0
        zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_value_name(&arrays[i]));
6827
0
        efree(array_pos);
6828
0
        RETURN_THROWS();
6829
0
      }
6830
42
      if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
6831
15
        maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
6832
15
      }
6833
42
    }
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
14
                ZVAL_NULL(&zv);
6855
14
                break;
6856
112
              } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
6857
112
                ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
6858
112
                array_pos[i] = pos + 1;
6859
112
                break;
6860
112
              }
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
936
}
6947
/* }}} */
6948
6949
/* {{{ Checks if the given key or index exists in the array */
6950
PHP_FUNCTION(array_key_exists)
6951
3
{
6952
3
  zval *key;
6953
3
  HashTable *ht;
6954
6955
6
  ZEND_PARSE_PARAMETERS_START(2, 2)
6956
6
    Z_PARAM_ZVAL(key)
6957
0
    Z_PARAM_ARRAY_HT(ht)
6958
3
  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
7027
0
  ZVAL_UNDEF(&chunk);
7028
7029
0
  ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
7030
    /* If new chunk, create and initialize it. */
7031
0
    if (Z_TYPE(chunk) == IS_UNDEF) {
7032
0
      array_init_size(&chunk, (uint32_t)size);
7033
0
    }
7034
7035
    /* Add entry to the chunk, preserving keys if necessary. */
7036
0
    if (preserve_keys) {
7037
0
      if (str_key) {
7038
0
        entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
7039
0
      } else {
7040
0
        entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
7041
0
      }
7042
0
    } else {
7043
0
      entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
7044
0
    }
7045
0
    zval_add_ref(entry);
7046
7047
    /* If reached the chunk size, add it to the result array, and reset the
7048
     * pointer. */
7049
0
    if (!(++current % size)) {
7050
0
      add_next_index_zval(return_value, &chunk);
7051
0
      ZVAL_UNDEF(&chunk);
7052
0
    }
7053
0
  } ZEND_HASH_FOREACH_END();
7054
7055
  /* Add the final chunk if there is one. */
7056
0
  if (Z_TYPE(chunk) != IS_UNDEF) {
7057
0
    add_next_index_zval(return_value, &chunk);
7058
0
  }
7059
0
}
7060
/* }}} */
7061
7062
/* {{{ Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
7063
PHP_FUNCTION(array_combine)
7064
0
{
7065
0
  HashTable *values, *keys;
7066
0
  uint32_t pos_values = 0;
7067
0
  zval *entry_keys, *entry_values;
7068
0
  int num_keys, num_values;
7069
7070
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
7071
0
    Z_PARAM_ARRAY_HT(keys)
7072
0
    Z_PARAM_ARRAY_HT(values)
7073
0
  ZEND_PARSE_PARAMETERS_END();
7074
7075
0
  num_keys = zend_hash_num_elements(keys);
7076
0
  num_values = zend_hash_num_elements(values);
7077
7078
0
  if (num_keys != num_values) {
7079
0
    zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
7080
0
    RETURN_THROWS();
7081
0
  }
7082
7083
0
  if (!num_keys) {
7084
0
    RETURN_EMPTY_ARRAY();
7085
0
  }
7086
7087
0
  array_init_size(return_value, num_keys);
7088
0
  ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
7089
0
    while (1) {
7090
0
      if (pos_values >= values->nNumUsed) {
7091
0
        break;
7092
0
      }
7093
0
      entry_values = ZEND_HASH_ELEMENT(values, pos_values);
7094
0
      if (Z_TYPE_P(entry_values) != IS_UNDEF) {
7095
0
        if (Z_TYPE_P(entry_keys) == IS_LONG) {
7096
0
          entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
7097
0
            Z_LVAL_P(entry_keys), entry_values);
7098
0
        } else {
7099
0
          zend_string *tmp_key;
7100
0
          zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
7101
0
          entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
7102
0
            key, entry_values);
7103
0
          zend_tmp_string_release(tmp_key);
7104
0
        }
7105
0
        zval_add_ref(entry_values);
7106
0
        pos_values++;
7107
0
        break;
7108
0
      }
7109
0
      pos_values++;
7110
0
    }
7111
0
  } ZEND_HASH_FOREACH_END();
7112
0
}
7113
/* }}} */