1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 08:12:21 +01:00

Don't allow separation in array functions

The only case here that might be *somewhat* sensible is the userdata
argument of array_walk(), which could be used to keep persistent state
between callback invokations -- with the WTF moment that the final
result after the walk finishes will be unchanged. Nowdays, this is
much better achieved using a closure with a use-by-reference.
This commit is contained in:
Nikita Popov
2020-07-07 09:08:56 +02:00
parent df8119d3e1
commit dadb92ea35
11 changed files with 74 additions and 146 deletions

View File

@@ -921,7 +921,7 @@ static inline int php_array_user_compare_unstable(Bucket *f, Bucket *s) /* {{{ *
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
BG(user_compare_fci).no_separation = 1;
call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
@@ -1063,7 +1063,7 @@ static inline int php_array_user_key_compare_unstable(Bucket *f, Bucket *s) /* {
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
BG(user_compare_fci).no_separation = 1;
call_failed = zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE || Z_TYPE(retval) == IS_UNDEF;
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[0]);
@@ -1374,7 +1374,7 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
BG(array_walk_fci).retval = &retval;
BG(array_walk_fci).param_count = userdata ? 3 : 2;
BG(array_walk_fci).params = args;
BG(array_walk_fci).no_separation = 0;
BG(array_walk_fci).no_separation = 1;
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
ht_iter = zend_hash_iterator_add(target_hash, pos);
@@ -4546,7 +4546,7 @@ static int zval_user_compare(zval *a, zval *b) /* {{{ */
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
BG(user_compare_fci).no_separation = 1;
if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
zend_long ret = zval_get_long(&retval);
@@ -5906,7 +5906,7 @@ PHP_FUNCTION(array_reduce)
fci.retval = &retval;
fci.param_count = 2;
fci.no_separation = 0;
fci.no_separation = 1;
ZEND_HASH_FOREACH_VAL(htbl, operand) {
ZVAL_COPY_VALUE(&args[0], return_value);
@@ -5959,7 +5959,7 @@ PHP_FUNCTION(array_filter)
if (ZEND_FCI_INITIALIZED(fci)) {
have_callback = 1;
fci.no_separation = 0;
fci.no_separation = 1;
fci.retval = &retval;
if (use_type == ARRAY_FILTER_USE_BOTH) {
fci.param_count = 2;
@@ -6062,7 +6062,7 @@ PHP_FUNCTION(array_map)
fci.retval = &result;
fci.param_count = 1;
fci.params = &arg;
fci.no_separation = 0;
fci.no_separation = 1;
ZVAL_COPY(&arg, zv);
ret = zend_call_function(&fci, &fci_cache);
@@ -6151,7 +6151,7 @@ PHP_FUNCTION(array_map)
fci.retval = &result;
fci.param_count = n_arrays;
fci.params = params;
fci.no_separation = 0;
fci.no_separation = 1;
if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
efree(array_pos);

View File

@@ -14,10 +14,6 @@ $input = array(0, 1, -1, 10, 100, 1000, 'Hello', null);
echo "Anonymous callback function with regular parameter and statement\n";
var_dump( array_filter($input, function($input) { return ($input > 1); }) );
// anonymous callback function with reference
echo "Anonymous callback function with reference parameter\n";
var_dump( array_filter($input, function(&$input) { return ($input < 1); }) );
// anonymous callback function with null argument
echo "Anonymous callback function with null argument\n";
var_dump( array_filter($input, function() { return true; }) );
@@ -39,17 +35,6 @@ array(3) {
[5]=>
int(1000)
}
Anonymous callback function with reference parameter
array(4) {
[0]=>
int(0)
[2]=>
int(-1)
[6]=>
string(5) "Hello"
[7]=>
NULL
}
Anonymous callback function with null argument
array(8) {
[0]=>

View File

@@ -8,9 +8,7 @@ $arr = array("k1" => "v1","k2"=>"v2");
$arr[]=&$arr["k1"];
$arr[]=&$arr;
function cb1 ($a) {var_dump ($a);return array ($a);};
function cb2 (&$a) {var_dump ($a);return array (&$a);};
var_dump( array_map("cb1", $arr));
var_dump( array_map("cb2", $arr,$arr));
var_dump( array_map(null, $arr));
var_dump( array_map(null, $arr, $arr));
@@ -66,50 +64,6 @@ array(4) {
}
}
}
string(2) "v1"
string(2) "v2"
string(2) "v1"
array(4) {
["k1"]=>
&string(2) "v1"
["k2"]=>
string(2) "v2"
[0]=>
&string(2) "v1"
[1]=>
*RECURSION*
}
array(4) {
[0]=>
array(1) {
[0]=>
&string(2) "v1"
}
[1]=>
array(1) {
[0]=>
string(2) "v2"
}
[2]=>
array(1) {
[0]=>
&string(2) "v1"
}
[3]=>
array(1) {
[0]=>
&array(4) {
["k1"]=>
&string(2) "v1"
["k2"]=>
string(2) "v2"
[0]=>
&string(2) "v1"
[1]=>
*RECURSION*
}
}
}
array(4) {
["k1"]=>
&string(2) "v1"

View File

@@ -15,5 +15,8 @@ uksort($arr, "array_compare");
var_dump($a);
?>
--EXPECT--
--EXPECTF--
Warning: array_compare(): Argument #1 ($key1) must be passed by reference, value given in %s on line %d
Warning: array_compare(): Argument #2 ($key2) must be passed by reference, value given in %s on line %d
string(1) "B"

View File

@@ -107,17 +107,23 @@ array(2) {
bool(true)
closure with array
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
array(1) {
["sum"]=>
int(42)
}
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
array(1) {
["sum"]=>
int(43)
int(42)
}
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
array(1) {
["sum"]=>
int(45)
int(42)
}
bool(true)
End result:int(42)
@@ -139,14 +145,20 @@ bool(true)
End result:int(48)
closure with object
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
object(stdClass)#1 (1) {
["sum"]=>
int(42)
}
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
object(stdClass)#1 (1) {
["sum"]=>
int(43)
}
Warning: {closure}(): Argument #3 ($udata) must be passed by reference, value given in %s on line %d
object(stdClass)#1 (1) {
["sum"]=>
int(45)

View File

@@ -6,8 +6,8 @@ class p {
public $x;
function __construct($x){$this->x=$x;}
}
function a(&$a, &$b){var_dump(__FUNCTION__);return $a->x - $b->x;}
function b(&$a, &$b){var_dump(__FUNCTION__);return $a->x - $b->x;}
function a($a, $b){var_dump(__FUNCTION__);return $a->x - $b->x;}
function b($a, $b){var_dump(__FUNCTION__);return $a->x - $b->x;}
$p1 = array(new p(2), new p(1), new p(0));
$p2 = array(new p(0), new p(2), new p(3));

View File

@@ -34,6 +34,14 @@ echo "Done\n";
?>
--EXPECTF--
Notice: Only variables should be passed by reference in %s on line %d
Warning: test(): Argument #3 ($columns) must be passed by reference, value given in %s on line %d
Warning: test(): Argument #3 ($columns) must be passed by reference, value given in %s on line %d
Warning: test(): Argument #3 ($columns) must be passed by reference, value given in %s on line %d
Warning: test(): Argument #3 ($columns) must be passed by reference, value given in %s on line %d
object(Test)#%d (4) {
["_table"]=>
string(0) ""

View File

@@ -11,5 +11,8 @@ array_walk_recursive(
);
echo "Done";
?>
--EXPECT--
--EXPECTF--
Warning: {closure}(): Argument #3 ($userdata) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #3 ($userdata) must be passed by reference, value given in %s on line %d
Done

View File

@@ -34,7 +34,7 @@ var_dump($array_arg);
echo "Done"
?>
--EXPECT--
--EXPECTF--
*** Testing uasort() : anonymous function as 'cmp_function' ***
-- Anonymous 'cmp_function' with parameters passed by value --
bool(true)
@@ -51,6 +51,22 @@ array(5) {
int(100)
}
-- Anonymous 'cmp_function' with parameters passed by reference --
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
bool(true)
array(4) {
["a"]=>

View File

@@ -1,69 +0,0 @@
--TEST--
Test uasort() function : usage variations - 'cmp_function' with reference argument
--FILE--
<?php
/* Testing uasort() functionality with comparison function having arguments as reference
*/
echo "*** Testing uasort() : 'cmp_function' with reference arguments ***\n";
// comparison function
function cmp(&$value1, &$value2)
{
if($value1 == $value2) {
return 0;
}
else if($value1 > $value2) {
return 1;
}
else
return -1;
}
// Int array with default keys
$int_values = array(1, 8, 9, 3, 2, 6, 7);
echo "-- Passing integer values to 'cmp_function' --\n";
var_dump( uasort($int_values, 'cmp') );
var_dump($int_values);
// String array with default keys
$string_values = array("Mango", "Apple", "Orange", "Banana");
echo "-- Passing string values to 'cmp_function' --\n";
var_dump( uasort($string_values, 'cmp') );
var_dump($string_values);
echo "Done"
?>
--EXPECT--
*** Testing uasort() : 'cmp_function' with reference arguments ***
-- Passing integer values to 'cmp_function' --
bool(true)
array(7) {
[0]=>
int(1)
[4]=>
int(2)
[3]=>
int(3)
[5]=>
int(6)
[6]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
}
-- Passing string values to 'cmp_function' --
bool(true)
array(4) {
[1]=>
string(5) "Apple"
[3]=>
string(6) "Banana"
[0]=>
string(5) "Mango"
[2]=>
string(6) "Orange"
}
Done

View File

@@ -32,7 +32,7 @@ echo "\n-- Anonymous 'cmp_function' with parameters passed by reference --\n";
var_dump( usort($array_arg, $cmp_function) );
var_dump($array_arg);
?>
--EXPECT--
--EXPECTF--
*** Testing usort() : usage variation ***
-- Anonymous 'cmp_function' with parameters passed by value --
@@ -51,6 +51,22 @@ array(5) {
}
-- Anonymous 'cmp_function' with parameters passed by reference --
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #1 ($value1) must be passed by reference, value given in %s on line %d
Warning: {closure}(): Argument #2 ($value2) must be passed by reference, value given in %s on line %d
bool(true)
array(4) {
[0]=>