こうこく
作 ▸

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接続文字列は、以下に公式の解説があります。

ODBC データ・ソースの手動による構成

接続ストリング・キーワード

以下がサンプルのPHPコードです。

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 以外) の場合、受け取った後で適宜変換する必要があります。それを踏まえて、上述のサンプルを修正したものが以下です。

内部エンコーディングがUTF-8の場合
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();
}
この記事に何かあればこちらまで (非公開)