PHPコードの品質を劇的に向上させる静的解析ライブラリ4選!
この記事では、内部品質を高めるために役立つ4つのツールを紹介したいと思います。
どれも慣れれば小一時間で導入できるものです。
1. phpstan : 型に特化した静的解析ツール
静的解析ツールとは、コードを実行する前に、コードの品質や潜在的なエラーを検出するためのツールのことです。
コード内の潜在的なエラーやタイポを自動的に検出してくれるツールです。
できること
- 静的コード解析による型検査
- PHPdocとの乖離がないかの検索
- 構文、文法の解析
- エラー/警告の無視した実装を検索
また、phpstanは設定ファイルを使って柔軟にカスタマイズできるため、プロジェクトに合わせたルールを設定できます。
これにより、不必要なエラーや警告が発生しないようにすることもできます。
マジックメソッドなどが多く存在するLaravelではLarastanという、
予めカスタムルールが設定された、phpStanのラッパーも存在しています。
追加の厳格ルールに関して
さらに厳格にプロジェクトを運用したい場合は追加のstrict-rulesも存在します。
詳細は上記のgithubに記載がありますが、幾つか抜粋すると
- if式内でbool値以外を使わないようにする
NG: if([0]) {
-
in_array
,array_search
などのstrictオプションがある関数のtrue
指定の必須化 - 緩やかな比較である
empty
関数の使用不可
などさらに設定できます(既存のプロダクトだとエラー出すぎて導入が大変かもしれない)
実行例
下記のような、レビューで見落としそうなコードに対しても、PHPStanは的確にレビューをしてくれます。
サンプルコード
<?php declare(strict_types = 1);
class Order
{
public function __construct(private array $items)
{
}
public function total()
{
return array_reduce($this->items, function($total, $item) {
return $total + $item->price();
}, 0);
}
}
class Item
{
public function __construct(private float $price)
{
}
public function price(): int
{
return $this->price;
}
}
$items = [
new Item(100),
new Item(200),
new Item(300),
];
$order = new Order($items);
echo 'Total: ' . $order->total();
Line Error
5 Property Order::$items type has no value type specified in iterable type array.
See: Solving PHPStan error "No value type specified in iterable type"
7 Method Order::__construct() has parameter $items with no value type specified in iterable type array.
See: Solving PHPStan error "No value type specified in iterable type"
12 Method Order::total() has no return type specified.
29 Method Item::price() should return int but returns float.
2. php-cs-fixer : フォーマッター
PHP Coding Standards Fixer (PHP CS Fixer) ツールは、標準に準拠するようにコードを修正します。
具体的には、コードのインデント、スペースの配置、改行の挿入などのルールを適用して、コードを整形します。
php-cs-fixerは、静的解析ライブラリではないと思いますが、厳密な型宣言を使用することが強制できるdeclare(strict_types=1);
の有無を調べるのにも利用できるので、この記事では静的解析ライブラリとして扱わせてください。
フォーマットとしては、PSR2
、PSR12
はもちろん、PhpCsFixer
、Symfony
がデフォルトで定めているルールセットを利用できます。
php-cs-fixerを導入することで、PSR2/12に準拠していない状態のコードが存在していない状態でレビューを開始することができます。
より詳しくルールに知りたい場合は下記のサイトが便利です。
https://mlocati.github.io/php-cs-fixer-configurator/
dry-runを上手く活用することで、PSR12に準拠していないものや、declare(strict_types=1);
が記載されていないコードを防ぐことも可能です。
declare(strict_types=1);
に関する補足
declare(strict_types=1);
これをファイルの先頭に記述することで、強い型付けを実行時に有効にすることができます。
強い型付けを有効にする理由としては、意図しない不具合の原因となる、暗黙の型変換を抑制することができます。
// declare(strict_types=1); // コメントアウトするとTypeErrorとなる
function dump(int $value): void
{
var_dump($value);
}
dump('13.37');
できること
・PSR2/12に準拠しているコードであることの保証(可読性の向上)
・declare(strict_types=1);
がついていないコードの検査
実行例
下記のようにフォーマットと、declare(strict_types=1);
の追加を行えます。
サンプルコード
フォーマット前
<?php
class Person{
private $firstName;
private $lastName;
function __construct($firstName, $lastName){
$this->firstName = $firstName;
$this->lastName = $lastName;
}
public function getFullName(){
return $this->firstName." ".$this->lastName;
}
}
echo "My name is ".(new Person("John","Doe"))->getFullName();
フォーマット後
<?php
declare(strict_types=1);
class a
{
private $firstName;
private $lastName;
public function __construct($firstName, $lastName)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
}
public function getFullName()
{
return $this->firstName.' '.$this->lastName;
}
}
echo 'My name is '.(new Person('John', 'Doe'))->getFullName();
3. Deptrac : 依存関係に特化した静的解析ツール
Deptracは、PHPアプリケーションの依存関係を管理するためのツールです。
PHPアプリケーションの依存関係を静的に解析し、アプリケーションのアーキテクチャに対して定義されたルールに従って依存関係を制限することができます。
できること
- 依存関係の可視化
- 依存関係の制約
- アーキテクチャの保持
特定のレイヤー間の依存関係を制限するルールを定義しておくことで、意図しない依存関係が導入されるのを防ぐことができます。
たとえばシンプルなMVCアーキテクチャを採用していた場合、ModelがController,Serivceに依存していないことを検査できあます。
MVCなどのシンプルなアーキテクチャでは恩恵は少ないですが、クリーンアーキテクチャのような、クラスの数と種類が膨大になると、
効果を発揮すると思います。
4. PHP Mess Detector : コードの設計を中心とした静的解析ツール
こちらもPHPコードの静的解析ツールの1つです。
phpstanと内容が被っている部分も多いのですが、こちらは設計を中心とした機能が多い印象です。
メソッドの長さ、ネストの深さ、制御構造の複雑さなどを評価し、維持が困難な複雑さを検出することができます。
Mess Detectorを導入することで、保守が困難なメソッド、クラスの定義が防がれるので、保守する側としてはとても有り難いです。
できること
ルールはここに説明しきれないぐらいありますが、特に役立ちそうなのを抜粋すると以下のような物があります。
- コードの複雑さの検出
- サイクロマティック複雑度(循環的複雑度)に上限を設定、NPath値も可
- public関数、クラスの依存数に上限を設定
- 依存しているオブジェクト数に上限を設ける
- コードの長さの検出
- クラス・メソッドの最大行数に上限を設定
- 命名
- 変数名、メソッド名、クラス名の長さに下限と上限を設定
現在のプロダクトだと循環的複雑度を10→5に変更して、Static関数呼び出し禁止だけ無効にして運用しています。
実行例
下記のような複雑度が高いサンプルがあった場合、以下のような出力が出ます。
サンプルコード
<?php
class ComplexClass {
public function complexMethod($param1, $param2) {
if ($param1 > 10 && $param2 < 5) {
for ($i = 0; $i < 10; $i++) {
if ($i % 2 === 0) {
echo 'Even number: ' . $i . PHP_EOL;
} else {
echo 'Odd number: ' . $i . PHP_EOL;
}
}
} elseif ($param1 <= 10) {
switch ($param2) {
case 1:
echo 'Param2 is 1.' . PHP_EOL;
break;
case 2:
echo 'Param2 is 2.' . PHP_EOL;
break;
default:
echo 'Param2 is not 1 or 2.' . PHP_EOL;
}
} else {
while ($param2 > 0) {
$param2--;
echo 'Countdown: ' . $param2 . PHP_EOL;
}
}
}
}
complex.php:4 CyclomaticComplexity The method complexMethod() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
complex.php:9 ElseExpression The method complexMethod uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.
complex.php:24 ElseExpression The method complexMethod uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.
まとめ
いかがだったでしょうか。
既存のプロダクトに入れるには、導入にコストがかるかもしれませんが、
新規プロダクトだと導入して損はないものばかりだと思います。
このような静的解析ツールを導入することで、レビューで細々としたことを指摘して、時間を浪費することがなくなり、
より少ない時間で、より高品質なものが作れると思います。