ADODBのsqliteドライバ

2008/04/08 | ADODB

現在簡単なウェブアプリを書く際は、フレームワークとしてguesswork、データベース接続にADODB、テンプレートエンジンはSmarty(もしくはPHP直接記述)という組み合わせを使い、データベースにMySQL(最近はSQLiteも)を使用することがほとんどだ。この組み合わせの中でADODBとSQLiteを使用した際に発生した問題についてメモ。
ADODBでは他のデータベースライブラリと同様に、使用するデータベースを定義することによって必要な処理が分岐されるようになっている。例えばPostgrSQLを使用する場合はpostgres、MySQLを使用する場合はmysqlといった具合だ(文字列の指定方法はデータベースライブラリによって違うので注意が必要)。ADODBでSQLiteを使う場合はドライバに2種類あり、それらは「sqlite」と「sqlitepo」だ。最初はその違いを把握せず(マニュアルなど読んでいない)、「sqlite」を使用していたが、あるとき不具合が出てしまった。

MySQLの場合は例えばselect文を投げてデータを取得した場合は以下のような配列で取得できる。

$row[‘id’]
$row[‘name’]

もちろんSQLiteでも基本的には上記のように返してくれる。しかし例えば複数のテーブルを結合したりしたselect文を発行したりすると、MySQLの場合は上記と同様に返してくれるのだが、SQLiteの場合はテーブル名をくっつけて返してしまうのだ。

$row[‘user.id’]
$row[‘user.name’]

こんな具合だ。「え〜これでは互換性が保てない・・・」と思ってよくよくマニュアルを読んでみた。そしてドライバの違いに気づいた。マニュアルによると、


移植性のあるSQLiteドライバ。sqliteでは他のドライバのように連想モードが機能しないため。つまり、複数テーブルを選択(結合)すると、テーブル名が”sqlite”ドライバの連想キーに含まれます。
“sqlitepo”ドライバでは、テーブル名は返されたカラム名から取り除かれます。この結果が衝突したとき、最初のフィールドが優先されます。

そういうことだったのね。互換性(移植性)を重視するなら「sqlitepo」を使用しなさい、と。しかしこれではどんなデータベースでも汎用的に利用できるライブラリという意味合いがなくなってしまっているような感が無きにしも非ず・・・。気を取り直して「sqlitepo」を使用することにした。

SQLite2のテーブル修正

2008/04/07 | SQLite

「便利に使えそう」と期待しているSQLiteだが、もろもろウェブを検索すると「断然SQLite3にすべき」という記述が多い。当然多機能だろうかバージョンが上のほうがいいに決まっているのだが・・・。
SQLite2の場合、本来は一度作ったテーブルの修正はできないらしい。つまりalterコマンドがないのだ(SQLite3から実装されたらしい)。これは確かに不便だ。
SQLiteManagerではいちおうSQLite2でもテーブルでカラムの追加や削除、修正ができる。これは以下の手順を踏んでいるらしいことがわかった。
(1) 元テーブルと同じ構造のテンポラリテーブルを作成
(2) テンポラリテーブルに元テーブルのデータを挿入
(3) 元テーブルを削除
(4) 希望のテーブルを新規で作成
(5) テンポラリテーブルに入れたデータを新規作成テーブルに挿入
(6) テンポラリテーブルを削除

上記の操作をトランザクションに囲んで処理させているようだ(本当はどうだかソースを読んでいないのでわからないけれど)。トランザクションをサポートしているのにテーブルの修正ができないとは・・・。まぁトランザクションのほうがあったほうがいいけれど、それでもテーブルの修正が容易でないというのは開発段階では致命的。

でもPHP5ではデフォルトではSQLite2しかサポートしていない。しょうがなしにSQLite2を使っていくということになるのだが・・・。早めに改善を希望する部分だな。

PHP4でSQLiteManagerを使う際に

2008/04/06 | SQLite

SQLiteManagerを活用してがんばってSQLiteを使っている。PHP4でSQLiteが使える環境があったのでそちらにも設置してみた。基本的には動作しているようだが、なぜかUTF-8化を実施すると以下のエラーが出る。

Warning: cannot yet handle MBCS in html_entity_decode()

htmlentity関連のエラーだ。マルチバイト文字はhtmlentity関数との相性がよくないような気がする。さて、これを解消する方法は以下の通り(include/common.lib.phpを修正)。

