PHPからSQLServerとOracleへの接続

2015/08/03

PHP

MVCモデルはインターフェイスであるViewerが要求を受け付けて、Controllerが内部処理して、Modelがデータベースに格納する、という3層構造になっているので、テーマ変更と追加処理とデータ構造変更の3つを、限りなくお互いの影響を排除しながら、分業化して行なうことができます。

ジャカルタ

インドネシアのITサービス

インターネット技術の急速な発展と普及により、優秀なIT人材を輩出することで知られるジャカルタのビヌス大学(BINUS)やバンドゥンのバンドゥン工科大学、インドネシアコンピューター大学(UNIKOM)の学生の多くがインターネット・WEB業界やソフトウェア業界を志望するようです。

続きを見る

開発プラットフォームとしてのCMS

WordPressなどのCMSは豊富なテンプレートやプラグインがあるので企業ホームページやポータルサイトなどのデザイン重視のWebサイト作成向きであり、僕のようにデザインセンスに自信のない人間にとっては大幅な開発工数短縮が可能になります。

またCMSなので投稿や固定ページの追加・修正・削除が容易であり、ブログ機能であるカテゴリ化、タグ付け、アーカイブなどによるデータ整理が楽であり、保守性に秀でています。

一方、頻繁にDB処理を行なうデータ処理中心のWEBサイト開発の場合は、CMS独自のテーブル構造やファイル構成の制約を受けるデメリットが強く働き、そもそもCMSの強みが発揮できないため、PHPでライブラリを使いながらスクラッチ開発したり、フレームワークを使ってMVCモデル(処理Model-出力Viewer-入力Controller)開発することになります。

多くのマスタデータを新設する必要がある業務系サイトではCMSの出る幕は少ないでしょう。

MVCモデルはインターフェイスであるViewerが要求を受け付けて、Controllerが内部処理して、Modelがデータベースに格納する、という3層構造になっているので、テーマ変更と追加処理とデータ構造変更の3つを、限りなくお互いの影響を排除しながら、分業化して行なうことができます。

CMSが開発プラットフォームとして利用されるとすれば、今後も参照系システム中心になると思われます。

MySQL関数

VBのADOでレコードセットから値を取り出す場合と異なる点は、mysql_queryで返された結果(2次元配列)にアクセスするためには、結果リソースをレコード(行)単位で mysql_fetch_array関数にてフィールド数分の配列として取得する必要があること。

Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset

Dim query, email As String

query="select * from patient where EMAIL='" & email & "'"
rs.Open query, cn, adOpenKeyset, adLockOptimistic

Do Until rs.EOF
    //VBの場合レコードセットからテーブルのフィールド名指定で値が取り出せる。
    MsgBox rs!SEI
    MsgBox rs!MEI

    rs.MoveNext
Loop

つまりADOの場合Query実行結果を2次元配列であるrs(レコードセット)に格納し、レコードセットからフィールド名を指定することで値を取得し、レコードセットに対して直接MoveNextで行を進めていくが、MySQLの場合mysql_query関数によるQuery実行結果を2次元配列であるret(レコードセット)に格納し、レコード単位にmysql_fetch_array関数でで配列に格納した上でフィールドの値を取得する。

フォームからデータを入力

if(isset($_POST["email"])){
    $email = $_POST["email"];
}

<form action="index.php" method="POST">
<input type="text" name="email" />
<input type="submit" />
</form>

DBに接続

mysql_connect($server,$user,$password);
mysql_select_db($dbname);
mysql_query('set names utf8');

SET NAMESが使えない場合はINSERT文で日本語をDBに挿入する場合、以下の文字化け防止処理をする。

mysql_set_charset("utf8");

入力データを元にQueryを実行

//SELECTの場合は検索結果を$retに格納(まだ2次元配列ではない)
$query="select * from patient where EMAIL='".$email."'";
$ret=mysql_query($query);

//INSERTの場合
$query2="insert into patient(SEI, MEI) ";
$query2.="values ('".$full_name1."','".$full_name2."'')";
mysql_query($query2);

レコードセットの行数と列数を取得(値の取得のためのループの最大値として使用)

$numRow=mysql_num_rows($ret); //レコード数(行数)
$numCol=mysql_num_fields($ret); //フィールド数(列数)

Queryの結果を表示

$rowをmysql_fetch_arrayの戻り値とした場合、$row[0]にSEIの値, $row[1]にMEIの値, $row[2]にEMAILの値が入り、もう一度 戻り値を$rowとしてmysql_fetch_arrayを実行すると、その次の行が同じ変数に格納される。

ちなみに似たようなMysql関数にmysql_fetch_assocというのがあるんですが、こちらは添え字配列専門です。

//レコード数の回数だけを繰り返す
for($i=0;$i<$numRow;$i++){
    echo '';
    //取得した行に対する配列を返し内部データポインタを前に進める。
    $row=mysql_fetch_array($ret);
    //フィールド(列)の数の回数だけ繰り返す
    for($j=0; $j<$numCol;$j++){
        $str=$row[$j];
        $str=htmlspecialchars($str);  //クロスサイトスクリプティング対策
        echo ''.$str.'';
    }
    echo '';
}

以下のようにwhileとforeachを使えばよりシンプルになります。mysql_fetch_arrayは繰り返し実行するだけで次のレコードに進む(rs.MoveNextみたいに)してくれるところがポイントです。

