SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PEARライブラリ活用

PEAR MDB2でPHPからデータベースを操作する

PEARライブラリ活用 (4)

  • X ポスト
  • このエントリーをはてなブックマークに追加

PEAR MDB2を使ってみよう

 PEAR MDB2を試しましょう。MDB2はMySQL以外のデータベースにも対応しています。標準的なSQLだけを使うようにしておけば、簡単にデータベースを切り替えられます(たとえば、以下の例で最初に実行するTRUNCATE文は、DB2では動かないので避けるべきです。本稿では使いませんが、結果の数を制限するLIMIT句もMySQL独自のものなのでなるべく使わない方がいいでしょう)。

 コードはUTF-8で保存して実行してください。なお、ファイル名を「MDB2.php」などとしないように注意してください。ライブラリのファイル名と同じ名前を付けてしまうと、ライブラリが読み込まれません。

接続

 次のようにMySQLに接続します。MDB2::connectの引数は、「'mysqli://ユーザ名:パスワード@ホスト/データベース名?charset=文字コード'」です(接続に失敗するとエラーが表示されます)。

require_once 'MDB2.php';//ライブラリのロード

$db=MDB2::connect('mysqli://test:pass@localhost/mydb?charset=utf8');
if(PEAR::isError($db)) die("<p>{$db->getMessage()}</p>");//エラー処理

 少し細かい話ですが、ここではconnectとして接続しましたが、他にfactorysingletonとして接続することもできます。connectとすると、データベースにすぐに接続します。factoryとすると、接続のためのオブジェクトは生成しますが、実際にデータベースに接続するのは、それが本当に必要になった時です。singletonとしたときも、最初の接続のタイミングはfactoryの場合と同じです。factoryの場合と違うのは、このメソッドをもう一度呼んだときの動作です。factoryとしたときは、接続のためのオブジェクトがもう一つ生成されます(connectの場合も同様)。それに対してsingletonとしたときは、最初に生成されたものと同じオブジェクトが返されます(デザインパターンでのsingletonパターン)。

エラーハンドラ

 いちいちエラーをチェックするのは面倒なので、エラーハンドラ(エラーが発生したときに実行される関数)を用意しておきましょう。

mdb2init.php
//エラーが発生したときに実行される関数
function errorHandler($error){
    echo "<p>エラー発生 {$error->getMessage()}</p>";
}

//errorHandlerを使うように設定
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'errorHandler');

 setErrorHandlingはPEARのパッケージによって生成されるエラーの処理方法を指定するメソッドです。このメソッドには、処理モードとそれに応じたオプションを引数として与えます。ここでは処理モードをPEAR_ERROR_CALLBACKをしているので、エラーが発生するとオプションで指定した関数が呼び出されます(詳細はマニュアルのPEAR::setErrorHandling()の項を参照)。

SQL文の実行

結果の返らないクエリ

 データベースへのクエリは、SELECTのように結果の返るものと、INSERTやUPDATE、DELETEのように結果の返らないものに分けることができます。結果の返らないクエリはexecメソッドで実行します。

mdb2-1.php
$db->exec('TRUNCATE TABLE books');
$db->exec("INSERT INTO books VALUES (1,'The Art of Computer Programming 1',
  'アスキー',2004,10290)");
$db->exec("INSERT INTO books VALUES (2,'フリーソフトウェアと自由な社会',
  'アスキー',2003,3360)");
$db->exec("INSERT INTO books VALUES (3,'計算機プログラムの構造と解釈',
  'ピアソンエデュケーション',2000,4830)");
$db->exec("INSERT INTO books VALUES (4,'プログラミング作法','アスキー',
  2000,2940)");
