Fork me on GitHub

ようこそ

時代遅れの情報がウェブ上にあふれている。そんな情報を見たPHP初心者は戸惑ってしまうだろう。そして、まずい手法やまずいコードが広まってしまう。 そんなのはもうやめよう。PHP: The Right Way は気軽に読めるクイックリファレンスだ。PHPの一般的なコーディング規約、 ウェブ上のよくできたチュートリアルへのリンク、そして現時点でのベストプラクティスだと執筆者が考えていることをまとめた。

大事なのは、 PHPを使うための正式なお作法など存在しない ってこと。 このサイトの狙いは、はじめて PHP を使うことになった開発者に、いろんなトピックを紹介すること。 経験豊富なプロの人にとっても、これまで深く考えることなく使ってきた内容について、新鮮な見方を伝えられるだろう。 このサイトは、決して「どのツールを使えばいいのか」を教えるものじゃない。 いくつかの選択肢を示して、それぞれの違いや使い道をできる限り紹介する。

このサイトは今も成長中なので、有用な情報やサンプルなどが見つかればどんどん更新していくつもりだ。

翻訳

PHP: The Right Way は、他の言語にも翻訳されつつある。

協力するには

みんなでこのサイトをもっとよいものにしよう。PHPを勉強したい人たちのために良質な情報を集めるんだ! GitHubでね。

拡散希望

PHP: The Right Way のバナー画像を用意したので、みんなのサイトで使ってほしい。 PHPを勉強したい人たちに「いい情報がここにあるよ!」ってぜひ伝えよう。

バナー画像

Back to Top

はじめに

最新の安定版 (5.6) を使う

とりあえず PHP を試したいっていうのなら、最新の安定版である PHP 5.6 を使おう。PHP はここ数年で大きく成長して、強力な新機能が追加された。5.2 から 5.6 って、マイナーバージョンが変わっただけに見えるかもしれない。でも実際は 大幅に 改良されているんだ。 どんな関数があってどんなふうに使えるのかを知りたければ、php.net のマニュアルを読めばいい。

ビルトインウェブサーバー

PHPの勉強を始めるときに、わざわざ本格的なウェブサーバーをインストールする必要はない。そう、PHP 5.4以降ならね。サーバーを立ち上げたかったら、ターミナルでプロジェクトのルートディレクトリに行って、こんなコマンドを実行するだけでいいんだ。

> php -S localhost:8000

Mac の人は

OS X には PHP が最初からインストールされているけど、最新の安定版からは微妙に遅れている。 Lion についてくるのは PHP 5.3.6 だし Mountain Lion でも 5.3.10。Mavericks にしても 5.4.17 だ。

OS X の PHP をアップデートするには、Mac 用の パッケージ管理ツールを使えばいい。 あと、php-osx by Liipもおすすめだ。

あるいは、自分でコンパイルしてもいい。ただその場合は、 Xcode あるいは “Command Line Tools for Xcode” をインストールしないといけない。 これは Apple の Mac Developer Center でダウンロードできる。

PHP だけじゃなくて Apache や MySQL とかも入っていて、さらに GUI の管理ツールもついてる「全部入り」のパッケージもある。それが MAMPXAMPP だ。

Windows の人は

Windows で PHP を使うには、いくつかの方法がある。バイナリをダウンロードすることもできるし、 つい最近までは’.msi’形式のインストーラも使えた。でも PHP 5.3.0 からは、インストーラ形式をサポートしなくなった。

学習用にローカルで開発する場合は PHP 5.4 以降のビルトインウェブサーバーを使えばよいので、細かい設定を気にする必要はない。 もしウェブサーバーとかMySQLとかも含めた「全部入り」を使いたければ、Web Platform InstallerZend Server CEXAMPPEasyPHP、そしてWAMPなどがお勧めだ。これらを使えば Windows 用の開発環境を手早く構築できる。 とはいうものの、これらのツールは実際の運用環境と微妙に異なる。なので、たとえば「Windows で開発して Linux にデプロイ」 とかいう場合は環境の違いに気をつける必要がある。

Windows 上でシステムを実運用する場合は、IIS 7 を使うとよい。これが一番安定しており、かつパフォーマンスも優れている。 phpmanager (IIS 7 用の GUI プラグイン) を使えば PHP の設定は管理をシンプルにできる。 IIS 7 には FastCGI が組み込まれており、すぐに使える。 単に PHP をハンドラとして設定するだけでよい。 その値の詳しい情報は、dedicated area on iis.netに PHP 専用のエリアがある。

Vagrant

開発環境と違う環境でアプリケーションを動かすと、謎のバグが出ることがある。 あと、チームで開発しているときとかに、各自の開発環境で使ってるライブラリのバージョンをきちんとそろえておくっていうのも けっこう大変。

Windows で開発して Linux (など、Windows 以外の環境) にデプロイするとか、 あるいはチームで開発しているとかいうのなら、 仮想マシンを導入すべきだろう。なんか難しいことを言ってるように聞こえるかもしれない。 けど、Vagrantを使えばちょっとした手順でシンプルなラッパーを用意できるし、 あとはPuppetとかChefを使えば仮想環境を配布できる。 ベースとなる環境を配布できるようにしておけば、複数の開発環境をまったく同じ状態に構築できる。 複雑怪奇なコマンドを羅列した「環境構築手順書」だとかいうのもいらなくなるってこと。 ベース環境を「破棄」したり作りなおしたりするのもそんなに手間がかからないので、 まっさらな環境を用意するのもお手軽にできる。

Vagrantは、フォルダを作って、ホストと仮想マシンの間でコードを共有する。 つまり、ホストマシンで作ったり編集したりしたコードを、そのまま仮想マシンの中で実行できるっていうことだ。

ちょっとした手助け

Vagrantを使い始めるときの手助けになるのが、これらのサービスだ。

Back to Top

コーディングスタイル

PHP のコミュニティはとてもでっかくて、いろんな人たちがいる。 そして、数え切れないほどのライブラリやフレームワークそしてコンポーネントが存在する。 そんな中からいくつか選んで、それを組み合わせてひとつのプロジェクトで使うっていうのもよくあることだ。 大切なのは、PHP のコードを書くときに、(できるだけ) 標準的なスタイルに従うことだ。 そうすれば、いろんなライブラリを組み合わせて使うのも簡単になる。

Framework Interop Group っていうところ が、おすすめのスタイルを提案している。 コーディングスタイルに関する提案は、PSR-0PSR-1PSR-2、そしてPSR-4だ。 これって要するに、 Drupal や Zend、Symfony、CakePHP、phpBB、AWS SDK、FuelPHP、Lithium などのプロジェクトが採用しつつある規約をまとめただけのものなんだ。 自分のプロジェクトでこれを使ってもいいし、今までの自分のスタイルを使い続けてもいい。

理想を言えば、PHP のコードを書くときには、よく知られた何らかの標準規約に従うべきだ。 さっき説明したPSRの組み合わせでもいいし、PEARとかZendのやつでもかまわない。 そうすれば、他の人にもコードを読んでもらいやすくなるし、手助けも得やすくなるだろう。 また、コンポーネントを実装するアプリケーションがいろんなサードパーティのコードを組み合わせても、一貫性を保てる。

PHP_CodeSnifferを使えば、 自分のコードがこれらの標準のどれかひとつに準拠しているかどうかを確認できる。 あと、Sublime Text 2みたいなテキストエディタのプラグインを使えば、 書いているその場でリアルタイムのフィードバックが得られる。

Fabien Potencierが作ったPHP Coding Standards Fixerを使えば、 これらの標準に準拠するようにコードを自動的に修正してくれる。 いちいち手作業で修正する手間を省けるってわけだ。

変数名や関数名、そしてディレクトリ名なんかは、英語にしておくことをおすすめする。 コードのコメントに関しては、別に英語にこだわらなくてもかまわない。 そのコードを扱う(将来扱う可能性がある)すべての人が読みやすいものであれば、何語でもかまわない。

Back to Top

言語仕様のポイント

プログラミングのパラダイム

PHP は柔軟性のある動的言語で、いろんなプログラミングテクニックに対応している。 長年の間に劇的に成長してきた。PHP 5.0 でのオブジェクト指向モデルの追加 (2004 年)、 PHP 5.3 での無名関数や名前空間の追加 (2009 年)、そして PHP 5.4 でのトレイトの追加 (2012 年) などが特筆すべきところだろう。

オブジェクト指向プログラミング

PHP には完全なオブジェクト指向プログラミングの機能が搭載されている。 クラスや抽象クラス、インターフェイス、継承、コンストラクタ、クローン、 例外などなどといった機能も、当然使える。

関数プログラミング

PHP は、ファーストクラスの関数をサポートしている。 つまり、関数を変数に代入できるってことだ。 自分で定義した関数だろうがもともと組み込まれている関数だろうが、 変数で参照したり動的に実行したりできる。 何かの関数を別の関数の引数として渡すこと (高階関数っていう機能) もできるし、関数の返り値を別の関数にすることもできる。

再帰 (ある関数の中から自分自身を呼ぶこと) も PHP の機能としてサポートしている。 しかし、たいていの PHP コードはそれよりも逐次処理を重視している。

新型の無名関数 (クロージャにも対応したもの) が使えるようになったのは、PHP 5.3 (2009 年) 以降だ。

PHP 5.4 からは、クロージャをオブジェクトのスコープにバインドできるようになった。 また callable のサポートも強化され、ほとんどの場合で無名関数と互換性を持つようになった。

メタプログラミング

PHP はいろんな形式のメタプログラミングに対応しており、リフレクション API やマジックメソッドが使える。 マジックメソッドには __get()__set()__clone()__toString()、そして __invoke() などがあり、これらを活用すればクラスの振る舞いをフックできる。 Ruby の人がよく「PHP には method_missing がなくてさあ」とか言うけど、ちゃんと __call() とか __callStatic() があるよ。

名前空間

さっきも言ったとおり、PHP のコミュニティでは多くの人たちがいろんなコードを書いている。 ってことは、誰かが書いたライブラリと別の人が書いたライブラリでクラス名がダブってしまう可能性があるということだ。 両者が同じ名前空間を使っていたら、衝突して問題の原因となってしまう。

そんな問題を解決するのが 名前空間 だ。 マニュアルにあるとおり、名前空間っていうのは OS のファイルシステムのディレクトリと似てる。 同じ名前のふたつのファイルを同一ディレクトリに置くことはできないけど、 別々のディレクトリに分ければ共存できるというわけだ。 同様に、別々の名前空間に分ければ同じ名前のクラスがふたつあっても共存できる。簡単に言うと、そういうこと。

自分の書くコードにも、名前空間を指定することが大切だ。 そうすれば、誰か他の人があなたのコードを使うときに「これ、他のライブラリと競合しないかな」 と悩まずに済む。

名前空間のおすすめの使い方が PSR-0 にまとまっている。 この文書の狙いは、ファイルやクラスそして名前空間の命名規則を標準化して お互いに再利用しやすくすることだ。

2013年12月、PHP-FIG はオートローディングに関する新しい標準を定めた。 それが PSR-4 で、ゆくゆくは PSR-0 に取って代わることになるだろう。 現時点ではどちらも使える。というのも PSR-4 は PHP 5.3 以降が必須だけれど、今でも PHP 5.2 のプロジェクトはたくさんあって、それらが PSR-0 に従っているからだ。 これから新しくアプリケーションやパッケージを作るときにオートローダーの使いかたの参考にするのなら、まず間違いなく PSR-4 だ。

