Upgrade to Pro — share decks privately, control downloads, hide ads and more …

レガシープロジェクトで メタプログラミングを使った PHPStan静的解析Level上げ - PHP Conference 2020

taki komiyama
December 09, 2020

レガシープロジェクトで メタプログラミングを使った PHPStan静的解析Level上げ - PHP Conference 2020

半年くらい前にPHPStan静的解析をはじめました。

徐々に対象ファイルを増やし、現在では2000超のファイルをスキャンしています。
level0(不明なclass、関数の参照などの基本的なチェック)から段階的に厳しくして、level2(未知の全ての関数のチェック、PHPDocの検証)に上がります。

レガシープロジェクトにありがちな名前空間がない、PHPDocがないといった問題を、nikic/PHP-Parserを武器に乗り越えてきました。

PHPDocで補いきれない部分は、自作のYii1フレームワーク用のPHPStan拡張で解析しています。

レガシープロジェクトで、静的解析を進めてきた方法を紹介します。

taki komiyama

December 09, 2020
Tweet

More Decks by taki komiyama

Other Decks in Programming

Transcript

  1. 小宮山 太樹 ( こみやま たき ) 弁護士ドットコム株式会社 在籍2年半 PHP歴 4年ぐらい

    コンテナアプリ開発が得意 Code for JapanでCI/CD & AWSまわりで活動 Bengo4.com 自己紹介
  2. PHPStan (PHP Static Analysis Tool) • slovemat/coding-standardの開発者が作成 • PHPDocがなくても解析可能 •

    composer autoloadなど一部PHPを実行するので解析が早い • Level 0 ~ 8まで段階的なルール設定 • エラー無視設定が柔軟 Bengo4.com
  3. Bengo4.com 2067/2067 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------ ----------------------------------------------------------------------- Line models/Category.php ------ -----------------------------------------------------------------------

    656 PHPDoc tag @param has invalid value ($actionId): Unexpected token "$actionId", expected type at offset 123 656 PHPDoc tag @param has invalid value ($additionalParams): Unexpected token "$additionalParams", expected type at offset 147 656 PHPDoc tag @param has invalid value ($controllerId): Unexpected token "$controllerId", expected type at offset 95 ------ ----------------------------------------------------------------------- [ERROR] Found 3 errors PHPStan エラー
  4. Bengo4.com - message: "#^Cannot access property \\$email on array\\.$#" count:

    2 path: ../../components/OpenID.php - message: "#^Access to an undefined property CAction\\:\\:\\$\.*\\.$#" path: ../../components/helpers/ReplaceStringToLinkHelper.php PHPStan エラー無視設定
  5. PHPStanの型推論, PHPDoc構文チェック • PHPDocを優先して理解する ◦ PHPDocの型省略は不可 ◦ 基本的な型、記法は全て使える ◦ nullableやユニオン記法etc..

    • PHPDocと実装の差も検知 Bengo4.com /** * @param int $id * @return static|null */ public function create($id) ....... * @return string|false
  6. PHPStanの継承時の型推論の限界 • 継承元のclassの関数呼び出し ◦ 戻り値が、self or staticだと ◦ 継承元classのままでエラー •

    PHPDoc等で補完してあげる必要あり Bengo4.com * @return static ....... public function find(): self ....... /** @var Model[] $result */ $result = $this->create();
  7. PHPStanでviewを解析 • class定義がなくても解析できる • Controllerから変数を渡している ◦ 未定義変数エラー ◦ PHPDocを書けば解析可能 •

    PHPStan v0.12.30以上 Bengo4.com <?php /** @var Yii\web\View $this */ /** @var ActiveForm $form */ <div> <p><?= $this->title; ?></p>
  8. Bengo4.com <?php function test($foo) { var_dump($foo); } AST(抽象構文木)変換 array( 0:

    Stmt_Function( byRef: false name: Identifier( name: test ) params: array( 0: Param( type: null
  9. Bengo4.com namespace application\controllers\prefecture; use CAction; use Yii; class indexAction extends

    CAction { public function run() { $userId = Yii::app()->user->getId(); After
  10. 技術負債: Active RecordのPHPDocがない • 事象 ◦ PHPStanのLevel1から、Active Recordで未定義エラー大量発生 • 原因

    ◦ Active Recordのカラムデータはマジックメソッド参照 ◦ プロパティ等の実態が定義されていない、PHPDocもない Bengo4.com
  11. Bengo4.com /** * class News */ class News extends CActiveRecord

    { public function relations() { return [ 'NewsTopImage' => ... Before
  12. Bengo4.com /** * class News * * @property int $id

    * @property string|null $name * @property NewsTopImage|null $HeadImage * @property NewsTopImage[] $Image */ class News extends CActiveRecord After
  13. 2000ファイル超でLevel 2達成 • Level 0 ~ 1(エラー 約250件) ◦ Active

    RecordのPHPDoc付与、名前空間の付与 etc... ◦ エラーを99%修正、4個だけエラー無視リストへ • Level 2(エラー 約2500件) ◦ PHPStan拡張の作成、PSR違反のPHPDocの修正 etc.. ◦ エラーを約70%修正、残りはベースラインで無視リストへ Bengo4.com
  14. 改善2 エラーの減少 • 既存のエラーの減少 ◦ Level 0 ~ 2 のエラー修正の効果

    ◦ Noticeエラー、潜在バグの減少 • 新しいエラーの減少 ◦ CIのPHPStan失敗で、 リリース前に気づけた Bengo4.com const WORD_MAX = 20; .... $newKeyword = array_merge( $addKeywords, $keyword ); if ($newKeyword > self::WORD_MAX) { $newKeyword = array_slice(
  15. これから • ベースラインを駆使してPHPStanのLevel 3 ~を目指す ◦ PHPStan拡張の開発も継続 ◦ viewを含め対象を拡大 •

    並行でユニットテストも増やす • 型づけの強化 • フレームワークの載せ替えなど、大規模な改修につなげる Bengo4.com