PHPの勝利への道のり、パート 1
もしPHP に関する私の過去の2つの記事を読んだら、すぐにでも IBM i で PHP を使わなければという衝動にかられるかもしれません。しかし一方で、実装後いったいどうやって技術的にこれらのアプリケーションをサポートするのか少し不安に思うかもしれません。
この記事では、PHPリクエストが様々なサブシステムを経由して RPG コードまでたどり着く道のりと、何か問題が発生した場合の基本的なトラブルシューティング方法についてお話しします。
タッグ・チーム・マッチ
偉大なことはしばしば誰か他の人の助けがあって達成されます。マイケル・ジョーダンにはスコッティー・ピッペンがいました。ベーブ・ルースにはルー・ゲーリックがいました。そして、アボットにはコステロがいました。PHP で言えば、ZENDSVR6 には QSQSRVR があります。では説明しましょう。
PHP (またはほかのウェブ・サーバー言語)は、SQL ステートメントでバックエンド・データベースにアクセスできなければ役に立ちませんが、実はPHP はそのままではDB2 を認識しないため、そのタスクを別のジョブに渡します。
このストーリーにはここからダウンロードできるコードが入っています。
WRKACTJOB SBS(QHTTPSVR) JOB(ZENDSVR6) と入力し、QHTTPSVR サブシステムの ZENDSVR6 ジョブを表示します。次に PHP CGI ジョブの横に "5" と入力します (図 1)。オプション 11で指定したコール・スタックを表示し、カーソルをコール・スタックの php-cgi.bin プログラムに置き、F22 を押します (図 2)。このプログラムは、 HTTPリクエストを待機している IFS ルート・システムの PHP バイナリーです。


このジョブで要求されたPHP スクリプトが SQL を使用していると判断されると、そのタスクは QSYSWRK サブシステムの QSQSRVR (SQL サーバー) ジョブに渡されます。WRKACTJOB SBS(QSYSWRK) JOB(QSQSRVR) と入力し、それらの SQL ジョブを表示します。ジョブがある場合、現行ユーザーが QTMHHTTP のジョブを探します (図 3)。

そのジョブの横に "5" と入力し、ジョブ・ログを表示します。そこにあるメッセージはアップストリーム ZENDSVR6 ジョブを参照しています。

同様に、その ZENDSVR6 ジョブのジョブ・ログは、メッセージ ID SQL7908 の SQL を処理しているダウンストリーム QSQSRVR ジョブを参照します。

特定のHTTP PHP 要求に対して、これらのメッセージは開発者にこれら 2つのジョブがリンクされていることを伝えます。
トリプル・プレイ
3つ目のサブシステム、ZENDSVR6 を見てみましょう。WRKACTJOB SBS(ZENDSVR6) と入力します (図 4)。このサブシステムも処理する PHP 要求に対してアクティブになっているはずです。

Zend Server メニューは、 QHTTPSVR サブシステムの ZENDSVR6 ジョブを制御し (図 1)、ZENDSVR6 サブシステムとその各種ジョブも制御します (図 4)。GO ZENDSVR6/ZSMENU と入力し、"Service Management" オプション 5 を使用して ZSVMENU メニューに移動します (図 5)。
オプション 1 および 2 は ZENDSVR6 サブシステムを制御し (図 4)、オプション 3 は図 4 を表示します。オプション 5、6、7 は QHTTPSVR サブシステムの ZENDSVR6 ジョブを制御します (図 1)。ここで考慮すべき事項は、 HTTP PHPリクエストを処理するために ZENDSVR6 サブシステムおよび Apache サーバー・インスタンスが活動中でなければならないことです。
オプション 9、10、11 はそれぞれ ZENDSVR6 サブシステムの ZSDAEMON、ZSDEPLMNG、ZSMONMNG ジョブを開始および停止します (図 4)。この記事のサンプル PHP スクリプトでは、これらのジョブを実行している必要はありません。

速攻で実行する
では、IBM i に接続するPHP スクリプトを実行し、結果セットおよびいくつかの出力パラメーターを返しそれらの値をブラウザーにエコー・バックするバックエンド RPG ストアード・プロシージャーを呼び出してみましょう。このPHP スクリプトは cust600.php という名前で、/www/zendsvr6/htdocs フォルダーの IFS に保存されています。これは、Apache が PHP スクリプトを検索するデフォルトの ドキュメント・ルート フォルダーです。この PHP スクリプトについては詳しくは触れませんが、DB2 接続をするためのいくつかの関数で構成され、RPG ストアード・プロシージャーを呼び出し、結果セットをループして HTML テーブルに顧客のアドレスをリストします。
バックエンドのRPG プログラムは CUST600SP という名前で、SQL ストアード・プロシージャー定義にラップされています。この SQL ステートメントは CUST600SP という外部ストアード・プロシージャーも作成します。4つのパラメーター定義は、 PHP スクリプトと RPG プログラム両方のパラメーター定義と一致する必要があります。