Standard PHP Library

Standard PHP Library (SPL) とは PHP に組み込まれた標準ライブラリで、 さまざまなクラスやインターフェイスを提供する。 よく使うデータ構造 (スタックやキュー、ヒープなど) やイテレータなどが含まれており、SPL のインターフェイスを実装したクラスをつくれば それをイテレータで反復処理されることもできる。

コマンドラインインターフェイス

PHP はもともとウェブアプリケーションを書くために作られたものだが、 コマンドラインインターフェイス (CLI) のプログラムを書くのにも便利だ。 コマンドラインのプログラムを書けば、テストやデプロイといった よくある作業を自動化する助けとなる。

CLI の PHP プログラムが便利なのは、アプリケーションのコードを使うときに わざわざウェブの UI を用意せずに済むところだ。 ただ、CLI の PHP スクリプトをウェブサーバーの公開ディレクトリに置くことは絶対禁止!

PHP をコマンドラインで実行してみよう。

> php -i

-i は、PHP の設定情報を phpinfo 関数みたいに表示するオプションだ。

-a オプションで対話シェルを使えるようになる。ruby の IRB とか、Python の対話シェルと同じようなものだ。 それ以外にも、いろんな コマンドラインオプション がある。

じゃあ、シンプルな “Hello, $name” プログラムを書いてみよう。hello.php というファイルを作って、 こんな内容にする。

<?php
if ($argc != 2) {
    echo "Usage: php hello.php [name].\n";
    exit(1);
}
$name = $argv[1];
echo "Hello, $name\n";

PHP のスクリプトを実行すると、コマンドラインの引数に関する変数がふたつ設定される。 $argc は整数値で、引数の を表し、 $argv は配列で、各引数の を含む。 最初の引数は、常に PHP スクリプトのファイル名となる。今回の場合なら hello.php だ。

exit() でゼロ以外の数値を返すと、コマンドが失敗したことをシェルに伝えることができる。 よく使われる終了コードは ここ で調べよう。

このスクリプトをコマンドラインから実行すると、次のようになる。

> php hello.php
Usage: php hello.php [name]
> php hello.php world
Hello, world

XDebug

ソフトウェア開発におけるいちばん便利なツールといえば、よくできたデバッガだ。 こいつがあれば、コードを実行しながらその状態を追いかけて、スタックの中身を監視したりできる。 XDebugはPHP用のデバッガで、さまざまなIDEに組み込んで使える。 ブレークポイントを設定したりスタックの中身を見たりできるんだ。 さらに、PHPUnitやKCacheGrindといったツールと組み合わせれば、 コードカバレッジ解析やプロファイリングもできる。

「実はいま困ってるんだよ。仕方なしにvar_dumpとかprint_rに頼ってるけど、もっといいやりかたがないかなあ…」 そんな君に必要なのは、たぶんデバッガだ。

XDebugのインストールはちょっと面倒だけど、 重要な機能として「リモートデバッグ」が使えるようになる。 ふだんはローカルで開発しているけど、テストはVMや別のサーバーでしているという人は、 今すぐにでも使えるようにしたい機能だろう。

慣例にしたがって、ApacheのVHostあるいは.htaccessファイルにこんな値を設定する。

php_value xdebug.remote_host=192.168.?.?
php_value xdebug.remote_port=9000

“remote host”と”remote port”は、それぞれ開発に使っているホストとIDEがリスンしているポートに対応する。 あとはIDEの設定で「接続をリスンする」モードに変えて、こんなURLを読み込むだけだ。

http://your-website.example.com/index.php?XDEBUG_SESSION_START=1

これで、IDEがスクリプトの実行に割り込んで、ブレークポイントを設定したり メモリの中身を調べたりできるようになる。

グラフィカルなデバッガを使えば、コードをステップ実行したり変数の中身を調べたり、 今の実行環境上でコードを評価したりといったことが簡単にできるようになる。 たいていのIDEには、xdebugを使ったグラフィカルなデバッグの仕組みが初めから組み込まれているか、 あるいはプラグインが用意されている。 MacGDBpというソフトもある。これは、xdebugを使うためのフリーでオープンソースなMac用GUI環境で、スタンドアロンで使える。

Back to Top

依存関係の管理

PHP のライブラリやフレームワークやコンポーネントって、大量に存在する。 きっと、あなたのプロジェクトでも何個か使っているだろう。そういうのを、プロジェクトの依存関係という。 つい最近まで、PHP には依存関係をうまく管理する仕組みがなかった。 手作業で管理していたところで、オートローダーのことも気にしないといけない。 もううんざりだ。

現時点でよく使われているパッケージ管理システムは、Composer と PEAR である。 どっちがおすすめかって?どっちもだ。

一般に、Composer のパッケージは、明示的に指定したプロジェクトの中にだけ存在する。 一方 PEAR のパッケージは、システム上のすべての PHP プロジェクトから使える。 ぱっと見た感じでは PEAR のほうがわかりやすそうに思えるかもしれないけど、 プロジェクトごとに依存関係を管理する方法にも利点がある。

Composer と Packagist

Composerは、PHP用の すばらしい 依存管理ツールだ。プロジェクト内の依存関係を composer.json ファイルに書いてシンプルなコマンドを打ち込めば、 Composer が自動的にそれをダウンロードしてくれるだけでなく、オートロードの設定までしてくれるんだ。

Composer に対応したライブラリは既にいろいろ出回っていて、自分のプロジェクトですぐに使える。 そんなパッケージをまとめたのが Packagist。これは、Composer 対応の PHP ライブラリをまとめた公式リポジトリである。

Composer のインストール

Composer はローカル (作業ディレクトリ) にインストールしてもよいし、グローバルに (/usr/local/bin などに) インストールしてもよい。ただし、ローカルにインストールする方法は今は非推奨となっている。 ローカルにインストールするには、プロジェクトのルートディレクトリに移動して次のコマンドを実行する。

curl -s https://getcomposer.org/installer | php

このコマンドは、 composer.phar (PHP バイナリアーカイブ) をダウンロードする。これを php コマンドで実行すれば、そのプロジェクトの依存関係を管理できる。 注意: ダウンロードしたコードを直接パイプで実行する前に、 まずはオンラインでコードを確認して安全であることを確かめておこう。

Windows でのインストール

Windowsの場合、一番簡単なのは ComposerSetup インストーラーを使う方法だ。 これは、すべてのユーザーで使えるようにインストールしたうえで $PATH も設定してくれるので、 あとはコマンドラインから composer を呼ぶだけで使えるようになる。

Composer の手動インストール

手動で Composer をインストールするのは初心者にはおすすめできない。 でもなぜか、さっきのインストール方法よりも手作業でのインストールをしたがる人もいるらしい。 さっきの対話的なインストールでは何をしていたのかというと、こんなことを確かめていたんだ。

手作業でのインストールではこういったチェックは一切行わない。 それでもいいの?

それでもいいという人向けに、手動での Composer のインストール方法を示す。

curl -s https://getcomposer.org/composer.phar -o $HOME/local/bin/composer
chmod +x $HOME/local/bin/composer

$HOME/local/bin (あるいは、その他あなたが指定した場所) にパスを通しておく必要がある。 これで、composer コマンドが使えるようになるだろう。

何かのドキュメントに「php composer.phar install で Composer を実行します」と書いてあれば、 その部分を次のように読み替えればいい。

composer install

このセクションでは、composer をグローバルにインストールしたものとして説明する。

依存関係の定義とインストール

Composer は、プロジェクトの依存関係を composer.json というファイルで管理する。 このファイルを手で書き換えてもいいし、Composer を使って編集してもいい。

composer require を実行すると、プロジェクトの依存関係を追加する。 もしまだ composer.json がなければ、新しいファイルを作る。 この例は、プロジェクトの依存関係に Twig を追加するものだ。

composer require twig/twig:~1.8

あるいは、 composer init コマンドを実行して、 自分のプロジェクト用の完全な composer.json ファイルを作ることもできる。 どちらの方法にせよ、一度 composer.json ファイルを作ってしまえば、 あとは Composer がすべての依存ライブラリをダウンロードして vendors/ にインストールしてくれる。 次のコマンドは、すでに composer.json ファイルを含むプロジェクトをダウンロードした場合にも使える。

composer install

次に、アプリケーションで最初に呼ばれる PHP ファイルにこんな行を追加する。 これは、Composer のオートローダーを使ってプロジェクトの依存ライブラリを読むよう指示している。

<?php
require 'vendor/autoload.php';

これで、依存ライブラリが使えるようになった。実際に使う場面で、必要に応じて読み込まれる。

依存関係の更新

Composer は composer.lock というファイルを作る。 これは、最初に php composer.phar install を実行したときにダウンロードした、各パッケージの正確なバージョンを記録しておくものだ。 他の開発者とプロジェクトを共有するときに composer.lock も一緒に配布しておくと、 他の人が php composer.phar install を実行したときにもまったく同じバージョンがインストールされるようになる。 依存関係を更新するには、 php composer.phar update を実行しよう。

これは、バージョンの要件を柔軟に定義できるので便利だ。 たとえば、バージョンに ~1.8 と書いた場合は「1.8.0 以降のバージョン。ただし 2.0.x-dev は含まない」と指定したことになる。 ワイルドカード * を使って 1.8.* にように指定してもいい。 これで、Composer で php composer.phar update を実行したときに、 定義した制約の範囲での最新版に依存ライブラリを更新してくれる。

更新通知

新バージョンのリリースの通知を受け取りたければ VersionEye にサインアップするといい。 このサービスは、自分の GitHub アカウントや BitBucket アカウントにある composer.json の内容を監視して、パッケージの新しいリリースがあればメールで教えてくれるものだ。

依存ライブラリのセキュリティ問題のチェック

Security Advisories Checker は、Webサービスとコマンドラインツールとして提供されている。 composer.lock ファイルを調べて、もし依存関係に更新が必要なら教えてくれるものだ。

PEAR

古くからあるもうひとつのパッケージ管理ツールが PEAR だ。 Composer とだいたい同じような感じだけど、違うところもある。

PEARの個々のパッケージは、ルールに従った構造にしておかないといけない。 つまり、PEARで使えるようにするにはパッケージの作者がきちんと準備しておかないといけないってことだ。 PEARで使うことを想定していないプロジェクトは、PEARではうまく使えない。

PEARはパッケージをグローバル環境にインストールする。つまり、 何かパッケージをインストールすれば、同じサーバー上のすべてのプロジェクトでそのパッケージが使えるようになる。 すべてのプロジェクトが同じパッケージの同じバージョンを使ってるというのなら、これは便利だ。 でも、プロジェクトによって使っているバージョンが違うなんていう場合はちょっとまずいことになる可能性がある。

PEARのインストール

PEARをインストールするには、pharインストーラをダウンロードして実行すればいい。 PEARのドキュメントを見れば、各種OS向けに インストール手順の詳しい説明 がある。

Linuxを使っている場合は、ディストリビューションが提供しているパッケージマネージャーもチェックしよう。 たとえば、DebianやUbuntuにはphp-pearというaptパッケージが用意されている。

パッケージのインストール

