PHP で処理する XML 構成ファイル

XML 構成ファイルを使って難なく PHP アプリケーションとオブジェクトを構成する

XML はアプリケーションの構成ファイルには便利で使いやすい式言語になりますが、この情報を PHP スクリプトに抽出するとなると厄介な問題に突き当たることがあります。そんなときに役立つのが、XJConf for PHP パッケージです。このパッケージが提供する API を使うと、XML でエンコードされた情報を読み取り、その情報を直接使ってスカラー、配列、PHP オブジェクトといった PHP データ構造を構成することができます。この記事ではそんな XJConf for PHP パッケージを紹介し、有用な実際のアプリケーションを例に、複合クラス・ツリーの構成方法や Web ベースの構成インターフェースのビルド方法など、このパッケージの使い方を説明します。

はじめに

一般に、かなり複雑なソフトウェアを開発するときに得策となるのは、十分時間をかけて製品の主要な構成変数を特定し、特定した構成変数を標準的な変数の名前空間から分けて別の領域に配置することです。このプロセスに従えば、アプリケーション構成情報を 1 つにまとめたリポジトリーを作成し、製品がさまざまな環境で動作するように変更する作業を簡単なものにすることができます。また、開発者が製品を稼動させるために必要となる重要な情報について理解を深める上でも役立ちます。

従来、構成変数は 1 つの (または複数の) 構成ファイルに保存され、たいていは XML を使用して表現されます。XML は、文書作成者がカスタム・タグやマークアップを使って柔軟にコンテンツを記述できるフレームワークです。そのため開発者はほとんどの場合、これらの XML 構成ファイルにアクセスし、そこに含まれる構成データを読み取って使用するためのユーザー・インターフェースも作成しなければなりません。

PHP 開発者がこの作業を行う際に役立つのが、XJConf for PHP です。このオープンソースのパッケージは、構成ファイルから XML でエンコードされた情報を抽出し、それをネイティブ・データ構造に変換してアプリケーション内ですぐに使用できるようにするための API を提供します。そんな XJConf for PHP は、あらゆる PHP アプリケーションにとって堅牢で使いやすいウィジェットを提供します。


必要なソフトウェアのインストール

XJConf パッケージは Frank Kleine と Stephan Schmidt によって管理され、PHP コミュニティーには GNU LGPL の下で公開されています。パッケージには PHP 5.0 (以降) が必要で、これをインストールするのに最も簡単な方法は、PHP ビルドにデフォルトで含まれている自動 PEAR インストーラーを使うことです。この場合、以下のコマンドをシェル・プロンプトで実行するだけでインストールすることができます。

shell> pear channel-discover pear.php-tools.net

shell> pear install pat/XJConfForPHP-alpha

上記のコマンドによって、PEAR インストーラーが新しいチャネルに接続し、パッケージをダウンロードしてシステムの適切な場所にインストールしてくれます。この記事で使用するのは、XJConf for PHP V. 0.2.0 です。

パッケージを手動でインストールするには、パッケージのホーム・ページにアクセスしてソース・コード・アーカイブをダウンロードし、ファイルを目的の場所に解凍してください。パッケージのホーム・ページへのリンク、そして開発中ビルドへのリンクは、この記事の「参考文献」に記載してあります。手動インストールのプロセスでは、PEAR のパッケージ編成構造についての知識があることが前提となります。

最後の依存関係として、XJConf for PHP パッケージでは PHP の xmlreader 拡張を有効にする必要があります。この拡張は PHP 5.1.0 以降ではデフォルトで有効になります。それ以前の PHP バージョンをお使いの方は、PHP マニュアル (「参考文献」にリンクを記載) の該当するセクションで詳細情報とこの拡張を有効にする手順を調べてください。

この記事では、読者に PHP と XML についての実用的な知識があること、そして PHP の単純なデータ型と複合型の両方を使い慣れていることを前提とします。また、一般的な OOP 概念と PHP 5 で使用する特定のクラス・モデルについてのある程度の知識、PHP の DOM (Document Object Model) 拡張による XML ツリー生成に関する知識も必要です。


基本的な使用法

