PHP V5.3 の名前空間を利用して、理解しやすく保守の容易なコードを作成する

コードを体系化し、名前の衝突を防ぐ

PHP アプリケーションの開発には名前空間を使用すべきでしょうか。この記事では、名前空間の構文の概要、名前空間を使用する上でのベスト・プラクティス、そして名前空間を使用する簡単な Model-View-Controller アプリケーションの例について説明します。

Don Denoncourt, Author, Consultant

Don DenoncourtDon Denoncourt は Java 技術、Groovy、Grails、PHP を専門とするフリーのコンサルタント、トレーナー、メンターであり、著作者でもあります。



2011年 3月 01日

「コナンが私の手本だよ。」と、私が夕食の食卓で話したとすると、息子はすぐに私が映画「コナン・ザ・グレート (原題: Conan the Barbarian)」の主人公を手本としているのだと思い、一方、妻は私が深夜のトークショーの司会を務めるコナン・オブライエン (Conan O'Brien) のようになりたがっているのだと思うでしょう。このような話の背景の曖昧さは、IT の世界では名前の衝突として知られています。多くの言語には名前の衝突を防ぐための仕組みがあり、PHP V5.3 にもこの仕組みが導入されています。PHP では、新しい名前空間の機能によって名前が衝突する問題を解決しています。もちろん、PHP で衝突を防いでいる名前は人々の名前ではなく、クラスや関数、定数の名前です。

この記事では、なぜ皆さんの次のプロジェクトで名前空間の使用を検討する必要があるのかを説明します。まず、名前空間のセマンティクスの概要、ベスト・プラクティス、および名前空間を使用する簡単な Model-View-Controller (MVC) アプリケーションの例について説明します。次に、名前空間が Eclipse、NetBeans、および Zend Studio でどのようにサポートされているかについて明らかにしますが、特に Eclipse で名前空間を使用する詳細な方法について取り上げます。

名前空間は必要なのか

PHP 言語の強みはその単純さです。したがって、皆さんが PHP の初心者なのであれば、名前空間はいずれ理解する必要がある概念の 1 つにすぎません。しかし、皆さんが以下のいずれかに当てはまる場合には、名前空間の使用を検討する必要があります。

  • 何百という PHP ファイルを持つ大規模なアプリケーションを開発する場合
  • アプリケーションがコーディング・チームによって開発されている場合
  • PHP V5.3 と名前空間を使用するフレームワークの使用を計画している場合
  • 他の言語 (Java™、Ruby、Python など) では名前空間 (あるいはパッケージのような類似機能) を使用していた場合

比較的小規模のアプリケーションを 1 人で開発している場合には、名前空間は必要ないかもしれません。しかし、それ以外の人々にとっては、名前空間を使用することでクラス構造をすっきりと体系化することができ、もちろん、名前の衝突を防ぐことができるようになります。この 2 つの理由から、多くのフレームワーク開発者は名前空間を採用するようになっています。例えば Zend Framework (巨大な PHP フレームワーク) は Zend Framework V2.0 で名前空間を使用しています。


簡単な概要

名前空間により、名前にコンテキストを追加することができます。例えば、リスト 1 に示す 2 つのクラスでは名前が衝突しています。

リスト 1. 名前空間を使用しないと衝突が起きる、同じ名前の 2 つのクラス
class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}

class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}

名前空間を指定するためには、ソースの最初の文として、名前空間宣言を追加するだけです (リスト 2)。

リスト 2. 名前空間を使用することで衝突を防ぐことができる、同じ名前の 2 つのクラス
<?php
namespace barbarian;
class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}
namespace obrien;
class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}
$conan = new \barbarian\Conan();
assert('extremely muscular born: before history' == 
   "$conan->bodyBuild born: $conan->birthDate");

$conan = new \obrien\Conan();
assert('very skinny born: 1963' == "$conan->bodyBuild born: $conan->birthDate");
?>

上記のコードは問題なく実行されますが、なぜ 2 つの Conan が同時に機能するかを説明する前に、2 つの点を指摘しておきましょう。第 1 に、このコードが想定どおりに動作していることを示すためにアサーションを使用しています。第 2 に、ここでは1 つのソース・ファイル内で複数の名前空間を宣言するという、絶対にしてはならないことをしています。