PEAR パッケージリストにあるパッケージなら、名前を指定して次のようにインストールできる。

pear install foo

別のチャネルで公開されているパッケージをインストールするには、まずそのチャネルを discover しないといけない。そして、インストールのときにもチャネル名を指定する。 詳しくは チャネルの使い方に関するドキュメント を参照すること。

PEARの依存関係をComposerで管理する

既にComposerを使っているけれどもPEARのコードもインストールしたいという場合は、 ComposerにPEARの依存関係を処理させることもできる。 たとえばこれは、pear2.php.netのコードをインストールする例だ。

{
    "repositories": [
        {
            "type": "pear",
            "url": "http://pear2.php.net"
        }
    ],
    "require": {
        "pear-pear2/PEAR2_Text_Markdown": "*",
        "pear-pear2/PEAR2_HTTP_Request": "*"
    }
}

最初のセクションでは、"repositories"を使ってpearリポジトリを「初期化」 (PEARの用語でいうと「discover」)する。 そして、requireセクションではパッケージ名を次のように指定する。

pear-channel/Package

“pear”というプレフィックスをハードコードすることで、衝突を回避している。 というのも、pearチャネルの名前と別のパッケージベンダーの名前が同じになってしまう可能性があるからだ。 このようにして、チャネルの短縮名(あるいは完全なURL)を使ってそのパッケージが属するチャネルを指定できるようにする。

このコードをインストールすると、venderディレクトリの中にチャネル名のディレクトリができあがって、 Composerのオートローダーを通して自動的に使えるようになる。

vendor/pear-pear2.php.net/PEAR2_HTTP_Request/pear2/HTTP/Request.php

このPEARパッケージを使うには、単純にこのように参照するだけでいい。

$request = new pear2\HTTP\Request();

Back to Top

コーディングに関する慣習

基本

PHP はとても懐が深い言語で、いろんなレベルの技術者が使えるし、手早く効率的にコードを書くことができる。 しかし実際に使っているうちに、初めて身につけたときの基本を忘れてしまい、 横着する方法とかあまりよろしくない習慣とかばかり身につけてしまう。 そんな風潮に一石を投じるために書いたのがこのセクションだ。PHPでコードを書くときの基本を忘れないようにしよう。

日付や時刻の扱いかた

PHP の DateTime クラスを使えば、日付や時刻の読み書き、比較、そして計算ができる。 PHP には DateTime クラス以外にも日付や時刻がらみの関数が大量にあるけど、 DateTime クラスにはちゃんとしたオブジェクト指向のインターフェイスがあるので たいていの場合はこのクラスを使ったほうがいい。 タイムゾーンだって扱えるけど、ここではそこまでは深追いしない。

DateTime を使って何かの操作をするためには、日付や時刻を表す文字列をファクトリーメソッド createFromFormat() でオブジェクトに変換するか、あるいは new \DateTime で現在の日時を取得する。format() メソッドを使えば、DateTime を文字列に戻して出力できる。

<?php
$raw = '22. 11. 1968';
$start = \DateTime::createFromFormat('d. m. Y', $raw);

echo 'Start date: ' . $start->format('m/d/Y') . "\n";

DateTime を使った計算をするときに使えるのが the DateInterval クラスだ。 DateTime には add()sub() といった関数があって、その引数に指定するのがこの DateInterval となる。 1日が86400秒であることを前提としたコードを書いてはいけない。 サマータイムとかタイムゾーンの移動がからむと、この前提はあっさり崩れてしまうからだ。 そんなときには DateInterval を使う。二つの日付の差を計算するときには diff() メソッドを使う。このメソッドは DateInterval を返し、結果を表示するのも簡単だ。

<?php
// $start をコピーして、1か月と6日を足す
$end = clone $start;
$end->add(new \DateInterval('P1M6D'));

$diff = $end->diff($start);
echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . "\n";
// Difference: 1 month, 6 days (total: 37 days)

DateTime オブジェクトどうしでごく普通に比較することもできる。

<?php
if ($start < $end) {
    echo "Start is before end!\n";
}

最後にもうひとつ DatePeriod クラスの例を示そう。繰り返し発生するイベントを順に処理するときに使える。 開始日時と終了日時を表す二つの DateTime 、そしてイベントの間隔を受け取って、すべてのイベントを返すものだ。

<?php
// $start から $end までの間のすべての木曜日を返す
$periodInterval = \DateInterval::createFromDateString('first thursday');
$periodIterator = new \DatePeriod($start, $periodInterval, $end, \DatePeriod::EXCLUDE_START_DATE);
foreach ($periodIterator as $date) {
    // 毎木曜日を表示する
    echo $date->format('m/d/Y') . ' ';
}

デザインパターン

アプリケーションをつくるときには、一般的なパターンに従ってコードを書くとよい。 同じくプロジェクトの全体構造についても、一般的なパターンに従おう。 なぜそうするといいかというと、自分のコードを管理しやすくなるし、 何がどうなっているのかを他の開発者にもわかってもらいやすくなるからだ。

フレームワークを使ってコードを書くと、上位レベルのコードやプロジェクトの構造のほとんどはそのフレームワークの流儀に従うことになる。 すでにそこには、いろんなパターンが適用されているだろう。 ただ、そのフレームワークの上で書く自分のコードの中でどんなパターンを適用するかは、自分次第だ。 一方、フレームワークを使わずにコードを書く場合はどうだろう。 自分が書こうとしているアプリケーションのタイプや規模に応じて、最適なパターンをみつける必要がある。

UTF-8の扱い

このセクションは、もともとAlex CabalPHP Best Practices向けに書いたものだ。この記事をもとに、UTF-8について説明する。

一発で済ませる方法はない。注意して、きちんと一貫性を保つこと。

今のところPHPは、低レベルではUnicodeをサポートしていない。 PHPでUTF-8文字列をきちんと処理する方法もあるにはあるが、簡単ではない。さらに、ウェブアプリケーションのあらゆるレベル (つまり、HTMLやSQLからPHPまで)に手を入れる必要がある。 ここでは、それらについて、現実的な範囲で手短にまとめた。

PHPレベルでのUTF-8

文字列の連結や変数への代入などの基本操作については、UTF-8だからといって何か特別なことをする必要はない。 しかし、大半の文字列関数(strpos()strlen() など)については、そういうわけにはいかない。 これらの関数には、対応する関数として mb_* が用意されていることが多い。 たとえば mb_strpos()mb_strlen() だ。 これらの mb_* 関数は マルチバイト文字列拡張モジュール が提供するもので、 Unicode文字列を扱えるように設計されている。

Unicode文字列を扱う場合は、常に mb_* 関数を使う必要がある。 たとえば、UTF-8文字列に対して substr() を使うと、その結果の中に文字化けした半角文字が含まれてしまう可能性がある。 この場合、マルチバイト文字列のときに使うべき関数は mb_substr() だ。

常に mb_* 関数を使うように覚えておくのが大変なところだ。 たとえ一か所でもそれを忘れてしまうと、それ以降の Unicode 文字列は壊れてしまう可能性がある。

そして、すべての文字列関数に mb_* 版があるわけではない。 自分が使いたい関数にマルチバイト版がないだって? ご愁傷様。

すべてのPHPスクリプトの先頭(あるいは、グローバルにインクルードするファイルの先頭)で mb_internal_encoding() 関数を使わないといけないし、スクリプトの中でブラウザに出力するつもりなら、それだけではなく mb_http_output() 関数も使わなければいけない。 すべてのスクリプトでエンコーディングを明示しておけば、後で悩まされることもなくなるだろう。

さらに、文字列を操作する関数の多くには、文字エンコーディングを指定するためのオプション引数が用意されている。 このオプションがある場合は、常に UTF-8 を明示しておくべきだ。 たとえば htmlentities() には文字エンコーディングを設定する引数があるので、 UTF-8 文字列を扱うなら常にそう指定しておかないといけない。 PHP 5.4.0 以降では、 htmlentities()htmlspecialchars() のデフォルトエンコーディングが UTF-8 に変わった。

最後に、他の人たち向けに配布するつもりのアプリケーションなど、その実行環境で mbstring が使えるかどうか定かではない場合は、 Composer の patchwork/utf8 パッケージを使うことも検討しよう。 これは、もし mbstring があればそれを使い、なければ非 UTF-8 関数にフォールバックするというものだ。

データベースレベルでのUTF-8

PHP スクリプトから MySQL に接続する場合は、上で書いた注意をすべて守ったにもかかわらず UTF-8 文字列がそれ以外のエンコーディングで格納されてしまう可能性がある。

PHP から MySQL に渡す文字列を確実に UTF-8 として扱わせるには、データベースとテーブルの文字セットや照合順序の設定を、すべて utf8mb4 にしておく必要がある。さらに、PDO の接続文字列にも、文字セットとして utf8mb4 を指定する。 詳細は以下のコードを参照すること。 これ、試験に出るよ。

UTF-8 を完全にサポートするには、文字セット utf8mb4 を使わないといけない。 utf8 はダメ!!! その理由が知りたければ「あわせて読みたい」を参照すること。 Further Reading for why.

ブラウザレベルでのUTF-8

mb_http_output() 関数を使えば、PHP スクリプトからブラウザへの出力が UTF-8 文字列になることを保証できる。

あとは、HTTPレスポンスの中で、そのページをUTF-8として扱うようブラウザに指示する必要がある。 昔ながらのやりかたは、そのページの <head> タグの中で charset の <meta> タグ を指定することだった。 この方法でも間違いではないが、 Content-Type ヘッダーの中で文字セットを指定したほうが、 ずっと高速になる

<?php
// PHP に対して、今後このスクリプトの中では UTF-8 文字列を使うことを伝える
mb_internal_encoding('UTF-8');
 
// PHP に対して、ブラウザに UTF-8 で出力することを伝える
mb_http_output('UTF-8');
 
// UTF-8 のテスト用文字列
$string = 'Êl síla erin lû e-govaned vîn.';
 
// 何らかのマルチバイト関数で文字列を操作する。
// ここでは、デモの意味も込めて、非ASCII文字のところで文字列をカットしてみた。
$string = mb_substr($string, 0, 15);
 
// データベースに接続し、この文字列を格納する。
// このドキュメントにある PDO のサンプルを見れば、より詳しい情報がわかる。
// ここでの肝は、 `set names utf8mb4` コマンドだ。
$link = new \PDO(   
                    'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
                    'your-username',
                    'your-password',
                    array(
                        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                        \PDO::ATTR_PERSISTENT => false
                    )
                );
 
// 変換した文字列を、UTF-8としてデータベースに格納する。
// DBとテーブルの文字セットや照合順序が、ちゃんとutf8mb4になっているかな?
$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->bindValue(2, $string);
$handle->execute();
 
// 今格納したばかりの文字列を取り出して、きちんと格納できているかどうかを確かめる
$handle = $link->prepare('select * from ElvishSentences where Id = ?');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->execute();
 
// 結果をオブジェクトに代入して、後でHTMLの中で使う
$result = $handle->fetchAll(\PDO::FETCH_OBJ);

header('Content-Type: text/html; charset=utf-8');
?><!doctype html>
<html>
    <head>
        <title>UTF-8 テストページ</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // 変換したUTF-8文字列が、ブラウザに正しく出力されるはず
        }
        ?>
    </body>
