Quantcast
Channel: 日本語訳タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 236

【PHP8.2】false疑似型およびnull型が単独で使えるようになる

$
0
0
PHP8.0において実装されたunion型の導入過程において、false疑似型およびnull型が導入されました。 これらはunion型の一部としてのみ有効な型であり、単独で使用することはできません。 しかし、これを単独で使えるようにしようというRFCが提出されました。 投票期間は2022/03/12から2022/03/26であり、賛成38反対0の全会一致で可決されました。 従ってPHP8.2から単独型として使用可能になります。 以下は該当のRFC、Allow null and false as stand-alone typesの日本語訳です。 Allow null and false as stand-alone types Introduction nullはPHPのUnit型です。 falseはbool型のリテラルです。 現在nullを単独で型宣言として使うことはできません。 nullはUnit型であるという性質上、いかなる情報も保持することはできないためです。 ところで関数がエラーであることを示すために、返り値として歴史的にfalseが使われてきました。 これがUNION型においてfalse疑似型が導入された大きな理由です。 Motivation このRFCの動機です。 Type system completeness 型システムの完全性。 PHPは、PHP8.0でトップ型のmixed型を、PHP8.1でボトム型のnever型をサポートしました。 さらにPHP8.0でUNION型、PHP8.1で交差型という複合型もサポートしました。 PHPでUnit型を型付けできないのは、PHPに残されたわずかな欠陥です。 Edge case with regards to the literal type false false疑似型のエッジケース。 false疑似型はUNION型でのみ使用できますが、null|falseは許されないため完全ではありません。 現在のところ、このエッジケースはbool|nullと表現するしかありません。 これはtrueを返すかもしれないという誤った印象を与えるため、人間や静的解析において有益ではありません。 PHPの組込関数にもnull|falseを必要とするものがあり、一例としてはgmp_random_seedです。 Providing precise type information while satisfying LSP LSPを満たしたうえで正確な型情報を提供する。 親クラスでpublic function foo(): ?Tが定義されている場合に、子クラスでは必ずTを返すのであればpublic function foo(): Tと、より正確な型情報を返すことができます。 しかし逆はできません。 子クラスが必ずnullを返す場合は、public function foo(): nullとすることはできません。 この場合はシグネチャは元のままとし、返り値はドキュメントなどで示すしかありません。 PHPの組込関数ではSplFileObject::getChildren()等が該当します。 Proposal 型宣言が許される位置全てにおいて、false型とnull型を使用可能にする。 class Nil { public null $nil = null; public function foo(null $v): null { /* ... */ } } class Falsy { public false $nil = false; public function foo(false $v): false { /* ... */ } } Redundancy of ?null ?nullはコンパイルエラーになります。 これはPHPの現在の型宣言の解釈規則に沿ったものです。 Reflection リフレクションはサポートされます。 ただし、null|TはReflectionNamedTypeになりますが、null|falseはReflectionUnionTypeになります。 以下はどのように見えるかの例です。 function dumpType(ReflectionUnionType $rt) { echo "Type $rt:\n"; echo "Allows null: " . ($rt->allowsNull() ? "true" : "false") . "\n"; foreach ($rt->getTypes() as $type) { echo " Name: " . $type->getName() . "\n"; echo " String: " . (string) $type . "\n"; echo " Allows Null: " . ($type->allowsNull() ? "true" : "false") . "\n"; } } function test1(): null|false { } function test2(): ?false { } dumpType((new ReflectionFunction('test1'))->getReturnType()); dumpType((new ReflectionFunction('test2'))->getReturnType()); /* Type false|null: Allows null: true Name: false String: false Allows Null: false Name: null String: null Allows Null: true Type false|null: Allows null: true Name: false String: false Allows Null: false Name: null String: null Allows Null: true */ Example 以下はユニットテストの例です。 class User {} interface UserFinder { function findUserByEmail(): User|null; } class AlwaysNullUserFinder implements UserFinder { function findUserByEmail(): null { return null; } } 現在、上記のコードはエラーになります。 Fatal error: Null can not be used as a standalone type すなわち、findUserByEmail()はnullしか返さないにもかかわらず、User|nullという正しくない戻り値を設定しなければなりません。 ところが逆に静的アナライザはクラスを分析してfindUserByEmail()はUserクラスを返さないの警告を出してしまうため、さらなる混乱に見舞われます。 同様の問題は、falseのUNION型にも存在します。 Backward Incompatible Changes 後方互換性のない変更点はありません。 Proposed PHP Version PHP8.2 Implementation 感想 gmp_random_seedはnull|falseを返すのですが、これまでこの型を正しく表現することはできませんでした。 本RFCによって、その問題が解消されます。 また、常にfalseやnullを返す型も正確に表現可能になります。 この改善によって、PHPの型システムに存在していた僅かな穴がふさがれ、より表現力豊かになりました。 実はもうひとつだけ大きな穴が残っていまして、resource型と言うのですが、こいつは現行の型システムで表すことができません。 ただ、resouce型はどんどこ削除されてオブジェクトに置き換えられつつあるため、いずれは無くなると思います。 その時こそが、PHPの型システムの完成となるでしょう。 あとは(A&B)|Cみたいな型パズルの領域ですが、こんなのが役に立つとも思えないのですがどうでしょうかね。 交差型において進められていた実装も放棄されており、今後復活するかは微妙なところです。

Viewing all articles
Browse latest Browse all 236

Trending Articles