XJConf は、XML フォーマットの構成ファイルを読み取り、ファイル内のデータをネイティブ PHP データ型、またはカスタム・オブジェクトに変換する API を PHP アプリケーション開発者に提供します。変換後のデータ型またはオブジェクトは PHP スクリプト内で通常と同じように使用できます。XJConf ベースのプログラムには、以下の 3 つのコンポーネントがあります。

  • XML フォーマットの構成ファイル
  • 構成ファイルのデータを解析して抽出するための PHP スクリプト
  • 構成ファイルの要素をネイティブ PHP 構造にマッピングする定義ファイル

この 3 つのコンポーネント間の相互作用は、実際の例から簡単に理解することができます。例えばリスト 1 のペット・ショップの XML 構成ファイルを見てください。

リスト 1. XML 構成ファイル (cat.xml)
<?xml version='1.0'?>
<cat>
    <name>Martha</name>
    <age>4</age>
    <breed>Siamese</breed>
</cat>

上記の構成値を PHP アプリケーションに読み込む場合、最初のステップとなるのは定義ファイルを作成することです。この定義ファイルで、リスト 1 の各要素をネイティブ PHP データ型にマッピングします。リスト 2 は、このような定義ファイルの一例です。

リスト 2. XJConf 定義ファイル (defs.xml)
<defines>
	<tag name="name" type="string"/>
	<tag name="breed" type="string"/>
	<tag name="age" type="integer"/>
</defines>

先に進む前に、定義ファイルの構造を説明しておきます。これを理解しておかないと、この後記載する例も理解できないからです。まず、外側の <defines> 要素が囲む一連の <tag> 要素は、そのそれぞれが XML 構成ファイルの要素を表します。各 <tag> には少なくとも name 属性と type 属性が必要です。name 属性は構成ファイルでの要素名を指定し、type 要素は対応するPHP データ型を指定します。共通の基本型は stringintegerboolean ですが、後で説明するように、要素を配列やカスタム・クラスにマッピングすることもできます。

後は PHP スクリプトで XJConf インスタンスを初期化し、リスト 2 の定義を使って構成データを取得するだけです。リスト 3 に、そのために必要な PHP コードを記載します。

リスト 3. XML 構成データを読み取る PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$conf = new XJConfFacade();

// attach definitions to parser
$conf->addDefinition('defs.xml');

// parse XML
$conf->parse('cat.xml');

// access XML element values
echo $conf->getConfigValue('name') . " is " . $conf->getConfigValue('breed') 
. " and " . $conf->getConfigValue('age') . " year(s) old.";
?>

リスト 4 は、上記のスクリプトによる出力です。

リスト 4. リスト 3 によって生成された出力
Martha is Siamese and 4 year(s) old.

リスト 3 をよく見れば、スクリプトの実行内容は簡単に理解できるはずです。このリストは必要なクラス定義ファイルを最初に読み取ってから、XJConf ファサード・オブジェクトをロードして初期化します。XJConf ではこのオブジェクトが、リスト 2 の定義ファイルを処理する DefinitionParser メソッド、そしてリスト 1 の XML 形式の構成ファイルを処理する XMLParser メソッドへの共通インターフェースを提供します。

スクリプトは最初に XJConfFacade オブジェクトの addDefinition() メソッドを呼び出し、リスト 2 のさまざまなタグ定義を読み取ってオブジェクトにアタッチします。次に、オブジェクトの parse() メソッドがリスト 1 の構成データ・ファイルを構文解析し、この解析プロセス中に、構成ファイルに含まれる要素とデータが PHP 連想配列にあるようなキーと値のペアに変換されます。

構文解析が完了すると (XML ファイル定義にエラーがある場合、このプロセスはエラー・メッセージを出力して停止します)、XJConfFacade オブジェクトの getConfigValue() メソッドによってキーと値にアクセスできるようになります。このメソッドはキー名を引数として受け入れて対応する値を返します。したがって、リスト 4 は、getConfigValue('name') 呼び出しによって、構成ファイル cat.xml (リスト 1) の <name> 要素の値 (この場合は Martha) が返されているというわけです。

ここで、構成ファイル (リスト 1) の値をリスト 5 のように編集してください。

リスト 5. 改訂版構成ファイル
<?xml version='1.0'?>
<cat>
    <name>Tom</name>
    <age>1</age>
    <breed>Norwegian</breed>
</cat>

