配列で存在確認する関数といえば、どこに行っても第三引数true
を指定しろと書かれているin_array・array_searchと、isset($array[$key])
って書いた方が短い謎関数array_key_existsくらいしかありませんでした。
いずれもまあそれなりに便利ではあるのですが、たとえば正規表現検索できないなど色々と使い勝手の足りないところがありました。
ということでコールバックで任意に検索できる関数を追加しようというRFCが提出されました。
既に受理されており、PHP8.4から使用可能になります。
以下は該当のRFC、array_findの日本語訳です。
PHP RFC: array_find
Introduction
このRFCでは、特定の条件に一致する要素が存在するかをチェックする新しい配列関数array_find
・array_find_key
・array_any
・array_all
を追加します。
コールバックを使って配列を処理する関数がいくつか存在します。
しかし、条件に一致する要素をコールバックを使って検索する関数はまだありません。
これらをユーザランドに実装するのは比較的簡単ですが、よく使われるため車輪の再発明になりがちです。
また、Rust・JavaScript・C++などでは既に言語として実装されています。
従って、これらの関数をPHPに実装する意義はあるでしょう。
また、関数の実装はarray_filterとよく似ているため、メンテナンスの労力も大きくありません。
Proposal
以下の4関数、array_find
・array_find_key
・array_any
・array_all
を追加します。
array_find
array_find
は、引数$callback
がtrueを返す最初の要素の値を返します。
一致する要素が存在しない場合はnullが返ります。
function array_find(array $array, callable $callback): mixed {
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $value;
}
}
return null;
}
以下は例です。
$array = [
'a' => 'dog',
'b' => 'cat',
'c' => 'cow',
'd' => 'duck',
'e' => 'goose',
'f' => 'elephant'
];
// 5文字以上の値を検索
var_dump(array_find($array, function (string $value) {
return strlen($value) > 4;
})); // string(5) "goose"
// fで始まる値を検索
var_dump(array_find($array, function (string $value) {
return str_starts_with($value, 'f');
})); // NULL
// 1文字目がキーになっている値を検索
var_dump(array_find($array, function (string $value, $key) {
return $value[0] === $key;
})); // string(3) "cow"
// 正規表現に一致するキーを検索
var_dump(array_find($array, function ($value, $key) {
return preg_match('/^([a-f])$/', $key);
})); // string(3) "dog"
array_find_key
array_find
は、引数$callback
がtrueを返す最初の要素のキーを返します。
一致する要素が存在しない場合はnullが返ります。
function array_find_key(array $array, callable $callback): mixed {
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $key;
}
}
return null;
}
以下は例です。
// 5文字以上の値を検索してキーを返す
var_dump(array_find_key($array, function (string $value) {
return strlen($value) > 4;
})); // string(1) "e"
// fで始まる値を検索してキーを返す
var_dump(array_find_key($array, function (string $value) {
return str_starts_with($value, 'f');
})); // NULL
// 1文字目がキーになっている値を検索してキーを返す
var_dump(array_find_key($array, function (string $value, $key) {
return $value[0] === $key;
})); // string(1) "c"
// 正規表現に一致するキーを検索してキーを返す
var_dump(array_find_key($array, function (string $value, $key) {
return preg_match('/^([a-f])$/', $key);
})); // string(1) "a"
array_any
array_any
は、引数$callback
がいずれかの要素に対してtrueを返す場合にtrueを返します。
そうでなければfalseを返します。
function array_any(array $array, callable $callback): bool {
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return true;
}
}
return false;
}
以下は例です。
// 5文字以上の値が存在するか
var_dump(array_any($array, function (string $value) {
return strlen($value) > 5;
})); // bool(true)
// 3文字未満の値が存在するか
var_dump(array_any($array, function (string $value) {
return strlen($value) < 3;
})); // bool(false)
// string型でないキーが存在するか
var_dump(array_any($array, function (string $value, $key) {
return !is_string($key);
})); // bool(false)
array_all
array_all
は、引数$callback
が全ての要素に対してtrueを返す場合にtrueを返します。
そうでなければfalseを返します。
function array_all(array $array, callable $callback): bool {
foreach ($array as $key => $value) {
if (!$callback($value, $key)) {
return false;
}
}
return true;
}
以下は例です。
// 全ての値が12文字未満か
var_dump(array_all($array, function (string $value) {
return strlen($value) < 12;
})); // bool(true)
// 全ての値が6文字以上か
var_dump(array_all($array, function (string $value) {
return strlen($value) > 5;
})); // bool(false)
// 全てのキーがstring型か
var_dump(array_all($array, function (string $value, $key) {
return is_string($key);
})); // bool(true)
Backward Incompatible Changes
下位互換性のない変更点。
ユーザランドで同名の関数を作っていた場合動作しなくなります。
GitHubで軽く検索したところ、array_find
が656件、array_find_key
が28件、array_any
が127件、array_all
が284件ありました。
Proposed PHP Version(s)
PHP8.4
RFC Impact
SAPIやエクステンションへの影響は特にありません。
新しい定数やini設定はありません。
Open Issues
この関数は必ずコールバックにキーと値を渡します。
コールバックに渡すパラメータを設定するパラメータがあるarray_filterとは動作が異なります。
キーを渡せないかわりに複数の配列を渡せるarray_mapとも動作が異なります。
Unaffected PHP Functionality
このRFCは新しい関数を追加するだけであり、他のPHP機能に影響は与えません。
Proposed Voting Choices
投票期間は2024/05/15~2024/05/29、投票の2/3の賛成で受理されます。
array_find()/array_find_key()
は賛成21反対0、array_any()/array_all()
は賛成20反対0の全会一致で可決されました。
Implementation
感想
これまでこういうことをやる場合は素直にforeachでぐるぐる回すか、array_walkあたりで芸術的な記述をしていたわけですが、なんかいいかんじに書けるようになりますね。
まあ私はおそらく今後もforeach使うと思いますが。
ちなみに『引数$callback
がtrueを返す全ての要素を返す』関数はarray_filterです。
名前が揃ってないのがそこはかとなくわかりにくい。