ようこそ

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

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

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

翻訳

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

協力するには

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

拡散希望

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

バナー画像

Back to Top

はじめに

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

とりあえず PHP を試したいっていうのなら、最新の安定版である PHP 7.0 を使おう。 PHP 7.0 は公開されたばかりの最新版で、PHP 5.x 系にはない数々の強力な新機能が追加された。 エンジンが大幅に書き直された PHP 7.0 は、今までのバージョンよりもずっと高速になっている。

とはいえ、しばらくは PHP 5.x を使うという人も多いだろう。PHP 5.x の最新バージョンは、5.6 系だ。 これを使うのも悪くない。でも、最新版をできるだけ早めに試してみるべきだ。 PHP 5.6の セキュリティ更新は、2018年になると提供されなくなる。 アップグレードはとても簡単だ。というのも、過去との互換性を損なうような変更 がそんなに多くないからだ。 何かの関数や機能がどのバージョンに含まれているのかを知りたければ、php.net にあるマニュアルを調べよう。

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

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

> php -S localhost:8000

Mac の人は

OS X には PHP が最初からインストールされているけど、最新の安定版からは微妙に遅れている。 Mavericks についてくるのは PHP 5.4.17 だし Yosemite でも 5.5.9。El Capitan にしても 5.5.29 だし Sierra でも 5.6.24 だ。 PHP 7.0 に比べると、十分だとはいえない。

PHP を OS X にインストールするには、いくつかの方法がある。

Homebrew によるインストール

Homebrew は OS X 用の強力なパッケージ管理ツールで、 PHP やその拡張モジュールも簡単にインストールできる。 Homebrew PHP が、Homebrew 用の PHP 関連の “Formula” をまとめたリポジトリだ。 これを使えば PHP をインストールできる。

現時点では、php53php54php55php56php70brew install コマンドでインストールできる。 これらを切り替えるには、環境変数 PATH を設定すればいい。 brew-php-switcher を使えば、そのへんを自動的にやってくれる。

Macports によるインストール

MacPorts プロジェクトはオープンソースのコミュニティによる取り組みで、 OS X上のオープンソースソフトウェアのコンパイルやインストールそしてアップグレードを簡単にできるようにする仕組みだ。 コマンドラインのソフトからX11やAquaベースのソフトにまで対応している。

MacPorts はコンパイル済みのバイナリにも対応しているので、関連するライブラリなどを毎回ソースからコンパイルしなおす必要はない。 なので、まだ何もパッケージをインストールしていない状態でも、時間の心配をする必要はない。

現時点でインストールできるのは php54php55php56php70 のいずれかで、port install コマンドを使ってこのようにインストールする。

sudo port install php56
sudo port install php70

そして、select コマンドを使って、アクティブな PHP を切り替える。

sudo port select --set php php70

phpbrew によるインストール

phpbrew は、複数のバージョンの PHP をインストールして管理するためのツールだ。 使っているアプリケーションやプロジェクトによって、PHP のバージョンが異なる場合に特に便利で、 仮想マシンを用意する必要がなくなる。

Liipのバイナリインストーラーによる PHP のインストール

php-osx.liip.ch は、たった一行で PHP をインストールできる方法だ。バージョン 5.3 から 7.0 に対応している。 Apple がインストールした php のバイナリは上書きせず、まったく別の場所 (/usr/local/php5) にすべてをインストールする。

ソースからのコンパイル

もうひとつの選択肢がある。 自分でコンパイル することで、この方法なら PHP の設定を完全にコントロールできる。 この場合は、Xcode あるいはその代用ツール “Command Line Tools for XCode” をインストールする必要がある。これらは、 Apple の Mac Developer Center からダウンロードできる。

全部入りのインストーラー

ここまでの方法は主に PHP 本体だけを扱うもので、たとえば Apache や Nginx、そしてデータベースサーバーなどは用意していない。 いわゆる「全部入り」のソリューションである MAMPXAMPP を使えば、 これらのソフトウェアもまとめてインストールしてくれる。 でも、簡単にインストールできるぶん、柔軟性に欠けるという弱点もある。

Windows の人は

windows.php.net/download からバイナリをダウンロードしよう。それを展開したら、 PHPフォルダのルート (php.exe がある場所) に PATH を通しておくといい。そうすれば、どこからでも PHP を実行できるようになる。

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

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

開発環境と運用環境が違っていると、いざ動かしたときにおかしなバグが発生しがちだ。 Windowsで開発したアプリケーションをLinux(などの非Windows環境)で動かしているのなら、仮想マシンを使うべきだ。

Chris Tankersley has a very helpful blog post on what tools he uses to do PHP development using Windows.

Back to Top

コーディングスタイル

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

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

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

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

コードのレイアウトを自動的に修正するツールとしては、二つの選択肢がある。

phpcs をシェルから手動で実行することもできる。

phpcs -sw --standard=PSR2 file.php

