PHPでクラウド連携するための準備

(1/1)
2010年代に入り、Yahoo!Google の検索エンジン、Amazon楽天市場の商品データベースといったものを、自分のホームページから利用できるようになってきた。従来のように GET/POST メソッドを使ってパラメータを渡して Yahoo! や Google のページとして表示するのではなく、見た目は自分のサイトデザインのまま、中身のデータだけを Yahoo! や Google から受け取れるようになったのである。
こうした仕組みを WebAPI、これを利用することを クラウド連携と呼んでいる(2010年代前半までは マッシュアップ(MushUp) と呼んでいた)。

WebAPI は無料で利用できるものが多い。また、WebAPI を公開する企業としても、無償でクラウド連携して顧客を呼び込んでくれるパワーユーザーの存在はありがたい。今後、WebAPI の公開が、ますます盛んになるだろう。
というわけで、このコーナーは「ぱふぅ家のホームページ」100万回アクセス記念として、PHPWeb API を利用し、クラウド連携していく方法を説明していくことにする。

目次

クラウド連携の仕組み

クラウド連携の仕組み
上の図は、クラウド連携の仕組みを表したものである。
クライアント(ブラウザ)から見えるのは、1つのドメイン(サーバサイド)だけである。サーバサイドは、PHP などのプログラムを介して、Yahoo! や Amazon といったクラウド・サービスが提供する WebAPI を利用する。したがって、クライアントからどんな WebAPI を利用しているのかはわからない。ここまでのデータ交換は、すべて http(s)通信で処理される。
さらに、WebAPI の裏側にはデータベースなどのシステムがあるが、クライアントから WebAPI が見えないのと同様、サーバサイドからそれらのシステムを見ることはできない。

サンプルXMLとプログラムのダウンロード

圧縮ファイルの内容
yahoosearch.xmlサンプルXMLファイル。
readxml4.phpサンプル・プログラム本体(PHP 4)。
readxml5.phpサンプル・プログラム本体(PHP 5/7/8)。
readxml5.phpサンプル・プログラム本体(PHP 4/5/7/8)。

XML のツボ

WebAPI を利用するためには、まず、XML に関する知識が必要になる。Google や Yahoo! のような WebAPI から戻ってくるデータ形式が XML だからだ。XML についてご存知の方は、この項を読み飛ばしていただいて構わない。XML を扱うのは初めてという方は、とりあえず、次の3点を押さえておいてほしい。

1.HTML のようなタグ構造をしているテキスト

XML は、<hoge>...</hoge> というタグ構造をなしているテキストデータである。
ただ、HTML と異なり、タグの名前は自由に設定できる(厳密には定義が必要)。


2.UTF-8 が標準

XML ファイルの冒頭行は
  <?xml version="1.0" encoding="utf-8"?>
と書かれていることが多い。つまり、標準コードは UTF-8 である。
ShiftJIS や EUC-JP で書くこともできるが、PHP が用意している XML 処理関数の中には UTF-8 でないと正常に処理できないものがあるため、ここではすべて UTF-8 として扱うことにする。


3.PHP では XML を扱う方法が複数ある

PHP バージョン 4 と 5 では、同じ DOM(Document Object Model)でも互換性がほとんどない。そこで、

  • PHP4 ⇒ DOM
  • PHP5 ⇒ SimpleXML
を利用することにする。SimpleXML は PHP バージョン 5 で導入された関数群で、DOM や XML パーサ(SAX)に比べて短いコードで XML 処理を行うことができるのが特長だ。

XML ファイルの構造

Yahoo! ウェブ検索Webサービス」は Web API の一種だが、これを利用して得られる XML ファイルを解釈するプログラムを作ってみることにする。
このサービスを利用し、ある日の "中央線E233系" の検索結果の XML ファイルを用意した。
応答データ構造(xml) ResultSet Result Title ページのタイトル Summary ページの要約 Url ページURL ClickUrl ページのリンクURL ModificationDate ページの最終更新日 MimeType MIMEタイプ Cache Url キャッシュ結果のURL Size キャッシュ・サイズ(byte)
この XML ファイルの構造は上図のようになっている。
冒頭の <?xml ...> はヘッダである。
それ以降は <ResultSet> で囲まれるブロックが1つある。
<ResultSet> の下には、<Result> で囲まれるブロックが複数ある。
<Result> の下には、<Title>, <Summary>, <Url>, <ClickUrl>, <ModificationDate>, <MimeType> で囲まれるブロックが各々1つずつある。

