japan.internet.com The Internet & IT Network


RSSニュース検索
カテゴリ
> トップページ
> Webビジネス
> Eコマース
> Webファイナンス
> Webマーケティング
> パブリック
> Webテクノロジー
> 携帯・ワイヤレス
> Linux Today
> Linux Tutorial
> J.I.C.ブログ
キャリア
> 転職ならen
> 派遣ならen
> アルバイトならen
> IT求人情報
ヘッドライン
> 今日のヘッドライン
> 週間ヘッドライン
Special Link
> フォトコミュニティ
> ストックフォト
> クリップアート
> イラスト
> フェリカ
> Web2.0
> 写真
イベント&セミナー
> イベントカレンダー
> 書評「IT の耳」
> 出張・接待検索
> ニュースガジェット 注目
無料ニュースメール
> 新規登録
> 変更・解除
> オプトインメールの登録・変更・解除
インフォメーション
> パートナーサイト
転職ならエン
就職ならen
求人ならen
履歴書ならen
アルバイトならエン
CRM/SFAならオラクル
> 会社概要地図
> グループ会社
株式会社アエリア
(株)サンゼロミニッツ
株式会社エアネット
> お問い合わせ
> 広告掲載について
> リンクについて
> 著作権について
> その他お問い合わせ
> 利用規約
> 個人情報保護方針
コラム コラム一覧へ戻る

japan.internet.com 編集部 japan.internet.com 編集部
米国 Jupitermedia が運営する、
企業向けアプリケーションの開発者向けの技術情報/サービスサイト。


 メール  著者にメールする
 ホーム  http://www.devx.com/

最新コラム

PHP5の新機能「リフレクションAPI」とその使用法

著者: Octavia Andreea Anghel プリンター用 記事を転送
2008年3月21日 10:00 付の記事
■海外internet.com発の記事

はじめに

 この記事で紹介する機能は、PHP4からPHP5にまだアップグレードしていない人にとっては、大いに心引かれる機能だと思います。それは、PHP5で新たにサポートされたリフレクションAPIです。リフレクションAPIを使用することで、変数、インターフェイス、関数、メソッド、パラメータ、クラスなどの詳細情報を入手できます。リフレクションAPIは機能が豊富で、多数のクラスやメソッドを利用してリフレクションを実現できます。

 この記事ではまず、リフレクションAPIの概念について理解していただくために、リフレクションAPIのクラスとメソッドについて、短い例を交えて簡単に説明します。その後で、PHPで作成した実際的なサンプルアプリケーションを2つ紹介します。1つは、呼び出すメソッドを実行時に判別するアプリケーションで、もう1つは、PHPクラスの情報を示す整形されたHTMLドキュメントを自動生成するアプリケーションです。

リフレクションAPIのクラスとメソッド

 リフレクションAPIの機能の概略を示すために、リフレクションAPIのクラスとメソッド、およびそれらの関係を表す図を紹介します(図1)。これを見るとわかるように、リフレクションAPIのほとんどのクラスは、Reflectorインターフェイスを実装しています(Reflection、ReflectionException、およびReflectionFunctionAbstractは除く)。エクスポート可能なすべてのリフレクションクラスがReflectorインターフェイスを実装しています。

図1 リフレクションAPIのクラスとメソッド
図1 リフレクションAPIのクラスとメソッド

