環境構築

CI(Continuous integration)を活用したPHPの実装

こんにちは。ササテンです。
今回は、CI(Continuous integration)を活用して、良質なPHPコードを生み出すための方法をご紹介いたします。

CI(Continuous integration)で何を行なうのか


PHPによるCIを解説していく前に、まずは前回の記事をご確認ください。CIについての説明を書いております。

今回、われわれがCIを導入しようという決断にいたった理由は、PHPプログラムの品質を上げるためです。そこで、CIを活用して、以下のチェックを行なうことにしました。

・PHPUnitを活用したユニットテスト(単体テスト)を自動化する
・PHP CodeSnifferにより、コーディング規約に沿ったコードになっているかどうかをチェックする
・Phanによる静的解析によって、行儀の良いPHPコードになっているかどうかをチェックする

それでは、各項目について詳しく説明していきます。

PHPUnitを活用したユニットテスト


PHPUnitは、PHPでユニットテスト(単体テスト)を書くための、デファクトスタンダードなライブラリです。

今回、CIを導入したことによって、Gitにプッシュを行なうと、自動的にユニットテストが走るようになりました。自動テストの結果は、テストが完了するとSlackに通知されます。また、GitLab上で確認することも可能です。

※ Slackの設定は以下の箇所でおこないます。Pipelineの結果を受け取ることで通知が可能です。

なお、弊社ではCIを導入するにあたり、初期のメルカリ流を採用することにしました。「費用対効果の高いテストを中心に書く」という方針です。詳しく知りたい方は「WEB+DB PRESS Vol.100 メルカリ開発ノウハウ大公開」をご覧ください。

具体的には、管理画面とWebAPIの処理を共通化したロジッククラスや、決済連携のコードなどが、手厚くテストを書く対象です。つまり、以下のようなコードに対してテストを書いていくことになります。

・利用頻度の高いコード
・絶対に失敗してはならない、重要度の高いコード

PHPUnitのテスト結果は、以下のように画面上で確認することができます。
※ プロジェクトを選んだ後に -> CI / CD -> Jobsを選択

PHP CodeSnifferによるコーディング規約のチェック


PHP CodeSnifferは、コーディング規約をチェックするためのツールです。

弊社では、PHPのコーディング規約にPSRを採用しています。そこで、PSRに準拠したコーディングになっているかどうかをチェックします。

なお、PSRはデファクトスタンダードになりつつあるPHPの標準コーディング規約です。PSRについて詳しく知りたい方は、以下のインフィニットループ様の記事をご参照ください

また、PHP_CodeSnifferについては、以下の記事を参考にして実装いたしました。

なお、規約に沿っていないコードをプッシュすると、このようなエラーが通知されます。

CIが通らずエラーが表示された場合は、コードを修正して再度プッシュするようにしてください。

Phanによる静的解析の導入

Phanは、静的解析を行なうツールです。PHPコード内にバッドプラクティスが含まれていないかをチェックすることができます。

CIが通らずエラーが表示された場合は、コードを修正して再度プッシュするようにしてください。

GitLabでCIを動かすための設定

それでは、PHPのCIをGitLabで動かすための設定を解説します。
・・・といっても、必要なことは多くありません。Gitリポジトリの直下に「.gitlab-ci.yml」という設定ファイルを置きます。そうすると、CIが動作するようになります。

弊社におけるCIの構成イメージを、サンプルとして掲載いたします。
なお、大まかな流れとして、以下の設定が必要になります。

1. Alpine LinuxのPHPコンテナを起動する(Alpine Linuxは軽量であるという理由で採用しましたが、他のLinuxでも問題ありません)
2. PHPのセットアップを行なう(必要なモジュールの追加インストールや設定を行ないます)
3. データベースを作成する(PHPUnitのみ、DBと連携したテストをするために必要です)
4. 依存ライブラリのインストール(弊社ではComposerを使用しています)
5. PHPUnit / PHP CodeSniffer / Phan を各自インストールする
6. テストの実行

なお、PHPのインストール設定を終えるまでの流れは、すべてのDockerコンテナで基本的に共通になります。そのため、基本的なPHPのセットアップまで行なったDockerをDockerHubにアップしておくことで、動作時間を短縮することも可能です(ちなみに、弊社ではそうしています)。

# How to php:alpine
# https://github.com/daper/docker-alpine-php/blob/master/5.6/Dockerfile

# サブモジュールを更新する場合にはこれを有効にする
variables:
  GIT_SUBMODULE_STRATEGY: recursive

