PHPでAS400のストアドプロシージャを実行して出力パラメータを受け取る
PDO
でODBC接続します。ここではWindows10に Client Access ODBC Driver (32bit版) をインストールして使いましたが、IBM i Access ODBC Driver でもドライバ名が違うだけで方法は同じです。
ストアドは適宜作成しておいてください。
以下のコードは PIYOPIYO
プロシージャを実行する例です。PIYOPIYO
プロシージャは2つのパラメータを使用し、1つ目が入力パラメータ (CHAR 4桁)、2つ目が出力パラメータ (CHAR 2桁) とします。
また、PIYOPIYO
プロシージャで呼び出されるプログラムはライブラリ LIB1
LIB2
を使用します。なので、ここでは接続文字列に DefaultLibraries
キーワードを入れてみました。不要ならキーワードごと外してください。
IBM i AccessのODBC接続文字列は、以下に公式の解説があります。
以下がサンプルのPHPコードです。
try {
// 接続設定
$driver = '{Client Access ODBC Driver (32-bit)}';
$server = '(接続先)';
$user = '(ユーザー名)';
$pass = '(パスワード)';
$libs = 'LIB1,LIB2';
// PDOでDB接続
$dbh = new PDO("odbc:Driver=$driver; System=$server; DefaultLibraries=$libs;", $user, $pass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // エラー時に例外を吐かせる (関係ないけど)
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE , PDO::FETCH_ASSOC); // 連想配列でfetchする (関係ないけど)
// ステートメント準備
$sth = $dbh->prepare("{ CALL PIYOPIYO(?, ?) }");
$inputParam = 'ABCD';
$outputParam = '';
$sth->bindParam(1, $inputParam, PDO::PARAM_STR);
$sth->bindParam(2, $outputParam, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 3); // ※備考あり 記事の続きを見てください
// 実行
$sth->execute();
// bindParamの第二引数 (参照渡し) にセットされた出力パラメータを確認
echo $outputParam;
}
catch (Exception $e) {
echo $e->getMessage();
}
サンプル中で ※備考あり としたところについて。
bindParam
の第4引数はパラメータ長で、出力パラメータの時は明示的に指定する必要があります。
このサンプルの PIYOPIYO
プロシージャの出力パラメータは2桁なのですが、当方環境ではなぜかいつも、出力パラメータを1桁余分に取っておかないとPHPが Out of memory
エラーを吐きました。なので3桁にしてます。
出力パラメータの文字コードについて
私の調査不足かもしれませんが、どうもWindows機にインストールしたドライバから IBM i にODBCで接続する場合、出力パラメータの文字コードは必ず CP932 (SJIS-win) になってしまうようです。それどころか PDO
が吐くメッセージすら CP932 固定です。これを接続設定とかで何とかする方法がどうしても見つけられませんでした。
なのでPHPの内部エンコーディングが UTF-8 (というか SJIS-win 以外) の場合、受け取った後で適宜変換する必要があります。それを踏まえて、上述のサンプルを修正したものが以下です。
try {
// 接続設定
$driver = '{Client Access ODBC Driver (32-bit)}';
$server = '(接続先)';
$user = '(ユーザー名)';
$pass = '(パスワード)';
$libs = 'LIB1,LIB2';
// PDOでDB接続
$dbh = new PDO("odbc:Driver=$driver; System=$server; DefaultLibraries=$libs;", $user, $pass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // エラー時に例外を吐かせる (関係ないけど)
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE , PDO::FETCH_ASSOC); // 連想配列でfetchする (関係ないけど)
// ステートメント準備
$sth = $dbh->prepare("{ CALL PIYOPIYO(?, ?) }");
$inputParam = 'ABCD';
$outputParam = '';
$sth->bindParam(1, $inputParam, PDO::PARAM_STR);
$sth->bindParam(2, $outputParam, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 3); // ←これが全角で返ってくる場合!
// 実行
$sth->execute();
// 変換
$outputParam = mb_convert_encoding($outputParam, 'UTF-8', 'SJIS-win');
// bindParamの第二引数 (参照渡し) にセットされた出力パラメータを確認
echo $outputParam;
}
catch (PDOException $e) {
echo mb_convert_encoding($e->getMessage(), 'UTF-8', 'SJIS-win');
}
catch (Exception $e) {
echo $e->getMessage();
}