</html>

あわせて読みたい

Back to Top

依存性の注入

Wikipediaによると、

Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it possible to change them, whether at run-time or compile-time.

依存性の注入とはソフトウェアデザインパターンの1つである。依存関係をソースコードから排除して、実行時あるいはコンパイル時にその依存関係を変更できるようにする。

とのことだが、この説明は必要以上に小難しく言っているように思える。 依存性の注入とはコンポーネントに依存関係を渡せる仕組みのことで、コンストラクタで注入したりメソッド呼び出しをしたりプロパティを設定したりといった方法がある。 それだけのことだ。

基本的な概念

考えかたを説明するために、シンプルな例を示そう。細かいところは手を抜いているけどね。

ここに Database クラスがある。こいつがデータベースとやりとりするためには、何らかのアダプターが必要だ。 そこで、コンストラクタの中でアダプターのインスタンスを作っている。アダプターの名前を直接指定してね。 こんな書きかただと Database クラスを単体でテストするのが難しくなるし、 このクラスがアダプターと密接に結びついてしまう。

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct()
    {
        $this->adapter = new MySqlAdapter;
    }
}

class MysqlAdapter {}

このコードを書き換えて依存性の注入を使うようにすれば、この依存関係を緩やかにできる。

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(MySqlAdapter $adapter)
    {
        $this->adapter = $adapter;
    }
}

class MysqlAdapter {}

これで、依存関係を外部から Database クラスに渡せるようになった。このクラス自身に作らせる必要がなくなったんだ。 コンストラクタで指定する以外にも、依存関係を引数で受け取ってそれを設定するようなメソッドを新たに作ってもいいし、 あるいは $adapter という public プロパティを作って、依存関係を直接設定できるようにしてもいい。

複雑な問題

これまでに依存性の注入について調べたことがある人なら、 “制御の反転(IoC:Inversion of Control)”“依存関係逆転の原則(DIP:Dependency Inversion Principle)” といった言葉に見覚えがあるだろう。これらは複雑な問題で、依存性の注入によって解決できるものだ。

制御の反転

制御の反転とは、文字通り、システムの「制御を反転」することだ。システム全体の制御を、オブジェクトから切り離したままで行う。 依存性の注入の文脈では、依存関係の制御や作成を、システム内のどこか別の場所でやるってことを意味する。

PHPのフレームワークでも、制御の反転は実現されてきた。でも、実際のところ、何の制御を反転しているのだろう。そして、反転した結果、制御はどこに行ってしまったのだろう。 たとえば、たいていのMVCフレームワークには、あらゆるコントローラの親になる基底コントローラが用意されている。 そして、それを継承しなければ依存関係のアクセスできない。 これはこれで制御の反転だが、でも、依存関係を緩くしたというよりは、単純に依存関係を別の場所に移しただけのことだ。

依存性の注入を使えば、これをもっとすっきりと解決できる。依存関係が必要になったときに、必要なものだけを注入すればいい。 ハードコーディングする必要はない。

依存関係逆転の原則

依存関係逆転の原則は、オブジェクト指向設計の原則である S.O.L.I.D の “D” にあたるもので、 「抽象に依存しろ。具象に依存するな」 という原則だ。 簡単に言うと、依存関係はインターフェイスや抽象クラスに対して設定すべきものであり、それを実装したクラスに対して設定してはいけないってこと。 先ほどの例をこの原則に沿って書き直すと、こんなふうになる。

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;
    }
}

interface AdapterInterface {}

class MysqlAdapter implements AdapterInterface {}

これで Database クラスは、具象クラスではなくインターフェイスに依存するように変わった。で、いったい何がうれしいんだろう。

こんな場面を考えてみよう。君は今、とあるチームの一員として作業をしている。アダプターを作っているのは別のメンバーだ。 最初の例だと、その人がアダプターを完成させるまでは、自分のコードのユニットテストのモックを作れない。 インターフェイスに依存するようにした新しいバージョンだと、そのインターフェイスを使ってモックを作ることができる。 同僚が作るアダプターもそのインターフェイスに沿っているとわかっているからだ。

そんなことより、もっとすばらしいメリットもある。こっちの方式のほうが、コードがずっとスケーラブルになるんだ。 仮に将来、別のデータベースに移行することになったとしよう。 そんな場合でも、このインターフェイスを実装した新しいアダプターを書いたらそれでおしまいだ。 それ以外は何もいじる必要がない。 新しいアダプターがきちんと決まりごとに従っているということを、インターフェイスが保証してくれるからだ。

コンテナ

DIコンテナについてまず知っておくべきなのは、DIコンテナを使ってさえいれば「依存性の注入」ができるわけではないってことだ。 DIコンテナは、依存性の注入を実現するための便利な道具として使える。 でも、使い方をミスって、サービスロケーションというアンチパターンを作ってしまっていることも多い。 DIコンテナをサービスロケーターとしてクラスに組み込んでしまうと、 依存関係を別の場所に移そうとしていたはずなのに、よりきつい依存関係を作り込むことになる。 おまけにそのコードはわかりにくくなってしまうし、テストもしづらくなる。

いまどきのフレームワークにはたいていDIコンテナが用意されていて、依存関係をコード以外のところで設定できる。 要するに、アプリケーションのコードがすっきりと書けて、フレームワーク本体とあまり密接につながりすぎないようにできるということ。

あわせて読みたい

Back to Top

データベース

PHP のコードを書いていると、情報を保存するためにデータベースを使うことが多くなる。 データベースを操作するには、いくつかの方法がある。 PHP 5.1.0 の時代までは 、おすすめの方法は mysqlipgsqlmssql などのネイティブドライバを使うことだった。

このデータベースしか使わないよ!っていうのならネイティブドライバもいい。 でもたとえば、MySQL を使っているけど一部は MSSQL であるとか、 Oracle にもつなぐことがあるとか、そんな場合は同じドライバでは対応できない。 そして、データベースが変わるたびに新しい API を覚えないといけないことになる。 ばかげた話だ。

MySQL 用の拡張モジュール

PHP 用の mysql 拡張モジュールの開発はすでに終了しており、 PHP 5.5.0で正式に「廃止予定」となった。 つまり、近い将来に削除されるということだ。 もし未だに mysql_connect() とか mysql_query() のような mysql_* 系の関数を使っているなら、 いずれ書き直さざるを得なくなる。mysql を使っているプログラムがあれば、 今のうちに mysqliPDO を使うように書き直しておこう。 そうすれば、後になって焦らずに済む。

今から新しく書き始めるっていうのなら、mysql 拡張モジュールを使うっていう選択肢はナシだ。 MySQLi 拡張モジュール か PDO を使うこと。

PDO 拡張モジュール

PDO はデータベースとの接続を抽象化するライブラリだ。PHP 5.1.0 以降に組み込まれていて、 いろんなデータベースを同じインターフェイスで扱える。 MySQLを使うコードもSQLiteを使うコードも、基本的には同じようになるってことだ。

// PDO + MySQL
$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT some\_field FROM some\_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

// PDO + SQLite
$pdo = new PDO('sqlite:/path/db/foo.sqlite');
$statement = $pdo->query("SELECT some\_field FROM some\_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

PDO は、SQL のクエリをデータベースにあわせて変換するものではないし、 もともと存在しない機能をエミュレートするものでもない。 純粋に、いろんなデータベースに同じ API で接続するためのものだ。

もっと大切なことは、PDO を使えば、外部からの入力 (ID など) を安全に SQL クエリに埋め込めるということだ。データベースへの SQL インジェクション攻撃を心配しなくてもよくなる。 そのためには、PDO ステートメントとバインド変数を使えばよい。

数値の ID をクエリ文字列として受け取る PHP スクリプトを考えてみよう。 渡された ID を使って、データベースからユーザー情報を取り出す。 最初に示すのは 悪い方法 だ。

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- ダメ、ゼッタイ!

こんな恐ろしいコードを書いちゃいけない。 これは、クエリ文字列のパラメータを SQL に直に埋め込んでいることになる。 あっという間に SQLインジェクション 攻撃を食らうだろう。 どこかのずるがしこい攻撃者が id に渡す内容をひと工夫して http://domain.com/?id=1%3BDELETE+FROM+users みたいな URL を呼んだとしよう。 このとき変数 $_GET['id'] の内容は 1;DELETE FROM users となり、全ユーザーが消えてしまうことになる! こんな書き方ではなく、PDO のバインド変数で ID を受け取らないといけない。

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT); // <-- PDOが自動的にエスケープ処理をする
$stmt->execute();

これが正しい方法だ。この例では、PDO ステートメントでバインド変数を使っている。 外部からの入力である ID がエスケープされてからデータベースに渡るので、 SQL インジェクション攻撃を受けることがなくなる。

あと、データベースのコネクションはリソースを使うということにも気をつけよう。 コネクションを明示的に閉じることを忘れたせいでリソースを食いつぶしてしまうなんて話は珍しくない。 とはいえ、これは別にPHPに限った話でもないけどね。 PDOを使っている場合は、オブジェクトへの参照をすべて削除して(Nullを代入するなどして) オブジェクトを破棄してしまえば、暗黙のうちにコネクションを閉じることが保証される。 オブジェクトを明示的に破棄しない場合は、スクリプトの実行が終わった時点でPHPが自動的に接続を閉じる。 もちろん、持続的接続を使っている場合は別だ。

データベースとのやりとり

PHPを勉強し始めたばかりの開発者がやってしまいがちなのが、データベースとのやりとりと画面表示のロジックをごちゃまぜにしてしまうことだ。 たとえば、こんなコードになる。

<ul>
<?php
foreach ($db->query('SELECT * FROM table') as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>";
}
</ul>

これは、あらゆる意味でよろしくない。 まず何と言っても、デバッグしづらいし、テストもしづらいし、読みづらい。 あと、何も制限をかけていないので、大量のフィールドを出力してしまうことになる。

同じことをもっとすっきり行う方法はいろいろある。OOPが好きな人向けのやりかたもあれば 関数型プログラミングが好きな人向けのやりかたもある。 が、まずは、分離することからはじめよう。

これが第一歩だ。

<?php
function getAllSomethings($db) {
    return $db->query('SELECT * FROM table');
}

foreach (getAllFoos() as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>"; // BAD!!
}

少しはマシになった。この二つを別々のファイルに分けてしまえば、きれいに分離できるだろう。

次に、このメソッドを保持するクラスを用意する。「モデル」だ。 そして、シンプルな .php ファイルをもうひとつ作って、そこに画面表示ロジックを入れる。「ビュー」だ。 これで、何となく MVC っぽくなった。これは、多くの フレームワーク で使われている、OOPのアーキテクチャだ。

foo.php

<?php

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

// モデルを読み込む
include 'models/FooModel.php';

// インスタンスを作る
$fooList = new FooModel($db);

// ビューを表示する
include 'views/foo-list.php';

models/FooModel.php

<?php
class Foo()
{
    protected $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
    }

    public function getAllFoos() {
        return $this->db->query('SELECT * FROM table');
    }
}

views/foo-list.php

<? foreach ($fooList as $row): ?>
    <?= $row['field1'] ?> - <?= $row['field1'] ?>
<? endforeach ?>