これは、エラーの内容とその修正方法を表示してくれる。 このコマンドをgit hookに仕込んでおけば便利だろう。 そうすれば、標準規約に反する変更を含むブランチは、それを修正するまでリポジトリに投入できなくなる。

PHP_CodeSnifferを使っている場合は、指摘されたコードレイアウトの問題を自動的に修正することもできる。そのためにはPHP Code Beautifier and Fixerを使えばいい。

phpcbf -w --standard=PSR2 file.php

もうひとつの選択肢はPHP Coding Standards Fixerで、 これは、実際に修正するまえにコードにどんな問題があったのかを表示してくれる。

php-cs-fixer fix -v --level=psr2 file.php

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

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-4 にまとまっている。 この文書の狙いは、ファイルやクラスそして名前空間の命名規則を標準化して お互いに再利用しやすくすることだ。

2014年10月、PHP-FIG はオートローディングに関する標準であった PSR-0 を非推奨とした。 PSR-0もPSR-4も、今はどちらでも使える。 というのも 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 には依存関係をうまく管理する仕組みがなかった。 手作業で管理していたところで、オートローダーのことも気にしないといけない。 もううんざりだ。

現時点でよく使われているパッケージ管理システムは、ComposerPEAR である。 Composer は現在 PHP のパッケージマネージャーとしていちばん人気があるものだけれど、かつてはパッケージマネージャーといえば PEAR だった。 PEAR の歴史も知っておいたほうがいい。今でも PEAR を使っているところがあるかもしれないし、 たとえ決して使うことがないとしても、知っておいて損はないだろう。

Composer と Packagist

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

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

Composer のインストール

composer をダウンロードするいちばん安全な方法は、公式サイトの指示に従うこと。 この方法だと、インストーラが壊れていたり改ざんされていたりしないかを確かめられる。 インストーラは Composer を ローカルに インストールする。つまり、いま作業中のディレクトリにインストールするということ。

お勧めは、グローバルにインストールする (要するに、/usr/local/bin にだけ置く) 方式だ。 そのためには、次のコマンドを実行すればいい。

mv composer.phar /usr/local/bin/composer

注意: パーミッションのエラーでこのコマンドが失敗する場合は、頭に sudo をつけて実行してみよう。

ローカルにインストールした Composer を実行するときには php composer.phar とする。グローバルにインストールしたのなら、単に composer と打つだけだ。

Windows でのインストール

Windowsの場合、一番簡単なのは [ComposerSetup][6] インストーラーを使う方法だ。 これは、すべてのユーザーで使えるようにインストールしたうえで $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 がすべての依存ライブラリをダウンロードして vendor/ にインストールしてくれる。 次のコマンドは、すでに composer.json ファイルを含むプロジェクトをダウンロードした場合にも使える。

composer install

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

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

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

依存関係の更新

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

これは、バージョンの要件を柔軟に定義できるので便利だ。 たとえば、バージョンに ~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 ファイルを調べて、もし依存関係に更新が必要なら教えてくれるものだ。

Composerでのグローバルな依存関係の扱い

Composer は、グローバルな依存関係やそのバイナリを扱うこともできる。 使いかたはとても簡単で、単にコマンドの前に global をつけるだけでいい。 たとえば、PHPUnit をグローバルに使えるようインストールしたければ、こんなコマンドを実行する。

composer global require phpunit/phpunit

このコマンドは、 ~/.composer ディレクトリを作って、グローバルな依存関係をそこに置く。 インストールされたパッケージのバイナリを全体で使えるようにするには、 ~/.composer/vendor/bin ディレクトリを環境変数 $PATH に追加すればいい。

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"を使ってComposerに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パッケージを使うには、単純にこのように参照するだけでいい。

<?php
$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('Y-m-d') . "\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('Y-m-d') . ' ';
}

PHP APIの拡張として有名なのが Carbon だ。 これは DateTime クラスのすべてを継承しているので、コードの書き換えを最小限に抑えられる。 さらに追加機能として、地域化のサポートや、 DateTimeオブジェクトの加減算とフォーマットの方法の追加がある。 また、自分で選んだ日付と時刻をシミュレートする機能もあって、これはコードをテストするときに使える。

デザインパターン

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

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

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 はダメ!!! その理由が知りたければ「あわせて読みたい」を参照すること。

ブラウザレベルでの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 のサンプルを見れば、より詳しい情報がわかる。
// ここでの肝は、データソース名 (DSN) における `charset=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>
        <meta charset="UTF-8">
        <title>UTF-8 テストページ</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // 変換したUTF-8文字列が、ブラウザに正しく出力されるはず
        }
        ?>
    </body>
</html>

あわせて読みたい

Internationalization (i18n) and Localization (l10n)

Disclaimer for newcomers: i18n and l10n are numeronyms, a kind of abbreviation where numbers are used to shorten words - in our case, internationalization becomes i18n and localization, l10n.

First of all, we need to define those two similar concepts and other related things:

