商品検索がエラーの時に再試行することで、キーワードによる商品検索をして、「アマゾン商品の検索結果」を取得する

「アマゾン商品の検索結果」を取得する時に、エラーが発生した場合、再試行する処理について、PHPソースコードを解説しています。

for文のループ処理によって、指定された回数、再試行の処理を行います。

再試行を行なって正常に処理できた場合、「アマゾン商品の検索結果」を取得します。

再試行を行なっても、正常に「アマゾン商品の検索結果」を取得できなかった場合は、エラーを通知します。

商品検索がエラーの時に再試行することで、キーワードによる商品検索をして、「アマゾン商品の検索結果」を取得する

$lastHttpRequestException;
for($i = 0; $i < $retryCount; $i ++) { // 例:リトライ回数:2回 //TODO 設定画面で指定する
	if ($i >= 1) {
		sleep ( 1 ); // 再試行の待ち時間(1秒)
	}

	try {
		/*
		 * ここでSDKあり・なしの分岐処理をするかもしれない。
		 * SDKAmazonRequest::requestSearchIndex()
		 */

		$searchItemsResponse = SearchIndexRequest::request ( $partnerTag, $keyword, $searchIndex, $resources, $hostname, $accessKey, $secretKey, $regionName );

		return $searchItemsResponse;
	} catch ( HttpRequestException $ex ) {
		$lastHttpRequestException = $ex;
	}
}

throw $lastHttpRequestException;

上記のソースコードでは、SearchIndexRequest::request()関数によって、「アマゾン商品の検索結果」を取得します。

「アマゾン商品の検索結果」を取得する時に、エラーが起きた場合、再試行します。

for文のループ処理によって、再試行の処理を行う

for($i = 0; $i < $retryCount; $i ++)

「アマゾン商品の検索結果」を取得する時にエラーが起きた場合、

上記のfor文によって、指定された回数だけ、「アマゾン商品の検索結果」を取得する処理を再試行します。

再試行する回数は、$retryCount変数の値です。

細かく言うと、再試行する回数は($retryCount変数の値 - 1 )です。
for文のループ処理の1回目は、再試行ではないからです。

再試行の処理を行う場合は、1秒間待ってから行う

if ($i >= 1) {
	sleep ( 1 ); // 再試行の待ち時間(1秒)
}

1回以上の再試行を行う場合は、1秒間待ってから行います。

上記ソースコードのsleep()関数によって、1秒間待ってから処理を続けます。

PHPのsleep()関数は、指定された秒数の間、現在のPHPスクリプトの実行を遅らせるために使用されます。

「アマゾン商品の検索結果」を取得する処理を、1秒間待ってから再開する理由は、
Amazon Product Advertising APIを、短時間に連続して呼び出さないようにするためです。

API呼び出しでエラーが起きても、
1秒後なら、API呼び出しが正常に終了するかもしれない、と期待しています。

Amazon Product Advertising API には、短時間に実行できるリクエストの数に制限があります。
リクエストが多すぎる場合、API はエラー メッセージ「TooManyRequests」を返します。

sleep()関数によって、1秒間待ってからAPIをリクエストすることで、エラー発生を防ぐことを試みます。

再試行してエラーが起きた場合、最後に起きたエラーを通知する

$lastHttpRequestException;
for($i = 0; $i < $retryCount; $i ++) { // 例:リトライ回数:2回 //TODO 設定画面で指定する

中略

	try {
		$searchItemsResponse = SearchIndexRequest::request ( $partnerTag, $keyword, $searchIndex, $resources, $hostname, $accessKey, $secretKey, $regionName );

		return $searchItemsResponse;
	} catch ( HttpRequestException $ex ) {
		$lastHttpRequestException = $ex;
	}
}

throw $lastHttpRequestException;

SearchIndexRequest::request()関数の処理が正常に終了した場合、
「アマゾン商品の検索結果」が$searchItemsResponse変数に代入されます。

そしてreturn文によって、$searchItemsResponse変数が保持する「アマゾン商品の検索結果」が返されます。

return文が実行されることで、再試行の処理を行うfor文のループ処理が終了します。

SearchIndexRequest::request()関数の処理で、HTTPリクエスト関連のエラーが起きた場合、
catch文の処理が実行されます。

$lastHttpRequestException変数に、$ex変数が保持する「HTTPリクエスト例外の内容」が代入されます。

例外の内容が代入されることより、最後に発生したエラー情報を保持することになります。

それ以前に発生したエラー情報は、代入することにより上書きされます

指定された回数($retryCount変数の値 - 1 )だけ再試行を行なっても、エラーが発生し続けた場合、

for文のループを終了後、最後に発生したHTTPリクエスト例外が、throw文によって通知されます。

最後に発生した例外ということを示したいから、lastという英単語を使いました

$lastHttpRequestException変数については、
最後に発生した例外だけを保持している、ということを示そうと思いました。

なので、lastという英単語を使ってみました。

例えば、再試行を3回実行して、1回目から3回目までのすべての再試行でエラーが起きた場合、
$lastHttpRequestException変数は、3回目に発生した例外だけを保持しています。

lastという英単語を使うことで、上記の説明内容を表現したかったです。
だけど、他のプログラマーに対して上記の説明内容が伝わったかどうかは、わかりません。

コメント文をなるべく書かないソースコードを、目指しました

プログラムのソースコードでは、

  • コメント文をなるべく書かない。
  • 英語文のようなソースコードで、プログラム処理の内容を表したい。

そんなことを目指してソースコードを書いたのですが、かなり難しいです。

ちなみに、コメント文をなるべく書きたくないのは、コメント文の内容がソースコードの内容と違う状態になったりするからです。

