Skip to content

Commit

Permalink
Allow array_diff() and array_intersect() with single array argument
Browse files Browse the repository at this point in the history
Both of these functions are well-defined when used with a single
array argument -- rejecting this case was an artificial limitation.
This is not useful when called with explicit arguments, but removes
edge-cases when used with argument unpacking:

    // OK even if $excludes is empty.
    array_diff($array, ...$excludes);

    // OK even if $arrays contains a single array only.
    array_intersect(...$arrays);

This matches the behavior of functions like array_merge() and
array_push(), which also allow calls with no array or a single
array respectively.

Closes GH-6097.
  • Loading branch information
nikic committed Sep 9, 2020
1 parent 9975986 commit 73ab7b3
Show file tree
Hide file tree
Showing 46 changed files with 1,251 additions and 1,460 deletions.
8 changes: 8 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,14 @@ PHP 8.0 UPGRADE NOTES
. Sorting functions are now stable, which means that equal-comparing elements
will retain their original order.
RFC: https://wiki.php.net/rfc/stable_sorting
. array_diff(), array_intersect() and their variations can now be used with
a single array as argument. This means that usages like the following are
now possible:

// OK even if $excludes is empty.
array_diff($array, ...$excludes);
// OK even if $arrays only contains a single array.
array_intersect(...$arrays);

- Zip:
. Extension updated to version 1.19.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ try {
}

try {
array_diff([]);
array_diff();
} catch (ArgumentCountError $e) {
echo get_class($e) . PHP_EOL;
echo $e->getMessage(), "\n";
Expand All @@ -21,4 +21,4 @@ try {
ArgumentCountError
substr() expects at least 2 arguments, 1 given
ArgumentCountError
At least 2 arguments are required, 1 given
array_diff() expects at least 1 argument, 0 given
55 changes: 0 additions & 55 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -4613,7 +4613,6 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa
int (*intersect_data_compare_func)(zval *, zval *) = NULL;
zend_bool ok;
zval *val, *data;
int req_args;
char *param_spec;
zend_string *key;
zend_ulong h;
Expand All @@ -4622,25 +4621,18 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa
argc = ZEND_NUM_ARGS();
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
/* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
req_args = 3;
param_spec = "+f";
intersect_data_compare_func = zval_user_compare;
} else {
/* INTERSECT_COMP_DATA_NONE - array_intersect_key()
INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
req_args = 2;
param_spec = "+";

if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
intersect_data_compare_func = zval_compare;
}
}

if (argc < req_args) {
zend_argument_count_error("At least %d arguments are required, %d given", req_args, argc);
RETURN_THROWS();
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
RETURN_THROWS();
}
Expand Down Expand Up @@ -4701,7 +4693,6 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int
int arr_argc, i, c = 0;
uint32_t idx;
Bucket **lists, *list, **ptrs, *p;
uint32_t req_args;
char *param_spec;
zend_fcall_info fci1, fci2;
zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
Expand All @@ -4717,24 +4708,17 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int

if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
/* array_intersect() */
req_args = 2;
param_spec = "+";
intersect_data_compare_func = php_array_data_compare_string_unstable;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
/* array_uintersect() */
req_args = 3;
param_spec = "+f";
intersect_data_compare_func = php_array_user_compare_unstable;
} else {
ZEND_ASSERT(0 && "Invalid data_compare_type");
return;
}

if (ZEND_NUM_ARGS() < req_args) {
zend_argument_count_error("At least %d arguments are required, %d given", req_args, ZEND_NUM_ARGS());
RETURN_THROWS();
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
RETURN_THROWS();
}
Expand All @@ -4747,29 +4731,25 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int

if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
/* array_intersect_assoc() or array_intersect_key() */
req_args = 2;
param_spec = "+";
intersect_key_compare_func = php_array_key_compare_string_unstable;
intersect_data_compare_func = php_array_data_compare_string_unstable;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
/* array_uintersect_assoc() */
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_key_compare_string_unstable;
intersect_data_compare_func = php_array_user_compare_unstable;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
/* array_intersect_uassoc() or array_intersect_ukey() */
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_user_key_compare_unstable;
intersect_data_compare_func = php_array_data_compare_string_unstable;
fci_key = &fci1;
fci_key_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
/* array_uintersect_uassoc() */
req_args = 4;
param_spec = "+ff";
intersect_key_compare_func = php_array_user_key_compare_unstable;
intersect_data_compare_func = php_array_user_compare_unstable;
Expand All @@ -4782,11 +4762,6 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int
return;
}