before_script:
  - curl -fsSL https://getcomposer.org/download/1.4.2/composer.phar > composer.phar

# ユニットテスト、コーディング規約チェック、静的解析の順に実行します。
# テストが失敗した時点でCIは終了してしまうので、優先度の高いチェックを先に持っていきます。
stages:
  - phpunit
  - phpcs
  - phan

phpunit:
  stage: phpunit
  image: php:alpine 
  # 公式のpostgresコンテナを関連付け、一緒に起動します。
  services:
    - postgres:10
  script:
    - apk --update upgrade

    # PHPの設定はプロダクトによって変わりますので、適宜変更してください
    # 以下はあくまでサンプル構成です
    - apk add autoconf automake make postgresql-client postgresql-dev # ・・・以降割愛
    - docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
    - docker-php-ext-install -j$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) pgsql pdo pdo_pgsql # ・・・以降割愛

    # 立ち上げたPostgreSQLに、ユニットテストに必要なDBを作成し、テストデータを流し込みます。
    # DBと連携するWebシステムのため、プログラムからアクセスするDBとして利用します。
    - cat /path/to/project/test/create_db.sql | psql -h postgres -U postgres
    # テストデータの作成手続きは、シェル化してしまうのがおすすめです。
    # 作成したDBにコマンドラインシェルで接続して、テストデータのINSERTを流していってください。
    - /path/to/project/test/create_table.sh

    # 外部モジュールのインストール
    # PHPUnitはコンポーザでインストールしています
    - php composer.phar install --prefer-dist --no-progress --ansi --dev

    # PHPUnitの実行
    - vendor/bin/phpunit
  cache:
    key: vendor
    paths:
      - vendor/

phpcs:
  stage: phpcs
  image: php:alpine 
  script:
    - apk --update upgrade

    # PHPの設定はプロダクトによって変わりますので、適宜変更してください
    # 以下はあくまでサンプル構成です
    - apk add autoconf automake make postgresql-client postgresql-dev # ・・・以降割愛
    - docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
    - docker-php-ext-install -j$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) pgsql pdo pdo_pgsql # ・・・以降割愛

    # 外部モジュールのインストール
    # PHP CodeSnifferはコンポーザでインストールしています
    - php composer.phar install --prefer-dist --no-progress --ansi --dev

    # PHP CodeSnifferの実行
    - vendor/bin/phpcs ./path/to/test/target/
  cache:
    key: vendor
    paths:
      - vendor/

phan:
  stage: phan
  image: php:alpine 
  script:
    - apk --update upgrade

    # PHPのインストール設定はプロダクトによって変わりますので、適宜変更してください
    # 以下はあくまでサンプル構成です
    - apk add autoconf automake make postgresql-client postgresql-dev # ・・・以降割愛
    - docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
    - docker-php-ext-install -j$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) pgsql pdo pdo_pgsql # ・・・以降割愛

    # phanのインストール
    - pecl install ast-0.1.6
    - docker-php-ext-enable ast
    - curl -L https://github.com/phan/phan/releases/download/0.8.8/phan.phar -o /usr/bin/phan
    - chmod 755 /usr/bin/phan

    # 外部モジュールのインストール
    - php composer.phar install --prefer-dist --no-progress --ansi --dev
    - phan --version

    # 静的解析の実行
    - phan -z -x -k .phan/config.php
  cache:
    paths:
      - vendor/

まとめと予告


いかがだったでしょうか。
CIによる自動テストの導入は、レビュー時間の短縮に繋がります。

プロジェクトメンバーからの評判も上々です。基本的なレビューを自動化したことによって、レビュアーの負担が軽くなりました。また、同じ指摘であっても、人に指摘されるよりは機械に自動的に指摘された方が、心理的負担も軽くなります。

プロダクトマネージャーの目線から見ても、システムが最低限の品質を担保してくれるので、自信を持ってサービスをリリースすることができるでしょう!

導入初期は慣れるための苦労が必要でしたが、今ではなくてはならない存在になりました。
CIを活用して、品質の良いコードをどんどん量産していきましょう!


次回は、コウダインテによる「社員インタビュー」をお届けします。
お楽しみに!

LINEで送る
Pocket

この記事を書いた人・プロフィール
ササテン
ニックネーム: ササテン

前職の会社が倒産したことを機に入社。初めて業務で使用した言語は Perlでした。Perl の精神である「There's More Than One Way To Do It.(やり方は何通りもある)」が気に入っていて、自由度の高いプログラミングスタイルが好きです。

趣味:ゲーム、将棋、麻雀