23
11

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.

PHPコードの品質を劇的に向上させる静的解析ライブラリ4選!

Last updated at Posted at 2023-05-15

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);の有無を調べるのにも利用できるので、この記事では静的解析ライブラリとして扱わせてください。

フォーマットとしては、PSR2PSR12はもちろん、PhpCsFixerSymfonyがデフォルトで定めているルールセットを利用できます。

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.

まとめ

いかがだったでしょうか。

既存のプロダクトに入れるには、導入にコストがかるかもしれませんが、
新規プロダクトだと導入して損はないものばかりだと思います。

このような静的解析ツールを導入することで、レビューで細々としたことを指摘して、時間を浪費することがなくなり、
より少ない時間で、より高品質なものが作れると思います。

23
11
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
23
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?