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