上記のように構成ファイルを編集した上でリスト 3 の PHP スクリプトを再実行すると、出力はリスト 6 のように改訂されます。

リスト 6. リスト 4 の改訂版出力
Tom is Norwegian and 1 year(s) old.

以上のように、リスト 3 を 1 行も変更しなくても、改訂版構成データを使うことができます。これで、構成変数を別のファイルに保存して、XJConf のようなツールを使って構成変数にアクセスする場合の利点がおわかりでしょう。つまり、全体での変更は 1 つのファイルを変更するのと同じくらい簡単で、その変更はアプリケーション全体に即時に反映されるということです。


配列を構成する

単純なデータ型は一例に過ぎません。XJConf では関連する構成変数の集合を定義してネイティブ PHP 配列に変換することもできます。XJConf は数値による索引付き配列と連想配列の両方をサポートします。

この機能を説明する例として、リスト 7 では値の集合が含まれる構成ファイルをセットアップしています。

リスト 7. 関連項目の集合が含まれるデータ・ファイル (collection.xml)
<?xml version='1.0'?>
<root>
    <collection>
        <item letter="a">apples</item>
        <item letter="p">pears</item>
        <item letter="o">oranges</item>
    </collection>
</root>

リスト 8 は、上記のファイルに対応する定義ファイルです。

リスト 8. 定義ファイル (defs.xml)
<defines>
    <tag name="collection" type="array" />
    <tag name="item" type="string" keyAttribute="letter" />
</defines>

この定義ファイルの type 属性の値に注目してください。<collection> 要素は配列にマッピングされている一方、ネストされた <item> は単純なストリング値にマッピングされています。また、このリストに新しく導入されている keyAttribute 属性も注目の点です。この属性によって、XJConf が出力連想配列のキーを設定するときに考慮する要素の属性が指定されます。

リスト 9 は、構成データを構文解析して表示する PHP コードです。

リスト 9. XML 構成データを PHP 配列に読み込む PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('collection.xml');

// access XML element values
print_r($xml->getConfigValue('collection'));
?>

上記による出力はリスト 10 のとおりです。

リスト 10. リスト 9 の出力
Array
(
    [a] => apples
    [p] => pears
    [o] => oranges
)

keyAttributeletter に設定されているため、XJConf が自動的にすべての <item> 要素に含まれる letter 属性の値を、出力連想配列の対応するキーに割り当てています。

数値でインデックスを付けた通常の配列がお望みなら、定義ファイルを変更して keyAttribute の値を特殊記号 __none に設定すればいいだけの話です。

リスト 11. 改訂版定義ファイル (defs.xml)
<defines>
    <tag name="collection" type="array" />
    <tag name="item" type="string" keyAttribute="__none" />
</defines>

改訂後の出力はリスト 12 のようになります。

リスト 12. リスト 9 の改訂版出力
Array
(
    [0] => apples
    [1] => pears
    [2] => oranges
)

ここで、この機能の現実的なアプリケーションとして、XML 構成ファイルに存在する変数からデータベース接続を構成してみます。構成ファイルはリスト 13 のような内容だとします。

リスト 13. XML データベース 構成ファイル (conf.xml)
<?xml version='1.0'?>
<conf>
    <database>
        <user>root</user>
        <pass>mysql123</pass>
        <host>localhost</host>
        <db>test</db>
    </database>
</conf>

さらに、このタスクではこれらの構成値を配列に読み込んでから、この配列を使って MySQL データベース・サーバーに接続するという前提です。最初のステップでは、リスト 14 のような定義ファイルを使用して XML を配列にマッピングします。

リスト 14. 定義ファイル (defs.xml)
<defines>
    <tag name="database" type="array">
        <tag name="user" type="string" />
        <tag name="pass" type="string" />
        <tag name="host" type="string" />
        <tag name="db" type="string" />
    </tag>
</defines>

リスト 15 は、このデータを使用して MySQL データベースに接続するための PHP スクリプトです。

リスト 15. XML ファイルからデータベース構成データを読み取って使用するための PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('conf.xml');

// access database configuration array
$db = $xml->getConfigValue('database');

// test MySQL connection
$conn = mysql_connect($db['host'], $db['user'], $db['pass']) 
or die('Connection error!');
mysql_select_db($db['db'], $conn) or die('Database selection error!');
mysql_close($conn);
echo 'Database test successful';
?>