仕様変更などでソースコードを修正した際、コメント文の内容を修正し忘れることがあります。

古い仕様を表しているコメント文は、他のプログラマーを迷わせることになります。
コメント文とソースコード、どっちが正しいか?迷います。

キーワードによる商品検索をして、「アマゾン商品の検索結果」を取得するのに必要な各情報を、用意する

public static function requestSearchIndex(URLInfo $urlInfo, CommonRESTParameter $commonParameter, RESTParameter $restParameter, int $itemCount, $retryCount = 1) {

中略

	$partnerTag = $commonParameter->getAssociateTag ();
	$keyword = $restParameter->getKeyword ();
	$searchIndex = $restParameter->getSearchIndex ();
	$resources = $restParameter->getSearchItemsResources ();
	$hostname = $urlInfo->getHostname ();
	$accessKey = $commonParameter->getAccessKey ();
	$secretKey = $commonParameter->getSecretKey ();
	$regionName = $commonParameter->getRegion ();

以下省略

	$searchItemsResponse = SearchIndexRequest::request ( $partnerTag, $keyword, $searchIndex, $resources, $hostname, $accessKey, $secretKey, $regionName );

以下省略

SearchIndexRequest::request()関数は、
Amazon Product Advertising APIを使って、キーワードで商品検索して、「アマゾン商品の検索結果」を取得します。

この関数を実行するために、以下の情報を用意します。

  • Product Advertising API ホスト($hostname変数)
  • Product Advertising API アクセスキー($accessKey変数)
  • Product Advertising API アソシエイトタグ($partnerTag変数)
  • Product Advertising API シークレットキー($secretKey変数)
  • Product Advertising API リージョン($regionName変数)
  • SearchItems の Resources Parameter($resources変数)

これらの情報は、「アマゾンの設定」で設定します。

※関連記事:アフィリエイト商品表示・WordPressプラグイン、アフィリエイトの設定、アマゾンの設定

なお、アソシエイトタグの変数名が$partnerTagになっているのは、参考にしたAmazon Product Advertising APIのサンプルコードにおいて、$partnerTagと書かれていたためです。

SearchIndexRequest::request()関数を実行するために、さらに以下の情報を用意します。

  • SearchItemsオペレーションの検索インデックス($searchIndex変数)
  • 商品検索で使うキーワード($keyword変数)

これらの情報は、当プラグインのショートコードの属性で設定します。

※関連記事:アフィリエイト商品表示・WordPressプラグイン、ショートコードの属性

「表示する商品数」が0件の場合、「アマゾン商品の検索結果」が存在しないことを示すNULL値を、return文で返す

public static function requestSearchIndex(URLInfo $urlInfo, CommonRESTParameter $commonParameter, RESTParameter $restParameter, int $itemCount, $retryCount = 1) {

	if ($itemCount <= 0) {
		return NULL; // 表示する件数が0件なので、商品情報なしとする。
	}

以下省略

当プラグインのショートコードにおいて、「表示する商品数」属性の値が0の場合、
$itemCount変数の値は、0になります。

※関連記事:アフィリエイト商品表示・WordPressプラグイン、ショートコードの属性

「表示する商品数」が0件なので、アマゾンの「アフィリエイト商品のHTML」を表示しません。

つまり、「アマゾン商品の検索結果」を取得しなくて良いです。

よって、「アマゾン商品の検索結果」が存在しないことを示すNULL値を、return文で返します。

AmazonRequest.phpのソースコード

<?php

namespace goodsmemo\amazon;

use goodsmemo\amazon\CommonRESTParameter;
use goodsmemo\amazon\RESTParameter;
use goodsmemo\amazon\withoutsdk\SearchIndexRequest;
use goodsmemo\network\URLInfo;
use goodsmemo\exception\HttpRequestException;

require_once GOODS_MEMO_DIR . "amazon/CommonRESTParameter.php";
require_once GOODS_MEMO_DIR . "amazon/RESTParameter.php";
require_once GOODS_MEMO_DIR . "amazon/withoutsdk/SearchIndexRequest.php";
require_once GOODS_MEMO_DIR . "network/URLInfo.php";
require_once GOODS_MEMO_DIR . "exception/HttpRequestException.php";

class AmazonRequest {

	public static function requestSearchIndex(URLInfo $urlInfo, CommonRESTParameter $commonParameter, RESTParameter $restParameter, int $itemCount, $retryCount = 1) {

		if ($itemCount <= 0) {
			return NULL; // 表示する件数が0件なので、商品情報なしとする。
		}

		$partnerTag = $commonParameter->getAssociateTag ();
		$keyword = $restParameter->getKeyword ();
		$searchIndex = $restParameter->getSearchIndex ();
		$resources = $restParameter->getSearchItemsResources ();
		$hostname = $urlInfo->getHostname ();
		$accessKey = $commonParameter->getAccessKey ();
		$secretKey = $commonParameter->getSecretKey ();
		$regionName = $commonParameter->getRegion ();

		$lastHttpRequestException;
		for($i = 0; $i < $retryCount; $i ++) { // 例:リトライ回数:2回 //TODO 設定画面で指定する
			if ($i >= 1) {
				sleep ( 1 ); // 再試行の待ち時間(1秒)
			}

			try {
				/*
				 * ここでSDKあり・なしの分岐処理をするかもしれない。
				 * SDKAmazonRequest::requestSearchIndex()
				 */

				$searchItemsResponse = SearchIndexRequest::request ( $partnerTag, $keyword, $searchIndex, $resources, $hostname, $accessKey, $secretKey, $regionName );

				return $searchItemsResponse;
			} catch ( HttpRequestException $ex ) {
				$lastHttpRequestException = $ex;
			}
		}

		throw $lastHttpRequestException;
	}
}