ReflectionClass

 ReflectionClassクラスは、リフレクションAPIの主役となるクラスです。このクラスを使用することで、他のクラス群へのリフレクションを適用し、その時点での全クラスコンポーネントについての情報を抽出します。ReflectionClassのメソッドにはよく似たものが多いので、ここでは、いくつかのメソッドについてのみ使用例を紹介し、残りはメソッド名と説明を示すにとどめます。

  • public void construct(string name) ― ReflectionClassのコンストラクタです。
  • public string getName() ― 調査したクラス名を返します。
  •    // EXAMPLE
       $class = new ReflectionClass('TestClass');
       echo "The class name: ".$class->getName();
    
  • public ReflectionMethod getConstructor() ― 解析されたクラスコンストラクタを表すReflectionMethodオブジェクトを返します。
  •    // EXAMPLE
       $class = new ReflectionClass('TestClass');
       $constructor = $class->getConstructor();
       echo $constructor;
    
  • public ReflectionMethod getMethod(string name) ― nameパラメータで指定したメソッドを表すReflectionMethodオブジェクトを返します。
  •    // EXAMPLE
       $class = new ReflectionClass('TestClass');
       $method = $class->getMethod('testMethod_1');
       echo $method;
    
  • public ReflectionMethod[] getMethods() ― 解析されたすべてのクラスメソッドを表すRelectionMethodオブジェクトの配列を返します。
  •    // EXAMPLE
       $class = new ReflectionClass('TestClass');
       $methods = $class->getMethods();
       foreach($methods as $in)
          { echo $in; }
    

 このクラスには、上記の挙げたメソッドの他にも、以下のような同様のメソッドがあります。使い方も同様です。つまり、調査するクラスの名前をパラメータで渡してReflectionClassのインスタンスを作成し、そのインスタンスを使用して以下のメソッドを呼び出します。

  • public ReflectionProperty getProperty(string name)
  • public ReflectionProperty[] getProperties()
  • public mixed getConstant(string name)
  • public array getConstants()
  • public ReflectionClass[] getInterfaces()

 以上のメソッドで取得したオブジェクトを使用すると、クラスの内部をさらに掘り下げた情報にアクセスできます。これについて、次のセクションで説明します。

ReflectionMethodクラス、ReflectionPropertyクラス、ReflectionFunctionクラス

 ReflectionMethodクラスでは、メソッドに対してリフレクションを適用して、解析されたメソッドについての個別情報を取得できます。ここでも、いくつかの例と類似するメソッドの名前を示します。

  • public void __construct(mixed class, string name) ― ReflectionMethodのコンストラクタです。
  • public mixed invoke(stdclass object [, mixed args [, ...]]) ― 解析されたメソッドを呼び出すときに使います。
  •    // EXAMPLES
       //call a non-static method with no arguments
       $testClass = new TestClass();
       $method = new ReflectionMethod('TestClass', 'testMethod_1');
       echo $method->invoke($testClass);
       
       //call a non-static method with two arguments
       $testClass = new TestClass();
       $method = new ReflectionMethod('TestClass', 'testMethod_2');
       echo $method->invoke($testClass, 'testValue_1', 'testValue_2');
       
       //call a static method with no arguments
       $method = new ReflectionMethod('TestClass', 'testMethod_3');
       echo $method->invoke(NULL);
       
       //call a static method with two arguments
       $method = new ReflectionMethod('TestClass', 'testMethod_4');
       echo $method->invoke(NULL,'testValue_1','testValue_2');
    

 他にも、次のようなメソッドがあります。

  • public bool isPublic() ― 解析されたメソッドがpublicの場合に、ブール値trueを返します。
  • public bool isPrivate() ― 解析されたメソッドがprivateの場合に、ブール値trueを返します。
  • public bool isProtected() ― 解析されたメソッドがprotectedの場合に、ブール値trueを返します。
  •    // EXAMPLE
       $method = new ReflectionMethod('TestClass', 'testMethod_1');
       echo $method->isPublic();
    

 ReflectionMethodクラスでメソッドにリフレクションを適用するのと同じように、ReflectionPropertyクラスでは、プロパティにリフレクションを適用できます。このクラスのメソッドはReflectionMethodクラスのメソッドとほぼ同じなので、最小限の例のみを示すことにします。ReflectionPropertyオブジェクトの構築には次のメソッドを使用します。

   public void __construct(mixed class, string name)

 プロパティ値の取得には次のメソッドを使用します。

   public mixed getValue(stdclass object): 

 getValueメソッドのパラメータにはクラスインスタンスを渡します。すると、そのインスタンスの対象プロパティの値が返ります。

   $testClass= new TestClass();
   $property = new ReflectionProperty('TestClass', 'testProperty_1');
   echo $property->getValue($testClass);

 値を設定するにはsetValueメソッドを使用し、クラスインスタンスと新しいプロパティ値の両方を渡します。使用例は次のとおりです。

   $testClass= new TestClass();
   $property = new ReflectionProperty('TestClass', 'testProperty_1');
   echo 'Before : '.$property->getValue($testClass).'<br />';
   $property->setValue($testClass,"new_testProperty_1");
   echo 'After : '.$property->getValue($testClass);

 ReflectionFunctionは、ReflectionMethodクラスやReflectionPropertyクラスと同様に、関数にリフレクションを適用するためのクラスです。関数を呼び出すにはinvokeメソッドを呼び出し、必要に応じてパラメータを渡します。構文は次のとおりです。

   public mixed invoke([mixed args [, ...]])

 簡単な例を次に示します。

   $function = new ReflectionFunction('testFunction_1');
   echo $function->invoke();

 最後に紹介するのはReflectionParameterクラスです。関数やメソッドのパラメータにリフレクションを適用します。ReflectionParameterのインスタンスを作成するときには、関数名とパラメータの位置を渡します(位置は文字列で渡します。1つ目のパラメータはarg1、2つ目のパラメータはarg2、という形です)。

   $parameter = new ReflectionParameter('testFunction_2','arg1');
   echo $parameter->getName();

 パラメータのデフォルト値を取得するには、getDefaultValueメソッドを使用します。また、isPassedByReferenceメソッドを使用すると、関数の特定のパラメータが参照渡しか値渡しかを調べることができます。パラメータが参照渡しの場合にはブール値のtrueが返ります。

 リフレクションAPIには、ここで紹介した以外にも、ReflectionException、ReflectionObject、ReflectionExtension、ReflectionFunctionAbstractといったクラスがあり、それぞれのクラス名が示す項目についての情報を得ることができます。全体を網羅したドキュメントとチュートリアルについては、PHP5の公式マニュアルを参照してください。