次のページで紹介するサンプルプログラムでは、複数登場する <Title> タグの内容を、配列変数に格納することを目指す。

解説:DOM XML (PHP 4)

  90: /**
  91:  * 指定XMLファイルを読み込んでDOMを返す.
  92:  * @param   string $xml XMLファイル名
  93:  * @return  object DOMオブジェクト/NULL 失敗
  94: */
  95: function read_xml4($xml) {
  96:     if (! isphp5over()) return NULL;
  97:     if (($fp = fopen($xml, "r")) == FALSE)  return NULL;
  98: 
  99:     //いったん変数に読み込む
 100:     $str = fgets($fp);
 101:     $str = preg_replace("/UTF-8/", "utf-8", $str);      //コーディング文字の置換
 102: 
 103:     while (! feof($fp)) {
 104:         $str = $str . fgets($fp);
 105:     }
 106:     fclose($fp);
 107: 
 108:     //DOMを返す
 109:     $dom = domxml_open_mem($str);
 110:     if ($dom == NULL) {
 111:         echo "\n>Error while parsing the document - " . $xml . "\n";
 112:         exit(1);
 113:     }
 114: 
 115:     return $dom;
 116: }

ユーザー関数 read_xml4 は、XML ファイルを読み込んで DOM オブジェクトを返す。
PHP4 には、XML ファイルを読み込んで DOM を返す関数  domxml_open_file  があるのだが、日本語ファイルを正常に処理できないことがあったので(バージョンや OS によるものかもしれない)、ユーザー関数として用意することにした。

まず、XML ファイルをテキストファイルとみなし、いったん変数 $str にすべて読み込む。この際、冒頭行のエンコーディング指定が大文字の 'UTF-8' だと、DOM 関数群が正常に作用しないので(これも PHP のバージョンや OS に依存する問題かもしれない)、小文字の 'utf-8' に置換しておく。
XML ファイルをすべて変数 $str に読み込んだら、関数  domxml_open_mem  を使って、DOM を得る。XML の構造が間違っていたり、UTF-8 で書かれていないとエラーになる。

解説:XML処理 (PHP 4)

 118: /**
 119:  * 指定したXMLファイルを読み込んで,タグ名を要素名にした連想配列に読み込む.
 120:  * XMLファイルはYahoo! ウェブ検索WebサービスAPIが出力するもの.
 121:  * @param   array $items  情報を格納する配列
 122:  * @param   string $xml   XMLファイル名
 123:  * @return  int 情報の数/FALSE
 124: */
 125: function getTitles4(&$items, $xml) {
 126:     $name = 'Title';
 127: 
 128:     if (($dom = read_xml4($xml)) == NULL)   return FALSE;
 129:     $resultset = $dom->get_elements_by_tagname('ResultSet');
 130:     $results   = $resultset[0]->get_elements_by_tagname('Result');
 131:     //検索結果取りだし
 132:     $cnt = 1;
 133:     foreach ($results as $element) {
 134:         $node = $element->get_elements_by_tagname($name);
 135:         if ($node !NULL) {
 136:             $items[$cnt] = $node[0]->get_content();
 137:         }
 138:         $cnt++;
 139:     }
 140: 
 141:     return ($cnt - 1);
 142: }

ユーザー関数 getTitles4 は、Yahoo! ウェブ検索Webサービス が出力する XML から、Title タグの内容を配列変数に格納して返す。引数は、格納したい配列変数と、XML ファイル名の2つ。

まず、ユーザー関数 read_xml を呼び出して DOM を取得する。
次に、全体を一括りにしているタグ <ResultSet> をオブジェクトとして、メソッド  getElementsByTagName  を適用する。ここでは <ResultSet> は1つしかないので、$resultset[0] が全体を括る <ResultSet> オブジェクトを示す。

<ResultSet> には複数の <Result> がぶら下がっているが、$resultset[0] に対してメソッド  getElementsByTagName  を適用することで、これらのオブジェクトを取得できる。
<Result> は複数あるので、各々のオブジェクトは、$results[0], $results[1], $results[3]‥‥に格納されていく。

