超PHPerになろう

Enjoy PHP Programming

PHPStan 1.12: PHPStan 2.0への道

この記事はPHPStan開発者のOndřej Mirtesによって2024年8月27日にPHPStan Blogに書かれた記事を翻訳したものです。

phpstan.org

最初のPHPStan 1.0リリースから3年が経ち、PHPStan 2.0が近づいてきました。新しいメジャーバージョンのアイデアのリストを精査した結果、いくつかを前倒しして1.xシリーズでリリースし、Bleeding Edge 設定トグルの背後に隠して、PHPStanユーザーにより早く楽しんでもらえるようにしました。

これは PHPStan 1.12に限ったことではなく、1.0以降に当てはまります。Bleeding Edgeを有効にすると、基本的には未来に生きることになります。次のメジャーバージョンですべてのユーザーに対して有効になる新しいルールと動作の変更を利用できます。これがアーリーアダプターへの特典です。

ここにひとつの方程式があります。

PHPStan 2.0 = PHPStan 1.12 + Bleeding Edge + BC breaks

PHPStan 1.12にアップグレードしてBleeding Edgeを有効にすると、今日からPHPStan 2.0の準備がほぼ整います。

しかし、将来についてはこれくらいにしておきましょう。本日の1.12リリースの新機能は次のとおりです。

正規表現の正確な型推論の一般提供

これは、Bleeding Edgeとしてリリースされたものの、次のメジャーバージョンを待たずに一般提供することにした珍しいケースです。複雑な機能のため、バグを取り除くために段階的なロールアウトが必要でした。

この機能は1.11.6で最初に導入され、それ以来数十のPull Requestによって改良されてきましたが、その目的は preg_match() および関連する関数からの、リファレンス引数 $matches の正確な型を判断することです。

if (preg_match('/Price: (?<currency>£|€)\d+/', $s, $matches, PREG_UNMATCHED_AS_NULL)) {
    // array{0: string, currency: non-empty-string, 1: non-empty-string}
    \PHPStan\dumpType($matches);
}

Composerで有名なMarkus StaabとJordi Boggianoはこの作業に熱心に取り組みました。これにより、PHPStanの型推論の精度はまったく新しいレベルに向上しました。

staabm.github.io

私が収集した情報によれば、この機能は非常にユニークで、多くのプログラミング言語では提供されていません。そして今、PHPでそれが実現しました!

PHPDoc タグの型チェックの盲点を修正

PHPStanはPHPDocの型に対して4つのカテゴリの健全性チェックを実行します:

  • 型の不足: レベル6以上で実行され、配列値の型、ジェネリック型、さらにオプションでcallableのシグネチャの指定を強制します
  • クラスの実在: 存在しないクラスとトレイトへの参照を検索します
  • 解決できない型: Foo::BARのような未定義定数参照やstring&intなどの不可能な交差から生じたneverボトム型を探します
  • ジェネリクス型チェック: ジェネリック型の健全性をチェックします。型内の型変数の数をクラス宣言の上の@templateの数と比較し、境界のサブタイプなどをチェックします

これらの型チェックは、サポートされているすべてのPHPDocタグに対して一貫しておらず、以下のタグに対してほとんどまたはすべてのチェックが欠落していました:

  • @param-out
  • @param-closure-this
  • @param-immediately-invoked-callable, @param-later-invoked-callable
  • @phpstan-self-out
  • @mixin
  • @property
  • @method
  • @extends, @implements, @use

PHPStan 1.12では、この問題を修正しました。間違った型や壊れた型は、全体的に一貫してチェックされます。これらは基本的に、ほとんどのコードベースで新しいエラーが報告される原因となる新しいルールなのでBleeding Edgeにのみ追加され、PHPStan 2.0ではすべてのユーザーに対して有効になります。

広すぎるプライベートプロパティタイプ

Bleeding Edgeへのもうひとつの追加機能として、何も影響を与えることなく、型をプライベートプロパティのユニオン型から削除できるかどうかをチェックします。

// 文字列が $this->a に割り当てられていない場合は、型から削除できます
private int|string $a;

PHPStanには関数とメソッドの戻り値の型に関するこのルールがすでにありましたが、今ではプロパティでも利用できるようになりました。

PHP 8.4ランタイムサポート

PHPStanで新しいPHPバージョンのサポートを実装するには、次の4つのフェーズがあります。

  1. PHPStanが新しいPHPバージョンで実行され、すべてのテストに合格することを確認する
  2. 新しいPHPバージョンで追加された関数と既存の関数の変更されたシグネチャを理解する
  3. 新しい構文と機能を理解することで、新しいPHPバージョン向けに記述されたコードを分析するときに誤ったエラーが除去される
  4. 新しい構文と機能に固有の新しいルールを実装する。新しいPHP機能のどの部分が扱いにくく、新しい静的分析ルールでチェックする必要があるかを検出できる

PHPStan 1.12では最初のフェーズ2までを実装しました。PHP 8.4で非推奨の通知なしに実行され、array_find()のような新しい関数を既に認識しています

新しい構文のため、残りはメジャーバージョンで実行する必要があるnikic/PHP-Parser v5 へのアップグレードに依存しています。

今後数か月間の私の計画は単純明快です。PHPStan 2.0を完成させてリリースし、その後PHP 8.4固有の機能に取り組むことです。


私とPHPStanの貢献者たちは、このリリースのために多大な労力を費しました。みなさんがこのリリースを本当に楽しんで、これらの新機能を活用していただけると幸いです。GitHubでみなさんのフィードバックをお待ちしています!


PHPStanが好きで、毎日使用していますか? GitHub SponsorsでPHPStanのさらなる開発をサポートすることを検討してください。本当にありがとう!

phpstan.org

訳者あとがき

ぬるっと出ましたね。PHPStan 1.12。PHPStan 1.11のときにもPHPStanの進化が意識されていましたが、いよいよ1.12を経て2.0が見えてきました。PHP-Parser v5の非互換はPsalmほどには辛くないのですが、PHP 8.4サポートのためにあまり遠くない時期(遅くても11月までには)PHPStan 2.0がお披露目されることになるでしょう。わくわくしますね…!