if (ZEND_NUM_ARGS() < req_args) {
zend_argument_count_error("At least %d arguments are required, %d given", req_args, ZEND_NUM_ARGS());
RETURN_THROWS();
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
RETURN_THROWS();
}
Expand Down Expand Up @@ -5023,19 +4998,11 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty
/* Get the argument count */
argc = ZEND_NUM_ARGS();
if (data_compare_type == DIFF_COMP_DATA_USER) {
if (argc < 3) {
zend_argument_count_error("At least 3 arguments are required, %d given", ZEND_NUM_ARGS());
RETURN_THROWS();
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
RETURN_THROWS();
}
diff_data_compare_func = zval_user_compare;
} else {
if (argc < 2) {
zend_argument_count_error("At least 2 arguments are required, %d given", ZEND_NUM_ARGS());
RETURN_THROWS();
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
RETURN_THROWS();
}
Expand Down Expand Up @@ -5100,7 +5067,6 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_
int arr_argc, i, c;
uint32_t idx;
Bucket **lists, *list, **ptrs, *p;
uint32_t req_args;
char *param_spec;
zend_fcall_info fci1, fci2;
zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
Expand All @@ -5116,24 +5082,17 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_

if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
/* array_diff */
req_args = 2;
param_spec = "+";
diff_data_compare_func = php_array_data_compare_string_unstable;
} else if (data_compare_type == DIFF_COMP_DATA_USER) {
/* array_udiff */
req_args = 3;
param_spec = "+f";
diff_data_compare_func = php_array_user_compare_unstable;
} else {
ZEND_ASSERT(0 && "Invalid data_compare_type");
return;
}

if (ZEND_NUM_ARGS() < req_args) {
zend_argument_count_error("At least %d arguments are required, %d given", req_args, ZEND_NUM_ARGS());
RETURN_THROWS();
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
RETURN_THROWS();
}
Expand All @@ -5146,29 +5105,25 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_

if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
/* array_diff_assoc() or array_diff_key() */
req_args = 2;
param_spec = "+";
diff_key_compare_func = php_array_key_compare_string_unstable;
diff_data_compare_func = php_array_data_compare_string_unstable;
} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
/* array_udiff_assoc() */
req_args = 3;
param_spec = "+f";
diff_key_compare_func = php_array_key_compare_string_unstable;
diff_data_compare_func = php_array_user_compare_unstable;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
/* array_diff_uassoc() or array_diff_ukey() */
req_args = 3;
param_spec = "+f";
diff_key_compare_func = php_array_user_key_compare_unstable;
diff_data_compare_func = php_array_data_compare_string_unstable;
fci_key = &fci1;
fci_key_cache = &fci1_cache;
} else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
/* array_udiff_uassoc() */
req_args = 4;
param_spec = "+ff";
diff_key_compare_func = php_array_user_key_compare_unstable;
diff_data_compare_func = php_array_user_compare_unstable;
Expand All @@ -5181,11 +5136,6 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_
return;
}

if (ZEND_NUM_ARGS() < req_args) {
zend_argument_count_error("At least %d arguments are required, %d given", req_args, ZEND_NUM_ARGS());
RETURN_THROWS();
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
RETURN_THROWS();
}
Expand Down Expand Up @@ -5377,11 +5327,6 @@ PHP_FUNCTION(array_diff)
zend_long idx;
zval dummy;

