モダンなPHPの開発の学び方

簡単なPHPのお仕事を頂きまして、ソースコードを見てみると、10年前に見たような中々レガシーな感じ。とはいえ、久々のPHPの仕事で、自分の知識もCakePHP1.Xを使用した当時で止まっているので、知識のアップデートがてら、ソースコードをリファクタリングすることにしました。 その際に集めた情報ややったことなどを、列挙していきます。

The Right Way

まずは、みんなどういう開発をしてるんだろうなぁと調べる時のキーワードですが、こういう時は「最新」とかで調べても上手くヒットしません。「モダン」は割といいキーワードだと思います。とりあえず「モダン PHP」とかで検索したところをざっと眺めます。

最近は、GitHubで「awesome ◯◯」で調べると、◯◯関連の情報をまとめたドキュメントなどがあるのでそれも参考にします。

そして見つけたのがこれ。

PHP: The Right Way

ここを見とけば、一通りはOKなのではないでしょうか。 このなかで、適用できそうなものをどんどん採用していくことにします。

開発環境

ディレクトリ構成

bin/      # 実行ファイルを配置
etc/      # 設定ファイルを配置
public_html/  # HTML,PHPを配置
src/      # ライブラリを配置
tests/    # テストを配置
tmp/      # テンポラリ

bin/

binには開発に便利なシェルスクリプトなどを置いておきます。 どんなときでも使えそうなものはエイリアスを.bashrcに書いとくといいかも。 今回はビルトインウェブサーバの起動スクリプトを書いておきます。

etc/

設定ファイルなどを入れておきます。 今回の場合はphp.iniなどがあるでしょう。開発用と本番用などもまとめて入れておきます。

src/

今回作るPHPを入れておきます。できれば、ライブラリとしてなるべく汎用的にしていき独立させていきたいところですが、今回はある程度のところで止めておきます。

tests/

リファクタリングするには、まずテストを書きます。今回はユニットテストを書く意味はあまりなさそうなので、エンドツーエンドテストだけ書いておきます。

tmp/

作りかけとかいらなくなったものとかはtmp/にぶち込んでおきます。 .gitignoreにはtmp/とだけ書いておけば、tmp/以下はリポジトリにはコミットされません。git statusなどでも余計なファイルが表示されないようにします。

README.md

プロジェクトの詳細や、インストール等の条件、サーバの詳細などはここに記載しておきます。 githubなりbitbucketなりでトップページに表示される内容になるので、関係者に知らせたい項目や、コーディング規約等を書いておいたりします。

ローカルサーバ

以前はXAMPPとかローカルのApacheなどがありました。Ruby on RailsでPowを使ってた場合などに併用に苦労した覚えがありますが、現在はビルトインウェブサーバがあるようなので、それを使用することにします。

以下のようなファイルをワーキングディレクトリに配置して、./bin/phpdとかでサーバを起動するようにします。

#!/bin/sh

abs_dirname() {
  local cwd="$(pwd)"
  local path="$1"

  while [ -n "$path" ]; do
    cd "${path%/*}"
    local name="${path##*/}"
    path="$(readlink "$name" || true)"
  done

  pwd -P
  cd "$cwd"
}

script_dir="$(abs_dirname "$0")"

php -S localhost:8123 -c $script_dir/../etc/php.ini-development -t $script_dir/../public_html

開発サーバの設定はetc/php.ini-developmentに配置しておきます。

display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On
output_buffering = On
sendmail_path = /usr/bin/env catchmail

メールサーバ

今回はメールを飛ばすプログラムなので、ダミーのSMTPサーバを用意しておきます。 メールはテスト中に実際に飛んでいってしまうとトンデモナイことになることが多いので注意が必要です。

Railsではletter_openerhttp://mailtrap.io/などで対応していましたが、今回はローカルのsendmail(postfix)を使う代わりに、MailCatcherを使うことで対応します。

php.ini-developmentの最下行に加えた一文でプログラムが送信したメールはMailCatcherが拾ってWebで確認できるようなります。

以下のようなファイルをbin/に置いておくと、しばらく立って忘れたとき、思い出すきっかけになるので簡単なものでも作っておいたりします。

#!/bin/sh
maicatcher && open http://localhost:1080/

Composer

ライブラリの依存性管理は、最近はPEARは使わず、Composerというもので、venderディレクトリ以下にインストールするのが一般的だそうで、RubyでいうところのBundlerのようなもののようです。 今回はPHPUnitとSeleniumを簡単に使えるFacebookのライブラリphp-webdriverを使って、E2Eのテストだけ書いておくことに。 ここらへんは長くなりそうなので別途要望があれば書きます。

リファクタリング

クラスローダー

元のファイルをみたところ、クラスの文字もないようなベタ書きのPHPスクリプトでした。 これをどんどんクラス化していくなかで、今までの感じだと、require_onceを列挙してクラスを読み込むことになってしまう。

しかし、現在は、命名規則とクラスローダーを使うことで煩雑な作業を省略することができる。 まずは、jwagejwage/SplClassLoader.php でクラスローダーをダウンロードしてきて、src/に配置すします。

./public_html/phptest.phpというファイルを用意して、以下のように記述し、SplClassLoader.phpを読み込みます。

<?php

require_once '../../src/SplClassLoader.php';
$classloaderTest = new SplClassLoader(’Test’, __DIR__ . '/../../src');
$classloaderTest->register();

$entry = new \Test\Entry();
$entry->run();

./src/Test/Entry.phpを用意して、以下のように記述する。

<?php

class Entry {
    public function run() {
        echo ‘Hello World’;
    }
}

./bin/phpdを起動し、http://localhost:8123/phptest.php をブラウザで表示する。

Hello Worldと表示されたら成功。問題があれば、ウェブサーバのログを確認して修正する。

コーディング規約

各フレームワークで乱立していたコーディング規約が最近は、皆で統一しようとなったようで、PHP-FIGで扱っている、PSR(PHP Standards Recommendations)というのに集約されつつあるようです。郷に入らば郷に従えなので、これに従って行きましょう。

大体のポイントを抑えれば、大分綺麗になるようです。 正確な記述はPSRを見てもらい、ここでは、以下のような感じで作っていきます。

あとはphp-cs-fixerPHP_CodeSnifferというようなコーディングスタイルチェックツールに自動で訂正してもらうのに従うのが楽そう。今回は最終的に、php-cs-fixerを使ってます。

### コードの整形

$err .= "<div class='caution'>「電話番号」は入力必須です。</div>\n";

$err .= $this->errorMessage(self::ERROR_REQUIRED, ’電話番号’);

private function errorMessage($type, $key)
{
    switch ($type) {
        case self::ERROR_REQUIRED:
            $msg = "<div class='caution'>「".$key."」は入力必須です。</div>\n";
            break;
…

などなど。

特に、今回はネストが深すぎたのでガード節で抜けていったら相当シンプルになりました。

まとめ

3日位で、駆け足で調べてみましたが、さすがはPHPは開発者が多いこともあって情報も多く、最近は大分道が整備されてるように思えます。ブログ等は結構古い(or古くなる)ことも多いので、以下の参考資料等が充実しているのでそちらを参考にするほうがいいでしょう(ここも例外ではないです(笑)