PHP 8.5のarray_first()とarray_last()で配列の端をきれいに取る
array_first()とarray_last()は、PHP 8.5で追加された、配列の先頭・末尾の「値」を内部ポインタを動かさずに取得する関数です。地味な追加ですが、reset()やend()を使い回してきた身としては地味に嬉しい変更でした。
今までどう取っていたのか
配列の先頭の値がほしいだけなのに、これまでは reset() や end() を使うのが定番でした。
$arr = ['b' => 2, 'a' => 1];
$first = reset($arr); // 2
$last = end($arr); // 1
reset()とend()は値を返してくれるので一見便利なのですが、副作用として配列の内部ポインタを動かします。関数を呼んだ後にその配列でcurrent()やnext()を使う処理があると、意図せず狂うことがあります。$arr[0]で済ませようとしても、キーが数値0とは限らない連想配列では通用しません。array_key_first()やarray_key_last()と組み合わせて $arr[array_key_first($arr)] と書くのが、これまでの安全策でした。
array_first()とarray_last()は何をしてくれるのか
PHP 8.5では、この「先頭の値」「末尾の値」を取るためだけの関数が標準で用意されました。シグネチャはシンプルです。
function array_first(array $array): mixed {}
function array_last(array $array): mixed {}
キーの並びに関係なく、配列の最初の値と最後の値をそのまま返します。空配列の場合はnullが返ります。
array_first([1, 2, 3]); // 1
array_last([1, 2, 3]); // 3
array_first(['a' => 2, 'b' => 1]); // 2
array_last(['a' => 2, 'b' => 1]); // 1
array_first([]); // null
やっていることは array_key_first()/array_key_last() を使ったワンライナーとほぼ同じですが、名前がそのまま用途を表しているので、コードを読むときに「先頭を取っているんだな」と一目で分かるのが利点です。
なぜポインタを動かさないことが重要なのか
reset()やend()との一番の違いは、呼び出しても配列の内部ポインタを一切動かさない点です。foreachの中でcurrent()やnext()を使ったループ処理をしている最中に、うっかり同じ配列に対してend()を呼んでしまい、ループの位置がずれてハマった経験がある人もいるかと思います。array_first()やarray_last()にはその心配がありません。読み取り専用で値を見るだけなら、この2つを使っておくほうが安全です。
使うときに気をつけたいこと
1つだけ注意点があります。array_first()もarray_last()も、空配列のときと、値そのものがnullのときの両方でnullを返す点です。区別が必要な場面では、次のように先にisset()やarray_key_first()で存在確認をしてください。
$arr = [null, 2, 3];
$value = array_first($arr); // null(値がnullなので)
if ($arr === []) {
// 空配列だった場合の処理
} else {
// 先頭の値(nullも含む)を使う処理
}
また、この2つはPHP 8.5以降でしか使えません。8.4以前をサポートする必要があるなら、array_key_first()/array_key_last()を使った自前の関数を用意するか、ポリフィルパッケージを使うことになります。すでに自分のコードやライブラリでarray_first/array_lastという名前の関数を定義している場合は、名前の衝突で致命的エラーになるので、8.5へのアップグレード前に確認しておくと安心です。
まとめ
array_first()とarray_last()は、機能としては小さな追加ですが、reset()やend()にまつわる「ポインタが動いてしまう」という地味な事故を防いでくれます。名前もそのまま意図を表しているので、コードレビューでも読みやすくなる変更だと思います。PHP 8.5に上げる機会があれば、reset()/end()で済ませていた箇所を置き換えていくとよさそうです。
よくある質問
Q. array_first()はキーが数値でない連想配列でも使えますか?
A. 使えます。キーの型や並びに関係なく、配列の中で最初に格納されている値を返します。
Q. array_first()が返したnullは、空配列だからですか、値がnullだからですか?
A. 戻り値だけでは区別できません。区別したい場合は、事前に $arr === [] などで空配列かどうかを確認してください。
Q. PHP 8.4以前でも同じことをしたいのですが。
A. array_key_first($arr)やarray_key_last($arr)で得たキーを使い、$arr[array_key_first($arr)]のように書けば同じ結果になります。自前で同名の関数を定義してポリフィルにする方法もあります。