おひとり

できる限りひとりで楽しむための情報やプログラミング情報など。

PHPにおける静的解析ツール(リンター)、ユニットテストの開発環境の構築

f:id:hitoridehitode:20200501233037p:plain:w150
PHP Logo - Author:Colin Viebrock, Licence:CC BY-SA 4.0, Source: here

PHPのComposerを使って、基本的な開発環境を構築する方法を紹介します。
基本的な開発環境って??というと、個人的には最低限以下のツールが含まれていることかな、と考えています。

  • 依存関係の解決ツール
  • 静的解析ツール
  • コーディング規約
  • ユニットテスト

今回は、Composerが使える前提で、
ユニットテストにはPHPUnitを、リンター(静的解析ツール)にはphpcsを使いたいと思います。
なお、コーディング規約はPSR-12を使って行きます。
autoloadの設定をし、コーディングを始める環境を整えます。

初期化

まずはアプリケーションを初期化します。
Composerを使って行います。

composer init

composer initを使うことで、対話的にアプリケーションを初期化できます。
質問に答えつつ、設定をしていきます。
今回は全てデフォルトで設定。 完了すると、おなじみのcomposer.jsonファイルが生成されます。まだ空っぽ。

{
    "name": "srsawaguchi/hello",
    "require": {}
}

PHPUnitのインストール

次はユニットテストのフレームワーク、PHPUnitをインストールしましょう。

composer require --dev phpunit/phpunit

コマンドを実行するとPHPUnitがインストールされます。

phpcsのインストール

次は静的解析ツールのphpcsのインストールです。こちらもComposerから簡単にインストールできます。

 composer require --dev squizlabs/php_codesniffer

今回使うコーディング規約はPSR-12。これはすでにphpcsにインストールされています。
以下のコマンドを実行すると、デフォルトで使えるコーディング規約が確認できます。

vendor/bin/phpcs -i

おおよそ、以下の結果になるはず。

The installed coding standards are PEAR, Zend, PSR2, MySource, Squiz, PSR1 and PSR12

autoloadの設定

今回は以下のような構成にしたいと思います。

❯ tree -I vendor
.
├── README.md
├── composer.json
├── composer.lock
├── src
│   └── Hello.php
└── tests
    └── HelloTest.php

2 directories, 5 files

※vendorフォルダは表示していません。
あらかじめsrcフォルダ、Hello.php、testsフォルダ、HelloTest.phpを作っておきましょう。(ファイルは空のままでOK。)

srcのnamespaceをApp、testsのnamespaceはTestsとしましょう。
testsフォルダにはもちろんユニットテストを追加していきます。

composer.jsonにautoloadの設定を追加しましょう。

{
    "name": "srsawaguchi/hello",
    "require": {},
    "require-dev": {
        "phpunit/phpunit": "^7.5",
        "squizlabs/php_codesniffer": "^3.5"
    },
    "autoload": {
        "psr-4": {
            "App\\" : "src/",
            "Tests\\" : "tests/"
        }
    }
}

続いて、以下のコマンドを実行してautoloadを初期化しましょう。

composer dumpautoload

さて、早速サンプルコードを書いて正しく設定できているか確かめて見ましょう。

Hello.php

<?php

namespace App;

class Hello
{
    public function hello()
    {
        return 'Hello, World!';
    }
}

HelloTest.php

<?php

namespace Tests;

use PHPUnit\Framework\TestCase;
use App\Hello;

class HelloTest extends TestCase
{
    public function testHello()
    {
        $h = new Hello();
        $this->assertEquals('Hello, World!', $h->hello());
    }
}

では、ユニットテストを実行してみましょう。

vendor/bin/phpunit ./tests

結果は問題なさそうです。

続いて、静的解析(リント)を実行してみましょう。

vendor/bin/phpcs --standard=psr12 ./src

こちらも問題なし。気になる人は、以下のように規約違反をしてからリントを実行して怒られる事を確認しましょう。
Hello.php

<?php

namespace App;

class Hello
{
    public function hello() {
        return 'Hello, World!';
    }
}

怒られます。

---------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
---------------------------------------------------------------------------------------
 7 | ERROR | [x] Opening brace should be on a new line
---------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------

Time: 89ms; Memory: 6MB

スクリプトへの登録

PHPUnitとphpcsの実行はそれぞれcomposer.jsonのスクリプトに登録しておきましょう。短いコマンドで済む用になります。

{
    "name": "srsawaguchi/hello",
    "scripts": {
        "test": [
          "vendor/bin/phpunit ./tests"
        ],
        "lint": [
          "vendor/bin/phpcs --standard=psr12 ./src"
        ]
    },
    "require": {},
    "require-dev": {
        "phpunit/phpunit": "^7.5",
        "squizlabs/php_codesniffer": "^3.5"
    },
    "autoload": {
        "psr-4": {
            "App\\" : "src/",
            "Tests\\" : "tests/"
        }
    }
}

上記の設定で、静的解析とユニットテストはそれぞれ以下のコマンドで実行できるようになります。

composer test
composer lint

お疲れ様でした。以上で基本的な環境の構築は終了です。
autoloadの設定はやや面倒でしたね。正直、ちょっとした実験程度であれば、ユニットテストのファイルに直接require_onceしても良いと思います。
目的に応じてもっと簡単なやり方でいいでしょう。
require_onceを使いたくないのであれば、autoloadのfilesも使えます。ですが、これは結局require_onceしているのと変わらないため、う〜ん。。。require_onceでいいと思います。

まとめ

今回はたまたまPHPの開発環境を構築する必要があったため、メモがてら共有。
PHPだとLaravelやCakePHP3のようなリッチなフレームワークを使うことが多く、環境構築はコマンドにおまかせ。
自分も、環境構築は基本的にはオールインワンでやってくれるツールに任せるべきものだと思います。
とはいえ、基本的な環境構築方法を知っておくのはトラブル対応でも役立つ可能性あり。
まだ開発では使わない場合も、慣れていない言語ほどリンターを使ってコーディングのスタイルを覚えたり、ユニットテストで挙動をテストしたりするのに必要最小限の開発環境は便利だなーと思います。

リンク集