<Title><ResultSet> にぶら下がっているので、$results[N] に対してメソッド  getElementsByTagName  を適用することで、<Title> オブジェクトを取得できる。
<Title> の内容は、オブジェクトに対してメソッド  get_content  を適用すれば取得できる。

解説:HTML BODY作成 (PHP 4)

ファイル入力と表示を1つのプログラムで賄っている。

 144: /**
 145:  * 指定したXMLを読み込んでHTML BODYを作成する.
 146:  * @param   string $fname XMLファイル名
 147:  * @return  string HTML BODY
 148: */
 149: function makeCommonBody($fname) {
 150:     $myself = MYSELF;
 151:     $refere = REFERENCE;
 152:     $p_title = TITLE;
 153:     $version = '<span style="font-size:small;">' . date('Y/m/d版', filemtime(__FILE__)) . '</span>';
 154:     $width   = WIDTH;
 155:     $debug   = '';
 156: 
 157:     //デバッグ情報
 158:     if (! FLAG_RELEASE) {
 159:         $phpver = phpversion();
 160:         $isSimpleXML = function_exists('simplexml_load_string'? 'enabled' : 'disabled';
 161:         $debug =<<< EOT
 162: <p>
 163: <span style="font-weight:bold;">★デバックモードで動作中...</span><br />
 164: PHPver : {$phpver}<br />
 165: SimpleXML : {$isSimpleXML}
 166: </p>
 167: 
 168: EOT;
 169:     }
 170: 
 171:     //XMLの解釈と表示
 172:     $items = array();
 173:     if (file_exists($fname)) {
 174:         $n = getTitles4($items, $fname);
 175:         if ($n == FALSE) {
 176:             $res = 'error - 正しいXMLファイル名を入力してください.';
 177:         } else {
 178:             $i = 1;
 179:             $res = '';
 180:             foreach ($items as $title) {
 181:                 $res ."{$i} : {$title}<br />\n";
 182:                 $i++;
 183:             }
 184:         }
 185:     } else {
 186:         $res = 'ファイル名を入力してください.';
 187:     }
 188: 
 189:     $body =<<< EOT
 190: <body>
 191: <h2>{$p_title} {$version}</h2>
 192: <form name="myform" method="post" action="{$myself}" enctype="multipart/form-data">
 193: XMLファイル:<input name="file" type="file" size="80" />
 194: <input type="submit" name="execute" value="抽出" />
 195: </form>
 196: 
 197: <div style="border-style:solid; border-width:1px; margin:20px 0px 0px 0px; padding:5px; width:{$width}px; font-size:small; overflow-wrap:break-word; word-break:break-all;">
 198: <h3>使い方</h3>
 199: <ol>
 200: <li>[<span style="font-weight:bold;">XMLファイル</span>]に抽出したいファイルを入力し、[<span style="font-weight:bold;">抽出</span>] ボタンを押してください。</li>
 201: <li>抽出結果が表示されます。</li>
 202: </ol>
 203: ※参考サイト:<a href="{$refere}">{$refere}</a>
 204: {$debug}
 205: </div>
 206: <hr />
 207: {$res}
 208: </body>
 209: 
 210: EOT;
 211:     return $body;
 212: }

サンプル・プログラム(PHP5/7/8)

解説:SimpleXML (PHP 5/7/8)

  90: /**
  91:  * Yahoo! ウェブ検索WebサービスAPIが出力するXMLから
  92:  * タイトルを配列に格納する
  93:  * @param   array $items  情報を格納する配列
  94:  * @param   string $xml   XMLファイル名
  95:  * @return  int 情報の数/FALSE
  96: */
  97: function getTitles5(&$items, $xml) {
  98:     $name = 'Title';
  99: 
 100:     if (($ResultSet = @simplexml_load_file($xml)) == FALSEreturn FALSE;
 101:     //検索結果取りだし
 102:     $cnt = 1;
 103:     foreach ($ResultSet->Result as $element) {
 104:         if (isset($element->$name)) {
 105:             $items[$cnt] = $element->$name;
 106:         }
 107:         $cnt++;
 108:     }
 109: 
 110:     return ($cnt - 1);
 111: }

次に PHP5 用のプログラムをつくる。基本構造はPHP4用と同じだが、DOM XML の代わりに SimpleXML を利用することにする。

SimpleXML はPHP5で導入された関数群で、DOMやXMLパーサ(SAX)に比べて短いコードでXML処理を行うことができるのが特長だ。
たとえば、今回のXMLファイルの <Title> の内容を参照したければ、
  $ResultSet->Result->Title
と記述するだけでよい。

ユーザー関数 read_xml は不要である。
関数  simplexml_load_file  を利用し、一気にXMLファイルを読み込む。

次に、ResultSet->Result まで一気に下り、複数ある <Title> の内容を配列変数 $items に逐次格納していく。

これ以外の部分はPHP4用と共通である。

サンプル・プログラム(PHP4/5/7/8)

解説:バージョン検査

  79: /**
  80:  * PHP5以上かどうか検査する.
  81:  * @return  bool TRUE:PHP5以上/FALSE:PHP5未満
  82: */
  83: function isphp5over() {
  84:     $version = explode('.', phpversion());
  85: 
  86:     return $version[0>5 ? TRUE : FALSE;
  87: }

最後に、PHP4用とPHP5用プログラムを1本化する。
関数  phpversion  を利用し、PHP5以上かどうかを判定する関数 isphp5over を用意する。

解説:read_xml(PHP4/5/7/8)

ユーザー関数 read_xml だが、PHP5以上の場合には常にNULLを返すようにする。

  89: /**
  90:  * 指定XMLファイルを読み込んでDOMを返す.
  91:  * @param   string $xml XMLファイル名
  92:  * @return  object DOMオブジェクト/NULL 失敗
  93: */
  94: function read_xml($xml) {
  95:     if (isphp5over())   return NULL;
  96:     if (($fp = fopen($xml, "r")) == FALSE)  return NULL;
  97: 
  98:     //いったん変数に読み込む
  99:     $str = fgets($fp);
 100:     $str = preg_replace("/UTF-8/", "utf-8", $str);      //コーディング文字の置換
 101: 
 102:     while (! feof($fp)) {
 103:         $str = $str . fgets($fp);
 104:     }
 105:     fclose($fp);
 106: 
 107:     //DOMを返す
 108:     $dom = domxml_open_mem($str);
 109:     if ($dom == NULL) {
 110:         echo "\n>Error while parsing the document - " . $xml . "\n";
 111:         exit(1);
 112:     }
 113: 
 114:     return $dom;
 115: }

解説:getTitles(PHP4/5/7/8)

ユーザー関数 getTitles だが、PHP4とPHP5以上の場合で処理を分けている。

 117: /**
 118:  * 指定したXMLファイルを読み込んで,タグ名を要素名にした連想配列に読み込む.
 119:  * XMLファイルはYahoo! ウェブ検索WebサービスAPIが出力するもの.
 120:  * @param   array $items  情報を格納する配列
 121:  * @param   string $xml   XMLファイル名
 122:  * @return  int 情報の数/FALSE
 123: */
 124: function getTitles(&$items, $xml) {
 125:     $name = 'Title';
 126: 
 127: //PHP4用; DOM XML利用
 128:     if (isphp5over() == FALSE) {
 129:         if (($dom = read_xml($xml)) == NULL)    return FALSE;
 130:         $resultset = $dom->get_elements_by_tagname('ResultSet');
 131:         $results   = $resultset[0]->get_elements_by_tagname('Result');
 132:         //検索結果取りだし
 133:         $cnt = 1;
 134:         foreach ($results as $element) {
 135:             $node = $element->get_elements_by_tagname($name);
 136:             if ($node !NULL) {
 137:                 $items[$cnt] = $node[0]->get_content();
 138:             }
 139:             $cnt++;
 140:         }
 141: 
 142: //PHP5/7/8用; SimpleXML利用
 143:     } else {
 144:         if (($ResultSet = @simplexml_load_file($xml)) == FALSEreturn FALSE;
 145:         //検索結果取りだし
 146:         $cnt = 1;
 147:         foreach ($ResultSet->Result as $element) {
 148:             if (isset($element->$name)) {
 149:                 $items[$cnt] = $element->$name;
 150:             }
 151:             $cnt++;
 152:         }
 153:     }
 154: 
 155:     return ($cnt - 1);
 156: }

参考サイト

(この項おわり)
header