リスト 15 では XJConf によって、$db という名前の配列を作成して構成ファイルconf.xml のデータを取り込んでいます。次に、この配列を使って MySQL データベース・サーバーへの接続を開き、使用するデーベースを選択します。前の例で説明したように、このスクリプトの鍵は定義ファイル、defs.xml にあります。このファイルが、XJConf に構成ファイルの要素をどのように PHP 配列にマッピングするかを指示するからです。


クラスとオブジェクトを構成する

しかしながら、XJConf でもっとも強力な機能の 1 つは PHP のクラスとオブジェクトのサポートにあります。XJConf で XML 構成ファイルからカスタム・オブジェクトをインスタンス化するのは非常に簡単で、さらに構成ファイルに存在するデータを使ってこのカスタム・オブジェクトのプロパティーを構成することさえわけありません。

説明のために、まず単純な RectangularObject クラスを定義します。このクラスが構成 API に公開する重要なプロパティーは、長さ、幅、高さの 3 つです。また、この 3 つのプロパティーを使用してオブジェクトのボリュームを計算する getVolume() メソッドも公開します。このコードはリスト 16 のとおりです。

リスト 16. PHP クラス定義 (RectangularObject.php)
<?php
class RectangularObject {

    // declare properties
    private $length;
    private $height;
    private $width;
    
    // declare constructor
    function __construct() {
        return true;    
    }
    
    // declare setter methods
    function setLength($l) {
        $this->length = $l;
    }
    
    function setHeight($h) {
        $this->height = $h;
    }
    
    function setWidth($w) {
        $this->width = $w;
    }
    
    // declare other methods
    function getVolume() {
        return ($this->length * $this->width * $this->height);
    }
}
?>

リスト 17 に、この RectangularObject クラスに対応する XML 形式の構成データ例を記載します。

リスト 17. XML 構成ファイル (data.xml)
<?xml version='1.0'?>
<conf>
    <rectangularobject>
        <height>20</height>
        <width>15</width>
        <length>10</length>
    </rectangularobject>
</conf>

データをオブジェクト・インスタンスにマッピングするのは至って簡単で、type 属性をインスタンス化するクラスの名前に設定すればよいだけです。リスト 18 に、これに対応する定義を記載します。

リスト 18. XJConf 定義ファイル (defs.xml)
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <tag name="length" type="integer"/>
        <tag name="width" type="integer"/>
        <tag name="height" type="integer"/>    
    </tag>
</defines>

セッター・メソッドに正しく名前が付けられていれば、XJConf は次の 2 つの操作を行うはずです。

  1. 該当するクラスのオブジェクトをインスタンス化する。
  2. ネストされたそれぞれの要素に対してセッター・メソッドを呼び出し、オブジェクトのプロパティーを自動的に構成する。

したがって、<length> 要素に対しては setLength() メソッドが自動的に呼び出され、<height> 要素に対しては setHeight() メソッドが自動的に呼び出されるという具合になります。

リスト 19リスト 20 にそれぞれ、PHP コード、その出力を記載します。

リスト 19. XML データをインスタンス化してオブジェクトを構成するための PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load class definition
include_once 'RectangularObject.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('data.xml');

// access object instance
$instance = $xml->getConfigValue('rectangularobject');
print 'The volume of the object is: ' . $instance->getVolume() . ' units';
?>

リスト 20 は、上記による出力です。

リスト 20. リスト 19 の出力
The volume of the object is: 3000 units

デフォルト値と必須値を構成する

ネストしたタグの代わりに属性を使って XML 構成ファイルを作成し、これらの属性によってオブジェクト・インスタンスを構成することもできます。実際にどのように動作するのかを確認するには、まず、ネストされた要素ではなく属性を使用するようにリスト 17 を改訂してください。

リスト 21. 改訂版 XML 構成ファイル (data.xml)
<?xml version='1.0'?>
<conf>
    <rectangularobject height="20" width="15" length="10" />
</conf>

もちろん、定義ファイルにも変更が必要です。

リスト 22. 改訂版 XJConf 定義ファイル (defs.xml)
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <attribute name="length" type="integer"/>
        <attribute name="width" type="integer"/>
        <attribute name="height" type="integer"/>    
    </tag>
</defines>