例1:リフレクションを利用した簡単なアプリケーション

 本稿のサンプルアプリケーションのテストには、リスト1に示すTestClassクラスを使用しました。このクラスのコードは本稿の冒頭のリンクからダウンロードできます。

リスト1 TestClassクラス
<?php

/**
 * Test function (no arguments)
 */
function testFunction_1(){
   return 'You just called the function testFunction_1...';
}

/**
 * Test function (two arguments)
 */
function testFunction_2($arg1,$arg2){
   return 'You just called the function testFunction_2...';
}

/**
 * Test interface (1) 
 */
interface TestInterface_1 {
}

/**
 * Test interface (2)
 */
interface TestInterface_2 {
}

/**
 * Test superclass 
 */
class SuperTestClass{
   function __construct() {}
}

/**
 * Test class for Reflection API
 */
class TestClass extends SuperTestClass 
   implements TestInterface_1, TestInterface_2 {
   public static $testProperty_1 = "testProperty_1";
   protected $testProperty_2 = "testProperty_2";
   private $testProperty_3 = "testProperty_3";

   const testConstant_1 = "testConstant_1";
   const testConstant_2 = "testConstant_2";
   const testConstant_3 = "testConstant_3";

   /**
   * The TestClass constructor
   */
   function __construct() {
   }

   /**
   * testMethod_1 (no arguments)
   */
   function testMethod_1(){
      return 'You just called the public method testMethod_1...';
   }

   /**
   * testMethod_2 (two arguments)
   */
   function testMethod_2($arg_1, $arg_2){
      return "You just called the public method testMethod_2 with " + 
         "two arguments: arg_1 = ".$arg_1." , arg_2=".$arg_2;
   }

   /**
   * static testMethod_3 (no arguments)
   */
   static function testMethod_3(){
      return "You just called the static method testMethod_3...";
   }

   /**
   * static testMethod_4 (two arguments)
   */
   static function testMethod_4($arg_1, $arg_2){
      return "You just called the static method testMethod_4 with " + 
         "two arguments: arg_1 = ".$arg_1." , arg_2=".$arg_2;
   }

} 
?>

 以降では、前節で示したリフレクションAPIの情報を使用して開発した、簡単な通貨換算アプリケーションの例を紹介します。処理の出発点は「index.htm」というHTMLページです(図2)。このページ自体には特別変わった点はありません。

