👏

PHPStanをどうやってインストールするか

2022/08/27に公開1

PHPStanはPHPの静的解析ツールです。現代のPHPerはみんな使ってるよ。

……ところで、みなさんはPHPStanをどうやってインストールしているのでしょうか。
PHPStan開発者のOndřej Mirtesは次のように述べています。

https://twitter.com/OndrejMirtes/status/1541771569735036928

多くの場合において、Ondřejの言う通りcomposer require --devが最善のインストール方法です。
この記事では、なぜそれが推奨のインストール方法だと言えるのか、どのような場合に別の選択肢が候補に挙がるのかを解説します。

あと、この記事はPHPカンファレンス沖縄2022でPHPStanの使い方を説明するのにインストール方法まで説明する時間がとれないので書きました。

https://fortee.jp/phpcon-okinawa-2022/proposal/604a95d0-d92d-48eb-8682-7cbdde8a8fef

あらかじめ知っておきたいこと

事前知識: PHPStanパッケージの構造

PHPStanのリポジトリはGitHubでは以下の2リポジトリに分かれています。

  • https://github.com/phpstan/phpstan
    • PHPStanのビルド済みパッケージ(Pharアーカイブ)を配布している
    • issueではPHPStanのバグ報告や機能追加要望も受け付けている
    • phpstan.orgのコンテンツはここで管理されているので、ドキュメントの修正などはこちらに送る
  • https://github.com/phpstan/phpstan-src
    • PHPStanのソースコードをホストする
    • 機能追加や修正のプルリクエストはこちらに送る
    • issueは無効化されているのでphpstan側に送る

Composerの標準リポジトリであるPackagistでphpstan/phpstanとして配布されているのは前者のリポジトリです。

PHPの「ビルド済みパッケージ」とはどういうことでしょうか。実際にComposerでインストールされるディレクトリ一式を見てみましょう。

vendor/phpstan/phpstan/
├── LICENSE ← ライセンスファイル
├── README.md ← ドキュメント(README)
├── bootstrap.php ← PHPStanをクラスとして使いたいときに読み込むファイル
├── composer.json ← Composer パッケージ設定ファイル
├── conf
│   └── bleedingEdge.neon ← bleedingEdgeを有効化するための設定ファイル
├── phpstan ← PHPStan実行スクリプト
├── phpstan.phar ← PHPStanのクラス一式がパッケージされたアーカイブ
└── phpstan.phar.asc ← PGP署名ファイル

なんとPHPStan 1.8.2時点でインストールされるファイルはたったのこれだけです。Pharという概念に馴染みのない方はPHPマニュアルの「Phar」の記事を「はじめに」だけでも読んでください。

https://www.php.net/manual/ja/intro.phar.php

要はPHPスクリプトをzipのような一個のアーカイブファイルにまとめたものです。PHPStanの実装と依存ファイル・依存パッケージはすべて、この1ファイルに格納されています。

Pharファイルを作成する過程で以下の処理も行われています。

これにより、PHPStanで使っている依存パッケージ(ライブラリ)は名前空間が隔離され、ほかのアプリケーションと衝突しないようになっています。

事前知識: 拡張とextension-installer

PHPStanは「拡張(extension)」によって通常の型宣言を読み取るだけでは実現できない型付けやフレームワーク固有のルールなどを検査できるようになります。

フレームワークやライブラリ用の公式およびサードパーティの拡張ライブラリはこちらの記事をお読みください。

https://phpstan.org/user-guide/extension-library

拡張によってどのようなことが可能なのかは以下の記事を読むと良いでしょう。

https://phpstan.org/developing-extensions/extension-types

さて、上の記事でも言及されていますが、拡張を有効化するには拡張ごとの設定ファイルをincludesに追加する必要がありますが、phpstan/extension-installerを使うと自動で有効化できます。

https://github.com/phpstan/extension-installer

どのような場合に使うべきで、どのような場合に使えないかは上記記事に書いてあるので、必要に応じて追加するなり、各拡張のREADMEを読んで設定するなりしておいてください。私としては基本はextension-installerで有効化するのがおすすめなので、以後そのていで書きます。

[RECOMMENDED] composer require --dev

前述した通り、PHPStan作者のOndřej Mirtesはこの方法だけを推奨しています。
PHPStanユーザーマニュアルのGetting Startedで紹介されているのもcomposer require --devです。

https://phpstan.org/user-guide/getting-started

一般に、QAツール類(たとえばPHP-CS-Fixer)はいくつかのライブラリへの依存があり、composer require --devでインストールするとプロジェクトの依存関係と衝突してしまい、アップデートの障壁になってしまうことがあります。

一方、PHPStanは先に説明した通り依存パッケージの名前空間を隔離しており一切のパッケージ依存がないので、そのような懸念もなく依存関係として追加できます。

インストール方法

composerはPHPの実行環境に併せて適宜 docker-compose exec php composer ... とか php composer.phar ... とか sail composer ... とかそういう感じに読み替えてください。

composer require --dev -W phpstan/phpstan phpstan/extension-installer

Larastanを同時に使いたい場合は同時に追加します。

composer require --dev -W phpstan/phpstan phpstan/extension-installer nunomaduro/larastan

composer requireではパッケージのバージョンを省略するとインストール可能な最新のバージョンが導入されるので、PHPStanや拡張をアップデートしたい場合にも同じコマンドを使います。

