第2回 「SimpleXML」と「JSON」で共通データ形式を便利に

亀本 大地
アシアル株式会社

2008/6/19

JavaScriptでの運用が容易なJSON

 もう1つ、Webアプリケーションと親和性の高い共通データ形式として「JSON(JavaScript Object Notation)」がある。

 JSONはもともと、JavaScriptにおけるオブジェクトなどの表現形式を一般化して、共通のデータ形式として利用するようにしたもの。Ajax(XHR)の流行に伴い、WebアプリケーションにおけるJavaScriptの重要性は圧倒的に高まったが、JavaScriptでの運用が容易なJSONは、その表現の容易さも相まって、広く普及した。

関連記事:
リンク JSONがRFCになり、どんどんこなれるAjaxサービス
http://www.atmarkit.co.jp/fwcr/rensai/ajaxwatch11/01.html
リンク リッチクライアント用語辞典:JSON
http://www.atmarkit.co.jp/aig/07wcr/json.html

 PHPはWebでの運用を第一に考えられた言語ということもあり、PHPのデータをJSON形式に置き換えるモジュールが、PHP 5.2から標準で利用できるようになった。

 このモジュールは非常に簡素で、json_encode()json_decode()の2つの関数しかない。使い方も至って簡単で、PHPの配列またはオブジェクトをjson_encode()関数に渡すと、JSONのオブジェクトに変換してくれる。逆も同様で、json_decode()関数でJSONのオブジェクト文字列をPHPのオブジェクトに直してくれる。

<?php

$array = array(
  "aaa" => "あああ",
  "bbb" => "いいい",
  "ccc" => "test"
);

$json = json_encode($array);

var_dump($json);

$obj = json_decode($json);

var_dump($obj);

string(68) "{"aaa":"\u3042\u3042\u3042","bbb":"\u3044\u3044\u3044","ccc":"test"}"
object(stdClass)#1 (3) {
  ["aaa"]=>
  string(9) "あああ"
  ["bbb"]=>
  string(9) "いいい"
  ["ccc"]=>
  string(4) "test"
}
結果

 また、json_decode()の第2引数にtrueを渡すと、オブジェクトではなく、連想配列を返却してくれる。利用法によって適宜切り替えて使うとよいだろう。

関連リンク:
リンク PHPマニュアル JSON
http://jp.php.net/manual/ja/book.json.php

ネイティブのデータベース抽象化レイヤ「PDO」

 PHP5が導入される際、新たな追加モジュールとして特に注目度の高かったものは、やはりPDO(PHP Data Object)だろう。

 これまでPHP4では、データベースへの接続には、各DB用に独自実装されたネイティブドライバを使用するモジュールが利用されてきた。これらのモジュールはすべて各DBに固有の実装になっており、単純にそのまま使用してしまうと、DBとロジックの分離が難しくなっていた。

 このため、「PEAR::DB」や「ADOdb」を代表とするpure PHPで実装されたDB抽象化クラスが多く用いられてきた。しかし、元々のモジュールが抽象化を想定したものではなかったために、抽象化部分が肥大化することになり、本来ならば必要のないロジックを多く処理することになっていた。

 これに対しPDOでは、この抽象化部分までをPHPのモジュール側で行うようにし、より高速で抽象度の高いDB接続を行えるようになった。また、独自に例外をthrowするようになっており、トランザクションのロールバックなども自然な形で実装できる。

 PDOは、抽象レイヤの役割を担うPDOコアモジュールと、各DBとの接続を行うPDOドライバモジュールの2つから成り立っている。コアモジュールがPHPで使用するAPIを提供し、各ドライバモジュールを経由してDBへアクセスする。

図1
図1 PDOの仕組み

 これにより、使用する関数APIを変更することなく、使用DBを切り替えることができる(※注:無論、DBによって固有のクエリを発行している場合などは、完全に互換することはできず、そのエミュレート機能も提供されてはいない。そのため、現実に抽象化を図るには、さらにクエリを抽象化するORマッピング層が必要になる)。

 では、PDOの基本的な使用法を、MySQLにアクセスする場合を例に取って紹介しよう。

<?php
$dsn = 'mysql:host=localhost;dbname=atmarkit';
$user = 'user';
$pass = 'pass';

try {

 // DB接続開始
  $dbh = new PDO($dsn, $user, $pass);

  // 実行時も失敗したらPDOExceptionを発行するように設定
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  if ($dbh->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
    $dbh->exec('SET NAMES UTF8');
  }

  // クエリの発行
  $query = 'SELECT * FROM member';
  foreach($dbh->query($query) as $row) {
    var_dump($row);
  }

  $dbh = null;
} catch (PDOException $e) {
  print "Error: " . $e->getMessage() . PHP_EOL;
  exit;
}

 単純にクエリを発行するだけであればPDOオブジェクトのexec()メソッドで、SELECTなどで結果オブジェクトが欲しい場合はquery()メソッドで実行すればよい。また、複数INSERTなどの場合には、トランザクションを利用するAPIも用意されている。

