Delphi for PHPを使い倒す!

Delphi for PHPを使い倒す!(中編)

データベースコンポーネントの使い方

はやしつとむ
アナハイムテクノロジー株式会社

2009/10/29

なぜPHPではビジュアル開発ができないのだろうか。そんな疑問を解消するのが、スペイン生まれのDelphi for PHPだ(編集部)
- PR -

 PHPプログラミングで“ぽとりぺたり”なお手軽Visual開発を可能にするDelphi for PHP 2.0

 第1回「えっ、まだPHPでVisual開発してないの?」では、フォームに配置したButtonがどのようなイベント機構で動作するかについて解説しました。

 今回は、より実用的な観点からデータベースにアクセスするプログラムの作成について取り上げます。

Delphi for PHPを使い倒す! バックナンバー

データベースコンポーネント

 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
Data Accessコンポーネント
Data Accessコンポーネント
Data Controlsコンポーネント
Data Controlsコンポーネント
InterBaseコンポーネント
InterBaseコンポーネント
MySQLコンポーネント
MySQLコンポーネント
Oracleコンポーネント
Oracleコンポーネント
PEARコンポーネント
PEARコンポーネント

 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の当該箇所を以下に示します。

interbase.inc.pas(1208)
        function prepare()
        {
            $this->Database->Prepare($this->buildQuery());
        }
interbase.inc.php(184)
        function prepare($query)
        {
            ibase_prepare($this->_connection, $query);
        }

 一見すると、「あれ? ibase_prepare()を呼んでるんじゃ?」と思いますが、PHPのマニュアルを見ると、ibase_prepare()関数はプリペアドクエリのハンドルを返すことが分かります。

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のコードをコメントアウトして終わっています。をいをいって感じですが、この方が何もしないだけまだましです。

mysql.inc.php(172)
        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()メソッドがあった方が使いやすいので、これも追加してあります。変更を反映したソースコードを別ページに示します。

改修後のinterbase.inc.php

IBDatabase->execute()で、count($params) > 0でプリペアドステートメントかどうかを判断しているが、パラメータなしのプリペアドステートメントはあり得るので、その判定方法の変更が必要です。筆者の現在の使い方ではこれで十分なのですが、必要に応じて修正してみて下さい。

 
1/3
next

Index
データベースコンポーネントの使い方
Page1
データベースコンポーネント
ネイティブか、それとも汎用か、それが問題だ!
InterBaseコンポーネントの機能追加
  Page2
簡単DBアプリ作成−サンプルデータベース
簡単DBアプリ作成−メインフォーム
簡単DBアプリ作成−データモジュール
簡単DBアプリ作成−コーディング
  Page3
簡単DBアプリ作成−レコードの追加(1)
簡単DBアプリ作成−レコードの追加(2)
簡単DBアプリ作成−レコードの追加(3)

index 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の世界を体験してみよう
  Coding Edgeフォーラムフィード  2.01.00.91

@IT Special

- PR -

TechTargetジャパン


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

イベントカレンダー

PickUpイベント

- PR -

アクセスランキング

もっと見る

ホワイトペーパーTechTargetジャパン

注目のテーマ

Coding Edge 記事ランキング

本日 月間
ソリューションFLASH