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