8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ラクスAdvent Calendar 2022

Day 11

大規模レガシーアプリにPHPStanを適用する時のTIPS

Last updated at Posted at 2022-12-10

この記事はラクスAdvent Calendar 2022 11日目の記事です。

最近、業務でPHPStanの導入を検討しており、まだ途中経過ですが、躓きや期待できそうな点などを書いておきます。

PHPStanとは

PHPの静的解析ツールで近年人気のツールです。
PHPStanの詳細については世の中にたくさん情報があるのでここでは割愛しますが、かいつまんで特徴を言うと下記のような感じです。

解析対象の設定や解析レベルなど各種設定はphpstan.neonというファイルに定義していきます。

前提

  • 10年以上のレガシーアプリ
  • 解析対象は約4500ファイル
  • 名前空間などない
  • 諸事情により自作オートローダーあり

方針

解析をかけると既存コードのエラーがすごい数になってしまうので、直すのは今後の新規開発や修正分のみとする(まずはこれ以上傷が広がらないようにする)
→baselineを作成する

実行できるようになるまで

実行時エラーのデバッグ

まずはbaselineを作成しようと解析対象全体に対して実行してみると、そもそも失敗しますね。歴史があって大規模なアプリになってくると、どこにエラーが潜んでいるかわかりません。

$ php vendor/bin/phpstan --generate-baseline[ERROR] An internal error occurred. Baseline could not be generated. Re-run PHPStan without --generate-baseline to see
         what's going on.

こういう時は、--debugオプションをつけて実行すると、下記のようにエラーとなるソースのところでストップします。

$ php vendor/bin/phpstan --debug
/PATH_TO_APP/app/controllers/SampleController.php
...
/PATH_TO_APP/app/controllers/BadController.php # エラー原因となるソース

次に、-vオプションをつけて実行するとエラースタックを吐いてくれるので、原因が特定しやすくなります。

php vendor/bin/phpstan analyse /PATH_TO_APP/app/controllers/BadController.php -v

ちなみに、-vvvオプションをつけて実行すると、各ファイル解析時点で消費された合計メモリや解析にかかった秒数も表示されるようになるので、リソース的な問題で問題が起きた時のデバッグに役立ちます。

/PATH_TO_APP/app/controllers/Sample1Controller.php
--- consumed 36 MB, total 82 MB, took 8.35 s
...
/PATH_TO_APP/app/controllers/Sample2Controller.php
--- consumed 0 B, total 1.25 GB, took 0.74 s
/PATH_TO_APP/app/controllers/Sample3Controller.php
--- consumed 0 B, total 1.29 GB, took 0.16 s

除外ファイルの設定

場合によっては都合上修正は難しい・このファイルは一旦解析対象から外しても問題ないという場合もあるかと思います。
その場合は、設定ファイルで除外設定を行うこともできます。

phpstan.neon
parameters:
  paths:
    # 解析対象
    - ../app
    - ../config
    …
  excludePaths:
    # 設定系は除外
    - ../app/config*
    # 廃止ソースは除外
    - ../app/controllers/AbondonedController.php
    …

余談ですが、baselineは複数作成して読み込むこともできます。
もし与えられた環境のスペックが厳しく、baselineを一括作成するのもままならない場合はディレクトリごとにbaselineを作成していくのもありかなと思います。

phpstan.neon
includes:
  - baseline/controllers-baseline.neon
  - baseline/models-baseline.neon
  - baseline/services-baseline.neon
 …

カスタムオートローダーの設定

composerなど便利なツールのない時代から継続しているアプリでは、クラスマップを自作していたり、名前空間の設定などなかったり、あちこちで定数ファイルをrequireしていたりなどあるのではないでしょうか。

PHPStanにはbootstrapという設定があり、PHPStanが実行される前にPHPランタイムで何かを初期化する必要がある場合 (独自のオートローダなど)、 独自のブートストラップファイルを提供することができます。

phpstan.neon
parameters:
	bootstrapFiles:
		- custom-autoloader.php

当アプリでも実行に必要な定数ファイルやカスタムオートローダーがあり、これを設定しないとそもそもクラスパスを解決できませんでした。

実行時間やリソース消費について

解析対象数は約4500で

  • 1コア・メモリ2GB環境で16分ほど
  • 8コア・メモリ15GB環境で2~3分ほど
  • メモリ消費はいずれも2~2.6GBほど

でした。
完全にコア数に依存していますね。

これはPHPStanがParallel processingに対応しているためです。
設定はデフォルトで有効になっています。

実際8コア環境で実行してみると、下記のようにコア数分workerが起動されています。

$ top
  PID  PPID USER     P S  %CPU %MEM   TIME   SWAP    DATA COMMAND
 7755  7736 root     7 R 100.0  4.9   0:17      0  787604 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7757  7736 root     5 R 100.0  4.9   0:17      0  791832 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7760  7736 root     1 R 100.0  4.9   0:17      0  787604 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7753  7736 root     4 R  99.7  5.0   0:17      0  797844 /usr//bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7756  7736 root     2 R  99.7  4.9   0:17      0  787604 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7759  7736 root     6 R  99.7  4.9   0:17      0  781460 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7758  7736 root     0 R  99.3  4.9   0:17      0  781460 /usr/bin/php -c /usr/lib/php.ini vendor/bin/phpstan worker --configuration /usr/
 7754  7736 root     3 R  99.0  4.9   0:17      0  783508 /usr/bin/php -c /usr/lib/php.ini

まだ運用は確定していませんが、解析対象が多いとそれだけ要求スペックや実行時間がかかってくるため、将来的にはそこそこのコア数のマシンを実行環境にしてCIした方が良いだろうなという感じです。

使ってみて期待できそうなこと

日々のコードレビューについては

  • 機械的にチェックされるため、人の目で見るより取りこぼしが少なく、コード品質向上が期待できる
  • 上記効果によりレビュワーの負担が軽減され、より高いレイヤーでのレビューに専念できそう

また、副次的な効果として

  • 大規模レガシーアプリのリファクタリングはどこから手をつけるかの判断が難しいが、PHPStanの解析結果とルールレベルを参考に、徐々に改善していくロードマップを描きやすそう

といった期待が持てそうでした。

また新たに工夫や効果が出た際には追記していきます。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?