ウエブAPIを用いた初めてのCGI (PHP編) プログラム演習4

今回はPHP言語で前回と同様の仕様でサイトを開発してみましょう。主に処理は前回同様

  • HTTPで接続してXMLを取得する部分
  • XMLを解析して必要なデータを取り出す部分

に分かれます。

本題に入る前に少し脱線して、PHPでライブラリを使うための準備の解説をします。PHP5では標準でさまざまなライブラリが組み込まれた形で提供されている場合が多いです(環境によってことなるので一律ではない。自前でコンパイルしてPHPを作った場合はその時点で任意に決定できる)。自分のPHP環境でどのようなライブラリが利用できるかを調べる場合は

bash-3.2$ cat phpinfo.php 
<?php
    phpinfo();
?>

というファイルを作成し、これにアクセスしてみてください。

PHP環境変数。組み込み済みモジュール一覧などさまざまな情報が確認できます。

↑例えばSimpleXMLライブラリがインストール済みであることが分かる
よく使うライブラリはmbstring, simpleXML, curl, mysqli などがあります。
また、後から拡張可能なモジュールとして pear, peclなどが利用できます. pearで利用したいパッケージがある場合は, pearコマンドを利用してインストールします。

(MAMP環境でのpearインストールの例)
bash-3.2$ sudo /Applications/MAMP/bin/php5/bin/pear install HTTP_Client
Password:
WARNING: channel "pear.php.net" has updated its protocols, use "channel-update pear.php.net" to update
WARNING: "pear/Net_URL" is deprecated in favor of "pear/Net_URL2"
downloading HTTP_Client-1.2.1.tgz ...
Starting to download HTTP_Client-1.2.1.tgz (10,202 bytes)
.....done: 10,202 bytes
downloading HTTP_Request-1.4.3.tgz ...
Starting to download HTTP_Request-1.4.3.tgz (16,791 bytes)
...done: 16,791 bytes
downloading Net_URL-1.0.15.tgz ...
Starting to download Net_URL-1.0.15.tgz (6,303 bytes)
...done: 6,303 bytes
downloading Net_Socket-1.0.9.tgz ...
Starting to download Net_Socket-1.0.9.tgz (5,173 bytes)
...done: 5,173 bytes
install ok: channel://pear.php.net/Net_URL-1.0.15
install ok: channel://pear.php.net/Net_Socket-1.0.9
install ok: channel://pear.php.net/HTTP_Request-1.4.3
install ok: channel://pear.php.net/HTTP_Client-1.2.1

XML_Serializerも使うのであわせてインストールしてください。ただし、XML_Serializerは現在betaパッケージなのでこの場合は

bash-3.2$ sudo /Applications/MAMP/bin/php5/bin/pear install XML_Serializer-beta

と -betaを最後につけます。最終的にインストール一覧を確認したい場合は pear listコマンドで

bash-3.2$ /Applications/MAMP/bin/php5/bin/pear list
Installed packages, channel pear.php.net:
=========================================
Package          Version State
Archive_Tar      1.3.2   stable
Console_Getopt   1.2.3   stable
HTTP_Client      1.2.1   stable
HTTP_Request     1.4.3   stable
Net_Socket       1.0.9   stable
Net_URL          1.0.15  stable
PEAR             1.6.1   stable
Structures_Graph 1.0.2   stable
XML_Parser       1.3.1   stable
XML_Serializer   0.19.1  beta
XML_Util         1.2.0   stable

確認できます。実際のインストール場所は phpinfo()で確認できる include_path のフォルダ以下にインストールされ require_onceでPHPプログラムから呼び出されます。

bash-3.2$ cat index.php 
<?php
    require_once('HTTP/Client.php');
    require_once('XML/Unserializer.php');

    $SEARCH_API_HOST  = 'search.yahooapis.jp';
    $SEARCH_API_URL   = '/WebSearchService/V1/webSearch';
    $SEARCH_API_APPID = 'p3O1lFOxg66up7VWRDmVkEJnF6baGNJj38rT6gUY3DzyF.kX4_PepRsfnk1AYss1J.YG';

    error_log('script start');
