#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_smart_str.h"
#ifdef HAVE_SPL
#include "ext/spl/spl_array.h"
#endif
#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 SORT_REGULAR 0
#define SORT_NUMERIC 1
#define SORT_STRING 2
#define SORT_LOCALE_STRING 5
#define SORT_DESC 3
#define SORT_ASC 4
#define CASE_LOWER 0
#define CASE_UPPER 1
#define COUNT_NORMAL 0
#define COUNT_RECURSIVE 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
#define DOUBLE_DRIFT_FIX 0.000000000000001
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", SORT_ASC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_DESC", SORT_DESC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_REGULAR", SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_NUMERIC", SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_STRING", SORT_STRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", SORT_LOCALE_STRING, 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);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(array)
{
#ifdef ZTS
ts_free_id(array_globals_id);
#endif
return SUCCESS;
}
static void set_compare_func(int sort_type TSRMLS_DC)
{
switch (sort_type) {
case SORT_NUMERIC:
ARRAYG(compare_func) = numeric_compare_function;
break;
case SORT_STRING:
ARRAYG(compare_func) = string_compare_function;
break;
#if HAVE_STRCOLL
case SORT_LOCALE_STRING:
ARRAYG(compare_func) = string_locale_compare_function;
break;
#endif
case SORT_REGULAR:
default:
ARRAYG(compare_func) = compare_function;
break;
}
}
static int array_key_compare(const void *a, const void *b TSRMLS_DC)
{
Bucket *f;
Bucket *s;
zval result;
zval first;
zval second;
f = *((Bucket **) a);
s = *((Bucket **) b);
if (f->nKeyLength == 0) {
Z_TYPE(first) = IS_LONG;
Z_LVAL(first) = f->h;
} else {
Z_TYPE(first) = IS_STRING;
Z_STRVAL(first) = f->arKey;
Z_STRLEN(first) = f->nKeyLength-1;
}
if (s->nKeyLength == 0) {
Z_TYPE(second) = IS_LONG;
Z_LVAL(second) = s->h;
} else {
Z_TYPE(second) = IS_STRING;
Z_STRVAL(second) = s->arKey;
Z_STRLEN(second) = s->nKeyLength-1;
}
if (ARRAYG(compare_func)(&result, &first, &second TSRMLS_CC) == FAILURE) {
return 0;
}
if (Z_TYPE(result) == IS_DOUBLE) {
if (Z_DVAL(result) < 0) {
return -1;
} else if (Z_DVAL(result) > 0) {
return 1;
} else {
return 0;
}
}
convert_to_long(&result);
if (Z_LVAL(result) < 0) {
return -1;
} else if (Z_LVAL(result) > 0) {
return 1;
}
return 0;
}
static int array_reverse_key_compare(const void *a, const void *b TSRMLS_DC)
{
return array_key_compare(a, b TSRMLS_CC) * -1;
}
PHP_FUNCTION(krsort)
{
zval *array;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_reverse_key_compare, 0 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(ksort)
{
zval *array;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_key_compare, 0 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
static int php_count_recursive(zval *array, long mode TSRMLS_DC)
{
long cnt = 0;
zval **element;
if (Z_TYPE_P(array) == IS_ARRAY) {
if (Z_ARRVAL_P(array)->nApplyCount > 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
return 0;
}
cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
if (mode == COUNT_RECURSIVE) {
HashPosition pos;
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)) {
Z_ARRVAL_P(array)->nApplyCount++;
cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
Z_ARRVAL_P(array)->nApplyCount--;
}
}
}
return cnt;
}
PHP_FUNCTION(count)
{
zval *array;
long mode = COUNT_NORMAL;
if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE)
return;
switch (Z_TYPE_P(array)) {
case IS_NULL:
RETURN_LONG(0);
break;
case IS_ARRAY:
RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
break;
case IS_OBJECT: {
#ifdef HAVE_SPL
zval *retval;
if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval);
if (retval) {
convert_to_long_ex(&retval);
RETVAL_LONG(Z_LVAL_P(retval));
zval_ptr_dtor(&retval);
}
return;
}
#endif
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) TSRMLS_CC)) {
return;
}
}
}
default:
RETURN_LONG(1);
break;
}
}
static int array_data_compare(const void *a, const void *b TSRMLS_DC)
{
Bucket *f;
Bucket *s;
zval result;
zval *first;
zval *second;
f = *((Bucket **) a);
s = *((Bucket **) b);
first = *((zval **) f->pData);
second = *((zval **) s->pData);
if (ARRAYG(compare_func)(&result, first, second TSRMLS_CC) == FAILURE) {
return 0;
}
if (Z_TYPE(result) == IS_DOUBLE) {
if (Z_DVAL(result) < 0) {
return -1;
} else if (Z_DVAL(result) > 0) {
return 1;
} else {
return 0;
}
}
convert_to_long(&result);
if (Z_LVAL(result) < 0) {
return -1;
} else if (Z_LVAL(result) > 0) {
return 1;
}
return 0;
}
static int array_reverse_data_compare(const void *a, const void *b TSRMLS_DC)
{
return array_data_compare(a, b TSRMLS_CC)*-1;
}
static int array_natural_general_compare(const void *a, const void *b, int fold_case)
{
Bucket *f, *s;
zval *fval, *sval;
zval first, second;
int result;
f = *((Bucket **) a);
s = *((Bucket **) b);
fval = *((zval **) f->pData);
sval = *((zval **) s->pData);
first = *fval;
second = *sval;
if (Z_TYPE_P(fval) != IS_STRING) {
zval_copy_ctor(&first);
convert_to_string(&first);
}
if (Z_TYPE_P(sval) != IS_STRING) {
zval_copy_ctor(&second);
convert_to_string(&second);
}
result = strnatcmp_ex(Z_STRVAL(first), Z_STRLEN(first), Z_STRVAL(second), Z_STRLEN(second), fold_case);
if (Z_TYPE_P(fval) != IS_STRING)
zval_dtor(&first);
if (Z_TYPE_P(sval) != IS_STRING)
zval_dtor(&second);
return result;
}
static int array_natural_compare(const void *a, const void *b TSRMLS_DC)
{
return array_natural_general_compare(a, b, 0);
}
static int array_natural_case_compare(const void *a, const void *b TSRMLS_DC)
{
return array_natural_general_compare(a, b, 1);
}
static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
{
zval **array;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
if (fold_case) {
if (zend_hash_sort(target_hash, zend_qsort, array_natural_case_compare, 0 TSRMLS_CC) == FAILURE) {
return;
}
} else {
if (zend_hash_sort(target_hash, zend_qsort, array_natural_compare, 0 TSRMLS_CC) == 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;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_data_compare, 0 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(arsort)
{
zval *array;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_reverse_data_compare, 0 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(sort)
{
zval *array;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_data_compare, 1 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(rsort)
{
zval *array;
long sort_type = SORT_REGULAR;
HashTable *target_hash;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) {
RETURN_FALSE;
}
target_hash = HASH_OF(array);
set_compare_func(sort_type TSRMLS_CC);
if (zend_hash_sort(target_hash, zend_qsort, array_reverse_data_compare, 1 TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
static int array_user_compare(const void *a, const void *b TSRMLS_DC)
{
Bucket *f;
Bucket *s;
zval **args[2];
zval *retval_ptr;
zend_fcall_info fci;
f = *((Bucket **) a);
s = *((Bucket **) b);
args[0] = (zval **) f->pData;
args[1] = (zval **) s->pData;
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = *BG(user_compare_func_name);
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = 2;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, &BG(user_compare_fci_cache) TSRMLS_CC)== SUCCESS
&& retval_ptr) {
long retval;
convert_to_long_ex(&retval_ptr);
retval = Z_LVAL_P(retval_ptr);
zval_ptr_dtor(&retval_ptr);
return retval < 0 ? -1 : retval > 0 ? 1 : 0;;
} else {
return 0;
}
}
#define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
if (!zend_is_callable(*func_name, 0, NULL)) { \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid comparison function"); \
BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
BG(user_compare_func_name) = old_compare_func; \
RETURN_FALSE; \
} \
#define PHP_ARRAY_CMP_FUNC_VARS \
zval **old_compare_func; \
zend_fcall_info_cache old_user_compare_fci_cache \
#define PHP_ARRAY_CMP_FUNC_BACKUP() \
old_compare_func = BG(user_compare_func_name); \
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_cache) = old_user_compare_fci_cache; \
BG(user_compare_func_name) = old_compare_func; \
PHP_FUNCTION(usort)
{
zval **array;
HashTable *target_hash;
PHP_ARRAY_CMP_FUNC_VARS;
PHP_ARRAY_CMP_FUNC_BACKUP();
if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &array, &BG(user_compare_func_name)) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_CHECK(BG(user_compare_func_name))
BG(user_compare_fci_cache).initialized = 0;
if (zend_hash_sort(target_hash, zend_qsort, array_user_compare, 1 TSRMLS_CC) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_TRUE;
}
PHP_FUNCTION(uasort)
{
zval **array;
HashTable *target_hash;
PHP_ARRAY_CMP_FUNC_VARS;
PHP_ARRAY_CMP_FUNC_BACKUP();
if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &array, &BG(user_compare_func_name)) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_CHECK(BG(user_compare_func_name))
BG(user_compare_fci_cache).initialized = 0;
if (zend_hash_sort(target_hash, zend_qsort, array_user_compare, 0 TSRMLS_CC) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_TRUE;
}
static int array_user_key_compare(const void *a, const void *b TSRMLS_DC)
{
Bucket *f;
Bucket *s;
zval *key1, *key2;
zval *args[2];
zval retval;
int status;
ALLOC_INIT_ZVAL(key1);
ALLOC_INIT_ZVAL(key2);
args[0] = key1;
args[1] = key2;
f = *((Bucket **) a);
s = *((Bucket **) b);
if (f->nKeyLength) {
Z_STRVAL_P(key1) = estrndup(f->arKey, f->nKeyLength-1);
Z_STRLEN_P(key1) = f->nKeyLength-1;
Z_TYPE_P(key1) = IS_STRING;
} else {
Z_LVAL_P(key1) = f->h;
Z_TYPE_P(key1) = IS_LONG;
}
if (s->nKeyLength) {
Z_STRVAL_P(key2) = estrndup(s->arKey, s->nKeyLength-1);
Z_STRLEN_P(key2) = s->nKeyLength-1;
Z_TYPE_P(key2) = IS_STRING;
} else {
Z_LVAL_P(key2) = s->h;
Z_TYPE_P(key2) = IS_LONG;
}
status = call_user_function(EG(function_table), NULL, *BG(user_compare_func_name), &retval, 2, args TSRMLS_CC);
zval_ptr_dtor(&key1);
zval_ptr_dtor(&key2);
if (status == SUCCESS) {
convert_to_long(&retval);
return Z_LVAL(retval);
} else {
return 0;
}
}
PHP_FUNCTION(uksort)
{
zval **array;
HashTable *target_hash;
PHP_ARRAY_CMP_FUNC_VARS;
PHP_ARRAY_CMP_FUNC_BACKUP();
if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &array, &BG(user_compare_func_name)) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_CHECK(BG(user_compare_func_name))
BG(user_compare_fci_cache).initialized = 0;
if (zend_hash_sort(target_hash, zend_qsort, array_user_key_compare, 0 TSRMLS_CC) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_FALSE;
}
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_TRUE;
}
PHP_FUNCTION(end)
{
zval **array, **entry;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
zend_hash_internal_pointer_end(target_hash);
if (return_value_used) {
if (zend_hash_get_current_data(target_hash, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
}
PHP_FUNCTION(prev)
{
zval **array, **entry;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
zend_hash_move_backwards(target_hash);
if (return_value_used) {
if (zend_hash_get_current_data(target_hash, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
}
PHP_FUNCTION(next)
{
zval **array, **entry;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
zend_hash_move_forward(target_hash);
if (return_value_used) {
if (zend_hash_get_current_data(target_hash, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
}
PHP_FUNCTION(reset)
{
zval **array, **entry;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
zend_hash_internal_pointer_reset(target_hash);
if (return_value_used) {
if (zend_hash_get_current_data(target_hash, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
}
PHP_FUNCTION(current)
{
zval **array, **entry;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
if (zend_hash_get_current_data(target_hash, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
PHP_FUNCTION(key)
{
zval **array;
char *string_key;
uint string_length;
ulong num_key;
HashTable *target_hash;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is not an array or object");
RETURN_FALSE;
}
switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_length, &num_key, 0, NULL)) {
case HASH_KEY_IS_STRING:
RETVAL_STRINGL(string_key, string_length - 1, 1);
break;
case HASH_KEY_IS_LONG:
RETVAL_LONG(num_key);
break;
case HASH_KEY_NON_EXISTANT:
return;
}
}
PHP_FUNCTION(min)
{
int argc=ZEND_NUM_ARGS();
zval **result;
if (argc<=0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one value should be passed");
RETURN_NULL();
}
set_compare_func(SORT_REGULAR TSRMLS_CC);
if (argc == 1) {
zval **arr;
if (zend_get_parameters_ex(1, &arr) == FAILURE || Z_TYPE_PP(arr) != IS_ARRAY) {
WRONG_PARAM_COUNT;
}
if (zend_hash_minmax(Z_ARRVAL_PP(arr), array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) {
RETVAL_ZVAL(*result, 1, 0);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
RETURN_FALSE;
}
} else {
zval ***args = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
zval **min, result;
int i;
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args)==FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
min = args[0];
for (i=1; i<ZEND_NUM_ARGS(); i++) {
is_smaller_function(&result, *args[i], *min TSRMLS_CC);
if (Z_LVAL(result) == 1) {
min = args[i];
}
}
RETVAL_ZVAL(*min, 1, 0);
efree(args);
}
}
PHP_FUNCTION(max)
{
int argc=ZEND_NUM_ARGS();
zval **result;
if (argc<=0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one value should be passed");
RETURN_NULL();
}
set_compare_func(SORT_REGULAR TSRMLS_CC);
if (argc == 1) {
zval **arr;
if (zend_get_parameters_ex(1, &arr) == FAILURE || Z_TYPE_PP(arr) != IS_ARRAY) {
WRONG_PARAM_COUNT;
}
if (zend_hash_minmax(Z_ARRVAL_PP(arr), array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) {
RETVAL_ZVAL(*result, 1, 0);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
RETURN_FALSE;
}
} else {
zval ***args = (zval ***) safe_emalloc(sizeof(zval **), ZEND_NUM_ARGS(), 0);
zval **max, result;
int i;
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
max = args[0];
for (i=1; i<ZEND_NUM_ARGS(); i++) {
is_smaller_or_equal_function(&result, *args[i], *max TSRMLS_CC);
if (Z_LVAL(result) == 0) {
max = args[i];
}
}
RETVAL_ZVAL(*max, 1, 0);
efree(args);
}
}
static int php_array_walk(HashTable *target_hash, zval **userdata, int recursive TSRMLS_DC)
{
zval **args[3],
*retval_ptr,
*key=NULL;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition pos;
zend_fcall_info_cache array_walk_fci_cache = empty_fcall_info_cache;
args[1] = &key;
args[2] = userdata;
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
while (!EG(exception) && zend_hash_get_current_data_ex(target_hash, (void **)&args[0], &pos) == SUCCESS) {
if (recursive && Z_TYPE_PP(args[0]) == IS_ARRAY) {
HashTable *thash;
SEPARATE_ZVAL_IF_NOT_REF(args[0]);
thash = HASH_OF(*(args[0]));
if (thash->nApplyCount > 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
return 0;
}
thash->nApplyCount++;
php_array_walk(thash, userdata, recursive TSRMLS_CC);
thash->nApplyCount--;
} else {
zend_fcall_info fci;
MAKE_STD_ZVAL(key);
if (zend_hash_get_current_key_ex(target_hash, &string_key, &string_key_len, &num_key, 0, &pos) == HASH_KEY_IS_LONG) {
Z_TYPE_P(key) = IS_LONG;
Z_LVAL_P(key) = num_key;
} else {
ZVAL_STRINGL(key, string_key, string_key_len-1, 1);
}
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = *BG(array_walk_func_name);
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = userdata ? 3 : 2;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, &array_walk_fci_cache TSRMLS_CC) == SUCCESS) {
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
} else {
char *func_name;
if (zend_is_callable(*BG(array_walk_func_name), 0, &func_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call %s()", func_name);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call %s() - function does not exist", func_name);
}
if (key) {
zval_ptr_dtor(&key);
key = NULL;
}
efree(func_name);
break;
}
}
if (key) {
zval_ptr_dtor(&key);
key = NULL;
}
zend_hash_move_forward_ex(target_hash, &pos);
}
return 0;
}
PHP_FUNCTION(array_walk)
{
zval *array,
*userdata = NULL,
*tmp,
**old_walk_func_name;
HashTable *target_hash;
old_walk_func_name = BG(array_walk_func_name);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz/|z/", &array, &tmp, &userdata) == FAILURE) {
return;
}
target_hash = HASH_OF(array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
RETURN_FALSE;
}
if (Z_TYPE_P(tmp) != IS_ARRAY && Z_TYPE_P(tmp) != IS_STRING) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong syntax for function name");
RETURN_FALSE;
} else {
BG(array_walk_func_name) = &tmp;
}
php_array_walk(target_hash, userdata ? &userdata: NULL, 0 TSRMLS_CC);
BG(array_walk_func_name) = old_walk_func_name;
RETURN_TRUE;
}
PHP_FUNCTION(array_walk_recursive)
{
zval *array,
*userdata = NULL,
*tmp,
**old_walk_func_name;
HashTable *target_hash;
old_walk_func_name = BG(array_walk_func_name);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz/|z/", &array, &tmp, &userdata) == FAILURE) {
return;
}
target_hash = HASH_OF(array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
RETURN_FALSE;
}
if (Z_TYPE_P(tmp) != IS_ARRAY && Z_TYPE_P(tmp) != IS_STRING) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong syntax for function name");
RETURN_FALSE;
} else {
BG(array_walk_func_name) = &tmp;
}
php_array_walk(target_hash, userdata ? &userdata : NULL, 1 TSRMLS_CC);
BG(array_walk_func_name) = old_walk_func_name;
RETURN_TRUE;
}
static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
{
zval **value,
**array,
**strict,
**entry,
res;
HashTable *target_hash;
HashPosition pos;
ulong num_key;
uint str_key_len;
char *string_key;
int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &value, &array, &strict) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(array) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong datatype for second argument");
RETURN_FALSE;
}
if (ZEND_NUM_ARGS() == 3) {
convert_to_boolean_ex(strict);
if (Z_LVAL_PP(strict)) {
is_equal_func = is_identical_function;
}
}
target_hash = HASH_OF(*array);
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
while (zend_hash_get_current_data_ex(target_hash, (void **)&entry, &pos) == SUCCESS) {
is_equal_func(&res, *value, *entry TSRMLS_CC);
if (Z_LVAL(res)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
switch (zend_hash_get_current_key_ex(target_hash, &string_key, &str_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_STRING:
RETURN_STRINGL(string_key, str_key_len-1, 1);
break;
case HASH_KEY_IS_LONG:
RETURN_LONG(num_key);
break;
}
}
}
zend_hash_move_forward_ex(target_hash, &pos);
}
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 int php_valid_var_name(char *var_name, int len)
{
int i, ch;
if (!var_name)
return 0;
ch = (int)((unsigned char *)var_name)[0];
if (var_name[0] != '_' &&
(ch < 65 || ch > 90) &&
(ch < 97 || ch > 122) &&
(ch < 127 || ch > 255)
) {
return 0;
}
if (len > 1) {
for (i = 1; i < len; i++) {
ch = (int)((unsigned char *)var_name)[i];
if (var_name[i] != '_' &&
(ch < 48 || ch > 57) &&
(ch < 65 || ch > 90) &&
(ch < 97 || ch > 122) &&
(ch < 127 || ch > 255)
) {
return 0;
}
}
}
return 1;
}
PHP_FUNCTION(extract)
{
zval **var_array, **z_extract_type, **prefix;
zval **entry, *data;
char *var_name;
smart_str final_name = {0};
ulong num_key;
uint var_name_len;
int var_exists, extract_type, key_type, count = 0;
int extract_refs = 0;
HashPosition pos;
switch (ZEND_NUM_ARGS()) {
case 1:
if (zend_get_parameters_ex(1, &var_array) == FAILURE) {
WRONG_PARAM_COUNT;
}
extract_type = EXTR_OVERWRITE;
break;
case 2:
if (zend_get_parameters_ex(2, &var_array, &z_extract_type) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long_ex(z_extract_type);
extract_type = Z_LVAL_PP(z_extract_type);
extract_refs = (extract_type & EXTR_REFS);
extract_type &= 0xff;
if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Prefix expected to be specified");
return;
}
break;
case 3:
if (zend_get_parameters_ex(3, &var_array, &z_extract_type, &prefix) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long_ex(z_extract_type);
extract_type = Z_LVAL_PP(z_extract_type);
extract_refs = (extract_type & EXTR_REFS);
extract_type &= 0xff;
convert_to_string_ex(prefix);
break;
default:
WRONG_PARAM_COUNT;
break;
}
if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown extract type");
return;
}
if (Z_TYPE_PP(var_array) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument should be an array");
return;
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(var_array), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(var_array), (void **)&entry, &pos) == SUCCESS) {
key_type = zend_hash_get_current_key_ex(Z_ARRVAL_PP(var_array), &var_name, &var_name_len, &num_key, 0, &pos);
var_exists = 0;
if (key_type == HASH_KEY_IS_STRING) {
var_name_len--;
var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
} else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) {
smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
smart_str_appendc(&final_name, '_');
smart_str_append_long(&final_name, num_key);
} else {
zend_hash_move_forward_ex(Z_ARRVAL_PP(var_array), &pos);
continue;
}
switch (extract_type) {
case EXTR_IF_EXISTS:
if (!var_exists) break;
case EXTR_OVERWRITE:
if (var_exists && !strcmp(var_name, "GLOBALS")) {
break;
}
smart_str_appendl(&final_name, var_name, var_name_len);
break;
case EXTR_PREFIX_IF_EXISTS:
if (var_exists) {
smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
smart_str_appendc(&final_name, '_');
smart_str_appendl(&final_name, var_name, var_name_len);
}
break;
case EXTR_PREFIX_SAME:
if (!var_exists)
smart_str_appendl(&final_name, var_name, var_name_len);
case EXTR_PREFIX_ALL:
if (final_name.len == 0 && var_name_len != 0) {
smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
smart_str_appendc(&final_name, '_');
smart_str_appendl(&final_name, var_name, var_name_len);
}
break;
case EXTR_PREFIX_INVALID:
if (final_name.len == 0) {
if (!php_valid_var_name(var_name, var_name_len)) {
smart_str_appendl(&final_name, Z_STRVAL_PP(prefix), Z_STRLEN_PP(prefix));
smart_str_appendc(&final_name, '_');
smart_str_appendl(&final_name, var_name, var_name_len);
} else
smart_str_appendl(&final_name, var_name, var_name_len);
}
break;
default:
if (!var_exists)
smart_str_appendl(&final_name, var_name, var_name_len);
break;
}
if (final_name.len) {
smart_str_0(&final_name);
if (php_valid_var_name(final_name.c, final_name.len)) {
if (extract_refs) {
zval **orig_var;
SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
zval_add_ref(entry);
if (zend_hash_find(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) &orig_var) == SUCCESS) {
zval_ptr_dtor(orig_var);
*orig_var = *entry;
} else {
zend_hash_update(EG(active_symbol_table), final_name.c, final_name.len+1, (void **) entry, sizeof(zval *), NULL);
}
} else {
MAKE_STD_ZVAL(data);
*data = **entry;
zval_copy_ctor(data);
ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), final_name.c, final_name.len+1, data, 1, 0);
}
count++;
}
final_name.len = 0;
}
zend_hash_move_forward_ex(Z_ARRVAL_PP(var_array), &pos);
}
smart_str_free(&final_name);
RETURN_LONG(count);
}
static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry)
{
zval **value_ptr, *value, *data;
if (Z_TYPE_P(entry) == IS_STRING) {
if (zend_hash_find(eg_active_symbol_table, Z_STRVAL_P(entry),
Z_STRLEN_P(entry)+1, (void **)&value_ptr) != FAILURE) {
value = *value_ptr;
ALLOC_ZVAL(data);
*data = *value;
zval_copy_ctor(data);
INIT_PZVAL(data);
zend_hash_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(entry),
Z_STRLEN_P(entry)+1, &data, sizeof(zval *), NULL);
}
}
else if (Z_TYPE_P(entry) == IS_ARRAY) {
HashPosition pos;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), (void**)&value_ptr, &pos) == SUCCESS) {
value = *value_ptr;
php_compact_var(eg_active_symbol_table, return_value, value);
zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos);
}
}
}
PHP_FUNCTION(compact)
{
zval ***args;
int i;
if (ZEND_NUM_ARGS() < 1) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval **), 0);
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
array_init(return_value);
for (i=0; i<ZEND_NUM_ARGS(); i++) {
php_compact_var(EG(active_symbol_table), return_value, *args[i]);
}
efree(args);
}
PHP_FUNCTION(array_fill)
{
zval **start_key, **num, **val, *newval;
long i;
if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &start_key, &num, &val) == FAILURE) {
WRONG_PARAM_COUNT;
}
switch (Z_TYPE_PP(start_key)) {
case IS_STRING:
case IS_LONG:
case IS_DOUBLE:
array_init(return_value);
if (PZVAL_IS_REF(*val)) {
SEPARATE_ZVAL(val);
}
convert_to_long_ex(start_key);
zval_add_ref(val);
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(start_key), val, sizeof(val), NULL);
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong data type for start key");
RETURN_FALSE;
break;
}
convert_to_long_ex(num);
i = Z_LVAL_PP(num) - 1;
if (i < 0) {
zend_hash_destroy(Z_ARRVAL_P(return_value));
efree(Z_ARRVAL_P(return_value));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements must be positive");
RETURN_FALSE;
}
newval = *val;
while (i--) {
zval_add_ref(&newval);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &newval, sizeof(zval *), NULL);
}
}
PHP_FUNCTION(array_fill_keys)
{
zval *keys, *val, **entry;
HashPosition pos;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &keys, &val) == FAILURE) {
return;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry, &pos) == SUCCESS) {
if (Z_TYPE_PP(entry) == IS_LONG) {
zval_add_ref(&val);
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &val, sizeof(zval *), NULL);
} else {
zval key, *key_ptr = *entry;
if (Z_TYPE_PP(entry) != IS_STRING) {
key = **entry;
zval_copy_ctor(&key);
convert_to_string(&key);
key_ptr = &key;
}
zval_add_ref(&val);
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, &val, sizeof(zval *), NULL);
if (key_ptr != *entry) {
zval_dtor(&key);
}
}
zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
}
}
PHP_FUNCTION(range)
{
zval *zlow, *zhigh, *zstep = NULL;
int err = 0, is_step_double = 0;
double step = 1.0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) {
RETURN_FALSE;
}
if (zstep) {
if (Z_TYPE_P(zstep) == IS_DOUBLE || (Z_TYPE_P(zstep) == IS_STRING && is_numeric_string(Z_STRVAL_P(zstep), Z_STRLEN_P(zstep), NULL, NULL, 0) == IS_DOUBLE)) {
is_step_double = 1;
}
convert_to_double_ex(&zstep);
step = Z_DVAL_P(zstep);
if (step < 0.0) {
step *= -1;
}
}
array_init(return_value);
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;
long lstep = (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;
}
convert_to_string(zlow);
convert_to_string(zhigh);
low = (unsigned char *)Z_STRVAL_P(zlow);
high = (unsigned char *)Z_STRVAL_P(zhigh);
if (*low > *high) {
if (lstep <= 0) {
err = 1;
goto err;
}
for (; *low >= *high; (*low) -= (unsigned int)lstep) {
add_next_index_stringl(return_value, low, 1, 1);
if (((signed int)*low - lstep) < 0) {
break;
}
}
} else if (*high > *low) {
if (lstep <= 0) {
err = 1;
goto err;
}
for (; *low <= *high; (*low) += (unsigned int)lstep) {
add_next_index_stringl(return_value, low, 1, 1);
if (((signed int)*low + lstep) > 255) {
break;
}
}
} else {
add_next_index_stringl(return_value, low, 1, 1);
}
} else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
double low, high;
double_str:
convert_to_double(zlow);
convert_to_double(zhigh);
low = Z_DVAL_P(zlow);
high = Z_DVAL_P(zhigh);
if (low > high) {
if (low - high < step || step <= 0) {
err = 1;
goto err;
}
for (; low >= (high - DOUBLE_DRIFT_FIX); low -= step) {
add_next_index_double(return_value, low);
}
} else if (high > low) {
if (high - low < step || step <= 0) {
err = 1;
goto err;
}
for (; low <= (high + DOUBLE_DRIFT_FIX); low += step) {
add_next_index_double(return_value, low);
}
} else {
add_next_index_double(return_value, low);
}
} else {
double low, high;
long lstep;
long_str:
convert_to_double(zlow);
convert_to_double(zhigh);
low = Z_DVAL_P(zlow);
high = Z_DVAL_P(zhigh);
lstep = (long) step;
if (low > high) {
if (low - high < lstep || lstep <= 0) {
err = 1;
goto err;
}
for (; low >= high; low -= lstep) {
add_next_index_long(return_value, (long)low);
}
} else if (high > low) {
if (high - low < lstep || lstep <= 0) {
err = 1;
goto err;
}
for (; low <= high; low += lstep) {
add_next_index_long(return_value, (long)low);
}
} else {
add_next_index_long(return_value, (long)low);
}
}
err:
if (err) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "step exceeds the specified range");
zval_dtor(return_value);
RETURN_FALSE;
}
}
static void array_data_shuffle(zval *array TSRMLS_DC)
{
Bucket **elems, *temp;
HashTable *hash;
int j, n_elems, rnd_idx, n_left;
n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
if (n_elems < 1) {
return;
}
elems = (Bucket **)safe_emalloc(n_elems, sizeof(Bucket *), 0);
hash = Z_ARRVAL_P(array);
n_left = n_elems;
for (j = 0, temp = hash->pListHead; temp; temp = temp->pListNext)
elems[j++] = temp;
while (--n_left) {
rnd_idx = php_rand(TSRMLS_C);
RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
if (rnd_idx != n_left) {
temp = elems[n_left];
elems[n_left] = elems[rnd_idx];
elems[rnd_idx] = temp;
}
}
HANDLE_BLOCK_INTERRUPTIONS();
hash->pListHead = elems[0];
hash->pListTail = NULL;
hash->pInternalPointer = hash->pListHead;
for (j = 0; j < n_elems; j++) {
if (hash->pListTail) {
hash->pListTail->pListNext = elems[j];
}
elems[j]->pListLast = hash->pListTail;
elems[j]->pListNext = NULL;
hash->pListTail = elems[j];
}
temp = hash->pListHead;
j = 0;
while (temp != NULL) {
temp->nKeyLength = 0;
temp->h = j++;
temp = temp->pListNext;
}
hash->nNextFreeElement = n_elems;
zend_hash_rehash(hash);
HANDLE_UNBLOCK_INTERRUPTIONS();
efree(elems);
}
PHP_FUNCTION(shuffle)
{
zval *array;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
RETURN_FALSE;
}
array_data_shuffle(array TSRMLS_CC);
RETURN_TRUE;
}
HashTable* php_splice(HashTable *in_hash, int offset, int length, zval ***list, int list_count, HashTable **removed)
{
HashTable *out_hash = NULL;
int num_in,
pos,
i;
Bucket *p;
zval *entry;
if (!in_hash)
return NULL;
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;
}
ALLOC_HASHTABLE(out_hash);
zend_hash_init(out_hash, 0, NULL, ZVAL_PTR_DTOR, 0);
for (pos=0, p=in_hash->pListHead; pos<offset && p ; pos++, p=p->pListNext) {
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
if (removed != NULL) {
for ( ; pos<offset+length && p; pos++, p=p->pListNext) {
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
}
} else
for ( ; pos<offset+length && p; pos++, p=p->pListNext);
if (list != NULL) {
for (i=0; i<list_count; i++) {
entry = *list[i];
entry->refcount++;
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
}
for ( ; p ; p=p->pListNext) {
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
zend_hash_internal_pointer_reset(out_hash);
return out_hash;
}
PHP_FUNCTION(array_push)
{
zval ***args,
*stack,
*new_var;
int i,
argc;
argc = ZEND_NUM_ARGS();
if (argc < 2) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
stack = *args[0];
if (Z_TYPE_P(stack) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument should be an array");
efree(args);
RETURN_FALSE;
}
for (i=1; i<argc; i++) {
new_var = *args[i];
new_var->refcount++;
if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var, sizeof(zval *), NULL) == FAILURE) {
new_var->refcount--;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied");
efree(args);
RETURN_FALSE;
}
}
efree(args);
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
}
static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
{
zval **stack,
**val;
char *key = NULL;
int key_len = 0;
ulong index;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &stack) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(stack) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
if (zend_hash_num_elements(Z_ARRVAL_PP(stack)) == 0) {
return;
}
if (off_the_end)
zend_hash_internal_pointer_end(Z_ARRVAL_PP(stack));
else
zend_hash_internal_pointer_reset(Z_ARRVAL_PP(stack));
zend_hash_get_current_data(Z_ARRVAL_PP(stack), (void **)&val);
RETVAL_ZVAL(*val, 1, 0);
zend_hash_get_current_key_ex(Z_ARRVAL_PP(stack), &key, &key_len, &index, 0, NULL);
if (key && Z_ARRVAL_PP(stack) == &EG(symbol_table)) {
zend_delete_global_variable(key, key_len-1 TSRMLS_CC);
} else {
zend_hash_del_key_or_index(Z_ARRVAL_PP(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX);
}
if (!off_the_end) {
unsigned int k = 0;
int should_rehash = 0;
Bucket *p = Z_ARRVAL_PP(stack)->pListHead;
while (p != NULL) {
if (p->nKeyLength == 0) {
if (p->h != k) {
p->h = k++;
should_rehash = 1;
} else {
k++;
}
}
p = p->pListNext;
}
Z_ARRVAL_PP(stack)->nNextFreeElement = k;
if (should_rehash) {
zend_hash_rehash(Z_ARRVAL_PP(stack));
}
} else if (!key_len && index >= Z_ARRVAL_PP(stack)->nNextFreeElement-1) {
Z_ARRVAL_PP(stack)->nNextFreeElement = Z_ARRVAL_PP(stack)->nNextFreeElement - 1;
}
zend_hash_internal_pointer_reset(Z_ARRVAL_PP(stack));
}
PHP_FUNCTION(array_pop)
{
_phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
PHP_FUNCTION(array_shift)
{
_phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
PHP_FUNCTION(array_unshift)
{
zval ***args,
*stack;
HashTable *new_hash;
int argc;
argc = ZEND_NUM_ARGS();
if (argc < 2) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
stack = *args[0];
if (Z_TYPE_P(stack) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be an array");
efree(args);
RETURN_FALSE;
}
new_hash = php_splice(Z_ARRVAL_P(stack), 0, 0, &args[1], argc-1, NULL);
zend_hash_destroy(Z_ARRVAL_P(stack));
if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
*Z_ARRVAL_P(stack) = *new_hash;
FREE_HASHTABLE(new_hash);
efree(args);
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
}
PHP_FUNCTION(array_splice)
{
zval ***args,
*array,
***repl = NULL;
HashTable *new_hash = NULL;
Bucket *p;
int argc,
i,
offset,
length,
repl_num = 0;
argc = ZEND_NUM_ARGS();
if (argc < 2 || argc > 4) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
array = *args[0];
if (Z_TYPE_P(array) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be an array");
efree(args);
return;
}
convert_to_long_ex(args[1]);
offset = Z_LVAL_PP(args[1]);
if (argc > 2) {
convert_to_long_ex(args[2]);
length = Z_LVAL_PP(args[2]);
} else
length = zend_hash_num_elements(Z_ARRVAL_P(array));
if (argc == 4) {
convert_to_array_ex(args[3]);
repl_num = zend_hash_num_elements(Z_ARRVAL_PP(args[3]));
repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0);
for (p=Z_ARRVAL_PP(args[3])->pListHead, i=0; p; p=p->pListNext, i++) {
repl[i] = ((zval **)p->pData);
}
}
array_init(return_value);
new_hash = php_splice(Z_ARRVAL_P(array), offset, length,
repl, repl_num,
&Z_ARRVAL_P(return_value));
zend_hash_destroy(Z_ARRVAL_P(array));
if (Z_ARRVAL_P(array) == &EG(symbol_table)) {
zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
*Z_ARRVAL_P(array) = *new_hash;
FREE_HASHTABLE(new_hash);
if (argc == 4)
efree(repl);
efree(args);
}
PHP_FUNCTION(array_slice)
{
zval *input,
**z_length,
**entry;
long offset,
length = 0;
zend_bool preserve_keys = 0;
int num_in,
pos;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition hpos;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|Zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
return;
}
num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
if (ZEND_NUM_ARGS() < 3 || Z_TYPE_PP(z_length) == IS_NULL) {
length = num_in;
} else {
convert_to_long_ex(z_length);
length = Z_LVAL_PP(z_length);
}
array_init(return_value);
if (offset > num_in) {
return;
} else if (offset < 0 && (offset = (num_in + offset)) < 0) {
offset = 0;
}
if (length < 0) {
length = num_in - offset + length;
} else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
length = num_in - offset;
}
if (length == 0) {
return;
}
pos = 0;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &hpos);
while (pos < offset && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
pos++;
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
}
while (pos < offset + length && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) {
zval_add_ref(entry);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &hpos)) {
case HASH_KEY_IS_STRING:
zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
if (preserve_keys) {
zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
} else {
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
}
break;
}
pos++;
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos);
}
}
PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC)
{
zval **src_entry, **dest_entry;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition pos;
zend_hash_internal_pointer_reset_ex(src, &pos);
while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) {
switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_STRING:
if (recursive && zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == SUCCESS) {
HashTable *thash = HASH_OF(*dest_entry);
if ((thash && thash->nApplyCount > 1) || (*src_entry == *dest_entry && (*dest_entry)->is_ref && ((*dest_entry)->refcount % 2))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
return 0;
}
SEPARATE_ZVAL(dest_entry);
SEPARATE_ZVAL(src_entry);
if (Z_TYPE_PP(dest_entry) == IS_NULL) {
convert_to_array_ex(dest_entry);
add_next_index_null(*dest_entry);
} else {
convert_to_array_ex(dest_entry);
}
if (Z_TYPE_PP(src_entry) == IS_NULL) {
convert_to_array_ex(src_entry);
add_next_index_null(*src_entry);
} else {
convert_to_array_ex(src_entry);
}
if (thash) {
thash->nApplyCount++;
}
if (!php_array_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry), recursive TSRMLS_CC)) {
if (thash) {
thash->nApplyCount--;
}
return 0;
}
if (thash) {
thash->nApplyCount--;
}
} else {
(*src_entry)->refcount++;
zend_hash_update(dest, string_key, string_key_len,
src_entry, sizeof(zval *), NULL);
}
break;
case HASH_KEY_IS_LONG:
(*src_entry)->refcount++;
zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL);
break;
}
zend_hash_move_forward_ex(src, &pos);
}
return 1;
}
static void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive)
{
zval ***args = NULL;
int argc, i, params_ok = 1;
argc = ZEND_NUM_ARGS();
if (argc < 1) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
for (i = 0; i < argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i+1);
params_ok = 0;
}
}
if (params_ok == 0) {
efree(args);
return;
}
array_init(return_value);
for (i=0; i<argc; i++) {
SEPARATE_ZVAL(args[i]);
convert_to_array_ex(args[i]);
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
}
efree(args);
}
PHP_FUNCTION(array_merge)
{
php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
PHP_FUNCTION(array_merge_recursive)
{
php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
PHP_FUNCTION(array_keys)
{
zval **input,
**search_value,
**entry,
res,
**strict,
*new_val;
int add_key;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition pos;
int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
search_value = NULL;
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 3 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &input, &search_value, &strict) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be an array");
return;
}
if (ZEND_NUM_ARGS() == 3) {
convert_to_boolean_ex(strict);
if (Z_LVAL_PP(strict)) {
is_equal_func = is_identical_function;
}
}
array_init(return_value);
add_key = 1;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, &pos) == SUCCESS) {
if (search_value != NULL) {
is_equal_func(&res, *search_value, *entry TSRMLS_CC);
add_key = zval_is_true(&res);
}
if (add_key) {
MAKE_STD_ZVAL(new_val);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
case HASH_KEY_IS_STRING:
Z_TYPE_P(new_val) = IS_STRING;
Z_STRVAL_P(new_val) = string_key;
Z_STRLEN_P(new_val) = string_key_len-1;
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val,
sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
Z_TYPE_P(new_val) = IS_LONG;
Z_LVAL_P(new_val) = num_key;
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val,
sizeof(zval *), NULL);
break;
}
}
zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos);
}
}
PHP_FUNCTION(array_values)
{
zval **input,
**entry;
HashPosition pos;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &input) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, &pos) == SUCCESS) {
(*entry)->refcount++;
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry,
sizeof(zval *), NULL);
zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos);
}
}
PHP_FUNCTION(array_count_values)
{
zval **input,
**entry,
**tmp;
HashTable *myht;
HashPosition pos;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &input) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
array_init(return_value);
myht = Z_ARRVAL_PP(input);
zend_hash_internal_pointer_reset_ex(myht, &pos);
while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) {
if (Z_TYPE_PP(entry) == IS_LONG) {
if (zend_hash_index_find(Z_ARRVAL_P(return_value),
Z_LVAL_PP(entry),
(void**)&tmp) == FAILURE) {
zval *data;
MAKE_STD_ZVAL(data);
ZVAL_LONG(data, 1);
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
} else {
Z_LVAL_PP(tmp)++;
}
} else if (Z_TYPE_PP(entry) == IS_STRING) {
if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) {
zval *data;
MAKE_STD_ZVAL(data);
ZVAL_LONG(data, 1);
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
} else {
Z_LVAL_PP(tmp)++;
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
}
zend_hash_move_forward_ex(myht, &pos);
}
}
PHP_FUNCTION(array_reverse)
{
zval **input,
**z_preserve_keys,
**entry;
char *string_key;
uint string_key_len;
ulong num_key;
zend_bool preserve_keys = 0;
HashPosition pos;
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &input, &z_preserve_keys) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
if (ZEND_NUM_ARGS() > 1) {
convert_to_boolean_ex(z_preserve_keys);
preserve_keys = Z_BVAL_PP(z_preserve_keys);
}
array_init(return_value);
zend_hash_internal_pointer_end_ex(Z_ARRVAL_PP(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, &pos) == SUCCESS) {
(*entry)->refcount++;
switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(input), &string_key, &string_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_STRING:
zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
if (preserve_keys) {
zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL);
} else {
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL);
}
break;
}
zend_hash_move_backwards_ex(Z_ARRVAL_PP(input), &pos);
}
}
PHP_FUNCTION(array_pad)
{
zval **input;
zval **pad_size;
zval **pad_value;
zval ***pads;
HashTable *new_hash;
int input_size;
int pad_size_abs;
int num_pads;
int do_pad;
int i;
if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &input, &pad_size, &pad_value) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
convert_to_long_ex(pad_size);
input_size = zend_hash_num_elements(Z_ARRVAL_PP(input));
pad_size_abs = abs(Z_LVAL_PP(pad_size));
if (pad_size_abs < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
zval_dtor(return_value);
RETURN_FALSE;
}
do_pad = (input_size >= pad_size_abs) ? 0 : 1;
RETVAL_ZVAL(*input, 1, 0);
if (!do_pad) {
return;
}
num_pads = pad_size_abs - input_size;
if(num_pads > 1048576) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time");
zval_dtor(return_value);
RETURN_FALSE;
}
pads = (zval ***)safe_emalloc(num_pads, sizeof(zval **), 0);
for (i = 0; i < num_pads; i++) {
pads[i] = pad_value;
}
if (Z_LVAL_PP(pad_size) > 0) {
new_hash = php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL);
} else {
new_hash = php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL);
}
zend_hash_destroy(Z_ARRVAL_P(return_value));
if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
*Z_ARRVAL_P(return_value) = *new_hash;
FREE_HASHTABLE(new_hash);
efree(pads);
}
PHP_FUNCTION(array_flip)
{
zval **array, **entry, *data;
HashTable *target_hash;
char *string_key;
uint str_key_len;
ulong num_key;
HashPosition pos;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
RETURN_FALSE;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
while (zend_hash_get_current_data_ex(target_hash, (void **)&entry, &pos) == SUCCESS) {
MAKE_STD_ZVAL(data);
switch (zend_hash_get_current_key_ex(target_hash, &string_key, &str_key_len, &num_key, 1, &pos)) {
case HASH_KEY_IS_STRING:
Z_STRVAL_P(data) = string_key;
Z_STRLEN_P(data) = str_key_len-1;
Z_TYPE_P(data) = IS_STRING;
break;
case HASH_KEY_IS_LONG:
Z_TYPE_P(data) = IS_LONG;
Z_LVAL_P(data) = num_key;
break;
}
if (Z_TYPE_PP(entry) == IS_LONG) {
zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL);
} else if (Z_TYPE_PP(entry) == IS_STRING) {
zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL);
} else {
zval_ptr_dtor(&data);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!");
}
zend_hash_move_forward_ex(target_hash, &pos);
}
}
PHP_FUNCTION(array_change_key_case)
{
zval **array, **entry, **to_upper;
char *string_key;
char *new_key;
uint str_key_len;
ulong num_key;
ulong change_to_upper=0;
HashPosition pos;
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &array, &to_upper) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (ZEND_NUM_ARGS() > 1) {
convert_to_long_ex(to_upper);
change_to_upper = Z_LVAL_PP(to_upper);
}
if (Z_TYPE_PP(array) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
RETURN_FALSE;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(array), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(array), (void **)&entry, &pos) == SUCCESS) {
(*entry)->refcount++;
switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_LONG:
zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(entry), NULL);
break;
case HASH_KEY_IS_STRING:
new_key=estrndup(string_key,str_key_len - 1);
if (change_to_upper)
php_strtoupper(new_key, str_key_len - 1);
else
php_strtolower(new_key, str_key_len - 1);
zend_hash_update(Z_ARRVAL_P(return_value), new_key, str_key_len, entry, sizeof(entry), NULL);
efree(new_key);
break;
}
zend_hash_move_forward_ex(Z_ARRVAL_PP(array), &pos);
}
}
PHP_FUNCTION(array_unique)
{
zval **array, *tmp;
HashTable *target_hash;
Bucket *p;
struct bucketindex {
Bucket *b;
unsigned int i;
};
struct bucketindex *arTmp, *cmpdata, *lastkept;
unsigned int i;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
target_hash = HASH_OF(*array);
if (!target_hash) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
RETURN_FALSE;
}
array_init(return_value);
zend_hash_copy(Z_ARRVAL_P(return_value), target_hash, (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*));
if (target_hash->nNumOfElements <= 1) {
return;
}
arTmp = (struct bucketindex *) pemalloc((target_hash->nNumOfElements + 1) * sizeof(struct bucketindex), target_hash->persistent);
if (!arTmp) {
RETURN_FALSE;
}
for (i = 0, p = target_hash->pListHead; p; i++, p = p->pListNext) {
arTmp[i].b = p;
arTmp[i].i = i;
}
arTmp[i].b = NULL;
set_compare_func(SORT_STRING TSRMLS_CC);
zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), array_data_compare TSRMLS_CC);
lastkept = arTmp;
for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) {
if (array_data_compare(lastkept, cmpdata TSRMLS_CC)) {
lastkept = cmpdata;
} else {
if (lastkept->i > cmpdata->i) {
p = lastkept->b;
lastkept = cmpdata;
} else {
p = cmpdata->b;
}
if (p->nKeyLength) {
if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
zend_delete_global_variable(p->arKey, p->nKeyLength-1 TSRMLS_CC);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength);
}
} else {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
}
}
}
pefree(arTmp, target_hash->persistent);
}
static int zval_compare(zval **a, zval **b TSRMLS_DC)
{
zval result;
zval *first;
zval *second;
first = *((zval **) a);
second = *((zval **) b);
if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
return 0;
}
if (Z_TYPE(result) == IS_DOUBLE) {
if (Z_DVAL(result) < 0) {
return -1;
} else if (Z_DVAL(result) > 0) {
return 1;
} else {
return 0;
}
}
convert_to_long(&result);
if (Z_LVAL(result) < 0) {
return -1;
} else if (Z_LVAL(result) > 0) {
return 1;
}
return 0;
}
static int zval_user_compare(zval **a, zval **b TSRMLS_DC)
{
zval **args[2];
zval *retval_ptr;
zend_fcall_info fci;
args[0] = (zval **) a;
args[1] = (zval **) b;
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = *BG(user_compare_func_name);
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = 2;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, &BG(user_compare_fci_cache) TSRMLS_CC)== SUCCESS
&& retval_ptr) {
long retval;
convert_to_long_ex(&retval_ptr);
retval = Z_LVAL_P(retval_ptr);
zval_ptr_dtor(&retval_ptr);
return retval < 0 ? -1 : retval > 0 ? 1 : 0;;
} else {
return 0;
}
}
static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type)
{
Bucket *p;
int argc, i;
zval ***args;
int (*intersect_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
zend_bool ok;
zval **data;
argc = ZEND_NUM_ARGS();
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (argc < 2 || zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
char *callback_name;
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
argc--;
if (!zend_is_callable(*args[argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
intersect_data_compare_func = zval_user_compare;
BG(user_compare_func_name) = args[argc];
BG(user_compare_fci_cache).initialized = 0;
} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
intersect_data_compare_func = zval_compare;
}
for (i = 0; i < argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
RETVAL_NULL();
goto out;
}
}
array_init(return_value);
for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
if (p->nKeyLength == 0) {
ok = 1;
for (i = 1; i < argc; i++) {
if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == FAILURE ||
(intersect_data_compare_func &&
intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)) {
ok = 0;
break;
}
}
if (ok) {
(*((zval**)p->pData))->refcount++;
zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
}
} else {
ok = 1;
for (i = 1; i < argc; i++) {
if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == FAILURE ||
(intersect_data_compare_func &&
intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0)) {
ok = 0;
break;
}
}
if (ok) {
(*((zval**)p->pData))->refcount++;
zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
}
}
}
out:
efree(args);
}
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type)
{
zval ***args = NULL;
HashTable *hash;
int argc, arr_argc, i, c = 0;
Bucket ***lists, **list, ***ptrs, *p;
char *callback_name;
PHP_ARRAY_CMP_FUNC_VARS;
int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC);
int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC);
argc = ZEND_NUM_ARGS();
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
PHP_ARRAY_CMP_FUNC_BACKUP();
if (behavior == INTERSECT_NORMAL) {
intersect_key_compare_func = array_key_compare;
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
if (argc < 2) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc;
intersect_data_compare_func = array_data_compare;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
intersect_data_compare_func = array_user_compare;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
return;
}
} else if (behavior & INTERSECT_ASSOC) {
intersect_key_compare_func = array_key_compare;
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL
&&
key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
if (argc < 2) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc;
intersect_key_compare_func = array_key_compare;
intersect_data_compare_func = array_data_compare;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER
&&
key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
intersect_key_compare_func = array_key_compare;
intersect_data_compare_func = array_user_compare;
} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL
&&
key_compare_type == INTERSECT_COMP_KEY_USER) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
intersect_key_compare_func = array_user_key_compare;
intersect_data_compare_func = array_data_compare;
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER
&&
key_compare_type == INTERSECT_COMP_KEY_USER) {
if (argc < 4) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 2;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
if (!zend_is_callable(*args[arr_argc + 1], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
intersect_key_compare_func = array_user_key_compare;
intersect_data_compare_func = array_user_compare;
BG(user_compare_func_name) = args[arr_argc + 1];
BG(user_compare_fci_cache).initialized = 0;
} else {
efree(args);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type);
return;
}
} else {
efree(args);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
return;
}
lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
set_compare_func(SORT_STRING TSRMLS_CC);
for (i = 0; i < arr_argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i+1);
arr_argc = i;
goto out;
}
hash = HASH_OF(*args[i]);
list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
if (!list) {
efree(args);
efree(lists);
efree(ptrs);
RETURN_FALSE;
}
lists[i] = list;
ptrs[i] = list;
for (p = hash->pListHead; p; p = p->pListNext) {
*list++ = p;
}
*list = NULL;
if (behavior == INTERSECT_NORMAL) {
zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC);
} else if (behavior & INTERSECT_ASSOC) {
zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC);
}
}
RETVAL_ZVAL(*args[0], 1, 0);
if (return_value->value.ht == &EG(symbol_table)) {
HashTable *ht;
zval *tmp;
ALLOC_HASHTABLE(ht);
zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
return_value->value.ht = ht;
}
if ((behavior & INTERSECT_NORMAL) && data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
}
while (*ptrs[0]) {
if ((behavior & INTERSECT_ASSOC)
&&
key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_func_name) = args[argc - 1];
BG(user_compare_fci_cache).initialized = 0;
}
for (i = 1; i < arr_argc; i++) {
if (behavior & INTERSECT_NORMAL) {
while (*ptrs[i] && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
ptrs[i]++;
}
} else if (behavior & INTERSECT_ASSOC) {
while (*ptrs[i] && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) {
ptrs[i]++;
}
if ((!c && *ptrs[i]) && (behavior == INTERSECT_ASSOC)) {
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
}
if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) {
c = 1;
if (key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_func_name) = args[argc - 1];
BG(user_compare_fci_cache).initialized = 0;
}
} else {
}
}
}
if (!*ptrs[i]) {
for (;;) {
p = *ptrs[0]++;
if (!p) {
goto out;
}
if (p->nKeyLength) {
zend_hash_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength);
} else {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
}
}
}
if (c)
break;
ptrs[i]++;
}
if (c) {
for (;;) {
p = *ptrs[0];
if (p->nKeyLength) {
zend_hash_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength);
} else {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
}
if (!*++ptrs[0]) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) {
break;
}
} else if (behavior & INTERSECT_ASSOC) {
break;
}
}
} else {
for (;;) {
if (!*++ptrs[0]) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (intersect_data_compare_func(ptrs[0]-1, ptrs[0] TSRMLS_CC)) {
break;
}
} else if (behavior & INTERSECT_ASSOC) {
break;
}
}
}
}
out:
for (i = 0; i < arr_argc; i++) {
hash = HASH_OF(*args[i]);
pefree(lists[i], hash->persistent);
}
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
efree(args);
}
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_uintersect_assoc)
{
php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
}
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_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)
{
Bucket *p;
int argc, i;
zval ***args;
int (*diff_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL;
zend_bool ok;
zval **data;
argc = ZEND_NUM_ARGS();
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (argc < 2 || zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
if (data_compare_type == DIFF_COMP_DATA_USER) {
char *callback_name;
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
argc--;
if (!zend_is_callable(*args[argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
diff_data_compare_func = zval_user_compare;
BG(user_compare_func_name) = args[argc];
BG(user_compare_fci_cache).initialized = 0;
} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
diff_data_compare_func = zval_compare;
}
for (i = 0; i < argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
RETVAL_NULL();
goto out;
}
}
array_init(return_value);
for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {
if (p->nKeyLength == 0) {
ok = 1;
for (i = 1; i < argc; i++) {
if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS &&
(!diff_data_compare_func ||
diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)) {
ok = 0;
break;
}
}
if (ok) {
(*((zval**)p->pData))->refcount++;
zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
}
} else {
ok = 1;
for (i = 1; i < argc; i++) {
if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS &&
(!diff_data_compare_func ||
diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)) {
ok = 0;
break;
}
}
if (ok) {
(*((zval**)p->pData))->refcount++;
zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
}
}
}
out:
efree(args);
}
static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type)
{
zval ***args = NULL;
HashTable *hash;
int argc, arr_argc, i, c;
Bucket ***lists, **list, ***ptrs, *p;
char *callback_name;
PHP_ARRAY_CMP_FUNC_VARS;
int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC);
int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC);
argc = ZEND_NUM_ARGS();
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
PHP_ARRAY_CMP_FUNC_BACKUP();
if (behavior == DIFF_NORMAL) {
diff_key_compare_func = array_key_compare;
if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
if (argc < 2) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc;
diff_data_compare_func = array_data_compare;
} else if (data_compare_type == DIFF_COMP_DATA_USER) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
diff_data_compare_func = array_user_compare;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
return;
}
} else if (behavior & DIFF_ASSOC) {
diff_key_compare_func = array_key_compare;
if (data_compare_type == DIFF_COMP_DATA_INTERNAL
&&
key_compare_type == DIFF_COMP_KEY_INTERNAL) {
if (argc < 2) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc;
diff_key_compare_func = array_key_compare;
diff_data_compare_func = array_data_compare;
} else if (data_compare_type == DIFF_COMP_DATA_USER
&&
key_compare_type == DIFF_COMP_KEY_INTERNAL) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
diff_key_compare_func = array_key_compare;
diff_data_compare_func = array_user_compare;
} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL
&&
key_compare_type == DIFF_COMP_KEY_USER) {
if (argc < 3) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 1;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
diff_key_compare_func = array_user_key_compare;
diff_data_compare_func = array_data_compare;
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
} else if (data_compare_type == DIFF_COMP_DATA_USER
&&
key_compare_type == DIFF_COMP_KEY_USER) {
if (argc < 4) {
efree(args);
WRONG_PARAM_COUNT;
}
arr_argc = argc - 2;
if (!zend_is_callable(*args[arr_argc], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
if (!zend_is_callable(*args[arr_argc + 1], 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback %s", callback_name);
efree(callback_name);
efree(args);
return;
}
efree(callback_name);
diff_key_compare_func = array_user_key_compare;
diff_data_compare_func = array_user_compare;
BG(user_compare_func_name) = args[arr_argc + 1];
BG(user_compare_fci_cache).initialized = 0;
} else {
efree(args);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type);
return;
}
} else {
efree(args);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
return;
}
lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0);
set_compare_func(SORT_STRING TSRMLS_CC);
for (i = 0; i < arr_argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
arr_argc = i;
goto out;
}
hash = HASH_OF(*args[i]);
list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent);
if (!list) {
efree(args);
efree(ptrs);
efree(lists);
RETURN_FALSE;
}
lists[i] = list;
ptrs[i] = list;
for (p = hash->pListHead; p; p = p->pListNext) {
*list++ = p;
}
*list = NULL;
if (behavior == DIFF_NORMAL) {
zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_data_compare_func TSRMLS_CC);
} else if (behavior & DIFF_ASSOC) {
zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_key_compare_func TSRMLS_CC);
}
}
RETVAL_ZVAL(*args[0], 1, 0);
if (return_value->value.ht == &EG(symbol_table)) {
HashTable *ht;
zval *tmp;
ALLOC_HASHTABLE(ht);
zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0);
zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
return_value->value.ht = ht;
}
if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
}
while (*ptrs[0]) {
if ((behavior & DIFF_ASSOC)
&&
key_compare_type == DIFF_COMP_KEY_USER) {
BG(user_compare_func_name) = args[argc - 1];
BG(user_compare_fci_cache).initialized = 0;
}
c = 1;
for (i = 1; i < arr_argc; i++) {
Bucket **ptr = ptrs[i];
if (behavior == DIFF_NORMAL) {
while (*ptr && (0 < (c = diff_data_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
ptr++;
}
} else if (behavior & DIFF_ASSOC) {
while (*ptr && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) {
ptr++;
}
}
if (!c) {
if (behavior == DIFF_NORMAL) {
if (*ptrs[i]) {
ptrs[i]++;
}
break;
} else if (behavior == DIFF_ASSOC) {
if (*ptr) {
if (data_compare_type == DIFF_COMP_DATA_USER) {
BG(user_compare_func_name) = args[arr_argc];
BG(user_compare_fci_cache).initialized = 0;
}
if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) {
c = -1;
if (key_compare_type == DIFF_COMP_KEY_USER) {
BG(user_compare_func_name) = args[argc - 1];
BG(user_compare_fci_cache).initialized = 0;
}
} else {
break;
}
}
} else if (behavior == DIFF_KEY) {
break;
}
}
}
if (!c) {
for (;;) {
p = *ptrs[0];
if (p->nKeyLength) {
zend_hash_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength);
} else {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
}
if (!*++ptrs[0]) {
goto out;
}
if (behavior == DIFF_NORMAL) {
if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) {
break;
}
} else if (behavior & DIFF_ASSOC) {
break;
}
}
} else {
for (;;) {
if (!*++ptrs[0]) {
goto out;
}
if (behavior == DIFF_NORMAL) {
if (diff_data_compare_func(ptrs[0]-1, ptrs[0] TSRMLS_CC)) {
break;
}
} else if (behavior & DIFF_ASSOC) {
break;
}
}
}
}
out:
for (i = 0; i < arr_argc; i++) {
hash = HASH_OF(*args[i]);
pefree(lists[i], hash->persistent);
}
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
efree(args);
}
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)
{
php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL,
DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
}
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
int multisort_compare(const void *a, const void *b TSRMLS_DC)
{
Bucket **ab = *(Bucket ***)a;
Bucket **bb = *(Bucket ***)b;
int r;
int result = 0;
zval temp;
r = 0;
do {
set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC);
ARRAYG(compare_func)(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData) TSRMLS_CC);
result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp);
if (result != 0) {
return result;
}
r++;
} while (ab[r] != NULL);
return result;
}
#define MULTISORT_ABORT \
for (k = 0; k < MULTISORT_LAST; k++) \
efree(ARRAYG(multisort_flags)[k]); \
efree(arrays); \
efree(args); \
RETURN_FALSE;
PHP_FUNCTION(array_multisort)
{
zval*** args;
zval*** arrays;
Bucket*** indirect;
Bucket* p;
HashTable* hash;
int argc;
int array_size;
int num_arrays = 0;
int parse_state[MULTISORT_LAST];
int sort_order = SORT_ASC;
int sort_type = SORT_REGULAR;
int i, k;
argc = ZEND_NUM_ARGS();
if (argc < 1) {
WRONG_PARAM_COUNT;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
WRONG_PARAM_COUNT;
}
arrays = (zval ***)ecalloc(argc, sizeof(zval **));
for (i = 0; i < MULTISORT_LAST; i++) {
parse_state[i] = 0;
ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
}
for (i = 0; i < argc; i++) {
if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
if (i > 0) {
ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays-1] = sort_order;
ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays-1] = sort_type;
sort_order = SORT_ASC;
sort_type = SORT_REGULAR;
}
arrays[num_arrays++] = args[i];
for (k = 0; k < MULTISORT_LAST; k++) {
parse_state[k] = 1;
}
} else if (Z_TYPE_PP(args[i]) == IS_LONG) {
switch (Z_LVAL_PP(args[i])) {
case SORT_ASC:
case SORT_DESC:
if (parse_state[MULTISORT_ORDER] == 1) {
sort_order = Z_LVAL_PP(args[i]) == SORT_DESC ? -1 : 1;
parse_state[MULTISORT_ORDER] = 0;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i+1);
MULTISORT_ABORT;
}
break;
case SORT_REGULAR:
case SORT_NUMERIC:
case SORT_STRING:
if (parse_state[MULTISORT_TYPE] == 1) {
sort_type = Z_LVAL_PP(args[i]);
parse_state[MULTISORT_TYPE] = 0;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
MULTISORT_ABORT;
}
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
MULTISORT_ABORT;
break;
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
MULTISORT_ABORT;
}
}
ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays-1] = sort_order;
ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays-1] = sort_type;
array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0]));
for (i = 0; i < num_arrays; i++) {
if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
MULTISORT_ABORT;
}
}
if (array_size < 1) {
for (k = 0; k < MULTISORT_LAST; k++)
efree(ARRAYG(multisort_flags)[k]);
efree(arrays);
efree(args);
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 (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) {
indirect[k][i] = p;
}
}
for (k = 0; k < array_size; k++)
indirect[k][num_arrays] = NULL;
zend_qsort(indirect, array_size, sizeof(Bucket **), multisort_compare TSRMLS_CC);
HANDLE_BLOCK_INTERRUPTIONS();
for (i = 0; i < num_arrays; i++) {
hash = Z_ARRVAL_PP(arrays[i]);
hash->pListHead = indirect[0][i];;
hash->pListTail = NULL;
hash->pInternalPointer = hash->pListHead;
for (k = 0; k < array_size; k++) {
if (hash->pListTail) {
hash->pListTail->pListNext = indirect[k][i];
}
indirect[k][i]->pListLast = hash->pListTail;
indirect[k][i]->pListNext = NULL;
hash->pListTail = indirect[k][i];
}
p = hash->pListHead;
k = 0;
while (p != NULL) {
if (p->nKeyLength == 0)
p->h = k++;
p = p->pListNext;
}
hash->nNextFreeElement = array_size;
zend_hash_rehash(hash);
}
HANDLE_UNBLOCK_INTERRUPTIONS();
for (i = 0; i < array_size; i++)
efree(indirect[i]);
efree(indirect);
for (k = 0; k < MULTISORT_LAST; k++)
efree(ARRAYG(multisort_flags)[k]);
efree(arrays);
efree(args);
RETURN_TRUE;
}
PHP_FUNCTION(array_rand)
{
zval **input, **num_req;
long randval;
int num_req_val, num_avail, key_type;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition pos;
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &input, &num_req) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument has to be an array");
return;
}
num_avail = zend_hash_num_elements(Z_ARRVAL_PP(input));
if (ZEND_NUM_ARGS() > 1) {
convert_to_long_ex(num_req);
num_req_val = Z_LVAL_PP(num_req);
if (num_req_val <= 0 || num_req_val > num_avail) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
return;
}
} else
num_req_val = 1;
if (num_req_val > 1) {
array_init(return_value);
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
while (num_req_val && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_PP(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
randval = php_rand(TSRMLS_C);
if ((double)(randval/(PHP_RAND_MAX+1.0)) < (double)num_req_val/(double)num_avail) {
if (Z_TYPE_P(return_value) != IS_ARRAY) {
if (key_type == HASH_KEY_IS_STRING) {
RETURN_STRINGL(string_key, string_key_len-1, 1);
} else {
RETURN_LONG(num_key);
}
} else {
if (key_type == HASH_KEY_IS_STRING)
add_next_index_stringl(return_value, string_key, string_key_len-1, 1);
else
add_next_index_long(return_value, num_key);
}
num_req_val--;
}
num_avail--;
zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos);
}
if (num_req_val == num_avail) {
array_data_shuffle(return_value TSRMLS_CC);
}
}
PHP_FUNCTION(array_sum)
{
zval **input,
**entry,
entry_n;
int argc = ZEND_NUM_ARGS();
HashPosition pos;
double dval;
if (argc != 1 || zend_get_parameters_ex(argc, &input) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
ZVAL_LONG(return_value, 0);
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos)) {
if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT)
continue;
entry_n = **entry;
zval_copy_ctor(&entry_n);
convert_scalar_to_number(&entry_n TSRMLS_CC);
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)LONG_MIN <= dval && dval <= (double)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);
}
}
PHP_FUNCTION(array_product)
{
zval **input,
**entry,
entry_n;
int argc = ZEND_NUM_ARGS();
HashPosition pos;
double dval;
if (argc != 1 || zend_get_parameters_ex(argc, &input) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an array");
return;
}
if (!zend_hash_num_elements(Z_ARRVAL_PP(input))) {
RETURN_LONG(0);
}
ZVAL_LONG(return_value, 1);
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos)) {
if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT)
continue;
entry_n = **entry;
zval_copy_ctor(&entry_n);
convert_scalar_to_number(&entry_n TSRMLS_CC);
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)LONG_MIN <= dval && dval <= (double)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);
}
}
PHP_FUNCTION(array_reduce)
{
zval **input, **callback, **initial;
zval **args[2];
zval **operand;
zval *result = NULL;
zval *retval;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
char *callback_name;
HashPosition pos;
HashTable *htbl;
if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &input, &callback, &initial) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be an array");
return;
}
if (!zend_is_callable(*callback, 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument, '%s', should be a valid callback", callback_name);
efree(callback_name);
return;
}
efree(callback_name);
if (ZEND_NUM_ARGS() > 2) {
ALLOC_ZVAL(result);
*result = **initial;
zval_copy_ctor(result);
convert_to_long(result);
INIT_PZVAL(result);
} else {
MAKE_STD_ZVAL(result);
ZVAL_NULL(result);
}
htbl = Z_ARRVAL_PP(input);
if (zend_hash_num_elements(htbl) == 0) {
if (result) {
RETVAL_ZVAL(result, 1, 1);
}
return;
}
zend_hash_internal_pointer_reset_ex(htbl, &pos);
while (zend_hash_get_current_data_ex(htbl, (void **)&operand, &pos) == SUCCESS) {
if (result) {
zend_fcall_info fci;
args[0] = &result;
args[1] = operand;
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = *callback;
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &retval;
fci.param_count = 2;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
zval_ptr_dtor(&result);
result = retval;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback");
return;
}
} else {
result = *operand;
zval_add_ref(&result);
}
zend_hash_move_forward_ex(htbl, &pos);
}
RETVAL_ZVAL(result, 1, 1);
}
PHP_FUNCTION(array_filter)
{
zval **input, **callback = NULL;
zval *array, *func = NULL;
zval **operand;
zval **args[1];
zval *retval = NULL;
char *callback_name;
char *string_key;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
uint string_key_len;
ulong num_key;
HashPosition pos;
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &input, &callback) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(input) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be an array");
return;
}
array = *input;
if (ZEND_NUM_ARGS() > 1) {
func = *callback;
if (!zend_is_callable(func, 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument, '%s', should be a valid callback", callback_name);
efree(callback_name);
return;
}
efree(callback_name);
}
array_init(return_value);
if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
return;
}
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&operand, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)) {
if (func) {
zend_fcall_info fci;
args[0] = operand;
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = func;
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &retval;
fci.param_count = 1;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
if (!zend_is_true(retval)) {
zval_ptr_dtor(&retval);
continue;
} else {
zval_ptr_dtor(&retval);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
return;
}
} else if (!zend_is_true(*operand)) {
continue;
}
zval_add_ref(operand);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_STRING:
zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand, sizeof(zval *), NULL);
break;
}
}
}
PHP_FUNCTION(array_map)
{
zval ***pargs = NULL;
zval ***params;
zval *callback;
zval *result, *null;
HashPosition *array_pos;
zval **args;
char *callback_name;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
int i, k, maxlen = 0;
int *array_len;
if (ZEND_NUM_ARGS() < 2) {
WRONG_PARAM_COUNT;
}
RETVAL_NULL();
pargs = (zval ***)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval **), 0);
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), pargs) == FAILURE) {
efree(pargs);
WRONG_PARAM_COUNT;
}
callback = *pargs[0];
if (Z_TYPE_P(callback) != IS_NULL) {
if (!zend_is_callable(callback, 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument, '%s', should be either NULL or a valid callback", callback_name);
efree(callback_name);
efree(pargs);
return;
}
efree(callback_name);
}
args = (zval **)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval *), 0);
array_len = (int *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(int), 0);
array_pos = (HashPosition *)safe_emalloc(ZEND_NUM_ARGS(), sizeof(HashPosition), 0);
for (i = 1; i < ZEND_NUM_ARGS(); i++) {
if (Z_TYPE_PP(pargs[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 1);
efree(pargs);
efree(args);
efree(array_len);
efree(array_pos);
return;
}
SEPARATE_ZVAL_IF_NOT_REF(pargs[i]);
args[i] = *pargs[i];
array_len[i] = zend_hash_num_elements(Z_ARRVAL_PP(pargs[i]));
if (array_len[i] > maxlen) {
maxlen = array_len[i];
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(pargs[i]), &array_pos[i]);
}
efree(pargs);
if (Z_TYPE_P(callback) == IS_NULL && ZEND_NUM_ARGS() == 2) {
RETVAL_ZVAL(args[1], 1, 0);
efree(array_len);
efree(array_pos);
efree(args);
return;
}
array_init(return_value);
params = (zval ***)safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval **), 0);
MAKE_STD_ZVAL(null);
ZVAL_NULL(null);
for (k = 0; k < maxlen; k++) {
uint str_key_len;
ulong num_key;
char *str_key;
int key_type = 0;
if (Z_TYPE_P(callback) == IS_NULL) {
MAKE_STD_ZVAL(result);
array_init(result);
}
for (i = 1; i < ZEND_NUM_ARGS(); i++) {
if (k < array_len[i]) {
zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), (void **)¶ms[i], &array_pos[i]);
if (ZEND_NUM_ARGS() == 2) {
key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(args[1]), &str_key, &str_key_len, &num_key, 0, &array_pos[i]);
}
zend_hash_move_forward_ex(Z_ARRVAL_P(args[i]), &array_pos[i]);
} else {
params[i] = &null;
}
if (Z_TYPE_P(callback) == IS_NULL) {
zval_add_ref(params[i]);
add_next_index_zval(result, *params[i]);
}
}
if (Z_TYPE_P(callback) != IS_NULL) {
zend_fcall_info fci;
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = callback;
fci.symbol_table = NULL;
fci.object_pp = NULL;
fci.retval_ptr_ptr = &result;
fci.param_count = ZEND_NUM_ARGS()-1;
fci.params = ¶ms[1];
fci.no_separation = 0;
if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || !result) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback");
efree(array_len);
efree(args);
efree(array_pos);
zval_dtor(return_value);
zval_ptr_dtor(&null);
efree(params);
RETURN_NULL();
}
}
if (ZEND_NUM_ARGS() > 2) {
add_next_index_zval(return_value, result);
} else {
if (key_type == HASH_KEY_IS_STRING) {
add_assoc_zval_ex(return_value, str_key, str_key_len, result);
} else {
add_index_zval(return_value, num_key, result);
}
}
}
zval_ptr_dtor(&null);
efree(params);
efree(array_len);
efree(array_pos);
efree(args);
}
PHP_FUNCTION(array_key_exists)
{
zval **key,
**array;
if (ZEND_NUM_ARGS() != 2 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &key, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(array) != IS_ARRAY && Z_TYPE_PP(array) != IS_OBJECT) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument should be either an array or an object");
RETURN_FALSE;
}
switch (Z_TYPE_PP(key)) {
case IS_STRING:
if (zend_symtable_exists(HASH_OF(*array), Z_STRVAL_PP(key), Z_STRLEN_PP(key)+1)) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_LONG:
if (zend_hash_index_exists(HASH_OF(*array), Z_LVAL_PP(key))) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_NULL:
if (zend_hash_exists(HASH_OF(*array), "", 1)) {
RETURN_TRUE;
}
RETURN_FALSE;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
RETURN_FALSE;
}
}
PHP_FUNCTION(array_chunk)
{
int argc = ZEND_NUM_ARGS(), key_type;
long size, current = 0;
char *str_key;
uint str_key_len;
ulong num_key;
zend_bool preserve_keys = 0;
zval *input = NULL;
zval *chunk = NULL;
zval **entry;
HashPosition pos;
if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) {
return;
}
if (size < 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0");
return;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&entry, &pos) == SUCCESS) {
if (!chunk) {
MAKE_STD_ZVAL(chunk);
array_init(chunk);
}
zval_add_ref(entry);
if (preserve_keys) {
key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &str_key,
&str_key_len, &num_key, 0, &pos);
if (key_type == HASH_KEY_IS_STRING) {
add_assoc_zval_ex(chunk, str_key, str_key_len, *entry);
} else {
add_index_zval(chunk, num_key, *entry);
}
} else {
add_next_index_zval(chunk, *entry);
}
if (!(++current % size)) {
add_next_index_zval(return_value, chunk);
chunk = NULL;
}
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
}
if (chunk) {
add_next_index_zval(return_value, chunk);
}
}
PHP_FUNCTION(array_combine)
{
zval *values, *keys;
HashPosition pos_values, pos_keys;
zval **entry_keys, **entry_values;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) {
return;
}
if (zend_hash_num_elements(Z_ARRVAL_P(keys)) != zend_hash_num_elements(Z_ARRVAL_P(values))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements");
RETURN_FALSE;
}
if (!zend_hash_num_elements(Z_ARRVAL_P(keys))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have at least 1 element");
RETURN_FALSE;
}
array_init(return_value);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos_keys);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry_keys, &pos_keys) == SUCCESS &&
zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&entry_values, &pos_values) == SUCCESS) {
if (Z_TYPE_PP(entry_keys) == IS_STRING) {
zval_add_ref(entry_values);
add_assoc_zval_ex(return_value, Z_STRVAL_PP(entry_keys), Z_STRLEN_PP(entry_keys)+1, *entry_values);
} else if (Z_TYPE_PP(entry_keys) == IS_LONG) {
zval_add_ref(entry_values);
add_index_zval(return_value, Z_LVAL_PP(entry_keys), *entry_values);
} else {
zval key;
key = **entry_keys;
zval_copy_ctor(&key);
convert_to_string(&key);
zval_add_ref(entry_values);
add_assoc_zval_ex(return_value, Z_STRVAL(key), Z_STRLEN(key)+1, *entry_values);
zval_dtor(&key);
}
zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos_keys);
zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
}
}