名前空間により、2 つの Conan に一意の修飾子を提供することができます。このコードは、無骨な破壊者である Conan を参照しているのか、それとも深夜のトークショーの司会者である Conan を参照しているのかを明確に示しています。インスタンス化のための構文には、以下のようにバックスラッシュ (\) が使用され、その後に名前空間の名前が続いていることに注意してください。

$conan = new \barbarian\Conan();

および

$conan = new \obrien\Conan();

これらの修飾子は Windows® スタイルのディレクトリー修飾子のように見えますが、そのように考えても問題ありません。その 1 つの理由は、名前空間は (ディレクトリーと同様に) 相対参照と絶対参照の両方をサポートしているからです。またもう 1 つの理由は、名前空間と一致するディレクトリーに、クラス・ファイルのソースを配置することがベスト・プラクティスだからです。


名前空間を使用する

現実的には、2 つの Conan クラスを barbarian と obrien というディレクトリーに分け、この 2 つのクラスを別の PHP ファイルから参照する必要があります。PHP の名前空間を参照するには、以下の 3 つの方法があります。

  • クラス名の接頭辞として名前空間を指定する
  • 名前空間をインポートする
  • 名前空間にエイリアスを付ける

第 1 の方法を使用するには、単純にクラス名の接頭辞として名前空間を指定します (当然ですが、名前空間の指定はソース・ファイルをインクルードした後に行います)。

include "barbarian/Conan.php";
$conan = new \barbarian\Conan();

この方法は非常に単純ですが、大規模なアプリケーションの場合には何度も名前空間を入力しなければならないという問題があります。また入力が面倒なことの他に、コード・ベースが必要以上に雑然としたものになります。第 2 の方法では、PHP V5.3 の予約語である use を使用して以下のように名前空間をインポートします。

include "barbarian/Conan.php";
use barbarian\Conan;  
$conan = new Conan();

第 3 の方法では、以下のように名前空間のエイリアスを指定します。

include "barbarian/Conan.php";
use \barbarian\Conan as Cimmerian;
$conan = new Cimmerian();

(ちなみに、Cimmerian はコナン・ザ・グレートの主人公の別名として知られています。)

上記の 3 つの方法に共通する問題は、include 文を使用している点です。include を不要にするためには __autoload 関数を使用します。PHP のマジック・メソッドである __autoload 関数は、まだソース・ファイルにインクルードされていないクラスが参照されると呼び出されます。リスト 3 のコードは autoload.php というファイルに配置します。

リスト 3. ソース・ファイルを動的にインクルードする __autoload マジック関数
<?php
function __autoload($classname) {
  $classname = ltrim($classname, '\\');
  $filename  = '';
  $namespace = '';
  if ($lastnspos = strripos($classname, '\\')) {
    $namespace = substr($classname, 0, $lastnspos);
    $classname = substr($classname, $lastnspos + 1);
    $filename  = str_replace('\\', '/', $namespace) . '/';
  }
  $filename .= str_replace('_', '/', $classname) . '.php';
  require $filename;
}
?>

次に autoload.php をソースにインポートします。

require_once "autoload.php"; 
use \barbarian\Conan as Cimmerian;

オートローダーの大きなメリットは、クラスごとに include 文を作成する必要がないことです。ただし、本来 PHP の名前空間はクラスに対してだけではなく、関数と定数にも使用できますが、オートローダーの方法はクラスに対してだけ使用できることに注意してください。オートローダーは非常に便利であり、関数をコーディングする代わりに、適切な名前が付けられたユーティリティー・クラスの中にメソッドを作成し、また不変クラスの中には定数を配置することができるようになります。


MVC について考察する

