ショートコードの属性に対応する「アフィリエイト商品のHTML」を作成する

アフィリエイト商品表示・WordPressプラグインのショートコードにおいて、service属性を指定できます。

当プラグインは、そのservice属性に対応する「アフィリエイト商品のHTML」を作成します。

service属性の各場合に分けて、「アフィリエイト商品のHTML」を作成する処理について、PHPソースコードを解説しています。

service属性は、以下の二種類に対応しています。

  1. アマゾン
  2. 楽天市場

「アフィリエイト商品のHTML」は、当プラグインのショートコードが書かれている部分に表示されます。

ショートコードのservice属性に対応する「アフィリエイト商品のHTML」を作成する

$affiliateHTML;
switch ($service) {
	case "amazon" :

		$affiliateHTML = AmazonAffiliate::makeHTML ( $shortcodeAttribute );
		break;

	case "rakuten" :

		$affiliateHTML = RakutenAffiliate::makeHTML ( $shortcodeAttribute );
		break;

	default :

		throw new IllegalArgumentException ( "無効なサービス名:" . $service );
}

ショートコードのservice属性に対応する、「アフィリエイト商品のHTML」を作成します。

service属性は、以下の二種類です。

  1. "amazon"は、アマゾンを意味します。
  2. "rakuten"は、楽天市場を意味します。

例えば、楽天市場のアフィリエイト商品を以下のように表示するHTMLを、作成します。

楽天市場のアフィリエイト商品の表示例
楽天市場のアフィリエイト商品の表示例

ショートコードのservice属性が無効な場合、エラーにする

if文やswitch文では、その他の条件、つまり仕様以外の条件が成り立った場合は、エラーにすると良いです。
エラーメッセージを見ることで、プログラムの不具合を修正しやすいからです。

※関連記事:Javaソースコードの条件分岐、プログラムで実行されない「その他の場合」のif文

上記のswitch文のdefault文では、無効なサービス名を示すIllegalArgumentExceptionという例外を、通知しています。

クラスを使えるようにする、use文とrequire_once文

use goodsmemo\shortcode\ShortcodeAttributeUtils;

require_once GOODS_MEMO_DIR . "shortcode/ShortcodeAttributeUtils.php";

例えばPHPソースコードにて、ShortcodeAttributeUtilsクラスを使いたい場合、

上記ソースコードのようにuse文とrequire_once文を用いて、使えるようにします。

最初、use文とrequire_once文の両方を使う意味が、わかりませんでした…

最初にuse文を知った時、Javaのimport文みたいだと思いました。

Javaのimport文の例

import goodsmemo.shortcode.ShortcodeAttributeUtils;

なので、use文だけ記述しておけば大丈夫だと思っていました。

ですがPHPでは、require_once文も必要みたいです。
最初、この事がわかりませんでした。

  1. use文で、クラスの名前空間を読み込む。
  2. require_once文で、クラスの外部ファイルを読み込む。

どうやらこの二つの処理が、必要ということです。

ちなみにuse文では、別名を指定します。

use クラス名 as 別名;

なお、別名を省略した場合は、クラス名と別名が同じとなります。

use goodsmemo\shortcode\ShortcodeAttributeUtils;
これは
use goodsmemo\shortcode\ShortcodeAttributeUtils as ShortcodeAttributeUtils;
と同じです。

require_once文を不要にできる、オートローディング

PHP言語のオートローディングという仕組みを使うと、require_once文を不要にできるようです。

オートローディングとは、クラスやインターフェイスなどのコードを必要に応じて自動的に読み込む仕組みです。

だけど、オートローディングの仕組みを有効にするには、ひと手間かかるようです。

PHPでは、spl_autoload_register()という関数でオートローダーを登録することができます。

オートローダーは、未定義のクラスやインターフェイスが使用された時に呼び出されるコールバック関数です。

コールバック関数は、引数にクラス名やインターフェース名を受け取り、その名前に対応するファイルを、requireやincludeなどで読み込む処理を記述します。

spl_autoload_register(
  ?callable $callback = null, 
  bool $throw = true, 
  bool $prepend = false): bool

第一引数には、登録したいコールバック関数を指定します。
nullを指定した場合は、デフォルトのspl_autoload関数が登録されます。

