PHP: 2006年1月アーカイブ

アプリケーションの設定を保存するには、文字列化してファイルなり DB なり、ストレージ コンテナへ保存するのが一般的です。PHP にはこの文字列を作成するのに便利な関数が用意されています。

string serialize (mixed value)
値の保存可能な表現を生成する
mixed unserialize (string str)
保存用表現から PHP の値を生成する

値型、配列型、オブジェクト型などを簡単に文字列に変換・復号できます。これをシリアライズと言います。日本語で言うと永続化、直列化ですね。
PHP のセッション変数 $_SESSION もシリアライズを用いて実現されています。 ただし文字列に出来ない型もあり、fopen や fsockopen 等で使用するリソース型がそうです。リソース型は PHP のプロセスの状態に左右される為です。

以下は FTP 設定を記憶するサンプルです。
Settings クラスは自身のシリアライズ・デシリアライズを行います。
このクラスを継承して FTP の項目を表すメンバを持った FtpSettings クラスを用いて実現します。

Settings.class.php の定義です。


<?php
/**
 * アプリケーションの設定を表します。
 */
class Settings
{
    /**
     * 現在の設定を指定したファイルへ保存します。
     *
     * @param    string    ファイル名
     */
    function save($fileName)
    {
        // ファイルストリームを確立します。
        // 追記読み書きモードで開く理由は以下の二つです。
        // 1. ファイルがない場合に作成する。
        // 2. ロック権が確保される前にファイルサイズを
        //    0 bytes にさせない。
        $fp = fopen($fileName, 'a+');
        
        if ($fp === false)
        {
            trigger_error('ファイルストリームを開けません。', E_USER_ERROR);
        }
        
        if (!flock($fp, LOCK_EX))
        {
            fclose($fp);

            trigger_error('ファイルの書き込み権を取得できません。', E_USER_ERROR);
        }
        
        if (!ftruncate($fp, 0))
        {
            fclose($fp);

            trigger_error('ファイルのサイズを変更できません。', E_USER_ERROR);
        }
        
        // 自身を表す文字列を作成します。
        $serializedString = serialize($this);

        rewind($fp);
        fwrite($fp, $serializedString);
        
        flock($fp, LOCK_UN);
        
        fclose($fp);
    }
    
    /**
     * ファイルから設定を読み込みます。
     *
     * @param    string    ファイル名
     * @return    mixid    Settings オブジェクト。失敗したら false を返します。
     */
    function & load($fileName)
    {
        $fp = fopen($fileName, 'a+');
        
        if ($fp === false)
        {
            trigger_error('ファイルストリームを開けません。', E_USER_ERROR);
        }
        
        if (!flock($fp, LOCK_SH))
        {
            fclose($fp);

            trigger_error('ファイルの読み込み権を取得できません。', E_USER_ERROR);
        }
        
        rewind($fp);
        $serializedString = fread($fp, filesize($fileName) + 1);
        
        flock($fp, LOCK_UN);

        fclose($fp);
        
        // 設定をデシリアライズします。
        // デシリアライズに失敗したら
        // 新しい設定を作成します。
        $settings = unserialize($serializedString);
        
        return $settings;
    }
}

?>

serialize_test.php の定義です。


<?php
require_once('Settings.class.php');

/**
 * FTP 用の設定を表します。
 */
class FtpSettings extends Settings
{
    var $host;
    var $port;
    var $homeDirectory;
    var $userName;
    var $password;
}


// 設定を読み込みます。
define('SETTING_FILENAME', 'settings.ini');

$settings =& FtpSettings::load(SETTING_FILENAME);

if ($settings === false)
{
    $settings =& new FtpSettings();
}