上記の変更では、<tag> 要素を <attribute> 要素に置き換え、XJConf にオブジェクト・プロパティーを設定するときにその子要素の CDATA コンテンツではなく、名前付きタグの属性を考慮するように指示しています。

定義ファイルに含まれる default 属性と required 属性を上手く使えば、特定の構成変数にデフォルト値を設定したり、あるいは一部の構成変数に必須のマークを付けることも可能です。例えばリスト 23 では、length および width 構成変数を必須とし、height にはデフォルト値を設定しています。

リスト 23. 「default」と「require」の制約を設定した XJConf 定義ファイル
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <attribute name="length" type="integer" required="true" />
        <attribute name="width" type="integer" required="true" />
        <attribute name="height" type="integer" default="10" />    
    </tag>
</defines>

これで必須値が指定されていない構成ファイルを読み取ろうとすると、XJConf は MissingAttributeException をスローすることになります (図 1)。

図 1. required 属性がない場合にスローされる XJConf 例外
required 属性がない場合にスローされる XJConf 例外

今までの例では、XJConf が自動的に適切なセッター・メソッドを呼び出してオブジェクト・インスタンスを構成することができましたが、メソッドを簡単に識別して要素/属性名にマッピングできるような正しい名前がセッター・メソッドに付けられていなければ、自動呼び出しは行われません。この条件が気に入らない場合は、定義ファイルの setter 属性を使って要素/属性ごとに明示的にセッター・メソッドを定義することもできます。 リスト 24 では、このようなカスタムのセッター・メソッドを length 属性と width 属性に使用しています。

リスト 24. カスタムのセッター・メソッドを使用した XJConf 定義ファイル
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <attribute name="length" setter="setL" />
        <attribute name="width" setter="setW" />
        <attribute name="height" />    
    </tag>
</defines>

ご存知かもしれませんが、PHP 5 には新規機能として特殊な __set() および __get() オーバーロード・メソッドが導入されています。これらのメソッドを使用すれば、オブジェクト・プロパティーにセッター・メソッドまたはゲッター・メソッドが明示的に定義されていなくても、そのプロパティーを設定することができます。これらのオーバーロード・メソッドの実際の動作を示しているのが、リスト 16 のクラス定義を改訂したリスト 25 です。

リスト 25. __set() および __get() を使用した PHP クラス定義
<?php
class RectangularObject {

    // declare properties
    public $length;
    public $height;
    public $width;
    
    // declare constructor
    function __construct() {
        return true;    
    }
    
    // declare generic setter method
    public function __set($property, $value) {
        $this->$property = $value;
    }
    
    // declare generic getter method
    public function __get($property) {
        return $this->property;
    }
    
    // declare other methods
    function getVolume() {
        return ($this->length * $this->width * $this->height);
    }
}
?>

リスト 17リスト 18 のデータ・ファイルと定義ファイルを使用して、リスト 26 の PHP スクリプトを実行してみます。

リスト 26. XJConf 構成ファイルを使用してオブジェクトを構成するための PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load class definition
include_once 'RectangularObject.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('data.xml');

// access object instance
$instance = $xml->getConfigValue('rectangularobject');
print 'The volume of the object is: ' . $instance->getVolume() . ' units';
?>

出力は以下のようになるはずです。

リスト 27. リスト 26 の出力
The volume of the object is: 3000 units

お気付きのように、クラス定義に明示的にセッター・メソッドが定義されていなくても、XJConf は __set() メソッドによってオブジェクトのプロパティーを構成することができます。このメソッドによっても、クラス定義を大幅に短くすることができ、アプリケーション・コードを管理しやすく、そして読みやすくすることができます。

上記の特殊なオーバーロード・メソッドは V. 0.2.0 より前の XJConf for PHP バージョンではサポートされていないため、これらのメソッドを使用しようとすると PHP スクリプトで致命的なエラーが発生します。

最後に付け加える点として、これまでのリストではすべて、XML 定義とデータを構文解析する前にクラス定義ファイルを明示的にロードしていましたが、XJConf は必要に応じて、この作業を独自のクラス・ローダーを使って自動的に行うことができます。XJConf for PHP パッケージにはこの機能を説明する例が用意されているので (「参考文献」を参照)、アプリケーションで多数のクラスを操作する場合には是非調べてみてください。


