Coverage Report

Created: 2022-10-14 11:19

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