【PHP】PHP8.4で注目してる新機能の紹介
PHP8.4のリリースが2024年11月21日に予定されています。前回のサポート期間の変更により、今回のリリースと同時にサポートが終了するバージョンはありません。
例によって新機能が増え、間違いを起こしやすい書き方や関数が非推奨になっています。新機能の全体像は次のリンク先の記事でまとめられています。毎年ありがとうございます。
【PHP8.4】PHP8.4の新機能 #rfc – Qiita
この記事ではその中から特に自分が使いそうな機能や関数を紹介します。
1. プロパティフック
まずはプロパティフックです。プロパティフックはプロパティの読み込みと書き込みに対してカスタムロジックを直接追加できる機能です。
従来プロパティへの値の設定や取得を制御するには、ゲッターやセッターといったメソッドを個別に実装して読み書きの口を制限するか get や set を書く必要がありました。しかしそれによってコードが散らばり、保守性が下がるケースがありました。プロパティフックではプロパティに対する処理をその場で記述できるため、見通しが良くなります。
以下はその使用例です:
<?php
class PhoneNumber
{
public function __construct(string $v) {
$this->v = $v;
}
public string $v {
set {
// setのブロックの中ではどこからともなく $value という変数が出てきます。
// $value にはプロパティにセットされる予定の値が入っています
if (preg_match("/\A[0-9\-]+\z/", $value)) {
$num_len = strlen(str_replace('-', '', $value));
if (10 <= $num_len && $num_len <= 11) {
$this->v = $value;
return;
}
}
throw new ValueError('電話番号は10桁から11桁の数字をハイフン区切りで指定してください。');
}
get {
// getの中ではプロパティの生の値を扱います。
return str_replace('-', '', $this->v);
}
}
}
// 使用例
echo (new PhoneNumber('06-6012-3456'))->v . PHP_EOL; // 0660123456
echo (new PhoneNumber('06-6012-345'))->v . PHP_EOL; // ValueError 電話番号は10桁から11桁の数字をハイフン区切りで指定してください。
プロパティフックを使うことで、プロパティに対するアクセスの管理を直接行えるため、可読性が向上します。また、setのみやgetのみのフックも可能です。例えば、次のように、プロパティに対して制約を個別に設定できます:
<?php
class Member {
public function __construct(array $values) {
$this->member_number = $values['member_number'];
$this->name = $values['name'];
}
public string $member_number {
get {
return str_pad($this->member_number, 6, '0', STR_PAD_LEFT);
}
}
public string $name {
set {
if (strlen($value) === 0) {
throw new ValueError("名前は空にできません");
}
$this->name = $value;
}
}
}
$member = new Member(['member_number' => 123, 'name' => '浜松太郎']);
echo $member->member_number . PHP_EOL; // 000123
echo $member->name . PHP_EOL; // 浜松太郎
new Member(['member_number' => 123, 'name' => '']); // ValueError
この機能は、例えば会員番号のようにPHPの基本的な型では表現しきれない値を扱うときに特に有用です。このやり方はむやみやたらに使うとファイルが際限なく増え、ほとんど意味のないクラスを作り出してしまうこともあり(単なるstringのラッパークラスなど)、可読性が下がりやすいので注意が必要ですが、適切に使うことでコードの保守性を向上させることができます。
2. 非対称可視性
次に紹介するのは、非対称可視性(Asymmetric Visibility)です。
PHP:RFC: Asymmetric Visibility v2
非対称可視性を使うと、プロパティの読み取りと書き込みの可視性を異なるレベルに設定できます。例えば、プロパティを外部からは読み取り可能であっても、書き込みはクラス内部でのみ許可する、といったことができます。
<?php
class Foo {
public private(set) string $bar = 'baz';
}
$foo = new Foo();
var_dump($foo->bar); // "baz"
$foo->bar = 'beep'; // Visibility error
これは外部から値の変更を防ぎたい場面では非常に便利です。従来はこの目的でprivateやprotectedを使用して都度外部から参照したい値がある時はゲッターを作っていましたが、この新機能でコードの意図がより明確になります。
3. 括弧なしインスタンス化
PHP 8.4では、括弧なしでのnewも可能になりました。
PHP:RFC: New without Parentheses
これにより、new を使ってクラスをインスタンス化する際に括弧を省略できるようになります。
// PHP8.4
$request = new Request()->withMethod('GET')->withUri('/hello-world');
// PHP8.3以前
$request = (new Request())->withMethod('GET')->withUri('/hello-world');
見た目は対して変わらないのですが、書く時の手間が明確に減って楽になります。今後はこの書き方をすることが主になるでしょう。
4. マルチバイト対応のトリム関数
最後に紹介するのは、**マルチバイト対応のトリム関数(mb_trim)**です。
これまで、日本語などマルチバイト文字列を扱う際に、trim()関数がうまく動作せず、先頭や末尾の特定の文字が消えてしまう問題がありました。特に全角スペースを削ろうとして先頭の文字を文字化けさせてしまうことが多かったです。そのため、mb_str_splitを使って独自にトリム処理を実装する必要がありましたが、PHP 8.4からはmb_trim()が標準で提供されるため、この問題が解決されます。実際の使用例が次です。デフォルトで全角スペースも削ってくれます。
<?php
var_dump(mb_trim(' あ い あ '));
// string(13) "あ い あ"
これにより、マルチバイト文字列の前後の空白や特定の文字を簡単に取り除けるようになり、よりスムーズに日本語を扱うことが可能になります。
まとめ
紹介したようにPHP8.4の新機能は更にコーディングを楽にしてくれそうです。最後にプロパティフックと非対称性とnewの括弧の省略を組み合わせた使用例を示します。
class IndexCursor
{
// プロパティフックと非対称性を使ったプロパティの定義
// 読み取りは自由で外部から書き込みは禁止。書き込み時は0以上の整数でなければならないというルールを定義しています。
public function __construct(public protected(set) int $idx = 0{
set {
if ($value < 0) {
throw new ValueError('idxは0以上の整数で指定してください。');
}
$this->idx = $value;
}
}){}
public function increment(): self
{
$this->idx++;
return $this;
}
}
// 括弧の省略
$cursor = new IndexCursor()->increment();
var_dump($cursor->idx); // int(1)
$cursor->increment();
var_dump($cursor->idx); // int(2)