Common ways to implement

The easiest way to internationalize PHP software is by using array files and using those strings in templates, such as <h1><?=$TRANS['title_about_page']?></h1>. This is, however, hardly a recommended way for serious projects, as it poses some maintenance issues along the road - some might appear in the very beginning, such as pluralization. So, please, don’t try this if your project will contain more than a couple of pages.

The most classic way and often taken as reference for i18n and l10n is a Unix tool called gettext. It dates back to 1995 and is still a complete implementation for translating software. It is pretty easy to get running, while it still sports powerful supporting tools. It’s about Gettext we will be talking here. Also, to help you not get messy over the command-line, we will be presenting a great GUI application that can be used to easily update your l10n source files.

Other tools

There are common libraries used that support Gettext and other implementations of i18n. Some of them may seem easier to install or sport additional features or i18n file formats. In this document, we focus on the tools provided with the PHP core, but here we list others for completion:

Other frameworks also include i18n modules, but those are not available outside of their codebases: - Laravel supports basic array files, has no automatic extractor but includes a @lang helper for template files. - Yii supports array, Gettext, and database-based translation, and includes a messages extractor. It is backed by the Intl extension, available since PHP 5.3, and based on the ICU project; this enables Yii to run powerful replacements, like spelling out numbers, formatting dates, times, intervals, currency, and ordinals.

If you decide to go for one of the libraries that provide no extractors, you may want to use the gettext formats, so you can use the original gettext toolchain (including Poedit) as described in the rest of the chapter.

Gettext

Installation

You might need to install Gettext and the related PHP library by using your package manager, like apt-get or yum. After installed, enable it by adding extension=gettext.so (Linux/Unix) or extension=php_gettext.dll (Windows) to your php.ini.

Here we will also be using Poedit to create translation files. You will probably find it in your system’s package manager; it’s available for Unix, Mac, and Windows, and can be downloaded for free on their website as well.

Structure

Types of files

There are three files you usually deal with while working with gettext. The main ones are PO (Portable Object) and MO (Machine Object) files, the first being a list of readable “translated objects” and the second, the corresponding binary to be interpreted by gettext when doing localization. There’s also a POT (Template) file, that simply contains all existing keys from your source files, and can be used as a guide to generate and update all PO files. Those template files are not mandatory: depending on the tool you’re using to do l10n, you can go just fine with only PO/MO files. You’ll always have one pair of PO/MO files per language and region, but only one POT per domain.

Domains

There are some cases, in big projects, where you might need to separate translations when the same words convey different meaning given a context. In those cases, you split them into different domains. They’re basically named groups of POT/PO/MO files, where the filename is the said translation domain. Small and medium-sized projects usually, for simplicity, use only one domain; its name is arbitrary, but we will be using “main” for our code samples.
In Symfony projects, for example, domains are used to separate the translation for validation messages.

Locale code

A locale is simply a code that identifies one version of a language. It’s defined following the ISO 639-1 and ISO 3166-1 alpha-2 specs: two lower-case letters for the language, optionally followed by an underline and two upper-case letters identifying the country or regional code. For rare languages, three letters are used.

For some speakers, the country part may seem redundant. In fact, some languages have dialects in different countries, such as Austrian German (de_AT) or Brazilian Portuguese (pt_BR). The second part is used to distinguish between those dialects - when it’s not present, it’s taken as a “generic” or “hybrid” version of the language.

Directory structure

To use Gettext, we will need to adhere to a specific structure of folders. First, you’ll need to select an arbitrary root for your l10n files in your source repository. Inside it, you’ll have a folder for each needed locale, and a fixed LC_MESSAGES folder that will contain all your PO/MO pairs. Example:

<project root>
 ├─ src/
 ├─ templates/
 └─ locales/
    ├─ forum.pot
    ├─ site.pot
    ├─ de/
    │  └─ LC_MESSAGES/
    │     ├─ forum.mo
    │     ├─ forum.po
    │     ├─ site.mo
    │     └─ site.po
    ├─ es_ES/
    │  └─ LC_MESSAGES/
    │     └─ ...
    ├─ fr/
    │  └─ ...
    ├─ pt_BR/
    │  └─ ...
    └─ pt_PT/
       └─ ...

Plural forms

As we said in the introduction, different languages might sport different plural rules. However, gettext saves us from this trouble once again. When creating a new .po file, you’ll have to declare the plural rules for that language, and translated pieces that are plural-sensitive will have a different form for each of those rules. When calling Gettext in code, you’ll have to specify the number related to the sentence, and it will work out the correct form to use - even using string substitution if needed.

Plural rules include the number of plurals available and a boolean test with n that would define in which rule the given number falls (starting the count with 0). For example:

Now that you understood the basis of how plural rules works - and if you didn’t, please look at a deeper explanation on the LingoHub tutorial -, you might want to copy the ones you need from a list instead of writing them by hand.

When calling out Gettext to do localization on sentences with counters, you’ll have to give him the related number as well. Gettext will work out what rule should be in effect and use the correct localized version. You will need to include in the .po file a different sentence for each plural rule defined.

Sample implementation

After all that theory, let’s get a little practical. Here’s an excerpt of a .po file - don’t mind with its format, but instead the overall content, you’ll learn how to edit it easily later:

msgid ""
msgstr ""
"Language: pt_BR\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

msgid "We're now translating some strings"
msgstr "Nós estamos traduzindo algumas strings agora"

msgid "Hello %1$s! Your last visit was on %2$s"
msgstr "Olá %1$s! Sua última visita foi em %2$s"

msgid "Only one unread message"
msgid_plural "%d unread messages"
msgstr[0] "Só uma mensagem não lida"
msgstr[1] "%d mensagens não lidas"

The first section works like a header, having the msgid and msgstr especially empty. It describes the file encoding, plural forms and other things that are less relevant. The second section translates a simple string from English to Brazilian Portuguese, and the third does the same, but leveraging string replacement from sprintf so the translation may contain the user name and visit date.
The last section is a sample of pluralization forms, displaying the singular and plural version as msgid in English and their corresponding translations as msgstr 0 and 1 (following the number given by the plural rule). There, string replacement is used as well so the number can be seen directly in the sentence, by using %d. The plural forms always have two msgid (singular and plural), so it’s advised to not use a complex language as the source of translation.

Discussion on l10n keys

As you might have noticed, we’re using as source ID the actual sentence in English. That msgid is the same used throughout all your .po files, meaning other languages will have the same format and the same msgid fields but translated msgstr lines.

Talking about translation keys, there are two main “schools” here:

  1. msgid as a real sentence.
    The main advantages are:
    • if there are pieces of the software untranslated in any given language, the key displayed will still maintain some meaning. Example: if you happen to translate by heart from English to Spanish but need help to translate to French, you might publish the new page with missing French sentences, and parts of the website would be displayed in English instead;
    • it’s much easier for the translator to understand what’s going on and make a proper translation based on the msgid;
    • it gives you “free” l10n for one language - the source one;
    • The only disadvantage: if you need to change the actual text, you would need to replace the same msgid across several language files.
  2. msgid as a unique, structured key.
    It would describe the sentence role in the application in a structured way, including the template or part where the string is located instead of its content.
    • it’s a great way to have the code organized, separating the text content from the template logic.
    • however, that could bring problems to the translator that would miss the context. A source language file would be needed as a basis for other translations. Example: the developer would ideally have an en.po file, that translators would read to understand what to write in fr.po for instance.
    • missing translations would display meaningless keys on screen (top_menu.welcome instead of Hello there, User! on the said untranslated French page). That’s good it as would force translation to be complete before publishing - but bad as translation issues would be really awful in the interface. Some libraries, though, include an option to specify a given language as “fallback”, having a similar behavior as the other approach.

The Gettext manual favors the first approach as, in general, it’s easier for translators and users in case of trouble. That’s how we will be working here as well. However, the Symfony documentation favors keyword-based translation, to allow for independent changes of all translations without affecting templates as well.

Everyday usage

In a common application, you would use some Gettext functions while writing static text in your pages. Those sentences would then appear in .po files, get translated, compiled into .mo files and then, used by Gettext when rendering the actual interface. Given that, let’s tie together what we have discussed so far in a step-by-step example:

1. A sample template file, including some different gettext calls

<?php include 'i18n_setup.php' ?>
<div id="header">
    <h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
    <!-- code indented this way only for legibility -->
    <?php if ($unread): ?>
        <h2><?=sprintf(
            ngettext('Only one unread message',
                     '%d unread messages',
                     $unread),
            $unread)?>
        </h2>
    <?php endif ?>
</div>

<h1><?=gettext('Introduction')?></h1>
<p><?=gettext('We\'re now translating some strings')?></p>

2. A sample setup file (i18n_setup.php as used above), selecting the correct locale and configuring Gettext

<?php
/**
 * Verifies if the given $locale is supported in the project
 * @param string $locale
 * @return bool
 */
function valid($locale) {
   return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es');
}

//setting the source/default locale, for informational purposes
$lang = 'en_US';

if (isset($_GET['lang']) && valid($_GET['lang'])) {
    // the locale can be changed through the query-string
    $lang = $_GET['lang'];    //you should sanitize this!
    setcookie('lang', $lang); //it's stored in a cookie so it can be reused
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
    // if the cookie is present instead, let's just keep it
    $lang = $_COOKIE['lang']; //you should sanitize this!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
    // default: look for the languages the browser says the user accepts
    $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
    array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
    foreach ($langs as $browser_lang) {
        if (valid($browser_lang)) {
            $lang = $browser_lang;
            break;
        }
    }
}

// here we define the global system locale given the found language
putenv("LANG=$lang");

// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instance
setlocale(LC_ALL, $lang);

// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');