本質的にこれは、今どきのフレームワークがやっていることと、ほぼ同じだ。それを手作業でやってみた。 毎回こんなことをする必要はないかもしれないが、画面表示とデータベース操作を混在させすぎると、 ユニットテスト をしたくなったときにやっかいな問題が発生してしまう。

PHPBridge に、 Creating a Data Class という記事が公開されている。 ここで扱ったのと同じ話題をとりあげていて、データベース操作に慣れた人にとって最適の記事だ。

抽象化レイヤー

多くのフレームワークは、データベースの抽象化レイヤーを用意している。PDOを利用しているものもあれば、そうでないものもある。 あるデータベースには存在するけれども別のデータベースには存在しない機能などをエミュレートするために、 クエリーをPHPのメソッドでラップしたりするものだ。 PDOではデータベースへの接続の抽象化しかしないが、フレームワークの抽象化レイヤーは、それ以上のことをしてくれる。 もちろんそれなりのオーバーヘッドが発生するだろう。 でも、MySQLでもPostgreSQLでもSQLiteでも動くようなアプリケーションを書いているのなら、 多少のオーバーヘッドを割り引いてでも、すっきりしたコードを書けるほうがうれしいだろう。

以下の抽象化レイヤーは、 PSR-0PSR-4 で定められた標準名前空間に従っている。 これらはアプリケーションを問わずに利用できる。

Back to Top

テンプレート

テンプレートは、コントローラーやドメインロジックを、プレゼンテーションロジックから切り離すための便利な手段だ。 テンプレ—トには、アプリケーションで使うHTMLを含めることが多いが、それ以外のフォーマット(XMLなど)を含めることもある。 テンプレートのことを「ビュー」と呼ぶこともある。いわゆる model–view–controller (MVC) パターンの、二番目の要素 の一部 だ。

メリット

テンプレートを使う主なメリットは、画面に表示する内容をアプリケーションから切り離せることだ。 テンプレートは、フォーマット済みのコンテンツを表示するという責務だけを負う。 データを検索したり、データベースに保存したりなどといった、雑多なタスクは気にしない。 このおかげで、すっきりしたリーダブルなコードが書けるようになる。チームで開発する際などには、これが特に有用だ。 開発者はサーバー側のコード(コントローラやモデル)、そしてデザイナーはクライアント側のコード(マークアップ) などという作業分担をしやすくなる。

テンプレートには、プレゼンテーションのコードの構造を改善するという効果もある。 テンプレートは一般的に「views」などのフォルダにまとめられて、それぞれ個別のファイルになっていることが多い。 こうしておけばコードの再利用がしやすくなる。大規模なコードブロックを、小さめの再利用しやすいパーツ(パーシャルと呼ばれることもある)に分割できるからだ。 たとえば、サイトのヘッダやフッタをテンプレートにしておけば、各ページのテンプレートにそれをインクルードできるようになる。

利用するライブラリによっては、テンプレートを使うことで、よりセキュリティを確保できるようにもなる。 ユーザーが作るコンテンツを自動的にエスケープしてくれたりする機能を持つようなテンプレートが、それにあたる。 さらに、サンドボックス機能を提供するライブラリもある。これは、デザイナーが、あらかじめ許可された変数と関数しか利用できないようにする仕組みだ。

プレーンなPHPによるテンプレート

プレーンなPHPによるテンプレートとは、単にPHPのコードを使ったテンプレートという意味だ。 ごく自然な選択肢だとも言える。そもそもPHP自体がテンプレート言語だし。 あ、これって単に、PHPのコードをHTMLとかにも埋め込めるよねっていう以上の深い意味はないからね。 PHPの開発者にとっては、新しい構文を覚えずに済むというメリットがある。 どんな関数が使えるのかもわかっているし、ふだんPHPを書いているエディタのシンタックスハイライトや 自動補完機能も、そのまま使える。 その上、プレーンなPHPのテンプレートは高速であることが多い。コンパイルが不要だからだ。

いまどきのPHPフレームワークは、たいてい何らかの仕組みのテンプレートシステムを持っている。 その多くでデフォルトになっているのが、プレーンPHPによるテンプレートだ。 フレームワーク以外では、PlatesAura.View といったライブラリがプレーンPHPテンプレートを使いやすくしてくれる。継承やレイアウト、拡張などの便利なテンプレート機能を用意してくれるんだ。

プレーンPHPテンプレートのシンプルな例

Plates ライブラリを使った。

<?php // user_profile.php ?>

<?php $this->insert('header', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

<?php $this->insert('footer') ?>

プレーンPHPテンプレートで継承を使う例

Plates ライブラリを使った。

<?php // template.php ?>

<html>
<head>
    <title><?=$title?></title>
</head>
<body>

<main>
    <?=$this->section('content')?>
</main>

</body>
</html>
<?php // user_profile.php ?>

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

コンパイル形式のテンプレート

PHPはオブジェクト指向言語として成熟してきてはいるものの、テンプレート言語としては いまいち だ。 コンパイル形式のテンプレート、たとえば TwigSmarty* が、この穴を埋めてくれる。テンプレートに特化した、新しい構文を用意してくれるんだ。 自動エスケープから継承や制御構文まで、コンパイル形式のテンプレートは、いかに読み書きしやすく、安心して使えるかを重視して作られている。 さらに、コンパイル形式のテンプレートは、別の言語でさえも使うことができる。Mustache がそのよい例だ。 テンプレートをコンパイルする時間がかかるので、多少はパフォーマンスに影響する。 しかし、適切にキャッシュをすれば、その影響は微々たるものだ。

*Smartyには自動エスケープ機能があるけど、これはデフォルトでは無効になっている。

コンパイル形式のテンプレートのシンプルな例

Twig ライブラリを使った。

{% include 'header.html' with {'title': 'User Profile'} %}

<h1>User Profile</h1>
<p>Hello, {{ name }}</p>

{% include 'footer.html' %}

コンパイル形式のテンプレートで継承を使う例

Twig ライブラリを使った。

// template.php

<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>

<main>
    {% block content %}{% endblock %}
</main>

</body>
</html>
// user_profile.php

{% extends "template.html" %}

{% block title %}User Profile{% endblock %}
{% block content %}
    <h1>User Profile</h1>
    <p>Hello, {{ name }}</p>
{% endblock %}

あわせて読みたい

記事やチュートリアル

ライブラリ

Back to Top

エラーと例外処理

エラー

例外処理を重視するプログラミング言語では、何か問題が起こったらすぐに例外を投げる。 それはそれでいいことではあるが、PHPはそうではなく、「例外処理も使える」プログラミング言語だ。 PHPには例外処理の仕組みがあるし、コアの中でもオブジェクトを扱うときには例外処理を行うことが増えている。 でも、PHPは基本的に、よっぽど致命的なエラーが発生しない限りは何があろうと処理を続行しようとする。

たとえば、こんなコードを考えてみよう。

$ php -a
php > echo $foo;
Notice: Undefined variable: foo in php shell code on line 1

単純にnoticeレベルのエラーになるだけで、PHPはそのまま処理を続行する。 例外処理を重視する世界からやってきた人にとっては、これは少しキモいと思うかもしれないね。 たとえばPythonなら、未定義の変数を参照しようとすると、例外が発生する。

$ python
>>> print foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined

実際の違いは、こういうことだ。 Pythonは、些細なことにまでこだわることで、プログラマーが余計な心配(「もし○○だったら、その場合は…」と考えたり、エッジケースを考慮したりなど) をせずに済むようにしている。 一方PHPは、どうしようもないエラーが発生しないかぎりは、一応エラーを報告したうえで処理を続行する。

エラーの深刻度

PHPのエラーは、何段階かの深刻度レベルに別れている。PHPでよく使われるメッセージの形式は、 エラー(error)と注意(notice)そして警告(warning)だ。 それぞれ別々の深刻度レベルが設定されていて、 E_ERRORE_NOTICE、そして E_WARNING になる。 「エラー」は実行時の致命的な問題で、ふつうはコードの書きかたがまずいせいで発生する。 これは修正しなければいけない。というのも、これが発生するとPHPの実行がそこで止まってしまうからだ。 「警告」は致命的ではない問題で、スクリプトの実行は止まらない。 「注意」は助言みたいなもので、問題を起こす可能性があるスクリプトを実行したときに発生する。 これもスクリプトの実行は止まらない。

もうひとつ、コンパイル時に発生する E_STRICT という形式のメッセージもある。 これは、相互運用性や将来のバージョンのPHPとの互換性を考えたときに、コードを書き換えたほうがいいと提案するためのメッセージだ。

PHPのエラー報告の挙動の変更

エラー報告の挙動は、PHPの設定で変更することもできるしPHPの関数で変更することもできる。 組み込みの関数 error_reporting() にエラーレベル定数を渡せば、そのスクリプトの実行中に どのレベルのエラー処理をするのかを設定できる。 たとえば、警告(warning)とかエラー(error)は表示させたいけれども、別に注意(notice)は見たくないという場合は、こんなふうにすればいい。

error_reporting(E_ERROR | E_WARNING);

画面に表示するかしないか(開発時に有効)だけでなく、ログに出力するかしないか(運用時に有効)も制御できる。 詳細は エラーレポート を参照。

インラインでのエラーの抑制

「ここでのエラーは無視すること」とPHPに指示することもできる。そのときに使うのが、 エラー制御演算子 @ だ。この演算子を何かの式の前に書くと、 その式で発生したあらゆるエラーは黙って揉み消される。

echo @$foo['bar'];

これは、もし $foo['bar'] が存在すればその内容を出力するが、仮に変数 $foo やキー 'bar' が存在しなくてもエラーにはならない。単純に null を返して何も表示しないだけだ。 もしエラー制御演算子がなかったら、 PHP Notice: Undefined variable: foo だとか PHP Notice: Undefined index: bar のようなエラーになる。

一見、よさげな機能だと感じるかもしれない。でも、これを使うと、あまり望ましくない代償を払うことになる。 まず、PHPでは、 @ つきの式は @ なしの式よりも処理効率が落ちてしまう。 「早まった最適化は諸悪の根源だ」とはいうものの、 もしパフォーマンスを重視するアプリケーションやライブラリを作っているのなら、 エラー制御演算子がパフォーマンスに及ぼす悪影響を知っておくべきだ。

次に、エラー制御演算子を使うと、発生したエラーが 完全に 隠蔽されてしまう。 画面にも表示されなければ、ログに書き出されることもない。 また、運用中のPHPシステムのエラー制御演算子を無効にする仕組みはない。 今見ているエラーが仮に些細なものだとしよう。だとしても、それ以外に無視できないエラーが発生するかもしれない。 そんなエラーも、同様に隠蔽されてしまう。

エラー制御演算子を使わずにすむ道があるなら、その方向で考えるべきだ。 たとえばさっきのコードなら、こんなふうに書き直せばいい。

echo isset($foo['bar']) ? $foo['bar'] : '';

エラーの抑制を使うのが理にかなっている場面として考えられるのは、 たとえば fopen() を使っていてファイルの読み込みに失敗した場合だ。 もちろん実際に読み込む前にファイルがあるかどうかをチェックするだろうが、 そのチェックが終わってから実際に fopen() で読み込むまでの間に ファイルが削除されるかもしれない(まあありえないだろうけど、可能性はゼロではない)。 そんな場合、 fopen() が false を返し、 そして エラーも発生する。 ほんとはPHP側で何とかしてもらいたいところだけれど、現状ではエラーを抑制するくらいしか手がない。

さっき、稼働中のPHPシステムではエラー制御演算子を無効化できないといったけれど、 実は、 xDebug の設定 xdebug.scream を使えば、エラー制御演算子を無効化できる。 php.ini に、こんなふうに書けばいい。

xdebug.scream = On

実行時に設定するなら、 ini_set 関数を使えばいい。

ini_set('xdebug.scream', '1')

PHPの拡張モジュール “Scream” にはxDebugと同じような機能がある。 ただ、Screamの場合は、設定項目の名前が scream.enabled になる。

この機能が役立つのは、コードのデバッグ中など、エラーから情報を読み取りたいときだ。 screamを使うときには気をつけて、あくまでも一時的なデバッグ用のツールとして使うようにしよう。 エラー制御演算子を無効にしたままでは正常に動かないというライブラリって、結構多いよ。

ErrorException

PHPは、例外が大好きな人たちにも対応したプログラミング言語だ。 ほんの数行のコードを足すだけで、例外に対応できる。 基本的に、エラーが発生したときには、 ErrorException クラスを使って「例外」を投げればいい。 このクラスは、 Exception クラスを継承したものだ。

これは、SymfonyやLaravelみたいな今どきのフレームワークでもよく使われている方法だ。 Laravelには、デフォルトですべてのエラーを例外扱いして表示する機能がある。その際に使うのは Whoops! パッケージだ。 この機能を有効にするには app.debug スイッチをオンにする。スイッチをオフにすれば、この機能は無効になる。

開発中は、エラーを例外として投げるようにしておくと、その処理をしやすくなる。 開発中にもし例外が発生したら、それをcatch文でラップして、その状況に対応する処理を書くこともできる。 例外をひとつキャッチするたびに、アプリケーションはほんの少しずつ頑丈になっていく。

もっと詳しいことが知りたい、あるいは ErrorException クラスを使ったエラー処理について調べたいという人は、 ErrorException クラス のドキュメントを読もう。

例外処理

最近はやりのプログラミング言語には、たいてい例外処理の仕組みがある。でも PHP の人は見落としがちだ。 たとえば Ruby なんかは例外処理を使いまくっている。 HTTP リクエストに失敗したときもデータベースへのクエリが失敗したときも、画像ファイルが見つからなかったときでさえも、 Ruby のプログラム (あるいはその中で使っている gem) は例外を投げる。 なので、画面を見ればすぐに「ああ、何か間違えたんだな」とわかる。

PHP はそのあたりが極めて緩くて、たとえば file_get_contents() が失敗しても単に FALSE を返して警告を出すだけだ。古いフレームワークの多くもそんな感じだ。 たとえば CodeIgniter の場合は、単に false を返してログファイルにメッセージを書き出すだけ。 原因を知りたければ $this->upload->get_error() みたいなメソッドを実行することになる。 何が問題かというと、まずエラーが発生したのかどうかを自分で調べないといけないこと。 そして次に、エラー情報を取得する方法をドキュメントで調べないといけないこと。 もっとはっきりわかるようにしてくれたらいいのに。

別の問題もある。何かのクラスがエラーを画面に投げっぱなしにしてそのまま終わるような場合だ。 そんなことをすれば、エラーがあったときにプログラム中で動的に対応することができなくなってしまう。 そんな場合は、エラーではなく例外を発生させないといけない。 例外にしておけば開発者がエラーに気づけるし、プログラムの中で対応できるようになる。 たとえばこんな感じだ。

<?php
$email = new Fuel\Email;
$email->subject('タイトル');
$email->body('ごきげんいかが?');
$email->to('guy@example.com', '誰かさん');

try
{
    $email->send();
}
catch(Fuel\Email\ValidationFailedException $e)
{
    // 検証に失敗した
}
catch(Fuel\Email\SendingFailedException $e)
{
    // ドライバがメールを送れなかった
}
finally
{
    // 例外が発生してもしなくても、ここは必ず実行される
}

SPL の例外

汎用的な Exception クラスには、開発者がデバッグするためのコンテキスト情報がほとんど含まれていない。 これを改善するには、特化型の Exception を作ればいい。つまり、Exception クラスのサブクラスを作るってことだ。

<?php
class ValidationException extends Exception {}

こんなふうにすれば、catch ブロックを複数用意してそれぞれの例外で別の処理をできるようになる。 その結果、自作の例外クラスが 大量に できあがってしまうかもしれないが、 SPL 拡張モジュール が用意する例外クラスを使えば少しはましになるだろう。

たとえば、マジックメソッド __call() を使っているときに、無効なメソッドを要求されたとしよう。 標準の Exception クラスを使うのは曖昧すぎるし、そのためだけに専用の例外クラスを作るのも何だし、 という場合には単に throw new BadMethodCallException; とすればよい。

Back to Top

セキュリティ

ウェブアプリケーションのセキュリティ

世の中には悪い人たちがいて、あなたの書いたウェブアプリケーションもきっと狙われている。 必要な対策をして、ウェブアプリケーションのセキュリティを固めておくことが大切だ。 ありがたいことに、The Open Web Application Security Project (OWASP) の人たちが、既知のセキュリティ問題とその対策をまとめてくれている。 セキュリティが気になる開発者は必読だ。

パスワードのハッシュ処理

誰もがいつかは、ログイン機能を持つ PHP アプリケーションを書くことになる。 ユーザー名とパスワード (のハッシュ) をデータベースに保存して、 ユーザーのログイン時にそれを使って認証するというやつだ。

データベースにパスワードを保存するときは、適切に ハッシュ することが大切だ。 パスワードのハッシュは不可逆な操作で、ユーザーのパスワードに対して一方通行で行う。 できあがる結果は固定長の文字列で、元には戻せない。 つまり、このハッシュを別のハッシュと比較すれば元の文字列どうしが一致するかどうかは判断できるが、 元の文字列が何だったかはわからないってことだ。 パスワードをハッシュせずにデータベースに保存していると、 万一第三者に不正アクセスされた場合に、すべてのユーザーアカウントが乗っ取られてしまう。 別のサービスでも同じパスワードを使い回しているって人も、中にはいるかもしれない。 なので、セキュリティについては真剣に考える必要がある。

password_hashによるパスワードのハッシュ

PHP 5.5からは、新たにpassword_hash関数が使えるようになった。 現時点では、この関数はBCryptを使っている。これは、現在のPHPがサポートしているアルゴリズムの中では最強のものだ。 必要に応じて、将来はもっと強力なアルゴリズムをサポートするように更新されるだろう。 この関数をPHP 5.5より前のバージョンでも使えるようにするため、password_compatライブラリも作られた。 このライブラリはPHP 5.3.7以降で使える。

この例では、文字列をハッシュした後でそのハッシュを新たな文字列と比較している。 二つの文字列は違っている(‘secret-password’と’bad-password’)ので、このログインは失敗する。

<?php
                      
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    // パスワードが一致した
} else {
    // パスワードが一致しなかった
}