ダウンロード可能なコードを使い、RPG プログラムを標準ライブラリー・リストのどこかにあるライブラリーにコンパイルし、PHP スクリプトを IFS ドキュメント・ルート・フォルダーにコピーして、SQL ストアード・プロシージャーを作成します ("YOURLIB" をライブラリー・リストのライブラリー名に置き換えます)。QHTTPSVR apache インスタンスと ZENDSVR6 サブシステムがアクティブであることを確認します。PHP スクリプトを実行するブラウザーURL は http://YourSystem.com:10080/cust600.php?sort=zip です。ここでソート値は name または state または zip です。デフォルトのソート順はカスタマー番号です。
下記、図 6 のサンプル出力をご覧ください。
1 行目は、現在の時間とソート順を示します。
2 行目は RPG カウンター、プログラム名、現行ユーザー、PHP DB2 接続の QSQSRVR ジョブを示しています。HTML テーブルは一目瞭然です。URLのソート順を変更するまで、カスタマーデータは静的なので、PHPスクリプトが再実行されたときにブラウザ出力が実際に更新されることを示す乱数列が表示されます。

ブラウザーを更新するたびに WRKJOB オプション 14 "Open Files"、次に "F11=I/O Details" からファイル入出力カウントが QSQSRVR ジョブで増えているのがわかります (図 7)。この場合、RPG カウンターは 3 、顧客テーブルには 12 行あり、合計 36 行が現時点で選択されています。

RPG プログラムは、この QSQSRVR ジョブでアクティブのままです。理由は 1) db2_pconnect() 関数は、接続しているユーザー・プロファイル(ここではQTMHHTTP)でパーシステントコネクション確立し、2) RPG カウンターが 10 未満の場合に RPG プログラムは *INLR=*OFF で終了するためです。ブラウザーを更新すると、ブラウザー出力で RPG カウンターが上がっていることがわかります。ある QSQSRVR ジョブは、単に 1 つの QHTTPSVR ZENDSVR6 ジョブではなく、当日中に多くの IBM i ジョブをサービスできる点を理解してください。それは必ずしも 1 対 1 の関係ではありません。ブラウザー上で同じ URL の 2 番目のタブを開き、各タブを交互に更新すると、両方のタブが同じ QSQSRVR ジョブにリンクされている場合、タブの RPG カウンターは 2ずつ増えます。 (最初のタブ・カウンターは 1、3、5、7、9 と進み、2 番目のタブは 2、4、6、8、10 と進みます)。
RPG カウンターが 10 になると、*INLR は *ON と設定され、ステータス・パラメーター値 END が PHP スクリプトに渡され、db2_pclose() 関数が呼び出されていることを知らせます。

このクローズは、 QSQSRVR 事前開始ジョブをジョブが最初に作成された状態にリセットします。次回 PHP スクリプトを実行したときに、RPG カウンターは再度 1 から始まります。たとえdb2_pclose() が呼び出されていなくても、万が一実動時間中に、より新しいバージョンがライブラリー・リスト上位にインストールされることを考えて、RPG プログラムを定期的にシャットダウンしたいと思います。
また、ブラウザー出力に表示されたQSQSRVR ジョブを STRSRVJOB/STRDBG またはサービス・エントリー・ポイントでデバッグできます。繰り返しますが、再実行された PHP スクリプトが同じバックエンド QSQSRVR ジョブに接続されている保証はありません。上記の 2つタブのシナリオで同じ QSQSRVR ジョブに接続した場合、これは簡単に証明できます。そのジョブをデバッグし、RPG コードにブレークポイントを設定します。最初のタブを更新し、ブレークポイントで実行を停止させます。2 番目のタブを更新すると、IBM i はそのジョブを検出し、その要求を別の QSQSRVR ジョブに経路指定します。明らかにブラウザー出力のジョブ名が変更されています。
db2_pconnect() の代わりとなるのが、db2_connect() で、これと似ていますが、暗黙的 db2_close() が PHP スクリプトが終了するたび (要求のたび) に発生する点が異なります。対応する QSQSRVR ジョブは常にリセットされます。つまりパフォーマンス全体が遅くなります。
後半戦に向けて準備する
さて、どうでしたか? 問題が全くなかったとしても、我々IT 開発者は常にこの先どんな障害がありそうかを知っておきたいものですよね。この記事のパート 2 では、PHP リクエストがブラウザーと RPG コード間を往復する際に遭遇するであろう様々な種類のエラーについて説明します。 Chris Ringer: 1989 年からの RPG 経験者。主に、受注処理、医薬業、製造業に精通。余暇時間にはランニングとトライアスロンを楽しむ。「決して早くはないんですが、いつも大集団の中でそれなりに激しく争ってるんです。」