PHP8.2にバージョンアップしてもっと型表現を豊かにしよう PHPカンファレンス沖縄2023 Akito.Tsukahara
PHP8.2からtrue/false型が実装されることはご存知でしょうか? どのようにPHPの型表現がさらに豊かになったのか紹介させていただきます。
目次 ・PHP8.2で新しくできること ・型表現を豊かにしてみよう ・バージョンアップしてみた ・まとめ
Copyright© M&AクラウドPHP8.2にバージョンアップしてもっと型表現を豊かにしようPHPカンファレンス沖縄2023 Akito.Tsukahara
View Slide
Copyright© M&Aクラウド 2⾃⼰紹介塚原彰仁AkitoTsukahara株式会社M&AクラウドAkitoTsukaharaakito_tsukahara
Copyright© M&Aクラウドこの発表でお話しすることは?今⽇話すこと● PHP8.2で新しくできること● 型表現を豊かにしてみよう● バージョンアップしてみた● まとめ
Copyright© M&AクラウドPHP8.2で新しくできることPHP8.2● 2022/12/08にPHP8.2がリリース🎉
Copyright© M&AクラウドPHP8.2で新しくできること新機能💡● true/false/nullが独⽴した型で利⽤可能にfunction alwaysTrue(): true{return true;}function alwaysFalse(): false{return true;}function alwaysNull(): null{return null;}function alwaysTrue(): bool{return true;}function alwaysFalse(): bool{return true;}function alwaysNull(): ?string //静的解析でエラー{return null;}PHP8.2~ ~PHP8.1
Copyright© M&AクラウドPHP8.2で新しくできること新機能💡● 選⾔標準形 (Disjunctive Normal Form) 型が使えるようにpublic function bar((A&B)|null $entity) {return $entity;}public function bar(mixed $entity) {if ((($entity instanceof A) &&($entity instanceof B))|| ($entity === null)) {return $entity;}throw new Exception('Invalid entity');}PHP8.2~ ~PHP8.1
Copyright© M&AクラウドPHP8.2で新しくできること新機能💡● クラスにreadonlyを指定することができるようにreadonly class OkinawaIsland {public function __construct(public string $name,public int $population,public int $area,) {}}class OkinawaIsland {public function __construct(public readonly string $name,public readonly int $population,public readonly int $area,) {}}PHP8.2~ ~PHP8.1
Copyright© M&AクラウドPHP8.2で新しくできること新機能💡● バックトレースにおけるセンシティブなパラメータの出⼒抑制ができるfunction getUser($name,$email,#[SensitiveParameter] $password) {throw new Exception('Error');}getUser('taro', '[email protected]', 'taropass');function getUser($name,$email,#[SensitiveParameter] $password) {throw new Exception('Error');}getUser('taro', '[email protected]', 'taropass');PHP8.2~ ~PHP8.1
Copyright© M&Aクラウド型表現を豊かにしてみよう9
Copyright© M&Aクラウド型表現を豊かにしてみようその1 Nullオブジェクトパターンで使う👻readonly class Speaker {public function __construct(private Name $name,private Email $email,private XAccountInterface $xAccount,){}public function toArray(): array {return ['name' => $this->name->getValue(),'email' => $this->email->getValue(),'x_account' => $this->xAccount->isUndefined()? null : $this->xAccount->getValue(),];}}Speaker.php
Copyright© M&Aクラウド型表現を豊かにしてみようinterface Undefinedable{public function isDefined(): bool;public function isUndefined(): bool;}その1 Nullオブジェクトパターンで使う👻Undefinedable.phpinterface XAccountInterface extends Undefinedable {}XAccountInterface.php
Copyright© M&Aクラウドclass UndefinedXAccount extends XAccountInterface {public function isDefined(): false {return false;}public function isUndefined(): true {return true;}}型表現を豊かにしてみようその1 Nullオブジェクトパターンで使う👻class XAccount extends XAccountInterface {public function isDefined(): true {return true;}public function isUndefined(): false {return false;}}UndefinedXAccount.phpXAccount.php
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(引数)🧩interface A {}interface B {}class ClassAB implements A,B {}class TryDNFArgument {public function getAandB((A&B) | null $hoge): void {echo 'Call getAandB';}}$tryDnfArg = new TryDNFArgument();$tryDnfArg->getAandB(new ClassAB()); //Call getAandBA&B ⬅ ClassAB OK
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(引数)🧩interface A {}interface B {}class ClassAB implements A,B {}interface C {}class ClassABC implements A,B,C {}class TryDNFArgument {public function getAandB((A&B) | null $data): void {echo 'Call getAandB';}public function getAB(ClassAB | null $data): void {echo 'Call getAB';}}……$tryDnfArg = new TryDNFArgument();$tryDnfArg->getAandB(new ClassAB()); //Call getAandB $tryDnfArg->getAB(new ClassAB()); //Call getAB$tryDnfArg->getAandB(new ClassABC()); //Call getAandB$tryDnfArg->getAB(new ClassABC()); //Fatal Errorサンプルコード引数の場合は、A&B ⬅ ClassABC OKClassAB ⬅ ClassABC Error
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(引数)🧩A泳げるインターフェースBえらで呼吸インターフェースA泳げるインターフェースA&B泳げて水中で呼吸できるC魚類
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(引数)🧩A泳げるインターフェースBえらで呼吸インターフェースC肺で呼吸インターフェースA&B泳げて水中で呼吸できるC魚類❌
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(引数)🧩A泳げるBえらで呼吸C肺で呼吸A&B泳げて水中で呼吸より広い型を受け入れられる反変性※DNF型だから反変性を持つ訳ではなく、型表現の幅が広がるという意味です。「共変性と反変性」の完全サポートはPHP7.4から
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(戻り値)🧩interface A {}interface B {function callB(): void;}class ClassAB implements A,B {function callB(): void {var_dump('callB');}}class TryDNFReturnValue {public function getAandB(): (A&B)| null {return new ClassAB();}}……$tryDnfRV = new TryDNFReturnValue();$tryDnfRV->getAandB()?->callB(); //CallB
Copyright© M&Aクラウド……class TryDNFReturnValue {public function getAandB(): (A&B)| null {return new ClassABC();}}$tryDnfRV = new TryDNFReturnValue();$tryDnfRV->getAandB()?->callB(); //CallB$tryDnfRV->getAandB()?->callC(); //CallC型表現を豊かにしてみようinterface B {function callB(): void;}interface C {function callC(): void;}class ClassABC implements A,B,C {function callB(): void {var_dump('callB');}function callC(): void {var_dump('callC');}}その2 DNF型で型パズルする(戻り値)🧩ただしPHPStanではエラーになる。Call to an undefined method A&B::callC().サンプルコード
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(戻り値)🧩会場までの行き方ガイドA&B&C&DA飛行機インターフェースBモノレールインターフェースCバスインターフェースD徒歩インターフェース羽田からの案内が記載ないちゃー
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(戻り値)🧩会場までの行き方ガイドB&C&DA飛行機インターフェースBモノレールインターフェースCバスインターフェースD徒歩インターフェース❌羽田からの案内は不要うちなーんちゅ
Copyright© M&Aクラウド型表現を豊かにしてみようその2 DNF型で型パズルする(戻り値)🧩会場までの行き方ガイド❌ 受け入れる型を絞る共変性※DNF型だから共変性を持つ訳ではなく、型表現の幅が広がるという意味です。「共変性と反変性」の完全サポートはPHP7.4から
Copyright© M&Aクラウドバージョンアップしてみた23
Copyright© M&Aクラウドよし!バージョンアップだ!● PHP8.1からのアップデートだし、⼤きな差分はないのでは?● バージョンを上げてみて、テストを動かしてみる!!○ ?⾝に覚えのない箇所でエラーがちらほら?バージョンアップしてみた
Copyright© M&Aクラウドバージョンアップしてみたfunction calcDay(int $timestamp, int $day){date_default_timezone_set('Asia/Tokyo');$calcTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . sprintf('+%s day', $day));echo date('Y-m-d H:i:s',$calcTimestamp);}$phpカンファレンス沖縄2023当日 = 1694790000; //'2023-09-16 00:00:00';calcDay($phpカンファレンス沖縄2023当日, -3);ここで問題です!● 以下の処理の実⾏結果はどうなる?(~PHP8.1)~PHP8.1
Copyright© M&Aクラウドバージョンアップしてみたfunction calcDay(int $timestamp, int $day){date_default_timezone_set('Asia/Tokyo');$calcTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . sprintf('+%s day', $day));echo date('Y-m-d H:i:s',$calcTimestamp);}$phpカンファレンス沖縄2023当日 = 1694790000; //'2023-09-16 00:00:00';calcDay($phpカンファレンス沖縄2023当日, -3);結果は...2023-09-13 00:00:00~PHP8.1
Copyright© M&Aクラウドバージョンアップしてみたfunction calcDay(int $timestamp, int $day){date_default_timezone_set('Asia/Tokyo');$calcTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . sprintf('+%s day', $day));echo date('Y-m-d H:i:s',$calcTimestamp);}$phpカンファレンス沖縄2023当日 = 1694790000; //'2023-09-16 00:00:00';calcDay($phpカンファレンス沖縄2023当日, -3);さらにもう⼀問!● 以下の処理の実⾏結果はどうなる?(PHP8.2~)PHP8.2~
Copyright© M&Aクラウドバージョンアップしてみたfunction calcDay(int $timestamp, int $day){date_default_timezone_set('Asia/Tokyo');$calcTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . sprintf('+%s day', $day));echo date('Y-m-d H:i:s',$calcTimestamp);}$phpカンファレンス沖縄2023当日 = 1694790000; //'2023-09-16 00:00:00';calcDay($phpカンファレンス沖縄2023当日, -3);結果は...2023-09-19 00:00:00PHP8.2~サンプルコード
Copyright© M&Aクラウドバージョンアップしてみた原因はどこなんだ...👀
Copyright© M&Aクラウドバージョンアップしてみたfunction calcDay(string $timestamp, int $day){date_default_timezone_set('Asia/Tokyo');$calcTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . sprintf('+%s day', $day));echo date('Y-m-d H:i:s',$calcTimestamp);}$phpカンファレンス沖縄2023当日 = 1694790000; //'2023-09-16 00:00:00';calcDay($phpカンファレンス沖縄2023当日, -3);おやおや...🤔PHP8.2~+-3 day
Copyright© M&Aクラウドバージョンアップしてみたつまりどうなるのか?🙄+-3 dayPHP8.2以降は「+3 day」として処理されるPHP8.2以前は「-3 day」として処理される
Copyright© M&Aクラウドバージョンアップしてみた下位互換性のない変更点は要チェック👀
Copyright© M&Aクラウドおまけ33
Copyright© M&AクラウドおまけもうすぐPHP8.3リリースですね🙌● 予定通り⾏けば 2023/11/23 リリース予定
Copyright© M&Aクラウドまとめ35
Copyright© M&Aクラウドまとめ● PHP8.2から新しく実装機能がたくさん● PHPのバージョンを上げて、型表現を豊かにしよう○ true/false/nullが独⽴した型で使える○ 選⾔標準形 (Disjunctive Normal Form) 型が使える● バージョンを上げる時には「下位互換性のない変更点」をチェック● もうすぐPHP8.3がリリースされる
Copyright© M&Aクラウド参考資料● https://www.php.net/releases/8.2/ja.php● https://www.prime-strategy.co.jp/column/archives/column_5253● https://qiita.com/rana_kualu/items/fc4b02e2daaf102aa92f● https://www.cyberer.net/2020/03/covariant-contravariant.html● https://www.php.net/manual/ja/language.oop5.variance.php● https://zukan.com/fish/internal2961
Copyright© M&Aクラウドありがとうございました!38