print<<<EOM
<html>
<head>
<title>Sample Program 4 php-1</title>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8">
</head>
<body>
EOM;
    print '<form action="./'. basename($_SERVER['SCRIPT_NAME']) .'">';
    print '<input type="text" name="query" value="' . $_GET['query'] . '">';
    print '<input type="submit" value="search">';
    print '</form>';

    $responseXML = '';
    # HTTPでXMLを取得する (socket関数を利用)
    if(0) {
        $socket = fsockopen($SEARCH_API_HOST, 80, $errno, $errstr, 30);
        $request = "GET ${SEARCH_API_URL}?appid=${SEARCH_API_APPID}&query=".urlencode($_GET['query']) . " HTTP/1.0\r\n\r\n";
        fwrite($socket, $request);
        $response = '';
        while($out = fgets($socket, 1024)) {
            $response .= $out;
        }
        fclose($socket);
        $tmp = explode("\r\n\r\n", $response, 2);
        $responseXML = $tmp[1];
    }
    # HTTPでXMLを取得する (HTTP_Clientライブラリを利用)
    if(0) {
        $client = new HTTP_Client();
        $status = $client->get("http://${SEARCH_API_HOST}${SEARCH_API_URL}?appid=${SEARCH_API_APPID}&query=".urlencode($_GET['query']));
        $response = $client->currentResponse();
        $responseXML = $response['body'];
    }
    # HTTPでXMLを取得する (curlライブラリを利用)
    if(1) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, "http://${SEARCH_API_HOST}${SEARCH_API_URL}?appid=${SEARCH_API_APPID}&query=".urlencode($_GET['query']));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $responseXML = curl_exec($curl);
        curl_close($curl);
    }
    # HTTPでXMLを取得する (file_get_contents関数を利用)
    if(0) {
        $responseXML = file_get_contents("http://${SEARCH_API_HOST}${SEARCH_API_URL}?appid=${SEARCH_API_APPID}&query=".urlencode($_GET['query']));
        print_r($response);
    }

    # XMLの解析と出力 ( XML_Unserializerを利用 )
    if(0) {
        $unserializer = new XML_Unserializer(array(
                                                XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => 'parseAttributes'
                                                ));
        $unserializer->unserialize($responseXML);
        $hashData = $unserializer->getUnserializedData();
        foreach($hashData['Result'] as $site) {
            print("<li><a href=\"{$site['ClickUrl']}\">{$site['Title']}</a>\n");
        }

    }

    # XMLの解析と出力 ( simpleXML を利用 )
    if(1) {
        $xmlObj = simplexml_load_string($responseXML); // simplexml_load_file で一気に取得も可能
        foreach($xmlObj->{'Result'} as $site) {
            print("<li><a href=\"{$site->{'ClickUrl'}}\">{$site->{'Title'}}</a>\n");
        }
    }

print<<<EOM
</body>
</html>
EOM;
    error_log('query=' . $_GET['query']);
    error_log('script end');

    exit;

?>

さまざまな方法で実現可能なことが分かると思います。 file_get_contents および simplexml_load_fileは簡単に利用でき便利ですが、この方法は一部の環境でセキュリティの観点から利用できません(php.ini で allow_url_fopen=Offと設定されている)。私の主観ですが curl + simpleXMLを利用するのがオススメです。 XML_UnserializerでXMLをハッシュ型変数にする方が後の処理が簡易になるのでこちらも一般的ですが、pearでのインストールが必要な点とパフォーマンスの点でsimpleXMLの方が有利です。用途に応じて使い分けるのが良いと思います。

【演習】
上のプログラムは随所でエラー処理を省略しています。たとえば

  • キーワードが入力されていない状態でのアクセス時にもHTTP接続してしまう
  • ウエブサーバにアクセスできない場合のエラー処理
  • 応答したXMLが不完全で、解析処理が失敗した場合
  • 検索結果が0件の場合、(検索結果が1件の場合もこのままでは不完全な場所があります)

これらのケースに適切な処理に変更して完全なものにしてみましょう。

【演習】
10件目以降も表示できるように拡張しましょう。その際にpearPagerというライブラリがあります。これを用いると、簡単に検索結果下部にある、ページ切り替えのモジュールを実現することが可能です。pear利用ドキュメントもしくは利用を解説しているウエブページなどを調べて実現させてみましょう。