図2 サンプルの通貨換算アプリケーション
図2 サンプルの通貨換算アプリケーション

 HTMLは次のとおりです。

<html>
<head>
 <h3>Currency converter</h3>
</head>
   
<body>
   
<form method="get" 
   action="currencyconverter.php">
   
<b>Convert this amount:<br /><br /></b>
<input type="text" value="1" name="amount"> <br /><br />
<hr>
<b> From this currency:</b><b>    
    To this currency </b> <br /><br />
   
<select name="currency1" size="1">
<option value="EUR">EUR 
<option value="USD">USD
<option value="GBP">GBP</select>
     
   
<select name="currency2" size="1">
<option value="EUR">EUR 
<option value="USD">USD
<option value="GBP">GBP</select>
   
<hr>
<input type="submit" value="Exchange">
<input type="reset" value="Reset">
</form>
   
</body>
</html> 

 肝心なのは、ユーザーが値を入力し、換算元と換算先の通貨を選択してからの処理です。換算元と換算先を示すドロップダウンリストには、3種類の通貨を示す略語が表示されています。米ドルを表す「USD」、英ポンドを表す「GBP」、ユーロを表す「EUR」です。ユーザーが[Exchange]ボタンをクリックすると、フォームからデータが送信され、CurrencyConverterという名前のPHPクラスインスタンスがサーバーによって作成されます。このクラスには、起こり得る6種類の換算のそれぞれに対応した6種類のメソッドが備わっています。

<?php
   
class CurrencyConverter {
   
   function __construct() {}
   
   function EUR_USD($eur) {
      $usd=1.38745*$eur;
      return $eur.' EUR = '.$usd.' USD ';
   }
   
   function USD_EUR($usd) {
      $eur=0.7274*$usd;
      return $usd.' USD = '.$eur.' EUR ';
   }
   
   function GBP_EUR($gbp) {
      $eur=1.44610*$gbp;
      return $gbp.' GBP = '.$eur.' EUR ';
   }
   
   function EUR_GBP($eur) {
      $gbp=0.69151*$eur;
      return $eur.' EUR = '.$gbp.' GBP ';
   }
   
   function GBP_USD($gbp) {
      $usd=2.00640*$gbp;
      return $gbp.' GBP = '.$usd.' USD ';
   }
   
   function USD_GBP($usd) {
      $gbp=0.49840*$usd;
      return $usd.' USD = '.$gbp.' GBP ';
   }
} 
?>

 換算に使用するメソッドは、ユーザーが選んだ通貨に応じて変わるので、呼び出すべき適切なメソッドを実行時に判断する必要があります。一連の条件分岐ステートメントで判断したり、call_user_funcメソッドを使用したりという手もありますが、PHP5のリフレクションを使用すれば、もっとエレガントに処理を実現できます。呼び出すメソッドを判断するのは簡単です。CurrencyConverterのメソッドの名前は、EUR_USDUSD_EURなどの形式になっています。ユーザーが2つの通貨を選択すると、コンソールから、それらの通貨を表す略語が送信されます。PHPのコードでは、送信された2つの通貨の文字列をアンダースコアで連結すればいいのです。たとえば、ユーザーが選んだ換算元がユーロ(EUR)で、換算先がポンド(GBP)の場合、アプリケーションで呼び出すのはEUR_GBPメソッドです。アプリケーションでそのメソッドを実際に呼び出すときには、リフレクションを使用します。以上の一連の処理のコードは「currencyconverter.php」ページにあり、次のようになっています。

