この記事では、構成可能な PHP アプリケーションを作成するいくつかの方法を説明します。この記事では、アプリケーションの理想的な構成ポイントについても検討し、アプリケーションが構成可能すぎることと閉じられすぎることの間のバランスを模索します。
PHP アプリケーションを他の人や企業が利用できるようにする場合は、アプリケーションが構成可能であることを確認する必要があります。少なくとも、データベース内の内容が公開されないように、ユーザーが安全な方法でデータベースのログインとパスワードを設定できるようにします。
この記事では、構成設定を保存し、これらの設定を編集するためのいくつかのテクニックを説明します。さらに、この記事では、どの要素を構成可能にする必要があるか、および構成の過剰または過少のジレンマに陥ることを回避する方法についてのガイダンスも提供します。
INI ファイルを使用した設定
PHP には、設定ファイルのサポートが組み込まれています。これは、php.ini ファイルなどの初期化ファイル (INI) メカニズムを通じて実現され、データベース接続のタイムアウトやセッションの保存方法などの定数が定義されます。必要に応じて、この php.ini ファイルでアプリケーションの構成をカスタマイズできます。説明のために、次のコード行を php.ini ファイルに追加しました。
myapptempdir=foo
次に、リスト 1 に示すように、この構成項目を読み取るための小さな PHP スクリプトを作成しました。
リスト 1. ini1.php
<?php
関数 get_template_directory()
{
$v = get_cfg_var( "myapptempdir" );
return ( $v == null ) "tempdir" : $v;
echo
( get_template_directory()."n" );
?>
このコードをコマンド ラインで実行すると、次の結果が得られます:
% php ini1.php
ふー
%
素晴らしい。しかし、標準の INI 関数を使用して myapptempdir 構成項目の値を取得できないのはなぜでしょうか?調べてみたところ、ほとんどの場合、これらの方法ではカスタム構成アイテムを取得できないことがわかりました。ただし、get_cfg_var 関数を使用してアクセスできます。
このアプローチを簡素化するには、以下に示すように、構成キー名とデフォルト値をパラメーターとして受け取る 2 番目の関数で変数へのアクセスをカプセル化します。
リスト 2. ini2.php
関数 get_ini_value( $n, $dv )
{
$c = get_cfg_var( $n );
戻り値 ( $c == null ) $dv : $c;
関数 get_template_directory(
)
{
return get_ini_value( "myapptempdir", "tempdir" );
これは INI ファイルにアクセスする方法の概要です。したがって、別のメカニズムを使用する場合、または INI ファイルを別の場所に保存する場合は、多くの機能を変更するという手間は必要ありません
。
アプリケーションの構成に INI ファイルを使用することはお勧めしません。理由は 2 つあります。まず、これにより INI ファイルの読み取りが容易になりますが、INI ファイルを安全に書き込むことはほぼ不可能になります。したがって、これは読み取り専用の構成項目にのみ適しています。次に、php.ini ファイルはサーバー上のすべてのアプリケーションで共有されるため、アプリケーション固有の構成項目をそのファイルに書き込むべきではないと思います。
INI ファイルについて知っておくべきことは何ですか?最も重要なことは、以下に示すように、構成アイテムを追加するためにインクルード パスをリセットする方法です。
リスト 3. ini3.php
<?php
echo( ini_get("include_path")."n" );
ini_set("パスを含める",
ini_get("include_path").":./mylib" );
echo( ini_get("include_path")."n" );
?>
この例では、ローカルの mylib ディレクトリをインクルード パスに追加したため、require ステートメントにパスを追加せずに、そのディレクトリから PHP ファイルを要求できます。
PHP での設定
INI ファイルに設定エントリを保存する一般的な代替方法は、単純な PHP スクリプトを使用してデータを永続化することです。以下に例を示します。
リスト 4. config.php
<?php
# 一時ディレクトリの場所を指定します
#
$TEMPLATE_DIRECTORY = "tempdir";
?>
この定数を使用したコードは以下の通りです。
リスト 5. php.php
<?php
require_once 'config.php'
関数 get_template_directory();
{
グローバル $TEMPLATE_DIRECTORY;
$TEMPLATE_DIRECTORY を返します。
echo
( get_template_directory()."n" );
?>
コードには最初に設定ファイル (config.php) が含まれており、その後これらの定数を直接使用できます。
このテクノロジーを使用すると多くの利点があります。まず、誰かが config.php ファイルを参照しただけでは、ページは空白です。したがって、config.php を Web アプリケーションのルートと同じファイルに置くことができます。次に、どのエディタでも編集でき、一部のエディタには構文の色付けや構文チェック機能も備わっています。
このテクノロジの欠点は、INI ファイルと同様に読み取り専用テクノロジであることです。このファイルからデータを抽出するのは簡単ですが、PHP ファイル内のデータを調整するのは難しく、場合によっては不可能です。
次の代替案は、本質的に読み取りと書き込みの両方が可能な構成システムを作成する方法を示しています。
前の 2 つのテキスト ファイル
の例は、読み取り専用の構成エントリには適していますが、読み取りと書き込みの両方が可能な構成パラメーターについてはどうなるでしょうか。まず、リスト 6 のテキスト構成ファイルを見てください。
リスト 6. config.txt
# アプリケーションの構成ファイル
タイトル=私のアプリ
TemplateDirectory=tempdir
これは INI ファイルと同じファイル形式ですが、独自のヘルパー ツールを作成しました。これを行うために、以下に示すように独自の Configuration クラスを作成しました。
リスト 7. text1.php
<?php
クラス構成
{
プライベート $configFile = 'config.txt';
プライベート $items = array();
関数 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ];
関数parse()
{
$fh = fopen( $this->configFile, 'r' );
while( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$this->items[ $found[1] ] = $found[2];
}
}
fclose( $fh );
}
$
c = 新しい設定();
echo( $c->TemplateDirectory."n" );
?>
このコードは、最初に Configuration オブジェクトを作成します。次にコンストラクターは config.txt を読み取り、解析されたファイルの内容をローカル変数 $items に設定します。
次にスクリプトは、オブジェクト内で直接定義されていない TemplateDirectory を探します。したがって、$id を「TemplateDirectory」に設定してマジック __get メソッドが呼び出され、そのキーの $items 配列の値が返されます。
この __get メソッドは PHP V5 環境に固有であるため、このスクリプトは PHP V5 で実行する必要があります。実際、この記事のすべてのスクリプトは PHP V5 で実行する必要があります。
このスクリプトをコマンド ラインから実行すると、次の結果が表示されます:
% php text1.php
テンポディレクトリ
%
すべてが予期されており、オブジェクトは config.txt ファイルを読み取り、TemplateDirectory 構成項目の正しい値を取得します。
しかし、構成値を設定するにはどうすればよいでしょうか?このクラスに新しいメソッドといくつかの新しいテスト コードを作成すると、以下に示すように、この機能を取得できます。
リスト 8. text2.php
<?php
クラス構成
{
...
function __get($id) { return $this->items[ $id ] }
function __set($id,$v) { $this->items[ $id ] = $v;
関数 parse() { ... }
}
$c = 新しい構成();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
?>
さて、__get 関数の「いとこ」である __set 関数があります。この関数はメンバー変数の値を取得しません。この関数はメンバー変数を設定するときに呼び出されます。下部のテスト コードは値を設定し、新しい値を出力します。
コマンド ラインからこのコードを実行すると、次のようになります:
% php text2.php
テンポディレクトリ
フーバー
%
とても良い!しかし、変更を修正するにはどうすればファイルに保存できるのでしょうか?これを行うには、ファイルを書き込んで読み取る必要があります。以下に示すようにファイルを書き込むための新しい機能。
リスト 9. text3.php
<?php
クラス構成
{
...
関数 save()
{
$nf = '';
$fh = fopen( $this->configFile, 'r' );
while( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$nf .= $found[1]."=".$this->items[$found[1]]."n";
}
それ以外
{
$nf .= $l;
}
}
fclose( $fh );
copy( $this->configFile, $this->configFile.'.bak' );
$fh = fopen( $this->configFile, 'w' );
fwrite( $fh, $nf );
fclose( $fh );
}
$
c = 新しい構成();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
$c->save();
?>
新しい保存機能は config.txt を巧みに操作します。更新された構成項目でファイルを書き換えるだけ (コメントは削除される) のではなく、ファイルを読み取り、$items 配列の内容を柔軟に書き換えました。こうすることで、ファイル内のコメントが保存されます。
コマンド ラインでスクリプトを実行し、テキスト構成ファイルの内容を出力します。次の出力が表示されます。
リスト 10. 関数の出力
%php text3.php の
保存
テンポディレクトリ
フーバー
% 猫の設定.txt
#私のアプリケーションの設定ファイル
タイトル=私のアプリ
TemplateDirectory=foobar
%
元の config.txt ファイルが新しい値で更新されます。
XML 構成ファイル
テキスト ファイルは読み取りや編集が簡単ですが、XML ファイルほど一般的ではありません。さらに、マークアップや特殊記号のエスケープなどを理解する、XML 用のエディターが多数あります。では、XML バージョンの構成ファイルはどのようなものになるでしょうか?リスト 11 は、XML 形式の構成ファイルを示しています。
リスト 11. config.xml
<?xml version="1.0"?>
<構成>
<タイトル>私のアプリ</タイトル>
<テンプレートディレクトリ>tempdir</テンプレートディレクトリ>
は
、XML を使用して構成設定をロードする Configuration クラスの更新バージョンを示しています。
リスト 12. xml1.php
<?php
クラス構成
{
プライベート $configFile = 'config.xml';
プライベート $items = array();
関数 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ];
関数parse()
{
$doc = 新しい DOMDocument();
$doc->load( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach( $nodes として $node )
$this->items[ $node->nodeName ] = $node->nodeValue;
}
$
c = 新しい構成();
echo( $c->TemplateDirectory."n" );
?>
XML にはもう 1 つの利点があるようです。テキスト バージョンよりもコードがシンプルで簡単です。この XML を保存するには、結果をテキスト形式ではなく XML 形式で保存する、別のバージョンの保存関数が必要です。
リスト 13. xml2.php
...
関数 save()
{
$doc = 新しい DOMDocument();
$doc->formatOutput = true;
$r = $doc->createElement( "config" );
$doc->appendChild( $r );
foreach( $this->items as $k => $v )
{
$kn = $doc->createElement( $k );
$kn->appendChild( $doc->createTextNode( $v ) );
$r->appendChild( $kn );
copy
( $this->configFile, $this->configFile.'.bak' );
$doc->save( $this->configFile );
}
...
このコードは、新しい XML ドキュメント オブジェクト モデル (DOM) を作成し、$items 配列内のすべてのデータをこのモデルに保存します。これが完了したら、save メソッドを使用して XML をファイルに保存します。
データベースを使用する
最後の代替方法は
、データベースを使用して構成要素の値を保存することです。最初のステップは、単純なスキーマを使用して構成データを保存することです。以下に簡単なパターンを示します。
リスト 14. schema.sql
DROP TABLE IF EXISTS 設定。
CREATE TABLE 設定 (
id MEDIUMINT NOT NULL AUTO_INCREMENT、
名前テキスト、
値TEXT、
主キー(id)
);
これには、アプリケーションの要件に基づいていくつかの調整が必要です。たとえば、構成要素をユーザーごとに保存したい場合は、ユーザー ID を追加の列として追加する必要があります。
データの読み取りと書き込みを行うために、図 15 に示す更新された Configuration クラスを作成しました。
リスト 15. db1.php
<?php
require_once( 'DB.php' );
$dsn = 'mysql://root:パスワード@localhost/config';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()) }
クラスの設定
{
プライベート $configFile = 'config.xml';
プライベート $items = array();
関数 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ];
関数 __set($id,$v)
{
グローバル $db;
$this->items[ $id ] = $v;
$sth1 = $db->prepare( 'DELETE FROM settings WHERE name=?' );
$db->execute( $sth1, $id );
if (PEAR::isError($db)) { die($db->getMessage()) }
$sth2 = $db->prepare('INSERT INTO settings ( id, name, value ) VALUES ( 0, ?, ? )' );
$db->execute( $sth2, array( $id, $v ) );
if (PEAR::isError($db)) { die($db->getMessage()) }
関数
parse()
{
グローバル $db;
$doc = 新しい DOMDocument();
$doc->load( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach( $nodes として $node )
$this->items[ $node->nodeName ] = $node->nodeValue;
$res = $db->query( 'SELECT name,value FROM settings' );
if (PEAR::isError($db)) { die($db->getMessage()) }
while( $res->fetchInto( $row ) ) {
$this->items[ $row[0] ] = $row[1];
}
}
$
c = 新しい構成();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = '新しい foo';
echo( $c->TemplateDirectory."n" );
?>
これは実際には、テキストとデータベースのハイブリッド ソリューションです。解析メソッドを詳しく見てください。このクラスは、最初にテキスト ファイルを読み取って初期値を取得し、次にデータベースを読み取ってキーを最新の値に更新します。値を設定した後、キーはデータベースから削除され、更新された値を含む新しいレコードが追加されます。
この記事の複数のバージョンを通じて、Configuration クラスがどのように機能し、すべて同じインターフェイスを維持しながら、テキスト ファイル、XML、データベースからデータを読み取ることができるかを見るのは興味深いことです。開発でも同様の安定性を備えたインターフェイスを使用することをお勧めします。これが正確にどのように機能するかは、オブジェクトのクライアントには不明です。鍵となるのは、オブジェクトとクライアントの間の契約です。
構成とは何か、およびその構成方法 構成
オプションが多すぎることと、構成が不十分であることの間の適切な中間点を見つけるのは困難な場合があります。確かに、データベース構成 (データベース名、データベース ユーザー、パスワードなど) はすべて構成可能である必要があります。さらに、基本的な推奨設定項目もいくつか紹介します。
詳細設定では、各機能に個別の有効/無効オプションが必要です。アプリケーションにとっての重要性に基づいて、これらのオプションを許可または無効にします。たとえば、Web フォーラム アプリケーションでは、遅延機能がデフォルトで有効になっています。ただし、電子メール通知はカスタマイズが必要なため、デフォルトでは無効になっています。
ユーザー インターフェイス (UI) オプションはすべて 1 つの場所に設定する必要があります。インターフェイスの構造 (例: メニューの場所、追加のメニュー項目、インターフェイスの特定の要素にリンクする URL、使用されるロゴなど) はすべて 1 つの場所に設定する必要があります。フォント、色、スタイルのエントリを構成項目として指定しないことを強くお勧めします。これらはカスケード スタイル シート (CSS) を介して設定する必要があり、構成システムは使用する CSS ファイルを指定する必要があります。 CSS は、フォント、スタイル、色などを設定するための効率的かつ柔軟な方法です。優れた CSS ツールは数多く存在するため、アプリケーションでは自分で標準を設定しようとするのではなく、CSS を有効に活用する必要があります。
各機能内で 3 ~ 10 の構成オプションを設定することをお勧めします。これらの構成オプションには意味のある名前を付ける必要があります。構成オプションを UI を通じて設定できる場合、テキスト ファイル、XML ファイル、データベース内のオプション名はインターフェイス要素のタイトルに直接関連している必要があります。さらに、これらのオプションにはすべて明確なデフォルト値が必要です。
一般に、電子メール アドレス、使用する CSS、ファイルから参照されるシステム リソースの場所、グラフィック要素のファイル名などのオプションを構成可能にする必要があります。
グラフィック要素の場合は、スキンと呼ばれる別のプロファイル タイプを作成するとよいでしょう。これには、CSS ファイルの配置、グラフィックの配置などのプロファイルの設定が含まれます。次に、ユーザーが複数のスキン ファイルから選択できるようにします。これにより、アプリケーションの外観と操作性の大規模な変更が簡単になります。これにより、ユーザーは、異なる製品のインストール間でアプリケーションをスキンする機会も得られます。この記事ではこれらのスキン ファイルについては説明しませんが、ここで学ぶ基本により、スキン ファイルのサポートがはるかに簡単になります。
結論
構成可能性はあらゆる PHP アプリケーションの重要な部分であり、最初から設計の中心部分である必要があります。この記事が構成アーキテクチャの実装に役立ち、どのような構成オプションを許可すべきかについてのガイダンスになれば幸いです。