第二引数には、コールバック関数の登録に失敗した時に、例外をスローするかどうかを指定します。
デフォルトはtrueです。

第三引数には、コールバック関数をキューの先頭に追加するかどうかを指定します。
デフォルトはfalseで、キューの最後に追加されます。

例えば、以下のようなコードでオートローディングを実現できます。

function my_autoloader($class) {
    // クラス名と同じファイル名を探す
    $file = __DIR__ . "/$class.php";

    // ファイルが存在すれば読み込む
    if (file_exists($file)) {
        require $file;
    }
}

// my_autoloader関数をオートローディングに登録する
spl_autoload_register('my_autoloader');

// 未定義のクラスFooを呼び出す
$foo = new Foo();

なお、当プラグインでは、オートローディングという仕組みを使っていません。
当プラグインを開発していた当時、オートローディングについて知りませんでした。

すでに、当プラグインの全てのPHPソースコードにrequire_once文を書いているので、今後もオートローディングを使う予定はありません。

ショートコードに指定した属性に対応する変数を作る

/*
 * ショートコードの名前は英小文字、数字、下線を使う必要があります。
 * 特にハイフン(ダッシュ)には注意して、使わないのが賢明です。
 * 注意: 属性名は大文字と小文字が混在可能ですが、パース後はいつも小文字になります。
 */
$attsMap = shortcode_atts ( array ( // 変数名(属性名) => 初期値
		"service" => "",
		"operation" => "",
		"search_index" => "",
		"keyword" => "",
		"number" => "",
		"item_title_length" => "",
		"item_review_length" => ""
), $atts );
extract ( $attsMap ); // 例:変数 $service などを作成する

shortcode_atts()関数により、ショートコードに指定した属性が変数$attsMapに保存されます。

extract()関数は、連想配列から変数を作成します。
連想配列の$attsMap変数より、ショートコードに指定した属性に対応する各変数が作られます。

例えば、以下のショートコードの場合、

[goodsmemo_affiliate service="amazon" keyword="WordPress 開発" number="1"]

作られる各変数は、以下のようになります。

$serviceの中身は、"amazon"です。
$keywordの中身は、"WordPress 開発"です。
$numberの中身は、"1"です。

以下の作られた変数には、初期値の空文字が代入されています。
$operationの中身は、""です。
$search_indexの中身は、""です。
$item_title_lengthの中身は、""です。
$item_review_lengthの中身は、""です。

ソースコードを見ただけでは、extract()関数が何をしているのか、予想できませんでした

ショートコードの属性を処理するプログラムコードは、ネット上で見つけたサンプルコードを真似しました。

最初にextract()関数を見た時、一体何をしているのか、わかりませんでした。

このextract()関数を調べて、連想配列から変数を作成する関数だと初めて知りました。

変数を勝手に作る関数があるなんて、と少しびっくりしました。

参考に、英単語のextractについて意味を調べました。
抜き出す、抽出する、という意味でした。

私には、何かを抽出する関数が、自動的に変数を作り出すとは予想できなかったです。
なのでextract()関数については、コメント文が必須となりそうです。

コメント文が必須ということより、

  • ソースコードに、なるべくコメント文を書きたくない。
  • ソースコードにおいて、まるで英語の文書のようにソースコードを記述して、処理の内容を示したい。

このように思っているプログラマーにとっては、extract()関数の名称は、注意が必要だと思います。

extract()関数について、試しに、わかりやすい関数名を考えてみました

extract()関数は、連想配列から変数を作成する関数ということより、試しに、わかりやすい関数名を考えてみました。

まずは単純に、

連想配列からローカル変数を作成する。

この日本語の文を自動翻訳しました。

Create local variables from associative arrays.

この英文より、関数名を考えてみました。

createLocalVariablesFrom()関数。

上記の関数名を使った場合、
当プラグインのソースコードは、以下のようになります。

$attsMap = shortcode_atts ( array ( // 変数名(属性名) => 初期値
		"service" => "",
		"operation" => "",
		"search_index" => "",
		"keyword" => "",
		"number" => "",
		"item_title_length" => "",
		"item_review_length" => ""
), $atts );

createLocalVariablesFrom ( $attsMap ); // 例:変数 $service などを作成する