<?php
$addusers[0] = array('name' => '太郎', 'age' => 17);
$addusers[1] = array('name' => '次郎', 'age' => 19);
$addusers[2] = array('name' => '三郎', 'age' => 21);

$dsn = 'mysql:host=localhost;dbname=atmarkit';
$user = 'user';
$pass = 'pass';

try {
  // DB接続開始
  $dbh = new PDO($dsn, $user, $pass);

  // 実行時も失敗したらPDOExceptionを発行するように設定
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  if ($dbh->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
    $dbh->exec('SET NAMES UTF8');
  }


  // トランザクション開始
  $dbh->beginTransaction();

  // プリペアードクエリをセット
  $query = 'INSERT INTO member(name, age) VALUES (:name, :age)';
  $sth = $dbh->prepare($query);
  foreach ($addusers as $user) {
    $sth->bindParam(':name', $user['name']);
    $sth->bindParam(':age', $user['age']);
    $sth->execute();
  }

  // コミット
  $dbh->commit();

  $dbh = null;
} catch (PDOException $e) {

  // ロールバック
  $dbh->rollback();

  echo "Error: " . $e->getMessage() . PHP_EOL;
  exit;
}

 beginTransaction()、commit()、rollback()の各メソッドによってトランザクション処理を行っている。

 同一テーブルへのINSERTを繰り返す場合には、上記のようにprepare()メソッドを用いて、プリペアードクエリを利用できる。指定するクエリ内で「:name」のようにコロンを付けることで、プレースホルダの名前を付与できる。

 PDOオブジェクトのprepare()メソッドを実行すると、PDOStatementオブジェクトが返ってくるので、それに対してbindParam()メソッドとexecute()メソッドでクエリを発行している。

 また、プレースホルダに名前を付与せずに、疑問符を用いることも可能だ。その場合には、bindParam()メソッドではプレースホルダ名の代わりに、1から始まる通番で代入先を指定すればよい。

// 疑問符プレースホルダの場合(一部抜粋)
  $query = 'INSERT INTO member(name, age) VALUES (?, ?)';
  $sth = $dbh->prepare($query);
  foreach ($addusers as $user) {
    $sth->bindParam(1, $user['name']);
    $sth->bindParam(2, $user['age']);
    $sth->execute();
  }

 なお、利用上の注意点だが、PDOは、クエリ発行に失敗していてもデフォルトでは例外をthrowしない。このため、例外処理を実装する場合は上記2つの例のようにsetAttribute()メソッドでエラーモードを指定する必要がある(※注:なお、PDOのコンストラクタは失敗時にPDOExceptionをthrowするので、心配する必要はない)。

 こうすることによって、各メソッドが実行時に失敗した場合にもPDOExceptionをthrowするようになり、エラー処理が適切に行えるようになる。逆に、これをせずに実装してしまうと、トランザクションで複数INSERTを行っている際のロールバック処理が適切に行えなかったり、余計なエラーハンドリングの実装を追加する必要があるので、注意しておこう。

関連リンク:
リンク PHPマニュアル PHP Data Object
http://jp.php.net/manual/ja/book.pdo.php

 今回紹介したSimpleXML、JSON、PDOといったモジュールは、PHP5から実装されたものの中でもメジャーなモジュールで、使い勝手も非常に良い。ぜひとも使いこなせるようになって、さらなる開発効率の向上に役立ててほしい。

2/2
 

Index
「SimpleXML」と「JSON」で共通データ形式を便利に
  Page1
手軽にXMLを扱えるようにしたSimpleXML
Page2
JavaScriptでの運用が容易なJSON
ネイティブのデータベース抽象化レイヤ「PDO」

PHP5で広がる! 開発環境

 PHP関連記事
例外処理の実装を把握する
PHP5で広がる! 開発環境(1)
 PHP4のサポートが終了し、いよいよPHP5への移行を視野に入れる時期が来た。PHP5の機能を生かした開発のポイントを紹介
クライアントPCに言語環境を入れる理由
Mac OS X+PHPでオールインワン環境(準備編)
 Webアプリ開発者に人気のMac OS X。効率的な開発のために複数バージョンのPHPを実行する環境を構築してみよう
PHPに押し寄せるリスクと国際化の波
PHPカンファレンス2008レポート(前編)
 PHP4のサポートが完全に終了する。多くの新機能が投入されるPHP5.3へ移行か、国際化対応で開発が遅れるPHP6を待つか
PHPによる大規模商用サービスの裏側
PHPカンファレンス2008レポート(中編)
 企業のWebアプリケーション開発現場で利用されるPHP。開発現場の裏側にはさまざまなドラマが隠されている
PHPユーザーは本当にほかの言語を知らないのか?
PHPカンファレンス2008レポート(後編)
 PHPは本当にダメな言語なのだろうか。Perl、Ruby、Python、Java、JavaScriptの使い手が白熱した議論を行った
  Coding Edgeフォーラムフィード  2.01.00.91


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

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間