<?php
   
//include the CurrencyConverter class
include("CurrencyConverter.inc");
   
//Get the amount to be converted
$amount=$_GET['amount'];
   
//From this currency...
$currency1=$_GET['currency1'];
   
//To this currency...
$currency2=$_GET['currency2'];
   
//Obtain the correct method name
$name=$_GET['currency1'].'_'.$_GET['currency2'];
   
//get a ReflectionMethod for the $name method
$reflectionMethod=new ReflectionMethod(
   'CurrencyConverter', $name);
   
//create an instance of the CurrencyConverter class
$currencyConverter=new CurrencyConverter();
   
//invoke the correct method
echo $reflectionMethod->invoke($currencyConverter, $amount);
   
?>

例2:リフレクションによるPHPドキュメントの生成

 次は、リフレクションを使用してPHPクラスのドキュメントを生成する例です。この処理には、クラス内部を掘り下げて解析する必要があり、したがってリフレクションAPIのメソッドを多用します。

 まずは、HTMLベースの簡単なコンソールを用意します。ドキュメントの対象となるクラスの名前をクライアントから指定するためのコンソールです。次のような簡単なコードのコンソールで問題ありません。

<html>
<head>
   <b>Provide the class name to be documentated: <br /><br /></b>
</head>
 <body>
   <form method="get" action="generatingDocumentation.php">
     <input type="text" name="class">
     <input type="submit" value="Generate documentation">
     <input type="reset" value="Reset">
   </form>
 </body>
</html>

 ドキュメント生成アプリケーションの中核となるのは、「generatingDocumentation.php」のコードです。長いコードなので、以降では重要な部分だけを抜粋して示します。コード全体については、本稿のダウンロードサンプルに収録されている「generatingDocumentation.php」ファイルを参照してください。まず、ユーザーからコンソールを通じて送信されたクラス名を取得し、ファイルの拡張子を付加して、目的のクラスをインクルードします。その後で、出力ドキュメントの書き込み先となるファイルを開きます。

//get the class name to be documentated    
$className=$_GET['class'];
      
//paste the name class with the extension .inc
$classNameExtension=$_GET['class'].".inc";
$b=$_GET['class'];
      
//include the $classNameExtension class 
include($classNameExtension);
      
//get a ReflectionClass for the $className class
$reflection=new ReflectionClass($className);
      
//prepare the output
$hf=fopen("PTD.html","w");

 次に、クラスの各部分に対して反復処理を行い、必要な情報(クラスがサポートするインターフェイス、親クラス名(ある場合)、定数、プロパティ、メソッドとその全パラメータなど)を取得します。そして、これらの情報すべてを、説明のコメントと共に、ドキュメントファイルに書き込みます。以下、クラスのメソッドとパラメータを取得してドキュメント化する部分のコードの抜粋を示します。

//get information about methods
$methods=$reflection->getMethods();
      
   if($methods != null) 
     {
     fwrite($hf,"¥t<tr><td align=¥"center¥"".
     " colspan=¥"0¥"><font face=¥"arial¥"".
     " size=¥"2¥" color=¥"purple¥">Methods:".
     "</td><td align=¥"center¥" colspan=¥"0¥">".
     "<font face=¥"arial¥" size=¥"2¥"".
     " color=¥"black¥"><b>Name</b></td>".
     "<td align=¥"center¥" colspan=¥"0¥">".
     "<font face=¥"arial¥" size=¥"2¥"".
     " color=¥"black¥"><b>Modifiers</b>".
     "</td><td align=¥"center¥" colspan=¥"0¥">".
     "<font face=¥"arial¥" size=¥"2¥"".
     " color=¥"black¥"><b>Parameters</b>".
     "</td><td align=¥"center¥" colspan=¥"0¥">".
     "<font face=¥"arial¥" size=¥"2¥"".
     " color=¥"black¥"><b>Description</b>".
     "</td></tr>¥n");
     foreach($methods as $in)
      {
      fwrite($hf,"¥t<tr><td></td><td>");
      fwrite($hf,$in->getName());
      
      if($in->isConstructor())
              { fwrite($hf," [c]"); }
           
      fwrite($hf,"</td><td>");  
      if ($in->isPublic())
              { fwrite($hf,"[public]"); } 
      if ($in->isPrivate())
              { fwrite($hf,"[private]"); }
      if ($in->isProtected())
              { fwrite($hf,"[protected]"); } 
      if ($in->isAbstract())
              { fwrite($hf,"[abstract]"); } 
      if ($in->isFinal())
              { fwrite($hf,"[final]"); } 
      if ($in->isStatic())
              { fwrite($hf,"[static]"); } 
      fwrite($hf,"</td>");      
               
      $parameters=$in->getParameters();
      if($parameters != null)
         {                  
         fwrite($hf,"<td align=¥"center¥">");
         $nr_parameters=count($parameters);
         foreach($parameters as $out)
          {
          fwrite($hf,"$");
          fwrite($hf,$out->getName());
          if($out->isPassedByReference())
           { fwrite($hf,"  [&]  "); }
             if($out->allowsNull())
           { fwrite($hf,"  [+]  "); }
           }
          fwrite($hf,"</td>");
          }else { fwrite($hf,"<td></td>"); }
      
      fwrite($hf,"<td align=¥"center¥"><i>");
      fwrite($hf,$in->getDocComment());
      fwrite($hf,"</i>¥n¥t</td></tr>¥n");
      }      
    }

 たとえば、このPHPドキュメントツールでリスト1のテストクラスを指定すると、図3のような情報が出力されます。

図3 PHPドキュメントツールの出力。インターフェイス、スーパークラス、定数、プロパティ、メソッドを示すドキュメントが生成される
図3 PHPドキュメントツールの出力。インターフェイス、スーパークラス、定数、プロパティ、メソッドを示すドキュメントが生成される

 このように、新しいリフレクションAPIはリフレクション機能の便利な実装であり、本稿のPHPドキュメントツールのような比較的複雑なアプリケーションの開発に利用できます。実行すべきクラスやメソッドをデザイン時に判断できない場合には、リフレクションは特に便利です。

著者紹介

Octavia Andreea Anghel(Octavia Andreea Anghel)
経験豊富なPHP開発者。現在は、国内外のソフトウェア開発コンテストに参加するプログラミングチームの主任トレーナーを務める。国レベルの教育プロジェクト開発のコンサルティングも担当している。共著書に『XML technologies?XML in Java』があり、XML部分の執筆を担当。PHPやXMLのほか、ソフトウェアアーキテクチャ、Webサービス、UML、ハイパフォーマンスな単体テストについても関心を寄せている。

過去コラム集
AJAXアプリケーションで標準的なブラウザナビゲーションを再現する
Java RTSによる金融アプリケーションの作成
エンタープライズソフトウェア構成をもっとシンプルに
Presentation Modelパターンによる動的XAMLフォームの作成
DataGridViewのカスタム列タイプの作成
SQL Serverにおける日付/時刻計算のベストプラクティス
Mylyn 2.0による効率的なタスク管理
Ajax4JSFを利用したデータ検証
SQLiteとPHPで軽快なWebアプリケーションを作る
階層化アーキテクチャと依存性注入・依存性逆転
海外のインターネットコム アメリカ韓国ドイツトルコ
関連企業のサイト:ストックフォト イラスト ネットストリート ホテル予約サイト タウン情報 出張 事業継承 シミュレーション トランクルーム 優待映画チケット 田舎暮らしガイド オリジナルTシャツ ニタコエ
Copyright 2008 Jupitermedia Corporation All Rights Reserved. http://www.internet.com/
space.gif space.gif