【PHP8.5】配列の最初と最後の値を取得する関数が公式実装される

かつてPHP7.3で[`array_key_first()`が実装](https://qiita.com/rana_kualu/items/a80ef40547496192de86)されました。

これは配列の最初のキーを取得する関数です。
このとき同時に配列の最初の値を取得する関数`array_value_first()`も提案されたのですが、こちらは何故か却下されました。
従って現在、配列の最初の値を取得するにはこうする必要があります。

```php
$array_value_first = $array[array_key_first($array)];
```

なんだかわかりにくいし`$array`を2回も使っています。

ということで、[配列の最初と最後の値を取得するRFC](https://wiki.php.net/rfc/array_first_last)が提案されました。
現在投票中ですが、ほぼ間違いなく受理され、PHP8.5から使用可能になります。

以下は該当のRFC、[array_first() and array_last()](https://wiki.php.net/rfc/array_first_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

```php
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](https://wiki.php.net/rfc/array_key_first_last)で否決された主な問題点は、失敗時の動作に関するものでした。

空の配列に対しては例外をスローすべきでしょうか、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年のディスカッションスレッド](https://externals.io/message/121313)では、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

使用例をいくつか示します。

```php
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

[プルリクエスト](https://github.com/php/php-src/pull/18210)。

## References

[ディスカッションスレッド](https://externals.io/message/127047)。

## Prior Work

・[提案](https://externals.io/message/121313)
・[`array_key_first()`のRFC](https://wiki.php.net/rfc/array_key_first_last)
・[却下された機能](https://externals.io/message/102663)


# 感想

単なるシンタックスシュガーであって、できることはこれまでと全く変わらないのですが、書式が極めてわかりやすくなりますね。
この関数の追加によって、配列操作関数はおおむね完成に達したのではないでしょうか。

まあ配列は基本的に`foreach`とかで全件隔てなく扱うべきで、順序に依存するような書き方はすべきでないと思いますが。