if (ZEND_NUM_ARGS() < 2) {
zend_argument_count_error("At least 2 arguments are required, %d given", ZEND_NUM_ARGS());
RETURN_THROWS();
}

ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();
Expand Down
32 changes: 16 additions & 16 deletions ext/standard/basic_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,47 +178,47 @@ function array_change_key_case(array $array, int $case = CASE_LOWER): array {}

function array_unique(array $array, int $flags = SORT_STRING): array {}

function array_intersect_key(array $array1, array $array2, array ...$arrays): array {}
function array_intersect_key(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_intersect_ukey(array $array1, array $array2, ...$rest): array {}
function array_intersect_ukey(array $array, ...$rest): array {}

function array_intersect(array $array1, array $array2, array ...$arrays): array {}
function array_intersect(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_uintersect(array $array1, array $array2, ...$rest): array {}
function array_uintersect(array $array, ...$rest): array {}

function array_intersect_assoc(array $array1, array $array2, array ...$arrays): array {}
function array_intersect_assoc(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_uintersect_assoc(array $array1, array $array2, ...$rest): array {}
function array_uintersect_assoc(array $array, ...$rest): array {}

/** @param array|callable $rest */
function array_intersect_uassoc(array $array1, array $array2, ...$rest): array {}
function array_intersect_uassoc(array $array, ...$rest): array {}

/** @param array|callable $rest */
function array_uintersect_uassoc(array $array1, array $array2, ...$rest): array {}
function array_uintersect_uassoc(array $array, ...$rest): array {}

function array_diff_key(array $array1, array $array2, array ...$arrays): array {}
function array_diff_key(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_diff_ukey(array $array1, array $array2, ...$rest): array {}
function array_diff_ukey(array $array, ...$rest): array {}

function array_diff(array $array1, array $array2, array ...$arrays): array {}
function array_diff(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_udiff(array $array1, array $array2, ...$rest): array {}
function array_udiff(array $array, ...$rest): array {}

function array_diff_assoc(array $array1, array $array2, array ...$arrays): array {}
function array_diff_assoc(array $array, array ...$arrays): array {}

/** @param array|callable $rest */
function array_diff_uassoc(array $array1, array $array2, ...$rest): array {}
function array_diff_uassoc(array $array, ...$rest): array {}

/** @param array|callable $rest */
function array_udiff_assoc(array $array1, array $array2, ...$rest): array {}
function array_udiff_assoc(array $array, ...$rest): array {}

/** @param array|callable $rest */
function array_udiff_uassoc(array $array1, array $array2, ...$rest): array {}
function array_udiff_uassoc(array $array, ...$rest): array {}

/**
* @param array $array1
Expand Down
12 changes: 5 additions & 7 deletions ext/standard/basic_functions_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 1d2a7229aa506c8da54ecbed6480836aa14fd484 */
* Stub hash: 56f49d359d2b11383a3f1401d0a3730192c28fc0 */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
Expand Down Expand Up @@ -273,15 +273,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_unique, 0, 1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "SORT_STRING")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_intersect_key, 0, 2, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array2, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_intersect_key, 0, 1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
ZEND_ARG_VARIADIC_TYPE_INFO(0, arrays, IS_ARRAY, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_intersect_ukey, 0, 2, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array2, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_intersect_ukey, 0, 1, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
ZEND_ARG_VARIADIC_INFO(0, rest)
ZEND_END_ARG_INFO()

Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/array/array_diff_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ try {
echo "OK!";
?>
--EXPECT--
array_diff(): Argument #2 ($array2) must be of type array, int given
array_diff(): Argument #2 must be of type array, int given
OK!
38 changes: 0 additions & 38 deletions ext/standard/tests/array/array_diff_assoc_error.phpt

This file was deleted.

0 comments on commit 73ab7b3

Please sign in to comment.