PHP
Linux
PDO
xampp
access

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

PHPにおけるデータベース接続において、ほとんどのデータベース接続に対応している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のバージョンを合わせてインストールするとうまくいきます。

参考(remiとか)
CentOS7にPHP7をyumでインストールする

# yum install 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で取得するという無茶振りをやってみたわけですが、もしこの研究、分析成果が、誰かのお役に立てればと思います。