1/*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2015 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Rasmus Lerdorf <rasmus@php.net> |
18 | Andrei Zmievski <andrei@php.net> |
19 | Stig Venaas <venaas@php.net> |
20 | Jason Greene <jason@php.net> |
21 +----------------------------------------------------------------------+
22*/
23
24/* $Id$ */
25
26#include "php.h"
27#include "php_ini.h"
28#include <stdarg.h>
29#include <stdlib.h>
30#include <math.h>
31#include <time.h>
32#include <stdio.h>
33#if HAVE_STRING_H
34#include <string.h>
35#else
36#include <strings.h>
37#endif
38#ifdef PHP_WIN32
39#include "win32/unistd.h"
40#endif
41#include "zend_globals.h"
42#include "zend_interfaces.h"
43#include "php_globals.h"
44#include "php_array.h"
45#include "basic_functions.h"
46#include "php_string.h"
47#include "php_rand.h"
48#include "php_smart_str.h"
49#ifdef HAVE_SPL
50#include "ext/spl/spl_array.h"
51#endif
52
53/* {{{ defines */
54#define EXTR_OVERWRITE 0
55#define EXTR_SKIP 1
56#define EXTR_PREFIX_SAME 2
57#define EXTR_PREFIX_ALL 3
58#define EXTR_PREFIX_INVALID 4
59#define EXTR_PREFIX_IF_EXISTS 5
60#define EXTR_IF_EXISTS 6
61
62#define EXTR_REFS 0x100
63
64#define CASE_LOWER 0
65#define CASE_UPPER 1
66
67#define DIFF_NORMAL 1
68#define DIFF_KEY 2
69#define DIFF_ASSOC 6
70#define DIFF_COMP_DATA_NONE -1
71#define DIFF_COMP_DATA_INTERNAL 0
72#define DIFF_COMP_DATA_USER 1
73#define DIFF_COMP_KEY_INTERNAL 0
74#define DIFF_COMP_KEY_USER 1
75
76#define INTERSECT_NORMAL 1
77#define INTERSECT_KEY 2
78#define INTERSECT_ASSOC 6
79#define INTERSECT_COMP_DATA_NONE -1
80#define INTERSECT_COMP_DATA_INTERNAL 0
81#define INTERSECT_COMP_DATA_USER 1
82#define INTERSECT_COMP_KEY_INTERNAL 0
83#define INTERSECT_COMP_KEY_USER 1
84
85#define DOUBLE_DRIFT_FIX 0.000000000000001
86/* }}} */
87
88ZEND_DECLARE_MODULE_GLOBALS(array)
89
90/* {{{ php_array_init_globals
91*/
92static void php_array_init_globals(zend_array_globals *array_globals)
93{
94 memset(array_globals, 0, sizeof(zend_array_globals));
95}
96/* }}} */
97
98PHP_MINIT_FUNCTION(array) /* {{{ */
99{
100 ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
101
102 REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
103 REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
104 REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
105 REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
106 REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
107 REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
108 REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
109 REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
110
111 REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
112 REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
113
114 REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
115 REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
116 REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
117 REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
118 REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
119 REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
120
121 REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
122 REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
123
124 REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
125 REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
126
127 REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
128 REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
129
130 return SUCCESS;
131}
132/* }}} */
133
134PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
135{
136#ifdef ZTS
137 ts_free_id(array_globals_id);
138#endif
139
140 return SUCCESS;
141}
142/* }}} */
143
144static void php_set_compare_func(int sort_type TSRMLS_DC) /* {{{ */
145{
146 switch (sort_type & ~PHP_SORT_FLAG_CASE) {
147 case PHP_SORT_NUMERIC:
148 ARRAYG(compare_func) = numeric_compare_function;
149 break;
150
151 case PHP_SORT_STRING:
152 ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_case_compare_function : string_compare_function;
153 break;
154
155 case PHP_SORT_NATURAL:
156 ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_natural_case_compare_function : string_natural_compare_function;
157 break;
158
159#if HAVE_STRCOLL
160 case PHP_SORT_LOCALE_STRING:
161 ARRAYG(compare_func) = string_locale_compare_function;
162 break;
163#endif
164
165 case PHP_SORT_REGULAR:
166 default:
167 ARRAYG(compare_func) = compare_function;
168 break;
169 }
170}
171/* }}} */
172
173static int php_array_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
174{
175 Bucket *f;
176 Bucket *s;
177 zval result;
178 zval first;
179 zval second;
180
181 f = *((Bucket **) a);
182 s = *((Bucket **) b);
183
184 if (f->nKeyLength == 0) {
185 Z_TYPE(first) = IS_LONG;
186 Z_LVAL(first) = f->h;
187 } else {
188 Z_TYPE(first) = IS_STRING;
189 Z_STRVAL(first) = (char*)f->arKey;
190 Z_STRLEN(first) = f->nKeyLength - 1;
191 }
192
193 if (s->nKeyLength == 0) {
194 Z_TYPE(second) = IS_LONG;
195 Z_LVAL(second) = s->h;
196 } else {
197 Z_TYPE(second) = IS_STRING;
198 Z_STRVAL(second) = (char*)s->arKey;
199 Z_STRLEN(second) = s->nKeyLength - 1;
200 }
201
202 if (ARRAYG(compare_func)(&result, &first, &second TSRMLS_CC) == FAILURE) {
203 return 0;
204 }
205
206 if (Z_TYPE(result) == IS_DOUBLE) {
207 if (Z_DVAL(result) < 0) {
208 return -1;
209 } else if (Z_DVAL(result) > 0) {
210 return 1;
211 } else {
212 return 0;
213 }
214 }
215
216 convert_to_long(&result);
217
218 if (Z_LVAL(result) < 0) {
219 return -1;
220 } else if (Z_LVAL(result) > 0) {
221 return 1;
222 }
223
224 return 0;
225}
226/* }}} */
227
228static int php_array_reverse_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
229{
230 return php_array_key_compare(a, b TSRMLS_CC) * -1;
231}
232/* }}} */
233
234/* {{{ proto bool krsort(array &array_arg [, int sort_flags])
235 Sort an array by key value in reverse order */
236PHP_FUNCTION(krsort)
237{
238 zval *array;
239 long sort_type = PHP_SORT_REGULAR;
240
241 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
242 RETURN_FALSE;
243 }
244
245 php_set_compare_func(sort_type TSRMLS_CC);
246
247 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_key_compare, 0 TSRMLS_CC) == FAILURE) {
248 RETURN_FALSE;
249 }
250 RETURN_TRUE;
251}
252/* }}} */
253
254/* {{{ proto bool ksort(array &array_arg [, int sort_flags])
255 Sort an array by key */
256PHP_FUNCTION(ksort)
257{
258 zval *array;
259 long sort_type = PHP_SORT_REGULAR;
260
261 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
262 RETURN_FALSE;
263 }
264
265 php_set_compare_func(sort_type TSRMLS_CC);
266
267 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_key_compare, 0 TSRMLS_CC) == FAILURE) {
268 RETURN_FALSE;
269 }
270 RETURN_TRUE;
271}
272/* }}} */
273
274PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
275{
276 long cnt = 0;
277 zval **element;
278
279 if (Z_TYPE_P(array) == IS_ARRAY) {
280 if (Z_ARRVAL_P(array)->nApplyCount > 1) {
281 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
282 return 0;
283 }
284
285 cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
286 if (mode == COUNT_RECURSIVE) {
287 HashPosition pos;
288
289 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
290 zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
291 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
292 ) {
293 Z_ARRVAL_P(array)->nApplyCount++;
294 cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
295 Z_ARRVAL_P(array)->nApplyCount--;
296 }
297 }
298 }
299
300 return cnt;
301}
302/* }}} */
303
304/* {{{ proto int count(mixed var [, int mode])
305 Count the number of elements in a variable (usually an array) */
306PHP_FUNCTION(count)
307{
308 zval *array;
309 long mode = COUNT_NORMAL;
310
311 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
312 return;
313 }
314
315 switch (Z_TYPE_P(array)) {
316 case IS_NULL:
317 RETURN_LONG(0);
318 break;
319 case IS_ARRAY:
320 RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
321 break;
322 case IS_OBJECT: {
323#ifdef HAVE_SPL
324 zval *retval;
325#endif
326 /* first, we check if the handler is defined */
327 if (Z_OBJ_HT_P(array)->count_elements) {
328 RETVAL_LONG(1);
329 if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value) TSRMLS_CC)) {
330 return;
331 }
332 }
333#ifdef HAVE_SPL
334 /* if not and the object implements Countable we call its count() method */
335 if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
336 zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval);
337 if (retval) {
338 convert_to_long_ex(&retval);
339 RETVAL_LONG(Z_LVAL_P(retval));
340 zval_ptr_dtor(&retval);
341 }
342 return;
343 }
344#endif
345 }
346 default:
347 RETURN_LONG(1);
348 break;
349 }
350}
351/* }}} */
352
353/* Numbers are always smaller than strings int this function as it
354 * anyway doesn't make much sense to compare two different data types.
355 * This keeps it consistent and simple.
356 *
357 * This is not correct any more, depends on what compare_func is set to.
358 */
359static int php_array_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
360{
361 Bucket *f;
362 Bucket *s;
363 zval result;
364 zval *first;
365 zval *second;
366
367 f = *((Bucket **) a);
368 s = *((Bucket **) b);
369
370 first = *((zval **) f->pData);
371 second = *((zval **) s->pData);
372
373 if (ARRAYG(compare_func)(&result, first, second TSRMLS_CC) == FAILURE) {
374 return 0;
375 }
376
377 if (Z_TYPE(result) == IS_DOUBLE) {
378 if (Z_DVAL(result) < 0) {
379 return -1;
380 } else if (Z_DVAL(result) > 0) {
381 return 1;
382 } else {
383 return 0;
384 }
385 }
386
387 convert_to_long(&result);
388
389 if (Z_LVAL(result) < 0) {
390 return -1;
391 } else if (Z_LVAL(result) > 0) {
392 return 1;
393 }
394
395 return 0;
396}
397/* }}} */
398
399static int php_array_reverse_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
400{
401 return php_array_data_compare(a, b TSRMLS_CC) * -1;
402}
403/* }}} */
404
405static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */
406{
407 Bucket *f, *s;
408 zval *fval, *sval;
409 zval first, second;
410 int result;
411
412 f = *((Bucket **) a);
413 s = *((Bucket **) b);
414
415 fval = *((zval **) f->pData);
416 sval = *((zval **) s->pData);
417 first = *fval;
418 second = *sval;
419
420 if (Z_TYPE_P(fval) != IS_STRING) {
421 zval_copy_ctor(&first);
422 convert_to_string(&first);
423 }
424
425 if (Z_TYPE_P(sval) != IS_STRING) {
426 zval_copy_ctor(&second);
427 convert_to_string(&second);
428 }
429
430 result = strnatcmp_ex(Z_STRVAL(first), Z_STRLEN(first), Z_STRVAL(second), Z_STRLEN(second), fold_case);
431
432 if (Z_TYPE_P(fval) != IS_STRING) {
433 zval_dtor(&first);
434 }
435
436 if (Z_TYPE_P(sval) != IS_STRING) {
437 zval_dtor(&second);
438 }
439
440 return result;
441}
442/* }}} */
443
444static int php_array_natural_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
445{
446 return php_array_natural_general_compare(a, b, 0);
447}
448/* }}} */
449
450static int php_array_natural_case_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
451{
452 return php_array_natural_general_compare(a, b, 1);
453}
454/* }}} */
455
456static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
457{
458 zval *array;
459
460 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
461 return;
462 }
463
464 if (fold_case) {
465 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_case_compare, 0 TSRMLS_CC) == FAILURE) {
466 return;
467 }
468 } else {
469 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_compare, 0 TSRMLS_CC) == FAILURE) {
470 return;
471 }
472 }
473
474 RETURN_TRUE;
475}
476/* }}} */
477
478/* {{{ proto void natsort(array &array_arg)
479 Sort an array using natural sort */
480PHP_FUNCTION(natsort)
481{
482 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
483}
484/* }}} */
485
486/* {{{ proto void natcasesort(array &array_arg)
487 Sort an array using case-insensitive natural sort */
488PHP_FUNCTION(natcasesort)
489{
490 php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
491}
492/* }}} */
493
494/* {{{ proto bool asort(array &array_arg [, int sort_flags])
495 Sort an array and maintain index association */
496PHP_FUNCTION(asort)
497{
498 zval *array;
499 long sort_type = PHP_SORT_REGULAR;
500
501 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
502 RETURN_FALSE;
503 }
504
505 php_set_compare_func(sort_type TSRMLS_CC);
506
507 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 0 TSRMLS_CC) == FAILURE) {
508 RETURN_FALSE;
509 }
510 RETURN_TRUE;
511}
512/* }}} */
513
514/* {{{ proto bool arsort(array &array_arg [, int sort_flags])
515 Sort an array in reverse order and maintain index association */
516PHP_FUNCTION(arsort)
517{
518 zval *array;
519 long sort_type = PHP_SORT_REGULAR;
520
521 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
522 RETURN_FALSE;
523 }
524
525 php_set_compare_func(sort_type TSRMLS_CC);
526
527 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 0 TSRMLS_CC) == FAILURE) {
528 RETURN_FALSE;
529 }
530 RETURN_TRUE;
531}
532/* }}} */
533
534/* {{{ proto bool sort(array &array_arg [, int sort_flags])
535 Sort an array */
536PHP_FUNCTION(sort)
537{
538 zval *array;
539 long sort_type = PHP_SORT_REGULAR;
540
541 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
542 RETURN_FALSE;
543 }
544
545 php_set_compare_func(sort_type TSRMLS_CC);
546
547 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 1 TSRMLS_CC) == FAILURE) {
548 RETURN_FALSE;
549 }
550 RETURN_TRUE;
551}
552/* }}} */
553
554/* {{{ proto bool rsort(array &array_arg [, int sort_flags])
555 Sort an array in reverse order */
556PHP_FUNCTION(rsort)
557{
558 zval *array;
559 long sort_type = PHP_SORT_REGULAR;
560
561 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
562 RETURN_FALSE;
563 }
564
565 php_set_compare_func(sort_type TSRMLS_CC);
566
567 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 1 TSRMLS_CC) == FAILURE) {
568 RETURN_FALSE;
569 }
570 RETURN_TRUE;
571}
572/* }}} */
573
574static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
575{
576 Bucket *f;
577 Bucket *s;
578 zval **args[2];
579 zval *retval_ptr = NULL;
580
581 f = *((Bucket **) a);
582 s = *((Bucket **) b);
583
584 args[0] = (zval **) f->pData;
585 args[1] = (zval **) s->pData;
586
587 BG(user_compare_fci).param_count = 2;
588 BG(user_compare_fci).params = args;
589 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
590 BG(user_compare_fci).no_separation = 0;
591 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
592 long retval;
593
594 convert_to_long_ex(&retval_ptr);
595 retval = Z_LVAL_P(retval_ptr);
596 zval_ptr_dtor(&retval_ptr);
597 return retval < 0 ? -1 : retval > 0 ? 1 : 0;
598 } else {
599 return 0;
600 }
601}
602/* }}} */
603
604/* check if comparison function is valid */
605#define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
606 if (!zend_is_callable(*func_name, 0, NULL TSRMLS_CC)) { \
607 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid comparison function"); \
608 BG(user_compare_fci) = old_user_compare_fci; \
609 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
610 RETURN_FALSE; \
611 } \
612
613 /* Clear FCI cache otherwise : for example the same or other array with
614 * (partly) the same key values has been sorted with uasort() or
615 * other sorting function the comparison is cached, however the name
616 * of the function for comparison is not respected. see bug #28739 AND #33295
617 *
618 * Following defines will assist in backup / restore values. */
619
620#define PHP_ARRAY_CMP_FUNC_VARS \
621 zend_fcall_info old_user_compare_fci; \
622 zend_fcall_info_cache old_user_compare_fci_cache \
623
624#define PHP_ARRAY_CMP_FUNC_BACKUP() \
625 old_user_compare_fci = BG(user_compare_fci); \
626 old_user_compare_fci_cache = BG(user_compare_fci_cache); \
627 BG(user_compare_fci_cache) = empty_fcall_info_cache; \
628
629#define PHP_ARRAY_CMP_FUNC_RESTORE() \
630 BG(user_compare_fci) = old_user_compare_fci; \
631 BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
632
633/* {{{ proto bool usort(array array_arg, string cmp_function)
634 Sort an array by values using a user-defined comparison function */
635PHP_FUNCTION(usort)
636{
637 zval *array;
638 unsigned int refcount;
639 PHP_ARRAY_CMP_FUNC_VARS;
640
641 PHP_ARRAY_CMP_FUNC_BACKUP();
642
643 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
644 PHP_ARRAY_CMP_FUNC_RESTORE();
645 return;
646 }
647
648 /* Clear the is_ref flag, so the attemts to modify the array in user
649 * comparison function will create a copy of array and won't affect the
650 * original array. The fact of modification is detected using refcount
651 * comparison. The result of sorting in such case is undefined and the
652 * function returns FALSE.
653 */
654 Z_UNSET_ISREF_P(array);
655 refcount = Z_REFCOUNT_P(array);
656
657 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 1 TSRMLS_CC) == FAILURE) {
658 RETVAL_FALSE;
659 } else {
660 if (refcount > Z_REFCOUNT_P(array)) {
661 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
662 RETVAL_FALSE;
663 } else {
664 RETVAL_TRUE;
665 }
666 }
667
668 if (Z_REFCOUNT_P(array) > 1) {
669 Z_SET_ISREF_P(array);
670 }
671
672 PHP_ARRAY_CMP_FUNC_RESTORE();
673}
674/* }}} */
675
676/* {{{ proto bool uasort(array array_arg, string cmp_function)
677 Sort an array with a user-defined comparison function and maintain index association */
678PHP_FUNCTION(uasort)
679{
680 zval *array;
681 unsigned int refcount;
682 PHP_ARRAY_CMP_FUNC_VARS;
683
684 PHP_ARRAY_CMP_FUNC_BACKUP();
685
686 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
687 PHP_ARRAY_CMP_FUNC_RESTORE();
688 return;
689 }
690
691 /* Clear the is_ref flag, so the attemts to modify the array in user
692 * comaprison function will create a copy of array and won't affect the
693 * original array. The fact of modification is detected using refcount
694 * comparison. The result of sorting in such case is undefined and the
695 * function returns FALSE.
696 */
697 Z_UNSET_ISREF_P(array);
698 refcount = Z_REFCOUNT_P(array);
699
700 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 0 TSRMLS_CC) == FAILURE) {
701 RETVAL_FALSE;
702 } else {
703 if (refcount > Z_REFCOUNT_P(array)) {
704 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
705 RETVAL_FALSE;
706 } else {
707 RETVAL_TRUE;
708 }
709 }
710
711 if (Z_REFCOUNT_P(array) > 1) {
712 Z_SET_ISREF_P(array);
713 }
714
715 PHP_ARRAY_CMP_FUNC_RESTORE();
716}
717/* }}} */
718
719static int php_array_user_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
720{
721 Bucket *f;
722 Bucket *s;
723 zval *key1, *key2;
724 zval **args[2];
725 zval *retval_ptr = NULL;
726 long result;
727
728 ALLOC_INIT_ZVAL(key1);
729 ALLOC_INIT_ZVAL(key2);
730 args[0] = &key1;
731 args[1] = &key2;
732
733 f = *((Bucket **) a);
734 s = *((Bucket **) b);
735
736 if (f->nKeyLength == 0) {
737 Z_LVAL_P(key1) = f->h;
738 Z_TYPE_P(key1) = IS_LONG;
739 } else {
740 Z_STRVAL_P(key1) = estrndup(f->arKey, f->nKeyLength - 1);
741 Z_STRLEN_P(key1) = f->nKeyLength - 1;
742 Z_TYPE_P(key1) = IS_STRING;
743 }
744 if (s->nKeyLength == 0) {
745 Z_LVAL_P(key2) = s->h;
746 Z_TYPE_P(key2) = IS_LONG;
747 } else {
748 Z_STRVAL_P(key2) = estrndup(s->arKey, s->nKeyLength - 1);
749 Z_STRLEN_P(key2) = s->nKeyLength - 1;
750 Z_TYPE_P(key2) = IS_STRING;
751 }
752
753 BG(user_compare_fci).param_count = 2;
754 BG(user_compare_fci).params = args;
755 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
756 BG(user_compare_fci).no_separation = 0;
757 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
758 convert_to_long_ex(&retval_ptr);
759 result = Z_LVAL_P(retval_ptr);
760 zval_ptr_dtor(&retval_ptr);
761 } else {
762 result = 0;
763 }
764
765 zval_ptr_dtor(&key1);
766 zval_ptr_dtor(&key2);
767
768 return result;
769}
770/* }}} */
771
772/* {{{ proto bool uksort(array array_arg, string cmp_function)
773 Sort an array by keys using a user-defined comparison function */
774PHP_FUNCTION(uksort)
775{
776 zval *array;
777 unsigned int refcount;
778 PHP_ARRAY_CMP_FUNC_VARS;
779
780 PHP_ARRAY_CMP_FUNC_BACKUP();
781
782 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
783 PHP_ARRAY_CMP_FUNC_RESTORE();
784 return;
785 }
786
787 /* Clear the is_ref flag, so the attemts to modify the array in user
788 * comaprison function will create a copy of array and won't affect the
789 * original array. The fact of modification is detected using refcount
790 * comparison. The result of sorting in such case is undefined and the
791 * function returns FALSE.
792 */
793 Z_UNSET_ISREF_P(array);
794 refcount = Z_REFCOUNT_P(array);
795
796 if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_key_compare, 0 TSRMLS_CC) == FAILURE) {
797 RETVAL_FALSE;
798 } else {
799 if (refcount > Z_REFCOUNT_P(array)) {
800 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function");
801 RETVAL_FALSE;
802 } else {
803 RETVAL_TRUE;
804 }
805 }
806
807 if (Z_REFCOUNT_P(array) > 1) {
808 Z_SET_ISREF_P(array);
809 }
810
811 PHP_ARRAY_CMP_FUNC_RESTORE();
812}
813/* }}} */
814
815/* {{{ proto mixed end(array array_arg)
816 Advances array argument's internal pointer to the last element and return it */
817PHP_FUNCTION(end)
818{
819 HashTable *array;
820 zval **entry;
821
822 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
823 return;
824 }
825
826 zend_hash_internal_pointer_end(array);
827
828 if (return_value_used) {
829 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
830 RETURN_FALSE;
831 }
832
833 RETURN_ZVAL_FAST(*entry);
834 }
835}
836/* }}} */
837
838/* {{{ proto mixed prev(array array_arg)
839 Move array argument's internal pointer to the previous element and return it */
840PHP_FUNCTION(prev)
841{
842 HashTable *array;
843 zval **entry;
844
845 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
846 return;
847 }
848
849 zend_hash_move_backwards(array);
850
851 if (return_value_used) {
852 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
853 RETURN_FALSE;
854 }
855
856 RETURN_ZVAL_FAST(*entry);
857 }
858}
859/* }}} */
860
861/* {{{ proto mixed next(array array_arg)
862 Move array argument's internal pointer to the next element and return it */
863PHP_FUNCTION(next)
864{
865 HashTable *array;
866 zval **entry;
867
868 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
869 return;
870 }
871
872 zend_hash_move_forward(array);
873
874 if (return_value_used) {
875 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
876 RETURN_FALSE;
877 }
878
879 RETURN_ZVAL_FAST(*entry);
880 }
881}
882/* }}} */
883
884/* {{{ proto mixed reset(array array_arg)
885 Set array argument's internal pointer to the first element and return it */
886PHP_FUNCTION(reset)
887{
888 HashTable *array;
889 zval **entry;
890
891 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
892 return;
893 }
894
895 zend_hash_internal_pointer_reset(array);
896
897 if (return_value_used) {
898 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
899 RETURN_FALSE;
900 }
901
902 RETURN_ZVAL_FAST(*entry);
903 }
904}
905/* }}} */
906
907/* {{{ proto mixed current(array array_arg)
908 Return the element currently pointed to by the internal array pointer */
909PHP_FUNCTION(current)
910{
911 HashTable *array;
912 zval **entry;
913
914 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
915 return;
916 }
917
918 if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
919 RETURN_FALSE;
920 }
921
922 RETURN_ZVAL_FAST(*entry);
923}
924/* }}} */
925
926/* {{{ proto mixed key(array array_arg)
927 Return the key of the element currently pointed to by the internal array pointer */
928PHP_FUNCTION(key)
929{
930 HashTable *array;
931
932 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) {
933 return;
934 }
935
936 zend_hash_get_current_key_zval(array, return_value);
937}
938/* }}} */
939
940/* {{{ proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
941 Return the lowest value in an array or a series of arguments */
942PHP_FUNCTION(min)
943{
944 int argc;
945 zval ***args = NULL;
946
947 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
948 return;
949 }
950
951 php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
952
953 /* mixed min ( array $values ) */
954 if (argc == 1) {
955 zval **result;
956
957 if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
958 php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
959 RETVAL_NULL();
960 } else {
961 if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) {
962 RETVAL_ZVAL_FAST(*result);
963 } else {
964 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
965 RETVAL_FALSE;
966 }
967 }
968 } else {
969 /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
970 zval **min, result;
971 int i;
972
973 min = args[0];
974
975 for (i = 1; i < argc; i++) {
976 is_smaller_function(&result, *args[i], *min TSRMLS_CC);
977 if (Z_LVAL(result) == 1) {
978 min = args[i];
979 }
980 }
981
982 RETVAL_ZVAL_FAST(*min);
983 }
984
985 if (args) {
986 efree(args);
987 }
988}
989/* }}} */
990
991/* {{{ proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
992 Return the highest value in an array or a series of arguments */
993PHP_FUNCTION(max)
994{
995 zval ***args = NULL;
996 int argc;
997
998 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
999 return;
1000 }
1001
1002 php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC);
1003
1004 /* mixed max ( array $values ) */
1005 if (argc == 1) {
1006 zval **result;
1007
1008 if (Z_TYPE_PP(args[0]) != IS_ARRAY) {
1009 php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array");
1010 RETVAL_NULL();
1011 } else {
1012 if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) {
1013 RETVAL_ZVAL_FAST(*result);
1014 } else {
1015 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
1016 RETVAL_FALSE;
1017 }
1018 }
1019 } else {
1020 /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1021 zval **max, result;
1022 int i;
1023
1024 max = args[0];
1025
1026 for (i = 1; i < argc; i++) {
1027 is_smaller_or_equal_function(&result, *args[i], *max TSRMLS_CC);
1028 if (Z_LVAL(result) == 0) {
1029 max = args[i];
1030 }
1031 }
1032
1033 RETVAL_ZVAL_FAST(*max);
1034 }
1035
1036 if (args) {
1037 efree(args);
1038 }
1039}
1040/* }}} */
1041
1042static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive TSRMLS_DC) /* {{{ */
1043{
1044 zval **args[3], /* Arguments to userland function */
1045 *retval_ptr = NULL, /* Return value - unused */
1046 *key=NULL; /* Entry key */
1047
1048 /* Set up known arguments */
1049 args[1] = &key;
1050 args[2] = &userdata;
1051 if (userdata) {
1052 Z_ADDREF_P(userdata);
1053 }
1054
1055 BG(array_walk_fci).retval_ptr_ptr = &retval_ptr;
1056 BG(array_walk_fci).param_count = userdata ? 3 : 2;
1057 BG(array_walk_fci).params = args;
1058 BG(array_walk_fci).no_separation = 0;
1059
1060 /* Iterate through hash */
1061 zend_hash_internal_pointer_reset(target_hash);
1062 while (!EG(exception) && zend_hash_get_current_data(target_hash, (void **)&args[0]) == SUCCESS) {
1063 if (recursive && Z_TYPE_PP(args[0]) == IS_ARRAY) {
1064 HashTable *thash;
1065 zend_fcall_info orig_array_walk_fci;
1066 zend_fcall_info_cache orig_array_walk_fci_cache;
1067
1068 SEPARATE_ZVAL_IF_NOT_REF(args[0]);
1069 thash = Z_ARRVAL_PP(args[0]);
1070 if (thash->nApplyCount > 1) {
1071 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
1072 if (userdata) {
1073 zval_ptr_dtor(&userdata);
1074 }
1075 return 0;
1076 }
1077
1078 /* backup the fcall info and cache */
1079 orig_array_walk_fci = BG(array_walk_fci);
1080 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1081
1082 thash->nApplyCount++;
1083 php_array_walk(thash, userdata, recursive TSRMLS_CC);
1084 thash->nApplyCount--;
1085
1086 /* restore the fcall info and cache */
1087 BG(array_walk_fci) = orig_array_walk_fci;
1088 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1089 } else {
1090 /* Allocate space for key */
1091 MAKE_STD_ZVAL(key);
1092 zend_hash_get_current_key_zval(target_hash, key);
1093
1094 /* Call the userland function */
1095 if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache) TSRMLS_CC) == SUCCESS) {
1096 if (retval_ptr) {
1097 zval_ptr_dtor(&retval_ptr);
1098 }
1099 } else {
1100 if (key) {
1101 zval_ptr_dtor(&key);
1102 key = NULL;
1103 }
1104 break;
1105 }
1106 }
1107
1108 if (key) {
1109 zval_ptr_dtor(&key);
1110 key = NULL;
1111 }
1112 zend_hash_move_forward(target_hash);
1113 }
1114
1115 if (userdata) {
1116 zval_ptr_dtor(&userdata);
1117 }
1118 return 0;
1119}
1120/* }}} */
1121
1122/* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
1123 Apply a user function to every member of an array */
1124PHP_FUNCTION(array_walk)
1125{
1126 HashTable *array;
1127 zval *userdata = NULL;
1128 zend_fcall_info orig_array_walk_fci;
1129 zend_fcall_info_cache orig_array_walk_fci_cache;
1130
1131 orig_array_walk_fci = BG(array_walk_fci);
1132 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1133
1134 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1135 BG(array_walk_fci) = orig_array_walk_fci;
1136 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1137 return;
1138 }
1139
1140 php_array_walk(array, userdata, 0 TSRMLS_CC);
1141 BG(array_walk_fci) = orig_array_walk_fci;
1142 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1143 RETURN_TRUE;
1144}
1145/* }}} */
1146
1147/* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
1148 Apply a user function recursively to every member of an array */
1149PHP_FUNCTION(array_walk_recursive)
1150{
1151 HashTable *array;
1152 zval *userdata = NULL;
1153 zend_fcall_info orig_array_walk_fci;
1154 zend_fcall_info_cache orig_array_walk_fci_cache;
1155
1156 orig_array_walk_fci = BG(array_walk_fci);
1157 orig_array_walk_fci_cache = BG(array_walk_fci_cache);
1158
1159 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
1160 BG(array_walk_fci) = orig_array_walk_fci;
1161 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1162 return;
1163 }
1164
1165 php_array_walk(array, userdata, 1 TSRMLS_CC);
1166 BG(array_walk_fci) = orig_array_walk_fci;
1167 BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
1168 RETURN_TRUE;
1169}
1170/* }}} */
1171
1172/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1173 * 0 = return boolean
1174 * 1 = return key
1175 */
1176static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
1177{
1178 zval *value, /* value to check for */
1179 *array, /* array to check in */
1180 **entry, /* pointer to array entry */
1181 res; /* comparison result */
1182 HashPosition pos; /* hash iterator */
1183 zend_bool strict = 0; /* strict comparison or not */
1184 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
1185
1186 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) {
1187 return;
1188 }
1189
1190 if (strict) {
1191 is_equal_func = is_identical_function;
1192 }
1193
1194 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
1195 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
1196 is_equal_func(&res, value, *entry TSRMLS_CC);
1197 if (Z_LVAL(res)) {
1198 if (behavior == 0) {
1199 RETURN_TRUE;
1200 } else {
1201 zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), return_value, &pos);
1202 return;
1203 }
1204 }
1205 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
1206 }
1207
1208 RETURN_FALSE;
1209}
1210/* }}} */
1211
1212/* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
1213 Checks if the given value exists in the array */
1214PHP_FUNCTION(in_array)
1215{
1216 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1217}
1218/* }}} */
1219
1220/* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
1221 Searches the array for a given value and returns the corresponding key if successful */
1222PHP_FUNCTION(array_search)
1223{
1224 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1225}
1226/* }}} */
1227
1228static int php_valid_var_name(char *var_name, int var_name_len) /* {{{ */
1229{
1230 int i, ch;
1231
1232 if (!var_name || !var_name_len) {
1233 return 0;
1234 }
1235
1236 /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
1237 ch = (int)((unsigned char *)var_name)[0];
1238 if (var_name[0] != '_' &&
1239 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1240 (ch < 97 /* a */ || /* z */ ch > 122) &&
1241 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1242 ) {
1243 return 0;
1244 }
1245
1246 /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
1247 if (var_name_len > 1) {
1248 for (i = 1; i < var_name_len; i++) {
1249 ch = (int)((unsigned char *)var_name)[i];
1250 if (var_name[i] != '_' &&
1251 (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
1252 (ch < 65 /* A */ || /* Z */ ch > 90) &&
1253 (ch < 97 /* a */ || /* z */ ch > 122) &&
1254 (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
1255 ) {
1256 return 0;
1257 }
1258 }
1259 }
1260 return 1;
1261}
1262/* }}} */
1263
1264PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, int var_name_len, zend_bool add_underscore TSRMLS_DC) /* {{{ */
1265{
1266 Z_STRLEN_P(result) = Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len;
1267 Z_TYPE_P(result) = IS_STRING;
1268 Z_STRVAL_P(result) = emalloc(Z_STRLEN_P(result) + 1);
1269 memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
1270
1271 if (add_underscore) {
1272 Z_STRVAL_P(result)[Z_STRLEN_P(prefix)] = '_';
1273 }
1274
1275 memcpy(Z_STRVAL_P(result) + Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
1276
1277 return SUCCESS;
1278}
1279/* }}} */
1280
1281/* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
1282 Imports variables into symbol table from an array */
1283PHP_FUNCTION(extract)
1284{
1285 zval *var_array, *prefix = NULL;
1286 long extract_type = EXTR_OVERWRITE;
1287 zval **entry, *data;
1288 char *var_name;
1289 ulong num_key;
1290 uint var_name_len;
1291 int var_exists, key_type, count = 0;
1292 int extract_refs = 0;
1293 HashPosition pos;
1294
1295 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) {
1296 return;
1297 }
1298
1299 extract_refs = (extract_type & EXTR_REFS);
1300 extract_type &= 0xff;
1301
1302 if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
1303 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid extract type");
1304 return;
1305 }
1306
1307 if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
1308 php_error_docref(NULL TSRMLS_CC, E_WARNING, "specified extract type requires the prefix parameter");
1309 return;
1310 }
1311
1312 if (prefix) {
1313 convert_to_string(prefix);
1314 if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) {
1315 php_error_docref(NULL TSRMLS_CC, E_WARNING, "prefix is not a valid identifier");
1316 return;
1317 }
1318 }
1319
1320 if (!EG(active_symbol_table)) {
1321 zend_rebuild_symbol_table(TSRMLS_C);
1322 }
1323
1324 /* var_array is passed by ref for the needs of EXTR_REFS (needs to
1325 * work on the original array to create refs to its members)
1326 * simulate pass_by_value if EXTR_REFS is not used */
1327 if (!extract_refs) {
1328 SEPARATE_ARG_IF_REF(var_array);
1329 }
1330
1331 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos);
1332 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&entry, &pos) == SUCCESS) {
1333 zval final_name;
1334
1335 ZVAL_NULL(&final_name);
1336
1337 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &var_name, &var_name_len, &num_key, 0, &pos);
1338 var_exists = 0;
1339
1340 if (key_type == HASH_KEY_IS_STRING) {
1341 var_name_len--;
1342 var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
1343 } else if (key_type == HASH_KEY_IS_LONG && (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID)) {
1344 zval num;
1345
1346 ZVAL_LONG(&num, num_key);
1347 convert_to_string(&num);
1348 php_prefix_varname(&final_name, prefix, Z_STRVAL(num), Z_STRLEN(num), 1 TSRMLS_CC);
1349 zval_dtor(&num);
1350 } else {
1351 zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
1352 continue;
1353 }
1354
1355 switch (extract_type) {
1356 case EXTR_IF_EXISTS:
1357 if (!var_exists) break;
1358 /* break omitted intentionally */
1359
1360 case EXTR_OVERWRITE:
1361 /* GLOBALS protection */
1362 if (var_exists && var_name_len == sizeof("GLOBALS")-1 && !strcmp(var_name, "GLOBALS")) {
1363 break;
1364 }
1365 if (var_exists && var_name_len == sizeof("this")-1 && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) {
1366 break;
1367 }
1368 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1369 break;
1370
1371 case EXTR_PREFIX_IF_EXISTS:
1372 if (var_exists) {
1373 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1374 }
1375 break;
1376
1377 case EXTR_PREFIX_SAME:
1378 if (!var_exists && var_name_len != 0) {
1379 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1380 }
1381 /* break omitted intentionally */
1382
1383 case EXTR_PREFIX_ALL:
1384 if (Z_TYPE(final_name) == IS_NULL && var_name_len != 0) {
1385 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1386 }
1387 break;
1388
1389 case EXTR_PREFIX_INVALID:
1390 if (Z_TYPE(final_name) == IS_NULL) {
1391 if (!php_valid_var_name(var_name, var_name_len)) {
1392 php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC);
1393 } else {
1394 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1395 }
1396 }
1397 break;
1398
1399 default:
1400 if (!var_exists) {
1401 ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
1402 }
1403 break;
1404 }
1405
1406 if (Z_TYPE(final_name) != IS_NULL && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
1407 if (extract_refs) {
1408 zval **orig_var;
1409
1410 SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
1411 zval_add_ref(entry);
1412
1413 if (zend_hash_find(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) &orig_var) == SUCCESS) {
1414 zval_ptr_dtor(orig_var);
1415 *orig_var = *entry;
1416 } else {
1417 zend_hash_update(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) entry, sizeof(zval *), NULL);
1418 }
1419 } else {
1420 MAKE_STD_ZVAL(data);
1421 *data = **entry;
1422 zval_copy_ctor(data);
1423
1424 ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
1425 }
1426 count++;
1427 }
1428 zval_dtor(&final_name);
1429
1430 zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos);
1431 }
1432
1433 if (!extract_refs) {
1434 zval_ptr_dtor(&var_array);
1435 }
1436
1437 RETURN_LONG(count);
1438}
1439/* }}} */
1440
1441static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry TSRMLS_DC) /* {{{ */
1442{
1443 zval **value_ptr, *value, *data;
1444
1445 if (Z_TYPE_P(entry) == IS_STRING) {
1446 if (zend_hash_find(eg_active_symbol_table, Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, (void **)&value_ptr) != FAILURE) {
1447 value = *value_ptr;
1448 ALLOC_ZVAL(data);
1449 MAKE_COPY_ZVAL(&value, data);
1450
1451 zend_hash_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, &data, sizeof(zval *), NULL);
1452 }
1453 }
1454 else if (Z_TYPE_P(entry) == IS_ARRAY) {
1455 HashPosition pos;
1456
1457 if ((Z_ARRVAL_P(entry)->nApplyCount > 1)) {
1458 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
1459 return;
1460 }
1461
1462 Z_ARRVAL_P(entry)->nApplyCount++;
1463
1464 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
1465 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), (void**)&value_ptr, &pos) == SUCCESS) {
1466 value = *value_ptr;
1467
1468 php_compact_var(eg_active_symbol_table, return_value, value TSRMLS_CC);
1469 zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos);
1470 }
1471 Z_ARRVAL_P(entry)->nApplyCount--;
1472 }
1473}
1474/* }}} */
1475
1476/* {{{ proto array compact(mixed var_names [, mixed ...])
1477 Creates a hash containing variables and their values */
1478PHP_FUNCTION(compact)
1479{
1480 zval ***args = NULL; /* function arguments array */
1481 int num_args, i;
1482
1483 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1484 return;
1485 }
1486
1487 if (!EG(active_symbol_table)) {
1488 zend_rebuild_symbol_table(TSRMLS_C);
1489 }
1490
1491 /* compact() is probably most used with a single array of var_names
1492 or multiple string names, rather than a combination of both.
1493 So quickly guess a minimum result size based on that */
1494 if (ZEND_NUM_ARGS() == 1 && Z_TYPE_PP(args[0]) == IS_ARRAY) {
1495 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(args[0])));
1496 } else {
1497 array_init_size(return_value, ZEND_NUM_ARGS());
1498 }
1499
1500 for (i=0; i<ZEND_NUM_ARGS(); i++) {
1501 php_compact_var(EG(active_symbol_table), return_value, *args[i] TSRMLS_CC);
1502 }
1503
1504 if (args) {
1505 efree(args);
1506 }
1507}
1508/* }}} */
1509
1510/* {{{ proto array array_fill(int start_key, int num, mixed val)
1511 Create an array containing num elements starting with index start_key each initialized to val */
1512PHP_FUNCTION(array_fill)
1513{
1514 zval *val;
1515 long start_key, num;
1516
1517 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llz", &start_key, &num, &val) == FAILURE) {
1518 return;
1519 }
1520
1521 if (num < 0) {
1522 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements can't be negative");
1523 RETURN_FALSE;
1524 }
1525
1526 /* allocate an array for return */
1527 array_init_size(return_value, num);
1528
1529 if (num == 0) {
1530 return;
1531 }
1532
1533 num--;
1534 zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, &val, sizeof(zval *), NULL);
1535 zval_add_ref(&val);
1536
1537 while (num--) {
1538 if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val, sizeof(zval *), NULL) == SUCCESS) {
1539 zval_add_ref(&val);
1540 } else {
1541 zval_dtor(return_value);
1542 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
1543 RETURN_FALSE;
1544 }
1545 }
1546}
1547/* }}} */
1548
1549/* {{{ proto array array_fill_keys(array keys, mixed val)
1550 Create an array using the elements of the first parameter as keys each initialized to val */
1551PHP_FUNCTION(array_fill_keys)
1552{
1553 zval *keys, *val, **entry;
1554 HashPosition pos;
1555
1556 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &keys, &val) == FAILURE) {
1557 return;
1558 }
1559
1560 /* Initialize return array */
1561 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
1562
1563 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
1564 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry, &pos) == SUCCESS) {
1565
1566 if (Z_TYPE_PP(entry) == IS_LONG) {
1567 zval_add_ref(&val);
1568 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &val, sizeof(zval *), NULL);
1569 } else {
1570 zval key, *key_ptr = *entry;
1571
1572 if (Z_TYPE_PP(entry) != IS_STRING) {
1573 key = **entry;
1574 zval_copy_ctor(&key);
1575 convert_to_string(&key);
1576 key_ptr = &key;
1577 }
1578
1579 zval_add_ref(&val);
1580 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, &val, sizeof(zval *), NULL);
1581
1582 if (key_ptr != *entry) {
1583 zval_dtor(&key);
1584 }
1585 }
1586
1587 zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
1588 }
1589}
1590/* }}} */
1591
1592/* {{{ proto array range(mixed low, mixed high[, int step])
1593 Create an array containing the range of integers or characters from low to high (inclusive) */
1594PHP_FUNCTION(range)
1595{
1596 zval *zlow, *zhigh, *zstep = NULL;
1597 int err = 0, is_step_double = 0;
1598 double step = 1.0;
1599
1600 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) {
1601 RETURN_FALSE;
1602 }
1603
1604 if (zstep) {
1605 if (Z_TYPE_P(zstep) == IS_DOUBLE ||
1606 (Z_TYPE_P(zstep) == IS_STRING && is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0) == IS_DOUBLE)
1607 ) {
1608 is_step_double = 1;
1609 }
1610
1611 convert_to_double_ex(&zstep);
1612 step = Z_DVAL_P(zstep);
1613
1614 /* We only want positive step values. */
1615 if (step < 0.0) {
1616 step *= -1;
1617 }
1618 }
1619
1620 /* Initialize the return_value as an array. */
1621 array_init(return_value);
1622
1623 /* If the range is given as strings, generate an array of characters. */
1624 if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
1625 int type1, type2;
1626 unsigned char *low, *high;
1627 long lstep = (long) step;
1628
1629 type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
1630 type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
1631
1632 if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
1633 goto double_str;
1634 } else if (type1 == IS_LONG || type2 == IS_LONG) {
1635 goto long_str;
1636 }
1637
1638 convert_to_string(zlow);
1639 convert_to_string(zhigh);
1640 low = (unsigned char *)Z_STRVAL_P(zlow);
1641 high = (unsigned char *)Z_STRVAL_P(zhigh);
1642
1643 if (*low > *high) { /* Negative Steps */
1644 unsigned char ch = *low;
1645
1646 if (lstep <= 0) {
1647 err = 1;
1648 goto err;
1649 }
1650 for (; ch >= *high; ch -= (unsigned int)lstep) {
1651 add_next_index_stringl(return_value, (const char *)&ch, 1, 1);
1652 if (((signed int)ch - lstep) < 0) {
1653 break;
1654 }
1655 }
1656 } else if (*high > *low) { /* Positive Steps */
1657 unsigned char ch = *low;
1658
1659 if (lstep <= 0) {
1660 err = 1;
1661 goto err;
1662 }
1663 for (; ch <= *high; ch += (unsigned int)lstep) {
1664 add_next_index_stringl(return_value, (const char *)&ch, 1, 1);
1665 if (((signed int)ch + lstep) > 255) {
1666 break;
1667 }
1668 }
1669 } else {
1670 add_next_index_stringl(return_value, (const char *)low, 1, 1);
1671 }
1672
1673 } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
1674 double low, high, value;
1675 long i;
1676double_str:
1677 convert_to_double(zlow);
1678 convert_to_double(zhigh);
1679 low = Z_DVAL_P(zlow);
1680 high = Z_DVAL_P(zhigh);
1681 i = 0;
1682
1683 if (low > high) { /* Negative steps */
1684 if (low - high < step || step <= 0) {
1685 err = 1;
1686 goto err;
1687 }
1688
1689 for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) {
1690 add_next_index_double(return_value, value);
1691 }
1692 } else if (high > low) { /* Positive steps */
1693 if (high - low < step || step <= 0) {
1694 err = 1;
1695 goto err;
1696 }
1697
1698 for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) {
1699 add_next_index_double(return_value, value);
1700 }
1701 } else {
1702 add_next_index_double(return_value, low);
1703 }
1704 } else {
1705 double low, high;
1706 long lstep;
1707long_str:
1708 convert_to_double(zlow);
1709 convert_to_double(zhigh);
1710 low = Z_DVAL_P(zlow);
1711 high = Z_DVAL_P(zhigh);
1712 lstep = (long) step;
1713
1714 if (low > high) { /* Negative steps */
1715 if (low - high < lstep || lstep <= 0) {
1716 err = 1;
1717 goto err;
1718 }
1719 for (; low >= high; low -= lstep) {
1720 add_next_index_long(return_value, (long)low);
1721 }
1722 } else if (high > low) { /* Positive steps */
1723 if (high - low < lstep || lstep <= 0) {
1724 err = 1;
1725 goto err;
1726 }
1727 for (; low <= high; low += lstep) {
1728 add_next_index_long(return_value, (long)low);
1729 }
1730 } else {
1731 add_next_index_long(return_value, (long)low);
1732 }
1733 }
1734err:
1735 if (err) {
1736 php_error_docref(NULL TSRMLS_CC, E_WARNING, "step exceeds the specified range");
1737 zval_dtor(return_value);
1738 RETURN_FALSE;
1739 }
1740}
1741/* }}} */
1742
1743static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */
1744{
1745 Bucket **elems, *temp;
1746 HashTable *hash;
1747 int j, n_elems, rnd_idx, n_left;
1748
1749 n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
1750
1751 if (n_elems < 1) {
1752 return;
1753 }
1754
1755 elems = (Bucket **)safe_emalloc(n_elems, sizeof(Bucket *), 0);
1756 hash = Z_ARRVAL_P(array);
1757 n_left = n_elems;
1758
1759 for (j = 0, temp = hash->pListHead; temp; temp = temp->pListNext)
1760 elems[j++] = temp;
1761 while (--n_left) {
1762 rnd_idx = php_rand(TSRMLS_C);
1763 RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
1764 if (rnd_idx != n_left) {
1765 temp = elems[n_left];
1766 elems[n_left] = elems[rnd_idx];
1767 elems[rnd_idx] = temp;
1768 }
1769 }
1770
1771 HANDLE_BLOCK_INTERRUPTIONS();
1772 hash->pListHead = elems[0];
1773 hash->pListTail = NULL;
1774 hash->pInternalPointer = hash->pListHead;
1775
1776 for (j = 0; j < n_elems; j++) {
1777 if (hash->pListTail) {
1778 hash->pListTail->pListNext = elems[j];
1779 }
1780 elems[j]->pListLast = hash->pListTail;
1781 elems[j]->pListNext = NULL;
1782 hash->pListTail = elems[j];
1783 }
1784 temp = hash->pListHead;
1785 j = 0;
1786 zend_hash_reindex(hash, 0);
1787 HANDLE_UNBLOCK_INTERRUPTIONS();
1788
1789 efree(elems);
1790}
1791/* }}} */
1792
1793/* {{{ proto bool shuffle(array array_arg)
1794 Randomly shuffle the contents of an array */
1795PHP_FUNCTION(shuffle)
1796{
1797 zval *array;
1798
1799 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
1800 RETURN_FALSE;
1801 }
1802
1803 php_array_data_shuffle(array TSRMLS_CC);
1804
1805 RETURN_TRUE;
1806}
1807/* }}} */
1808
1809PHPAPI void php_splice(HashTable *ht, zend_uint offset, zend_uint length, zval ***list, zend_uint list_count, HashTable *removed TSRMLS_DC) /* {{{ */
1810{
1811 zend_hash_splice(ht, sizeof(zval *), (copy_ctor_func_t) zval_add_ref, offset, length, (void **) list, list_count, removed);
1812
1813 zend_hash_internal_pointer_reset(ht);
1814
1815 if (ht == &EG(symbol_table)) {
1816 zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
1817 }
1818}
1819/* }}} */
1820
1821/* {{{ proto int array_push(array stack, mixed var [, mixed ...])
1822 Pushes elements onto the end of the array */
1823PHP_FUNCTION(array_push)
1824{
1825 zval ***args, /* Function arguments array */
1826 *stack, /* Input array */
1827 *new_var; /* Variable to be pushed */
1828 int i, /* Loop counter */
1829 argc; /* Number of function arguments */
1830
1831
1832 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
1833 return;
1834 }
1835
1836 /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
1837 for (i = 0; i < argc; i++) {
1838 new_var = *args[i];
1839 Z_ADDREF_P(new_var);
1840
1841 if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var, sizeof(zval *), NULL) == FAILURE) {
1842 Z_DELREF_P(new_var);
1843 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
1844 efree(args);
1845 RETURN_FALSE;
1846 }
1847 }
1848
1849 /* Clean up and return the number of values in the stack */
1850 efree(args);
1851 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
1852}
1853/* }}} */
1854
1855/* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */
1856static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
1857{
1858 zval *stack, /* Input stack */
1859 **val; /* Value to be popped */
1860 char *key = NULL;
1861 uint key_len = 0;
1862 ulong index;
1863
1864 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &stack) == FAILURE) {
1865 return;
1866 }
1867
1868 if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
1869 return;
1870 }
1871
1872 /* Get the first or last value and copy it into the return value */
1873 if (off_the_end) {
1874 zend_hash_internal_pointer_end(Z_ARRVAL_P(stack));
1875 } else {
1876 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
1877 }
1878 zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val);
1879 RETVAL_ZVAL_FAST(*val);
1880
1881 /* Delete the first or last value */
1882 zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL);
1883 if (key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
1884 zend_delete_global_variable(key, key_len - 1 TSRMLS_CC);
1885 } else {
1886 zend_hash_del_key_or_index(Z_ARRVAL_P(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX);
1887 }
1888
1889 /* If we did a shift... re-index like it did before */
1890 if (!off_the_end) {
1891 zend_hash_reindex(Z_ARRVAL_P(stack), 1);
1892 } else if (!key_len && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && index >= Z_ARRVAL_P(stack)->nNextFreeElement - 1) {
1893 Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
1894 }
1895
1896 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
1897}
1898/* }}} */
1899
1900/* {{{ proto mixed array_pop(array stack)
1901 Pops an element off the end of the array */
1902PHP_FUNCTION(array_pop)
1903{
1904 _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1905}
1906/* }}} */
1907
1908/* {{{ proto mixed array_shift(array stack)
1909 Pops an element off the beginning of the array */
1910PHP_FUNCTION(array_shift)
1911{
1912 _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1913}
1914/* }}} */
1915
1916/* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
1917 Pushes elements onto the beginning of the array */
1918PHP_FUNCTION(array_unshift)
1919{
1920 zval ***args, /* Function arguments array */
1921 *stack; /* Input stack */
1922 int argc; /* Number of function arguments */
1923
1924 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
1925 return;
1926 }
1927
1928 /* Use splice to insert the elements at the beginning. */
1929 php_splice(Z_ARRVAL_P(stack), 0, 0, args, argc, NULL TSRMLS_CC);
1930
1931 /* Clean up and return the number of elements in the stack */
1932 efree(args);
1933 RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
1934}
1935/* }}} */
1936
1937/* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
1938 Removes the elements designated by offset and length and replace them with supplied array */
1939PHP_FUNCTION(array_splice)
1940{
1941 zval *array, /* Input array */
1942 **repl_array = NULL, /* Replacement array */
1943 ***repl = NULL; /* Replacement elements */
1944 HashTable *rem_hash = NULL; /* Removed elements' hash */
1945 Bucket *p; /* Bucket used for traversing hash */
1946 long i,
1947 offset,
1948 length = 0,
1949 repl_num = 0; /* Number of replacement elements */
1950 int num_in; /* Number of elements in the input array */
1951
1952 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|lZ", &array, &offset, &length, &repl_array) == FAILURE) {
1953 return;
1954 }
1955
1956 num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
1957
1958 if (ZEND_NUM_ARGS() < 3) {
1959 length = num_in;
1960 }
1961
1962 if (repl_array) {
1963 /* Make sure the last argument, if passed, is an array */
1964 convert_to_array_ex(repl_array);
1965
1966 /* Create the array of replacement elements */
1967 repl_num = zend_hash_num_elements(Z_ARRVAL_PP(repl_array));
1968 repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0);
1969 for (p = Z_ARRVAL_PP(repl_array)->pListHead, i = 0; p; p = p->pListNext, i++) {
1970 repl[i] = ((zval **)p->pData);
1971 }
1972 }
1973
1974 /* Clamp the offset */
1975 if (offset < 0 && (offset = num_in + offset) < 0) {
1976 offset = 0;
1977 } else if (offset > num_in) {
1978 offset = num_in;
1979 }
1980
1981 /* Clamp the length */
1982 if (length < 0 && (length = num_in - offset + length) < 0) {
1983 length = 0;
1984 } else if ((unsigned long) offset + (unsigned long) length > (unsigned) num_in) {
1985 length = num_in - offset;
1986 }
1987
1988 /* Don't create the array of removed elements if it's not going
1989 * to be used; e.g. only removing and/or replacing elements */
1990 if (return_value_used) {
1991 array_init_size(return_value, length);
1992 rem_hash = Z_ARRVAL_P(return_value);
1993 }
1994
1995 /* Perform splice */
1996 php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash TSRMLS_CC);
1997
1998 /* Clean up */
1999 if (repl) {
2000 efree(repl);
2001 }
2002}
2003/* }}} */
2004
2005/* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
2006 Returns elements specified by offset and length */
2007PHP_FUNCTION(array_slice)
2008{
2009 zval *input, /* Input array */
2010 **z_length = NULL, /* How many elements to get */
2011 **entry; /* An array entry */
2012 long offset, /* Offset to get elements from */
2013 length = 0;
2014 zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */
2015 int num_in, /* Number of elements in the input array */
2016 pos; /* Current position in the array */
2017 char *string_key;
2018 uint string_key_len;
2019 ulong num_key;
2020 HashPosition hpos;
2021
2022 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|Zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
2023 return;
2024 }
2025
2026 /* Get number of entries in the input hash */
2027 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
2028
2029 /* We want all entries from offset to the end if length is not passed or is null */
2030 if (ZEND_NUM_ARGS() < 3 || Z_TYPE_PP(z_length) == IS_NULL) {
2031 length = num_in;
2032 } else {
2033 convert_to_long_ex(z_length);
2034 length = Z_LVAL_PP(z_length);
2035 }
2036
2037 /* Clamp the offset.. */
2038 if (offset > num_in) {
2039 array_init(return_value);
2040 return;
2041 } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
2042 offset = 0;
2043 }
2044
2045 /* ..and the length */
2046 if (length < 0) {
2047 length = num_in - offset + length;
2048 } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
2049 length = num_in - offset;
2050 }
2051
2052 /* Initialize returned array */
2053 array_init_size(return_value, length > 0 ? length : 0);
2054
2055 if (length <= 0) {
2056 return;
2057 }
2058
2059 /* Start at the beginning and go until we hit offset */
2060 pos = 0;
2061 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &hpos);
2062 while (pos < offset && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
2063 pos++;
2064 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
2065 }
2066
2067 /* Copy elements from input array to the one that's returned */
2068 while (pos < offset + length && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
2069
2070 zval_add_ref(entry);
2071
2072 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &hpos)) {
2073 case HASH_KEY_IS_STRING:
2074 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
2075 break;
2076
2077 case HASH_KEY_IS_LONG:
2078 if (preserve_keys) {
2079 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
2080 } else {
2081 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2082 }
2083 break;
2084 }
2085 pos++;
2086 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
2087 }
2088}
2089/* }}} */
2090
2091PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */
2092{
2093 zval **src_entry, **dest_entry;
2094 char *string_key;
2095 uint string_key_len;
2096 ulong num_key;
2097 HashPosition pos;
2098
2099 zend_hash_internal_pointer_reset_ex(src, &pos);
2100 while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) {
2101 switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
2102 case HASH_KEY_IS_STRING:
2103 if (recursive && zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == SUCCESS) {
2104 HashTable *thash = Z_TYPE_PP(dest_entry) == IS_ARRAY ? Z_ARRVAL_PP(dest_entry) : NULL;
2105 zval *src_zval;
2106 zval *tmp = NULL;
2107
2108 if ((thash && thash->nApplyCount > 1) || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) {
2109 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2110 return 0;
2111 }
2112 SEPARATE_ZVAL(dest_entry);
2113
2114 if (Z_TYPE_PP(dest_entry) == IS_NULL) {
2115 convert_to_array_ex(dest_entry);
2116 add_next_index_null(*dest_entry);
2117 } else {
2118 convert_to_array_ex(dest_entry);
2119 }
2120 if (Z_TYPE_PP(src_entry) == IS_OBJECT) {
2121 ALLOC_ZVAL(src_zval);
2122 INIT_PZVAL_COPY(src_zval, *src_entry);
2123 zval_copy_ctor(src_zval);
2124 convert_to_array(src_zval);
2125 tmp = src_zval;
2126 } else {
2127 src_zval = *src_entry;
2128 }
2129 if (Z_TYPE_P(src_zval) == IS_ARRAY) {
2130 if (thash) {
2131 thash->nApplyCount++;
2132 }
2133 if (!php_array_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_P(src_zval), recursive TSRMLS_CC)) {
2134 if (thash) {
2135 thash->nApplyCount--;
2136 }
2137 return 0;
2138 }
2139 if (thash) {
2140 thash->nApplyCount--;
2141 }
2142 } else {
2143 Z_ADDREF_PP(src_entry);
2144 zend_hash_next_index_insert(Z_ARRVAL_PP(dest_entry), &src_zval, sizeof(zval *), NULL);
2145 }
2146 if (tmp) {
2147 zval_ptr_dtor(&tmp);
2148 }
2149 } else {
2150 Z_ADDREF_PP(src_entry);
2151 zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
2152 }
2153 break;
2154
2155 case HASH_KEY_IS_LONG:
2156 Z_ADDREF_PP(src_entry);
2157 zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL);
2158 break;
2159 }
2160 zend_hash_move_forward_ex(src, &pos);
2161 }
2162 return 1;
2163}
2164/* }}} */
2165
2166PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */
2167{
2168 zval **src_entry, **dest_entry;
2169 char *string_key;
2170 uint string_key_len;
2171 ulong num_key;
2172 HashPosition pos;
2173
2174 for (zend_hash_internal_pointer_reset_ex(src, &pos);
2175 zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS;
2176 zend_hash_move_forward_ex(src, &pos)) {
2177 switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
2178 case HASH_KEY_IS_STRING:
2179 if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
2180 zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == FAILURE ||
2181 Z_TYPE_PP(dest_entry) != IS_ARRAY) {
2182
2183 Z_ADDREF_PP(src_entry);
2184 zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
2185
2186 continue;
2187 }
2188 break;
2189
2190 case HASH_KEY_IS_LONG:
2191 if (Z_TYPE_PP(src_entry) != IS_ARRAY ||
2192 zend_hash_index_find(dest, num_key, (void **)&dest_entry) == FAILURE ||
2193 Z_TYPE_PP(dest_entry) != IS_ARRAY) {
2194
2195 Z_ADDREF_PP(src_entry);
2196 zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL);
2197
2198 continue;
2199 }
2200 break;
2201 }
2202
2203 if (Z_ARRVAL_PP(dest_entry)->nApplyCount > 1 || Z_ARRVAL_PP(src_entry)->nApplyCount > 1 || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) {
2204 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
2205 return 0;
2206 }
2207 SEPARATE_ZVAL(dest_entry);
2208 Z_ARRVAL_PP(dest_entry)->nApplyCount++;
2209 Z_ARRVAL_PP(src_entry)->nApplyCount++;
2210
2211
2212 if (!php_array_replace_recursive(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC)) {
2213 Z_ARRVAL_PP(dest_entry)->nApplyCount--;
2214 Z_ARRVAL_PP(src_entry)->nApplyCount--;
2215 return 0;
2216 }
2217 Z_ARRVAL_PP(dest_entry)->nApplyCount--;
2218 Z_ARRVAL_PP(src_entry)->nApplyCount--;
2219 }
2220
2221 return 1;
2222}
2223/* }}} */
2224
2225static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
2226{
2227 zval ***args = NULL;
2228 int argc, i, init_size = 0;
2229
2230 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
2231 return;
2232 }
2233
2234 for (i = 0; i < argc; i++) {
2235 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
2236 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
2237 efree(args);
2238 RETURN_NULL();
2239 } else {
2240 int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i]));
2241
2242 if (num > init_size) {
2243 init_size = num;
2244 }
2245 }
2246 }
2247
2248 array_init_size(return_value, init_size);
2249
2250 for (i = 0; i < argc; i++) {
2251 if (!replace) {
2252 php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
2253 } else if (recursive && i > 0) { /* First array will be copied directly instead */
2254 php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC);
2255 } else {
2256 zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1);
2257 }
2258 }
2259
2260 efree(args);
2261}
2262/* }}} */
2263
2264/* {{{ proto array array_merge(array arr1, array arr2 [, array ...])
2265 Merges elements from passed arrays into one array */
2266PHP_FUNCTION(array_merge)
2267{
2268 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
2269}
2270/* }}} */
2271
2272/* {{{ proto array array_merge_recursive(array arr1, array arr2 [, array ...])
2273 Recursively merges elements from passed arrays into one array */
2274PHP_FUNCTION(array_merge_recursive)
2275{
2276 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
2277}
2278/* }}} */
2279
2280/* {{{ proto array array_replace(array arr1, array arr2 [, array ...])
2281 Replaces elements from passed arrays into one array */
2282PHP_FUNCTION(array_replace)
2283{
2284 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
2285}
2286/* }}} */
2287
2288/* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...])
2289 Recursively replaces elements from passed arrays into one array */
2290PHP_FUNCTION(array_replace_recursive)
2291{
2292 php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
2293}
2294/* }}} */
2295
2296/* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
2297 Return just the keys from the input array, optionally only for the specified search_value */
2298PHP_FUNCTION(array_keys)
2299{
2300 zval *input, /* Input array */
2301 *search_value = NULL, /* Value to search for */
2302 **entry, /* An entry in the input array */
2303 res, /* Result of comparison */
2304 *new_val; /* New value */
2305 int add_key; /* Flag to indicate whether a key should be added */
2306 zend_bool strict = 0; /* do strict comparison */
2307 HashPosition pos;
2308 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
2309
2310 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
2311 return;
2312 }
2313
2314 if (strict) {
2315 is_equal_func = is_identical_function;
2316 }
2317
2318 /* Initialize return array */
2319 if (search_value != NULL) {
2320 array_init(return_value);
2321 } else {
2322 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2323 }
2324 add_key = 1;
2325
2326 /* Go through input array and add keys to the return array */
2327 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
2328 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2329 if (search_value != NULL) {
2330 is_equal_func(&res, search_value, *entry TSRMLS_CC);
2331 add_key = zval_is_true(&res);
2332 }
2333
2334 if (add_key) {
2335 MAKE_STD_ZVAL(new_val);
2336 zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(input), new_val, &pos);
2337 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
2338 }
2339
2340 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
2341 }
2342}
2343/* }}} */
2344
2345/* {{{ proto array array_values(array input)
2346 Return just the values from the input array */
2347PHP_FUNCTION(array_values)
2348{
2349 zval *input, /* Input array */
2350 **entry; /* An entry in the input array */
2351 HashPosition pos;
2352
2353 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2354 return;
2355 }
2356
2357 /* Initialize return array */
2358 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2359
2360 /* Go through input array and add values to the return array */
2361 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
2362 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2363 zval_add_ref(entry);
2364 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2365 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
2366 }
2367}
2368/* }}} */
2369
2370/* {{{ proto array array_count_values(array input)
2371 Return the value as key and the frequency of that value in input as value */
2372PHP_FUNCTION(array_count_values)
2373{
2374 zval *input, /* Input array */
2375 **entry, /* An entry in the input array */
2376 **tmp;
2377 HashTable *myht;
2378 HashPosition pos;
2379
2380 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
2381 return;
2382 }
2383
2384 /* Initialize return array */
2385 array_init(return_value);
2386
2387 /* Go through input array and add values to the return array */
2388 myht = Z_ARRVAL_P(input);
2389 zend_hash_internal_pointer_reset_ex(myht, &pos);
2390 while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) {
2391 if (Z_TYPE_PP(entry) == IS_LONG) {
2392 if (zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), (void **)&tmp) == FAILURE) {
2393 zval *data;
2394 MAKE_STD_ZVAL(data);
2395 ZVAL_LONG(data, 1);
2396 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
2397 } else {
2398 Z_LVAL_PP(tmp)++;
2399 }
2400 } else if (Z_TYPE_PP(entry) == IS_STRING) {
2401 if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) {
2402 zval *data;
2403 MAKE_STD_ZVAL(data);
2404 ZVAL_LONG(data, 1);
2405 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
2406 } else {
2407 Z_LVAL_PP(tmp)++;
2408 }
2409 } else {
2410 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
2411 }
2412
2413 zend_hash_move_forward_ex(myht, &pos);
2414 }
2415}
2416/* }}} */
2417
2418/* {{{ array_column_param_helper
2419 * Specialized conversion rules for array_column() function
2420 */
2421static inline
2422zend_bool array_column_param_helper(zval **param,
2423 const char *name TSRMLS_DC) {
2424 switch (Z_TYPE_PP(param)) {
2425 case IS_DOUBLE:
2426 convert_to_long_ex(param);
2427 /* fallthrough */
2428 case IS_LONG:
2429 return 1;
2430
2431 case IS_OBJECT:
2432 convert_to_string_ex(param);
2433 /* fallthrough */
2434 case IS_STRING:
2435 return 1;
2436
2437 default:
2438 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The %s key should be either a string or an integer", name);
2439 return 0;
2440 }
2441}
2442
2443/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
2444 Return the values from a single column in the input array, identified by the
2445 value_key and optionally indexed by the index_key */
2446PHP_FUNCTION(array_column)
2447{
2448 zval **zcolumn = NULL, **zkey = NULL, **data;
2449 HashTable *arr_hash;
2450 HashPosition pointer;
2451
2452 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "hZ!|Z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
2453 return;
2454 }
2455
2456 if ((zcolumn && !array_column_param_helper(zcolumn, "column" TSRMLS_CC)) ||
2457 (zkey && !array_column_param_helper(zkey, "index" TSRMLS_CC))) {
2458 RETURN_FALSE;
2459 }
2460
2461 array_init(return_value);
2462 for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
2463 zend_hash_get_current_data_ex(arr_hash, (void**)&data, &pointer) == SUCCESS;
2464 zend_hash_move_forward_ex(arr_hash, &pointer)) {
2465 zval **zcolval, **zkeyval = NULL;
2466 HashTable *ht;
2467
2468 if (Z_TYPE_PP(data) != IS_ARRAY) {
2469 /* Skip elemens which are not sub-arrays */
2470 continue;
2471 }
2472 ht = Z_ARRVAL_PP(data);
2473
2474 if (!zcolumn) {
2475 /* NULL column ID means use entire subarray as data */
2476 zcolval = data;
2477
2478 /* Otherwise, skip if the value doesn't exist in our subarray */
2479 } else if ((Z_TYPE_PP(zcolumn) == IS_STRING) &&
2480 (zend_hash_find(ht, Z_STRVAL_PP(zcolumn), Z_STRLEN_PP(zcolumn) + 1, (void**)&zcolval) == FAILURE)) {
2481 continue;
2482 } else if ((Z_TYPE_PP(zcolumn) == IS_LONG) &&
2483 (zend_hash_index_find(ht, Z_LVAL_PP(zcolumn), (void**)&zcolval) == FAILURE)) {
2484 continue;
2485 }
2486
2487 /* Failure will leave zkeyval alone which will land us on the final else block below
2488 * which is to append the value as next_index
2489 */
2490 if (zkey && (Z_TYPE_PP(zkey) == IS_STRING)) {
2491 zend_hash_find(ht, Z_STRVAL_PP(zkey), Z_STRLEN_PP(zkey) + 1, (void**)&zkeyval);
2492 } else if (zkey && (Z_TYPE_PP(zkey) == IS_LONG)) {
2493 zend_hash_index_find(ht, Z_LVAL_PP(zkey), (void**)&zkeyval);
2494 }
2495
2496 Z_ADDREF_PP(zcolval);
2497 if (zkeyval && Z_TYPE_PP(zkeyval) == IS_STRING) {
2498 add_assoc_zval(return_value, Z_STRVAL_PP(zkeyval), *zcolval);
2499 } else if (zkeyval && Z_TYPE_PP(zkeyval) == IS_LONG) {
2500 add_index_zval(return_value, Z_LVAL_PP(zkeyval), *zcolval);
2501 } else if (zkeyval && Z_TYPE_PP(zkeyval) == IS_OBJECT) {
2502 SEPARATE_ZVAL(zkeyval);
2503 convert_to_string(*zkeyval);
2504 add_assoc_zval(return_value, Z_STRVAL_PP(zkeyval), *zcolval);
2505 } else {
2506 add_next_index_zval(return_value, *zcolval);
2507 }
2508 }
2509}
2510/* }}} */
2511
2512/* {{{ proto array array_reverse(array input [, bool preserve keys])
2513 Return input as a new array with the order of the entries reversed */
2514PHP_FUNCTION(array_reverse)
2515{
2516 zval *input, /* Input array */
2517 **entry; /* An entry in the input array */
2518 char *string_key;
2519 uint string_key_len;
2520 ulong num_key;
2521 zend_bool preserve_keys = 0; /* whether to preserve keys */
2522 HashPosition pos;
2523
2524 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &input, &preserve_keys) == FAILURE) {
2525 return;
2526 }
2527
2528 /* Initialize return array */
2529 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
2530
2531 zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos);
2532 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
2533 zval_add_ref(entry);
2534
2535 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) {
2536 case HASH_KEY_IS_STRING:
2537 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
2538 break;
2539
2540 case HASH_KEY_IS_LONG:
2541 if (preserve_keys) {
2542 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
2543 } else {
2544 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
2545 }
2546 break;
2547 }
2548
2549 zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos);
2550 }
2551}
2552/* }}} */
2553
2554/* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
2555 Returns a copy of input array padded with pad_value to size pad_size */
2556PHP_FUNCTION(array_pad)
2557{
2558 zval *input; /* Input array */
2559 zval *pad_value; /* Padding value obviously */
2560 zval ***pads; /* Array to pass to splice */
2561 long pad_size; /* Size to pad to */
2562 long pad_size_abs; /* Absolute value of pad_size */
2563 int input_size; /* Size of the input array */
2564 int num_pads; /* How many pads do we need */
2565 int do_pad; /* Whether we should do padding at all */
2566 int i;
2567
2568 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "alz", &input, &pad_size, &pad_value) == FAILURE) {
2569 return;
2570 }
2571
2572 /* Do some initial calculations */
2573 input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
2574 pad_size_abs = abs(pad_size);
2575 if (pad_size_abs < 0) {
2576 php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2577 zval_dtor(return_value);
2578 RETURN_FALSE;
2579 }
2580 do_pad = (input_size >= pad_size_abs) ? 0 : 1;
2581
2582 /* Copy the original array */
2583 RETVAL_ZVAL(input, 1, 0);
2584
2585 /* If no need to pad, no need to continue */
2586 if (!do_pad) {
2587 return;
2588 }
2589
2590 /* Populate the pads array */
2591 num_pads = pad_size_abs - input_size;
2592 if (num_pads > 1048576) {
2593 php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
2594 zval_dtor(return_value);
2595 RETURN_FALSE;
2596 }
2597 pads = (zval ***)safe_emalloc(num_pads, sizeof(zval **), 0);
2598 for (i = 0; i < num_pads; i++) {
2599 pads[i] = &pad_value;
2600 }
2601
2602 /* Pad on the right or on the left */
2603 if (pad_size > 0) {
2604 php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL TSRMLS_CC);
2605 } else {
2606 php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL TSRMLS_CC);
2607 }
2608
2609 /* Clean up */
2610 efree(pads);
2611}
2612/* }}} */
2613
2614/* {{{ proto array array_flip(array input)
2615 Return array with key <-> value flipped */
2616PHP_FUNCTION(array_flip)
2617{
2618 zval *array, **entry, *data;
2619 HashPosition pos;
2620
2621 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2622 return;
2623 }
2624
2625 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2626
2627 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
2628 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
2629 MAKE_STD_ZVAL(data);
2630 zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), data, &pos);
2631
2632 if (Z_TYPE_PP(entry) == IS_LONG) {
2633 zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
2634 } else if (Z_TYPE_PP(entry) == IS_STRING) {
2635 zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
2636 } else {
2637 zval_ptr_dtor(&data); /* will free also zval structure */
2638 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
2639 }
2640
2641 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
2642 }
2643}
2644/* }}} */
2645
2646/* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
2647 Retuns an array with all string keys lowercased [or uppercased] */
2648PHP_FUNCTION(array_change_key_case)
2649{
2650 zval *array, **entry;
2651 char *string_key;
2652 char *new_key;
2653 uint str_key_len;
2654 ulong num_key;
2655 long change_to_upper=0;
2656 HashPosition pos;
2657
2658 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &change_to_upper) == FAILURE) {
2659 return;
2660 }
2661
2662 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2663
2664 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
2665 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
2666 zval_add_ref(entry);
2667
2668 switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
2669 case HASH_KEY_IS_LONG:
2670 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(entry), NULL);
2671 break;
2672 case HASH_KEY_IS_STRING:
2673 new_key = estrndup(string_key, str_key_len - 1);
2674 if (change_to_upper) {
2675 php_strtoupper(new_key, str_key_len - 1);
2676 } else {
2677 php_strtolower(new_key, str_key_len - 1);
2678 }
2679 zend_hash_update(Z_ARRVAL_P(return_value), new_key, str_key_len, entry, sizeof(entry), NULL);
2680 efree(new_key);
2681 break;
2682 }
2683
2684 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
2685 }
2686}
2687/* }}} */
2688
2689/* {{{ proto array array_unique(array input [, int sort_flags])
2690 Removes duplicate values from array */
2691PHP_FUNCTION(array_unique)
2692{
2693 zval *array, *tmp;
2694 Bucket *p;
2695 struct bucketindex {
2696 Bucket *b;
2697 unsigned int i;
2698 };
2699 struct bucketindex *arTmp, *cmpdata, *lastkept;
2700 unsigned int i;
2701 long sort_type = PHP_SORT_STRING;
2702
2703 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
2704 return;
2705 }
2706
2707 php_set_compare_func(sort_type TSRMLS_CC);
2708
2709 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
2710 zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*));
2711
2712 if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
2713 return;
2714 }
2715
2716 /* create and sort array with pointers to the target_hash buckets */
2717 arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent);
2718 if (!arTmp) {
2719 zval_dtor(return_value);
2720 RETURN_FALSE;
2721 }
2722 for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) {
2723 arTmp[i].b = p;
2724 arTmp[i].i = i;
2725 }
2726 arTmp[i].b = NULL;
2727 zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC);
2728
2729 /* go through the sorted array and delete duplicates from the copy */
2730 lastkept = arTmp;
2731 for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) {
2732 if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
2733 lastkept = cmpdata;
2734 } else {
2735 if (lastkept->i > cmpdata->i) {
2736 p = lastkept->b;
2737 lastkept = cmpdata;
2738 } else {
2739 p = cmpdata->b;
2740 }
2741 if (p->nKeyLength == 0) {
2742 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
2743 } else {
2744 if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
2745 zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC);
2746 } else {
2747 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
2748 }
2749 }
2750 }
2751 }
2752 pefree(arTmp, Z_ARRVAL_P(array)->persistent);
2753}
2754/* }}} */
2755
2756static int zval_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
2757{
2758 zval result;
2759 zval *first;
2760 zval *second;
2761
2762 first = *((zval **) a);
2763 second = *((zval **) b);
2764
2765 if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
2766 return 0;
2767 }
2768
2769 if (Z_TYPE(result) == IS_DOUBLE) {
2770 if (Z_DVAL(result) < 0) {
2771 return -1;
2772 } else if (Z_DVAL(result) > 0) {
2773 return 1;
2774 } else {
2775 return 0;
2776 }
2777 }
2778
2779 convert_to_long(&result);
2780
2781 if (Z_LVAL(result) < 0) {
2782 return -1;
2783 } else if (Z_LVAL(result) > 0) {
2784 return 1;
2785 }
2786
2787 return 0;
2788}
2789/* }}} */
2790
2791static int zval_user_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */
2792{
2793 zval **args[2];
2794 zval *retval_ptr = NULL;
2795
2796 args[0] = (zval **) a;
2797 args[1] = (zval **) b;
2798
2799 BG(user_compare_fci).param_count = 2;
2800 BG(user_compare_fci).params = args;
2801 BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
2802 BG(user_compare_fci).no_separation = 0;
2803
2804 if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
2805 long retval;
2806
2807 convert_to_long_ex(&retval_ptr);
2808 retval = Z_LVAL_P(retval_ptr);
2809 zval_ptr_dtor(&retval_ptr);
2810 return retval < 0 ? -1 : retval > 0 ? 1 : 0;;
2811 } else {
2812 return 0;
2813 }
2814}
2815/* }}} */
2816
2817static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
2818{
2819 Bucket *p;
2820 int argc, i;
2821 zval ***args;
2822 int (*intersect_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
2823 zend_bool ok;
2824 zval **data;
2825 int req_args;
2826 char *param_spec;
2827
2828 /* Get the argument count */
2829 argc = ZEND_NUM_ARGS();
2830 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
2831 /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
2832 req_args = 3;
2833 param_spec = "+f";
2834 intersect_data_compare_func = zval_user_compare;
2835 } else {
2836 /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
2837 INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
2838 req_args = 2;
2839 param_spec = "+";
2840
2841 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
2842 intersect_data_compare_func = zval_compare;
2843 }
2844 }
2845
2846 if (argc < req_args) {
2847 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
2848 return;
2849 }
2850
2851 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
2852 return;
2853 }
2854
2855 for (i = 0; i < argc; i++) {
2856 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
2857 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
2858 RETVAL_NULL();
2859 goto out;
2860 }
2861 }
2862
2863 array_init(return_value);
2864
2865 for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
2866 if (p->nKeyLength == 0) {
2867 ok = 1;
2868 for (i = 1; i < argc; i++) {
2869 if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == FAILURE ||
2870 (intersect_data_compare_func &&
2871 intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
2872 ) {
2873 ok = 0;
2874 break;
2875 }
2876 }
2877 if (ok) {
2878 Z_ADDREF_PP((zval**)p->pData);
2879 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
2880 }
2881 } else {
2882 ok = 1;
2883 for (i = 1; i < argc; i++) {
2884 if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == FAILURE ||
2885 (intersect_data_compare_func &&
2886 intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)
2887 ) {
2888 ok = 0;
2889 break;
2890 }
2891 }
2892 if (ok) {
2893 Z_ADDREF_PP((zval**)p->pData);
2894 zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
2895 }
2896 }
2897 }
2898out:
2899 efree(args);
2900}
2901/* }}} */
2902
2903static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
2904{
2905 zval ***args = NULL;
2906 HashTable *hash;
2907 int arr_argc, i, c = 0;
2908 Bucket ***lists, **list, ***ptrs, *p;
2909 int req_args;
2910 char *param_spec;
2911 zend_fcall_info fci1, fci2;
2912 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
2913 zend_fcall_info *fci_key = NULL, *fci_data;
2914 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
2915 PHP_ARRAY_CMP_FUNC_VARS;
2916
2917 int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC);
2918 int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC);
2919
2920 if (behavior == INTERSECT_NORMAL) {
2921 intersect_key_compare_func = php_array_key_compare;
2922
2923 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
2924 /* array_intersect() */
2925 req_args = 2;
2926 param_spec = "+";
2927 intersect_data_compare_func = php_array_data_compare;
2928 } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
2929 /* array_uintersect() */
2930 req_args = 3;
2931 param_spec = "+f";
2932 intersect_data_compare_func = php_array_user_compare;
2933 } else {
2934 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
2935 return;
2936 }
2937
2938 if (ZEND_NUM_ARGS() < req_args) {
2939 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
2940 return;
2941 }
2942
2943 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
2944 return;
2945 }
2946 fci_data = &fci1;
2947 fci_data_cache = &fci1_cache;
2948
2949 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
2950 /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
2951 * no comparison of the data is done (part of INTERSECT_ASSOC) */
2952 intersect_key_compare_func = php_array_key_compare;
2953
2954 if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
2955 /* array_intersect_assoc() or array_intersect_key() */
2956 req_args = 2;
2957 param_spec = "+";
2958 intersect_key_compare_func = php_array_key_compare;
2959 intersect_data_compare_func = php_array_data_compare;
2960 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
2961 /* array_uintersect_assoc() */
2962 req_args = 3;
2963 param_spec = "+f";
2964 intersect_key_compare_func = php_array_key_compare;
2965 intersect_data_compare_func = php_array_user_compare;
2966 fci_data = &fci1;
2967 fci_data_cache = &fci1_cache;
2968 } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
2969 /* array_intersect_uassoc() or array_intersect_ukey() */
2970 req_args = 3;
2971 param_spec = "+f";
2972 intersect_key_compare_func = php_array_user_key_compare;
2973 intersect_data_compare_func = php_array_data_compare;
2974 fci_key = &fci1;
2975 fci_key_cache = &fci1_cache;
2976 } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
2977 /* array_uintersect_uassoc() */
2978 req_args = 4;
2979 param_spec = "+ff";
2980 intersect_key_compare_func = php_array_user_key_compare;
2981 intersect_data_compare_func = php_array_user_compare;
2982 fci_data = &fci1;
2983 fci_data_cache = &fci1_cache;
2984 fci_key = &fci2;
2985 fci_key_cache = &fci2_cache;
2986 } else {
2987 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type);
2988 return;
2989 }
2990
2991 if (ZEND_NUM_ARGS() < req_args) {
2992 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
2993 return;
2994 }
2995
2996 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
2997 return;
2998 }
2999
3000 } else {
3001 php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3002 return;
3003 }
3004
3005 PHP_ARRAY_CMP_FUNC_BACKUP();
3006
3007 /* for each argument, create and sort list with pointers to the hash buckets */
3008 lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3009 ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3010 php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3011
3012 if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
3013 BG(user_compare_fci) = *fci_data;
3014 BG(user_compare_fci_cache) = *fci_data_cache;
3015 } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
3016 BG(user_compare_fci) = *fci_key;
3017 BG(user_compare_fci_cache) = *fci_key_cache;
3018 }
3019
3020 for (i = 0; i < arr_argc; i++) {
3021 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3022 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3023 arr_argc = i; /* only free up to i - 1 */
3024 goto out;
3025 }
3026 hash = Z_ARRVAL_PP(args[i]);
3027 list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
3028 if (!list) {
3029 PHP_ARRAY_CMP_FUNC_RESTORE();
3030
3031 efree(ptrs);
3032 efree(lists);
3033 efree(args);
3034 RETURN_FALSE;
3035 }
3036 lists[i] = list;
3037 ptrs[i] = list;
3038 for (p = hash->pListHead; p; p = p->pListNext) {
3039 *list++ = p;
3040 }
3041 *list = NULL;
3042 if (behavior == INTERSECT_NORMAL) {
3043 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC);
3044 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3045 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC);
3046 }
3047 }
3048
3049 /* copy the argument array */
3050 RETVAL_ZVAL(*args[0], 1, 0);
3051 if (return_value->value.ht == &EG(symbol_table)) {
3052 HashTable *ht;
3053 zval *tmp;
3054
3055 ALLOC_HASHTABLE(ht);
3056 zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
3057 zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
3058 return_value->value.ht = ht;
3059 }
3060
3061 /* go through the lists and look for common values */
3062 while (*ptrs[0]) {
3063 if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
3064 &&
3065 key_compare_type == INTERSECT_COMP_KEY_USER) {
3066
3067 BG(user_compare_fci) = *fci_key;
3068 BG(user_compare_fci_cache) = *fci_key_cache;
3069 }
3070
3071 for (i = 1; i < arr_argc; i++) {
3072 if (behavior & INTERSECT_NORMAL) {
3073 while (*ptrs[i] && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3074 ptrs[i]++;
3075 }
3076 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3077 while (*ptrs[i] && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3078 ptrs[i]++;
3079 }
3080 if ((!c && *ptrs[i]) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
3081 /* this means that ptrs[i] is not NULL so we can compare
3082 * and "c==0" is from last operation
3083 * in this branch of code we enter only when INTERSECT_ASSOC
3084 * since when we have INTERSECT_KEY compare of data is not wanted. */
3085 if (data_compare_type == INTERSECT_COMP_DATA_USER) {
3086 BG(user_compare_fci) = *fci_data;
3087 BG(user_compare_fci_cache) = *fci_data_cache;
3088 }
3089 if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) {
3090 c = 1;
3091 if (key_compare_type == INTERSECT_COMP_KEY_USER) {
3092 BG(user_compare_fci) = *fci_key;
3093 BG(user_compare_fci_cache) = *fci_key_cache;
3094 /* When KEY_USER, the last parameter is always the callback */
3095 }
3096 /* we are going to the break */
3097 } else {
3098 /* continue looping */
3099 }
3100 }
3101 }
3102 if (!*ptrs[i]) {
3103 /* delete any values corresponding to remains of ptrs[0] */
3104 /* and exit because they do not present in at least one of */
3105 /* the other arguments */
3106 for (;;) {
3107 p = *ptrs[0]++;
3108 if (!p) {
3109 goto out;
3110 }
3111 if (p->nKeyLength == 0) {
3112 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3113 } else {
3114 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3115 }
3116 }
3117 }
3118 if (c) /* here we get if not all are equal */
3119 break;
3120 ptrs[i]++;
3121 }
3122 if (c) {
3123 /* Value of ptrs[0] not in all arguments, delete all entries */
3124 /* with value < value of ptrs[i] */
3125 for (;;) {
3126 p = *ptrs[0];
3127 if (p->nKeyLength == 0) {
3128 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3129 } else {
3130 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3131 }
3132 if (!*++ptrs[0]) {
3133 goto out;
3134 }
3135 if (behavior == INTERSECT_NORMAL) {
3136 if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) {
3137 break;
3138 }
3139 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3140 /* no need of looping because indexes are unique */
3141 break;
3142 }
3143 }
3144 } else {
3145 /* ptrs[0] is present in all the arguments */
3146 /* Skip all entries with same value as ptrs[0] */
3147 for (;;) {
3148 if (!*++ptrs[0]) {
3149 goto out;
3150 }
3151 if (behavior == INTERSECT_NORMAL) {
3152 if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3153 break;
3154 }
3155 } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
3156 /* no need of looping because indexes are unique */
3157 break;
3158 }
3159 }
3160 }
3161 }
3162out:
3163 for (i = 0; i < arr_argc; i++) {
3164 hash = Z_ARRVAL_PP(args[i]);
3165 pefree(lists[i], hash->persistent);
3166 }
3167
3168 PHP_ARRAY_CMP_FUNC_RESTORE();
3169
3170 efree(ptrs);
3171 efree(lists);
3172 efree(args);
3173}
3174/* }}} */
3175
3176/* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
3177 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. */
3178PHP_FUNCTION(array_intersect_key)
3179{
3180 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
3181}
3182/* }}} */
3183
3184/* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
3185 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. */
3186PHP_FUNCTION(array_intersect_ukey)
3187{
3188 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3189}
3190/* }}} */
3191
3192/* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
3193 Returns the entries of arr1 that have values which are present in all the other arguments */
3194PHP_FUNCTION(array_intersect)
3195{
3196 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
3197}
3198/* }}} */
3199
3200/* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
3201 Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback. */
3202PHP_FUNCTION(array_uintersect)
3203{
3204 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
3205}
3206/* }}} */
3207
3208/* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
3209 Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
3210PHP_FUNCTION(array_intersect_assoc)
3211{
3212 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
3213}
3214/* }}} */
3215
3216/* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
3217 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 an user-supplied callback. */
3218PHP_FUNCTION(array_intersect_uassoc)
3219{
3220 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
3221}
3222/* }}} */
3223
3224/* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
3225 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 an user-supplied callback. */
3226PHP_FUNCTION(array_uintersect_assoc)
3227{
3228 php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
3229}
3230/* }}} */
3231
3232/* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
3233 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. */
3234PHP_FUNCTION(array_uintersect_uassoc)
3235{
3236 php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
3237}
3238/* }}} */
3239
3240static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
3241{
3242 Bucket *p;
3243 int argc, i;
3244 zval ***args;
3245 int (*diff_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
3246 zend_bool ok;
3247 zval **data;
3248
3249 /* Get the argument count */
3250 argc = ZEND_NUM_ARGS();
3251 if (data_compare_type == DIFF_COMP_DATA_USER) {
3252 if (argc < 3) {
3253 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
3254 return;
3255 }
3256 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
3257 return;
3258 }
3259 diff_data_compare_func = zval_user_compare;
3260 } else {
3261 if (argc < 2) {
3262 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
3263 return;
3264 }
3265 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3266 return;
3267 }
3268 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3269 diff_data_compare_func = zval_compare;
3270 }
3271 }
3272
3273 for (i = 0; i < argc; i++) {
3274 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3275 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3276 RETVAL_NULL();
3277 goto out;
3278 }
3279 }
3280
3281 array_init(return_value);
3282
3283 for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
3284 if (p->nKeyLength == 0) {
3285 ok = 1;
3286 for (i = 1; i < argc; i++) {
3287 if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS &&
3288 (!diff_data_compare_func ||
3289 diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
3290 ) {
3291 ok = 0;
3292 break;
3293 }
3294 }
3295 if (ok) {
3296 Z_ADDREF_PP((zval**)p->pData);
3297 zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
3298 }
3299 } else {
3300 ok = 1;
3301 for (i = 1; i < argc; i++) {
3302 if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS &&
3303 (!diff_data_compare_func ||
3304 diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)
3305 ) {
3306 ok = 0;
3307 break;
3308 }
3309 }
3310 if (ok) {
3311 Z_ADDREF_PP((zval**)p->pData);
3312 zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
3313 }
3314 }
3315 }
3316out:
3317 efree(args);
3318}
3319/* }}} */
3320
3321static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
3322{
3323 zval ***args = NULL;
3324 HashTable *hash;
3325 int arr_argc, i, c;
3326 Bucket ***lists, **list, ***ptrs, *p;
3327 int req_args;
3328 char *param_spec;
3329 zend_fcall_info fci1, fci2;
3330 zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
3331 zend_fcall_info *fci_key = NULL, *fci_data;
3332 zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
3333 PHP_ARRAY_CMP_FUNC_VARS;
3334
3335 int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC);
3336 int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC);
3337
3338 if (behavior == DIFF_NORMAL) {
3339 diff_key_compare_func = php_array_key_compare;
3340
3341 if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
3342 /* array_diff */
3343 req_args = 2;
3344 param_spec = "+";
3345 diff_data_compare_func = php_array_data_compare;
3346 } else if (data_compare_type == DIFF_COMP_DATA_USER) {
3347 /* array_udiff */
3348 req_args = 3;
3349 param_spec = "+f";
3350 diff_data_compare_func = php_array_user_compare;
3351 } else {
3352 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
3353 return;
3354 }
3355
3356 if (ZEND_NUM_ARGS() < req_args) {
3357 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3358 return;
3359 }
3360
3361 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
3362 return;
3363 }
3364 fci_data = &fci1;
3365 fci_data_cache = &fci1_cache;
3366
3367 } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
3368 /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
3369 * no comparison of the data is done (part of DIFF_ASSOC) */
3370
3371 if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3372 /* array_diff_assoc() or array_diff_key() */
3373 req_args = 2;
3374 param_spec = "+";
3375 diff_key_compare_func = php_array_key_compare;
3376 diff_data_compare_func = php_array_data_compare;
3377 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
3378 /* array_udiff_assoc() */
3379 req_args = 3;
3380 param_spec = "+f";
3381 diff_key_compare_func = php_array_key_compare;
3382 diff_data_compare_func = php_array_user_compare;
3383 fci_data = &fci1;
3384 fci_data_cache = &fci1_cache;
3385 } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
3386 /* array_diff_uassoc() or array_diff_ukey() */
3387 req_args = 3;
3388 param_spec = "+f";
3389 diff_key_compare_func = php_array_user_key_compare;
3390 diff_data_compare_func = php_array_data_compare;
3391 fci_key = &fci1;
3392 fci_key_cache = &fci1_cache;
3393 } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
3394 /* array_udiff_uassoc() */
3395 req_args = 4;
3396 param_spec = "+ff";
3397 diff_key_compare_func = php_array_user_key_compare;
3398 diff_data_compare_func = php_array_user_compare;
3399 fci_data = &fci1;
3400 fci_data_cache = &fci1_cache;
3401 fci_key = &fci2;
3402 fci_key_cache = &fci2_cache;
3403 } else {
3404 php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type);
3405 return;
3406 }
3407
3408 if (ZEND_NUM_ARGS() < req_args) {
3409 php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
3410 return;
3411 }
3412
3413 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
3414 return;
3415 }
3416
3417 } else {
3418 php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
3419 return;
3420 }
3421
3422 PHP_ARRAY_CMP_FUNC_BACKUP();
3423
3424 /* for each argument, create and sort list with pointers to the hash buckets */
3425 lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3426 ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
3427 php_set_compare_func(PHP_SORT_STRING TSRMLS_CC);
3428
3429 if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
3430 BG(user_compare_fci) = *fci_data;
3431 BG(user_compare_fci_cache) = *fci_data_cache;
3432 } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
3433 BG(user_compare_fci) = *fci_key;
3434 BG(user_compare_fci_cache) = *fci_key_cache;
3435 }
3436
3437 for (i = 0; i < arr_argc; i++) {
3438 if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
3439 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
3440 arr_argc = i; /* only free up to i - 1 */
3441 goto out;
3442 }
3443 hash = Z_ARRVAL_PP(args[i]);
3444 list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
3445 if (!list) {
3446 PHP_ARRAY_CMP_FUNC_RESTORE();
3447
3448 efree(ptrs);
3449 efree(lists);
3450 efree(args);
3451 RETURN_FALSE;
3452 }
3453 lists[i] = list;
3454 ptrs[i] = list;
3455 for (p = hash->pListHead; p; p = p->pListNext) {
3456 *list++ = p;
3457 }
3458 *list = NULL;
3459 if (behavior == DIFF_NORMAL) {
3460 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_data_compare_func TSRMLS_CC);
3461 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3462 zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_key_compare_func TSRMLS_CC);
3463 }
3464 }
3465
3466 /* copy the argument array */
3467 RETVAL_ZVAL(*args[0], 1, 0);
3468 if (return_value->value.ht == &EG(symbol_table)) {
3469 HashTable *ht;
3470 zval *tmp;
3471
3472 ALLOC_HASHTABLE(ht);
3473 zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
3474 zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
3475 return_value->value.ht = ht;
3476 }
3477
3478 /* go through the lists and look for values of ptr[0] that are not in the others */
3479 while (*ptrs[0]) {
3480 if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
3481 &&
3482 key_compare_type == DIFF_COMP_KEY_USER
3483 ) {
3484 BG(user_compare_fci) = *fci_key;
3485 BG(user_compare_fci_cache) = *fci_key_cache;
3486 }
3487 c = 1;
3488 for (i = 1; i < arr_argc; i++) {
3489 Bucket **ptr = ptrs[i];
3490 if (behavior == DIFF_NORMAL) {
3491 while (*ptrs[i] && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
3492 ptrs[i]++;
3493 }
3494 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3495 while (*ptr && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
3496 ptr++;
3497 }
3498 }
3499 if (!c) {
3500 if (behavior == DIFF_NORMAL) {
3501 if (*ptrs[i]) {
3502 ptrs[i]++;
3503 }
3504 break;
3505 } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
3506 /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
3507 * data comparison is not needed - skipped. */
3508 if (*ptr) {
3509 if (data_compare_type == DIFF_COMP_DATA_USER) {
3510 BG(user_compare_fci) = *fci_data;
3511 BG(user_compare_fci_cache) = *fci_data_cache;
3512 }
3513 if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) {
3514 /* the data is not the same */
3515 c = -1;
3516 if (key_compare_type == DIFF_COMP_KEY_USER) {
3517 BG(user_compare_fci) = *fci_key;
3518 BG(user_compare_fci_cache) = *fci_key_cache;
3519 }
3520 } else {
3521 break;
3522 /* we have found the element in other arrays thus we don't want it
3523 * in the return_value -> delete from there */
3524 }
3525 }
3526 } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
3527 /* the behavior here differs from INTERSECT_KEY in php_intersect
3528 * since in the "diff" case we have to remove the entry from
3529 * return_value while when doing intersection the entry must not
3530 * be deleted. */
3531 break; /* remove the key */
3532 }
3533 }
3534 }
3535 if (!c) {
3536 /* ptrs[0] in one of the other arguments */
3537 /* delete all entries with value as ptrs[0] */
3538 for (;;) {
3539 p = *ptrs[0];
3540 if (p->nKeyLength == 0) {
3541 zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
3542 } else {
3543 zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h);
3544 }
3545 if (!*++ptrs[0]) {
3546 goto out;
3547 }
3548 if (behavior == DIFF_NORMAL) {
3549 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3550 break;
3551 }
3552 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3553 /* in this case no array_key_compare is needed */
3554 break;
3555 }
3556 }
3557 } else {
3558 /* ptrs[0] in none of the other arguments */
3559 /* skip all entries with value as ptrs[0] */
3560 for (;;) {
3561 if (!*++ptrs[0]) {
3562 goto out;
3563 }
3564 if (behavior == DIFF_NORMAL) {
3565 if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
3566 break;
3567 }
3568 } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
3569 /* in this case no array_key_compare is needed */
3570 break;
3571 }
3572 }
3573 }
3574 }
3575out:
3576 for (i = 0; i < arr_argc; i++) {
3577 hash = Z_ARRVAL_PP(args[i]);
3578 pefree(lists[i], hash->persistent);
3579 }
3580
3581 PHP_ARRAY_CMP_FUNC_RESTORE();
3582
3583 efree(ptrs);
3584 efree(lists);
3585 efree(args);
3586}
3587/* }}} */
3588
3589/* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
3590 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. */
3591PHP_FUNCTION(array_diff_key)
3592{
3593 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
3594}
3595/* }}} */
3596
3597/* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
3598 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. */
3599PHP_FUNCTION(array_diff_ukey)
3600{
3601 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3602}
3603/* }}} */
3604
3605/* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
3606 Returns the entries of arr1 that have values which are not present in any of the others arguments. */
3607PHP_FUNCTION(array_diff)
3608{
3609 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
3610}
3611/* }}} */
3612
3613/* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
3614 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. */
3615PHP_FUNCTION(array_udiff)
3616{
3617 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
3618}
3619/* }}} */
3620
3621/* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
3622 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 */
3623PHP_FUNCTION(array_diff_assoc)
3624{
3625 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
3626}
3627/* }}} */
3628
3629/* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
3630 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. */
3631PHP_FUNCTION(array_diff_uassoc)
3632{
3633 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
3634}
3635/* }}} */
3636
3637/* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
3638 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. */
3639PHP_FUNCTION(array_udiff_assoc)
3640{
3641 php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
3642}
3643/* }}} */
3644
3645/* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
3646 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. */
3647PHP_FUNCTION(array_udiff_uassoc)
3648{
3649 php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
3650}
3651/* }}} */
3652
3653#define MULTISORT_ORDER 0
3654#define MULTISORT_TYPE 1
3655#define MULTISORT_LAST 2
3656
3657PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
3658{
3659 Bucket **ab = *(Bucket ***)a;
3660 Bucket **bb = *(Bucket ***)b;
3661 int r;
3662 int result = 0;
3663 zval temp;
3664
3665 r = 0;
3666 do {
3667 php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC);
3668
3669 ARRAYG(compare_func)(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData) TSRMLS_CC);
3670 result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp);
3671 if (result != 0) {
3672 return result;
3673 }
3674 r++;
3675 } while (ab[r] != NULL);
3676
3677 return result;
3678}
3679/* }}} */
3680
3681#define MULTISORT_ABORT \
3682 for (k = 0; k < MULTISORT_LAST; k++) \
3683 efree(ARRAYG(multisort_flags)[k]); \
3684 efree(arrays); \
3685 efree(args); \
3686 RETURN_FALSE;
3687
3688/* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]], ...])
3689 Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
3690PHP_FUNCTION(array_multisort)
3691{
3692 zval*** args;
3693 zval*** arrays;
3694 Bucket*** indirect;
3695 Bucket* p;
3696 HashTable* hash;
3697 int argc;
3698 int array_size;
3699 int num_arrays = 0;
3700 int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
3701 int sort_order = PHP_SORT_ASC;
3702 int sort_type = PHP_SORT_REGULAR;
3703 int i, k;
3704
3705 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
3706 return;
3707 }
3708
3709 /* Allocate space for storing pointers to input arrays and sort flags. */
3710 arrays = (zval ***)ecalloc(argc, sizeof(zval **));
3711 for (i = 0; i < MULTISORT_LAST; i++) {
3712 parse_state[i] = 0;
3713 ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
3714 }
3715
3716 /* Here we go through the input arguments and parse them. Each one can
3717 * be either an array or a sort flag which follows an array. If not
3718 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
3719 * accordingly. There can't be two sort flags of the same type after an
3720 * array, and the very first argument has to be an array. */
3721 for (i = 0; i < argc; i++) {
3722 if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
3723 /* We see the next array, so we update the sort flags of
3724 * the previous array and reset the sort flags. */
3725 if (i > 0) {
3726 ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
3727 ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
3728 sort_order = PHP_SORT_ASC;
3729 sort_type = PHP_SORT_REGULAR;
3730 }
3731 arrays[num_arrays++] = args[i];
3732
3733 /* Next one may be an array or a list of sort flags. */
3734 for (k = 0; k < MULTISORT_LAST; k++) {
3735 parse_state[k] = 1;
3736 }
3737 } else if (Z_TYPE_PP(args[i]) == IS_LONG) {
3738 switch (Z_LVAL_PP(args[i]) & ~PHP_SORT_FLAG_CASE) {
3739 case PHP_SORT_ASC:
3740 case PHP_SORT_DESC:
3741 /* flag allowed here */
3742 if (parse_state[MULTISORT_ORDER] == 1) {
3743 /* Save the flag and make sure then next arg is not the current flag. */
3744 sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1;
3745 parse_state[MULTISORT_ORDER] = 0;
3746 } else {
3747 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
3748 MULTISORT_ABORT;
3749 }
3750 break;
3751
3752 case PHP_SORT_REGULAR:
3753 case PHP_SORT_NUMERIC:
3754 case PHP_SORT_STRING:
3755 case PHP_SORT_NATURAL:
3756#if HAVE_STRCOLL
3757 case PHP_SORT_LOCALE_STRING:
3758#endif
3759 /* flag allowed here */
3760 if (parse_state[MULTISORT_TYPE] == 1) {
3761 /* Save the flag and make sure then next arg is not the current flag. */
3762 sort_type = Z_LVAL_PP(args[i]);
3763 parse_state[MULTISORT_TYPE] = 0;
3764 } else {
3765 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
3766 MULTISORT_ABORT;
3767 }
3768 break;
3769
3770 default:
3771 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
3772 MULTISORT_ABORT;
3773 break;
3774
3775 }
3776 } else {
3777 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
3778 MULTISORT_ABORT;
3779 }
3780 }
3781 /* Take care of the last array sort flags. */
3782 ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
3783 ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
3784
3785 /* Make sure the arrays are of the same size. */
3786 array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0]));
3787 for (i = 0; i < num_arrays; i++) {
3788 if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) {
3789 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
3790 MULTISORT_ABORT;
3791 }
3792 }
3793
3794 /* If all arrays are empty we don't need to do anything. */
3795 if (array_size < 1) {
3796 for (k = 0; k < MULTISORT_LAST; k++) {
3797 efree(ARRAYG(multisort_flags)[k]);
3798 }
3799 efree(arrays);
3800 efree(args);
3801 RETURN_TRUE;
3802 }
3803
3804 /* Create the indirection array. This array is of size MxN, where
3805 * M is the number of entries in each input array and N is the number
3806 * of the input arrays + 1. The last column is NULL to indicate the end
3807 * of the row. */
3808 indirect = (Bucket ***)safe_emalloc(array_size, sizeof(Bucket **), 0);
3809 for (i = 0; i < array_size; i++) {
3810 indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0);
3811 }
3812 for (i = 0; i < num_arrays; i++) {
3813 k = 0;
3814 for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) {
3815 indirect[k][i] = p;
3816 }
3817 }
3818 for (k = 0; k < array_size; k++) {
3819 indirect[k][num_arrays] = NULL;
3820 }
3821
3822 /* Do the actual sort magic - bada-bim, bada-boom. */
3823 zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
3824
3825 /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
3826 HANDLE_BLOCK_INTERRUPTIONS();
3827 for (i = 0; i < num_arrays; i++) {
3828 hash = Z_ARRVAL_PP(arrays[i]);
3829 hash->pListHead = indirect[0][i];;
3830 hash->pListTail = NULL;
3831 hash->pInternalPointer = hash->pListHead;
3832
3833 for (k = 0; k < array_size; k++) {
3834 if (hash->pListTail) {
3835 hash->pListTail->pListNext = indirect[k][i];
3836 }
3837 indirect[k][i]->pListLast = hash->pListTail;
3838 indirect[k][i]->pListNext = NULL;
3839 hash->pListTail = indirect[k][i];
3840 }
3841
3842 zend_hash_reindex(hash, 1);
3843 }
3844 HANDLE_UNBLOCK_INTERRUPTIONS();
3845
3846 /* Clean up. */
3847 for (i = 0; i < array_size; i++) {
3848 efree(indirect[i]);
3849 }
3850 efree(indirect);
3851 for (k = 0; k < MULTISORT_LAST; k++) {
3852 efree(ARRAYG(multisort_flags)[k]);
3853 }
3854 efree(arrays);
3855 efree(args);
3856 RETURN_TRUE;
3857}
3858/* }}} */
3859
3860/* {{{ proto mixed array_rand(array input [, int num_req])
3861 Return key/keys for random entry/entries in the array */
3862PHP_FUNCTION(array_rand)
3863{
3864 zval *input;
3865 long randval, num_req = 1;
3866 int num_avail, key_type;
3867 char *string_key;
3868 uint string_key_len;
3869 ulong num_key;
3870 HashPosition pos;
3871
3872 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) {
3873 return;
3874 }
3875
3876 num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
3877
3878 if (ZEND_NUM_ARGS() > 1) {
3879 if (num_req <= 0 || num_req > num_avail) {
3880 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
3881 return;
3882 }
3883 }
3884
3885 /* Make the return value an array only if we need to pass back more than one result. */
3886 if (num_req > 1) {
3887 array_init_size(return_value, num_req);
3888 }
3889
3890 /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
3891 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
3892 while (num_req && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTENT) {
3893
3894 randval = php_rand(TSRMLS_C);
3895
3896 if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
3897 /* If we are returning a single result, just do it. */
3898 if (Z_TYPE_P(return_value) != IS_ARRAY) {
3899 if (key_type == HASH_KEY_IS_STRING) {
3900 RETURN_STRINGL(string_key, string_key_len - 1, 1);
3901 } else {
3902 RETURN_LONG(num_key);
3903 }
3904 } else {
3905 /* Append the result to the return value. */
3906 if (key_type == HASH_KEY_IS_STRING) {
3907 add_next_index_stringl(return_value, string_key, string_key_len - 1, 1);
3908 } else {
3909 add_next_index_long(return_value, num_key);
3910 }
3911 }
3912 num_req--;
3913 }
3914 num_avail--;
3915 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
3916 }
3917}
3918/* }}} */
3919
3920/* {{{ proto mixed array_sum(array input)
3921 Returns the sum of the array entries */
3922PHP_FUNCTION(array_sum)
3923{
3924 zval *input,
3925 **entry,
3926 entry_n;
3927 HashPosition pos;
3928
3929 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
3930 return;
3931 }
3932
3933 ZVAL_LONG(return_value, 0);
3934
3935 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
3936 zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
3937 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
3938 ) {
3939 if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
3940 continue;
3941 }
3942 entry_n = **entry;
3943 zval_copy_ctor(&entry_n);
3944 convert_scalar_to_number(&entry_n TSRMLS_CC);
3945 fast_add_function(return_value, return_value, &entry_n TSRMLS_CC);
3946 }
3947}
3948/* }}} */
3949
3950/* {{{ proto mixed array_product(array input)
3951 Returns the product of the array entries */
3952PHP_FUNCTION(array_product)
3953{
3954 zval *input,
3955 **entry,
3956 entry_n;
3957 HashPosition pos;
3958 double dval;
3959
3960 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) {
3961 return;
3962 }
3963
3964 ZVAL_LONG(return_value, 1);
3965 if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
3966 return;
3967 }
3968
3969 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
3970 zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS;
3971 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos)
3972 ) {
3973 if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) {
3974 continue;
3975 }
3976 entry_n = **entry;
3977 zval_copy_ctor(&entry_n);
3978 convert_scalar_to_number(&entry_n TSRMLS_CC);
3979
3980 if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
3981 dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
3982 if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) {
3983 Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
3984 continue;
3985 }
3986 }
3987 convert_to_double(return_value);
3988 convert_to_double(&entry_n);
3989 Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
3990 }
3991}
3992/* }}} */
3993
3994/* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
3995 Iteratively reduce the array to a single value via the callback. */
3996PHP_FUNCTION(array_reduce)
3997{
3998 zval *input;
3999 zval **args[2];
4000 zval **operand;
4001 zval *result = NULL;
4002 zval *retval;
4003 zend_fcall_info fci;
4004 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4005 zval *initial = NULL;
4006 HashPosition pos;
4007 HashTable *htbl;
4008
4009 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
4010 return;
4011 }
4012
4013 if (ZEND_NUM_ARGS() > 2) {
4014 ALLOC_ZVAL(result);
4015 MAKE_COPY_ZVAL(&initial, result);
4016 } else {
4017 MAKE_STD_ZVAL(result);
4018 ZVAL_NULL(result);
4019 }
4020
4021 /* (zval **)input points to an element of argument stack
4022 * the base pointer of which is subject to change.
4023 * thus we need to keep the pointer to the hashtable for safety */
4024 htbl = Z_ARRVAL_P(input);
4025
4026 if (zend_hash_num_elements(htbl) == 0) {
4027 if (result) {
4028 RETVAL_ZVAL(result, 1, 1);
4029 }
4030 return;
4031 }
4032
4033 fci.retval_ptr_ptr = &retval;
4034 fci.param_count = 2;
4035 fci.no_separation = 0;
4036
4037 zend_hash_internal_pointer_reset_ex(htbl, &pos);
4038 while (zend_hash_get_current_data_ex(htbl, (void **)&operand, &pos) == SUCCESS) {
4039
4040 if (result) {
4041 args[0] = &result;
4042 args[1] = operand;
4043 fci.params = args;
4044
4045 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
4046 zval_ptr_dtor(&result);
4047 result = retval;
4048 } else {
4049 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback");
4050 return;
4051 }
4052 } else {
4053 result = *operand;
4054 zval_add_ref(&result);
4055 }
4056 zend_hash_move_forward_ex(htbl, &pos);
4057 }
4058 RETVAL_ZVAL(result, 1, 1);
4059}
4060/* }}} */
4061
4062/* {{{ proto array array_filter(array input [, mixed callback])
4063 Filters elements from the array via the callback. */
4064PHP_FUNCTION(array_filter)
4065{
4066 zval *array;
4067 zval **operand;
4068 zval **args[2];
4069 zval *retval = NULL;
4070 zval *key = NULL;
4071 zend_bool have_callback = 0;
4072 long use_type = 0;
4073 char *string_key;
4074 zend_fcall_info fci = empty_fcall_info;
4075 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4076 uint string_key_len;
4077 ulong num_key;
4078 HashPosition pos;
4079
4080 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
4081 return;
4082 }
4083
4084 array_init(return_value);
4085 if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
4086 return;
4087 }
4088
4089 if (ZEND_NUM_ARGS() > 1) {
4090 have_callback = 1;
4091 fci.no_separation = 0;
4092 fci.retval_ptr_ptr = &retval;
4093
4094 if (use_type == ARRAY_FILTER_USE_BOTH) {
4095 fci.param_count = 2;
4096 args[1] = &key;
4097 } else {
4098 fci.param_count = 1;
4099 if (use_type == ARRAY_FILTER_USE_KEY) {
4100 args[0] = &key;
4101 }
4102 }
4103 }
4104
4105 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
4106 zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&operand, &pos) == SUCCESS;
4107 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
4108 ) {
4109 int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos);
4110
4111 if (have_callback) {
4112 if (use_type) {
4113 MAKE_STD_ZVAL(key);
4114 /* Set up the key */
4115 switch (key_type) {
4116 case HASH_KEY_IS_LONG:
4117 Z_TYPE_P(key) = IS_LONG;
4118 Z_LVAL_P(key) = num_key;
4119 break;
4120
4121 case HASH_KEY_IS_STRING:
4122 ZVAL_STRINGL(key, string_key, string_key_len - 1, 1);
4123 break;
4124 }
4125 }
4126
4127 if (use_type != ARRAY_FILTER_USE_KEY) {
4128 args[0] = operand;
4129 }
4130 fci.params = args;
4131
4132 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
4133 int retval_true = zend_is_true(retval);
4134
4135 zval_ptr_dtor(&retval);
4136 if (use_type) {
4137 zval_ptr_dtor(&key);
4138 }
4139 if (!retval_true) {
4140 continue;
4141 }
4142 } else {
4143 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
4144 return;
4145 }
4146 } else if (!zend_is_true(*operand)) {
4147 continue;
4148 }
4149
4150 zval_add_ref(operand);
4151 switch (key_type) {
4152 case HASH_KEY_IS_STRING:
4153 zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
4154 break;
4155
4156 case HASH_KEY_IS_LONG:
4157 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand, sizeof(zval *), NULL);
4158 break;
4159 }
4160 }
4161}
4162/* }}} */
4163
4164/* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
4165 Applies the callback to the elements in given arrays. */
4166PHP_FUNCTION(array_map)
4167{
4168 zval ***arrays = NULL;
4169 int n_arrays = 0;
4170 zval ***params;
4171 zval *result, *null;
4172 HashPosition *array_pos;
4173 zval **args;
4174 zend_fcall_info fci = empty_fcall_info;
4175 zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
4176 int i, k, maxlen = 0;
4177 int *array_len;
4178
4179 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) {
4180 return;
4181 }
4182
4183 RETVAL_NULL();
4184
4185 args = (zval **)safe_emalloc(n_arrays, sizeof(zval *), 0);
4186 array_len = (int *)safe_emalloc(n_arrays, sizeof(int), 0);
4187 array_pos = (HashPosition *)safe_emalloc(n_arrays, sizeof(HashPosition), 0);
4188
4189 for (i = 0; i < n_arrays; i++) {
4190 if (Z_TYPE_PP(arrays[i]) != IS_ARRAY) {
4191 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2);
4192 efree(arrays);
4193 efree(args);
4194 efree(array_len);
4195 efree(array_pos);
4196 return;
4197 }
4198 SEPARATE_ZVAL_IF_NOT_REF(arrays[i]);
4199 args[i] = *arrays[i];
4200 array_len[i] = zend_hash_num_elements(Z_ARRVAL_PP(arrays[i]));
4201 if (array_len[i] > maxlen) {
4202 maxlen = array_len[i];
4203 }
4204 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(arrays[i]), &array_pos[i]);
4205 }
4206
4207 efree(arrays);
4208
4209 /* Short-circuit: if no callback and only one array, just return it. */
4210 if (!ZEND_FCI_INITIALIZED(fci) && n_arrays == 1) {
4211 RETVAL_ZVAL(args[0], 1, 0);
4212 efree(array_len);
4213 efree(array_pos);
4214 efree(args);
4215 return;
4216 }
4217
4218 array_init_size(return_value, maxlen);
4219 params = (zval ***)safe_emalloc(n_arrays, sizeof(zval **), 0);
4220 MAKE_STD_ZVAL(null);
4221 ZVAL_NULL(null);
4222
4223 /* We iterate through all the arrays at once. */
4224 for (k = 0; k < maxlen; k++) {
4225 uint str_key_len;
4226 ulong num_key;
4227 char *str_key;
4228 int key_type = 0;
4229
4230 /* If no callback, the result will be an array, consisting of current
4231 * entries from all arrays. */
4232 if (!ZEND_FCI_INITIALIZED(fci)) {
4233 MAKE_STD_ZVAL(result);
4234 array_init_size(result, n_arrays);
4235 }
4236
4237 for (i = 0; i < n_arrays; i++) {
4238 /* If this array still has elements, add the current one to the
4239 * parameter list, otherwise use null value. */
4240 if (k < array_len[i]) {
4241 zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), (void **)&params[i], &array_pos[i]);
4242
4243 /* It is safe to store only last value of key type, because
4244 * this loop will run just once if there is only 1 array. */
4245 if (n_arrays == 1) {
4246 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(args[0]), &str_key, &str_key_len, &num_key, 0, &array_pos[i]);
4247 }
4248 zend_hash_move_forward_ex(Z_ARRVAL_P(args[i]), &array_pos[i]);
4249 } else {
4250 params[i] = &null;
4251 }
4252
4253 if (!ZEND_FCI_INITIALIZED(fci)) {
4254 zval_add_ref(params[i]);
4255 add_next_index_zval(result, *params[i]);
4256 }
4257 }
4258
4259 if (ZEND_FCI_INITIALIZED(fci)) {
4260 fci.retval_ptr_ptr = &result;
4261 fci.param_count = n_arrays;
4262 fci.params = params;
4263 fci.no_separation = 0;
4264
4265 if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || !result) {
4266 php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
4267 efree(array_len);
4268 efree(args);
4269 efree(array_pos);
4270 zval_dtor(return_value);
4271 zval_ptr_dtor(&null);
4272 efree(params);
4273 RETURN_NULL();
4274 }
4275 }
4276
4277 if (n_arrays > 1) {
4278 add_next_index_zval(return_value, result);
4279 } else {
4280 if (key_type == HASH_KEY_IS_STRING) {
4281 add_assoc_zval_ex(return_value, str_key, str_key_len, result);
4282 } else {
4283 add_index_zval(return_value, num_key, result);
4284 }
4285 }
4286 }
4287
4288 zval_ptr_dtor(&null);
4289 efree(params);
4290 efree(array_len);
4291 efree(array_pos);
4292 efree(args);
4293}
4294/* }}} */
4295
4296/* {{{ proto bool array_key_exists(mixed key, array search)
4297 Checks if the given key or index exists in the array */
4298PHP_FUNCTION(array_key_exists)
4299{
4300 zval *key; /* key to check for */
4301 HashTable *array; /* array to check in */
4302
4303 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zH", &key, &array) == FAILURE) {
4304 return;
4305 }
4306
4307 switch (Z_TYPE_P(key)) {
4308 case IS_STRING:
4309 if (zend_symtable_exists(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1)) {
4310 RETURN_TRUE;
4311 }
4312 RETURN_FALSE;
4313 case IS_LONG:
4314 if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
4315 RETURN_TRUE;
4316 }
4317 RETURN_FALSE;
4318 case IS_NULL:
4319 if (zend_hash_exists(array, "", 1)) {
4320 RETURN_TRUE;
4321 }
4322 RETURN_FALSE;
4323
4324 default:
4325 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
4326 RETURN_FALSE;
4327 }
4328}
4329/* }}} */
4330
4331/* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
4332 Split array into chunks */
4333PHP_FUNCTION(array_chunk)
4334{
4335 int argc = ZEND_NUM_ARGS(), key_type, num_in;
4336 long size, current = 0;
4337 char *str_key;
4338 uint str_key_len;
4339 ulong num_key;
4340 zend_bool preserve_keys = 0;
4341 zval *input = NULL;
4342 zval *chunk = NULL;
4343 zval **entry;
4344 HashPosition pos;
4345
4346 if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) {
4347 return;
4348 }
4349 /* Do bounds checking for size parameter. */
4350 if (size < 1) {
4351 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0");
4352 return;
4353 }
4354
4355 num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
4356
4357 if (size > num_in) {
4358 size = num_in > 0 ? num_in : 1;
4359 }
4360
4361 array_init_size(return_value, ((num_in - 1) / size) + 1);
4362
4363 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
4364 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&entry, &pos) == SUCCESS) {
4365 /* If new chunk, create and initialize it. */
4366 if (!chunk) {
4367 MAKE_STD_ZVAL(chunk);
4368 array_init_size(chunk, size);
4369 }
4370
4371 /* Add entry to the chunk, preserving keys if necessary. */
4372 zval_add_ref(entry);
4373
4374 if (preserve_keys) {
4375 key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &str_key, &str_key_len, &num_key, 0, &pos);
4376 switch (key_type) {
4377 case HASH_KEY_IS_STRING:
4378 add_assoc_zval_ex(chunk, str_key, str_key_len, *entry);
4379 break;
4380 default:
4381 add_index_zval(chunk, num_key, *entry);
4382 break;
4383 }
4384 } else {
4385 add_next_index_zval(chunk, *entry);
4386 }
4387
4388 /* If reached the chunk size, add it to the result array, and reset the
4389 * pointer. */
4390 if (!(++current % size)) {
4391 add_next_index_zval(return_value, chunk);
4392 chunk = NULL;
4393 }
4394
4395 zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
4396 }
4397
4398 /* Add the final chunk if there is one. */
4399 if (chunk) {
4400 add_next_index_zval(return_value, chunk);
4401 }
4402}
4403/* }}} */
4404
4405/* {{{ proto array array_combine(array keys, array values)
4406 Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
4407PHP_FUNCTION(array_combine)
4408{
4409 zval *values, *keys;
4410 HashPosition pos_values, pos_keys;
4411 zval **entry_keys, **entry_values;
4412 int num_keys, num_values;
4413
4414 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) {
4415 return;
4416 }
4417
4418 num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys));
4419 num_values = zend_hash_num_elements(Z_ARRVAL_P(values));
4420
4421 if (num_keys != num_values) {
4422 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements");
4423 RETURN_FALSE;
4424 }
4425
4426 array_init_size(return_value, num_keys);
4427
4428 if (!num_keys) {
4429 return;
4430 }
4431
4432 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos_keys);
4433 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
4434 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry_keys, &pos_keys) == SUCCESS &&
4435 zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&entry_values, &pos_values) == SUCCESS
4436 ) {
4437 if (Z_TYPE_PP(entry_keys) == IS_LONG) {
4438 zval_add_ref(entry_values);
4439 add_index_zval(return_value, Z_LVAL_PP(entry_keys), *entry_values);
4440 } else {
4441 zval key, *key_ptr = *entry_keys;
4442
4443 if (Z_TYPE_PP(entry_keys) != IS_STRING) {
4444 key = **entry_keys;
4445 zval_copy_ctor(&key);
4446 convert_to_string(&key);
4447 key_ptr = &key;
4448 }
4449
4450 zval_add_ref(entry_values);
4451 add_assoc_zval_ex(return_value, Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, *entry_values);
4452
4453 if (key_ptr != *entry_keys) {
4454 zval_dtor(&key);
4455 }
4456 }
4457
4458 zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos_keys);
4459 zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
4460 }
4461}
4462/* }}} */
4463
4464/*
4465 * Local variables:
4466 * tab-width: 4
4467 * c-basic-offset: 4
4468 * End:
4469 * vim600: noet sw=4 ts=4 fdm=marker
4470 * vim<600: noet sw=4 ts=4
4471 */
4472