#include "php.h"
#include "php_ini.h"
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <stdio.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef PHP_WIN32
#include "win32/unistd.h"
#endif
#include "zend_globals.h"
#include "zend_interfaces.h"
#include "php_globals.h"
#include "php_array.h"
#include "basic_functions.h"
#include "php_string.h"
#include "php_rand.h"
#include "php_math.h"
#include "zend_smart_str.h"
#include "zend_bitset.h"
#include "ext/spl/spl_array.h"
#define EXTR_OVERWRITE 0
#define EXTR_SKIP 1
#define EXTR_PREFIX_SAME 2
#define EXTR_PREFIX_ALL 3
#define EXTR_PREFIX_INVALID 4
#define EXTR_PREFIX_IF_EXISTS 5
#define EXTR_IF_EXISTS 6
#define EXTR_REFS 0x100
#define CASE_LOWER 0
#define CASE_UPPER 1
#define DIFF_NORMAL 1
#define DIFF_KEY 2
#define DIFF_ASSOC 6
#define DIFF_COMP_DATA_NONE -1
#define DIFF_COMP_DATA_INTERNAL 0
#define DIFF_COMP_DATA_USER 1
#define DIFF_COMP_KEY_INTERNAL 0
#define DIFF_COMP_KEY_USER 1
#define INTERSECT_NORMAL 1
#define INTERSECT_KEY 2
#define INTERSECT_ASSOC 6
#define INTERSECT_COMP_DATA_NONE -1
#define INTERSECT_COMP_DATA_INTERNAL 0
#define INTERSECT_COMP_DATA_USER 1
#define INTERSECT_COMP_KEY_INTERNAL 0
#define INTERSECT_COMP_KEY_USER 1
ZEND_DECLARE_MODULE_GLOBALS(array)
static void php_array_init_globals(zend_array_globals *array_globals)
{
memset(array_globals, 0, sizeof(zend_array_globals));
}
PHP_MINIT_FUNCTION(array)
{
ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(array)
{
#ifdef ZTS
ts_free_id(array_globals_id);
#endif
return SUCCESS;
}
static int php_array_key_compare(const void *a, const void *b)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
zend_uchar t;
zend_long l1, l2;
double d;
if (f->key == NULL) {
if (s->key == NULL) {
return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
} else {
l1 = (zend_long)f->h;
t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
if (t == IS_LONG) {
} else if (t == IS_DOUBLE) {
return ZEND_NORMALIZE_BOOL((double)l1 - d);
} else {
l2 = 0;
}
}
} else {
if (s->key) {
return zendi_smart_strcmp(f->key, s->key);
} else {
l2 = (zend_long)s->h;
t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
if (t == IS_LONG) {
} else if (t == IS_DOUBLE) {
return ZEND_NORMALIZE_BOOL(d - (double)l2);
} else {
l1 = 0;
}
}
}
return l1 > l2 ? 1 : (l1 < l2 ? -1 : 0);
}
static int php_array_reverse_key_compare(const void *a, const void *b)
{
return php_array_key_compare(b, a);
}
static int php_array_key_compare_numeric(const void *a, const void *b)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
if (f->key == NULL && s->key == NULL) {
return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
} else {
double d1, d2;
if (f->key) {
d1 = zend_strtod(f->key->val, NULL);
} else {
d1 = (double)(zend_long)f->h;
}
if (s->key) {
d2 = zend_strtod(s->key->val, NULL);
} else {
d2 = (double)(zend_long)s->h;
}
return ZEND_NORMALIZE_BOOL(d1 - d2);
}
}
static int php_array_reverse_key_compare_numeric(const void *a, const void *b)
{
return php_array_key_compare_numeric(b, a);
}
static int php_array_key_compare_string_case(const void *a, const void *b)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
char *s1, *s2;
size_t l1, l2;
char buf1[MAX_LENGTH_OF_LONG + 1];
char buf2[MAX_LENGTH_OF_LONG + 1];
if (f->key) {
s1 = f->key->val;
l1 = f->key->len;
} else {
s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
l1 = buf1 + sizeof(buf1) - 1 - s1;
}
if (s->key) {
s2 = s->key->val;
l2 = s->key->len;
} else {
s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
l2 = buf2 + sizeof(buf2) - 1 - s1;
}
return zend_binary_strcasecmp_l(s1, l1, s2, l2);
}
static int php_array_reverse_key_compare_string_case(const void *a, const void *b)
{
return php_array_key_compare_string_case(b, a);
}
static int php_array_key_compare_string(const void *a, const void *b)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
char *s1, *s2;
size_t l1, l2;
char buf1[MAX_LENGTH_OF_LONG + 1];
char buf2[MAX_LENGTH_OF_LONG + 1];
if (f->key) {
s1 = f->key->val;
l1 = f->key->len;
} else {
s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
l1 = buf1 + sizeof(buf1) - 1 - s1;
}
if (s->key) {
s2 = s->key->val;
l2 = s->key->len;
} else {
s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
l2 = buf2 + sizeof(buf2) - 1 - s2;
}
return zend_binary_strcmp(s1, l1, s2, l2);
}
static int php_array_reverse_key_compare_string(const void *a, const void *b)
{
return php_array_key_compare_string(b, a);
}
static int php_array_key_compare_string_natural_general(const void *a, const void *b, int fold_case)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
char *s1, *s2;
size_t l1, l2;
char buf1[MAX_LENGTH_OF_LONG + 1];
char buf2[MAX_LENGTH_OF_LONG + 1];
if (f->key) {
s1 = f->key->val;
l1 = f->key->len;
} else {
s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
l1 = buf1 + sizeof(buf1) - 1 - s1;
}
if (s->key) {
s2 = s->key->val;
l2 = s->key->len;
} else {
s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
l2 = buf2 + sizeof(buf2) - 1 - s1;
}
return strnatcmp_ex(s1, l1, s2, l2, fold_case);
}
static int php_array_key_compare_string_natural_case(const void *a, const void *b)
{
return php_array_key_compare_string_natural_general(a, b, 1);
}
static int php_array_reverse_key_compare_string_natural_case(const void *a, const void *b)
{
return php_array_key_compare_string_natural_general(b, a, 1);
}
static int php_array_key_compare_string_natural(const void *a, const void *b)
{
return php_array_key_compare_string_natural_general(a, b, 0);
}
static int php_array_reverse_key_compare_string_natural(const void *a, const void *b)
{
return php_array_key_compare_string_natural_general(b, a, 0);
}
#if HAVE_STRCOLL
static int php_array_key_compare_string_locale(const void *a, const void *b)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
char *s1, *s2;
char buf1[MAX_LENGTH_OF_LONG + 1];
char buf2[MAX_LENGTH_OF_LONG + 1];
if (f->key) {
s1 = f->key->val;
} else {
s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
}
if (s->key) {
s2 = s->key->val;
} else {
s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
}
return strcoll(s1, s2);
}
static int php_array_reverse_key_compare_string_locale(const void *a, const void *b)
{
return php_array_key_compare_string_locale(b, a);
}
#endif
static int php_array_data_compare(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval result;
zval *first;
zval *second;
f = (Bucket *) a;
s = (Bucket *) b;
first = &f->val;
second = &s->val;
if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
first = Z_INDIRECT_P(first);
}
if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
second = Z_INDIRECT_P(second);
}
if (compare_function(&result, first, second) == FAILURE) {
return 0;
}
ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
return Z_LVAL(result);
}
static int php_array_reverse_data_compare(const void *a, const void *b)
{
return php_array_data_compare(a, b) * -1;
}
static int php_array_data_compare_numeric(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval *first;
zval *second;
f = (Bucket *) a;
s = (Bucket *) b;
first = &f->val;
second = &s->val;
if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
first = Z_INDIRECT_P(first);
}
if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
second = Z_INDIRECT_P(second);
}
return numeric_compare_function(first, second);
}
static int php_array_reverse_data_compare_numeric(const void *a, const void *b)
{
return php_array_data_compare_numeric(b, a);
}
static int php_array_data_compare_string_case(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval *first;
zval *second;
f = (Bucket *) a;
s = (Bucket *) b;
first = &f->val;
second = &s->val;
if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
first = Z_INDIRECT_P(first);
}
if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
second = Z_INDIRECT_P(second);
}
return string_case_compare_function(first, second);
}
static int php_array_reverse_data_compare_string_case(const void *a, const void *b)
{
return php_array_data_compare_string_case(b, a);
}
static int php_array_data_compare_string(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval *first;
zval *second;
f = (Bucket *) a;
s = (Bucket *) b;
first = &f->val;
second = &s->val;
if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
first = Z_INDIRECT_P(first);
}
if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
second = Z_INDIRECT_P(second);
}
return string_compare_function(first, second);
}
static int php_array_reverse_data_compare_string(const void *a, const void *b)
{
return php_array_data_compare_string(b, a);
}
static int php_array_natural_general_compare(const void *a, const void *b, int fold_case)
{
Bucket *f = (Bucket *) a;
Bucket *s = (Bucket *) b;
zend_string *str1 = zval_get_string(&f->val);
zend_string *str2 = zval_get_string(&s->val);
int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
zend_string_release(str1);
zend_string_release(str2);
return result;
}
static int php_array_natural_compare(const void *a, const void *b)
{
return php_array_natural_general_compare(a, b, 0);
}
static int php_array_reverse_natural_compare(const void *a, const void *b)
{
return php_array_natural_general_compare(b, a, 0);
}
static int php_array_natural_case_compare(const void *a, const void *b)
{
return php_array_natural_general_compare(a, b, 1);
}
static int php_array_reverse_natural_case_compare(const void *a, const void *b)
{
return php_array_natural_general_compare(b, a, 1);
}
#if HAVE_STRCOLL
static int php_array_data_compare_string_locale(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval *first;
zval *second;
f = (Bucket *) a;
s = (Bucket *) b;
first = &f->val;
second = &s->val;
if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
first = Z_INDIRECT_P(first);
}
if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
second = Z_INDIRECT_P(second);
}
return string_locale_compare_function(first, second);
}
static int php_array_reverse_data_compare_string_locale(const void *a, const void *b)
{
return php_array_data_compare_string_locale(b, a);
}
#endif
static compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse)
{
switch (sort_type & ~PHP_SORT_FLAG_CASE) {
case PHP_SORT_NUMERIC:
if (reverse) {
return php_array_reverse_key_compare_numeric;
} else {
return php_array_key_compare_numeric;
}
break;
case PHP_SORT_STRING:
if (sort_type & PHP_SORT_FLAG_CASE) {
if (reverse) {
return php_array_reverse_key_compare_string_case;
} else {
return php_array_key_compare_string_case;
}
} else {
if (reverse) {
return php_array_reverse_key_compare_string;
} else {
return php_array_key_compare_string;
}
}
break;
case PHP_SORT_NATURAL:
if (sort_type & PHP_SORT_FLAG_CASE) {
if (reverse) {
return php_array_reverse_key_compare_string_natural_case;
} else {
return php_array_key_compare_string_natural_case;
}
} else {
if (reverse) {
return php_array_reverse_key_compare_string_natural;
} else {
return php_array_key_compare_string_natural;
}
}
break;
#if HAVE_STRCOLL
case PHP_SORT_LOCALE_STRING:
if (reverse) {
return php_array_reverse_key_compare_string_locale;
} else {
return php_array_key_compare_string_locale;
}
break;
#endif
case PHP_SORT_REGULAR:
default:
if (reverse) {
return php_array_reverse_key_compare;
} else {
return php_array_key_compare;
}
break;
}
return NULL;
}
static compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse)
{
switch (sort_type & ~PHP_SORT_FLAG_CASE) {
case PHP_SORT_NUMERIC:
if (reverse) {
return php_array_reverse_data_compare_numeric;
} else {
return php_array_data_compare_numeric;
}
break;
case PHP_SORT_STRING:
if (sort_type & PHP_SORT_FLAG_CASE) {
if (reverse) {
return php_array_reverse_data_compare_string_case;
} else {
return php_array_data_compare_string_case;
}
} else {
if (reverse) {
return php_array_reverse_data_compare_string;
} else {
return php_array_data_compare_string;
}
}
break;
case PHP_SORT_NATURAL:
if (sort_type & PHP_SORT_FLAG_CASE) {
if (reverse) {
return php_array_reverse_natural_case_compare;
} else {
return php_array_natural_case_compare;
}
} else {
if (reverse) {
return php_array_reverse_natural_compare;
} else {
return php_array_natural_compare;
}
}
break;
#if HAVE_STRCOLL
case PHP_SORT_LOCALE_STRING:
if (reverse) {
return php_array_reverse_data_compare_string_locale;
} else {
return php_array_data_compare_string_locale;
}
break;
#endif
case PHP_SORT_REGULAR:
default:
if (reverse) {
return php_array_reverse_data_compare;
} else {
return php_array_data_compare;
}
break;
}
return NULL;
}
PHP_FUNCTION(krsort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_ARRAY_EX(array, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(sort_type)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
cmp = php_get_key_compare_func(sort_type, 1);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(ksort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_ARRAY_EX(array, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(sort_type)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
cmp = php_get_key_compare_func(sort_type, 0);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHPAPI zend_long php_count_recursive(zval *array, zend_long mode)
{
zend_long cnt = 0;
zval *element;
if (Z_TYPE_P(array) == IS_ARRAY) {
if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
}
cnt = zend_array_count(Z_ARRVAL_P(array));
if (mode == COUNT_RECURSIVE) {
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
Z_ARRVAL_P(array)->u.v.nApplyCount++;
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
ZVAL_DEREF(element);
cnt += php_count_recursive(element, COUNT_RECURSIVE);
} ZEND_HASH_FOREACH_END();
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
Z_ARRVAL_P(array)->u.v.nApplyCount--;
}
}
}
return cnt;
}
PHP_FUNCTION(count)
{
zval *array;
zend_long mode = COUNT_NORMAL;
zend_long cnt;
zval *element;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_ZVAL(array)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(mode)
ZEND_PARSE_PARAMETERS_END();
switch (Z_TYPE_P(array)) {
case IS_NULL:
RETURN_LONG(0);
break;
case IS_ARRAY:
cnt = zend_array_count(Z_ARRVAL_P(array));
if (mode == COUNT_RECURSIVE) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
ZVAL_DEREF(element);
cnt += php_count_recursive(element, COUNT_RECURSIVE);
} ZEND_HASH_FOREACH_END();
}
RETURN_LONG(cnt);
break;
case IS_OBJECT: {
zval retval;
if (Z_OBJ_HT_P(array)->count_elements) {
RETVAL_LONG(1);
if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value))) {
return;
}
}
if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) {
zend_call_method_with_0_params(array, NULL, NULL, "count", &retval);
if (Z_TYPE(retval) != IS_UNDEF) {
RETVAL_LONG(zval_get_long(&retval));
zval_ptr_dtor(&retval);
}
return;
}
}
default:
RETURN_LONG(1);
break;
}
}
static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
{
zval *array;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) {
return;
}
if (fold_case) {
if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0) == FAILURE) {
return;
}
} else {
if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0) == FAILURE) {
return;
}
}
RETURN_TRUE;
}
PHP_FUNCTION(natsort)
{
php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
PHP_FUNCTION(natcasesort)
{
php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
PHP_FUNCTION(asort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
cmp = php_get_data_compare_func(sort_type, 0);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(arsort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
cmp = php_get_data_compare_func(sort_type, 1);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(sort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
cmp = php_get_data_compare_func(sort_type, 0);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(rsort)
{
zval *array;
zend_long sort_type = PHP_SORT_REGULAR;
compare_func_t cmp;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
cmp = php_get_data_compare_func(sort_type, 1);
if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
static int php_array_user_compare(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval args[2];
zval retval;
f = (Bucket *) a;
s = (Bucket *) b;
ZVAL_COPY(&args[0], &f->val);
ZVAL_COPY(&args[1], &s->val);
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
zend_long ret = zval_get_long(&retval);
zval_ptr_dtor(&retval);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
return ret < 0 ? -1 : ret > 0 ? 1 : 0;
} else {
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
return 0;
}
}
#define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
if (!zend_is_callable(*func_name, 0, NULL)) { \
php_error_docref(NULL, E_WARNING, "Invalid comparison function"); \
BG(user_compare_fci) = old_user_compare_fci; \
BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
RETURN_FALSE; \
} \
#define PHP_ARRAY_CMP_FUNC_VARS \
zend_fcall_info old_user_compare_fci; \
zend_fcall_info_cache old_user_compare_fci_cache \
#define PHP_ARRAY_CMP_FUNC_BACKUP() \
old_user_compare_fci = BG(user_compare_fci); \
old_user_compare_fci_cache = BG(user_compare_fci_cache); \
BG(user_compare_fci_cache) = empty_fcall_info_cache; \
#define PHP_ARRAY_CMP_FUNC_RESTORE() \
BG(user_compare_fci) = old_user_compare_fci; \
BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber)
{
zval *array;
zend_array *arr;
zend_bool retval;
PHP_ARRAY_CMP_FUNC_VARS;
PHP_ARRAY_CMP_FUNC_BACKUP();
if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
return;
}
arr = Z_ARR_P(array);
if (zend_hash_num_elements(arr) == 0) {
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_TRUE;
}
arr = zend_array_dup(arr);
retval = zend_hash_sort(arr, compare_func, renumber) != FAILURE;
zval_ptr_dtor(array);
ZVAL_ARR(array, arr);
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_BOOL(retval);
}
PHP_FUNCTION(usort)
{
php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
}
PHP_FUNCTION(uasort)
{
php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
}
static int php_array_user_key_compare(const void *a, const void *b)
{
Bucket *f;
Bucket *s;
zval args[2];
zval retval;
zend_long result;
ZVAL_NULL(&args[0]);
ZVAL_NULL(&args[1]);
f = (Bucket *) a;
s = (Bucket *) b;
if (f->key == NULL) {
ZVAL_LONG(&args[0], f->h);
} else {
ZVAL_STR_COPY(&args[0], f->key);
}
if (s->key == NULL) {
ZVAL_LONG(&args[1], s->h);
} else {
ZVAL_STR_COPY(&args[1], s->key);
}
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
result = zval_get_long(&retval);
zval_ptr_dtor(&retval);
} else {
result = 0;
}
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
return result < 0 ? -1 : result > 0 ? 1 : 0;
}
PHP_FUNCTION(uksort)
{
php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
}
PHP_FUNCTION(end)
{
HashTable *array;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
ZEND_PARSE_PARAMETERS_END();
zend_hash_internal_pointer_end(array);
if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) {
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
ZVAL_DEREF(entry);
ZVAL_COPY(return_value, entry);
}
}
PHP_FUNCTION(prev)
{
HashTable *array;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
ZEND_PARSE_PARAMETERS_END();
zend_hash_move_backwards(array);
if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) {
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
ZVAL_DEREF(entry);
ZVAL_COPY(return_value, entry);
}
}
PHP_FUNCTION(next)
{
HashTable *array;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
ZEND_PARSE_PARAMETERS_END();
zend_hash_move_forward(array);
if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) {
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
ZVAL_DEREF(entry);
ZVAL_COPY(return_value, entry);
}
}
PHP_FUNCTION(reset)
{
HashTable *array;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
ZEND_PARSE_PARAMETERS_END();
zend_hash_internal_pointer_reset(array);
if (USED_RET()) {
if ((entry = zend_hash_get_current_data(array)) == NULL) {
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
ZVAL_DEREF(entry);
ZVAL_COPY(return_value, entry);
}
}
PHP_FUNCTION(current)
{
HashTable *array;
zval *entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT(array)
ZEND_PARSE_PARAMETERS_END();
if ((entry = zend_hash_get_current_data(array)) == NULL) {
RETURN_FALSE;
}
if (Z_TYPE_P(entry) == IS_INDIRECT) {
entry = Z_INDIRECT_P(entry);
}
ZVAL_DEREF(entry);
ZVAL_COPY(return_value, entry);
}
PHP_FUNCTION(key)
{
HashTable *array;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_OR_OBJECT_HT(array)
ZEND_PARSE_PARAMETERS_END();
zend_hash_get_current_key_zval(array, return_value);
}
PHP_FUNCTION(min)
{
int argc;
zval *args = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
if (argc == 1) {
zval *result;
if (Z_TYPE(args[0]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array");
RETVAL_NULL();
} else {
if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) {
ZVAL_DEREF(result);
ZVAL_COPY(return_value, result);
} else {
php_error_docref(NULL, E_WARNING, "Array must contain at least one element");
RETVAL_FALSE;
}
}
} else {
zval *min, result;
int i;
min = &args[0];
for (i = 1; i < argc; i++) {
is_smaller_function(&result, &args[i], min);
if (Z_TYPE(result) == IS_TRUE) {
min = &args[i];
}
}
ZVAL_DEREF(min);
ZVAL_COPY(return_value, min);
}
}
PHP_FUNCTION(max)
{
zval *args = NULL;
int argc;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
if (argc == 1) {
zval *result;
if (Z_TYPE(args[0]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array");
RETVAL_NULL();
} else {
if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) {
ZVAL_DEREF(result);
ZVAL_COPY(return_value, result);
} else {
php_error_docref(NULL, E_WARNING, "Array must contain at least one element");
RETVAL_FALSE;
}
}
} else {
zval *max, result;
int i;
max = &args[0];
for (i = 1; i < argc; i++) {
is_smaller_or_equal_function(&result, &args[i], max);
if (Z_TYPE(result) == IS_FALSE) {
max = &args[i];
}
}
ZVAL_DEREF(max);
ZVAL_COPY(return_value, max);
}
}
static int php_array_walk(zval *array, zval *userdata, int recursive)
{
zval args[3],
retval,
*zv;
HashTable *target_hash = HASH_OF(array);
HashPosition pos;
uint32_t ht_iter;
int result = SUCCESS;
ZVAL_UNDEF(&args[1]);
if (userdata) {
ZVAL_COPY(&args[2], userdata);
}
BG(array_walk_fci).retval = &retval;
BG(array_walk_fci).param_count = userdata ? 3 : 2;
BG(array_walk_fci).params = args;
BG(array_walk_fci).no_separation = 0;
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
ht_iter = zend_hash_iterator_add(target_hash, pos);
do {
zv = zend_hash_get_current_data_ex(target_hash, &pos);
if (zv == NULL) {
break;
}
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
if (Z_TYPE_P(zv) == IS_UNDEF) {
zend_hash_move_forward_ex(target_hash, &pos);
continue;
}
}
ZVAL_MAKE_REF(zv);
zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
zend_hash_move_forward_ex(target_hash, &pos);
EG(ht_iterators)[ht_iter].pos = pos;
if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
HashTable *thash;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
zval ref;
ZVAL_COPY_VALUE(&ref, zv);
ZVAL_DEREF(zv);
SEPARATE_ARRAY(zv);
thash = Z_ARRVAL_P(zv);
if (thash->u.v.nApplyCount > 1) {
php_error_docref(NULL, E_WARNING, "recursion detected");
result = FAILURE;
break;
}
orig_array_walk_fci = BG(array_walk_fci);
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
Z_ADDREF(ref);
thash->u.v.nApplyCount++;
result = php_array_walk(zv, userdata, recursive);
if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
thash->u.v.nApplyCount--;
}
zval_ptr_dtor(&ref);
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
} else {
ZVAL_COPY(&args[0], zv);
result = zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache));
if (result == SUCCESS) {
zval_ptr_dtor(&retval);
}
zval_ptr_dtor(&args[0]);
}
if (Z_TYPE(args[1]) != IS_UNDEF) {
zval_ptr_dtor(&args[1]);
ZVAL_UNDEF(&args[1]);
}
if (result == FAILURE) {
break;
}
if (Z_TYPE_P(array) == IS_ARRAY) {
pos = zend_hash_iterator_pos_ex(ht_iter, array);
target_hash = Z_ARRVAL_P(array);
} else if (Z_TYPE_P(array) == IS_OBJECT) {
target_hash = Z_OBJPROP_P(array);
pos = zend_hash_iterator_pos(ht_iter, target_hash);
} else {
php_error_docref(NULL, E_WARNING, "Iterated value is no longer an array or object");
result = FAILURE;
break;
}
} while (!EG(exception));
if (userdata) {
zval_ptr_dtor(&args[2]);
}
zend_hash_iterator_del(ht_iter);
return result;
}
PHP_FUNCTION(array_walk)
{
zval *array;
zval *userdata = NULL;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
orig_array_walk_fci = BG(array_walk_fci);
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL_EX(userdata, 0, 1)
ZEND_PARSE_PARAMETERS_END_EX(
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
return
);
php_array_walk(array, userdata, 0);
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
RETURN_TRUE;
}
PHP_FUNCTION(array_walk_recursive)
{
zval *array;
zval *userdata = NULL;
zend_fcall_info orig_array_walk_fci;
zend_fcall_info_cache orig_array_walk_fci_cache;
orig_array_walk_fci = BG(array_walk_fci);
orig_array_walk_fci_cache = BG(array_walk_fci_cache);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "A/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) {
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
return;
}
php_array_walk(array, userdata, 1);
BG(array_walk_fci) = orig_array_walk_fci;
BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
RETURN_TRUE;
}
static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
{
zval *value,
*array,
*entry;
zend_ulong num_idx;
zend_string *str_idx;
zend_bool strict = 0;
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ZVAL(value)
Z_PARAM_ARRAY(array)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(strict)
ZEND_PARSE_PARAMETERS_END();
if (strict) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
ZVAL_DEREF(entry);
if (fast_is_identical_function(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else {
if (Z_TYPE_P(value) == IS_LONG) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_long(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else if (Z_TYPE_P(value) == IS_STRING) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_string(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_function(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
}
}
RETURN_FALSE;
}
PHP_FUNCTION(in_array)
{
php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
PHP_FUNCTION(array_search)
{
php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
static zend_always_inline int php_valid_var_name(char *var_name, size_t var_name_len)
{
#if 1
static const uint32_t charset[16] = {
0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
};
#endif
size_t i;
uint32_t ch;
if (UNEXPECTED(!var_name_len)) {
return 0;
}
ch = (uint32_t)((unsigned char *)var_name)[0];
#if 1
if (UNEXPECTED(!(charset[ch >> 5] & (1 << (ch & 0x1f))))) {
#else
if (var_name[0] != '_' &&
(ch < 65 || ch > 90) &&
(ch < 97 || ch > 122) &&
(ch < 127 || ch > 255)
) {
#endif
return 0;
}
if (var_name_len > 1) {
i = 1;
do {
ch = (uint32_t)((unsigned char *)var_name)[i];
#if 1
if (UNEXPECTED(!(charset[8 + (ch >> 5)] & (1 << (ch & 0x1f))))) {
#else
if (var_name[i] != '_' &&
(ch < 48 || ch > 57) &&
(ch < 65 || ch > 90) &&
(ch < 97 || ch > 122) &&
(ch < 127 || ch > 255)
) {
#endif
return 0;
}
} while (++i < var_name_len);
}
return 1;
}
PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t var_name_len, zend_bool add_underscore)
{
ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
if (add_underscore) {
Z_STRVAL_P(result)[Z_STRLEN_P(prefix)] = '_';
}
memcpy(Z_STRVAL_P(result) + Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
return SUCCESS;
}
PHP_FUNCTION(extract)
{
zval *var_array_param, *prefix = NULL;
zend_long extract_type = EXTR_OVERWRITE;
zval *entry;
zend_string *var_name;
zend_ulong num_key;
int var_exists, count = 0;
int extract_refs = 0;
int exception_thrown = 0;
zend_array *symbol_table;
zval var_array;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_ARRAY(var_array_param)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(extract_type)
Z_PARAM_ZVAL_EX(prefix, 0, 1)
ZEND_PARSE_PARAMETERS_END();
extract_refs = (extract_type & EXTR_REFS);
if (extract_refs) {
SEPARATE_ZVAL(var_array_param);
}
extract_type &= 0xff;
if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
php_error_docref(NULL, E_WARNING, "Invalid extract type");
return;
}
if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
php_error_docref(NULL, E_WARNING, "specified extract type requires the prefix parameter");
return;
}
if (prefix) {
convert_to_string(prefix);
if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) {
php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier");
return;
}
}
if (zend_forbid_dynamic_call("extract()") == FAILURE) {
return;
}
symbol_table = zend_rebuild_symbol_table();
#if 0
if (!symbol_table) {
php_error_docref(NULL, E_WARNING, "failed to build symbol table");
return;
}
#endif
ZVAL_COPY(&var_array, var_array_param);
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(var_array), num_key, var_name, entry) {
zval final_name;
ZVAL_NULL(&final_name);
var_exists = 0;
if (var_name) {
var_exists = zend_hash_exists_ind(symbol_table, var_name);
} else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) {
zend_string *str = zend_long_to_str(num_key);
php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
zend_string_release(str);
} else {
continue;
}
switch (extract_type) {
case EXTR_IF_EXISTS:
if (!var_exists) break;
case EXTR_OVERWRITE:
if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) {
break;
}
ZVAL_STR_COPY(&final_name, var_name);
break;
case EXTR_PREFIX_IF_EXISTS:
if (var_exists) {
php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
}
break;
case EXTR_PREFIX_SAME:
if (!var_exists && ZSTR_LEN(var_name) != 0) {
ZVAL_STR_COPY(&final_name, var_name);
}
case EXTR_PREFIX_ALL:
if (Z_TYPE(final_name) == IS_NULL && ZSTR_LEN(var_name) != 0) {
php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
}
break;
case EXTR_PREFIX_INVALID:
if (Z_TYPE(final_name) == IS_NULL) {
if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
} else {
ZVAL_STR_COPY(&final_name, var_name);
}
}
break;
default:
if (!var_exists) {
ZVAL_STR_COPY(&final_name, var_name);
}
break;
}
if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
zval *orig_var;
if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) {
if (!exception_thrown) {
exception_thrown = 1;
zend_throw_error(NULL, "Cannot re-assign $this");
}
zval_dtor(&final_name);
continue;
}
if (extract_refs) {
ZVAL_MAKE_REF(entry);
Z_ADDREF_P(entry);
if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
orig_var = Z_INDIRECT_P(orig_var);
}
zval_ptr_dtor(orig_var);
ZVAL_COPY_VALUE(orig_var, entry);
} else {
zend_hash_update(symbol_table, Z_STR(final_name), entry);
}
} else {
ZVAL_DEREF(entry);
if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
orig_var = Z_INDIRECT_P(orig_var);
}
ZVAL_DEREF(orig_var);
zval_ptr_dtor(orig_var);
ZVAL_COPY_VALUE(orig_var, entry);
} else {
zend_hash_update(symbol_table, Z_STR(final_name), entry);
}
}
count++;
}
zval_dtor(&final_name);
} ZEND_HASH_FOREACH_END();
zval_ptr_dtor(&var_array);
RETURN_LONG(count);
}
static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry)
{
zval *value_ptr, data;
ZVAL_DEREF(entry);
if (Z_TYPE_P(entry) == IS_STRING) {
if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
ZVAL_DEREF(value_ptr);
ZVAL_COPY(&data, value_ptr);
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
if (zend_string_equals_literal(Z_STR_P(entry), "this")) {
zend_object *object = zend_get_this_object(EG(current_execute_data));
if (object) {
GC_REFCOUNT(object)++;
ZVAL_OBJ(&data, object);
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return;
}
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
Z_ARRVAL_P(entry)->u.v.nApplyCount++;
}
ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
php_compact_var(eg_active_symbol_table, return_value, value_ptr);
} ZEND_HASH_FOREACH_END();
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
Z_ARRVAL_P(entry)->u.v.nApplyCount--;
}
}
}
PHP_FUNCTION(compact)
{
zval *args = NULL;
uint32_t num_args, i;
zend_array *symbol_table;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) {
return;
}
if (zend_forbid_dynamic_call("compact()") == FAILURE) {
return;
}
symbol_table = zend_rebuild_symbol_table();
if (UNEXPECTED(symbol_table == NULL)) {
return;
}
if (ZEND_NUM_ARGS() == 1 && Z_TYPE(args[0]) == IS_ARRAY) {
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
} else {
array_init_size(return_value, ZEND_NUM_ARGS());
}
for (i=0; i<ZEND_NUM_ARGS(); i++) {
php_compact_var(symbol_table, return_value, &args[i]);
}
}
PHP_FUNCTION(array_fill)
{
zval *val;
zend_long start_key, num;
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_LONG(start_key)
Z_PARAM_LONG(num)
Z_PARAM_ZVAL(val)
ZEND_PARSE_PARAMETERS_END();
if (EXPECTED(num > 0)) {
if (sizeof(num) > 4 && UNEXPECTED(EXPECTED(num > 0x7fffffff))) {
php_error_docref(NULL, E_WARNING, "Too many elements");
RETURN_FALSE;
} else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied");
RETURN_FALSE;
} else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
Bucket *p;
zend_long n;
array_init_size(return_value, (uint32_t)(start_key + num));
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
Z_ARRVAL_P(return_value)->nNumUsed = start_key + num;
Z_ARRVAL_P(return_value)->nNumOfElements = num;
Z_ARRVAL_P(return_value)->nInternalPointer = start_key;
Z_ARRVAL_P(return_value)->nNextFreeElement = start_key + num;
if (Z_REFCOUNTED_P(val)) {
GC_REFCOUNT(Z_COUNTED_P(val)) += num;
}
p = Z_ARRVAL_P(return_value)->arData;
n = start_key;
while (start_key--) {
ZVAL_UNDEF(&p->val);
p++;
}
while (num--) {
ZVAL_COPY_VALUE(&p->val, val);
p->h = n++;
p->key = NULL;
p++;
}
} else {
array_init_size(return_value, (uint32_t)num);
zend_hash_real_init(Z_ARRVAL_P(return_value), 0);
if (Z_REFCOUNTED_P(val)) {
GC_REFCOUNT(Z_COUNTED_P(val)) += num;
}
zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
while (--num) {
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
start_key++;
}
}
} else if (EXPECTED(num == 0)) {
array_init(return_value);
return;
} else {
php_error_docref(NULL, E_WARNING, "Number of elements can't be negative");
RETURN_FALSE;
}
}
PHP_FUNCTION(array_fill_keys)
{
zval *keys, *val, *entry;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "az", &keys, &val) == FAILURE) {
return;
}
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
ZVAL_DEREF(entry);
Z_TRY_ADDREF_P(val);
if (Z_TYPE_P(entry) == IS_LONG) {
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
} else {
zend_string *key = zval_get_string(entry);
zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
zend_string_release(key);
}
} ZEND_HASH_FOREACH_END();
}
#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
double __calc_size = ((start - end) / step) + 1; \
if (__calc_size >= (double)HT_MAX_SIZE) { \
php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
RETURN_FALSE; \
} \
size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
array_init_size(return_value, size); \
zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
} while (0)
#define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
zend_ulong __calc_size = (start - end) / lstep; \
if (__calc_size >= HT_MAX_SIZE - 1) { \
php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
RETURN_FALSE; \
} \
size = (uint32_t)(__calc_size + 1); \
array_init_size(return_value, size); \
zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
} while (0)
PHP_FUNCTION(range)
{
zval *zlow, *zhigh, *zstep = NULL, tmp;
int err = 0, is_step_double = 0;
double step = 1.0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &zlow, &zhigh, &zstep) == FAILURE) {
RETURN_FALSE;
}
if (zstep) {
if (Z_TYPE_P(zstep) == IS_DOUBLE) {
is_step_double = 1;
} else if (Z_TYPE_P(zstep) == IS_STRING) {
int type = is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0);
if (type == IS_DOUBLE) {
is_step_double = 1;
}
if (type == 0) {
php_error_docref(NULL, E_WARNING, "Invalid range string - must be numeric");
RETURN_FALSE;
}
}
step = zval_get_double(zstep);
if (step < 0.0) {
step *= -1;
}
}
if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
int type1, type2;
unsigned char low, high;
zend_long lstep = (zend_long) step;
type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
goto double_str;
} else if (type1 == IS_LONG || type2 == IS_LONG) {
goto long_str;
}
low = (unsigned char)Z_STRVAL_P(zlow)[0];
high = (unsigned char)Z_STRVAL_P(zhigh)[0];
if (low > high) {
if (lstep <= 0) {
err = 1;
goto err;
}
array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1));
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (; low >= high; low -= (unsigned int)lstep) {
if (CG(one_char_string)[low]) {
ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
} else {
ZVAL_STRINGL(&tmp, (char*)&low, 1);
}
ZEND_HASH_FILL_ADD(&tmp);
if (((signed int)low - lstep) < 0) {
break;
}
}
} ZEND_HASH_FILL_END();
} else if (high > low) {
if (lstep <= 0) {
err = 1;
goto err;
}
array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1));
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (; low <= high; low += (unsigned int)lstep) {
if (CG(one_char_string)[low]) {
ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
} else {
ZVAL_STRINGL(&tmp, (char*)&low, 1);
}
ZEND_HASH_FILL_ADD(&tmp);
if (((signed int)low + lstep) > 255) {
break;
}
}
} ZEND_HASH_FILL_END();
} else {
array_init(return_value);
if (CG(one_char_string)[low]) {
ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]);
} else {
ZVAL_STRINGL(&tmp, (char*)&low, 1);
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
}
} else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
double low, high, element;
uint32_t i, size;
double_str:
low = zval_get_double(zlow);
high = zval_get_double(zhigh);
if (zend_isinf(high) || zend_isinf(low)) {
php_error_docref(NULL, E_WARNING, "Invalid range supplied: start=%0.0f end=%0.0f", low, high);
RETURN_FALSE;
}
Z_TYPE_INFO(tmp) = IS_DOUBLE;
if (low > high) {
if (low - high < step || step <= 0) {
err = 1;
goto err;
}
RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step)) {
Z_DVAL(tmp) = element;
ZEND_HASH_FILL_ADD(&tmp);
}
} ZEND_HASH_FILL_END();
} else if (high > low) {
if (high - low < step || step <= 0) {
err = 1;
goto err;
}
RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step)) {
Z_DVAL(tmp) = element;
ZEND_HASH_FILL_ADD(&tmp);
}
} ZEND_HASH_FILL_END();
} else {
array_init(return_value);
Z_DVAL(tmp) = low;
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
}
} else {
zend_long low, high;
zend_ulong lstep;
uint32_t i, size;
long_str:
low = zval_get_long(zlow);
high = zval_get_long(zhigh);
if (step <= 0) {
err = 1;
goto err;
}
lstep = step;
if (step <= 0) {
err = 1;
goto err;
}
Z_TYPE_INFO(tmp) = IS_LONG;
if (low > high) {
if ((zend_ulong)(low - high) < lstep) {
err = 1;
goto err;
}
RANGE_CHECK_LONG_INIT_ARRAY(low, high);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0; i < size; ++i) {
Z_LVAL(tmp) = low - (i * lstep);
ZEND_HASH_FILL_ADD(&tmp);
}
} ZEND_HASH_FILL_END();
} else if (high > low) {
if ((zend_ulong)(high - low) < lstep) {
err = 1;
goto err;
}
RANGE_CHECK_LONG_INIT_ARRAY(high, low);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0; i < size; ++i) {
Z_LVAL(tmp) = low + (i * lstep);
ZEND_HASH_FILL_ADD(&tmp);
}
} ZEND_HASH_FILL_END();
} else {
array_init(return_value);
Z_LVAL(tmp) = low;
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
}
}
err:
if (err) {
php_error_docref(NULL, E_WARNING, "step exceeds the specified range");
RETURN_FALSE;
}
}
#undef RANGE_CHECK_DOUBLE_INIT_ARRAY
#undef RANGE_CHECK_LONG_INIT_ARRAY
static void php_array_data_shuffle(zval *array)
{
uint32_t idx, j, n_elems;
Bucket *p, temp;
HashTable *hash;
zend_long rnd_idx;
zend_long n_left;
n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
if (n_elems < 1) {
return;
}
hash = Z_ARRVAL_P(array);
n_left = n_elems;
if (EXPECTED(hash->u.v.nIteratorsCount == 0)) {
if (hash->nNumUsed != hash->nNumOfElements) {
for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
p = hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (j != idx) {
hash->arData[j] = *p;
}
j++;
}
}
while (--n_left) {
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = hash->arData[n_left];
hash->arData[n_left] = hash->arData[rnd_idx];
hash->arData[rnd_idx] = temp;
}
}
} else {
uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
if (hash->nNumUsed != hash->nNumOfElements) {
for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
p = hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (j != idx) {
hash->arData[j] = *p;
if (idx == iter_pos) {
zend_hash_iterators_update(hash, idx, j);
iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
}
}
j++;
}
}
while (--n_left) {
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = hash->arData[n_left];
hash->arData[n_left] = hash->arData[rnd_idx];
hash->arData[rnd_idx] = temp;
zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
}
}
}
hash->nNumUsed = n_elems;
hash->nInternalPointer = 0;
for (j = 0; j < n_elems; j++) {
p = hash->arData + j;
if (p->key) {
zend_string_release(p->key);
}
p->h = j;
p->key = NULL;
}
hash->nNextFreeElement = n_elems;
if (!(hash->u.flags & HASH_FLAG_PACKED)) {
zend_hash_to_packed(hash);
}
}
PHP_FUNCTION(shuffle)
{
zval *array;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) {
RETURN_FALSE;
}
php_array_data_shuffle(array);
RETURN_TRUE;
}
static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed)
{
HashTable out_hash;
zend_long num_in;
zend_long pos;
uint32_t idx;
Bucket *p;
zval *entry;
uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
num_in = zend_hash_num_elements(in_hash);
if (offset > num_in) {
offset = num_in;
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
offset = 0;
}
if (length < 0) {
length = num_in - offset + length;
} else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
length = num_in - offset;
}
zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
p = in_hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
entry = &p->val;
if (p->key == NULL) {
zend_hash_next_index_insert_new(&out_hash, entry);
} else {
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
}
pos++;
}
if (removed != NULL) {
for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
p = in_hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
pos++;
entry = &p->val;
if (Z_REFCOUNTED_P(entry)) {
Z_ADDREF_P(entry);
}
if (p->key == NULL) {
zend_hash_next_index_insert_new(removed, entry);
zend_hash_index_del(in_hash, p->h);
} else {
zend_hash_add_new(removed, p->key, entry);
if (in_hash == &EG(symbol_table)) {
zend_delete_global_variable(p->key);
} else {
zend_hash_del(in_hash, p->key);
}
}
}
} else {
int pos2 = pos;
for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
p = in_hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
pos2++;
if (p->key == NULL) {
zend_hash_index_del(in_hash, p->h);
} else {
if (in_hash == &EG(symbol_table)) {
zend_delete_global_variable(p->key);
} else {
zend_hash_del(in_hash, p->key);
}
}
}
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
if (replace) {
ZEND_HASH_FOREACH_VAL_IND(replace, entry) {
if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
zend_hash_next_index_insert_new(&out_hash, entry);
pos++;
} ZEND_HASH_FOREACH_END();
}
for ( ; idx < in_hash->nNumUsed ; idx++) {
p = in_hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
entry = &p->val;
if (p->key == NULL) {
zend_hash_next_index_insert_new(&out_hash, entry);
} else {
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx == iter_pos) {
if ((zend_long)idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
}
pos++;
}
in_hash->u.v.nIteratorsCount = 0;
in_hash->pDestructor = NULL;
zend_hash_destroy(in_hash);
in_hash->u.v.flags = out_hash.u.v.flags;
in_hash->nTableSize = out_hash.nTableSize;
in_hash->nTableMask = out_hash.nTableMask;
in_hash->nNumUsed = out_hash.nNumUsed;
in_hash->nNumOfElements = out_hash.nNumOfElements;
in_hash->nNextFreeElement = out_hash.nNextFreeElement;
in_hash->arData = out_hash.arData;
in_hash->pDestructor = out_hash.pDestructor;
zend_hash_internal_pointer_reset(in_hash);
}
PHP_FUNCTION(array_push)
{
zval *args,
*stack,
new_var;
int i,
argc;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) {
return;
}
for (i = 0; i < argc; i++) {
ZVAL_COPY(&new_var, &args[i]);
if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
if (Z_REFCOUNTED(new_var)) Z_DELREF(new_var);
php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied");
RETURN_FALSE;
}
}
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
}
PHP_FUNCTION(array_pop)
{
zval *stack,
*val;
uint32_t idx;
Bucket *p;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_EX(stack, 0, 1)
ZEND_PARSE_PARAMETERS_END();
if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
return;
}
idx = Z_ARRVAL_P(stack)->nNumUsed;
while (1) {
if (idx == 0) {
return;
}
idx--;
p = Z_ARRVAL_P(stack)->arData + idx;
val = &p->val;
if (Z_TYPE_P(val) == IS_INDIRECT) {
val = Z_INDIRECT_P(val);
}
if (Z_TYPE_P(val) != IS_UNDEF) {
break;
}
}
ZVAL_DEREF(val);
ZVAL_COPY(return_value, val);
if (!p->key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
}
if (p->key) {
if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
zend_delete_global_variable(p->key);
} else {
zend_hash_del(Z_ARRVAL_P(stack), p->key);
}
} else {
zend_hash_index_del(Z_ARRVAL_P(stack), p->h);
}
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
}
PHP_FUNCTION(array_shift)
{
zval *stack,
*val;
uint32_t idx;
Bucket *p;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_EX(stack, 0, 1)
ZEND_PARSE_PARAMETERS_END();
if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
return;
}
idx = 0;
while (1) {
if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
return;
}
p = Z_ARRVAL_P(stack)->arData + idx;
val = &p->val;
if (Z_TYPE_P(val) == IS_INDIRECT) {
val = Z_INDIRECT_P(val);
}
if (Z_TYPE_P(val) != IS_UNDEF) {
break;
}
idx++;
}
ZVAL_DEREF(val);
ZVAL_COPY(return_value, val);
if (p->key) {
if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
zend_delete_global_variable(p->key);
} else {
zend_hash_del(Z_ARRVAL_P(stack), p->key);
}
} else {
zend_hash_index_del(Z_ARRVAL_P(stack), p->h);
}
if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) {
uint32_t k = 0;
if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
p = Z_ARRVAL_P(stack)->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (idx != k) {
Bucket *q = Z_ARRVAL_P(stack)->arData + k;
q->h = k;
q->key = NULL;
ZVAL_COPY_VALUE(&q->val, &p->val);
ZVAL_UNDEF(&p->val);
}
k++;
}
} else {
uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
p = Z_ARRVAL_P(stack)->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (idx != k) {
Bucket *q = Z_ARRVAL_P(stack)->arData + k;
q->h = k;
q->key = NULL;
ZVAL_COPY_VALUE(&q->val, &p->val);
ZVAL_UNDEF(&p->val);
if (idx == iter_pos) {
zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
}
}
k++;
}
}
Z_ARRVAL_P(stack)->nNumUsed = k;
Z_ARRVAL_P(stack)->nNextFreeElement = k;
} else {
uint32_t k = 0;
int should_rehash = 0;
for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
p = Z_ARRVAL_P(stack)->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (p->key == NULL) {
if (p->h != k) {
p->h = k++;
should_rehash = 1;
} else {
k++;
}
}
}
Z_ARRVAL_P(stack)->nNextFreeElement = k;
if (should_rehash) {
zend_hash_rehash(Z_ARRVAL_P(stack));
}
}
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
}
PHP_FUNCTION(array_unshift)
{
zval *args,
*stack;
HashTable new_hash;
int argc;
int i;
zend_string *key;
zval *value;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) {
return;
}
zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
for (i = 0; i < argc; i++) {
if (Z_REFCOUNTED(args[i])) {
Z_ADDREF(args[i]);
}
zend_hash_next_index_insert_new(&new_hash, &args[i]);
}
if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
if (key) {
zend_hash_add_new(&new_hash, key, value);
} else {
zend_hash_next_index_insert_new(&new_hash, value);
}
} ZEND_HASH_FOREACH_END();
} else {
uint32_t old_idx;
uint32_t new_idx = i;
uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
if (key) {
zend_hash_add_new(&new_hash, key, value);
} else {
zend_hash_next_index_insert_new(&new_hash, value);
}
old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData;
if (old_idx == iter_pos) {
zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx);
iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
}
new_idx++;
} ZEND_HASH_FOREACH_END();
}
Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0;
Z_ARRVAL_P(stack)->pDestructor = NULL;
zend_hash_destroy(Z_ARRVAL_P(stack));
Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags;
Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
Z_ARRVAL_P(stack)->arData = new_hash.arData;
Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
}
PHP_FUNCTION(array_splice)
{
zval *array,
*repl_array = NULL;
HashTable *rem_hash = NULL;
zend_long offset,
length = 0;
int num_in;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/l|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
return;
}
num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
if (ZEND_NUM_ARGS() < 3) {
length = num_in;
}
if (ZEND_NUM_ARGS() == 4) {
convert_to_array_ex(repl_array);
}
if (USED_RET()) {
zend_long size = length;
if (offset > num_in) {
offset = num_in;
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
offset = 0;
}
if (length < 0) {
size = num_in - offset + length;
} else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
size = num_in - offset;
}
array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
rem_hash = Z_ARRVAL_P(return_value);
}
php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
}
PHP_FUNCTION(array_slice)
{
zval *input,
*z_length = NULL,
*entry;
zend_long offset,
length = 0;
zend_bool preserve_keys = 0;
int num_in,
pos;
zend_string *string_key;
zend_ulong num_key;
ZEND_PARSE_PARAMETERS_START(2, 4)
Z_PARAM_ARRAY(input)
Z_PARAM_LONG(offset)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(z_length)
Z_PARAM_BOOL(preserve_keys)
ZEND_PARSE_PARAMETERS_END();
num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
if (ZEND_NUM_ARGS() < 3 || Z_TYPE_P(z_length) == IS_NULL) {
length = num_in;
} else {
length = zval_get_long(z_length);
}
if (offset > num_in) {
array_init(return_value);
return;
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
offset = 0;
}
if (length < 0) {
length = num_in - offset + length;
} else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
length = num_in - offset;
}
if (length <= 0) {
array_init(return_value);
return;
}
array_init_size(return_value, (uint32_t)length);
pos = 0;
if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
(!preserve_keys ||
(offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
pos++;
if (pos <= offset) {
continue;
}
if (pos > offset + length) {
break;
}
if (UNEXPECTED(Z_ISREF_P(entry)) &&
UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
ZVAL_UNREF(entry);
}
Z_TRY_ADDREF_P(entry);
ZEND_HASH_FILL_ADD(entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
pos++;
if (pos <= offset) {
continue;
}
if (pos > offset + length) {
break;
}
if (string_key) {
entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
} else {
if (preserve_keys) {
entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
} else {
entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
}
}
zval_add_ref(entry);
} ZEND_HASH_FOREACH_END();
}
}
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src)
{
zval *src_entry, *dest_entry;
zend_string *string_key;
ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
if (string_key) {
if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) {
zval *src_zval = src_entry;
zval *dest_zval = dest_entry;
HashTable *thash;
zval tmp;
int ret;
ZVAL_DEREF(src_zval);
ZVAL_DEREF(dest_zval);
thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
}
ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
SEPARATE_ZVAL(dest_entry);
dest_zval = dest_entry;
if (Z_TYPE_P(dest_zval) == IS_NULL) {
convert_to_array_ex(dest_zval);
add_next_index_null(dest_zval);
} else if (Z_TYPE_P(dest_zval) == IS_ARRAY) {
if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > (zend_long)Z_ARRVAL_P(dest_zval)->nNumUsed)) {
Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed;
}
} else {
convert_to_array_ex(dest_zval);
}
ZVAL_UNDEF(&tmp);
if (Z_TYPE_P(src_zval) == IS_OBJECT) {
ZVAL_COPY(&tmp, src_zval);
convert_to_array(&tmp);
src_zval = &tmp;
}
if (Z_TYPE_P(src_zval) == IS_ARRAY) {
if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
thash->u.v.nApplyCount++;
}
ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
thash->u.v.nApplyCount--;
}
if (!ret) {
return 0;
}
} else {
if (Z_REFCOUNTED_P(src_entry)) {
Z_ADDREF_P(src_entry);
}
zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
}
zval_ptr_dtor(&tmp);
} else {
zval *zv = zend_hash_add_new(dest, string_key, src_entry);
zval_add_ref(zv);
}
} else {
zval *zv = zend_hash_next_index_insert_new(dest, src_entry);
zval_add_ref(zv);
}
} ZEND_HASH_FOREACH_END();
return 1;
}
PHPAPI int php_array_merge(HashTable *dest, HashTable *src)
{
zval *src_entry;
zend_string *string_key;
if ((dest->u.flags & HASH_FLAG_PACKED) && (src->u.flags & HASH_FLAG_PACKED)) {
zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
ZEND_HASH_FILL_PACKED(dest) {
ZEND_HASH_FOREACH_VAL(src, src_entry) {
if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
ZVAL_UNREF(src_entry);
}
Z_TRY_ADDREF_P(src_entry);
ZEND_HASH_FILL_ADD(src_entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
if (UNEXPECTED(Z_ISREF_P(src_entry) &&
Z_REFCOUNT_P(src_entry) == 1)) {
ZVAL_UNREF(src_entry);
}
Z_TRY_ADDREF_P(src_entry);
if (string_key) {
zend_hash_update(dest, string_key, src_entry);
} else {
zend_hash_next_index_insert_new(dest, src_entry);
}
} ZEND_HASH_FOREACH_END();
}
return 1;
}
PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src)
{
zval *src_entry, *dest_entry, *src_zval, *dest_zval;
zend_string *string_key;
zend_ulong num_key;
int ret;
ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
src_zval = src_entry;
ZVAL_DEREF(src_zval);
if (string_key) {
if (Z_TYPE_P(src_zval) != IS_ARRAY ||
(dest_entry = zend_hash_find(dest, string_key)) == NULL ||
(Z_TYPE_P(dest_entry) != IS_ARRAY &&
(!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
zval *zv = zend_hash_update(dest, string_key, src_entry);
zval_add_ref(zv);
continue;
}
} else {
if (Z_TYPE_P(src_zval) != IS_ARRAY ||
(dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
(Z_TYPE_P(dest_entry) != IS_ARRAY &&
(!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
zval *zv = zend_hash_index_update(dest, num_key, src_entry);
zval_add_ref(zv);
continue;
}
}
dest_zval = dest_entry;
ZVAL_DEREF(dest_zval);
if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 ||
Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 ||
(Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
php_error_docref(NULL, E_WARNING, "recursion detected");
return 0;
}
ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
SEPARATE_ZVAL(dest_entry);
dest_zval = dest_entry;
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++;
}
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
Z_ARRVAL_P(src_zval)->u.v.nApplyCount++;
}
ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--;
}
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
Z_ARRVAL_P(src_zval)->u.v.nApplyCount--;
}
if (!ret) {
return 0;
}
} ZEND_HASH_FOREACH_END();
return 1;
}
static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace)
{
zval *args = NULL;
zval *arg;
int argc, i;
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();
for (i = 0; i < argc; i++) {
zval *arg = args + i;
ZVAL_DEREF(arg);
if (Z_TYPE_P(arg) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
RETURN_NULL();
}
}
if (replace) {
HashTable *dest;
arg = args;
ZVAL_DEREF(arg);
dest = zend_array_dup(Z_ARRVAL_P(arg));
ZVAL_ARR(return_value, dest);
if (recursive) {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
}
} else {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
}
}
} else {
zval *src_entry;
HashTable *src, *dest;
arg = args;
ZVAL_DEREF(arg);
src = Z_ARRVAL_P(arg);
array_init_size(return_value, zend_hash_num_elements(src));
dest = Z_ARRVAL_P(return_value);
if (src->u.flags & HASH_FLAG_PACKED) {
zend_hash_real_init(dest, 1);
ZEND_HASH_FILL_PACKED(dest) {
ZEND_HASH_FOREACH_VAL(src, src_entry) {
if (UNEXPECTED(Z_ISREF_P(src_entry) &&
Z_REFCOUNT_P(src_entry) == 1)) {
ZVAL_UNREF(src_entry);
}
Z_TRY_ADDREF_P(src_entry);
ZEND_HASH_FILL_ADD(src_entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
zend_string *string_key;
ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
if (UNEXPECTED(Z_ISREF_P(src_entry) &&
Z_REFCOUNT_P(src_entry) == 1)) {
ZVAL_UNREF(src_entry);
}
Z_TRY_ADDREF_P(src_entry);
if (string_key) {
zend_hash_add_new(dest, string_key, src_entry);
} else {
zend_hash_next_index_insert_new(dest, src_entry);
}
} ZEND_HASH_FOREACH_END();
}
if (recursive) {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
}
} else {
for (i = 1; i < argc; i++) {
arg = args + i;
ZVAL_DEREF(arg);
php_array_merge(dest, Z_ARRVAL_P(arg));
}
}
}
}
PHP_FUNCTION(array_merge)
{
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}
PHP_FUNCTION(array_merge_recursive)
{
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
}
PHP_FUNCTION(array_replace)
{
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
}
PHP_FUNCTION(array_replace_recursive)
{
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
}
PHP_FUNCTION(array_keys)
{
zval *input,
*search_value = NULL,
*entry,
new_val;
zend_bool strict = 0;
zend_ulong num_idx;
zend_string *str_idx;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_ARRAY(input)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(search_value)
Z_PARAM_BOOL(strict)
ZEND_PARSE_PARAMETERS_END();
if (search_value != NULL) {
array_init(return_value);
if (strict) {
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
ZVAL_DEREF(entry);
if (fast_is_identical_function(search_value, entry)) {
if (str_idx) {
ZVAL_STR_COPY(&new_val, str_idx);
} else {
ZVAL_LONG(&new_val, num_idx);
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
}
} ZEND_HASH_FOREACH_END();
} else {
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
if (fast_equal_check_function(search_value, entry)) {
if (str_idx) {
ZVAL_STR_COPY(&new_val, str_idx);
} else {
ZVAL_LONG(&new_val, num_idx);
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
}
} ZEND_HASH_FOREACH_END();
}
} else {
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
return;
}
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
if (str_idx) {
ZVAL_STR_COPY(&new_val, str_idx);
} else {
ZVAL_LONG(&new_val, num_idx);
}
ZEND_HASH_FILL_ADD(&new_val);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
}
}
PHP_FUNCTION(array_values)
{
zval *input,
*entry;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(input)
ZEND_PARSE_PARAMETERS_END();
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
return;
}
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
entry = Z_REFVAL_P(entry);
}
Z_TRY_ADDREF_P(entry);
ZEND_HASH_FILL_ADD(entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
}
PHP_FUNCTION(array_count_values)
{
zval *input,
*entry,
*tmp;
HashTable *myht;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
return;
}
array_init(return_value);
myht = Z_ARRVAL_P(input);
ZEND_HASH_FOREACH_VAL(myht, entry) {
ZVAL_DEREF(entry);
if (Z_TYPE_P(entry) == IS_LONG) {
if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
zval data;
ZVAL_LONG(&data, 1);
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
} else {
Z_LVAL_P(tmp)++;
}
} else if (Z_TYPE_P(entry) == IS_STRING) {
if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
zval data;
ZVAL_LONG(&data, 1);
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
} else {
Z_LVAL_P(tmp)++;
}
} else {
php_error_docref(NULL, E_WARNING, "Can only count STRING and INTEGER values!");
}
} ZEND_HASH_FOREACH_END();
}
static inline
zend_bool array_column_param_helper(zval *param,
const char *name) {
switch (Z_TYPE_P(param)) {
case IS_DOUBLE:
convert_to_long_ex(param);
case IS_LONG:
return 1;
case IS_OBJECT:
convert_to_string_ex(param);
case IS_STRING:
return 1;
default:
php_error_docref(NULL, E_WARNING, "The %s key should be either a string or an integer", name);
return 0;
}
}
static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
{
zval *prop = NULL;
if (Z_TYPE_P(data) == IS_OBJECT) {
if (!Z_OBJ_HANDLER_P(data, has_property) || !Z_OBJ_HANDLER_P(data, read_property)) {
return NULL;
}
if (Z_OBJ_HANDLER_P(data, has_property)(data, name, 2, NULL)
|| Z_OBJ_HANDLER_P(data, has_property)(data, name, 0, NULL)) {
prop = Z_OBJ_HANDLER_P(data, read_property)(data, name, BP_VAR_R, NULL, rv);
}
} else if (Z_TYPE_P(data) == IS_ARRAY) {
if (Z_TYPE_P(name) == IS_STRING) {
prop = zend_symtable_find(Z_ARRVAL_P(data), Z_STR_P(name));
} else if (Z_TYPE_P(name) == IS_LONG) {
prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name));
}
}
if (prop) {
ZVAL_DEREF(prop);
}
return prop;
}
PHP_FUNCTION(array_column)
{
zval *zcolumn = NULL, *zkey = NULL, *data;
HashTable *arr_hash;
zval *zcolval = NULL, *zkeyval = NULL, rvc, rvk;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
return;
}
if ((zcolumn && !array_column_param_helper(zcolumn, "column")) ||
(zkey && !array_column_param_helper(zkey, "index"))) {
RETURN_FALSE;
}
array_init_size(return_value, zend_hash_num_elements(arr_hash));
if (!zkey) {
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_VAL(arr_hash, data) {
ZVAL_DEREF(data);
if (!zcolumn) {
zcolval = data;
Z_TRY_ADDREF_P(zcolval);
} else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
continue;
} else if (zcolval != &rvc) {
Z_TRY_ADDREF_P(zcolval);
}
ZEND_HASH_FILL_ADD(zcolval);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
ZEND_HASH_FOREACH_VAL(arr_hash, data) {
ZVAL_DEREF(data);
if (!zcolumn) {
zcolval = data;
Z_TRY_ADDREF_P(zcolval);
} else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
continue;
} else if (zcolval != &rvc) {
Z_TRY_ADDREF_P(zcolval);
}
if (zkey) {
zkeyval = array_column_fetch_prop(data, zkey, &rvk);
}
if (zkeyval) {
if (Z_TYPE_P(zkeyval) == IS_STRING) {
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval);
} else if (Z_TYPE_P(zkeyval) == IS_LONG) {
add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval);
} else if (Z_TYPE_P(zkeyval) == IS_OBJECT) {
zend_string *key = zval_get_string(zkeyval);
zend_symtable_update(Z_ARRVAL_P(return_value), key, zcolval);
zend_string_release(key);
} else {
add_next_index_zval(return_value, zcolval);
}
if (zkeyval == &rvk) {
zval_ptr_dtor(&rvk);
}
} else {
add_next_index_zval(return_value, zcolval);
}
} ZEND_HASH_FOREACH_END();
}
}
PHP_FUNCTION(array_reverse)
{
zval *input,
*entry;
zend_string *string_key;
zend_ulong num_key;
zend_bool preserve_keys = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &input, &preserve_keys) == FAILURE) {
return;
}
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
if ((Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED) && !preserve_keys) {
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
if (UNEXPECTED(Z_ISREF_P(entry) &&
Z_REFCOUNT_P(entry) == 1)) {
ZVAL_UNREF(entry);
}
Z_TRY_ADDREF_P(entry);
ZEND_HASH_FILL_ADD(entry);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
} else {
ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
if (string_key) {
entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
} else {
if (preserve_keys) {
entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
} else {
entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
}
}
zval_add_ref(entry);
} ZEND_HASH_FOREACH_END();
}
}
PHP_FUNCTION(array_pad)
{
zval *input;
zval *pad_value;
zend_long pad_size;
zend_long pad_size_abs;
zend_long input_size;
zend_long num_pads;
zend_long i;
zend_string *key;
zval *value;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "alz", &input, &pad_size, &pad_value) == FAILURE) {
return;
}
input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
pad_size_abs = ZEND_ABS(pad_size);
if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) {
php_error_docref(NULL, E_WARNING, "You may only pad up to 1048576 elements at a time");
RETURN_FALSE;
}
if (input_size >= pad_size_abs) {
ZVAL_COPY(return_value, input);
return;
}
num_pads = pad_size_abs - input_size;
if (Z_REFCOUNTED_P(pad_value)) {
GC_REFCOUNT(Z_COUNTED_P(pad_value)) += num_pads;
}
array_init_size(return_value, pad_size_abs);
if (Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED) {
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
if (pad_size < 0) {
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0; i < num_pads; i++) {
ZEND_HASH_FILL_ADD(pad_value);
}
} ZEND_HASH_FILL_END();
}
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), value) {
Z_TRY_ADDREF_P(value);
ZEND_HASH_FILL_ADD(value);
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
if (pad_size > 0) {
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
for (i = 0; i < num_pads; i++) {
ZEND_HASH_FILL_ADD(pad_value);
}
} ZEND_HASH_FILL_END();
}
} else {
if (pad_size < 0) {
for (i = 0; i < num_pads; i++) {
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
}
}
ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) {
Z_TRY_ADDREF_P(value);
if (key) {
zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
} else {
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
}
} ZEND_HASH_FOREACH_END();
if (pad_size > 0) {
for (i = 0; i < num_pads; i++) {
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
}
}
}
}
PHP_FUNCTION(array_flip)
{
zval *array, *entry, data;
zend_ulong num_idx;
zend_string *str_idx;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
return;
}
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
ZVAL_DEREF(entry);
if (Z_TYPE_P(entry) == IS_LONG) {
if (str_idx) {
ZVAL_STR_COPY(&data, str_idx);
} else {
ZVAL_LONG(&data, num_idx);
}
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
} else if (Z_TYPE_P(entry) == IS_STRING) {
if (str_idx) {
ZVAL_STR_COPY(&data, str_idx);
} else {
ZVAL_LONG(&data, num_idx);
}
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
} else {
php_error_docref(NULL, E_WARNING, "Can only flip STRING and INTEGER values!");
}
} ZEND_HASH_FOREACH_END();
}
PHP_FUNCTION(array_change_key_case)
{
zval *array, *entry;
zend_string *string_key;
zend_string *new_key;
zend_ulong num_key;
zend_long change_to_upper=0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &change_to_upper) == FAILURE) {
return;
}
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
if (!string_key) {
entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
} else {
if (change_to_upper) {
new_key = php_string_toupper(string_key);
} else {
new_key = php_string_tolower(string_key);
}
entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
zend_string_release(new_key);
}
zval_add_ref(entry);
} ZEND_HASH_FOREACH_END();
}
struct bucketindex {
Bucket b;
unsigned int i;
};
static void array_bucketindex_swap(void *p, void *q)
{
struct bucketindex *f = (struct bucketindex *)p;
struct bucketindex *g = (struct bucketindex *)q;
struct bucketindex t;
t = *f;
*f = *g;
*g = t;
}
PHP_FUNCTION(array_unique)
{
zval *array;
uint idx;
Bucket *p;
struct bucketindex *arTmp, *cmpdata, *lastkept;
unsigned int i;
zend_long sort_type = PHP_SORT_STRING;
compare_func_t cmp;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &sort_type) == FAILURE) {
return;
}
cmp = php_get_data_compare_func(sort_type, 0);
if (Z_ARRVAL_P(array)->nNumOfElements <= 1) {
ZVAL_COPY(return_value, array);
return;
}
RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
if (!arTmp) {
zval_dtor(return_value);
RETURN_FALSE;
}
for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) {
p = Z_ARRVAL_P(array)->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue;
arTmp[i].b = *p;
arTmp[i].i = i;
i++;
}
ZVAL_UNDEF(&arTmp[i].b.val);
zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
cmp, (swap_func_t)array_bucketindex_swap);
lastkept = arTmp;
for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
if (cmp(lastkept, cmpdata)) {
lastkept = cmpdata;
} else {
if (lastkept->i > cmpdata->i) {
p = &lastkept->b;
lastkept = cmpdata;
} else {
p = &cmpdata->b;
}
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
zend_delete_global_variable(p->key);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
}
}
}
pefree(arTmp, Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT);
}
static int zval_compare(zval *first, zval *second)
{
return string_compare_function(first, second);
}
static int zval_user_compare(zval *a, zval *b)
{
zval args[2];
zval retval;
ZVAL_COPY_VALUE(&args[0], a);
ZVAL_COPY_VALUE(&args[1], b);
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
zend_long ret = zval_get_long(&retval);
zval_ptr_dtor(&retval);
return ret < 0 ? -1 : ret > 0 ? 1 : 0;;
} else {
return 0;
}
}
static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type)
{
uint idx;
Bucket *p;
int argc, i;
zval *args;
int (*intersect_data_compare_func)(zval *, zval *) = NULL;
zend_bool ok;
zval *val, *data;
int req_args;
char *param_spec;
argc = ZEND_NUM_ARGS();
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
req_args = 3;
param_spec = "+f";
intersect_data_compare_func = zval_user_compare;
} else {
req_args = 2;
param_spec = "+";
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
intersect_data_compare_func = zval_compare;
}
}
if (argc < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, argc);
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
return;
}
for (i = 0; i < argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
RETURN_NULL();
}
}
array_init(return_value);
for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
p = Z_ARRVAL(args[0])->arData + idx;
val = &p->val;
if (Z_TYPE_P(val) == IS_UNDEF) continue;
if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
val = Z_INDIRECT_P(val);
if (Z_TYPE_P(val) == IS_UNDEF) continue;
}
if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
ZVAL_UNREF(val);
}
if (p->key == NULL) {
ok = 1;
for (i = 1; i < argc; i++) {
if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) == NULL ||
(intersect_data_compare_func &&
intersect_data_compare_func(val, data) != 0)
) {
ok = 0;
break;
}
}
if (ok) {
if (Z_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
}
} else {
ok = 1;
for (i = 1; i < argc; i++) {
if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) == NULL ||
(intersect_data_compare_func &&
intersect_data_compare_func(val, data) != 0)
) {
ok = 0;
break;
}
}
if (ok) {
if (Z_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
}
}
}
}
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type)
{
zval *args = NULL;
HashTable *hash;
int arr_argc, i, c = 0;
uint idx;
Bucket **lists, *list, **ptrs, *p;
uint32_t req_args;
char *param_spec;
zend_fcall_info fci1, fci2;
zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
zend_fcall_info *fci_key = NULL, *fci_data;
zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
PHP_ARRAY_CMP_FUNC_VARS;
int (*intersect_key_compare_func)(const void *, const void *);
int (*intersect_data_compare_func)(const void *, const void *);
if (behavior == INTERSECT_NORMAL) {
intersect_key_compare_func = php_array_key_compare_string;
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
req_args = 2;
param_spec = "+";
intersect_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
req_args = 3;
param_spec = "+f";
intersect_data_compare_func = php_array_user_compare;
} else {
php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
return;
}
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (behavior & INTERSECT_ASSOC) {
intersect_key_compare_func = php_array_key_compare_string;
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
req_args = 2;
param_spec = "+";
intersect_key_compare_func = php_array_key_compare_string;
intersect_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_key_compare_string;
intersect_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_user_key_compare;
intersect_data_compare_func = php_array_data_compare_string;
fci_key = &fci1;
fci_key_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
req_args = 4;
param_spec = "+ff";
intersect_key_compare_func = php_array_user_key_compare;
intersect_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
fci_key = &fci2;
fci_key_cache = &fci2_cache;
} else {
php_error_docref(NULL, 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);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
return;
}
} else {
php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
return;
}
PHP_ARRAY_CMP_FUNC_BACKUP();
lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
} else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
for (i = 0; i < arr_argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
arr_argc = i;
goto out;
}
hash = Z_ARRVAL(args[i]);
list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
if (!list) {
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
RETURN_FALSE;
}
lists[i] = list;
ptrs[i] = list;
for (idx = 0; idx < hash->nNumUsed; idx++) {
p = hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
*list++ = *p;
}
ZVAL_UNDEF(&list->val);
if (hash->nNumOfElements > 1) {
if (behavior == INTERSECT_NORMAL) {
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), intersect_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
} else if (behavior & INTERSECT_ASSOC) {
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), intersect_key_compare_func, (swap_func_t)zend_hash_bucket_swap);
}
}
}
RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
if ((behavior & INTERSECT_ASSOC)
&& key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
for (i = 1; i < arr_argc; i++) {
if (behavior & INTERSECT_NORMAL) {
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
ptrs[i]++;
}
} else if (behavior & INTERSECT_ASSOC) {
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
ptrs[i]++;
}
if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) {
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
}
if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
c = 1;
if (key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
} else {
}
}
}
if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
for (;;) {
p = ptrs[0]++;
if (Z_TYPE(p->val) == IS_UNDEF) {
goto out;
}
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
}
}
if (c)
break;
ptrs[i]++;
}
if (c) {
for (;;) {
p = ptrs[0];
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
break;
}
} else if (behavior & INTERSECT_ASSOC) {
break;
}
}
} else {
for (;;) {
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
break;
}
} else if (behavior & INTERSECT_ASSOC) {
break;
}
}
}
}
out:
for (i = 0; i < arr_argc; i++) {
hash = Z_ARRVAL(args[i]);
pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
}
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
}
PHP_FUNCTION(array_intersect_key)
{
php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
}
PHP_FUNCTION(array_intersect_ukey)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
}
PHP_FUNCTION(array_intersect)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
}
PHP_FUNCTION(array_uintersect)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
}
PHP_FUNCTION(array_intersect_assoc)
{
php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
}
PHP_FUNCTION(array_intersect_uassoc)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
}
PHP_FUNCTION(array_uintersect_assoc)
{
php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
}
PHP_FUNCTION(array_uintersect_uassoc)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
}
static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type)
{
uint idx;
Bucket *p;
int argc, i;
zval *args;
int (*diff_data_compare_func)(zval *, zval *) = NULL;
zend_bool ok;
zval *val, *data;
argc = ZEND_NUM_ARGS();
if (data_compare_type == DIFF_COMP_DATA_USER) {
if (argc < 3) {
php_error_docref(NULL, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
return;
}
diff_data_compare_func = zval_user_compare;
} else {
if (argc < 2) {
php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
diff_data_compare_func = zval_compare;
}
}
for (i = 0; i < argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
RETURN_NULL();
}
}
array_init(return_value);
for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) {
p = Z_ARRVAL(args[0])->arData + idx;
val = &p->val;
if (Z_TYPE_P(val) == IS_UNDEF) continue;
if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
val = Z_INDIRECT_P(val);
if (Z_TYPE_P(val) == IS_UNDEF) continue;
}
if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
ZVAL_UNREF(val);
}
if (p->key == NULL) {
ok = 1;
for (i = 1; i < argc; i++) {
if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) != NULL &&
(!diff_data_compare_func ||
diff_data_compare_func(val, data) == 0)
) {
ok = 0;
break;
}
}
if (ok) {
if (Z_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val);
}
} else {
ok = 1;
for (i = 1; i < argc; i++) {
if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) != NULL &&
(!diff_data_compare_func ||
diff_data_compare_func(val, data) == 0)
) {
ok = 0;
break;
}
}
if (ok) {
if (Z_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
zend_hash_update(Z_ARRVAL_P(return_value), p->key, val);
}
}
}
}
static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type)
{
zval *args = NULL;
HashTable *hash;
int arr_argc, i, c;
uint idx;
Bucket **lists, *list, **ptrs, *p;
uint32_t req_args;
char *param_spec;
zend_fcall_info fci1, fci2;
zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
zend_fcall_info *fci_key = NULL, *fci_data;
zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
PHP_ARRAY_CMP_FUNC_VARS;
int (*diff_key_compare_func)(const void *, const void *);
int (*diff_data_compare_func)(const void *, const void *);
if (behavior == DIFF_NORMAL) {
diff_key_compare_func = php_array_key_compare_string;
if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
req_args = 2;
param_spec = "+";
diff_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == DIFF_COMP_DATA_USER) {
req_args = 3;
param_spec = "+f";
diff_data_compare_func = php_array_user_compare;
} else {
php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
return;
}
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (behavior & DIFF_ASSOC) {
if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
req_args = 2;
param_spec = "+";
diff_key_compare_func = php_array_key_compare_string;
diff_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
req_args = 3;
param_spec = "+f";
diff_key_compare_func = php_array_key_compare_string;
diff_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
req_args = 3;
param_spec = "+f";
diff_key_compare_func = php_array_user_key_compare;
diff_data_compare_func = php_array_data_compare_string;
fci_key = &fci1;
fci_key_cache = &fci1_cache;
} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
req_args = 4;
param_spec = "+ff";
diff_key_compare_func = php_array_user_key_compare;
diff_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
fci_key = &fci2;
fci_key_cache = &fci2_cache;
} else {
php_error_docref(NULL, 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);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
return;
}
} else {
php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
return;
}
PHP_ARRAY_CMP_FUNC_BACKUP();
lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
} else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
for (i = 0; i < arr_argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
arr_argc = i;
goto out;
}
hash = Z_ARRVAL(args[i]);
list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT);
if (!list) {
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
RETURN_FALSE;
}
lists[i] = list;
ptrs[i] = list;
for (idx = 0; idx < hash->nNumUsed; idx++) {
p = hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
*list++ = *p;
}
ZVAL_UNDEF(&list->val);
if (hash->nNumOfElements > 1) {
if (behavior == DIFF_NORMAL) {
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), diff_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
} else if (behavior & DIFF_ASSOC) {
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), diff_key_compare_func, (swap_func_t)zend_hash_bucket_swap);
}
}
}
RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
if ((behavior & DIFF_ASSOC)
&&
key_compare_type == DIFF_COMP_KEY_USER
) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
c = 1;
for (i = 1; i < arr_argc; i++) {
Bucket *ptr = ptrs[i];
if (behavior == DIFF_NORMAL) {
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
ptrs[i]++;
}
} else if (behavior & DIFF_ASSOC) {
while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
ptr++;
}
}
if (!c) {
if (behavior == DIFF_NORMAL) {
if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
ptrs[i]++;
}
break;
} else if (behavior == DIFF_ASSOC) {
if (Z_TYPE(ptr->val) != IS_UNDEF) {
if (data_compare_type == DIFF_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
}
if (diff_data_compare_func(ptrs[0], ptr) != 0) {
c = -1;
if (key_compare_type == DIFF_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
} else {
break;
}
}
} else if (behavior == DIFF_KEY) {
break;
}
}
}
if (!c) {
for (;;) {
p = ptrs[0];
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == DIFF_NORMAL) {
if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
break;
}
} else if (behavior & DIFF_ASSOC) {
break;
}
}
} else {
for (;;) {
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == DIFF_NORMAL) {
if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
break;
}
} else if (behavior & DIFF_ASSOC) {
break;
}
}
}
}
out:
for (i = 0; i < arr_argc; i++) {
hash = Z_ARRVAL(args[i]);
pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT);
}
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
}
PHP_FUNCTION(array_diff_key)
{
php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
}
PHP_FUNCTION(array_diff_ukey)
{
php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
}
PHP_FUNCTION(array_diff)
{
zval *args;
int argc, i;
uint32_t num;
HashTable exclude;
zval *value;
zend_string *str, *key;
zend_long idx;
zval dummy;
if (ZEND_NUM_ARGS() < 2) {
php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
if (Z_TYPE(args[0]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #1 is not an array");
RETURN_NULL();
}
num = 0;
for (i = 1; i < argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1);
RETURN_NULL();
}
num += zend_hash_num_elements(Z_ARRVAL(args[i]));
}
if (num == 0) {
ZVAL_COPY(return_value, &args[0]);
return;
}
ZVAL_NULL(&dummy);
zend_hash_init(&exclude, num, NULL, NULL, 0);
for (i = 1; i < argc; i++) {
ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), value) {
str = zval_get_string(value);
zend_hash_add(&exclude, str, &dummy);
zend_string_release(str);
} ZEND_HASH_FOREACH_END();
}
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), idx, key, value) {
str = zval_get_string(value);
if (!zend_hash_exists(&exclude, str)) {
if (key) {
value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
} else {
value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
}
zval_add_ref(value);
}
zend_string_release(str);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&exclude);
}
PHP_FUNCTION(array_udiff)
{
php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
}
PHP_FUNCTION(array_diff_assoc)
{
php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
}
PHP_FUNCTION(array_diff_uassoc)
{
php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
}
PHP_FUNCTION(array_udiff_assoc)
{
php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
}
PHP_FUNCTION(array_udiff_uassoc)
{
php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
}
#define MULTISORT_ORDER 0
#define MULTISORT_TYPE 1
#define MULTISORT_LAST 2
PHPAPI int php_multisort_compare(const void *a, const void *b)
{
Bucket *ab = *(Bucket **)a;
Bucket *bb = *(Bucket **)b;
int r;
zend_long result;
r = 0;
do {
result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
if (result != 0) {
return result > 0 ? 1 : -1;
}
r++;
} while (Z_TYPE(ab[r].val) != IS_UNDEF);
return 0;
}
#define MULTISORT_ABORT \
efree(ARRAYG(multisort_func)); \
efree(arrays); \
RETURN_FALSE;
static void array_bucket_p_sawp(void *p, void *q) {
Bucket *t;
Bucket **f = (Bucket **)p;
Bucket **g = (Bucket **)q;
t = *f;
*f = *g;
*g = t;
}
PHP_FUNCTION(array_multisort)
{
zval* args;
zval** arrays;
Bucket** indirect;
uint idx;
Bucket* p;
HashTable* hash;
int argc;
int array_size;
int num_arrays = 0;
int parse_state[MULTISORT_LAST];
int sort_order = PHP_SORT_ASC;
int sort_type = PHP_SORT_REGULAR;
int i, k, n;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
arrays = (zval **)ecalloc(argc, sizeof(zval *));
for (i = 0; i < MULTISORT_LAST; i++) {
parse_state[i] = 0;
}
ARRAYG(multisort_func) = (compare_func_t*)ecalloc(argc, sizeof(compare_func_t));
for (i = 0; i < argc; i++) {
zval *arg = &args[i];
ZVAL_DEREF(arg);
if (Z_TYPE_P(arg) == IS_ARRAY) {
SEPARATE_ARRAY(arg);
if (i > 0) {
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
sort_order = PHP_SORT_ASC;
sort_type = PHP_SORT_REGULAR;
}
arrays[num_arrays++] = arg;
for (k = 0; k < MULTISORT_LAST; k++) {
parse_state[k] = 1;
}
} else if (Z_TYPE_P(arg) == IS_LONG) {
switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
case PHP_SORT_ASC:
case PHP_SORT_DESC:
if (parse_state[MULTISORT_ORDER] == 1) {
sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
parse_state[MULTISORT_ORDER] = 0;
} else {
php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
MULTISORT_ABORT;
}
break;
case PHP_SORT_REGULAR:
case PHP_SORT_NUMERIC:
case PHP_SORT_STRING:
case PHP_SORT_NATURAL:
#if HAVE_STRCOLL
case PHP_SORT_LOCALE_STRING:
#endif
if (parse_state[MULTISORT_TYPE] == 1) {
sort_type = (int)Z_LVAL_P(arg);
parse_state[MULTISORT_TYPE] = 0;
} else {
php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
MULTISORT_ABORT;
}
break;
default:
php_error_docref(NULL, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
MULTISORT_ABORT;
break;
}
} else {
php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
MULTISORT_ABORT;
}
}
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
for (i = 0; i < num_arrays; i++) {
if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent");
MULTISORT_ABORT;
}
}
if (array_size < 1) {
efree(ARRAYG(multisort_func));
efree(arrays);
RETURN_TRUE;
}
indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
for (i = 0; i < array_size; i++) {
indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
}
for (i = 0; i < num_arrays; i++) {
k = 0;
for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
p = Z_ARRVAL_P(arrays[i])->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
indirect[k][i] = *p;
k++;
}
}
for (k = 0; k < array_size; k++) {
ZVAL_UNDEF(&indirect[k][num_arrays].val);
}
zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
for (i = 0; i < num_arrays; i++) {
int repack;
hash = Z_ARRVAL_P(arrays[i]);
hash->nNumUsed = array_size;
hash->nInternalPointer = 0;
repack = !(hash->u.flags & HASH_FLAG_PACKED);
for (n = 0, k = 0; k < array_size; k++) {
hash->arData[k] = indirect[k][i];
if (hash->arData[k].key == NULL) {
hash->arData[k].h = n++;
} else {
repack = 0;
}
}
hash->nNextFreeElement = array_size;
if (repack) {
zend_hash_to_packed(hash);
} else {
zend_hash_rehash(hash);
}
}
for (i = 0; i < array_size; i++) {
efree(indirect[i]);
}
efree(indirect);
efree(ARRAYG(multisort_func));
efree(arrays);
RETURN_TRUE;
}
PHP_FUNCTION(array_rand)
{
zval *input;
zend_long num_req = 1;
zend_string *string_key;
zend_ulong num_key;
int i;
int num_avail;
zend_bitset bitset;
int negative_bitset = 0;
uint32_t bitset_len;
ALLOCA_FLAG(use_heap)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) {
return;
}
num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
if (num_avail == 0) {
php_error_docref(NULL, E_WARNING, "Array is empty");
return;
}
if (num_req == 1) {
HashTable *ht = Z_ARRVAL_P(input);
if (num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) {
zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1);
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
if (i == randval) {
if (string_key) {
RETURN_STR_COPY(string_key);
} else {
RETURN_LONG(num_key);
}
}
i++;
} ZEND_HASH_FOREACH_END();
}
do {
zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
Bucket *bucket = &ht->arData[randval];
if (!Z_ISUNDEF(bucket->val)) {
if (bucket->key) {
RETURN_STR_COPY(bucket->key);
} else {
RETURN_LONG(bucket->h);
}
}
} while (1);
}
if (num_req <= 0 || num_req > num_avail) {
php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
return;
}
array_init_size(return_value, (uint32_t)num_req);
if (num_req > (num_avail >> 1)) {
negative_bitset = 1;
num_req = num_avail - num_req;
}
bitset_len = zend_bitset_len(num_avail);
bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
zend_bitset_clear(bitset, bitset_len);
i = num_req;
while (i) {
zend_long randval = php_mt_rand_range(0, num_avail - 1);
if (!zend_bitset_in(bitset, randval)) {
zend_bitset_incl(bitset, randval);
i--;
}
}
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
zval zv;
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
if (zend_bitset_in(bitset, i) ^ negative_bitset) {
if (string_key) {
ZVAL_STR_COPY(&zv, string_key);
} else {
ZVAL_LONG(&zv, num_key);
}
ZEND_HASH_FILL_ADD(&zv);
}
i++;
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FILL_END();
free_alloca(bitset, use_heap);
}
PHP_FUNCTION(array_sum)
{
zval *input,
*entry,
entry_n;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
return;
}
ZVAL_LONG(return_value, 0);
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
continue;
}
ZVAL_COPY(&entry_n, entry);
convert_scalar_to_number(&entry_n);
fast_add_function(return_value, return_value, &entry_n);
} ZEND_HASH_FOREACH_END();
}
PHP_FUNCTION(array_product)
{
zval *input,
*entry,
entry_n;
double dval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) {
return;
}
ZVAL_LONG(return_value, 1);
if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
return;
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
continue;
}
ZVAL_COPY(&entry_n, entry);
convert_scalar_to_number(&entry_n);
if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
continue;
}
}
convert_to_double(return_value);
convert_to_double(&entry_n);
Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
} ZEND_HASH_FOREACH_END();
}
PHP_FUNCTION(array_reduce)
{
zval *input;
zval args[2];
zval *operand;
zval result;
zval retval;
zend_fcall_info fci;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
zval *initial = NULL;
HashTable *htbl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) {
return;
}
if (ZEND_NUM_ARGS() > 2) {
ZVAL_DUP(&result, initial);
} else {
ZVAL_NULL(&result);
}
htbl = Z_ARRVAL_P(input);
if (zend_hash_num_elements(htbl) == 0) {
ZVAL_COPY_VALUE(return_value, &result);
return;
}
fci.retval = &retval;
fci.param_count = 2;
fci.no_separation = 0;
ZEND_HASH_FOREACH_VAL(htbl, operand) {
ZVAL_COPY(&args[0], &result);
ZVAL_COPY(&args[1], operand);
fci.params = args;
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&result);
ZVAL_COPY_VALUE(&result, &retval);
} else {
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
return;
}
} ZEND_HASH_FOREACH_END();
RETVAL_ZVAL(&result, 1, 1);
}
PHP_FUNCTION(array_filter)
{
zval *array;
zval *operand;
zval *key;
zval args[2];
zval retval;
zend_bool have_callback = 0;
zend_long use_type = 0;
zend_string *string_key;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
zend_ulong num_key;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
return;
}
array_init(return_value);
if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
return;
}
if (ZEND_NUM_ARGS() > 1) {
have_callback = 1;
fci.no_separation = 0;
fci.retval = &retval;
if (use_type == ARRAY_FILTER_USE_BOTH) {
fci.param_count = 2;
key = &args[1];
} else {
fci.param_count = 1;
key = &args[0];
}
}
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, string_key, operand) {
if (have_callback) {
if (use_type) {
if (!string_key) {
ZVAL_LONG(key, num_key);
} else {
ZVAL_STR_COPY(key, string_key);
}
}
if (use_type != ARRAY_FILTER_USE_KEY) {
ZVAL_COPY(&args[0], operand);
}
fci.params = args;
if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
zval_ptr_dtor(&args[0]);
if (use_type == ARRAY_FILTER_USE_BOTH) {
zval_ptr_dtor(&args[1]);
}
if (!Z_ISUNDEF(retval)) {
int retval_true = zend_is_true(&retval);
zval_ptr_dtor(&retval);
if (!retval_true) {
continue;
}
} else {
continue;
}
} else {
zval_ptr_dtor(&args[0]);
if (use_type == ARRAY_FILTER_USE_BOTH) {
zval_ptr_dtor(&args[1]);
}
return;
}
} else if (!zend_is_true(operand)) {
continue;
}
if (string_key) {
operand = zend_hash_update(Z_ARRVAL_P(return_value), string_key, operand);
} else {
operand = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand);
}
zval_add_ref(operand);
} ZEND_HASH_FOREACH_END();
}
PHP_FUNCTION(array_map)
{
zval *arrays = NULL;
int n_arrays = 0;
zval result;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
int i;
uint32_t k, maxlen = 0;
ZEND_PARSE_PARAMETERS_START(2, -1)
Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
Z_PARAM_VARIADIC('+', arrays, n_arrays)
ZEND_PARSE_PARAMETERS_END();
RETVAL_NULL();
if (n_arrays == 1) {
zend_ulong num_key;
zend_string *str_key;
zval *zv, arg;
if (Z_TYPE(arrays[0]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d should be an array", 2);
return;
}
maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
if (!ZEND_FCI_INITIALIZED(fci)) {
ZVAL_COPY(return_value, &arrays[0]);
return;
}
array_init_size(return_value, maxlen);
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
fci.retval = &result;
fci.param_count = 1;
fci.params = &arg;
fci.no_separation = 0;
ZVAL_COPY(&arg, zv);
if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
zval_dtor(return_value);
zval_ptr_dtor(&arg);
RETURN_NULL();
} else {
zval_ptr_dtor(&arg);
}
if (str_key) {
zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result);
} else {
zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
}
} ZEND_HASH_FOREACH_END();
} else {
uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
for (i = 0; i < n_arrays; i++) {
if (Z_TYPE(arrays[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Argument #%d should be an array", i + 2);
efree(array_pos);
return;
}
if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
}
}
array_init_size(return_value, maxlen);
if (!ZEND_FCI_INITIALIZED(fci)) {
zval zv;
for (k = 0; k < maxlen; k++) {
array_init_size(&result, n_arrays);
for (i = 0; i < n_arrays; i++) {
uint32_t pos = array_pos[i];
while (1) {
if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
ZVAL_NULL(&zv);
break;
} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
array_pos[i] = pos + 1;
break;
}
pos++;
}
zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
}
} else {
zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
for (k = 0; k < maxlen; k++) {
for (i = 0; i < n_arrays; i++) {
uint32_t pos = array_pos[i];
while (1) {
if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
ZVAL_NULL(¶ms[i]);
break;
} else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
array_pos[i] = pos + 1;
break;
}
pos++;
}
}
fci.retval = &result;
fci.param_count = n_arrays;
fci.params = params;
fci.no_separation = 0;
if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
efree(array_pos);
zval_dtor(return_value);
for (i = 0; i < n_arrays; i++) {
zval_ptr_dtor(¶ms[i]);
}
efree(params);
RETURN_NULL();
} else {
for (i = 0; i < n_arrays; i++) {
zval_ptr_dtor(¶ms[i]);
}
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
}
efree(params);
}
efree(array_pos);
}
}
PHP_FUNCTION(array_key_exists)
{
zval *key;
HashTable *array;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_ZVAL(key)
Z_PARAM_ARRAY_OR_OBJECT_HT(array)
ZEND_PARSE_PARAMETERS_END();
switch (Z_TYPE_P(key)) {
case IS_STRING:
if (zend_symtable_exists_ind(array, Z_STR_P(key))) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_LONG:
if (zend_hash_index_exists(array, Z_LVAL_P(key))) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_NULL:
if (zend_hash_exists_ind(array, ZSTR_EMPTY_ALLOC())) {
RETURN_TRUE;
}
RETURN_FALSE;
default:
php_error_docref(NULL, E_WARNING, "The first argument should be either a string or an integer");
RETURN_FALSE;
}
}
PHP_FUNCTION(array_chunk)
{
int argc = ZEND_NUM_ARGS(), num_in;
zend_long size, current = 0;
zend_string *str_key;
zend_ulong num_key;
zend_bool preserve_keys = 0;
zval *input = NULL;
zval chunk;
zval *entry;
if (zend_parse_parameters(argc, "al|b", &input, &size, &preserve_keys) == FAILURE) {
return;
}
if (size < 1) {
php_error_docref(NULL, E_WARNING, "Size parameter expected to be greater than 0");
return;
}
num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
if (size > num_in) {
size = num_in > 0 ? num_in : 1;
}
array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
ZVAL_UNDEF(&chunk);
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
if (Z_TYPE(chunk) == IS_UNDEF) {
array_init_size(&chunk, (uint32_t)size);
}
if (preserve_keys) {
if (str_key) {
entry = zend_hash_update(Z_ARRVAL(chunk), str_key, entry);
} else {
entry = zend_hash_index_update(Z_ARRVAL(chunk), num_key, entry);
}
} else {
entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
}
zval_add_ref(entry);
if (!(++current % size)) {
add_next_index_zval(return_value, &chunk);
ZVAL_UNDEF(&chunk);
}
} ZEND_HASH_FOREACH_END();
if (Z_TYPE(chunk) != IS_UNDEF) {
add_next_index_zval(return_value, &chunk);
}
}
PHP_FUNCTION(array_combine)
{
HashTable *values, *keys;
uint32_t pos_values = 0;
zval *entry_keys, *entry_values;
int num_keys, num_values;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "hh", &keys, &values) == FAILURE) {
return;
}
num_keys = zend_hash_num_elements(keys);
num_values = zend_hash_num_elements(values);
if (num_keys != num_values) {
php_error_docref(NULL, E_WARNING, "Both parameters should have an equal number of elements");
RETURN_FALSE;
}
array_init_size(return_value, num_keys);
if (!num_keys) {
return;
}
ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
while (1) {
if (pos_values >= values->nNumUsed) {
break;
} else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) {
entry_values = &values->arData[pos_values].val;
if (Z_TYPE_P(entry_keys) == IS_LONG) {
entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
Z_LVAL_P(entry_keys), entry_values);
} else {
zend_string *key = zval_get_string(entry_keys);
entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
key, entry_values);
zend_string_release(key);
}
zval_add_ref(entry_values);
pos_values++;
break;
}
pos_values++;
}
} ZEND_HASH_FOREACH_END();
}