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