プロジェクト: 複合クラス・ツリーを構成する

これまでのリストで説明したように、XJConf では属性または子要素のどちらを使用しても、インスタンス化したオブジェクトを正しく構成することができます。したがって、この 2 つのどちらが使いやすいかによって、どのような構成方法を採るかが決まってきますが、1 つだけ例外があります。その例外とは、複合ツリー内で 1 つ以上のオブジェクトを別のオブジェクトにリンクする場合です。この場合は、属性でなく子要素を使用したほうが便利です。

これを説明する例として、リスト 28 ではいくつかのクラスを定義しています。

リスト 28. 複数の PHP クラス定義 (App.php)
<?php
// configuration class
class Config {
    // declare properties
    private $host;
    private $port;
    private $user;
    private $pass;
    
    // declare methods
    function setConfig($confArray) {
        if (isset($confArray['host'])) {
            $this->host = $confArray['host'];
        }    
        if (isset($confArray['port'])) {
            $this->port = $confArray['port'];
        }    
        if (isset($confArray['user'])) {
            $this->user = $confArray['user'];
        }    
        if (isset($confArray['pass'])) {
            $this->pass = $confArray['pass'];
        }    
    }    
}

// these inherit from the Config class
class DBConfig extends Config {
}

class SMTPConfig extends Config {
}

// application class
class App {
    // declare properties
    public $DBConfig;
    public $SMTPConfig;
    
    // declare constructor
    public function __construct() {
        return true;    
    }
    
    // declare setter methods
    function setDBConfig($db) {
        $this->DBConfig = $db;    
    }    
    function setSMTPConfig($smtp) {
        $this->SMTPConfig = $smtp;    
    }    
    
    // declare getter methods
    function getDBConfig() {
        return $this->DBConfig;    
    }    
    function getSMTPConfig() {
        return $this->SMTPConfig;    
    }    
}
?>

リスト 28 の先頭にある Config クラスは、サーバーのアクセス・パラメーターの連想配列を受け取るための汎用 API を公開します。DBConfig および SMTPConfig クラスは Config クラスのサブクラスで、それぞれデータベース・サーバーのアクセス・パラメーター、SMTP サーバーのアクセス・パラメーターの保存専用です。DBConfig および SMTPConfig オブジェクトは、アプリケーションを表す App クラスの中で使われています。この App クラスは、必要に応じて対応する Config クラスを設定、取得するためのセッター・メソッドとゲッター・メソッドを公開します。

以上のクラスの構成は、XML 構成ファイルで行われます。リスト 29 はその一例です。

リスト 29. XML 構成ファイル (conf.xml)
<?xml version='1.0'?>
<configuration>
  <app>
    <db>
      <array>
        <host>localhost</host>
        <user>guest</user>
        <pass>mysql123</pass>
      </array>
    </db>
    
    <smtp>
      <array>
        <host>mail.domain.com</host>
        <user>sluggo</user>
        <pass>mypass</pass>
        <port>25</port>
      </array>
    </smtp>
  </app>
</configuration>

リスト 29 の XML データを完全に構成された App オブジェクト・インスタンスに変換する役目を果たすのは、定義ファイル (リスト 30) とそれに対応する PHP スクリプト (リスト 31) です。まずは定義ファイルを見てください。

リスト 30. XJConf 定義ファイル (defs.xml)
<defines>
  <tag name="app" type="App">  
    <tag name="db" type="DBConfig" setter="setDBConfig">
        <tag name="array" type="array" keyAttribute="name" setter="setConfig">
            <tag name="host" type="string" />
            <tag name="user" type="string" />
            <tag name="pass" type="string" />
        </tag>
    </tag>
    <tag name="smtp" type="SMTPConfig" setter="setSMTPConfig">
        <tag name="array" type="array" keyAttribute="name" setter="setConfig">
            <tag name="host" type="string" />
            <tag name="port" type="integer" />
            <tag name="user" type="string" />
            <tag name="pass" type="string" />
        </tag>
    </tag>
  </tag>
</defines>