// 上書きする設定があれば保存します。
if (isset($_POST['action']) && $_POST['action'] === 'execute')
{
    if (isset($_POST['host']) && is_string($_POST['host']))
    {
        $settings->host = $_POST['host'];
    }

    if (isset($_POST['port']) && is_string($_POST['port']))
    {
        $settings->port = $_POST['port'];
    }

    if (isset($_POST['userName']) && is_string($_POST['userName']))
    {
        $settings->userName = $_POST['userName'];
    }

    if (isset($_POST['password']) && is_string($_POST['password']))
    {
        $settings->password = $_POST['password'];
    }

    if (isset($_POST['homeDirectory']) && is_string($_POST['homeDirectory']))
    {
        $settings->homeDirectory = $_POST['homeDirectory'];
    }

    $settings->save(SETTING_FILENAME);
}

?>

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html>
    <head>
        <title>シリアライズ・デシリアライズのサンプル</title>

        <style type="text/css">
            kbd { padding: 0 0.4em; font-weight: bold; border: 1px outset gray; }
        </style>
    </head>
    
    <body>
        <fieldset>

            <legend>設定</legend>
            
            <form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
                <input type="hidden" name="action" value="execute" />

                <table>
                    <tr>
                        <td><label for="Host">ホスト <kbd>H</kbd></label></td>

                        <td><input id="Host" type="text" name="host" value="<?php echo htmlspecialchars($settings->host); ?>" accesskey="H" /></td>
                    </tr>

                    <tr>
                        <td><label for="Port">ポート <kbd>P</kbd></label></td>
                        <td><input id="Port" type="text" name="port" value="<?php echo htmlspecialchars($settings->port); ?>" accesskey="P" /></td>

                    </tr>
                    <tr>
                        <td><label for="UserName">ユーザー名 <kbd>U</kbd></label></td>
                        <td><input id="UserName" type="text" name="userName" value="<?php echo htmlspecialchars($settings->userName); ?>" accesskey="U" /></td>

                    </tr>
                    <tr>
                        <td><label for="Password">パスワード <kbd>P</kbd></label></td>
                        <td><input id="Password" type="password" name="password" value="<?php echo htmlspecialchars($settings->password); ?>" accesskey="P" /></td>

                    </tr>
                    <tr>
                        <td><label for="HomeDirectory">ホーム ディレクトリ <kbd>M</kbd></label></td>
                        <td><input id="HomeDirectory" type="text" name="homeDirectory" value="<?php echo htmlspecialchars($settings->homeDirectory); ?>" accesskey="M" /></td>

                    </tr>
                </table>                
                
                <input type="submit" name="submit" value="更新" />
            </form>
        </fieldset>
    </body>

</html>

実行結果のイメージです。
figure1.png

PDO関数 単純な接続

| | コメント(2)

実際に MySQL サーバーに接続しデータを取得してみます。 サンプルではホスト名 localhost に接続し、testdb データベースの testtable テーブルにある全ての行を取得します。

<html><head><title>PDO サンプル - 接続テスト</title></head>
<body>
<style type="text/css">
table { border-collapse: collapse; }
caption { font-weight: bold; }
th, td { padding: 0.2em 1em; border: 1px solid gray; }
th { background-color: whitesmoke; border-bottom-width: 2px; }
td.id { text-align: center; }
col.id { background-color: aliceblue; }
</style>

<?php
/**
 * DSN (Data Source Name) です。
 * PDO は DSN を元に DB へ接続します。
 *
 * 'mysql:localhost; dbname=testdb;' の場合、
 * MySQL 用の PDO ドライバを使用し、
 * ホスト名 "localhost" にある MySQL サーバに接続し、
 * デフォルトで使用するデータベースは "testdb" とします。
 *
 * 書式は使用する PDO ドライバによって異なります。
 * 詳しくは PDO ドライバ毎のマニュアルを参照下さい。
 * http://www.php.net/manual/en/ref.pdo.php#pdo.drivers
 */
$dsn = 'mysql:host=localhost; dbname=testdb;';

/**
 * DB サーバの接続に使用するユーザ名です。
 */
$userName = 'username';