使いかた

OSにインストールされたPHPを直接使える場合は以下のように実行します。

./vendor/bin/phpstan analyze ...

プロジェクトの実行環境をDockerまたはdocker-composeで構築していて、既にコンテナが起動している場合は以下のように実行します。

docker exec <service_name> vendor/bin/phpstan analyze ...
docker-compose exec <service_name> vendor/bin/phpstan analyze ...

Laravel Sailを使っている場合はsail vendor/bin/phpstan analyze ...みたいな感じで起動できると思いますが、私は試していません。

この方法の利点

  • 開発メンバー全員の環境に確実に導入でき、PHPStanのバージョンを合わせやすい
  • 通常のPHPスクリプトとしてホスト環境のPHPまたはDockerなどで実行できる

この方法を適用できない条件

  • プロジェクトのPHPバージョンが7.2未満
    • composerplatformが7.2未満の設定になっている
    • DockerコンテナのPHPランタイムが7.2
    • 環境が仮想化されておらず、PHP 7.2以上をインストールすることもできない
  • プロジェクト責任者との合意がとれておらず、Composerの依存に追加できない

composer global require

Composerはパッケージごとに依存を管理するものですが、composer globalは特定のプロジェクトとは独立したディレクトリにComposerパッケージをインストールできます。

インストール方法

composerはPHPの実行環境に併せて適宜 php composer.phar ... とか読み替えてください。

composer global require --dev -W phpstan/phpstan phpstan/extension-installer

composer global requireでも通常と同様にパッケージのバージョンを省略するとインストール可能な最新のバージョンが導入されるので、PHPStanや拡張をアップデートしたい場合にも同じコマンドを使います。

歴史的経緯により、composer globalを実行した結果は以下のディレクトリのどこかに配置されています

  • ~/.config/composer
    • 比較的最近Composerをインストールした人はこっち
  • ~/.composer
    • 割と前からComposerを使っていて最新新規インストールとかしてない人はこっち

あとは$COMPOSER_HOMEという環境変数で制御することもできますが、基本的に勝手にセットされることはないのでこの2パターンを想定しておけばいいと思います。Composerでインストールされたスクリプトはこれらのディレクトリの直下のbinディレクトリに配置されるので、そこにPATHを通せば通常のコマンドとして使えます。

よくわかんないひとはcomposer global showで検索してチェック… してもいいのですが、めんどくさいので両方にPATH環境変数を通しておくといいですね。

PATH=$HOME/.config/composer/bin:$HOME/.composer/bin:$PATH

上記の操作をしてcomposerコマンドが起動できるようになっていれば成功です。

この方法の利点

  • 検査対象のプロジェクトに変更を加えずに導入できる
  • 更新頻度の少ない小規模なライブラリなどに個別の導入が必要ない
  • 通常のPHPスクリプトとしてホスト環境で実行できるためDockerが必要ない
    • Dockerの実行パフォーマンスが悪い環境でも使える

この方法を適用できない条件

  • ホストにPHP(7.2以上)とComposerをインストールできない環境

composer-bin-plugin

composer-bin-pluginはプロジェクトのサブディレクトリに隔離されたComposerパッケージを作って、実行したいツールを隔離された依存関係としてインストールできます。この方法は、たとえばPHP 5.x、7.x、8.xをすべてサポートする必要があるライブラリなどをPHP 8.xまたは7.x環境下で開発しているといった場合に有効です。

https://github.com/bamarni/composer-bin-plugin

正直これはツールを導入してサブディレクトリを切っていくだけなので、この記事での詳細な説明は避けます。

この方法の利点

  • 検査対象のプロジェクトの依存関係に変更を加えずに導入できる

この方法を適用できない条件

  • リポジトリ内にサブディレクトリを切ってツールを導入する権限がない

Phive

PhiveはPHPのための便利ツールをPharファイル単位でインストールできるツールです。このツールもcomposer require --devでは依存性が混ざる問題を防ぐ問題を避けられます。

なのですが、使いやすいかは自己判断してください。

https://twitter.com/tadsan/status/1110402167846821888

この記事を書いた時期にあった「sks-keyservers.netの不安定性」は解決されてるかもしれません。(サーバー自体が廃止されたので)

https://tadsan.fanbox.cc/posts/328653

この方法の利点

  • 検査対象のプロジェクトの依存関係に変更を加えずに導入できる
  • GPGでファイルの真正性を保証できる (これはComposerにはない機能)

この方法を適用できない条件

  • それぞれの環境(開発者環境・CI環境)でPhiveをインストールできない場合

jakzal/phpqa

Ondřejのツイートに挙がっているので紹介だけしておきます。これはPHPのQAツール(静的解析とかテストフレームワークとか品質管理のためのツールの総称)をまとめて導入するためのDockerコンテナです。

https://github.com/jakzal/phpqa

READMEを見ると長大なテーブルがありますが、ほとんど知らないツールばかりなのではないでしょうか。……全部使いたいなら選択肢に入るのかもしれません。

結局どれを使えばいいの?

いちおう図にしてみたのですが、composer require --dev以外は緊急避難的な方法だと思ってください。

Discussion

msngmsng

手元で確認した限りでは

sail vendor/bin/phpstan analyze ...

は動かなくて

sail composer exec phpstan analyze ...

でいけました。