[SQLite][PHP] PHPのPEAR::DBはSQLite3に未対応
問題点
PHPからSQLite3を使おうとして、
<?php $dsn = 'sqlite:///パス'; $db =& DB::connect($dsn); ?>
としたのですが、Extention not Foundとなり、接続できません。
対処方法
PDOを使用して下さい。
原因の特定方法
このエラーの原因を探るため、/usr/share/php/DB.phpのconnectメソッドの中身を見てみました。
function &connect($dsn, $options = array()) { $dsninfo = DB::parseDSN($dsn); $type = $dsninfo['phptype']; if (!is_array($options)) { /* * For backwards compatibility. $options used to be boolean, * indicating whether the connection should be persistent. */ $options = array('persistent' => $options); } if (isset($options['debug']) && $options['debug'] >= 2) { // expose php errors with sufficient debug level include_once "DB/${type}.php"; } else { @include_once "DB/${type}.php"; } $classname = "DB_${type}"; if (!class_exists($classname)) { $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, "Unable to include the DB/{$type}.php" . " file for '$dsn'", 'DB_Error', true); return $tmp; } @$obj =& new $classname; foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); if (DB::isError($test)) { return $test; } } $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
connectメソッドの中では、まず
$dsninfo = DB::parseDSN($dsn);
があり、おそらく接続情報の文字列をパースし、その結果を$dsninfoに格納しているのだろうと推測されます。そして、$dsninfoは、
$type = $dsninfo['phptype'];
と使われているので、連想配列になっているはずです。この$dsninfo['phptype']というのは、connectメソッドに渡した文字列の先頭にあるデータベースの種別ではないかとあたりをつけることができます。すなわち、上の例の場合だと、$typeは"sqlite"になります。で、その後で、
@include_once "DB/${type}.php";
がくるので、/usr/share/php/DB/sqlite.phpをインクルードします。確認しましたが、このファイルは存在しました。次に、
$classname = "DB_${type}"; (中略) @$obj =& new $classname;
があります。$typeは先ほどのように"sqlite"だと思われるので、$classnameは"DB_sqlite"となり、$objはDB_sqliteのインスタンス、ということになります。で、このインスタンスを、
$err = $obj->connect($dsninfo, $obj->getOption('persistent'));
のようにして使って、データベースに接続しています。
そこで、次に/usr/share/php/DB/sqlite.phpのDB_sqliteクラスのconnectメソッドをみてみます。
function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('sqlite')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); }
先頭でPEAR::loadExtensionなるメソッドを呼び出し、それが失敗したらDB_ERROR_EXTENSION_NOT_FOUNDというエラーにしろ、と読めます。どうやら、件のエラーはここで発生しているようです。
ではPEAR::loadExtensionを調べるため、/usr/share/php/PEAR.phpをみます(はてなの文法を回避するため、一部に余計な空白が追加されています)。
function loadExtension($ext) { if (!extension_loaded($ext)) { // if either returns true dl() will produce a FATAL error, stop that if ( (ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { return false; } if (OS_WINDOWS) { $suffix = '.dll'; } elseif (PHP_OS == 'HP-UX') { $suffix = '.sl'; } elseif (PHP_OS == 'AIX') { $suffix = '.a'; } elseif (PHP_OS == 'OSX') { $suffix = '.bundle'; } else { $suffix = '.so'; } return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); } return true; }
先頭の
if (!extension_loaded($ext)) {
は、$ext(この場合'sqlite')がロードされていなかったら以下を実行する、というように読めます。で、いまは初めてロードする場合なので、このif文の内部を見てみます。まず始めに、
if ( (ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { return false; }
があります(はてなの文法を回避するため、一部に余計な空白が追加されています)。ini_getメソッドというのは、名前から推察するにphp.iniの指定された値を読み取るメソッドと思われます。で、enable_dlというのが1ではない場合、またはsafe_modeが1である場合にエラーとなります。ここでエラーになっているのでしょうか? 調べてみました。
$ grep 'enable_dl\|safe_mode' /etc/php5/apache2/php.ini safe_mode = Off ; then turn on safe_mode_gid. safe_mode_gid = Off ; When safe_mode is on, UID/GID checks are bypassed when safe_mode_include_dir = ; When safe_mode is on, only executables located in the safe_mode_exec_dir safe_mode_exec_dir = safe_mode_allowed_env_vars = PHP_ ; protected even if safe_mode_allowed_env_vars is set to allow to change them. safe_mode_protected_env_vars = LD_LIBRARY_PATH enable_dl = On sql.safe_mode = Off
safe_modeは"Off", enable_dlは"On"になっています。確証はありませんが、常識的に考えて"Off"は0, "On"は1になりそうです。ならば、上のif文ではエラーになりません。では次を見てみます。
if (OS_WINDOWS) { $suffix = '.dll'; } elseif (PHP_OS == 'HP-UX') { $suffix = '.sl'; } elseif (PHP_OS == 'AIX') { $suffix = '.a'; } elseif (PHP_OS == 'OSX') { $suffix = '.bundle'; } else { $suffix = '.so'; } return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
OSの種類で処理をわけています。使用しているOSはUbuntu Linuxなので、そのとき実行される文を抜き出してみると、
$suffix = '.so'; return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
と、なると思います。確信はありませんが、最後に呼び出しているdlというメソッドが拡張モジュールをロードする本体ではないでしょうか。このdlメソッドに渡したモジュールが読み込まれるに違いありません。さらに、このdlメソッドは2回呼び出され(る可能性があり)、最初の読み込みに失敗したら、次のを読み込んで、それでもダメならエラーになりそうです。では、なにを読みこませようとしているのでしょうか? 変数を展開すると、最初に渡しているのは、'php_sqlite.so'です。調べてみましたが、こんなファイルはないようです。で次に渡しているのは、'sqlite.so'です。存在するかどうか調べてみたら、sqlite.soはありませんでしたが、/usr/lib/php5/20051025/sqlite3.soというのがありました。dpkgによると、
$ dpkg --search /usr/lib/php5/20051025/sqlite3.so php5-sqlite3: /usr/lib/php5/20051025/sqlite3.so
となり、このファイルはphp5-sqlite3パッケージをインストールしたときに入ったもののようです。確かに、私はPHPからSQLite3を使うため、このパッケージをインストールしました。
しかし困りました。じゃあこのsqlite3.soを読み込ませるために、DB::connectメソッドに渡すDSNを、"sqlite3:///..."にしたらいいのでしょうか? しかし、それだとPEAR::DBのconnectメソッドの、
$dsninfo = DB::parseDSN($dsn); $type = $dsninfo['phptype']; (中略) @include_once "DB/${type}.php";
というところで、DB/sqlite3.phpをインクルードするようになってしまいます。/usr/share/php/DB/sqlite3.phpというファイルはありません。
ここで検索エンジンで適当なキーワードで探してみたところ、http://php-sqlite3.sourceforge.net/pmwiki/pmwiki.phpに着きました。ここでDB/sqlite3.phpを配布しているようです。しかし、以下のような記述があります。
php-sqlite3 is a PHP extension that lets you access SQLite3 databases (see : http://www.sqlite3.org) within your scripts.
PHP 4 and PHP 5 have already built-in support for this RDBM, but this is limited to the 2.x releases. This extension adds support for SQLite 3.x release.
Please note that this project is still alpha-quality. Please test and report if it works for you and how it can be enhanced.
すなわち、
php-sqlite3は、スクリプトからSQLite3データベース(http://www.sqlite3.orgをみよ)にアクセスするためのPHPの拡張です。
PHP 4とPHP 5はすでに、RDBMに対応していますが、これは2.xに限定されています。この拡張は、SQLite 3.xへの対応を追加します。
ただし、このプロジェクトは依然アルファ品質です。これが動いたかどうか、そしてどのように変えればいいか、テストして教えてください。
です。つまり、PHPのPEAR::DBはSQLite3にアクセスできないようです。
私はSQLite3をあきらめ、2.xを使うことにしました。