このファイルがどのような動作をするかと言えば、見かけほど難しくはありません。

  1. XJConf が構成ファイル内で <app> 要素を検出すると、App オブジェクトをインスタンス化します。リスト 28 のクラス定義から、この App オブジェクトが setDBConfig() メソッドと setSMTPConfig() メソッドを公開することがわかります。
  2. XJConf が続いて <db> 要素または <smtp> 要素を検出すると、DBConfig あるいは SMTPConfig オブジェクトをインスタンス化し、そのオブジェクトの setConfig() セッター・メソッドを呼び出します。セッター・メソッドに渡されるのは、構成パラメーターの配列です。この配列は、<array> 要素とその子要素をそのまま使って作成されたものです。
  3. ステップ 2 で作成された DBConfig または SMTPConfig オブジェクトは、setDBConfig() および setSMTPConfig() セッター・メソッドによって App オブジェクトにアタッチされます。定義ファイルを見るとわかるように、これらのメソッドにも setter 属性によって名前が付けられています。
  4. 最終的な App オブジェクト・インスタンスを取得するには、PHP スクリプト内で getConfigValue() を呼び出します。同じように、インスタンス化および構成された DBConfig オブジェクトと SMTPConfig オブジェクトを取得するには、App::getDBConfig() または App::getSMTPConfig() を呼び出します。リスト 31 を参照してください。
リスト 31. 複合クラス・ツリーを構成するための PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// include class file
include_once 'App.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('conf.xml');

// access objects
$app = $xml->getConfigValue('app');
$db = $app->getDBConfig();
$smtp = $app->getSMTPConfig();

// display App object
print_r($app);
?>

上記による出力は、リスト 32 のとおりです。

リスト 32. リスト 31 の出力
App Object
(
    [DBConfig] => DBConfig Object
        (
            [host:private] => localhost
            [port:private] => 
            [user:private] => guest
            [pass:private] => mysql123
        )

    [SMTPConfig] => SMTPConfig Object
        (
            [host:private] => mail.domain.com
            [port:private] => 25
            [user:private] => sluggo
            [pass:private] => mypass
        )

)

プロジェクト: Web ベースの構成インターフェースを作成する

実際の多くのプロジェクトに共通して必要なものとしては、Web ベースの構成ツールも挙げられます。この構成ツールはユーザーが Web フォームに構成データを入力できるようにして、入力されたデータを XML フォーマットのファイルに保存するものですが、このコンテキストにおける XJConf の有用性は限られています。パッケージには XML 形式の構成データを PHP データ構造に読み込むための完全な機能を備えた API が用意されていますが、ファイルにデータを書き込むためのメソッドは提供されていません。そのため、このような状況では XJConf を PHP の DOM 拡張で補完し、XML ツリーを動的に構成してファイルに書き込むための API を用意する必要があります。

リスト 33 に、この 2 つのツールを併用するスクリプトを記載します。

リスト 33. 構成データを編集してファイルに保存するための PHP スクリプト
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

$dataFile = 'config.xml';
$defsFile = 'defs.xml';
    
// if form is not submitted
if (!isset($_POST['submit'])) {
    
    // check to see if config file exists
    if (file_exists($dataFile)) {

        // load facade
        XJConfLoader::load('XJConfFacade');
        $xml = new XJConfFacade();

        // attach definitions to parser
        $xml->addDefinition($defsFile);

        // parse XML
        $xml->parse($dataFile);
    } 
?>
<html>
 <head><basefont face="Arial"></head>
 <body>
  <h2>Configuration</h2>
  <table border="0" cellspacing="5" cellpadding="5">
   <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
    <tr>
     <td>POP3 host name</td>
     <td><input type="text" name="pop3_host" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('host') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 port</td>
     <td><input type="text" name="pop3_port" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('port') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 user name</td>
     <td><input type="text" name="pop3_user" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('user') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 user password</td>
     <td><input type="text" name="pop3_pass" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('pass') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td colspan="2" align="center"><input type="submit" name="submit" 
     value="Save Configuration"></td>
    </tr>
   </form>
  </table>           
 </body>
</html>
<?php
} else {
    // do some input validation here
    
    // write the submitted values to a configuration file
    // generate DOM tree and root node
    $dom = new DOMDocument('1.0', 'iso-8859-1');
    $conf = $dom->createElement('conf');
    $dom->appendChild($conf);    
    
    // attach child nodes
    $host = new DOMElement('host', $_POST['pop3_host']);
    $port = new DOMElement('port', $_POST['pop3_port']);
    $user = new DOMElement('user', $_POST['pop3_user']);
    $pass = new DOMElement('pass', $_POST['pop3_pass']);
    $conf->appendChild($host);
    $conf->appendChild($port);
    $conf->appendChild($user);
    $conf->appendChild($pass);
    
    // save to configuration file
    $dom->formatOutput = true;
    if ($dom->save($dataFile)) {
        echo 'Configuration successfully saved!';
    }
}
?>

