Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

巨大なモノリスの静的解析をレベルMaxにする方法

atsu.kg
October 02, 2021

 巨大なモノリスの静的解析をレベルMaxにする方法

普段開発しているコードベースでは PHPStan で静的解析をしているものの Lev.1 に留まっており、レベル上げをしようとすると大量のファイルがあって膨大な数のエラーが出てしまい手付かずの状態でした。静的解析が弱い分、ユニットテストや手動テストを主にして検証を行っていますが、手動テストの終盤で型エラーが起きてやり直しになるなど、非効率なことが起きていました。

その状況を改善すべく、モジュール毎に静的解析レベルを設定することで独立したメンテナンスを可能にし、比較的新しいモジュールからレベル上げをしていきました。
本セッションではその取り組みやつまずいたポイント等について紹介し、これから静的解析を強化していく方の参考になれば幸いです。

・解析対象と実行方法の整理
・レベル別静的解析の恩恵
・Laravel IDE Helper の問題点とその対応
・レベルを上げてからのコードの書き味

ide-helper で外部キー有無を考慮してPHPDocを生成する: https://github.com/barryvdh/laravel-ide-helper/pull/1231

atsu.kg

October 02, 2021
Tweet

More Decks by atsu.kg

Other Decks in Programming

Transcript

  1. PHPStanレベル別主な検査内容
 0: 未知のクラス・関数、 thisで呼ばれる未知のメソッド、メソッドや関数に渡される引数の数 
 1: 未定義の変数、未知のマジックメソッド、__call および __get を持つクラスのプロパティ

    
 2: 未知のメソッド、PHPDocバリデーション 
 3: 戻り値の型、プロパティに割り当てられた型 
 4: デッドコードチェック 
 5: メソッドや関数に渡される引数の型 
 6: 欠落した型宣言
 7: 部分的に間違った直和型の検査 
 8: nullable型のメソッド呼び出しやプロパティアクセス 

  2. LenetのPHPStan適用範囲
 0: 未知のクラス・関数、 thisで呼ばれる未知のメソッド、メソッドや関数に渡される引数の数 
 1: 未定義の変数、未知のマジックメソッド、__call および __get を持つクラスのプロパティ

    
 2: 未知のメソッド、PHPDocバリデーション 
 3: 戻り値の型、プロパティに割り当てられた型 
 4: デッドコードチェック 
 5: メソッドや関数に渡される引数の型 
 6: 欠落した型宣言
 7: 部分的に間違った直和型の検査 
 8: nullable型のメソッド呼び出しやプロパティアクセス 
 ごく一部の検査しかされていない

  3. 設定ファイルを分割する案
 PHPStanをレベルの数だけ実行
 phpstan analyse -c phpstan-level1.neon 
 phpstan analyse -c

    phpstan-level2.neon 
 共通設定
 phpstan-level1.neon
 level: 1
 paths: lib/Lenet/User
 includes
 レベル別設定
 phpstan-level2.neon
 level: 2
 paths: lib/Lenet/Cleaning
 phpstan.neon

  4. 共通設定
 phpstan-level1.neon
 level: 1
 paths: lib/Lenet/User
 includes
 レベル別設定
 PHPStanをレベルの数だけ実行
 phpstan-level2.neon


    level: 2
 paths: lib/Lenet/Cleaning
 phpstan.neon
 phpstan analyse -c phpstan-level1.neon 
 phpstan analyse -c phpstan-level2.neon 
 👍 シンプル
 👎 masterとの差分実行ができない
 不採用 設定ファイルを分割する案

  5. 差分実行の仕組み
 lib/Finance/File1.php
 lib/Lenet/Cleaning/File1.php
 - level: 1
 paths: lib/Finance
 - level:

    2
 paths: lib/Lenet/Cleaning
 照合ロジック
 Lev.1: lib/Finance/File1.php
 Lev.2: lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Finance/File1.php 
 phpstan analyse -c phpstan.neon --level=2 lib/Lenet/Cleaning/File1.php 
 差分
 対応表
 git diff でmasterと開発ブランチの 差分ファイルを出力

  6. 差分実行コマンドを作成
 lib/Finance/File1.php
 lib/Lenet/Cleaning/File1.php
 - level: 1
 paths: lib/Finance
 - level:

    2
 paths: lib/Lenet/Cleaning
 照合ロジック
 Lev.1: lib/Finance/File1.php
 Lev.2: lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Finance/File1.php 
 phpstan analyse -c phpstan.neon --level=2 lib/Lenet/Cleaning/File1.php 
 対応表
 PHPStanClient
 (artisan phpstan-client --diff)
 差分

  7. (artisan phpstan-client)
 全ファイル静的解析実行時
 - level: 1
 paths: lib/Finance
 - level:

    2
 paths: lib/Lenet/Cleaning
 phpstan analyse -c phpstan.neon --level=1 lib/Finance
 phpstan analyse -c phpstan.neon --level=2 lib/Lenet/Cleaning
 対応表
 対応表を読み込んで レベル分実行する
 PHPStanClient

  8. 新しいモジュールの設定
 lib/Lenet/Alliance/File1.php
 lib/Lenet/Cleaning/File1.php
 - level: 2
 paths: lib/Lenet/Cleaning
 - level:

    8
 paths: lib/Lenet/Alliance
 照合ロジック
 Lev.2: lib/Lenet/Cleaning/File1.php 
 Lev.8: lib/Lenet/Alliance/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=8 lib/Lenet/Alliance/File1.php 
 対応表
 追加しないといけない 
 PHPStanClient
 (artisan phpstan-client --diff)
 差分

  9. 新しいモジュールの設定
 - level: 2
 paths: lib/Lenet/Cleaning
 - level: 8
 paths:

    lib/Lenet/Alliance
 照合ロジック
 Lev.2: lib/Lenet/Cleaning/File1.php 
 Lev.8: lib/Lenet/Alliance/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=8 lib/Lenet/Alliance/File1.php 
 対応表
 追加しないといけない 
 漏れそう 設定知識が必要 PHPStanClient
 (artisan phpstan-client --diff)
 差分
 lib/Lenet/Alliance/File1.php
 lib/Lenet/Cleaning/File1.php

  10. 業務モジュールはデフォルトでLev.MAX
 - level: 2
 paths: lib/Lenet/Cleaning
 - level: 8
 paths:

    lib
 照合ロジック
 Lev.2: lib/Lenet/Cleaning/File1.php 
 Lev.8: lib/Lenet/Alliance/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=8 lib/Lenet/Alliance/File1.php 
 対応表
 複数のレベルが
 ヒットしたら
 低い方を適用
 PHPStanClient
 (artisan phpstan-client --diff)
 差分
 lib/Lenet/Alliance/File1.php
 lib/Lenet/Cleaning/File1.php

  11. 新しいモジュールのデフォルトはLev.MAX
 - level: 2
 paths: lib/Lenet/Cleaning
 - level: 8
 paths:

    lib
 照合ロジック
 Lev.2: lib/Lenet/Cleaning/File1.php 
 Lev.8: lib/Lenet/Alliance/File1.php 
 phpstan analyse -c phpstan.neon --level=1 lib/Lenet/Cleaning/File1.php 
 phpstan analyse -c phpstan.neon --level=8 lib/Lenet/Alliance/File1.php 
 対応表
 複数のレベルが
 ヒットしたら
 低い方を適用
 PHPStanClient
 (artisan phpstan-client --diff)
 設定不要 開発に集中できる 差分
 lib/Lenet/Alliance/File1.php
 lib/Lenet/Cleaning/File1.php

  12. 0: 未知のクラス・関数、 thisで呼ばれる未知のメソッド、メソッドや関数に渡される引数の数 
 1: 未定義の変数、未知のマジックメソッド、__call および __get を持つクラスのプロパティ 


    2: 未知のメソッド、PHPDocバリデーション 
 3: 戻り値の型、プロパティに割り当てられた型 
 4: デッドコードチェック 
 5: メソッドや関数に渡される引数の型 
 6: 欠落した型宣言
 7: 部分的に間違った直和型の検査 
 8: nullable型のメソッド呼び出しやプロパティアクセス 
 レベル上げのロードマップ

  13. 0: 未知のクラス・関数、 thisで呼ばれる未知のメソッド、メソッドや関数に渡される引数の数 
 1: 未定義の変数、未知のマジックメソッド、__call および __get を持つクラスのプロパティ 


    2: 未知のメソッド、PHPDocバリデーション 
 3: 戻り値の型、プロパティに割り当てられた型 
 4: デッドコードチェック 
 5: メソッドや関数に渡される引数の型 
 6: 欠落した型宣言
 7: 部分的に間違った直和型の検査 
 8: nullable型のメソッド呼び出しやプロパティアクセス 
 Lenet でのレベル上げのポイント
 
 

  14. 0: 未知のクラス・関数、 thisで呼ばれる未知のメソッド、メソッドや関数に渡される引数の数 
 1: 未定義の変数、未知のマジックメソッド、__call および __get を持つクラスのプロパティ 


    2: 未知のメソッド、PHPDocバリデーション 
 3: 戻り値の型、プロパティに割り当てられた型 
 4: デッドコードチェック 
 5: メソッドや関数に渡される引数の型 
 6: 欠落した型宣言
 7: 部分的に間違った直和型の検査 
 8: nullable型のメソッド呼び出しやプロパティアクセス 
 レベル上げのロードマップ
 
 
 
 
 
 発表で説明できなかったレベル別の ルールについて簡単に紹介します!