コナン・ザ・グレートでコナンの両親を殺害した首領が殺されるのを、コナン・オブライエンが冷笑しているのは忘れて、簡単な MVC アプリケーションの例に移りましょう。名前空間のメリットを生かすためには、コード行の入力を始める前に命名規則を決める必要があります。一般的なベスト・プラクティスは、名前空間ツリーを使用することです。名前空間には、上位レベルの名前空間とサブ名前空間があることを理解してください。皆さんの会社に複数のアプリケーションがある場合には、会社の名前を上位レベルの名前空間として使用し、サブ名前空間をアプリケーション用に使用すると便利です。そして、さまざまなディレクトリーが含まれるレベルでは、それらのディレクトリーの名前によって、各ディレクトリーに含まれている PHP クラスのアプリケーションでの機能を明確に示すようにするのです。例えば、上位レベルの会社の名前空間には denoncourt、最初のサブレベルには retail、それに続くレベルには機能名を指定します (リスト 4)。

リスト 4. ネストされたサブ名前空間を含むことができるような名前空間の設計
/denoncourt
	/retail
		/common
		/controller
		/model
		/utility
		/view

controllermodel、および view というサブ名前空間は当然、MVC アーキテクチャー用ですが、私が追加した utilitycommon というサブ名前空間は、他のサブ名前空間のどれにも明確に当てはまらなかった一般的なクラスに使用するためのものです。

では早速、簡単な MVC アプリケーションのコードに移りましょう。リスト 5 に、ルート・フォルダーに配置される index.php のコードを示します。

リスト 5. controller クラスを使用する MVC アプリケーションの index.php
<?php
require "autoload.php";
use denoncourt\retail\controller as Control;
$controller = new Control\Controller();
$controller->execute();
?>

名前空間が長いので Control というエイリアス名が使用されていることに注意してください。私は名前空間を使用する場合には、エイリアスを使用するのを好みますが、その理由は 2 つあります。第 1 の理由は、あとで名前空間をリネームする場合、ソース・ファイルごとにコードを 1 行変更するだけで済むからです。そして第 2 の理由は、クラスをインスタンス化する場合には名前空間を完全修飾することがベスト・プラクティスですが、Control\Controller() を使用すると実質的には \denoncourt\retail\controller\Controller() を使用したのと同じことになるからです。

もちろん、以下のように上位レベルの名前空間のエイリアスを作成し、サブ名前空間の名前を使用してクラスをインスタンス化してもよかったことに注意してください。

use denoncourt\retail as Retail;
$controller = new retail\controller\Controller();

この方法は同じソース・ファイルの中で、複数のレベルの名前空間を参照する場合に便利です。リスト 6 に示すコードでは、denoncourt/retail/controller ディレクトリーの中に Controller.php を作成しました。

リスト 6. ユーザー入力に基づくアクションを記述する MVC の controller クラス
<?php
namespace denoncourt\retail\controller;
use denoncourt\retail as retail;

class Controller {
  public function execute() {
    switch ($_GET['action']) {
    case 'showItem' :
      $item = new retail\model\Item();
      require "denoncourt/retail/utils/format.php";
      require "denoncourt/retail/view/item.php";
      break;
    }
  }
}
?>

リスト 7 に示すコードでは、denoncourt/retail/model の中に Item.php を作成しました。

リスト 7. model サブ名前空間に属する MVC の Item クラス
<?php
namespace denoncourt\retail\model;
class Item {
  public $itemNo = '123';
  public $price = 2.45;
  public $qtyOnHand = 87;
}
?>

リスト 8 に示すコードでは、denoncourt/retail/utils の中に format.php を作成しました。

リスト 8. 関数に名前空間を指定する方法を示す PHP の dollar 関数
<?php
namespace denoncourt\retail;
function dollar($dollar) {
    return "\$$dollar";
}
?>

先ほど触れたように、私はフォーマット関数をユーティリティー・クラスの中に入れるのを好みます (そうすればオートローダーによってコードのインポートが処理されるため、format.php 用に require 文をコーディングする必要がなくなるからです)。

最後に、denoncourt/retail/views にある item.php というビュー・ページのコードをリスト 9 に示します。