while($row=mysql_fetch_array($ret)){
    echo "";    //配列$rowのインデックス番号$keyと値$valueを取得
    foreach($row as $key => $value){
        $value=htmlspecialchars($value);  //クロスサイトスクリプティング対策
        echo ''.$value.'';
    }
echo "";
}

mysql_fetch_arrayがFalesを返すまで(取得すべき行が存在しない)ループし、その中でforeachにより配列のキーと値(列の値)を取り出す。foreachはwhile文やfor文などと異なり繰り返しが終了される条件式は存在せず、配列の要素の数だけ繰り返して終了する。

DBから切断

mysql_close();

ライブラリを使用する

PEARのライブラリPEAR MDB2でDB接続
Smartyでコードビハインド(ロジックがphpファイル、デザインがtplファイル)

ライブラリの読み込み

require_once 'MDB2.php';
require_once 'Smarty/Smarty.class.php';

フォームからのデータを取得

if(isset($_POST["email"])){
    $email = $_POST["email"];
}

DBに接続

$dsn='mysql://$user:$password@$server/$dbname?charset=utf8';
$con=MDB2::connect($dsn);

入力されたデータを元にQueryを実行

//SELECTの場合は検索結果を$retに格納(まだ連想配列ではない)
$sql=sprintf("select * from patient where EMAIL='".$email."'");
$ret=$con->query($sql);

//今回は先にレコードセットのrowを配列に格納してしまう
$data=array();
while ($row=$result->fetchRow(MDB2_FETCHMODE_ASSOC)){
    $data[]=$row;
}

SmartyのテンプレートにQuery結果を表示

$smarty=new Smarty();
$smarty->assign('data', $data);
$smarty->display('index.tpl')

テンプレートファイル

//コードビハインドなのでtplにあるのはタグとSmarty関数のみ
<form action="index.php" method="POST"><input type="text" name="email" />
<input type="submit" /></form>

{foreach from=$data item=row}

{/foreach}

PHPとMySQLとの接続

さて、PHPからMySQLに接続するためには拡張ディレクトリに拡張モジュールであるphp_mysql.dllが配置され、php.iniに拡張指定がある必要がありました。

extension=php_mysql.dll

拡張ディレクトリのパスはphp.ini(D:\xampp\php\php.ini)のextension_dirに定義されています。

extension_dir="D:\xampp\php\ext"

PHPとSQL Serverとの接続

同じように、PHPからSQLServerに接続するにはMicrosoft Drivers for PHP for SQL Serverという拡張モジュール(ドライバー)のdllファイルが必要であり、PHPコードからはドライバーの関数(API)を呼び出してSQL文を実行します。

パッケージはPHPのバージョンによって以下のように分かれていますが、今回はWin7上のPHP5.6.3なのでSQLSRV32.EXEをダウンロードします。

  • SQLSRV32.EXE : PHP5.6
  • SQLSRV31.EXE : PHP5.5
  • SQLSRV30.EXE : PHP5.4

このパッケージを展開するとさらにPHPのバージョンごとのdllファイルが生成されますが、今回の環境はPHP5.6.3なので以下の2つを拡張ディレクトリに保存します。

extension=php_pdo_sqlsrv_56_ts.dll      //PHP5.6用PDOのスレッドセーフドライバー
extension=php_sqlsrv_56_ts.dll          //PHP5.6用SQL Serverのスレッドセーフドライバー

スレッドセーフ(Thread Safe)か非スレッドセーフ(Non Thread Safe)かは、例のphpinfo()関数を書いたphpファイルを呼び出して以下の記述で確認できます。

PHPとSQL Serverとの接続

Apacheを再起動してPDOとSQL Serverのドライバーが読み込まれていれば以下の記述が確認できます。

PHPとSQL Serverとの接続

PHPINFO SQL Server

また下図のODBCとSQL Server間に別途ODBC driver 11 for SQL server x64が必要になります。

PHPとSQL Serverとの接続

PHPとOracleとの接続

PHPからOracle DBを操作するにはOCI(Oracle Call Interface)というRDBMSのAPIを使用し、これはoci.dllとして実装されています。ですからまずはApache起動時にoci.dllがロードされる必要があるわけで、その拡張モジュールとしてphp_oci8_11g.dllがプロシージャーエントリーポイントでリンクします。

Winでは環境変数のpathにあるディレクトリのdllがロードされるため、pathに以下の記述が必要です。

D:\app\HP\product\11.2.0\client_1\bin;

Oracle Instant Clientは、標準のOracleクライアントなしでOracleクライアントとして接続できる環境を提供するものですが、Oracle ServerとPHP が同じマシンで動いている場合は、必要なライブラリはデータベースソフトウェアの中にすべて含まれているので必要ないです。要はOracleサーバーがあればOracle ClientもOracle Instant Clientも必要ないということですな。

Oracle 11gに接続するための拡張モジュールphp_oci8_11g.dllの5.6 Thread Safe (TS) x86をPECLから入手し、拡張ディレクトリに配置します。そしてphp.iniの拡張指定を行います(コメントアウトするだけ)。

;extension=php_oci8.dll      ; Use with Oracle 10gR2 Instant Client
extension=php_oci8_11g.dll  ; Use with Oracle 11gR2 Instant Client

Apacheを再起動するとOracleのドライバーが読み込まれていることが確認できます。
PHPとOracleとの接続