Coverage Report

Created: 2022-02-19 20:30

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