データのフィルタリング

PHP のコードに外部から渡される入力は、絶対に信用してはいけない。 外部からの入力は、常に検証してから使うようにしよう。 filter_var 関数や filter_input 関数で、入力の検証や書式の判定 (メールアドレスなど) ができる。

外部からの入力にはいろいろな種類がある。フォームから渡される $_GET$_POST もあれば、 スーパーグローバル $_SERVER に含まれるものもある。そして fopen('php://input', 'r') でやってくる HTTP リクエストのボディもそうだ。 外部からの入力といっても、ユーザーがフォームで入力したものばかりとは限らないことに注意。 アップロードしたりダウンロードしたりしたファイル、セッションのデータ、 クッキーのデータ、サードパーティのウェブサービスからのデータ。 これらはみんな外部からの入力となる。

外部からのデータをいったん保存して、何かと組み合わせて後で使うとしよう。 それでも、そのデータが外部からの入力であるという事実は変わらない。 そのデータを処理したり出力したり何かとつなげたりコードに組み込んだりするときには 「適切にフィルタリングできてる?」「信頼できる?」と確認しよう。

データのフィルタリング方法は、その利用目的によって異なる。 たとえば、外部の入力を何も処理せずに HTML ページに出力すると、 あなたのサイト上で任意の JavaScript が実行できてしまうことになる! これが、いわゆるクロスサイトスクリプティング (XSS) である。 とても危険な攻撃だ。こんなときに XSS を回避する方法のひとつは、 入力からすべての HTML タグを取り除くか、 あるいはエスケープして HTML エンティティに変換することだ。

別の例として、外部の入力をコマンドラインのオプションとして渡すことを考えよう。 これって非常に危険なことだし、ふつうはあまりやるべきではないことだ。 でも、もしやるなら、組み込みの関数 escapeshellarg を使えばコマンドの引数を実行されてしまうことが防げる。

最後の例は、外部の入力に基づいてファイルシステム上のファイルを読み込むというものだ。 このときは、ファイル名のかわりにファイルパスを渡されてしまうという攻撃が考えられる。 外部の入力から”/”や”../”、null バイトなどを取り除いて、 隠しファイルや公開すべきでない場所のファイルを読み込まないようにする必要がある。

サニタイズ

サニタイズとは、外部の入力から危険な文字を取り除く (あるいはエスケープする) ことだ。

たとえば、外部の入力を HTML に含めたり SQL クエリに組み込んだりする前に、 サニタイズが必要となる。PDO でバインド変数を使う場合は、 PDO が入力をサニタイズする。

外部の入力を HTML として組み込むときに、いくつかの安全な HTML タグはそのまま使わせたいという場合もある。そんな要求を実現するのは非常に難しいので、 たいていの場合は Markdown や BBCode などのフォーマットを代替手段として使うのだが、 どうしてもとうい場合は HTML Purifier のようなホワイトリストライブラリを使える。

サニタイズフィルター

バリデーション

バリデーションとは、外部の入力が期待通りであるかどうかを確かめること。 たとえばユーザー登録の処理では、 メールアドレスや電話番号、あるいは年齢などを検証することになるだろう。

バリデーションフィルター

設定ファイル

自作のアプリケーションで設定ファイルを使うときには、これらの指針に従うのがお勧めだ。

Register Globals

注意: PHP 5.4.0 からは register_globals という設定項目がなくなったので、この設定は使えない。 このページは単に、大昔のアプリケーションをアップグレードしている人たち向けの警告として用意したものでしかない。

register_globalsを有効にすると、$_POST$_GETそして$_REQUEST などの内容にアプリケーションのグローバルスコープでアクセスできるようになる。 これを使うとセキュリティの問題が発生しやすくなる。 というのも、そのデータがどこからきたものなのかをアプリケーション側で判断できなくなるからだ。

たとえば $_GET['foo'] の内容に $foo でアクセスできることになるのだが、 これは、宣言されていない変数の中身を自動的に上書きしてしまうことにつながる。 PHP 5.4.0 より前のバージョンを使っている場合は、 確実に register_globalsoff にしておこう。

エラーレポート

エラーを記録しておくと、アプリケーションに何か問題があったときにその原因を見つけやすくなる。 しかしその一方で、アプリケーションの構造に関する情報を外部に公開してしまうことにもなる。 エラーメッセージを出すことで起こる問題からアプリケーションを守るには、 開発環境と本番環境でサーバーの設定を切り替える必要がある。

