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