/**
 * DB サーバの接続に使用するパスワードです。
 */
$password = 'password';

try
{
	// 接続を確立します。
	//
	// なんらかの理由により接続出来なかった場合、
	// PDOException 例外が発生します。
	$connection = new PDO($dsn, $userName, $password);
	
	// 接続毎の設定を行います。
	//
	// PDO::ATTR_ERRMODE 定数は「エラーが発生した時の通知方法を設定する」を示し、
	// PDO::ERRMODE_EXCEPTION 定数は 「例外を発生させる」を示します。
	//
	// もし例外ではなく E_WARNING エラーを発生させたい時は
	// $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
	// とします。
	//
	// setAttribute メソッドに渡せる定数はこちらを参照下さい。
	// http://www.php.net/manual/en/function.pdo-setattribute.php
	$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	
	// 接続に対しコマンドを発行します。
	// PDO::query メソッドはステートメント実行後の結果セットを表す
	// PDOStatement オブジェクトを返します。
	//
	// なんらかの理由によりエラーが発生した時、
	// PDO::ATTR_ERRMODE の値にそってエラーが通知されます。
	// デフォルト、もしくは値が PDO::ERRMODE_SILENT の場合エラーは通知されず、
	// 代わりに戻り値が false となります。
	// このスクリプトでは PDOException 例外が発生します。
	//
	// // デフォルト動作時のエラーチェック
	// $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
	// $statement = $connection->query('でたらめSQL');
	// if ($statement === false)
	// {
	//     trigger_error('よくわからないけどダメだった', E_USER_WARNING);
	// }
	$statement = $connection->query('select * from `testtable`');
	
	// 結果セットが表すデータの取得方法を設定します。
	// PDO::FETCH_NAMED 定数は「カラム名を添え字にした配列として受け取る」を
	// 表します。
	$statement->setFetchMode(PDO::FETCH_NAMED);
	
	// 結果を取得します。
	echo '<table summary="testtable の中身">';
	echo '<caption>testtable の中身</caption>';
	echo '<col class="id" /><col class="name" /><col class="tel" />';
	echo '<thead><tr><th>ID</th><th>Name</th><th>Tel</th></tr></thead>';
	echo '<tbody>';

	while (($row = $statement->fetch()) !== false)
	{
		echo '<tr>';
		echo '<td class="id">' . htmlspecialchars($row['Id']) . '</td>';
		echo '<td class="name">' . htmlspecialchars($row['Name']) . '</td>';
		echo '<td class="tel">' . htmlspecialchars($row['Tel']) . '</td>';
		echo '</tr>';
	}
	
	echo '</tbody>';
	echo '</table>';

	// 接続を切断します。
	//
	// PDO クラスには明示的に切断するメソッドが無く、
	// オブジェクトが破棄される時に切断されます。
	// 通常スクリプト終了時にオブジェクトも破棄されますので
	// 明示的に切断する必要はありません。
	//
	// もしスクリプトの途中で切断が必要になった時は、
	// オブジェクトを GC(ガベージ コレクト) によって破棄する必要があります。
	// GC とは、不要になった資源(メモリ・各種接続等)を開放する PHP の機能です。
	// PDO オブジェクトを GC の対象にさせるには、PDO オブジェクトを参照している
	// 変数を削除するか、NULL 等を代入してオブジェクトへの参照をなくします。
	//
	// unset($connection);
	// $connection = null;
	// $connection = "お前など絶縁だ!";
	//
	// これらは全て PDO オブジェクトを破棄させるのに有効です。
}
catch (PDOException $e)
{
	echo "エラー: " . $e->getMessage();
}

?>

</body></html>

実行結果のイメージです。
実行結果のイメージ

続く...

このアーカイブについて

このページには、2006年1月以降に書かれたブログ記事のうちPHPカテゴリに属しているものが含まれています。

前のアーカイブはPHP: 2005年11月です。

次のアーカイブはPHP: 2006年3月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 4.12