解説コードを読んでみよう
このサンプルでわかること
- 外部の API に、PHP からリクエストを送る流れ
- 秘密のキーで「署名」を作り、本人からのリクエストだと証明するしくみ
- 返ってきた XML を
simplexmlで読み取る方法
このコードの API(AWSECommerceService)は提供終了しているため、そのまま動かしても結果は返りません。今から Amazon の商品データを使う場合は、Amazon アソシエイトの管理画面で案内されている新しい API(Creators API)のドキュメントを参照してください。
使っているファイル
- search.phpAPI にリクエストを送り、結果を表示するスクリプト(キーはダミーに置き換え済み)
1. 認証情報と検索条件を用意する
API を使うには、Amazon から発行される「アクセスキー」「シークレットキー」「アソシエイトタグ」が必要でした。ここではダミーの文字に置き換えています。
検索の条件は、$params という配列にまとめます。Operation が「何をしたいか」(ItemSearch = 検索)、Keywords が検索語、SearchIndex が検索するカテゴリー(Books = 本)です。
// 1. 認証情報と API の URL
define("ACCESS_KEY_ID" , '********');
define("SECRET_ACCESS_KEY" , '********');
define("ASSOCIATE_TAG" , 'YOUR_ASSOCIATE_TAG');
define("ACCESS_URL" , 'https://aws.amazonaws.jp/onca/xml');
// 2. 検索の条件(パラメーター)
$params = array();
$params['AssociateTag'] = ASSOCIATE_TAG;
$params['Service'] = 'AWSECommerceService';
$params['Operation'] = 'ItemSearch';
$params['Keywords'] ='tofle';
$params['SearchIndex'] = 'Books';
$params['ResponseGroup'] = 'ItemAttributes,Offers,Images,Reviews';
$params['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
本物のキーは、絶対にコードに直接書いて公開しないでください。他人に使われてしまいます。実際のサイトでは、公開フォルダの外にある設定ファイルや環境変数に書きます。
2. 署名(シグネチャ)を作る
「このリクエストは確かにキーの持ち主から来た」ことを証明するために、決まった手順で文字列を作り、シークレットキーで暗号化します。これを署名といいます。
- パラメーターを名前の順に並べる(
ksort) - 「名前=値」を
&でつないだ文字列を作る(値は URL エンコード) - 「GET」「ホスト名」「パス」「2の文字列」を改行でつなぐ
- 3を、シークレットキーを使って
hash_hmac('sha256', ...)で暗号化し、Base64 に変換する
// 3. パラメーターを名前順に並べる(署名の決まり)
ksort($params);
function urlencode_RFC3986($str) {
return str_replace('%7E', '~', rawurlencode($str));
}
$canonical_string = 'AWSAccessKeyId='.ACCESS_KEY_ID;
foreach ($params as $k => $v) {
$canonical_string .= '&'.urlencode_RFC3986($k).'='.urlencode_RFC3986($v);
}
// 4. 署名を作る(秘密キーで HMAC-SHA256 → Base64)
$parsed_url = parse_url(ACCESS_URL);
$string_to_sign = "GET\n{$parsed_url['host']}\n{$parsed_url['path']}\n{$canonical_string}";
$signature = base64_encode(
hash_hmac('sha256', $string_to_sign, SECRET_ACCESS_KEY, true)
);
受け取った側(Amazon)も、同じ手順で同じ文字列を作り、自分が持っているシークレットキーで署名を計算します。結果が一致すれば「本人からのリクエストで、途中で書き換えられていない」と判断できます。シークレットキーそのものは送らないのがポイントです。
3. API にアクセスして XML を受け取る
署名をつけた URL を file_get_contents() で開くと、結果が XML という形式の文字列で返ってきます。simplexml_load_string() で、PHP から扱いやすい形に変換します。
// 5. 署名をつけた URL にアクセスして、XML を受け取る
$url = ACCESS_URL.'?'.$canonical_string.'&Signature='.urlencode_RFC3986($signature);
// 注意: この旧 Product Advertising API (AWSECommerceService) は Amazon 側で提供終了しているため、現在はデータを取得できません。
$response = @file_get_contents($url); //Amazonへレスポンス
$parsed_xml = null;
// レスポンスを配列で取得
if (is_string($response) && $response !== '') {
$parsed_xml = @simplexml_load_string($response) ?: null;
}
4. XML から必要な情報を取り出す
XML は入れ子の構造になっていて、-> でたどっていきます。$parsed_xml->Items->Item が本の一覧なので、foreach で1冊ずつ取り出します。
// 6. 結果の XML から、本のタイトルなどを取り出す
if ($response && isset($parsed_xml) && empty($parsed_xml->faultstring) && empty($parsed_xml->Items->Request->Errors)) {
$data=[];
$i = 0;
foreach ($parsed_xml->Items->Item as $current) {
$data[$i]['title'] = $current->ItemAttributes->Title;
$data[$i]['author'] = $current->ItemAttributes->Author;
$data[$i]['manufacturer'] = $current->ItemAttributes->Manufacturer;
$data[$i]['imgURL'] = $current->LargeImage->URL;
$data[$i]['bookURL'] = $current->DetailPageURL;
$data[$i]['releaseDate'] = $current->ItemAttributes->ReleaseDate;
// 著者が複数いる場合(PHP8.3対応: 未定義の $author を count() すると TypeError になっていたため修正)
$author = [];
foreach ($current->ItemAttributes->Author as $a) { $author[] = (string)$a; }
$data[$i]['authors'] = implode(',', $author);
$i++;
}
}
PHP 8.3 で書き直したポイント
- 旧版では、未定義の変数
$authorをcount()に渡していました。PHP 8 からは配列以外をcount()に渡すとエラーで止まるため、XML から著者を取り出して配列にするよう直しました。 - API から何も返ってこないとき(
false)に XML として読もうとしないよう、事前に確認するようにしました。
天気・地図・翻訳など、無料で試せる API はたくさんあります。「キーを付けてリクエスト → JSON や XML を受け取る → 必要な部分を取り出す」という流れは、どの API でもほぼ同じです。