// indicates in what encoding the file should be read
bind_textdomain_codeset('main', 'UTF-8');

// if your application has additional domains, as cited before, you should bind them here as well
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');

// here we indicate the default domain the gettext() calls will respond to
textdomain('main');

// this would look for the string in forum.mo instead of main.mo
// echo dgettext('forum', 'Welcome back!');
?>

3. Preparing translation for the first run

To make matters easier - and one of the powerful advantages Gettext has over custom framework i18n packages - is its custom file type. “Oh man, that’s quite hard to understand and edit by hand, a simple array would be easier!” Make no mistake, applications like Poedit are here to help - a lot. You can get the program from their website, it’s free and available for all platforms. It’s a pretty easy tool to get used to, and a very powerful one at the same time - using all powerful features Gettext has available.

In the first run, you should select “File > New Catalog” from the menu. There you’ll have a small screen where we will set the terrain so everything else runs smoothly. You’ll be able to find those settings later through “Catalog > Properties”:

After setting those points you’ll be prompted to save the file - using that directory structure we mentioned as well, and then it will run a scan through your source files to find the localization calls. They’ll be fed empty into the translation table, and you’ll start typing in the localized versions of those strings. Save it and a .mo file will be (re)compiled into the same folder and ta-dah: your project is internationalized.

4. Translating strings

As you may have noticed before, there are two main types of localized strings: simple ones and the ones with plural forms. The first ones have simply two boxes: source and localized string. The source string can’t be modified as Gettext/Poedit do not include the powers to alter your source files - you should change the source itself and rescan the files. Tip: you may right-click a translation line and it will hint you with the source files and lines where that string is being used.
On the other hand, plural form strings include two boxes to show the two source strings, and tabs so you can configure the different final forms.

Whenever you change your sources and need to update the translations, just hit Refresh and Poedit will rescan the code, removing non-existent entries, merging the ones that changed and adding new ones. It may also try to guess some translations, based on other ones you did. Those guesses and the changed entries will receive a “Fuzzy” marker, indicating it needs review, being highlighted in the list. It’s also useful if you have a translation team and someone tries to write something they’re not sure about: just mark Fuzzy and someone else will review later.

Finally, it’s advised to leave “View > Untranslated entries first” marked, as it will help you a lot to not forget any entry. From that menu, you can also open parts of the UI that allow you to leave contextual information for translators if needed.

Tips & Tricks

Possible caching issues

If you’re running PHP as a module on Apache (mod_php), you might face issues with the .mo file being cached. It happens the first time it’s read, and then, to update it, you might need to restart the server. On Nginx and PHP5 it usually takes only a couple of page refreshes to refresh the translation cache, and on PHP7 it is rarely needed.

Additional helper functions

As preferred by many people, it’s easier to use _() instead of gettext(). Many custom i18n libraries from frameworks use something similar to t() as well, to make translated code shorter. However, that’s the only function that sports a shortcut. You might want to add in your project some others, such as __() or _n() for ngettext(), or maybe a fancy _r() that would join gettext() and sprintf() calls. Other libraries, such as oscarotero’s Gettext also provide helper functions like these.

In those cases, you’ll need to instruct the Gettext utility on how to extract the strings from those new functions. Don’t be afraid, it’s very easy. It’s just a field in the .po file, or a Settings screen on Poedit. In the editor, that option is inside “Catalog > Properties > Source keywords”. You need to include there the specifications of those new functions, following a specific format:

After including those new rules in the .po file, a new scan will bring in your new strings just as easy as before.

References

Back to Top

依存性の注入

Wikipediaによると、

依存性の注入とはソフトウェアデザインパターンの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 拡張モジュールは既に思いっきり古くなっていて、これらの拡張モジュールがその後継になっている。

mysql 拡張モジュールの開発は大昔に終了していて、 PHP 5.5.0で「廃止予定」となった。 そして、 PHP 7.0.0で公式に削除された

いま使っているモジュールがどれなのかを知りたいなら、わざわざ php.ini の設定を調べるまでもない。 お好みのエディターで mysql_* を検索してみればいい。 mysql_connect() とか mysql_query() みたいな関数がヒットしたら、 mysql モジュールを使ってるってことだ。

当面は PHP 7.0 を使うつもりがないのだとしても、今ちゃんと考えておかないと、いざというときに大変なことになる。 いちばんいいのは、通常の開発スケジュールの中で、mysql モジュールを使っている部分を mysqliPDO に徐々に置き換えていくことだ。 そうすれば、後になってあせらずにすむ。

** mysql から mysqli への移行について、単に「mysql_*mysqli_* に置換すればOK」などと書いているような記事には用心すること。話を単純化しすぎているだけではなく、mysqli ならではの利点(パラメータのバインドなど。これは PDO でも用意されている)の活用ができなくなってしまう。 **

PDO 拡張モジュール

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

