はじめに
こんばんは。
緊急事態宣言でましたね。
ぼくは、以前同様家族で引きこもる形になると思います。
今回は触ろう触ろうと思って触ってなかったphp8を触ってみます。
phpマニュアルみながら、「使いそうだな〜」と思った機能を厳選して触ってみます。
本題
名前付き引数
引数が何個も多いようなメソッドや関数で、他の引数は別に指定しなくていいんだけど、ここの引数だけ指定したいみたいなニーズあると思うのですが、そんなときにこれ便利ですね。
<?php function fn_example(?int $foo = null, ?int $bar = null, ?int $baz = null, ?string $hoge = null, ?string $fuga = null) { var_dump($foo); var_dump($bar); var_dump($baz); var_dump($hoge); var_dump($fuga); } # php8未満 fn_example(null, null, null, null, 'aaaaa'); # php8以降 fn_example(fuga: 'aaaaa') /* > # php example1.php NULL NULL NULL NULL string(5) "aaaaa" */
アトリビュート
読んだときに、アノテーションとどう違うのかよくわからなかったのですが、こちらのブログで解説されていました。
パフォーマンスの向上
アノテーションの Doc コメントは文字列で取得することしか出来ず、パースするのに時間がかかります。そのため doctrine/annotations では一度取得したアノテーションをキャッシュすることが可能になっています。 Attributes の形式だと Reflection から取得出来るようになるので、パースの時間を省略することが出来ます。
取得容易性の向上
ReflectionClass::getAttributes() などのメソッドで、 Reflection から簡単に Attributes を取り出せるようになったので、 Doc コメントアノテーションに比べ取得が格段に簡単になります。
明確な型インスタンス
Attribute::newInstance() メソッドを使って Attribute クラスのインスタンスを生成することが出来るため、明確な型を使って利用ロジックを実装することが出来ます。そのため、静的解析やIDE補完などが容易に行えるようになります。
PHP Core や Extensions での利用
PHP拡張の実装時に Attributes を使うことによって、拡張機能により実装された Attributes をアプリケーションコードに落とし込むことが出来るようになります。
個人的には パフォーマンスの向上と取得容易性の向上がアツいと思いました。
php マニュアルそのまま試してみましたが、まだ、実際の実装時にどこまで活用していけるかは想像できていません。
<?php #[Attribute] class MyAttribute { public $value; public function __construct($value) { $this->value = $value; } } #[MyAttribute(value: 1234)] class Thing { } function dumpAttributeData($reflection) { $attributes = $reflection->getAttributes(); foreach ($attributes as $attribute) { var_dump($attribute->getName()); var_dump($attribute->getArguments()); var_dump($attribute->newInstance()); } } dumpAttributeData(new ReflectionClass(Thing::class)); /* string(11) "MyAttribute" array(1) { ["value"]=> int(1234) } object(MyAttribute)#3 (1) { ["value"]=> int(1234) } */
コンストラクタのプロモーション
クラスのコンストラクタで引数をもらって、その引数をプロパティに詰め替えるとかってよくやってると思いますが、それがぐんと簡単になります。
<?php # php8未満 class Foo { public function __construct(int $aaa, int $bbb, int $ccc, int $ddd, int $eee) { $this->aaa = $aaa; $this->bbb = $bbb; $this->ccc = $ccc; $this->ddd = $ddd; $this->eee = $eee; } public function toArray(): array { return [ $this->aaa, $this->bbb, $this->ccc, $this->ddd, $this->eee, ]; } } # php8以降 class Foo { public function __construct( private int $aaa, private int $bbb, private int $ccc, private int $ddd, private int $eee ){} public function toArray(): array { return [ $this->aaa, $this->bbb, $this->ccc, $this->ddd, $this->eee, ]; } } $foo = new Foo(10, 20, 30, 40, 50); var_dump($foo->toArray()); /* # php example3.php array(5) { [0]=> int(10) [1]=> int(20) [2]=> int(30) [3]=> int(40) [4]=> int(50) } */
union型
int か floatで来るみたいなときに、どっちにも対応したいからとりあえず stringでみたいな逃げ方をしなくても良くなります。
<?php class Item { private int|float $rate; public function __construct(private string $itemName, private int $price) {} public function setRate(int|float $rate): void { $this-> rate = $rate; } public function calc(): int|float { return $this->price * $this->rate; } } $item1 = new Item("toybox1", 50); $item1->setRate(10); var_dump($item1->calc()); $item2 = new Item("toybox2", 55); $item2->setRate(8.9); var_dump($item2->calc()); /* # php example4.php int(500) float(489.5) */
match式
switchに似てますが、より厳密によりシンプルに書けるみたいです。
match式が出てきたことにより match
が予約語になってます。
<?php $type = 'red'; echo match ($type) { 'blue' => 'BLUE', 'green' => 'GREEN', 'red' => 'RED', 'yellow' => 'YELLOW', }; > RED
switchだとネストも激しくなるし、あと厳格な比較ができないのが難点だったので、ifを多用していたのですが、これですごくシンプルにかけます。
nullsafe 演算子
個人的に激アツ機能です。
取得先のオブジェクトがnullでメソッドチェーンでつなげてやってしまい、 PHP Fatal error: Uncaught Error: Call to a member function xxxx() on null
みたいなってめちゃめちゃ経験ありますよね。。
if文で大体回避するかもしれませんが、それを解消してくれます。
ただ、多様してると追いにくくなりそうな気もしていますので、使い所は気をつけないとと思いました。
<?php class User { public function __construct(private string $name, private int $age){} public function name(): string { return $this->name; } } class UserRepository { private array $userList = [ 1 => ['name' => 'AAA', 'age' => 30], 2 => ['name' => 'BBB', 'age' => 35], ]; public function findUser(int $id): ?User { if (!isset($this->userList[$id])) { return null; } return new User( $this->userList[$id]['name'], $this->userList[$id]['age'], ); } } $repository = new UserRepository(); var_dump($repository->findUser(1)?->name()); var_dump($repository->findUser(2)?->name()); var_dump($repository->findUser(3)?->name()); /* # php example6.php string(3) "AAA" string(3) "BBB" NULL */
throw が式として使える
地味に嬉しい機能でした。 シンプルに書けるようになります。
例えば試してないけど、こんな感じにもかけるのかな?
<?php $this->User->save(); $userId = $this->User->getLastInsertId() ?? throw new UserRegisterException('ユーザーを登録できませんでした。');
str_contains()の実装
文字列が含まれているかを調べる関数です。
もともと php8未満でも再現可能ですが、専用の関数としてできて扱いやすくなった印象ですかね?
var_dump(str_contains("初めまして!! Kojirockですよ!", "Kojirock")); // true var_dump(str_contains("初めまして!! Kojirockですよ!", "kojikoji")); // false
終わりに
ほかにも新機能色々とあるみたいなのですが、個人的に使いやすそうなのをピックアップしてみました。
php7 -> php8のバージョンアップはそこまで苦ではないと思いますが、使用しているFWのバージョンアップのほうが鬼門ですよね。
現場からは以上です。