diff --git a/NEWS b/NEWS index 98f8efa4fadfd..706533b9c898a 100644 --- a/NEWS +++ b/NEWS @@ -211,6 +211,7 @@ PHP NEWS (Michael Orlitzky). . Fixed bug GH-18062 (is_callable(func(...), callable_name: $name) for first class callables returns wrong name). (timwolla) + . Added array_first() and array_last(). (nielsdos) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). diff --git a/UPGRADING b/UPGRADING index 04bc66c4890c4..6000c237f85d0 100644 --- a/UPGRADING +++ b/UPGRADING @@ -356,6 +356,10 @@ PHP 8.5 UPGRADE NOTES . ReflectionConstant::getAttributes() was introduced. RFC: https://wiki.php.net/rfc/attributes-on-constants +- Standard: + . Added array_first() and array_last(). + RFC: https://wiki.php.net/rfc/array_first_last + ======================================== 7. New Classes and Interfaces ======================================== diff --git a/ext/standard/array.c b/ext/standard/array.c index 6fe91fb714915..522e7f715acb7 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4513,6 +4513,32 @@ PHP_FUNCTION(array_key_last) } /* }}} */ +PHP_FUNCTION(array_first) +{ + HashTable *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(array) + ZEND_PARSE_PARAMETERS_END(); + + ZEND_HASH_FOREACH_VAL(array, zval *zv) { + RETURN_COPY_DEREF(zv); + } ZEND_HASH_FOREACH_END(); +} + +PHP_FUNCTION(array_last) +{ + HashTable *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(array) + ZEND_PARSE_PARAMETERS_END(); + + ZEND_HASH_REVERSE_FOREACH_VAL(array, zval *zv) { + RETURN_COPY_DEREF(zv); + } ZEND_HASH_FOREACH_END(); +} + /* {{{ Return just the values from the input array */ PHP_FUNCTION(array_values) { diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index e7f4ff8844714..4bec9d07348c0 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1712,6 +1712,16 @@ function array_key_first(array $array): int|string|null {} */ function array_key_last(array $array): int|string|null {} +/** + * @compile-time-eval + */ +function array_first(array $array): mixed {} + +/** + * @compile-time-eval + */ +function array_last(array $array): mixed {} + /** * @return array * @compile-time-eval diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 3d92288643159..c39ddafc827ec 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 85677dc3476d25b7820fd3a26fe39f2e9378b6e7 */ + * Stub hash: f1fdd58097ccd7562c63aee8e9cc1ca88f5bdf31 */ 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) @@ -235,6 +235,12 @@ ZEND_END_ARG_INFO() #define arginfo_array_key_last arginfo_array_key_first +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_first, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_array_last arginfo_array_first + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_values, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -2350,6 +2356,8 @@ ZEND_FUNCTION(array_replace_recursive); ZEND_FUNCTION(array_keys); ZEND_FUNCTION(array_key_first); ZEND_FUNCTION(array_key_last); +ZEND_FUNCTION(array_first); +ZEND_FUNCTION(array_last); ZEND_FUNCTION(array_values); ZEND_FUNCTION(array_count_values); ZEND_FUNCTION(array_column); @@ -2943,6 +2951,8 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY("array_keys", zif_array_keys, arginfo_array_keys, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("array_key_first", zif_array_key_first, arginfo_array_key_first, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("array_key_last", zif_array_key_last, arginfo_array_key_last, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) + ZEND_RAW_FENTRY("array_first", zif_array_first, arginfo_array_first, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) + ZEND_RAW_FENTRY("array_last", zif_array_last, arginfo_array_last, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("array_values", zif_array_values, arginfo_array_values, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("array_count_values", zif_array_count_values, arginfo_array_count_values, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("array_column", zif_array_column, arginfo_array_column, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) diff --git a/ext/standard/tests/array/array_first_last.phpt b/ext/standard/tests/array/array_first_last.phpt new file mode 100644 index 0000000000000..f8f7a6afc3624 --- /dev/null +++ b/ext/standard/tests/array/array_first_last.phpt @@ -0,0 +1,53 @@ +--TEST-- +array_first()/array_last() +--FILE-- + 1, 0 => 0, 3 => 3, 2 => 2], + [100 => []], + [new stdClass, false], + [true, new stdClass], +]; + +foreach ($test_cases as $test_case) { + // Output the checked values + echo "--- Testing: ", json_encode($test_case), " ---\n"; + echo "First: ", json_encode(array_first($test_case)), "\n"; + echo "Last: ", json_encode(array_last($test_case)), "\n"; + + // Sanity check consistency with array_key_first()/array_key_last() + if (array_first($test_case) !== $test_case[array_key_first($test_case)]) { + throw new Error("Key first and value first inconsistency"); + } + if (array_last($test_case) !== $test_case[array_key_last($test_case)]) { + throw new Error("Key last and value last inconsistency"); + } +} +?> +--EXPECT-- +--- Testing: ["single element"] --- +First: "single element" +Last: "single element" +--- Testing: ["hello world",1] --- +First: "hello world" +Last: 1 +--- Testing: [1,"hello world"] --- +First: 1 +Last: "hello world" +--- Testing: {"1":1,"0":0,"3":3,"2":2} --- +First: 1 +Last: 2 +--- Testing: {"100":[]} --- +First: [] +Last: [] +--- Testing: [{},false] --- +First: {} +Last: false +--- Testing: [true,{}] --- +First: true +Last: {} diff --git a/ext/standard/tests/array/array_first_last_errors.phpt b/ext/standard/tests/array/array_first_last_errors.phpt new file mode 100644 index 0000000000000..4225bf8289198 --- /dev/null +++ b/ext/standard/tests/array/array_first_last_errors.phpt @@ -0,0 +1,20 @@ +--TEST-- +array_first()/array_last() error cases +--FILE-- + +--EXPECT-- +NULL +NULL +NULL +NULL