<?php
// 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');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- まずデータのフィルタリングを行う ([データのフィルタリング](#data_filtering) を参照)。特に INSERT や UPDATE などで重要
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- PDOが自動的にSQLのエスケープ処理をする
$stmt->execute();

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

INSERT や UPDATE などの書き込み操作の場合は、まず データをフィルタリング することが大切だ。 その後で、その他 (HTML タグや JavaScript など) のエスケープを行う。 PDO はあくまでも 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 getAllFoos($db) {
    return $db->query('SELECT * FROM table');
}

foreach (getAllFoos($db) 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';

// インスタンスを作る
$fooModel = new FooModel($db);
// Fooのリストを作る
$fooList = $fooModel->getAllFoos();

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

models/FooModel.php

<?php
class FooModel
{
    protected $db;

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

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

views/foo-list.php

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

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

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

Twig ライブラリを使った。

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

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

{% include 'footer.html' %}

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

Twig ライブラリを使った。

// template.html

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

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

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

{% 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() にエラーレベル定数を渡せば、そのスクリプトの実行中に どのレベルのエラー処理をするのかを設定できる。 たとえば、エラー(error)とか警告(warning)は表示させたいけれども、別に注意(notice)は見たくないという場合は、こんなふうにすればいい。

<?php
error_reporting(E_ERROR | E_WARNING);

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

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

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

<?php
echo @$foo['bar'];

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

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

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

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

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

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

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

xdebug.scream = On

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

<?php
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) の人たちが、既知のセキュリティ問題とその対策をまとめてくれている。 セキュリティが気になる開発者は必読だ。 Padraic Bradyが書いたSurvive The Deep End: PHP Security も、 ウェブアプリケーションセキュリティに関してPHP向けに書かれたよいドキュメントだ。

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

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

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

Passwords should also be individually salted by adding a random string to each password before hashing. This prevents dictionary attacks and the use of “rainbow tables” (a reverse list of crytographic hashes for common passwords.)

Hashing and salting are vital as often users use the same password for multiple services and password quality can be poor.

Fortunately, nowadays PHP makes this easy.

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 {
    // パスワードが一致しなかった
}

password_hash() takes care of password salting for you. The salt is stored, along with the algorithm and “cost”, as part of the hash. password_verify() extracts this to determine how to check the password, so you don’t need a separate database field to store your salts.

データのフィルタリング

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

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

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

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

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

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

サニタイズ

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

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

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

サニタイズフィルター

アンシリアライズ

ユーザーからの入力など、信頼できないところから渡されたデータを unserialize() するのは危険だ。 悪意のあるユーザーが送り込んだオブジェクト(ユーザー定義のプロパティつきのもの)のインスタンスを生成できてしまい、 たとえそのオブジェクトを一切使わなくても そのデストラクタは実行されてしまう。 なので、信頼できないデータのアンシリアライズは避けるべきだ。

どうしてもその必要がある場合は、PHP 7で追加されたオプション allowed_classes を使うこと。このオプションは、許可した型のオブジェクトだけをアンシリアライズできるように制限するものだ。

バリデーション

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

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

設定ファイル

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

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 を組み合わせる。この設定はメモリの利用効率がよくて高速に動作するが、設定に手間がかかる。

If you are running Apache 2.4 or later, you can use mod_proxy_fcgi to get great performance that is easy to setup.

共有サーバー

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

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

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

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

デプロイツール

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

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

Phing を使えば、パッケージングやデプロイそしてテストといった処理をシンプルなXMLビルドファイルで設定できる。 PhingはApache Ant をベースに作られたもので、 Webアプリのインストールやアップデートに必要となるタスク群を提供する。 カスタムタスクで機能を追加することもでき、カスタムタスクはPHPで書ける。 古くからあるツールだけあって堅実で安定してるけど、ちょっと古臭さも感じる (設定をXMLファイルで管理するところとかね)。

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

Rocketeer は、Laravelフレームワークの思想にヒントを得たものだ。 高速かつエレガントであり、よく考えられたデフォルト設定で簡単に使えることを目標としている。 複数サーバーや複数ステージ、アトミックなデプロイに対応していて、複数のデプロイを並列に実行できる。 すべてのパーツは動かしたままでの交換や拡張に対応しているし、ぜんぶPHPで書かれている。

Deployer はPHPで書かれたデプロイツールで、シンプルかつ機能的だ。 タスクを並列に実行し、アトミックなデプロイを行い、サーバー間の整合性を維持する。 SymfonyやLaravel、Zend Framework、そしてYiiなどで使える、一般的なタスクのレシピが用意されている。 Younes Rafieの記事Easy Deployment of PHP Applications with Deployer は、Deployerを使ってアプリケーションをデプロイするためのよいチュートリアルになっている。

Magallanes もPHPで書かれたツールで、YAMLでのシンプルな設定ができる。 複数サーバーや複数環境、アトミックなデプロイに対応していて、 一般的なツールやフレームワークで使える組み込みのタスクが用意されている。

あわせて読みたい:

サーバープロビジョニング

サーバーの構成管理は、大量のサーバーを扱うようになると特に大変なタスクだ。 いろんなツールが用意されているので、こういったインフラの構築を自動化できる。 ツールを使えば、適切なサーバーが適切な構成になっていることを確実にできる。 これらのツールは大規模なクラウドホスティングプロバイダー (Amazon Web Services, Heroku, DigitalOceanなど) にもインスタンスの管理用に統合されていることが多くて、 より簡単にアプリケーションをスケールできるようになっている。

Ansible は、YAMLファイルでインフラを管理するツールだ。 気軽に使い始められるし、複雑で大規模なアプリケーションにも使える。 クラウドのインスタンスを管理するためのAPIも用意されていて、 対応したツールを使えば動的インベントリを通じてインスタンスを管理できる。

Puppet は、独自の言語やファイルタイプを使ってサーバーや構成を管理する。 マスター/クライアント形式で使うこともできるし、「マスターレス」モードで使うこともできる。 マスター/クライアントモードの場合は、所定のインターバルでクライアントが中央サーバーをポーリングして、 新しい構成が見つかったら自分自身を更新する。 マスターレスモードでは、変更内容を各ノードにプッシュする。

Chef はRubyで作られた強力なシステムインテグレーションフレームワークで、 サーバー環境や仮想マシンをまるごと構築できる。 Amazon Web Servicesとも統合されていて、OpsWorksというサービスを通じて利用する。

あわせて読みたい:

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

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

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

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

あわせて読みたい

Back to Top

仮想化

開発環境だの本番環境だの、いろいろ違う環境でアプリケーションを動かしていると、おかしなバグに出くわしてしまいかねない。 「開発環境だと問題ないのに、本番環境だと動かない」みたいなやつだ。 また、チームで開発しているときに、開発環境のいろんなライブラリのバージョンをきちんと統一しておくのも、面倒だ。

Windowsで開発してLinux(などWindows以外の環境)にデプロイしていたり、チームで開発していたりする場合は、 仮想マシンを使うことを考えるべきだ。 何も難しいことはない。VMwareやVirtualBoxみたいな有名どころだけでなく、 仮想環境を簡単に準備するためのツールも用意されている。

Vagrant

Vagrantを使えば、既知の仮想環境を使って自分用のボックスを作れて、その環境の構成も、たったひとつの設定ファイルだけでできる。 このボックスを手動で設定することもできるし、 PuppetとかChefみたいな「プロビジョニング」ソフトにおまかせすることだってできる。 ベースとなる環境を配布できるようにしておけば、複数の開発環境をまったく同じ状態に構築できる。 複雑怪奇なコマンドを羅列した「環境構築手順書」だとかいうのもいらなくなるってこと。 ベース環境を「破棄」したり作りなおしたりするのもそんなに手間がかからないので、 まっさらな環境を用意するのもお手軽にできる。

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

ちょっとした手助け

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

Docker

Docker - a lightweight alternative to a full virtual machine - is so called because it’s all about “containers”. A container is a building block which, in the simplest case, does one specific job, e.g. running a web server. An “image” is the package you use to build the container - Docker has a repository full of them.

A typical LAMP application might have three containers: a web server, a PHP-FPM process and MySQL. As with shared folders in Vagrant, you can leave your application files where they are and tell Docker where to find them.

You can generate containers from the command line (see example below) or, for ease of maintenance, build a docker-compose.yml file for your project specifying which to create and how they communicate with one another.

Docker may help if you’re developing multiple websites and want the separation that comes from installing each on it’s own virtual machine, but don’t have the necessary disk space or the time to keep everything up to date. It’s efficient: the installation and downloads are quicker, you only need to store one copy of each image however often it’s used, containers need less RAM and share the same OS kernel, so you can have more servers running simultaneously, and it takes a matter of seconds to stop and start them, no need to wait for a full server boot.

例:PHPアプリケーションをDockerで実行する

マシンにDockerをインストールしたら、あと一手間だけで、PHP が使える Apache 環境を用意できる。 次のコマンドは、最新版のPHP入りのApache環境をダウンロードして、 ディレクトリ /path/to/your/php/fileshttp://localhost:8080 で見られるようにするものだ。

docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache

これで、コンテナを初期化して実行できる。-d は、バックグラウンドで実行するオプションだ。 コンテナを停止したり、再開したりしたくなったりした場合は、nameに指定した名前を使って docker stop my-php-webserverdocker start my-php-webserver などとするだけだ。 さっきのパラメータを何度も指定する必要はない。

Dockerについてもっと知りたい

ここではとりあえず、PHP の使える Apache 環境をお手軽に実行するためのコマンドだけを紹介した。 でも、Docker の実力はこんなものじゃなくて、まだまだいろんなことができる。 中でも、PHP 開発者にとっていちばん重要なのは、Web サーバーをデータベースのインスタンスに接続したりなどといった設定だ。 このあたりについては Docker User Guide を参照すること。 Take time to learn the terminology and read the Docker User Guide to get the most from it, and don’t run random code you’ve downloaded without checking it’s safe – unofficial images may not have the latest security patches. If in doubt, stick to the official repositiories.

The PHPDocker.io site will auto-generate all the files you need for a fully-featured LAMP/LEMP stack, including your choice of PHP version and extensions.

Back to Top

キャッシュ

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

オペコードキャッシュ

PHPファイルを実行するときには、まずそれをオペコード (CPU用の機械語の指示) にコンパイルしなければいけない。 ソースコードに変更がなければ、オペコードも同じものになる。 ということは、PHP ファイルに変更がなければコンパイル処理は CPU リソースの無駄遣いになるということだ。

オペコードキャッシュは、コンパイル済みのオペコードをメモリに格納し、それ以降の呼び出しで再利用することで、 冗長なコンパイルを回避している。よくあるのは、ファイルのシグネチャや更新時刻をチェックして変更の有無を判断する方式だ。

オペコードキャッシュを使えば、アプリケーションの実行速度が相当向上する可能性がある。 PHP 5.5 からは、 Zend OPcache というオペコードキャッシュが標準で組み込まれるようになった。 使っている PHP パッケージやディストリビューションにもよるけど、普通はデフォルトで有効になっていることが多い。 opcache.enable や、 phpinfo() の出力で確認しよう。 5.5 より前のバージョンなら、PECL の拡張モジュールが使える。

オペコードキャッシュについて詳しく知りたければ、以下を参照すること。

オブジェクトキャッシュ

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

バイトコードキャッシュ用ソリューションの多くはカスタムデータも同様にキャッシュできるので、さらに活用できる。 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

Documenting your Code

PHPDoc

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

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

<?php
/**
 * @author A Name <a.name@example.com>
 * @link http://www.phpdoc.org/docs/latest/index.html
 */
class DateTimeHelper
{
    /**
     * @param mixed $anything Anything that we can convert to a \DateTime object
     *
     * @throws \InvalidArgumentException
     *
     * @return \DateTime
     */
    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 タグだ。 @author タグは、コードの作者を表す。作者が複数いる場合は、何度も繰り返し使ってもかまわない。 @link タグは、そのコードに関連するウェブサイトへのリンクを示すために使う。

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

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

Back to Top

情報源

ソースから

フォローすべき人たち

PHPコミュニティの中で注目すべき人を見つけるのは、コミュニティに加わったばかりの人たちにとっては難しいものだ。 PHPコミュニティの主要メンバーとTwitterアカウントの一覧が、ここにまとまっている。

メンタリング

PHP PaaS プロバイダ

これらのPaaSでどのバージョンのPHPが使われているのかを知りたければ、PHP Versions をチェックしよう。

フレームワーク

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

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

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

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

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

コンポーネント

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

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

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

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

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

その他の資料

チートシート

ベストプラクティス

News around the PHP and web development communities

You can subscribe to weekly newsletters to keep yourself informed on new libraries, latest news, events and general announcements, as well as additional resources being published every now and then:

PHP界

動画チュートリアル

YouTubeチャンネル

有償の動画

書籍

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

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

無償の書籍

有償の書籍

Back to Top

コミュニティ

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

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

PHP ユーザーグループ

大都市に住んでいるなら、きっと近場に PHP ユーザーグループがあるはずだ。 近場の PHP ユーザーグループを見つけるには、php.net のユーザーグループ一覧 を使えばいい。 これはPHP.ugをベースにしたものだ。それ以外にも、Meetup.com を使ってもいいし、 お好みのサーチエンジン (たとえば Google とか) で php user group near me みたいに検索してみるとかね。 小さな町なら、もしかしたらユーザーグループがないかもしれない。 だったら、作ればいい!

数々のグループの中でも、特筆すべきものがある。NomadPHPPHPWomen だ。 NomadPHP は、オンラインでのミーティングを毎月2回開催している。 そのミーティングでは、PHP コミュニティの有名人によるプレゼンも行われている。 PHPWomen は、PHP 界の女性をターゲットにしたユーザーグループだったが、今はそれにとどまらない。 より多様性のあるコミュニティを目指そうという人なら、誰でも受け入れている。 PHPWomen は、サポートやメンターシップ、教育などのネットワークを提供している。 そして「女性にやさしい」プロフェッショナルな空気を広めることを目指している。

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

PHP カンファレンス

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

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

ElePHPants

ElePHPant は PHP プロジェクトのマスコットだ。 もともとは 1998 年に Vincent Pontier がデザインしたもの。つまり彼は、世界中の elePHPant たちの父なる神である。 その約10年後には、かわいらしいぬいぐるみもできあがった。 いまやあちこちのPHPカンファレンスでelePHPantが見られるし、自分のPCのそばにelePHPantを置いているPHP開発者も多い。

Vincent Pontierへのインタビュー