ショートコードの属性に対応する「アフィリエイト商品のHTML」を作成する
アフィリエイト商品表示・WordPressプラグインのショートコードにおいて、service属性を指定できます。
当プラグインは、そのservice属性に対応する「アフィリエイト商品のHTML」を作成します。
service属性の各場合に分けて、「アフィリエイト商品のHTML」を作成する処理について、PHPソースコードを解説しています。
service属性は、以下の二種類に対応しています。
- アマゾン
- 楽天市場
「アフィリエイト商品の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属性は、以下の二種類です。
- “amazon”は、アマゾンを意味します。
- “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文も必要みたいです。
最初、この事がわかりませんでした。
- use文で、クラスの名前空間を読み込む。
- 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;
}
}
}