Skip to content

add ability to use array keys with array_filter() #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 30, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ PHP_MINIT_FUNCTION(array) /* {{{ */
REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);

REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);

return SUCCESS;
}
/* }}} */
Expand Down Expand Up @@ -4194,17 +4197,19 @@ PHP_FUNCTION(array_filter)
{
zval *array;
zval **operand;
zval **args[1];
zval **args[2];
zval *retval = NULL;
zval *key = NULL;
zend_bool have_callback = 0;
long use_type = 0;
char *string_key;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
uint string_key_len;
ulong num_key;
HashPosition pos;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|f", &array, &fci, &fci_cache) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
return;
}

Expand All @@ -4217,23 +4222,54 @@ PHP_FUNCTION(array_filter)
have_callback = 1;
fci.no_separation = 0;
fci.retval_ptr_ptr = &retval;
fci.param_count = 1;

if (use_type == ARRAY_FILTER_USE_BOTH) {
fci.param_count = 2;
args[1] = &key;
} else {
fci.param_count = 1;
if (use_type == ARRAY_FILTER_USE_KEY) {
args[0] = &key;
}
}
}

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)
) {
int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos);

if (have_callback) {
args[0] = operand;
if (use_type) {
MAKE_STD_ZVAL(key);
/* Set up the key */
switch (key_type) {
case HASH_KEY_IS_LONG:
Z_TYPE_P(key) = IS_LONG;
Z_LVAL_P(key) = num_key;
break;

case HASH_KEY_IS_STRING:
ZVAL_STRINGL(key, string_key, string_key_len - 1, 1);
break;
}
}

if (use_type != ARRAY_FILTER_USE_KEY) {
args[0] = operand;
}
fci.params = args;

if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
if (!zend_is_true(retval)) {
zval_ptr_dtor(&retval);
int retval_true = zend_is_true(retval);

zval_ptr_dtor(&retval);
if (use_type) {
zval_ptr_dtor(&key);
}
if (!retval_true) {
continue;
} else {
zval_ptr_dtor(&retval);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
Expand All @@ -4244,7 +4280,7 @@ PHP_FUNCTION(array_filter)
}

zval_add_ref(operand);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos)) {
switch (key_type) {
case HASH_KEY_IS_STRING:
zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
break;
Expand Down
1 change: 1 addition & 0 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_array_filter, 0, 0, 1)
ZEND_ARG_INFO(0, arg) /* ARRAY_INFO(0, arg, 0) */
ZEND_ARG_INFO(0, callback)
ZEND_ARG_INFO(0, use_keys)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_array_map, 0, 0, 2)
Expand Down
3 changes: 3 additions & 0 deletions ext/standard/php_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC);
#define PHP_SORT_NATURAL 6
#define PHP_SORT_FLAG_CASE 8

#define ARRAY_FILTER_USE_BOTH 1
#define ARRAY_FILTER_USE_KEY 2

ZEND_BEGIN_MODULE_GLOBALS(array)
int *multisort_flags[2];
int (*compare_func)(zval *result, zval *op1, zval *op2 TSRMLS_DC);
Expand Down
4 changes: 2 additions & 2 deletions ext/standard/tests/array/array_filter_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ $extra_arg = 10;

// with one more than the expected number of arguments
echo "-- Testing array_filter() function with more than expected no. of arguments --";
var_dump( array_filter($input, "odd", $extra_arg) );
var_dump( array_filter($input, "odd", $extra_arg, $extra_arg) );

// with incorrect callback function
echo "-- Testing array_filter() function with incorrect callback --";
Expand All @@ -42,7 +42,7 @@ echo "Done"
Warning: array_filter() expects at least 1 parameter, 0 given in %s on line %d
NULL
-- Testing array_filter() function with more than expected no. of arguments --
Warning: array_filter() expects at most 2 parameters, 3 given in %s on line %d
Warning: array_filter() expects at most 3 parameters, 4 given in %s on line %d
NULL
-- Testing array_filter() function with incorrect callback --
Warning: array_filter() expects parameter 2 to be a valid callback, function 'even' not found or invalid function name in %s on line %d
Expand Down
103 changes: 103 additions & 0 deletions ext/standard/tests/array/array_filter_variation10.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
--TEST--
Test array_filter() function : usage variations - using the array keys inside 'callback'
--FILE--
<?php
/* Prototype : array array_filter(array $input [, callback $callback [, bool $use_type = ARRAY_FILTER_USE_VALUE]])
* Description: Filters elements from the array via the callback.
* Source code: ext/standard/array.c
*/

/*
* Using array keys as an argument to the 'callback'
*/

echo "*** Testing array_filter() : usage variations - using array keys in 'callback' ***\n";

$input = array(0, 1, -1, 10, 100, 1000, 'Hello', null);
$small = array(123);

function dump($value, $key)
{
echo "$key = $value\n";
}

var_dump( array_filter($input, 'dump', true) );

echo "*** Testing array_filter() : usage variations - 'callback' filters based on key value ***\n";

function dump2($value, $key)
{
return $key > 4;
}

var_dump( array_filter($input, 'dump2', true) );

echo "*** Testing array_filter() : usage variations - 'callback' expecting second argument ***\n";

var_dump( array_filter($small, 'dump', false) );

echo "*** Testing array_filter() with various use types ***\n";

$mixed = array(1 => 'a', 2 => 'b', 'a' => 1, 'b' => 2);

var_dump(array_filter($mixed, 'is_numeric', ARRAY_FILTER_USE_KEY));

var_dump(array_filter($mixed, 'is_numeric', 0));

var_dump(array_filter($mixed, 'is_numeric', ARRAY_FILTER_USE_BOTH));

echo "Done"
?>
--EXPECTF--
*** Testing array_filter() : usage variations - using array keys in 'callback' ***
0 = 0
1 = 1
2 = -1
3 = 10
4 = 100
5 = 1000
6 = Hello
7 =
array(0) {
}
*** Testing array_filter() : usage variations - 'callback' filters based on key value ***
array(3) {
[5]=>
int(1000)
[6]=>
string(5) "Hello"
[7]=>
NULL
}
*** Testing array_filter() : usage variations - 'callback' expecting second argument ***

Warning: Missing argument 2 for dump() in %s on line %d

Notice: Undefined variable: key in %s on line %d
= 123
array(0) {
}
*** Testing array_filter() with various use types ***
array(2) {
[1]=>
string(1) "a"
[2]=>
string(1) "b"
}
array(2) {
["a"]=>
int(1)
["b"]=>
int(2)
}

Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44

Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44

Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44

Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
array(0) {
}
Done