リスト 33 は 2 つの部分に分けられます。1 つは現行の構成 (利用可能な場合) を表示して、ユーザーが構成を編集できるようにするためのフォーム、そしてもう 1 つは新規構成を受け入れてファイルに保存するフォーム・プロセッサーです。スクリプトの前半では XJConf を使用して、XML 構成ファイルを構文解析し、そのコンテンツを PHP の変数に取り込んでいます。これらの PHP の変数が、Web フォームのフィールドを事前入力するために使用されます。

リスト 34 は、リスト 33 で使用する単純な定義ファイルです。

リスト 34. XJConf 定義ファイル (defs.xml)
<defines>
  <tag name="host" type="string"/>  
  <tag name="port" type="integer"/>  
  <tag name="user" type="string"/>  
  <tag name="pass" type="string"/>  
</defines>

フォームがサブミットされると、代わって PHP の DOM 拡張が引き継ぎます。DOM 拡張によって DOMDocument インスタンスが作成され、子要素がこのインスタンスにアタッチされてユーザーが入力した構成値が保持されます。DOM ツリー全体の構成が完了すると、ツリーは DOMDocument::save() メソッドによって構成ファイルに書き込まれます。

このフォームは図 2 のように表示されます。

図 2. 構成データを編集、保存するための Web フォーム
構成データを編集、保存するための Web フォーム

リスト 35 は、このスクリプトを使って作成された構成ファイルの一例です。

リスト 35. リスト 34 によって作成されたファイルの例
<?xml version="1.0" encoding="iso-8859-1"?>
<conf>
  <host>localhost</host>
  <port>110</port>
  <user>joelle</user>
  <pass>guessme</pass>
</conf>

以上のリストでわかるように、XJConf パッケージは XML 形式の構成ファイルを読み取って、そこに含まれる値を PHP のデータ構成に変換する上で使いやすく柔軟な API を提供します。単純なストリング値と数値の他、配列やオブジェクトの使用もサポートし、さらにセッター・メソッドによって新しくインスタンス化されたオブジェクトを自動的に構成する優れた機能も組み込まれています。このようなすべての理由から、XJConf パッケージは PHP 開発者のツールキットに追加する値打ちがあります。今度 PHP アプリケーションと XML 構成ファイルとのインターフェースが必要になったら、是非このパッケージを使ってみてください。その価値が実感できるはずです。

参考文献

学ぶために

  • XJConf for PHP Web サイト: XJConf パッケージの詳細を調べてください。
  • The Stubbles プロジェクト: 実際に使われている XJConf for PHP を確かめてみてください。この PHP 5 フレームワークでは、他のプログラミング言語とフレームワークのお気に入りの機能を組み合わせられます。必要なパッケージだけを使って、PEAR やZend Framework、またはその他の PHP ベースのフレームワークと組み合わせてください。
  • XJConf Timeline: XJConf for PHP 開発の経緯と最新情報を調べられます。
  • XMLReader 関数: PHP の xmlreader 拡張を有効にする手順が記載されています。
  • PHP および XML 開発に関係するその他の PEAR パッケージ: PHP および XML 開発に関係するその他の PEAR パッケージを調べてください。
  • developerWorks XML ゾーン: XML 分野でのスキルを磨くための記事、チュートリアルをはじめ、豊富な資料を入手してください。
  • テクノロジーのブックストア: この技術やその他の技術に関する本を参照してください。
  • IBM XML 認証: XML や関連技術の IBM 認定開発者になる方法について調べてください。
  • XML Technical library: 広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM レッドブックについては、developerWorks XML ゾーンを参照してください。
  • developerWorks technical events and webcasts: これらのセッションで最新情報を入手してください。

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

議論するために

コメント

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=XML, Open source
ArticleID=280845
ArticleTitle=PHP で処理する XML 構成ファイル
publish-date=11062007