Delphi for PHPを使い倒す!(中編)
データベースコンポーネントの使い方
はやしつとむ
アナハイムテクノロジー株式会社
2009/10/29
なぜPHPではビジュアル開発ができないのだろうか。そんな疑問を解消するのが、スペイン生まれのDelphi for PHPだ(編集部)
PHPプログラミングで“ぽとりぺたり”なお手軽Visual開発を可能にするDelphi for PHP 2.0。
第1回「えっ、まだPHPでVisual開発してないの?」では、フォームに配置したButtonがどのようなイベント機構で動作するかについて解説しました。
今回は、より実用的な観点からデータベースにアクセスするプログラムの作成について取り上げます。
データベースコンポーネント
Delphi for PHP 2.0には、以下のデータベースにアクセスすることが可能です。これらは、ADOdbをベースとしたDatabaseコンポーネントがサポートするほか、InterBase/Firebird、MySQL、Oracleについては別途ネイティブコンポーネントも付属しています。
記事公開当初、「PDO(PHP Data Object)をベースとしたDatabaseコンポーネントがサポートする」としておりましたが、これは「ADOdbをベースとした」の間違いでした。お詫びして訂正いたします(編集部 2009/11/4)
Databaseコンポーネントで利用できるデータベース(主要なもの)
- ADO
- Firebird
- IBM DB2
- Informix
- InterBase
- Microsoft Office Access
- Microsoft SQL Server
- MySQL
- ODBC
- Oracle Database
- PostgreSQL
- SAP DB
- SQLite
- Sybase ASE
- Sybase SQLAnywhere
DelphiでのVCLと同様に、データベース接続コンポーネント+データセット系コンポーネント+データソース+データベース系Visualコンポーネントという接続方法を踏襲しています。
ここで、Delphi for Windowsと違うのはLabel、Memo、Button、CheckBox、RadioButton、ListBox、ComboBox、RadioGroupといったStandardコンポーネントに、そのままデータベース系コンポーネントと接続できる機能が備わっていることです。
ネイティブか、それとも汎用か、それが問題だ!
さて、Delphi for PHP 2.0には大きく分けて2つのデータベース接続コンポーネントがあるわけですが、ユーザーはどちらを使えばいいのでしょうか。
汎用データベースコンポーネントで利用されているADOdbは、PHPのネイティブなデータベース接続関数が各データベースごとに独自の実装を進めてきてたため、汎用性がなくなっていることを補完する目的でリリースされてきたものです。汎用性を高めることが目的とはいえ、オーバーヘッドはそれほどないといわれています。
記事公開当初、汎用コンポーネントを「PDO」としておりましたが、これは「ADOdb」の間違いでした。お詫びして訂正いたします(編集部 2009/11/4)
汎用データベースコンポーネントのDatabase+QueryコンポーネントとInterBase/Firebird専用コンポーネントであるIBDatabase+IBQueryコンポーネントでのベンチマークを行ってみた結果は次のようになりました(ベンチマークのソースコードをZip形式でダウンロードできます)。
Database+Queryコンポーネント
10,000 Inserts:00:09.567
10,000 Updates:00:13.170
IBDatabase+IBQueryコンポーネント
10,000 Inserts:00:09.721
10,000 Updates:00:12.831
※VMWare Workstation上のCentOS 5.3、Firebird 2.1.2SS、PHP5.2.9
こうしてみると、それほど違いはないようです。もっとも、VCL for PHP自体が各データベース接続関数の独自性を吸収する作りになっているので、ibase関数をラップしているIBコンポーネントの方が速度的な面で有利なうえ、トランザクションモードを変更できるので、細かい制御が必要な場面ではやはりネイティブコンポーネントを使いたいところです。
記事公開当初、汎用コンポーネントを「PDO」とした記述となっておりましたが、これは間違いでした。お詫びして訂正いたします(編集部 2009/11/4)
InterBaseコンポーネントの機能追加
ところで、細かい制御が得意なはずの専用コンポーネントですが、実は問題があります。Oracleコンポーネントでは実装されているのですが、InterBase/FirebirdとMySQLコンポーネントではPrepare()メソッドが未実装のためプリペアドステートメントが利用できないのです。
連続して同様のクエリを実行する場合はもちろん、昨今問題となっているSQLインジェクション攻撃を回避するためにも利用できるプリペアドステートメントが実行できないのは大変困ります。これも開発者のホセ・レオンにはパッチの提案も含めて伝わっているのですが、本バージョンでの実装は予定していないという話でした。
そこで、InterBase/FirebirdコンポーネントにもPrepare()メソッドの実装を追加してみましょう。そこはオープンソースですから筆者が変更して公開しても何ら問題はありません。まずは、VCL for PHPの当該箇所を以下に示します。
function prepare() { $this->Database->Prepare($this->buildQuery()); }
function prepare($query) { ibase_prepare($this->_connection, $query); }
一見すると、「あれ? ibase_prepare()を呼んでるんじゃ?」と思いますが、PHPのマニュアルを見ると、ibase_prepare()関数はプリペアドクエリのハンドルを返すことが分かります。
(PHP 4、PHP 5)
ibase_prepare — 後でパラメータのバインド及び実行を行うためにクエリを準備する
説明
resource ibase_prepare ( string $query )
resource ibase_prepare ( resource $link_identifier , string $query )
resource ibase_prepare ( resource $link_identifier , string $trans , string $query )
対応するパラメータのバインドや(ibase_execute()による)実行をするためのクエリを準備します。
パラメータ
query InterBase クエリ
返り値
プリペアドクエリのハンドル、あるいはエラー時にFALSEを返します。
つまり、IBQuery->prepare()とした場合に実行される1208行目のコードから呼び出されている、184行目の IBDatabase->prepare()ではibase_prepare()関数を呼びながら返されるハンドルを保持せずに終了してしまっているので、何にもならないわけです。ある意味、何もしないより悪いですね。
MySQLの方のコードは、interbase.ini.phpのコードをコメントアウトして終わっています。をいをいって感じですが、この方が何もしないだけまだましです。
function Prepare($query) { // Not implemented // ibase_prepare($this->_connection, $query); }
それでは、interbase.inc.phpを改修していきましょう。改修点は以下のようになります。
- IBDataSetクラスに、プリペアドクエリのハンドルを保持するprotectedフィールド$_sthを追加
- IBDatabaseクラスのprepare()メソッドで、プリペアドクエリのハンドルを返すように変更
- IBQueryクラスのprepare()メソッドで、戻されたハンドルを$this->_sthに格納するよう変更
- IBDatabaseクラスのexecute()メソッドに引数$sthを追加し、ibase_execute()とibase_query()を使い分けるように変更する
- IBDatabaseクラスのexecuteLimit()メソッドに引数$shtを追加し、$this->execute()へ渡すよう変更
- IBDataSetクラスのinternalOpen()メソッドで、$this->Database->Execute()と$this->ExecuteLimit()の呼び出しに、$sthを渡すように変更
ついでに、IBQuery、IBTable、IBStoredProcコンポーネントのLimitStart、LimitCountプロパティをnullに設定しても0〜999ミリオンに変換されたROWS句としてクエリに付加されるのがいやなので、これをそれぞれが-1に設定されていた場合にはそもそもROWS句を付加しないように変更しました。
また、IBQueryの基底クラスであるIBCustomQueryクラスにexecute()メソッドがあった方が使いやすいので、これも追加してあります。変更を反映したソースコードを別ページに示します。
1/3 |
Index | |
データベースコンポーネントの使い方 | |
Page1 データベースコンポーネント ネイティブか、それとも汎用か、それが問題だ! InterBaseコンポーネントの機能追加 |
|
Page2 簡単DBアプリ作成−サンプルデータベース 簡単DBアプリ作成−メインフォーム 簡単DBアプリ作成−データモジュール 簡単DBアプリ作成−コーディング |
|
Page3 簡単DBアプリ作成−レコードの追加(1) 簡単DBアプリ作成−レコードの追加(2) 簡単DBアプリ作成−レコードの追加(3) |
Delphi for PHPを使い倒す! |
Coding Edgeお勧め記事 |
いまさらアルゴリズムを学ぶ意味 コーディングに役立つ! アルゴリズムの基本(1) コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう |
|
Zope 3の魅力に迫る Zope 3とは何ぞや?(1) Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか? |
|
貧弱環境プログラミングのススメ 柴田 淳のコーディング天国 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く? |
|
Haskellプログラミングの楽しみ方 のんびりHaskell(1) 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう |
|
ちょっと変わったLisp入門 Gaucheでメタプログラミング(1) Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう |
|
- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
|
|