Coverage Report

Created: 2026-01-18 06:49

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