なんとなく、ソースコードの処理内容がわかりやすくなった気がします。

createの代わりに、expandを使ってみます

だけど、createという動詞を使っている関数が、戻り値を返していません。
この点が、ちょっと違和感があります。

別の動詞として、ある漫画を参考にして領域展開する、という動詞を考えてみます。

変数をローカル領域に展開する。

自動翻訳すると、

Expand variables to local area.

この英文より、関数名を考えてみました。

ExpandVariablesToLocalArea()関数。

createよりもExpandの方が、extract()関数のふるまいを表現しているかもしれません。

補足:

連想配列から作成した変数をローカル領域に展開する。
これを自動翻訳したら、

Expand variables created from associative arrays into the local area

になりました。

try文の中に記述する必要がないソースコードが、ありました

try {
	$shortcodeAttribute = ShortcodeAttributeUtils::makeShortcodeAttribute ( $operation, $search_index, $keyword, $number, $item_title_length, $item_review_length );

	if ($number == 0) {

		$message = <<< EOD
		<p class="gma-zero-ads-displayed-message">広告はありません(表示件数の設定{$number}件)。</p>
		EOD;
		return $message;
	}

以下省略

上記のif文について、

  • 例外を通知していない。
  • $shortcodeAttribute変数を使用していない。

以上のことより、try文の中に記述する必要はありませんでした。

以下のように、if文をtry文の前方に移動できます。

if ($number == 0) {

	$message = <<< EOD
	<p class="gma-zero-ads-displayed-message">広告はありません(表示件数の設定{$number}件)。</p>
	EOD;
	return $message;
}

try {
	$shortcodeAttribute = ShortcodeAttributeUtils::makeShortcodeAttribute ( $operation, $search_index, $keyword, $number, $item_title_length, $item_review_length );

以下省略

このプログラムコードを記述していた当時、気づかなかったようです。

当初、表示件数が0件の場合、エラーであると判断していました

当プラグインの利用者が、ショートコードにおいて表示件数を0件と指定した場合について、
初めの頃は「エラーである」と判断していました。

しかし途中から、「正常処理である」と判断しました。

当プラグインの利用者が、意図的に表示件数を0件に指定するのは正しい、と考え直したからです。

よって、if ($number == 0)というif文では、エラーを示す例外を通知することをやめました。

例外を通知することをやめたので、try文の中に記述する必要がなくなりました。

当プラグインで起きた全てのエラーを、受け取る

try {

アフィリエイト商品のHTMLを作成する処理

} catch ( IllegalArgumentException $ex ) {

	$message = '<p class="gma-error-message">引数の例外:' . $ex->getMessage () . '</p>';
	return $message;
} catch ( OptionException $ex ) {

	$message = '<p class="gma-error-message">オプションデータベースの例外:' . $ex->getMessage () . '</p>';
	return $message;
} catch ( HttpRequestException $ex ) {

	$message = '<p class="gma-error-message">HTTPリクエストの例外:' . $ex->getMessage () . '。コード:' . $ex->getCode () . '</p>';
	return $message;
} catch ( HttpResponseException $ex ) {

	$message = '<p class="gma-error-message">HTTPレスポンスの例外:' . $ex->getMessage () . '</p>';
	return $message;
} catch ( \Exception $ex ) {
	// \Exceptionをキャッチすれば、WordPressの「サイトに技術的な問題が発生しています。」を防げるかも??
	$message = '<p class="gma-error-message">例外:' . $ex->getMessage () . '</p>';
	return $message;
}

当プラグインの主な処理は、「アフィリエイト商品のHTML」を作成する処理です。

「アフィリエイト商品のHTML」を作成する処理で発生した全てのエラーは、上記のcatch文に例外として通知されます。

これらのcatch文において、エラーの種類に対応したエラーメッセージ文が作成されます。
このエラーメッセージ文は、当プラグインのショートコードが書かれてある部分に表示されます。

上記の複数のcatch文は、同じ場所にまとめられています。
なので、それぞれのエラーメッセージ文を一覧できます。

よって、当プラグインにはどんなエラーメッセージ文があるのか、わかりやすいです。

Shortcode.phpのソースコード

<?php

namespace goodsmemo\shortcode;

use goodsmemo\shortcode\ShortcodeAttributeUtils;
use goodsmemo\amazon\AmazonAffiliate;
use goodsmemo\rakuten\RakutenAffiliate;
use goodsmemo\exception\IllegalArgumentException;
use goodsmemo\exception\OptionException;
use goodsmemo\exception\HttpRequestException;
use goodsmemo\exception\HttpResponseException;

require_once GOODS_MEMO_DIR . "shortcode/ShortcodeAttributeUtils.php";
require_once GOODS_MEMO_DIR . "amazon/AmazonAffiliate.php";
require_once GOODS_MEMO_DIR . "rakuten/RakutenAffiliate.php";
require_once GOODS_MEMO_DIR . "exception/IllegalArgumentException.php";
require_once GOODS_MEMO_DIR . "exception/OptionException.php";
require_once GOODS_MEMO_DIR . "exception/HttpRequestException.php";
require_once GOODS_MEMO_DIR . "exception/HttpResponseException.php";

class Shortcode {

	public static function makeAffiliateHTML($atts, $content = null) {

		/*
		 * ショートコードの名前は英小文字、数字、下線を使う必要があります。
		 * 特にハイフン(ダッシュ)には注意して、使わないのが賢明です。
		 * 注意: 属性名は大文字と小文字が混在可能ですが、パース後はいつも小文字になります。
		 */
		$attsMap = shortcode_atts ( array ( // 変数名(属性名) => 初期値
				"service" => "",
				"operation" => "",
				"search_index" => "",
				"keyword" => "",
				"number" => "",
				"item_title_length" => "",
				"item_review_length" => ""
		), $atts );
		extract ( $attsMap ); // 例:変数 $service などを作成する

		try {
			$shortcodeAttribute = ShortcodeAttributeUtils::makeShortcodeAttribute ( $operation, $search_index, $keyword, $number, $item_title_length, $item_review_length );

			if ($number == 0) {

				$message = <<< EOD
				<p class="gma-zero-ads-displayed-message">広告はありません(表示件数の設定{$number}件)。</p>
				EOD;
				return $message;
			}

			$affiliateHTML;
			switch ($service) {
				case "amazon" :

					$affiliateHTML = AmazonAffiliate::makeHTML ( $shortcodeAttribute );
					break;

				case "rakuten" :

					$affiliateHTML = RakutenAffiliate::makeHTML ( $shortcodeAttribute );
					break;

				default :

					throw new IllegalArgumentException ( "無効なサービス名:" . $service );
			}

			return $affiliateHTML;
		} catch ( IllegalArgumentException $ex ) {

			$message = '<p class="gma-error-message">引数の例外:' . $ex->getMessage () . '</p>';
			return $message;
		} catch ( OptionException $ex ) {

			$message = '<p class="gma-error-message">オプションデータベースの例外:' . $ex->getMessage () . '</p>';
			return $message;
		} catch ( HttpRequestException $ex ) {

			$message = '<p class="gma-error-message">HTTPリクエストの例外:' . $ex->getMessage () . '。コード:' . $ex->getCode () . '</p>';
			return $message;
		} catch ( HttpResponseException $ex ) {

			$message = '<p class="gma-error-message">HTTPレスポンスの例外:' . $ex->getMessage () . '</p>';
			return $message;
		} catch ( \Exception $ex ) {
			// \Exceptionをキャッチすれば、WordPressの「サイトに技術的な問題が発生しています。」を防げるかも??
			$message = '<p class="gma-error-message">例外:' . $ex->getMessage () . '</p>';
			return $message;
		}
	}
}

【まとめ記事】へのリンク

【まとめ記事】アマゾンの商品を表示する場合、「アフィリエイト商品表示・WordPressプラグイン」のPHPソースコード解説

「アフィリエイト商品表示・WordPressプラグイン」を用いて、アマゾンの商品を表示する処理について、PHPソースコードを解説しています。

【まとめ記事】楽天市場の商品を表示する場合、「アフィリエイト商品表示・WordPressプラグイン」のPHPソースコード解説

「アフィリエイト商品表示・WordPressプラグイン」を用いて、楽天市場の商品を表示する処理について、PHPソースコードを解説しています。