Edited at

【PHP】PDOでMS ACCESSに接続し、データを取得してみた

PHPにおけるデータベース接続において、ほとんどのDBMSに対応しているPDO。実はMS ACCESSのmdb、accdbファイルにも対応しており、データを取得することができます。

ただ、それにはこまごまと準備が必要です(特にLinux系の場合は色々と準備が必要になり、データの取得に対しても一工夫必要になります)。

※Linuxの場合のデータ取得方法を追記しました。Linuxの場合、本当に困難を極めますが、以下の方法で取得可能です(centOS7にて取得テスト済)。


1 接続前の準備


xamppなどのWindowsの場合

php.iniファイルからpdo-odbcドライバを使用できるようにします。


php.ini

extension=pdo-odbc; //コメントを外す


その後はapache再起動で準備完了です。


Linuxの場合


必要なドライバのインストール

php-odbcドライバを使用するので、yumコマンドで開発用ドライバ、それからmdbodbcライブラリと一緒にインストールします(当然、php-pdoなどは事前に使えるようにしておく必要があります)。もし、依存関係などで一部のドライバがインストールできなかった場合、remiレポジトリでPHPのバージョンを合わせてインストールするとうまくいきます。例はPHP7.2-xを使用する場合)


参考(remiとか)

CentOS7にPHP7をyumでインストールする


# yum install --enablerepo=remi-php72 php-pdo php-odbc unixODBC unixODBC-devel libmdbodbc


libmdbodbc.so.1ライブラリのインストールと設定

上記のドライバに加え、libmdbodbc.so.1ライブラリが必要になりますが、標準ではインストールされていませんので、外部からインストールしてきます。その上で、odbcinst.iniを編集して、ドライバに読み込ませる必要があります。


このページが参考になりました

MDB Tools missing driver


# wget https://forensics.cert.org/cert-forensics-tools-release-el7.rpm //リポジトリのインストール

# rpm -Uvh cert-forensics-tools-release-el7.rpm //リポジトリの解凍
# yum --enablerepo=forensics install libmdbodbc1 //リポジトリを使って、yumコマンドでインストール
# find / -name *libmdbodbc* //インストールされた場所を探す
# cp -ap /usr/lib64/libmdbodbc.so.1 /usr/lib/ //インストール元から複製する(32bit機で動かす場合)

これで、ライブラリをセットできました。そして、次はこのライブラリを読み込むためにodbcinst.iniを編集します。

#vi /etc/odbcinst.ini

/*以下を追記して保存する。また、[***]内の名称(例ではMDBTools)が、LinuxでのPDO接続ドライバ名となる。*/
[MDBTools]
Description = ODBC for MDBTools
Driver64 = /usr/lib64/libmdbodbc.so.1
Setup64 = /usr/lib64/libmdbodbc.so.1
Driver = /usr/lib/libmdbodbc.so.1
Setup = /usr/lib/libmdbodbc.so.1
FileUsage = 1
UsageCount = 1

このようにパスを設定します。これでLinuxの準備は完了です。あとは

# systemctl restart httpd //centos7、Apacheの場合

でWEBサーバーを再起動します。


2 PDOでACCESSに接続する

ここからPDOを使って、ACCESSに接続するのですが、ここにも色々とトラップがあります。


このサイト(Github)のフォーラムがかなり参考になりました。

How to handle MS Access MDB files in Linux with PHP5 PDO and ODBC

Problems with MDBTools + unixODBC + php_odbc


このサイトの記述を使えば、OSがWindowsでもLinuxでも対応できます。

注意点は、WindowsとLinuxでドライバの記述が異なる点です。


ファイルパス記述の注意


  • ファイルパスは必ず/(スラッシュ)を使ってください(¥は無効となります)。

  • パスは必ず絶対パスで記述し、かつ書き方も癖があります(詳しくはサンプルで)

  • DbqパラメータはLinuxの場合、DBQと大文字で記述しないとデータベース名を取得できません。


PHP

/*ファイルパスの記述例*/

$dbName = "C:/xampp/htdocs/hogehoge.mdb"; //Xamppの場合はドライブの記述方法に注意。
$dbName = "/var/www/html/hogehoge.mdb"; //Linuxの場合は必ずサーバ内のパスで記述する


PHP

$uname = explode(" ",php_uname());

$os = $uname[0];
switch ($os){
case 'Windows':
$dbName = "D:/xampp/htdocs/hogehoge.mdb";
$driver = '{Microsoft Access Driver (*.mdb, *.accdb)}';
break;
case 'Linux':
$dbName = "/var/www/html/hogehoge.mdb";
$driver = 'MDBTools'; //odbcinst.iniで追記したドライバ名
break;
default:
exit("非対応のOSです");
}
$dbh = new PDO("odbc:Driver=$driver;DBQ=$dbName;");


これでCould not find Driver などのエラーが表示されなかったら、PDOへの接続は成功です。


3 データを取得する


Windowsの場合

データは普通にPDOオブジェクトを利用するだけで取得可能です。テーブル名が日本語などの場合でも"テーブル名"と、ダブルクォートで囲えば問題なく取得できます。

※現在、日本語カラムからデータを取得できないかテスト中です。成功次第、記事を追記したいと思います。


Linuxの場合

色々と注意点があり、各カラムをバインドしないとデータを取得できないようです(普通にFETCHしようとすると、カラムをBINDしてくださいと怒られて何も取得できません)


このページを参考にしました。

PDO Error when ODBC_Exec works correctly


カラムは逐一記述して、それぞれにPDO::bindColumn()メソッドでバインド処理を行っていきます。そして各行をfetchしていくのですが、unixODBCの仕様上PDO::FETCH_NUMPDO::FETCH_ASSOCなどが使用できないため(使用方法を探しましたが見つかりませんでした)、PDO::FETCH_BOUNDで、各行に対して判定結果をループさせ、個別の変数から直接値を取得するようにします。

また、SQLの記述もかなり厳格なルールがあり、それを守らないと正しくデータを取得してくれません。


  • カラム名はしっかりと書くこと。*(アスタリスク)を使った場合でも、取得する行番号をしっかりと付記しないと、正しいデータが取得できません。

  • SQL文の定型句(SELECT FROM WHEREなど)は英大文字で記述しないと、不正なSQL構文とエラー表示されます)

  • SQL文の末尾に;(コロン)を付けると、クエリを読み取ってくれません。


PHP

   //num,nameというカラムを持つテーブルtest

$sql = "SELECT num,name FROM test"; //ルールは前述した通りです
$sth = $dbh -> prepare($sql);
$sth -> execute();
//各カラムにバインドしていく
$sth -> bindColumn(1,$num);
$sth -> bindColumn(2,$name);

//カラムに対してバインドをかけるため、$flgには判定結果しか帰ってこない
$values = []; //値を代入する配列
while( $flg = $sth -> fetch(PDO::FETCH_BOUND) ){
$values[] = [$num,$name];
}


※なお、例では変数名をカラムに合わせていますが、特に決まりはありません。数量が不確定の場合\$val[1],$val[2],…のような変数でも問題ありません。


注意


  • ACCESSでメモ型は使わないでください。データの値が取得できなくなるバグがあるようです(テキスト型に直すと取得できます)

  • カラム名でアンダーバー( _ )を使うと正しくデータを取得できないバグもあるようです。


4 最後に

どうしても実務に必要だったので、ACCESSデータをPHPで取得するという無茶振りをやってみたわけですが、もしこの研究、分析成果が、誰かのお役に立てればと思います。