Coverage Report

Created: 2025-09-27 06:26

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