リスト 9. controller でインスタンス化された model を表示する item ページ
<html>
<head>
<style>
dt {
  float:left; clear:left;
  font-weight:bold;
  margin-right:10px;
  width:15%;
  text-align: right;
}
dd { text-align:left; }
</style>
</head>
<body>
<dl>
  <dt>Item No:</dt><dd><?php echo "$item->itemNo"; ?></dd>
  <dt>Price:</dt><dd>
       <?php echo \denoncourt\retail\dollar($item->price); ?>
       </dd>
  <dt>Quantity On Hand:</dt><dd><?php echo "$item->qtyOnHand"; ?></dd>
</dl>
</body>
</html>

item ページでは、dollar 関数が \denoncourt\retail\ という名前空間で、どのように修飾されているかに着目してください。


フォールバック

ソース・ファイルに名前空間宣言がある場合、クラス、関数、および定数への参照にはすべて、名前空間のセマンティクスを使用します。PHP は、修飾されていないクラス、関数、または定数に遭遇すると、フォールバックと呼ばれる動作を実行します。ユーザー・クラスでフォールバックが実行されると、コンパイラーはそのクラスが現在の名前空間に属していることを前提に処理を行います。名前空間が指定されていないクラスを参照するためには、バックスラッシュを 1 つ付ける必要があります。例えば、PHP の Exception クラスを参照するためには $error = new \Exception(); のようにします。PHP 標準ライブラリーのクラス (ArrayObjectFindFileKeyFilter など) を使用する場合には、そのことを頭に入れておいてください。

関数や定数の場合、その関数または定数が現在の名前空間に属していない場合には、PHP のフォールバック・メカニズムによって PHP の標準関数にフォールバックします。従って、例えば皆さんが独自の strlen 関数をコーディングした場合、PHP はその strlen 関数を使用することになるでしょう。しかし、PHP の標準関数である strlen を (例えば、皆さん独自の strlen の実装の中で) 使用したい場合には、strlen 関数の前にバックスラッシュを付けて呼び出す必要があります (リスト 10)。

リスト 10. PHP の標準関数を指定する場合、グローバルな名前空間を示すためにバックスラッシュで修飾するコード
<?php
namespace denoncourt\retail;
function strlen($str) {
    return \strlen();
}
?>

名前空間でのグローバル変数と文字列

動的なメソッドをコーディングしたい場合、"denoncourt\retail\controller" のように二重引用符で囲んだ文字列の中に名前空間を配置したくなるかもしれません。しかし、これらのバックスラッシュをエスケープし、"denoncourt\\retail\\controller" のようにする必要があることを忘れないでください。次善の策としては 'denoncourt\retail\controller' のように、単純に単一引用符を使用する方法もあります。

動的プログラミングを行う場合、PHP V5.3 には __NAMESPACE__ という新しいグローバル変数があることを忘れないでください。名前空間を入力する代わりに、以下のように __NAMESPACE__ を使用するようにします。

$echo 'I am using this namespace:'.__NAMESPACE__;

IDE での名前空間のサポート

主要な IDE のほとんどは、既に PHP V5.3 をサポートしています。NetBeans V6.8 は名前空間を十分にサポートしているとともに、コード補完機能を備えているだけでなく、ベスト・プラクティスを基にコードを改善するための提案も行います。例えば PHP の名前空間では、相対参照ではなく絶対参照を使用してコード内の名前空間を完全修飾することがベスト・プラクティスです。相対的な名前空間修飾子を使用するコードを作成すると、NetBeans はコード左端の余白部分に電球のアイコンを表示します。そのアイコンの上にマウス・ポインターを重ねると、NetBeans は変更候補を提案するツール・チップを表示します。そのアイコンをクリックすると、NetBeans はコードを変更してくれます。

Zend Studio にも同じような機能が用意されています。名前空間を使い始めることをためらっている人は、IDE をアップグレードし、お気に入りの IDE のヘルプを参照して名前空間を試してみてください。場合によっては IDE をアップグレードする必要もないかもしれません。多くの IDE は PHP V5.3 の機能を 1 年以上も前から提供しているからです。

PDT (PHP Development Tools) V2.1 も確実に名前空間をサポートしています。PDT は Eclipse 用のプラグインです。PDT をインストールするための参考資料へのリンクが「参考文献」セクションにあります。

