かつてPHP7.3でarray_key_first()が実装されました。
これは配列の最初のキーを取得する関数です。
このとき同時に配列の最初の値を取得する関数array_value_first()も提案されたのですが、こちらは何故か却下されました。
従って現在、配列の最初の値を取得するにはこうする必要があります。
$array_value_first = $array[array_key_first($array)];
なんだかわかりにくいし$arrayを2回も使っています。
ということで、配列の最初と最後の値を取得するRFCが提案されました。
現在投票中ですが、ほぼ間違いなく受理され、PHP8.5から使用可能になります。
以下は該当のRFC、array_first() and array_last()の日本語訳です。
PHP RFC: array_first() and array_last()
Introduction
PHP7.3において、配列の最初と最後のキーを取得する関数array_key_first()・array_key_last()が導入されました。
しかし、配列の最初と最後の値を取得する方法はまだありません。
これは思ったより難しいです。
・配列のキーは必ずしも整数ではなく、必ずしも0始まりでもない。
・reset()やend()は内部イテレータを操作するものであり、意味的に正しい使い方ではない。さらにリファレンス等利用できない場合もある。
・$array[array_key_first($array)]はわかりにくい。
Proposal
function array_first(array $array): mixed {}
function array_last(array $array): mixed {}
それぞれ配列の最初の値、最後の値を返します。
すなわち、$array[array_key_first($array)]と同等です。
返される値がリファレンスであった場合、リファレンスは解除されて値になります。
Behaviour on empty arrays
array_key_first()のRFCで否決された主な問題点は、失敗時の動作に関するものでした。
空の配列に対しては例外をスローすべきでしょうか、NULLを返すべきでしょうか。
NULLは配列値として有効な値であるため、NULLを返すと空の配列と値がNULLの配列を区別できません。
しかしながら、NULLを返す強力な根拠も多数存在します。
・$array[array_key_first($array)]と挙動が一致する。すなわち、存在しないキーはNULLを返す。
・array_find($arr, fn () => true)と挙動が一致する。
・array_shift()・array_pop()が空配列にNULLを返すことと挙動が一致する。
・Arr::first()など、よくあるフレームワークヘルパーが採用している。
・正しい値としてNULLを使用することはあんまりない。
・??演算子と合わせやすい。
もうひとつのアイデアとしては、オプションとして$default引数を追加することです。
すなわち、指定した場合はデフォルト値を返し、指定がなければ例外とします。
しかし、PHPの配列には型指定がないので、デフォルトとして意味のある値を指定することが難しいことがあります。
また、このアプローチは配列関数において採用例がありません。
さらに、プログラマーがarray_first($somevar, $someothervar)を見た際、シグネチャを知らないかぎり直感的に分かりにくい引数に見えます。
Naming
なぜarray_value_first/array_value_lastではなくarray_first/array_lastなのでしょうか。
これは、既存の配列関数の命名方法に合わせたものです。
array_find・array_find_keyなどを考えると、キーを扱う関数は関数名にkeyが含まれ、値を扱う関数は関数名にvalueが含まれません。
そして短い関数名でも、直感的に値に関するものだと理解できます。
Fibers
2023年のディスカッションスレッドでは、Fiberとの相互作用について混乱がありました。
まとめると、array_key_first()に続いてarray_first()を呼び出す際、Fiberの状態によって結果に矛盾が生じる可能性があるというものです。
しかし、これはFiberがThreadであるという誤った認識によるものであり、実際にはそのような状況は発生しません。
Why include this in the engine rather than userland?
ユーザランドではなくPHPエンジンに組み込む理由。
・array_key_first()・array_key_last()との一貫性のため。
・実装が驚くほどシンプル(実質3行)であり、メンテナンスコストもほとんどゼロである。
・PHP実装よりも非常に高速となる。
Examples
使用例をいくつか示します。
array_first(["single element"]); // "single element"
array_last(["single element"]); // "single element"
array_first([]); // NULL
array_last([]); // NULL
array_first([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'a'
array_last([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'd'
$str = "hello";
array_first([&$str, false]); // "hello" (値)
array_last([false, &$str]); // "hello" (値)
Backward Incompatible Changes
後方互換性のない変更点。
グローバル関数array_first()・array_last()を使っている場合は名前が衝突します。
Proposed PHP Version(s)
PHP8.5。
Proposed Voting Choices
投票期間は2025/04/22から2025/05/06まで、2/3の賛成で受理されます。
2025/04/28時点では賛成33反対0であり、これから却下されることはないでしょう。
Patches and Tests
References
Prior Work
・提案
・array_key_first()のRFC
・却下された機能
感想
単なるシンタックスシュガーであって、できることはこれまでと全く変わらないのですが、書式が極めてわかりやすくなりますね。
この関数の追加によって、配列操作関数はおおむね完成に達したのではないでしょうか。
まあ配列は基本的にforeachとかで全件隔てなく扱うべきで、順序に依存するような書き方はすべきでないと思いますが。