(1) 上記ファイルのどこか適当なところに以下の関数を記述する。
function unhtmlentities($string)
{
  $string = preg_replace(‘~&#x([0-9a-f]+);~ei’, ‘chr(hexdec(“¥¥1″))’, $string);
  $string = preg_replace(‘~&#([0-9]+);~e’, ‘chr(¥¥1)’, $string);
  $trans_tbl = get_html_translation_table(HTML_ENTITIES);
  $trans_tbl = array_flip($trans_tbl);
  return strtr($string, $trans_tbl);
}

(2) 元ファイル中で133行目、180行目、184行目ででてくるhtml_entity_decode関数を上記のunhtmlentities関数で書き換える。その際2つ目以降の引数はばっさり切り取る。

以上でOK。正しく表示された。多分標準の関数を自前処理の関数で置き換えた、ということだろう。

guessworkでパラメータを自由に設定

2008/04/03 | guesswork

cakePHPも試しつつ、実は使い続けているguesswork。この軽量なフレームワークはPHP5でも動作し、いまだに手放すことができずにいる(多分使い続けるだろう)。

さて。guessworkでは外部からgetやpostで引き渡された値をcontrollerクラス内の同名のクラス変数に割り当てられるようになっている。しかし外部からの変数の汚染を防ぐために、デフォルトのクラス変数にしか割り当てられない(つまりデフォルトで想定していない引数を外部から割り当てようとしてもできない)。

これは有効なようだが、意外と使えないかもしれない。特にフォームの入力項目を可変にしたいような場合には、デフォルトでクラス変数を定義することができないので「実現不可能」ということになる(回避策はいろいろあるのだが)。

しかし本来guessworkでは、変更されたくないクラス変数は_(アンダースコア)で始めるというルールがあるわけだから、上記のような機能が不要なようだろうと思っている。もちろん想定外の変数を割り当てられてしまうというのは致命的なバグになる可能性もあるわけだから、安全をとるなら現状の記述がよいとは思う。

しかし不便だから書き換える。該当箇所はGuesswork/Controller.phpの200行目付近のinitializeController関数内の記述だ(デフォルトの状態は下記にようになっている)。

function initializeController()
{
  // call initialize method
  $method_name = “init”;
  if (method_exists($this, $method_name)) {
    call_user_func(array(&$this, $method_name));
  }

  // set acceptable form parameters to instance variable
  $vars = get_class_vars(get_class($this));
  $acceptable = array();
  foreach ($vars as $key => $value) {
    if (substr($key, 0, 1) == ‘_’) {
      continue;
    }
    $acceptable[] = $key;
  }

  // call form parameter callback if defined
  if (is_string($this->_gw_form_parameter_callback)) {
    if (method_exists($this, $this->_gw_form_parameter_callback)) {
      $this->_gw_params = call_user_func(array(&$this, $this->_gw_form_parameter_callback), $this->_gw_params);
    }
  }

  foreach ($this->_gw_params as $key => $value) {
    if (in_array($key, $acceptable)) {
      $this->$key = $value;
    }
  }

  return true;
}

この関数は3つのステージで構成されているようだ
○「// call initialize method」
 init(初期化)関数があれば実行
○「// set acceptable form parameters to instance variable」
 デフォルトの受け入れ可能なクラス変数を取得
○「// call form parameter callback if defined」
 1.外部から渡された変数群にフィルター関数をかませて
 2.受け入れ可能な変数であれば、クラス変数に代入する

「あ゛」ここでフィルターがかけられるのか。わざわざ専用関数を書いてinit関数から呼び出していたよ(後日書き直すことにする)。で最後の「受け入れ可能な変数で・・・」なところさえ書き直せば、デフォルトで変数を定義していなくても使用できるようになりそうだ。

ということで上記のプログラムの最下部を以下のように書き換えた。

  foreach ($this->_gw_params as $key => $value) {
    if (in_array($key, $vars) && substr($key,0,1)==’_’) {
      continue;
    }
    $this->$key = $value;
  }

  return true;
}

これで「外部からの変数は何でも来い」の状態になってしまっている。当面は注意するという前提で、必要に応じて関数を上書きして使用することにする。

guesswork。新しいバージョンは出ないのだろうか。だったら派生させてオリジナルクラスを作るべきだろうな。超便利だから。

関数の呼び出し方(変数と括弧)

2008/04/02 | PHPの基本

以前、文字列操作関数substrの特別な場合として以下のような記述方法について記事を書いた。

$variable=’abcdefg';
print($valiable{3});
// 結果は「d」が表示される

変数名に{}をつけることで関数の代わりになるというお話しだった。
今回あるプログラムを読んでいて、こんな記述を見つけた。
$variable();

変数名に()って・・・今度は何?、と思って調べたらこれは以下のようなことだった。

$variable=’abcdefg';
$valiable();
// こちらはabcdefg()という関数が実行される

もちろんabcdefg()という関数が定義されていないとエラーになる。call_user_func関数の代わりに使うのだろうか。いずれにしてもプログラムの可読性が悪くなるような気がしないではない。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net