$db->exec("INSERT INTO books (id,title,publisher,year,price) VALUES 
  (5,'Webアプリケーション構築入門','森北出版',2007,3360)");

内容が実行中に決まる場合

 次のような新しいデータを挿入することを考えましょう。

$title='プログラミングPHP';
$publisher="O'Reilly";

 次のように実行したいと思うかもしれませんが、これは失敗する悪い例なので、まねしないでください

//悪い例
$sql="INSERT INTO books (title,publisher) VALUES ('{$title}','{$publisher}')";
echo "<p>$sql</p>";
//INSERT INTO books (title,publisher) VALUES ('プログラミングPHP','O'Reilly')
$db->exec($sql);

 表示されるINSERT文を見ると、「O'Reilly」の「'」のために、文法的に間違ったものになっていることがわかります。

プリペアードステートメント

 変数を使ってSQL文を作るときは、変数の部分をプレースホルダとしてあらかじめ決めておいて、あとで変数の内容を埋め込みます。この仕掛けを「プリペアードステートメント」と呼びます(ちなみに、プリペアードステートメントには、同じSQL文を効率よく複数回実行するという利点もあるはずなのですが、筆者の環境ではかえって遅くなりました)。

$stmt=$db->prepare('INSERT INTO books (title,publisher) VALUES (?,?)');
$stmt->execute(array($title,$publisher));

 まず、prepareメソッドで実行したいSQL文のひな形を作ります。「?」というのは、あとで変数を埋め込むという意味です。次にexecuteメソッドで実行します。その際に、「?」の部分に埋め込むデータを配列で与えます。すでにひな形ができているので、「O'Reilly」のようなデータでもエラーにはなりません。

 ちなみにこの例ではidを省略していますが、この列はAUTO_INCREMENTと指定してあるので、MySQLによって自動的にidが生成されます。生成されたidは次のように確認することができます。

echo "<p>挿入されたデータのidは{$db->lastInsertId('books','id')}.</p>";//6

空文字列の挿入

 次のように空文字列を挿入しようとするとエラーになります。

空文字列の挿入(※レイアウト調整)
$stmt=$db->prepare('INSERT INTO books (title,publisher,year,price) '.
  'VALUES (?,?,?,?)');
$stmt->execute(array('',"O'%Reilly",2008,1995));
//MDB2 Error: null value violates not-null constraint

 空文字列はNULLに変換してからデータベースに格納されます。列titleは「NOT NULL」に設定してあるため、その際にエラーが発生するのです。

 この空文字列のNULLへの変換は、Oracleの仕様にあわせたものなのですが、勝手にこんなことをされては困るので、この自動変換はオフにしておきましょう。可搬性のためのすべての機能(MDB2_PORTABILITY_ALL)のうち、自動変換(MDB2_PORTABILITY_EMPTY_TO_NULL)だけをオフにします(ちなみに、単にMDB2_PORTABILITY_NONEとすると、すべての機能が無効になります)。

 可搬性のための設定には、ここで確認したMDB2_PORTABILITY_EMPTY_TO_NULLの他に、データの右側にある空白を取り除くMDB2_PORTABILITY_RTRIMやテーブル名やフィールド名を大文字あるいは小文字に変換するMDB2_PORTABILITY_FIX_CASEなどがあります。詳細はマニュアルの可搬性を参照してください。

 これで先ほどのINSERT文が実行できるようになります。

$db->setOption('portability',
  MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL);

クォート

 「O'Reilly」というデータをデータベースに登録しようとしてエラーになる問題を、プレースホルダを用いて解決してきました。

 手でSQLを書く場合には、O'ReillyではなくO\'Reillyと書けばよいのですが、これを実現するのがquoteメソッドです。このメソッドで処理すれば、基本的にはどのような文字列でもSQL文に挿入可能になります(使えない場面は後述)。

$title='初めてのPHP';
$publisher="O'Reilly";

$sql="INSERT INTO books (title,publisher) VALUES ".
  "({$db->quote($title)},{$db->quote($publisher)})";
echo "<p>$sql</p>";
//INSERT INTO books (title,publisher) VALUES ('初めてのPHP','O\'Reilly')
$db->exec($sql);

 しかし、この方法はお勧めできません。quoteメソッドとプリペアードステートメントを一緒に使うと、余計な「'」が入ってしまうのです。

$stmt=$db->prepare('INSERT INTO books (title,publisher) VALUES (?,?)');
$stmt->execute(array($db->quote($title),$db->quote($publisher)));
//「'初めてのPHP'」と「'O\'Reilly'」が入る
Magic quotesはオフにしよう
 本題からは外れますが、特別な理由がない限り、SQL文はプリペアードステートメントを使って実行するようにしてください。これはSQLインジェクション攻撃に対する最も基本的な対策です。基本的なことにも関わらず、プリペアードステートメントがあまり使われていないのには以下のような理由があるのではないかと、筆者は推測しています。
  1. SQLを文字列で作って実行するという直観的にわかりやすい方法が入門書等で紹介される。
  2. 初心者がそれをそのまま真似る。
  3. SQLインジェクション攻撃に対して脆弱なウェブアプリが量産される。
  4. PHPの開発者が「Magic quotes」というマジックを導入する。Magic quotesによって、ブラウザから送られた「O'Reilly」という文字列は、自動的に「O\'Reilly」に変換されようになる(脆弱性回避)。
  5. この「O\'Reilly」という文字列は、プリペアードステートメントでは使えない(余計なバックスラッシュが入ってしまう)。
  6. プリペアードステートメントは使われなくなる。
 データを勝手に書き換えてしまうMagic quotesがデフォルトで有効になっているのは困ったことです。PHPをインストールしたら、設定ファイル(php.ini)を探して、「magic_quotes_gpc = Off」としておきましょう。

次のページ
トランザクション

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PEARライブラリ活用連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 矢吹 太朗(ヤブキ タロウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2480 2008/05/26 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング