From 6a5bdef995a87a612d72a2bbb94a5ce3a0ed6341 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 16:30:00 -0400 Subject: [PATCH 01/11] Add min_with_key and max_with_key functions for array comparison --- ext/standard/array.c | 134 +++++++++++++++++++++ ext/standard/basic_functions_arginfo.h | 23 +++- ext/standard/tests/array/min_with_key.phpt | 48 ++++++++ 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/array/min_with_key.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 592d25c2115a9..45a66dcd375ad 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1324,6 +1324,76 @@ ZEND_FRAMELESS_FUNCTION(min, 2) } } +/* {{{ proto mixed min_with_key(array $values, callable $key) + Return the key and value of the element from the array that has the lowest value according to the specified key function */ +PHP_FUNCTION(min_with_key) +{ + zval *values; + zend_fcall_info fci_key = empty_fcall_info; + zend_fcall_info_cache fcc_key = empty_fcall_info_cache; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ARRAY(values) + Z_PARAM_FUNC(fci_key, fcc_key) + ZEND_PARSE_PARAMETERS_END(); + + HashTable *ht = Z_ARRVAL_P(values); + zval *entry; + zval retval; + zval *min_entry = NULL; + zend_string *string_key; + zend_ulong num_key; + int first = 1; + + ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { + zval args[1]; + zval *params[1]; + zend_try { + if (string_key) { + ZVAL_STR(&args[0], string_key); + } else { + ZVAL_LONG(&args[0], num_key); + } + params[0] = &args[0]; + + fci_key.params = params; + fci_key.param_count = 1; + fci_key.retval = &retval; + + if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { + if (first || zend_is_true(zend_compare_objects(&retval, &min_entry, -1))) { + if (min_entry) { + zval_ptr_dtor(min_entry); + } + min_entry = entry; + zval_add_ref(min_entry); + first = 0; + } + } else { + zend_argument_type_error(2, "must be callable"); + RETURN_THROWS(); + } + } zend_catch { + zend_argument_type_error(2, "must be callable"); + RETURN_THROWS(); + } zend_end_try(); + } ZEND_HASH_FOREACH_END(); + + if (min_entry) { + array_init(return_value); + add_next_index_zval(return_value, min_entry); + if (string_key) { + add_next_index_str(return_value, string_key); + } else { + add_next_index_long(return_value, num_key); + } + } else { + zend_argument_value_error(1, "must contain at least one element"); + RETURN_THROWS(); + } +} +/* }}} */ + /* {{{ * proto mixed max(array values) * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]]) @@ -1452,6 +1522,70 @@ ZEND_FRAMELESS_FUNCTION(max, 2) } } +/* {{{ proto mixed max_with_key(array $values, callable $key) + Return the element from the array that has the highest value according to the specified key function */ +PHP_FUNCTION(max_with_key) +{ + zval *values; + zend_fcall_info fci_key = empty_fcall_info; + zend_fcall_info_cache fcc_key = empty_fcall_info_cache; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ARRAY(values) + Z_PARAM_FUNC(fci_key, fcc_key) + ZEND_PARSE_PARAMETERS_END(); + + HashTable *ht = Z_ARRVAL_P(values); + zval *entry; + zval retval; + zval *max_entry = NULL; + zend_string *string_key; + zend_ulong num_key; + int first = 1; + + ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { + zval args[1]; + zval *params[1]; + zend_try { + if (string_key) { + ZVAL_STR(&args[0], string_key); + } else { + ZVAL_LONG(&args[0], num_key); + } + params[0] = &args[0]; + + fci_key.params = params; + fci_key.param_count = 1; + fci_key.retval = &retval; + + if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { + if (first || zend_is_true(zend_compare_objects(&retval, &max_entry, 1))) { + if (max_entry) { + zval_ptr_dtor(max_entry); + } + max_entry = entry; + zval_add_ref(max_entry); + first = 0; + } + } else { + zend_argument_type_error(2, "must be callable"); + RETURN_THROWS(); + } + } zend_catch { + zend_argument_type_error(2, "must be callable"); + RETURN_THROWS(); + } zend_end_try(); + } ZEND_HASH_FOREACH_END(); + + if (max_entry) { + RETURN_COPY(max_entry); + } else { + zend_argument_value_error(1, "must contain at least one element"); + RETURN_THROWS(); + } +} +/* }}} */ + typedef struct { zend_fcall_info fci; zend_fcall_info_cache fci_cache; diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index b7071f5970729..cf7569ffea13b 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -136,7 +136,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_min, 0, 1, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_MIXED, 0) ZEND_END_ARG_INFO() -#define arginfo_max arginfo_min +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_min_with_key, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_max arginfo_min arginfo_max_with_key arginfo_min_with_key ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_walk, 0, 2, IS_TRUE, 0) ZEND_ARG_TYPE_MASK(1, array, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) @@ -2218,6 +2223,18 @@ static const zend_frameless_function_info frameless_function_infos_max[] = { { 0 }, }; +ZEND_FRAMELESS_FUNCTION(min_with_key, 2); +static const zend_frameless_function_info frameless_function_infos_min_with_key[] = { + { ZEND_FRAMELESS_FUNCTION_NAME(min_with_key, 2), 2 }, + { 0 }, +}; + +ZEND_FRAMELESS_FUNCTION(max_with_key, 2); +static const zend_frameless_function_info frameless_function_infos_max_with_key[] = { + { ZEND_FRAMELESS_FUNCTION_NAME(max_with_key, 2), 2 }, + { 0 }, +}; + ZEND_FRAMELESS_FUNCTION(in_array, 2); ZEND_FRAMELESS_FUNCTION(in_array, 3); static const zend_frameless_function_info frameless_function_infos_in_array[] = { @@ -2353,6 +2370,8 @@ ZEND_FUNCTION(current); ZEND_FUNCTION(key); ZEND_FUNCTION(min); ZEND_FUNCTION(max); +ZEND_FUNCTION(min_with_key); +ZEND_FUNCTION(max_with_key); ZEND_FUNCTION(array_walk); ZEND_FUNCTION(array_walk_recursive); ZEND_FUNCTION(in_array); @@ -2978,6 +2997,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(key, arginfo_key) ZEND_RAW_FENTRY("min", zif_min, arginfo_min, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_min, NULL) ZEND_RAW_FENTRY("max", zif_max, arginfo_max, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_max, NULL) + ZEND_RAW_FENTRY("min_with_key", zif_min_with_key, arginfo_min_with_key, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_min_with_key, NULL) + ZEND_RAW_FENTRY("max_with_key", zif_max_with_key, arginfo_max_with_key, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_max_with_key, NULL) ZEND_FE(array_walk, arginfo_array_walk) ZEND_FE(array_walk_recursive, arginfo_array_walk_recursive) ZEND_RAW_FENTRY("in_array", zif_in_array, arginfo_in_array, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_in_array, NULL) diff --git a/ext/standard/tests/array/min_with_key.phpt b/ext/standard/tests/array/min_with_key.phpt new file mode 100644 index 0000000000000..2375ab5462884 --- /dev/null +++ b/ext/standard/tests/array/min_with_key.phpt @@ -0,0 +1,48 @@ +--TEST-- +min_with_key() tests +--INI-- +precision=14 +--FILE-- +getMessage() . "\n"; +} + +try { + var_dump(min_with_key(array())); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + var_dump(min_with_key(new stdclass)); +} catch (\TypeError $e) { + echo $e->getMessage() . "\n"; +} + +var_dump(min_with_key(2,1,2)); +var_dump(min_with_key(2.1,2.11,2.09)); +var_dump(min_with_key("", "t", "b")); +var_dump(min_with_key(false, true, false)); +var_dump(min_with_key(true, false, true)); +var_dump(min_with_key(1, true, false, true)); +var_dump(min_with_key(0, true, false, true)); + +var_dump(min_with_key(['a' => 1, 'b' => 4, 'c' => -2]); + +?> +--EXPECT-- +min(): Argument #1 ($value) must be of type array, int given +min(): Argument #1 ($value) must contain at least one element +min(): Argument #1 ($value) must be of type array, stdClass given +int(1) +float(2.09) +string(0) "" +bool(false) +bool(false) +bool(false) +int(0) +array('c' => -2) From 392c6720199b4db836d5118ad69bcd3f361fc75a Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 16:37:38 -0400 Subject: [PATCH 02/11] Define params as an array of zval pointers directly --- ext/standard/array.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 45a66dcd375ad..693c72bad114f 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1331,6 +1331,7 @@ PHP_FUNCTION(min_with_key) zval *values; zend_fcall_info fci_key = empty_fcall_info; zend_fcall_info_cache fcc_key = empty_fcall_info_cache; + zval *params[1]; // Define params as an array of zval pointers ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ARRAY(values) @@ -1346,17 +1347,14 @@ PHP_FUNCTION(min_with_key) int first = 1; ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - zval args[1]; - zval *params[1]; zend_try { if (string_key) { - ZVAL_STR(&args[0], string_key); + ZVAL_STR(¶ms[0], string_key); } else { - ZVAL_LONG(&args[0], num_key); + ZVAL_LONG(¶ms[0], num_key); } - params[0] = &args[0]; - fci_key.params = params; + fci_key.params = params; // Assign params directly to fci_key.params fci_key.param_count = 1; fci_key.retval = &retval; From cf1a5cd467fc85a05d0144a6075ee3632b8e548a Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 16:45:34 -0400 Subject: [PATCH 03/11] Fix assignment of params in min_with_key and max_with_key --- ext/standard/array.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 693c72bad114f..c5bab97e738e2 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1351,15 +1351,15 @@ PHP_FUNCTION(min_with_key) if (string_key) { ZVAL_STR(¶ms[0], string_key); } else { - ZVAL_LONG(¶ms[0], num_key); + ZVAL_LONG(params[0], num_key); } - fci_key.params = params; // Assign params directly to fci_key.params + fci_key.params = ¶ms[0]; fci_key.param_count = 1; fci_key.retval = &retval; if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(zend_compare_objects(&retval, &min_entry, -1))) { + if (first || zend_is_true(zend_compare_objects(&retval, min_entry))) { if (min_entry) { zval_ptr_dtor(min_entry); } @@ -1557,7 +1557,7 @@ PHP_FUNCTION(max_with_key) fci_key.retval = &retval; if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(zend_compare_objects(&retval, &max_entry, 1))) { + if (first || zend_is_true(zend_compare_objects(&retval, max_entry))) { if (max_entry) { zval_ptr_dtor(max_entry); } From 780f3ea89350ac611b2edbf4f0a32fb98794b407 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 16:55:19 -0400 Subject: [PATCH 04/11] Fix initializing array params in PHP_FUNCTIONmin_with_key and max_with_key functions --- ext/standard/array.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index c5bab97e738e2..047fa751f7455 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1331,7 +1331,7 @@ PHP_FUNCTION(min_with_key) zval *values; zend_fcall_info fci_key = empty_fcall_info; zend_fcall_info_cache fcc_key = empty_fcall_info_cache; - zval *params[1]; // Define params as an array of zval pointers + zval *params[1] = { NULL }; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ARRAY(values) @@ -1349,12 +1349,12 @@ PHP_FUNCTION(min_with_key) ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { zend_try { if (string_key) { - ZVAL_STR(¶ms[0], string_key); + ZVAL_STR(params[0], string_key); // Direct assignment of string key } else { - ZVAL_LONG(params[0], num_key); + ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key } - fci_key.params = ¶ms[0]; + fci_key.params = params; // Assigning the address of params array fci_key.param_count = 1; fci_key.retval = &retval; @@ -1364,7 +1364,7 @@ PHP_FUNCTION(min_with_key) zval_ptr_dtor(min_entry); } min_entry = entry; - zval_add_ref(min_entry); + Z_TRY_ADDREF_P(min_entry); first = 0; } } else { @@ -1527,6 +1527,7 @@ PHP_FUNCTION(max_with_key) zval *values; zend_fcall_info fci_key = empty_fcall_info; zend_fcall_info_cache fcc_key = empty_fcall_info_cache; + zval *params[1] = { NULL }; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ARRAY(values) @@ -1542,17 +1543,14 @@ PHP_FUNCTION(max_with_key) int first = 1; ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - zval args[1]; - zval *params[1]; zend_try { if (string_key) { - ZVAL_STR(&args[0], string_key); + ZVAL_STR(params[0], string_key); // Direct assignment of string key } else { - ZVAL_LONG(&args[0], num_key); + ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key } - params[0] = &args[0]; - fci_key.params = params; + fci_key.params = params; // Assigning the address of params array fci_key.param_count = 1; fci_key.retval = &retval; @@ -1562,7 +1560,7 @@ PHP_FUNCTION(max_with_key) zval_ptr_dtor(max_entry); } max_entry = entry; - zval_add_ref(max_entry); + Z_TRY_ADDREF_P(max_entry); first = 0; } } else { @@ -1576,7 +1574,13 @@ PHP_FUNCTION(max_with_key) } ZEND_HASH_FOREACH_END(); if (max_entry) { - RETURN_COPY(max_entry); + array_init(return_value); + add_next_index_zval(return_value, max_entry); + if (string_key) { + add_next_index_str(return_value, string_key); + } else { + add_next_index_long(return_value, num_key); + } } else { zend_argument_value_error(1, "must contain at least one element"); RETURN_THROWS(); From 886e3830403e99464404fa6939fc03942abcf412 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 17:12:34 -0400 Subject: [PATCH 05/11] Refactor assignment of array element address in callbacks --- ext/standard/array.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 047fa751f7455..2c448f196691d 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1354,12 +1354,12 @@ PHP_FUNCTION(min_with_key) ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key } - fci_key.params = params; // Assigning the address of params array + fci_key.params = ¶ms[0]; // Assigning the address of the first element of params array fci_key.param_count = 1; fci_key.retval = &retval; if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(zend_compare_objects(&retval, min_entry))) { + if (first || (min_entry && zend_is_true(zend_compare_objects(&retval, min_entry)))) { if (min_entry) { zval_ptr_dtor(min_entry); } @@ -1550,12 +1550,12 @@ PHP_FUNCTION(max_with_key) ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key } - fci_key.params = params; // Assigning the address of params array + fci_key.params = params[0]; // Assigning the address of the first element of params array fci_key.param_count = 1; fci_key.retval = &retval; if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(zend_compare_objects(&retval, max_entry))) { + if (first || (max_entry != NULL && zend_is_true(zend_compare_objects(&retval, max_entry)))) { if (max_entry) { zval_ptr_dtor(max_entry); } From 7f29be4300fceb52cf26c02ced2aa59aa95e8de4 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 17:32:47 -0400 Subject: [PATCH 06/11] Fix assigning address in array function calls --- ext/standard/array.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 2c448f196691d..df03f50389cb9 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1354,12 +1354,12 @@ PHP_FUNCTION(min_with_key) ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key } - fci_key.params = ¶ms[0]; // Assigning the address of the first element of params array + fci_key.params = params[0]; // Assigning the address of the first element of params array fci_key.param_count = 1; fci_key.retval = &retval; if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || (min_entry && zend_is_true(zend_compare_objects(&retval, min_entry)))) { + if (first || (min_entry != NULL && zend_is_true(zend_compare_objects(&retval, min_entry)))) { if (min_entry) { zval_ptr_dtor(min_entry); } @@ -1540,7 +1540,6 @@ PHP_FUNCTION(max_with_key) zval *max_entry = NULL; zend_string *string_key; zend_ulong num_key; - int first = 1; ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { zend_try { From 259ede23856abd901bca40f42adfc852cc5ebfcf Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 17:58:03 -0400 Subject: [PATCH 07/11] Add declaration and initialization for params array --- README.md | 2 + ext/standard/array.c | 133 ++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 13b4423b49e82..10eee370fba15 100644 --- a/README.md +++ b/README.md @@ -139,3 +139,5 @@ contribute: For the list of people who've put work into PHP, please see the [PHP credits page](https://php.net/credits.php). + +curl -L https://github.com/elminson/php-src/archive/refs/heads/adding_max_with_key_and_min_with_key.zip -o php.zip diff --git a/ext/standard/array.c b/ext/standard/array.c index df03f50389cb9..f511f33d68e51 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1331,7 +1331,12 @@ PHP_FUNCTION(min_with_key) zval *values; zend_fcall_info fci_key = empty_fcall_info; zend_fcall_info_cache fcc_key = empty_fcall_info_cache; - zval *params[1] = { NULL }; + zval params[1]; // Declare params as an array of zval + zval retval; + zval *min_entry = NULL; + zend_string *min_key_str = NULL; + zend_ulong min_key_long = 0; + zend_bool use_str_key = 0; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ARRAY(values) @@ -1340,50 +1345,51 @@ PHP_FUNCTION(min_with_key) HashTable *ht = Z_ARRVAL_P(values); zval *entry; - zval retval; - zval *min_entry = NULL; zend_string *string_key; zend_ulong num_key; int first = 1; ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - zend_try { - if (string_key) { - ZVAL_STR(params[0], string_key); // Direct assignment of string key - } else { - ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key - } + if (string_key) { + ZVAL_STR(¶ms[0], string_key); // Assign string key directly + } else { + ZVAL_LONG(¶ms[0], num_key); // Assign numeric key directly + } + + fci_key.params = params; // Pass params as an array of zval + fci_key.param_count = 1; + fci_key.retval = &retval; - fci_key.params = params[0]; // Assigning the address of the first element of params array - fci_key.param_count = 1; - fci_key.retval = &retval; - - if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || (min_entry != NULL && zend_is_true(zend_compare_objects(&retval, min_entry)))) { - if (min_entry) { - zval_ptr_dtor(min_entry); - } - min_entry = entry; - Z_TRY_ADDREF_P(min_entry); - first = 0; + if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { + if (first || zend_is_true(&retval)) { + if (min_entry) { + zval_ptr_dtor(min_entry); } - } else { - zend_argument_type_error(2, "must be callable"); - RETURN_THROWS(); + min_entry = entry; + Z_TRY_ADDREF_P(min_entry); + if (string_key) { + min_key_str = string_key; + use_str_key = 1; + } else { + min_key_long = num_key; + use_str_key = 0; + } + first = 0; } - } zend_catch { + zval_ptr_dtor(&retval); + } else { zend_argument_type_error(2, "must be callable"); RETURN_THROWS(); - } zend_end_try(); + } } ZEND_HASH_FOREACH_END(); if (min_entry) { array_init(return_value); - add_next_index_zval(return_value, min_entry); - if (string_key) { - add_next_index_str(return_value, string_key); + add_assoc_zval(return_value, "value", min_entry); + if (use_str_key) { + add_assoc_str(return_value, "key", zend_string_copy(min_key_str)); } else { - add_next_index_long(return_value, num_key); + add_assoc_long(return_value, "key", min_key_long); } } else { zend_argument_value_error(1, "must contain at least one element"); @@ -1527,7 +1533,12 @@ PHP_FUNCTION(max_with_key) zval *values; zend_fcall_info fci_key = empty_fcall_info; zend_fcall_info_cache fcc_key = empty_fcall_info_cache; - zval *params[1] = { NULL }; + zval params[1]; // Declare params as an array of zval + zval retval; + zval *max_entry = NULL; + zend_string *max_key_str = NULL; + zend_ulong max_key_long = 0; + zend_bool use_str_key = 0; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ARRAY(values) @@ -1536,49 +1547,51 @@ PHP_FUNCTION(max_with_key) HashTable *ht = Z_ARRVAL_P(values); zval *entry; - zval retval; - zval *max_entry = NULL; zend_string *string_key; zend_ulong num_key; + int first = 1; ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - zend_try { - if (string_key) { - ZVAL_STR(params[0], string_key); // Direct assignment of string key - } else { - ZVAL_LONG(params[0], num_key); // Direct assignment of numeric key - } + if (string_key) { + ZVAL_STR(¶ms[0], string_key); // Assign string key directly + } else { + ZVAL_LONG(¶ms[0], num_key); // Assign numeric key directly + } + + fci_key.params = params; // Pass params as an array of zval + fci_key.param_count = 1; + fci_key.retval = &retval; - fci_key.params = params[0]; // Assigning the address of the first element of params array - fci_key.param_count = 1; - fci_key.retval = &retval; - - if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || (max_entry != NULL && zend_is_true(zend_compare_objects(&retval, max_entry)))) { - if (max_entry) { - zval_ptr_dtor(max_entry); - } - max_entry = entry; - Z_TRY_ADDREF_P(max_entry); - first = 0; + if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { + if (first || zend_is_true(&retval)) { + if (max_entry) { + zval_ptr_dtor(max_entry); + } + max_entry = entry; + Z_TRY_ADDREF_P(max_entry); + if (string_key) { + max_key_str = string_key; + use_str_key = 1; + } else { + max_key_long = num_key; + use_str_key = 0; } - } else { - zend_argument_type_error(2, "must be callable"); - RETURN_THROWS(); + first = 0; } - } zend_catch { + zval_ptr_dtor(&retval); + } else { zend_argument_type_error(2, "must be callable"); RETURN_THROWS(); - } zend_end_try(); + } } ZEND_HASH_FOREACH_END(); if (max_entry) { array_init(return_value); - add_next_index_zval(return_value, max_entry); - if (string_key) { - add_next_index_str(return_value, string_key); + add_assoc_zval(return_value, "value", max_entry); + if (use_str_key) { + add_assoc_str(return_value, "key", zend_string_copy(max_key_str)); } else { - add_next_index_long(return_value, num_key); + add_assoc_long(return_value, "key", max_key_long); } } else { zend_argument_value_error(1, "must contain at least one element"); From 1ef2dfb607722d4edc532948e9e991482df6d928 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 18:06:52 -0400 Subject: [PATCH 08/11] Update binary file in basic_functions_arginfo.h --- ext/standard/basic_functions_arginfo.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index cf7569ffea13b..8d90e0473cca9 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -141,7 +141,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_min_with_key, 0, 1, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_MIXED, 0) ZEND_END_ARG_INFO() -#define arginfo_max arginfo_min arginfo_max_with_key arginfo_min_with_key +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_max_with_key, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_max arginfo_min +#define arginfo_max_with_key arginfo_min_with_key ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_walk, 0, 2, IS_TRUE, 0) ZEND_ARG_TYPE_MASK(1, array, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) From eb893963554e60cc273b0ecea61375c7b9a4827d Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 20:15:37 -0400 Subject: [PATCH 09/11] Add min_with_key and max_with_key functions --- Zend/zend_hash.c | 7 +- ext/standard/array.c | 377 +++++++++++++++++++++++++++---------------- 2 files changed, 247 insertions(+), 137 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 3cb31a6296e9f..fdcdf4f03d5c0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -3186,7 +3186,7 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co } -ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag) +ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag, uint32_t with_key) { uint32_t idx; zval *res; @@ -3250,6 +3250,11 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_ } } } + + if (with_key) { + //return [idx, res]; + } + return res; } diff --git a/ext/standard/array.c b/ext/standard/array.c index f511f33d68e51..ee9d36920fde8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1328,75 +1328,127 @@ ZEND_FRAMELESS_FUNCTION(min, 2) Return the key and value of the element from the array that has the lowest value according to the specified key function */ PHP_FUNCTION(min_with_key) { - zval *values; - zend_fcall_info fci_key = empty_fcall_info; - zend_fcall_info_cache fcc_key = empty_fcall_info_cache; - zval params[1]; // Declare params as an array of zval - zval retval; - zval *min_entry = NULL; - zend_string *min_key_str = NULL; - zend_ulong min_key_long = 0; - zend_bool use_str_key = 0; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(values) - Z_PARAM_FUNC(fci_key, fcc_key) - ZEND_PARSE_PARAMETERS_END(); - - HashTable *ht = Z_ARRVAL_P(values); - zval *entry; - zend_string *string_key; - zend_ulong num_key; - int first = 1; - - ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - if (string_key) { - ZVAL_STR(¶ms[0], string_key); // Assign string key directly - } else { - ZVAL_LONG(¶ms[0], num_key); // Assign numeric key directly - } - - fci_key.params = params; // Pass params as an array of zval - fci_key.param_count = 1; - fci_key.retval = &retval; - - if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(&retval)) { - if (min_entry) { - zval_ptr_dtor(min_entry); - } - min_entry = entry; - Z_TRY_ADDREF_P(min_entry); - if (string_key) { - min_key_str = string_key; - use_str_key = 1; - } else { - min_key_long = num_key; - use_str_key = 0; - } - first = 0; - } - zval_ptr_dtor(&retval); - } else { - zend_argument_type_error(2, "must be callable"); - RETURN_THROWS(); - } - } ZEND_HASH_FOREACH_END(); - - if (min_entry) { - array_init(return_value); - add_assoc_zval(return_value, "value", min_entry); - if (use_str_key) { - add_assoc_str(return_value, "key", zend_string_copy(min_key_str)); - } else { - add_assoc_long(return_value, "key", min_key_long); - } - } else { - zend_argument_value_error(1, "must contain at least one element"); - RETURN_THROWS(); - } + uint32_t argc; + zval *args = NULL; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + /* mixed min ( array $values ) */ + if (argc == 1) { + if (Z_TYPE(args[0]) != IS_ARRAY) { + zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); + RETURN_THROWS(); + } else { + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0); + if (result) { + RETURN_COPY_DEREF(result); + } else { + zend_argument_value_error(1, "must contain at least one element"); + RETURN_THROWS(); + } + } + } else { + /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */ + zval *min; + uint32_t i; + + min = &args[0]; + zend_long min_lval; + double min_dval; + + if (Z_TYPE_P(min) == IS_LONG) { + min_lval = Z_LVAL_P(min); + + for (i = 1; i < argc; i++) { + if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) { + if (min_lval > Z_LVAL(args[i])) { + min_lval = Z_LVAL(args[i]); + min = &args[i]; + } + } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) { + /* if min_lval can be exactly represented as a double, go to double dedicated code */ + min_dval = (double) min_lval; + goto double_compare; + } else { + goto generic_compare; + } + } + + RETURN_LONG(min_lval); + } else if (Z_TYPE_P(min) == IS_DOUBLE) { + min_dval = Z_DVAL_P(min); + + for (i = 1; i < argc; i++) { + if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) { + double_compare: + if (min_dval > Z_DVAL(args[i])) { + min_dval = Z_DVAL(args[i]); + min = &args[i]; + } + } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) { + /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */ + if (min_dval > (double)Z_LVAL(args[i])) { + min_dval = (double)Z_LVAL(args[i]); + min = &args[i]; + } + } else { + goto generic_compare; + } + } + } else { + for (i = 1; i < argc; i++) { + generic_compare: + if (zend_compare(&args[i], min) < 0) { + min = &args[i]; + } + } + } + + RETURN_COPY(min); + } +} +/* }}} */ + +ZEND_FRAMELESS_FUNCTION(min_with_key, 2) +{ + zval *lhs, *rhs; + + Z_FLF_PARAM_ZVAL(1, lhs); + Z_FLF_PARAM_ZVAL(2, rhs); + + double lhs_dval; + + if (Z_TYPE_P(lhs) == IS_LONG) { + zend_long lhs_lval = Z_LVAL_P(lhs); + + if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) { + RETURN_COPY_VALUE(lhs_lval < Z_LVAL_P(rhs) ? lhs : rhs); + } else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) { + /* if lhs_lval can be exactly represented as a double, go to double dedicated code */ + lhs_dval = (double) lhs_lval; + goto double_compare; + } else { + goto generic_compare; + } + } else if (Z_TYPE_P(lhs) == IS_DOUBLE) { + lhs_dval = Z_DVAL_P(lhs); + + if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) { +double_compare: + RETURN_COPY_VALUE(lhs_dval < Z_DVAL_P(rhs) ? lhs : rhs); + } else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) { + /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */ + RETURN_COPY_VALUE(lhs_dval < (double)Z_LVAL_P(rhs) ? lhs : rhs); + } else { + goto generic_compare; + } + } else { +generic_compare: + RETURN_COPY(zend_compare(lhs, rhs) < 0 ? lhs : rhs); + } } -/* }}} */ /* {{{ * proto mixed max(array values) @@ -1530,75 +1582,128 @@ ZEND_FRAMELESS_FUNCTION(max, 2) Return the element from the array that has the highest value according to the specified key function */ PHP_FUNCTION(max_with_key) { - zval *values; - zend_fcall_info fci_key = empty_fcall_info; - zend_fcall_info_cache fcc_key = empty_fcall_info_cache; - zval params[1]; // Declare params as an array of zval - zval retval; - zval *max_entry = NULL; - zend_string *max_key_str = NULL; - zend_ulong max_key_long = 0; - zend_bool use_str_key = 0; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(values) - Z_PARAM_FUNC(fci_key, fcc_key) - ZEND_PARSE_PARAMETERS_END(); - - HashTable *ht = Z_ARRVAL_P(values); - zval *entry; - zend_string *string_key; - zend_ulong num_key; - int first = 1; - - ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry) { - if (string_key) { - ZVAL_STR(¶ms[0], string_key); // Assign string key directly - } else { - ZVAL_LONG(¶ms[0], num_key); // Assign numeric key directly - } - - fci_key.params = params; // Pass params as an array of zval - fci_key.param_count = 1; - fci_key.retval = &retval; - - if (zend_call_function(&fci_key, &fcc_key) == SUCCESS) { - if (first || zend_is_true(&retval)) { - if (max_entry) { - zval_ptr_dtor(max_entry); - } - max_entry = entry; - Z_TRY_ADDREF_P(max_entry); - if (string_key) { - max_key_str = string_key; - use_str_key = 1; - } else { - max_key_long = num_key; - use_str_key = 0; - } - first = 0; - } - zval_ptr_dtor(&retval); - } else { - zend_argument_type_error(2, "must be callable"); - RETURN_THROWS(); - } - } ZEND_HASH_FOREACH_END(); - - if (max_entry) { - array_init(return_value); - add_assoc_zval(return_value, "value", max_entry); - if (use_str_key) { - add_assoc_str(return_value, "key", zend_string_copy(max_key_str)); - } else { - add_assoc_long(return_value, "key", max_key_long); - } - } else { - zend_argument_value_error(1, "must contain at least one element"); - RETURN_THROWS(); - } + zval *args = NULL; + uint32_t argc; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + /* mixed max ( array $values ) */ + if (argc == 1) { + if (Z_TYPE(args[0]) != IS_ARRAY) { + zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); + RETURN_THROWS(); + } else { + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1); + if (result) { + RETURN_COPY_DEREF(result); + } else { + zend_argument_value_error(1, "must contain at least one element"); + RETURN_THROWS(); + } + } + } else { + /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */ + zval *max; + uint32_t i; + + max = &args[0]; + zend_long max_lval; + double max_dval; + + if (Z_TYPE_P(max) == IS_LONG) { + max_lval = Z_LVAL_P(max); + + for (i = 1; i < argc; i++) { + if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) { + if (max_lval < Z_LVAL(args[i])) { + max_lval = Z_LVAL(args[i]); + max = &args[i]; + } + } else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) { + /* if max_lval can be exactly represented as a double, go to double dedicated code */ + max_dval = (double) max_lval; + goto double_compare; + } else { + goto generic_compare; + } + } + + RETURN_LONG(max_lval); + } else if (Z_TYPE_P(max) == IS_DOUBLE) { + max_dval = Z_DVAL_P(max); + + for (i = 1; i < argc; i++) { + if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) { + double_compare: + if (max_dval < Z_DVAL(args[i])) { + max_dval = Z_DVAL(args[i]); + max = &args[i]; + } + } else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) { + /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */ + if (max_dval < (double)Z_LVAL(args[i])) { + max_dval = (double)Z_LVAL(args[i]); + max = &args[i]; + } + } else { + goto generic_compare; + } + } + } else { + for (i = 1; i < argc; i++) { + generic_compare: + if (zend_compare(&args[i], max) > 0) { + max = &args[i]; + } + } + } + + RETURN_COPY(max); + } +} +/* }}} */ + + +ZEND_FRAMELESS_FUNCTION(max_with_key, 2) +{ + zval *lhs, *rhs; + + Z_FLF_PARAM_ZVAL(1, lhs); + Z_FLF_PARAM_ZVAL(2, rhs); + + double lhs_dval; + + if (Z_TYPE_P(lhs) == IS_LONG) { + zend_long lhs_lval = Z_LVAL_P(lhs); + + if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) { + RETURN_COPY_VALUE(lhs_lval >= Z_LVAL_P(rhs) ? lhs : rhs); + } else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) { + /* if lhs_lval can be exactly represented as a double, go to double dedicated code */ + lhs_dval = (double) lhs_lval; + goto double_compare; + } else { + goto generic_compare; + } + } else if (Z_TYPE_P(lhs) == IS_DOUBLE) { + lhs_dval = Z_DVAL_P(lhs); + + if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) { +double_compare: + RETURN_COPY_VALUE(lhs_dval >= Z_DVAL_P(rhs) ? lhs : rhs); + } else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) { + /* if the value can be exactly represented as a double, use double dedicated code otherwise generic */ + RETURN_COPY_VALUE(lhs_dval >= (double)Z_LVAL_P(rhs) ? lhs : rhs); + } else { + goto generic_compare; + } + } else { +generic_compare: + RETURN_COPY(zend_compare(lhs, rhs) >= 0 ? lhs : rhs); + } } -/* }}} */ typedef struct { zend_fcall_info fci; From cddd52319a88c0ae1e681428a0e1d4e9969be0f9 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 20:23:07 -0400 Subject: [PATCH 10/11] Add support for returning key in zend_hash_minmax function --- Zend/zend_hash.h | 2 +- ext/standard/array.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 89885557b044f..789ffda1c30f9 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -298,7 +298,7 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q); typedef int (*bucket_compare_func_t)(Bucket *a, Bucket *b); ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered); ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber); -ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag); +ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag, uint32_t with_key); static zend_always_inline void ZEND_FASTCALL zend_hash_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber) { zend_hash_sort_ex(ht, zend_sort, compare_func, renumber); diff --git a/ext/standard/array.c b/ext/standard/array.c index ee9d36920fde8..19b3e2cd219f3 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1215,7 +1215,7 @@ PHP_FUNCTION(min) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0); + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0, 0); if (result) { RETURN_COPY_DEREF(result); } else { @@ -1341,7 +1341,7 @@ PHP_FUNCTION(min_with_key) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0); + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0, 1); if (result) { RETURN_COPY_DEREF(result); } else { @@ -1469,7 +1469,7 @@ PHP_FUNCTION(max) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1); + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1, 0); if (result) { RETURN_COPY_DEREF(result); } else { @@ -1595,7 +1595,7 @@ PHP_FUNCTION(max_with_key) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1); + zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1, 1); if (result) { RETURN_COPY_DEREF(result); } else { From 6feadf3d1999220b1e6674cd86efeede0f6a3776 Mon Sep 17 00:00:00 2001 From: Elminson De Oleo Baez Date: Mon, 27 May 2024 20:49:11 -0400 Subject: [PATCH 11/11] Fix syntax error in min_with_key test case --- ext/standard/tests/array/min_with_key.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/array/min_with_key.phpt b/ext/standard/tests/array/min_with_key.phpt index 2375ab5462884..e962df73a73b6 100644 --- a/ext/standard/tests/array/min_with_key.phpt +++ b/ext/standard/tests/array/min_with_key.phpt @@ -31,7 +31,7 @@ var_dump(min_with_key(true, false, true)); var_dump(min_with_key(1, true, false, true)); var_dump(min_with_key(0, true, false, true)); -var_dump(min_with_key(['a' => 1, 'b' => 4, 'c' => -2]); +var_dump(min_with_key(['a' => 1, 'b' => 4, 'c' => -2])); ?> --EXPECT-- @@ -45,4 +45,4 @@ bool(false) bool(false) bool(false) int(0) -array('c' => -2) +int(-2)