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