Skip to content

Commit e4a8d5b

Browse files
1 parent a58c3a7 commit e4a8d5b

10 files changed

+757
-1
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ PHP NEWS
283283
as the magic $http_response_header variable.
284284
. Add php_base64_encode_ex() API. (Remi)
285285
. Implemented "Raising zero to the power of negative number" RFC. (Jorg Sowa)
286+
. Added array_find(), array_find_key(), array_all(), and array_any(). (josh)
286287

287288
- XML:
288289
. Added XML_OPTION_PARSE_HUGE parser option. (nielsdos)

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ PHP 8.4 UPGRADE NOTES
566566
http_clear_last_response_headers() that allows retrieving the same content
567567
as the magic $http_response_header variable.
568568
. Added function fpow() following rules of IEEE 754.
569+
. Added functions array_find(), array_find_key(), array_all(), and
570+
array_any().
571+
RFC: https://wiki.php.net/rfc/array_find
569572

570573
- XSL:
571574
. Added XSLTProcessor::registerPhpFunctionNS().

ext/standard/array.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6580,6 +6580,165 @@ PHP_FUNCTION(array_filter)
65806580
}
65816581
/* }}} */
65826582

6583+
/* {{{ Internal function to find an array element for a user closure. */
6584+
static zend_result php_array_find(const HashTable *array, zend_fcall_info fci, zend_fcall_info_cache fci_cache, zval *result_key, zval *result_value, bool negate_condition)
6585+
{
6586+
zend_ulong num_key;
6587+
zend_string *str_key;
6588+
zval retval;
6589+
zval args[2];
6590+
zval *operand;
6591+
6592+
if (result_value != NULL) {
6593+
ZVAL_UNDEF(result_value);
6594+
}
6595+
6596+
if (result_key != NULL) {
6597+
ZVAL_UNDEF(result_key);
6598+
}
6599+
6600+
if (zend_hash_num_elements(array) == 0) {
6601+
return SUCCESS;
6602+
}
6603+
6604+
ZEND_ASSERT(ZEND_FCI_INITIALIZED(fci));
6605+
6606+
fci.retval = &retval;
6607+
fci.param_count = 2;
6608+
fci.params = args;
6609+
6610+
ZEND_HASH_FOREACH_KEY_VAL(array, num_key, str_key, operand) {
6611+
/* Set up the key */
6612+
if (!str_key) {
6613+
ZVAL_LONG(&args[1], num_key);
6614+
} else {
6615+
ZVAL_STR_COPY(&args[1], str_key);
6616+
}
6617+
6618+
ZVAL_COPY(&args[0], operand);
6619+
6620+
zend_result result = zend_call_function(&fci, &fci_cache);
6621+
if (EXPECTED(result == SUCCESS)) {
6622+
int retval_true;
6623+
6624+
retval_true = zend_is_true(&retval);
6625+
zval_ptr_dtor(&retval);
6626+
6627+
/* This negates the condition, if negate_condition is true. Otherwise it does nothing with `retval_true`. */
6628+
retval_true ^= negate_condition;
6629+
6630+
if (retval_true) {
6631+
if (result_value != NULL) {
6632+
ZVAL_COPY(result_value, &args[0]);
6633+
}
6634+
6635+
if (result_key != NULL) {
6636+
ZVAL_COPY(result_key, &args[1]);
6637+
}
6638+
6639+
zval_ptr_dtor(&args[0]);
6640+
zval_ptr_dtor(&args[1]);
6641+
6642+
return SUCCESS;
6643+
}
6644+
}
6645+
6646+
zval_ptr_dtor(&args[0]);
6647+
zval_ptr_dtor(&args[1]);
6648+
6649+
if (UNEXPECTED(result != SUCCESS)) {
6650+
return FAILURE;
6651+
}
6652+
} ZEND_HASH_FOREACH_END();
6653+
6654+
return SUCCESS;
6655+
}
6656+
/* }}} */
6657+
6658+
/* {{{ Search within an array and returns the first found element value. */
6659+
PHP_FUNCTION(array_find)
6660+
{
6661+
zval *array = NULL;
6662+
zend_fcall_info fci;
6663+
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6664+
6665+
ZEND_PARSE_PARAMETERS_START(2, 2)
6666+
Z_PARAM_ARRAY(array)
6667+
Z_PARAM_FUNC(fci, fci_cache)
6668+
ZEND_PARSE_PARAMETERS_END();
6669+
6670+
if (php_array_find(Z_ARR_P(array), fci, fci_cache, NULL, return_value, false) != SUCCESS) {
6671+
RETURN_THROWS();
6672+
}
6673+
6674+
if (Z_TYPE_P(return_value) == IS_UNDEF) {
6675+
RETURN_NULL();
6676+
}
6677+
}
6678+
/* }}} */
6679+
6680+
/* {{{ Search within an array and returns the first found element key. */
6681+
PHP_FUNCTION(array_find_key)
6682+
{
6683+
zval *array = NULL;
6684+
zend_fcall_info fci;
6685+
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6686+
6687+
ZEND_PARSE_PARAMETERS_START(2, 2)
6688+
Z_PARAM_ARRAY(array)
6689+
Z_PARAM_FUNC(fci, fci_cache)
6690+
ZEND_PARSE_PARAMETERS_END();
6691+
6692+
if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) {
6693+
RETURN_THROWS();
6694+
}
6695+
6696+
if (Z_TYPE_P(return_value) == IS_UNDEF) {
6697+
RETURN_NULL();
6698+
}
6699+
}
6700+
/* }}} */
6701+
6702+
/* {{{ Search within an array and returns true if an element is found. */
6703+
PHP_FUNCTION(array_any)
6704+
{
6705+
zval *array = NULL;
6706+
zend_fcall_info fci;
6707+
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6708+
6709+
ZEND_PARSE_PARAMETERS_START(2, 2)
6710+
Z_PARAM_ARRAY(array)
6711+
Z_PARAM_FUNC(fci, fci_cache)
6712+
ZEND_PARSE_PARAMETERS_END();
6713+
6714+
if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) {
6715+
RETURN_THROWS();
6716+
}
6717+
6718+
RETURN_BOOL(Z_TYPE_P(return_value) != IS_UNDEF);
6719+
}
6720+
/* }}} */
6721+
6722+
/* {{{ Search within an array and returns true if an element is found. */
6723+
PHP_FUNCTION(array_all)
6724+
{
6725+
zval *array = NULL;
6726+
zend_fcall_info fci;
6727+
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
6728+
6729+
ZEND_PARSE_PARAMETERS_START(2, 2)
6730+
Z_PARAM_ARRAY(array)
6731+
Z_PARAM_FUNC(fci, fci_cache)
6732+
ZEND_PARSE_PARAMETERS_END();
6733+
6734+
if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, true) != SUCCESS) {
6735+
RETURN_THROWS();
6736+
}
6737+
6738+
RETURN_BOOL(Z_TYPE_P(return_value) == IS_UNDEF);
6739+
}
6740+
/* }}} */
6741+
65836742
/* {{{ Applies the callback to the elements in given arrays. */
65846743
PHP_FUNCTION(array_map)
65856744
{

ext/standard/basic_functions.stub.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,14 @@ function array_reduce(array $array, callable $callback, mixed $initial = null):
18971897

18981898
function array_filter(array $array, ?callable $callback = null, int $mode = 0): array {}
18991899

1900+
function array_find(array $array, callable $callback): mixed {}
1901+
1902+
function array_find_key(array $array, callable $callback): mixed {}
1903+
1904+
function array_any(array $array, callable $callback): bool {}
1905+
1906+
function array_all(array $array, callable $callback): bool {}
1907+
19001908
function array_map(?callable $callback, array $array, array ...$arrays): array {}
19011909

19021910
/**

ext/standard/basic_functions_arginfo.h

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
--TEST--
2+
Test array_all() function : basic functionality
3+
--FILE--
4+
<?php
5+
$array1 = [
6+
"a" => 1,
7+
"b" => 2,
8+
"c" => 3,
9+
"d" => 4,
10+
"e" => 5,
11+
];
12+
13+
$array2 = [
14+
1, 2, 3, 4, 5
15+
];
16+
17+
function even($input) {
18+
return $input % 2 === 0;
19+
}
20+
21+
class SmallerTenClass {
22+
public static function smallerTen($input) {
23+
return $input < 10;
24+
}
25+
}
26+
27+
var_dump(array_all($array1, fn($value) => $value > 0));
28+
var_dump(array_all($array2, fn($value) => $value > 0));
29+
var_dump(array_all($array2, fn($value) => $value > 1));
30+
var_dump(array_all([], fn($value) => true));
31+
32+
echo '*** Test Exception after false result ***' . PHP_EOL;
33+
try {
34+
var_dump(array_all($array2, function ($value) {
35+
if ($value > 1) {
36+
throw new Exception("Test-Exception");
37+
}
38+
39+
return false;
40+
}));
41+
} catch (Exception) {
42+
var_dump("Unexpected Exception");
43+
}
44+
45+
echo '*** Test aborting with exception ***' . PHP_EOL;
46+
try {
47+
var_dump(array_all($array2, function ($value) {
48+
if ($value === 2) {
49+
throw new Exception("Test-Exception");
50+
}
51+
52+
var_dump($value);
53+
54+
return true;
55+
}));
56+
} catch (Exception) {
57+
var_dump("Catched Exception");
58+
}
59+
60+
var_dump(array_all($array1, 'even'));
61+
62+
var_dump(array_all($array1, function($value) {
63+
// return nothing
64+
}));
65+
66+
var_dump(array_all($array1, [
67+
'SmallerTenClass',
68+
'smallerTen'
69+
]));
70+
71+
var_dump(array_all($array1, "SmallerTenClass::smallerTen"));
72+
?>
73+
--EXPECT--
74+
bool(true)
75+
bool(true)
76+
bool(false)
77+
bool(true)
78+
*** Test Exception after false result ***
79+
bool(false)
80+
*** Test aborting with exception ***
81+
int(1)
82+
string(17) "Catched Exception"
83+
bool(false)
84+
bool(false)
85+
bool(true)
86+
bool(true)

0 commit comments

Comments
 (0)