開発環境

 開発環境で、起こりうるエラーをすべて表示するときには、php.iniで次のように設定する。

display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On

値に-1を指定すると、仮に将来のバージョンのPHPで新しいレベルと定数が追加されたとしてもすべてのエラーを表示するようになります。E_ALL 定数も、PHP 5.4以降これと同じ挙動になります。 - php.net

E_STRICTエラーレベル定数は5.3.0で導入されたもので、当時は E_ALLには含まれていなかった。でも5.4.0からはE_ALLに含まれるようになった。 だからどうなんだって? あらゆるエラーを表示させたいときには、5.3の場合は -1あるいはE_ALL | E_STRICTを使わないといけないってことだ。

PHPのバージョン別の、すべてのエラーを表示させるための設定

本番環境

 本番環境でエラーの情報を見せないようにするには、php.iniで次のように設定する。

display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On

この本番環境用の設定をしても、ウェブサーバーのエラーログにはエラーの内容がきちんと残る。 しかし、ユーザーにはエラーが見えなくなる。これらの設定項目についてもっと詳しく知りたければ、 PHP のマニュアルを読もう。

Back to Top

テスト

PHPのコードを書くときには、自動化されたテストも書くのがよい習慣だとされている。 そうすれば、頑丈なアプリケーションが作れるようになる。自動テストを活用すれば、 何かを変更したり機能を追加したりしたときにもアプリケーションがきちんと動くことを確認できる。 欠かせないツールだ。

PHP で使えるテスト用ツール (あるいはフレームワーク) にはいくつかのものがあり、 それぞれ異なる手法を使っている。が、目指すところは同じ。 手作業でのテストをなくす、そして最新の変更で既存の機能を壊していないかどうかを確かめるためだけに 大規模な品質保証チームを使うなんてことをなくす、というのが目標だ。

テスト駆動開発

Wikipedia によると、

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or ‘rediscovered’ the technique, stated in 2003 that TDD encourages simple designs and inspires confidence

アプリケーションのテストには、いくつかの種類がある。

ユニットテスト

ユニットテストとはプログラミングの手法のひとつだ。 関数やクラスやメソッドが期待通りに動いていることを開発中に常に確かめる。 さまざまな関数やメソッドはの入出力の値をチェックすれば、 その内部ロジックが正しく動いていることを確認できる。 依存性注入の仕組みを利用してクラスのモックやスタブを使えば、 依存ライブラリが正しく使われていることを確かめられる。

クラスや関数を作るときに、その振る舞いを確かめるためのユニットテストも同時に作る。 最も基本的なレベルだと、間違った引数を渡した場合にエラーになることや、 正しい引数を渡したときに正常に動くことなどを確認しないといけない。 こうしておけば、後にクラスや関数に手を加えたときにも 今までの機能が期待通りに動くかどうかを確かめられるようになる。 test.php で var_dump() とかいうやり方もあるけど、 まともなアプリケーションを作るつもりならそれはあり得ない。

それ以外にもユニットテストの使い道はある。オープンソースに貢献する手段として使えるのだ。 うまく機能していないことを示すためにテストを書く。そして動くように修正する。 最後にテストが通ることを確認する。こんなパッチを送れば、きっと受け入れてもらいやすくなるだろう。 もし何かプロジェクトを運営していて pull request を受け付けているのなら、 「パッチにはテストをつけること」という条件をつけておくといいだろう。

PHPUnitは、PHPアプリケーションでユニットテストを書くための デファクトスタンダードのフレームワークだ。しかしそれ以外にも選択肢がある。

インテグレーションテスト

Wikipedia によると、

Integration testing (sometimes called Integration and Testing, abbreviated “I&T”) is the phase in software testing in which individual software modules are combined and tested as a group. It occurs after unit testing and before validation testing. Integration testing takes as its input modules that have been unit tested, groups them in larger aggregates, applies tests defined in an integration test plan to those aggregates, and delivers as its output the integrated system ready for system testing.

ユニットテスト用のツールの多くはインテグレーションテストにも使える。 ほぼ同じような指針で行うものだからである。

機能テスト

受け入れテストと呼ばれることもある。実際にアプリケーションを使う観点での自動テストを作ってその動きを確認する。 単にコード片が正しく動くとか、個々のパーツがお互いに正しくやりとりできるかとかいうレベルのテストではない。 このレベルのテストでは、実際のデータを使ったりアプリケーションの実際のユーザーをシミュレートしたりすることが一般的だ。

機能テスト用のツール

振る舞い駆動開発

振る舞い駆動開発 (BDD) には二種類ある。SpecBDD と StoryBDD だ。 SpecBDD はコードの技術的な振る舞いを重視し、StoryBDD は業務的あるいは機能的な振る舞いを重視する。 PHP には、これら二種類の BDD 用のフレームワークが存在する。

StoryBDD では、人間が読める形式のストーリーを書いてアプリケーションの振る舞いを表す。 そしてそのストーリーを、アプリケーションのテストとして実行する。 PHP アプリケーションで StoryBDD をするために使えるフレームワークが Behat で、これは Ruby の Cucumber の影響を受けたフレームワークである。 Gherkin DSL を使ってフィーチャを記述できる。

SpecBDD では、実際のコードのあるべき振る舞いをスペックとして書く。 関数やメソッドを単独でテストするのではなく、その関数やメソッドがどのように振る舞うのかを記述するのだ。 PHP で SpecBDD をするときに使えるフレームワークが PHPSpec で、これは Ruby の RSpec project の影響を受けている。

BDD に関するリンク

その他のテスト用ツール

これまでに取り上げたテストツールや振る舞い駆動開発フレームワーク以外にも、 いろいろなフレームワークやヘルパーライブラリがある。これらも有用に使える。

ツールへのリンク

Back to Top

各種サーバーへのデプロイ

PHP のアプリケーションをデプロイして本番サーバーで運用するための方法を紹介する。

Platform as a Service (PaaS)

PaaS を使えば、PHP アプリケーションをウェブ上で動かすために必要なシステムやネットワーク環境を用意してくれる。 ほとんど何も設定せずに、PHP のアプリケーションやフレームワークを実行できるということだ。

最近は、PHP アプリケーションのデプロイ先として PaaS を使うことが多くなった。あらゆる規模のアプリケーションを扱える。 PHP 用の PaaS “Platform as a Service” プロバイダ を、情報源 にまとめた。

仮想サーバーあるいは専用サーバー

サーバー管理が苦にならない人、あるいはサーバー管理を勉強してみたい人は、仮想サーバーあるいは専用サーバーを選ぶといい。 そうすれば、アプリケーションの運用環境を完全に制御できる。

nginx と PHP-FPM

PHP に組み込まれた FastCGI Process Manager (FPM) は nginx と組み合わせるのに最適だ。nginx は、軽量でパフォーマンスに優れたウェブサーバーである。 Apache よりも少ないメモリで動き、同時にさばけるリクエストの数も多い。 これは特に、共有メモリの少ない仮想サーバーでは重要だ。

Apache と PHP

PHP と Apache は長い付き合いだ。 Apache はいろんな設定が可能で、さまざまな モジュール で機能を拡張できる。共有サーバーで PHP のフレームワークを動かしたり、WordPress みたいなアプリケーションを動かしたりするときにはよく使われる選択肢だ。 残念ながら Apache は、デフォルトでは nginx よりもメモリを食うし、 同時にさばけるユーザー数も nginx より少ない。

Apache で PHP を動かすにはいくつかの選択肢がある。 一番よく使われていて簡単に設定できるのが、prefork MPM と mod_php5 の組み合わせだ。 メモリの使用効率はそれほどよくないが、とりあえず動かして使うには一番シンプルだ。 サーバー管理方面にあまり足を突っ込みたくない場合は、この方法がいいだろう。 注意すべき点は、mod_php5 を使う場合は必ず prefork MPM を使わないといけないということだ。

Apache 本来のパフォーマンスや安定性をもっと絞り出したいという場合は、nginx と同じように FPM を使うこともできる。 この場合は、worker MPM あるいは event MPM に mod_fastcgi あるいは mod_fcgid を組み合わせる。この設定はメモリの利用効率がよくて高速に動作するが、設定に手間がかかる。

共有サーバー

PHP の人気のおかげで、いろんな共有サーバーで PHP が使える。 むしろ PHP が使えない共有サーバーを見つけるほうが難しいだろう。 ただし、最新バージョンが使えるかどうかは要注意だ。 共有サーバーでは、あなただけでなく他の開発者も同じマシンにウェブサイトをデプロイする。 その利点は、安上がりに使えるということだ。 ただ欠点もあって、同じサーバーに同居しているお隣さんが何をしでかすかがわからない。 めちゃめちゃ負荷のかかることをしてしまったり、セキュリティホールを作り込んでしまったりといった恐れがある。 もし充分な予算があるのなら、できるだけ共有サーバーは避けよう。

アプリケーションのビルドとデプロイ

まさか、データベースのスキーマを変更したりテストを実行したりとかいったことを手作業でやってるなんてことはないよね? ちょっと待った!新しいバージョンのアプリケーションをデプロイするときに手作業がひとつでも増えると、 致命的な間違いを犯してしまう可能性もそのぶん増えてしまうんだ。たとえ単純な更新作業だとしても、 きちんとしたビルド手順にしたがうこと。継続的インテグレーションの戦略にしたがって、 ビルドの自動化 をしておくといい。

自動化できるタスクには、こんなものがある。

ビルド自動化ツール

ビルドツールとは、ソフトウェアのデプロイにからむありがちな作業を処理するスクリプトをまとめたものだと言える。 ビルドツール自体は君が作るソフトウェアの一部ではない。ソフトウェアを「外部から」支援するものだ。

ビルド自動化の助けとなるオープンソースのツールがたくさん公開されている。PHPで書かれているものもあれば、 そうじゃないものもある。PHP製じゃないからといって、それを使わない理由はない。 もし自分のやりたいことに適したツールがあるのなら、使うべきだ。いくつか例をあげよう。

Phing は、PHPな人がデプロイを自動化しようとするときに、いちばん取っつきやすいツールだ。 Phingを使えば、パッケージングやデプロイそしてテストといった処理をシンプルなXMLビルドファイルで設定できる。 PhingはApache Ant をベースに作られたもので、 Webアプリのインストールやアップデートに必要となるタスク群を提供する。 カスタムタスクで機能を追加することもでき、カスタムタスクはPHPで書ける。

Capistrano中級から上級のプログラマー 向けのシステムだ。構造化された、繰り返し可能な形式で、 複数のリモートマシン上でコマンドを実行できる。 Ruby on Railsのアプリをデプロイするように設定されているが、 これを使って PHP のアプリをデプロイすることもできる。 Capistranoを使いこなすには、RubyとRakeに関するそれなりの知識が必要だ。

Dave Gardnerのblog記事PHP Deployment with Capistrano は、Capistranoに興味のあるPHP開発者への入門記事としておすすめだ。

Chef は単なるデプロイフレームワークではない。 Rubyで書かれた強力なシステムインテグレーションフレームワークで、 単にアプリをデプロイするだけじゃなくサーバー環境全体を構築したり 仮想環境を構築したりもできる。

Chefの資料として、PHP開発者向けにおすすめものは、これらだ。

あわせて読みたい:

継続的インテグレーション