名前空間のサポートを有効にするには、まず Eclipse/PDT に PHP V5.3 を使用することを指示する必要があります。そのためには、アプリケーションのメイン・メニューから「Window (ウィンドウ)」 > 「Preferences (設定)」の順にクリックします (図 1)。ツリー・ペインで「PHP」を展開し、「PHP Interpreter (PHP インタープリター)」を選択します。そして PHP のバージョンを「PHP 5.3」に変更し、「OK」をクリックします。

図 1. Eclipse の PDT プラグインでインタープリターを PHP V5.3 に設定する様子
インタープリターを V5.3 に設定する

PHP プロジェクトを作成するには、「File (ファイル)」 > 「New Project (新規プロジェクト)」の順にクリックし、「PHP」ノードを展開したら、「PHP Project (PHP プロジェクト)」をクリックします。PHP ファイルを作成するには、単純に「PHP Explorer (PHP エクスプローラー)」でプロジェクトを右クリックし、「PHP file (PHP ファイル)」をクリックします。PDT は名前空間を示すキーワードの namespaceuse に、適切な構文強調表示を適用します (図 2)。

図 2. PDT では構文強調表示機能を使用して名前空間のキーワードが強調表示され、「PHP Explorer (PHP エクスプローラー) 」ビューと「Outline (アウトライン)」ビューに名前空間が表示されている様子
PHP で構文強調表示機能が使用されている様子を示す画像

PDT で名前空間を「PHP Explorer (PHP エクスプローラー)」ビューと「Outline (アウトライン)」ビューに表示すると、名前空間がさまざまなクラスに割り当てられている様子が表示されて便利です。また PDT は、IDE に備わっている機能として期待される、コード補完機能も提供しています (図 3)。use 文を入力すると、PHP によってコード補完機能が呼び出されます。

図 3. PDT では名前空間に対してもコード補完機能が提供されている様子
コード・リストの中でコード補完機能が動作し、名前空間の選択肢のリストが表示されている様子を示すスクリーンショット

また、クラス名を入力していくと、PDT によりコード補完ウィンドウがポップアップ表示されます。例えば 「new Item」と入力すると、PDT は「Item � denoncourt\retail\item」と記されたウィンドウが表示されます。

「denoncourt\retail\item」を選択すると、PDT によって以下のように必要な use 文と修飾子が、インスタンス化を実行する行に挿入されます。

use denoncourt\retail\model;
new model\Item();

素晴しいことに、「new Conan」と入力すると、PDT は以下の内容のウィンドウを表示します。

	Conan � obrien
	Conan � barbarian

このウィンドウから適切な Conan を選択することができます。私は再び 2 人のコナンに夢中になっている状態に戻ってしまったので、そろそろまとめに入ることにしましょう。


まとめ

まだ名前空間を使い始めることに、皆さんがためらいを感じているのであれば、名前空間の習得をもう 1 年延期してしまう前に、皆さんのお気に入りの IDE に PHP V5.3 のサポートを追加し、名前空間を試してみることをお勧めします。命名規則については、完璧な方式を考え出そうと苦労するよりも、まずは単純な規則を設定することの方が重要です。私個人としては、長年 Java での開発に従事してきた経験から、Java の命名規則に従うのが好みであり、PHP の名前空間にキャメル記法の名前を使用し、アンダーバーは使用しません。皆さんの次の PHP プロジェクトでも名前空間を使用することで、すっきりと体系化されたコードにすることができ、さらには主要な言語のほとんどで一般的となっている機能を使いこなせるようになります。そして、PHP V5.3 (特に名前空間) を既に使用しているフレームワークのメリットを享受する準備が整うことになるでしょう。


ダウンロード

内容ファイル名サイズ
Sample scriptsos-php-5.3namespaces_code.zip6KB

参考文献

学ぶために

製品や技術を入手するために

  • Eclipse PDT V2.1 をダウンロードしてください。
  • 皆さんの次のオープンソース開発プロジェクトを IBM ソフトウェアの試用版を使用して革新してください。ダウンロード、あるいは DVD で入手することができます。
  • BM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版を調べて、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を入手してください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=643532
ArticleTitle=PHP V5.3 の名前空間を利用して、理解しやすく保守の容易なコードを作成する
publish-date=03012011