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