継続的インテグレーションはソフトウェア開発のプラクティスのひとつで、 チームのメンバーが自分たちの作業を頻繁に統合するというものだ。 通常は、各自が少なくとも一日に一度は統合する。一日に何度も統合することもある。 多くのチームが、この方針のおかげで統合時の問題が少なくなるし、 きちんとしたソフトウェアをより素早く開発できるようになると実感している。

– マーティン・ファウラー

PHPで継続的インテグレーションを実践する方法はいろいろある。 最近人気のTravis CI のおかげで、 ちょっとしたプロジェクトにも簡単に継続的インテグレーションを組み込めるようになった。 Travis CIは継続的インテグレーションのホスティング環境で、オープンソースコミュニティに開放されている。 GitHubと統合されており、PHPを含むさまざまな言語に対応している。

あわせて読みたい:

Back to Top

キャッシュ

PHP 自体は極めて高速だけど、リモート接続やファイルの読み込みなどが絡むとボトルネックになるかもしれない。 ありがたいことに、いろんなツールを活用すればアプリケーションを高速化できるし、 時間のかかる処理の実行回数を減らすこともできる。

バイトコードキャッシュ

PHP ファイルを実行するときにその裏側で行われているのは、 まずバイトコード (オペコード) にコンパイルしてからそのバイトコードを実行するという処理だ。 PHP ファイルに変更がなければ、バイトコードも同じものになる。 ということは、PHP ファイルに変更がなければコンパイル処理は CPU リソースの無駄遣いになるということだ。

そこでバイトコードキャッシュの出番だ。 これはバイトコードをメモリに格納してそれ以降の呼び出しで再利用するという仕組みで、 冗長なコンパイルを回避する。バイトコードキャッシュを設定するのはほんの数分で済み、 それだけでアプリケーションの速度が劇的に向上する。使わない理由はないね。

PHP 5.5 からは、 OPcache というバイトコードキャッシュが標準で組み込まれるようになった。 5.5 より前のバージョンでも使うことができる。

その他、バイトコードキャッシュとしてはこれらが有名だ。

オブジェクトキャッシュ

コード内の個々のオブジェクトをキャッシュできれば便利なこともある。 持ってくるのにコストがかかるデータや、結果がほとんど変わらないデータベースへの問い合わせなどだ。 オブジェクトキャッシュ用のソフトウェアを使えば、こういったデータをメモリ上に保持でき、 その後のアクセスの高速化につながる。 一度取得したデータをどこかに格納しておいて、それ以降のリクエストではそこから直接取り出す。 そうすればパフォーマンスは劇的に向上するし、データベースサーバーにかかる負荷も減らせる。

バイトコードキャッシュ用ソリューションの多くはカスタムデータも同様にキャッシュできるので、さらに活用できる。 APCu や XCache そして WinCache はすべて API を提供しており、PHP コードのデータをメモリキャッシュに格納できる。

オブジェクトキャッシュシステムとして最もよく使われているのは APCu と memcached だ。 APCu はオブジェクトキャッシュの選択肢として最適で、 シンプルな API を使ってデータをメモリキャッシュに格納できる。 セットアップも簡単で、すぐに使えるようになる。 APCu の制約のひとつは、インストールしたサーバーと密接に結合してしまうことだ。 一方、Memcached は個別のサービスとしてインストールするものであり、 ネットワーク越しにアクセスできる。つまり、 中央で管理している超高速なストレージにオブジェクトを格納して、 いろんなシステムからそれを取り出せるということだ。

PHPを(Fast-)CGIアプリケーションとしてウェブサーバーで実行するときには、 すべてのPHPプロセスが自身のキャッシュを持つことになる。つまり APCuのデータもワーカープロセス間では共有されないということだ。 こんなときには、かわりにmemcachedを検討すればいい。 こっちはPHPのプロセスには結びついていない。

ネットワーク環境において、アクセス速度の面では APCu のほうが memcached より優れている。 しかし、memcached のほうが、より手軽にスケールアップできる。 複数のサーバーを使う予定がないとか memcached の追加機能が不要だという場合は、 オブジェクトキャッシュに APCu を選ぶのが最適だろう。

APCu を使うロジックの例を示す。

<?php
// 'expensive_data' がキャッシュに保存されているかどうかを調べる
$data = apc_fetch('expensive_data');
if ($data === false) {
    // データがキャッシュにないときは、コストのかかる操作をして取得する。
    // そして、その結果を保存してあとで使えるようにする。
    apc_add('expensive_data', $data = get_expensive_data());
}

print_r($data);

PHP 5.5 より前のバージョンでは、APC がオブジェクトキャッシュとバイトコードキャッシュの両方の機能を提供していた。 APCu は、APC のオブジェクトキャッシュ機能を PHP 5.5 以降で使えるようにするものだ。 というのも、PHP 5.5 以降ではバイトコードキャッシュ(OPcache)の機能が標準で組み込まれるようになったからだ。

オブジェクトキャッシュシステムについて詳しく知りたい場合は、これらが参考になる。

Back to Top

情報源

ソースから

People to Follow

メンタリング

PHP PaaS プロバイダ

フレームワーク

PHP 開発者の多くは、ウェブアプリケーションを作るときに車輪の再発明を避けてフレームワークを使っている。 フレームワークを使えば低レベルの作業の多くを抽象化でき、 便利で使いやすいインターフェイスでよくある作業をこなせる。

別に、フレームワークを使わないといけないというわけじゃない。 ふつうに PHP で書くべき場面だってある。 もしフレームワークを使うのなら、フレームワークはこんな感じに分類できることを知っておこう。

マイクロフレームワークというのは本質的にはラッパーで、 HTTP リクエストを手っ取り早くコールバックに振り分けるだけのものだ。 場合によっては、開発を支援するためのちょっとした追加ライブラリが付属することもある。 データベースの基本的なラッパーなどだ。マイクロフレームワークの主な使い道は、 リモート HTTP サービスの構築である。

多くのフレームワークは、マイクロフレームワークが持つ機能に加えて大量の機能を用意している。 この種のフレームワークのことを、フルスタックフレームワークと呼ぶ。 ORM や認証パッケージなどが含まれることが多い。

コンポーネントフレームワークとは、特定の目的のための専用ライブラリをとりまとめたフレームワークである。 この種のフレームワークのコンポーネントを各種組み合わせて、 マイクロフレームワークやフルスタックフレームワークを作ることもできる。

コンポーネント

先ほど説明したように、「コンポーネント」っていうのは 共有するコードを作ったりそれを配布したりするための手段のひとつだ。 コンポーネントを登録するリポジトリにもいろいろあるけど、中でも有名なのがこのふたつだ。

どちらのリポジトリについてもコマンドラインのツールが存在し、 コンポーネントのインストールやアップグレードを簡単にできる。 詳細は依存関係の管理を参照すること。

コンポーネントベースのフレームワークもあれば、フレームワークを持たずにコンポーネントだけを提供するベンダーもある。 こういったプロジェクトが提供するパッケージは、他のパッケージや特定のフレームワークへの依存がほとんどない。

たとえばFuelPHPのValidationパッケージを使うときには、 別にFuelPHPフレームワークを使う必要はない。

Laravelの Illuminate コンポーネント も、将来的には Laravel フレームワークからきちんと切り離されるようになるだろう。 現段階では、Laravel フレームワークからうまく切り離せているコンポーネントだけをリストにあげておいた。

書籍

PHPに関する本はいっぱい出てるけど、絶望的に古くなってしまっていてもはや正しい情報ではなくなっているものもある。 なぜか、まだ世に出てすらいない「PHP 6」向けの本まで出ていたりする。 次のメジャーバージョンの名前が「PHP 7」に決まったのも、もしかしたらその本のせいかも。

このセクションでは、PHPでの開発全般でおすすめの書籍を紹介する。 自分の本を載せて欲しいという人は、プルリクエストを出してもらえばレビューのうえで追加する。

無償の書籍

有償の書籍

Back to Top

コミュニティ

PHP のコミュニティは、規模が大きくなるにつれて多様化しており、 どこのコミュニティでも新たな仲間を歓迎している。 近所のユーザーグループに参加したり、ちょっと大きめのカンファレンスに参加したりすれば、 このページで紹介したことよりもずっと多くを学べるだろう。 IRC なら irc.freenode.com に #phpc というチャンネルがあるし、 twitter なら @phpc をフォローするといい。 いろんなところに飛び込んで、新しい人と出会って、いろんなことを学んで、友達になるんだ。 Google+にもPHPプログラマー向けの コミュニティ があるし、 StackOverflow にもある。

PHP 公式サイトのイベントカレンダー

PHP ユーザーグループ

大都市に住んでいるなら、きっと近場に PHP ユーザーグループがあるはずだ。 公式ページの一覧に載ってなかったとしても、Google とか Meetup.com あるいは PHP.ug で探せばすぐ見つかる。小さな町なら、もしかしたらユーザーグループがないかもしれない。 だったら、作ればいい!

PHP Wiki のユーザーグループ情報

PHP カンファレンス

世界各国で、地域レベルあるいは国レベルの大規模なカンファレンスが開かれている。 PHP界の有名人が登壇することも多いので、彼らから直接学ぶチャンスだ。

PHP のカンファレンスを探す

Back to Top

PHPDoc

PHPDocは、非公式ながら、PHPのコードにコメントを書くときの標準として使われているものだ。 さまざまな タグ が使える。 すべてのタグの一覧や実際の使用例は、 PHPDoc のマニュアル を参照すること。

いくつかのメソッドを持つクラスのドキュメントを書く例を示そう。

<?php
/**
 * @author A Name <a.name@example.com>
 * @link http://www.phpdoc.org/docs/latest/index.html
 * @package helper
 */
class DateTimeHelper
{
    /**
     * @param mixed $anything Anything that we can convert to a \DateTime object
     *
     * @return \DateTime
     * @throws \InvalidArgumentException
     */
    public function dateTimeFromAnything($anything)
    {
        $type = gettype($anything);

        switch ($type) {
            // Some code that tries to return a \DateTime object
        }

        throw new \InvalidArgumentException(
            "Failed Converting param of type '{$type}' to DateTime object"
        );
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     *
     * @return void
     */
    public function printISO8601Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('c');
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     */
    public function printRFC2822Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('r');
    }
}

クラス全体のドキュメントの最初にあるのが @author タグで、 これは、コードの作者を表す。作者が複数いる場合は、何度も繰り返し使ってもかまわない。 その次にあるのが @link タグで、 これは、そのコードに関連するウェブサイトへのリンクを示すために使う。 三番目の @pacakge タグは、このコードが属するカテゴリーを指定するものだ。

クラスの中に目を移すと、最初のメソッドには @param タグが記されている。 これは、このメソッドに渡すパラメータの型と名前そして説明を記述するものだ。 さらに、@return タグと @throws タグも書かれている。 これらはそれぞれ、戻り値の型と、発生する可能性のある例外を記述するものだ。

二番目と三番目のメソッドはほぼ同じだ。まず、最初のメソッドと同様に @param タグが書かれている。 二番目と三番目のメソッドの大きな違いは、 @return タグの有無だ。 @return void は、このメソッドが何も戻さないことを明示している。 一方、歴史的に、 @return void を省略した場合も同じ意味(何も値を戻さない)になる。

Back to Top