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