先日「管理画面上からPHPを自由に動かせるのは危ないよ」という記事を書きましたが、「条件分岐をさせたいから記事内でPHPを動かしたい。」という声もちらほら見られたとか見られていないとか。 そこで記事内で条件分岐ができるショートコードができそうなので書いてみました。 ちなみに条件分岐をしたくてExec-PHPとか入れていた方向けなので、PHPを使える方を対象とした中~上級者向けの記事です。

コードと概要、使い方

なんでもかんでも好きな条件で変更できるのではなく、以下の条件分岐に対応しています。
  • WordPress Codexに書かれている条件分岐(この記事を公開した時点で実装されている分岐)
  • wp_is_mobileによるスマートフォンとPCの分岐(要:WordPress 3.4以上)
  • 公開開始日時と終了日時
  • ランダム表示(ABテスト用?)
下のようにリンクをランダムに切り替えたりできます。分岐後は他のショートコードも使えるんですよ。

ショートコードを実装する

以下のコードをfunctions.phpに記述してください。
function shortcode_wpif($opt, $content = null) {
	$reg1 = '/\[else(.*)?\]/mU';
	preg_match_all($reg1, $content, $elseArray);
	$contentArray = preg_split($reg1,$content);
	$deny_content1 = end($contentArray);
	if(!empty($opt['query'])) {
		parse_str(html_entity_decode($opt['query']), $opt);
	}
	if(shortcode_wpif_branch($opt)) {
		return shortcode_wpif_do_shortcode($contentArray[0]);
	} elseif(!empty($elseArray[0])) {
		$deny_content2 = '';
		foreach($elseArray[1] as $k => $else) {
			$opt = (!empty($elseArray[1][$k]))?shortcode_parse_atts($elseArray[1][$k]):'';
			if(!empty($opt ['query'])) {
				parse_str(html_entity_decode($opt ['query']), $opt );
			}
			$deny_content2 = $contentArray[$k+1];
			if(shortcode_wpif_branch($opt)) {
				return shortcode_wpif_do_shortcode($deny_content2);
			}
		}
		return shortcode_wpif_do_shortcode($deny_content1);
	}
}
function shortcode_wpif_do_shortcode($content) {
	$content = preg_replace('/{(if|else|\/if)(.*)?}/mU', '[$1$2]', strtr(wp_specialchars_decode($content),array('”'=>'"','″'=>'"')));
	return do_shortcode($content);
}
function shortcode_wpif_branch($opt) {
	try {
		foreach((array)$opt as $func => $arg){
			$reg2 = '/^(deny_)?(is_(404|admin|archive|attachment|author|category|comments_popup|date|day|feed|front_page|home|month|multi_author|multisite|page|page_template|paged|preview|active_sidebar|search|single|singular|sticky|post_type_hierarchical|post_type_archive|super_admin|tag|tax|tax|time|trackback|year|plugin_active)|in_(category|the_loop)|has_(tag|excerpt|nav_menu)|(comments_open|pings_open|current_theme_supports|wp_is_mobile|get_post_type))$/';
			if(preg_match($reg2, $func)) {
				shortcode_wpif_exec_wp_function($func, $arg);
			}
			if(preg_match($reg2, $arg)) {
				shortcode_wpif_exec_wp_function($arg);
			}
			
			// 日付分岐
			if($func === 'start' && !empty($arg)) {
				$ts1 = time();
				$ts2 = strtotime($arg);
				shortcode_wpif_exec_start($ts1, $ts2);
			}
			if($func === 'end' && !empty($arg)) {
				$ts1 = time();
				$ts2 = strtotime($arg);
				shortcode_wpif_exec_end($ts1, $ts2);
			}
			// 日付個別
			$y = date('Y');
			$m = date('n');
			$d = date('j');
			$hr = date('H');
			$mn = date('i');
			$sc = date('s');
			if($func === 'start_year' && !empty($arg)) {
				shortcode_wpif_exec_start($y, $arg);
			}
			if($func === 'start_month' && !empty($arg)) {
				shortcode_wpif_exec_start($m, $arg);
			}
			if($func === 'start_date' && !empty($arg)) {
				shortcode_wpif_exec_start($d, $arg);
			}
			if($func === 'end_year' && !empty($arg)) {
				shortcode_wpif_exec_end($y, $arg);
			}
			if($func === 'end_month' && !empty($arg)) {
				shortcode_wpif_exec_end($m, $arg);
			}
			if($func === 'end_date' && !empty($arg)) {
				shortcode_wpif_exec_end($d, $arg);
			}
			
			// 時間分岐
			if($func === 'start_time' && !empty($arg)) {
				if(preg_match('/^\d{1,2}(:\d{1,2})*$/', $arg)) {
					$arg = explode(':', $arg);
					$time1 = $hr*3600 + $mn*60 + $sc;
					if(!isset($arg[1])) $arg[1] = 0;
					if(!isset($arg[2])) $arg[2] = 0;
					$time2 = $arg[0]*3600 + $arg[1]*60 + $arg[2];
					shortcode_wpif_exec_start($time1, $time2);
				}
			}
			if($func === 'end_time' && !empty($arg)) {
				if(preg_match('/^\d{1,2}(:\d{1,2})*$/', $arg)) {
					$arg = explode(':', $arg);
					$time1 = $hr*3600 + $mn*60 + $sc;
					if(!isset($arg[1])) $arg[1] = 59;
					if(!isset($arg[2])) $arg[2] = 59;
					$time2 = $arg[0]*3600 + $arg[1]*60 + $arg[2];
					shortcode_wpif_exec_end($time1, $time2);
				}
			}
			
			// 乱数分岐
			if(($arg === 'rand' && rand(0,1)) || ($func === 'rand' && ((empty($arg) && mt_rand(0,1)) || (!empty($arg) && mt_rand(1, 100) > $arg)))) {
				throw new Exception($func);
			}
		}
		return true;
	} catch (Exception $e) {
		return false;
	}
}
function shortcode_wpif_exec_start($o, $s) {
	if($o < $s) {
		throw new Exception($func);
	}
}
function shortcode_wpif_exec_end($o, $s) {
	if($o > $s) {
		throw new Exception($func);
	}
}
function shortcode_wpif_exec_wp_function($func, $o = '') {
	if(preg_match('/^deny_/', $func)) {
		$f = preg_replace('/^deny_/', '', $func);
		if($func === 'get_post_type'){
			if(get_post_type() == $o) throw new Exception($func);
		}elseif($f === 'wp_is_mobile') {
			if(function_exists('wp_is_mobile') && wp_is_mobile()) throw new Exception($func);
		} elseif(!empty($o)) {
			$o = explode(',', $o);
			if(call_user_func_array($f, array(&$o))) throw new Exception($func);
		} else {
			if(call_user_func_array($f, array())) throw new Exception($func);
		}
	} elseif($func === 'get_post_type'){
		if(get_post_type() != $o) throw new Exception($func);
	}elseif($func === 'wp_is_mobile') {
		if(function_exists('wp_is_mobile') && !wp_is_mobile()) throw new Exception($func);
	} else {
		$arr = array();
		if(!empty($o)) {
			$o = explode(',', $o);
			$arr = array(&$o);
		}
		if(!call_user_func_array($func, $arr)) {
			throw new Exception($func);
		}
	}
	return true;
}
add_shortcode('if', 'shortcode_wpif');

【コード修正】 32行目: (変更前)foreach($opt as $func => $arg){ (変更後)foreach((array)$opt as $func => $arg){

プラグインでやれって感じの長さですね。まぁ気にしない。

使い方

まずは簡単な使い方から。

日付の指定

○年○月○日以降に表示
PHPのdateフォーマットでstart属性を指定します。
2013年8月1日以降に表示されますよー


// 条件外の時に何か表示したい時は[else]を使う

2013年8月1日以降に表示されますよー

○年○月○日から×年×月×日まで表示
start属性とend属性を指定します。
// 8月1日~8月15日まで(8月15日も終日表示されます)

時間も指定
もちろん、何時何分以降まで指定できます。
// 8月1日の12時00分~8月15日9時59分59秒まで

ランダムに表示させる

ランダムに表示させるにはrand属性を指定します。値を入れなければ 1/2の確率で分岐、値を入れるとP%の確率で表示されます。
1/2の確率で現れたり消えたり







WordPressの条件で分岐させる

WordPress Codexの条件分岐で分岐ができます。
個別投稿ページで表示


シングルページ (固定ページ、個別投稿ページ、添付ファイルページ)で表示







タグが設定されていたら表示


"wordpress"というタグが設定されていたら表示






分岐を否定する
WordPressの条件文は関数の頭に”deny_”をつけると否定できます。





検索結果じゃなければ表示

複雑な条件分岐

elseに条件分岐を付与したりといった複雑な条件分岐も可能です。
2013/8/4~2013/8/10まで表示

詳細な日時の指定
日付は指定せずに何時~何時まで表示という指定もできます。
  • 年(start_year/end_year)
  • 月(start_month/end_month)
  • 日(start_date/end_date)
  • 時間(start_time/end_time)
これらの属性に数値を指定します。
# 月指定



1年のうち1月~6月はこちらを表示


#時間の指定
* start_timeは分、秒が省略されると 00分、00秒
* end_timeは省略されると59分、59秒として扱われます。


0時~12時台(12:59:59まで)表示



6時~8時59分59秒に表示

条件分岐の入れ子
条件分岐内に書かれたショートコードは実行されますが、終了タグなどの仕様の関係でこの記事のショートコードのifは使えません。しかし{if}~{/if}というタグで記述すると実行させることができます。

2013/8/4~2013/8/10の午前


ちなみに条件分岐の中で使える条件分岐は1階層まで。下みたいに入れ子を深くすると動作がおかしくなっちゃうので気をつけて。
{if start_time="00:00" end_time="11:59"}
{if start_time="09:00" end_time="10:00"}
2013/7/21~2013/7/27の午前
{/if}
{/if}

果たして需要はあるのかと

記事内での条件分岐に需要があるか謎ですが、ショートコードでこんなこともできますよ!ってことで。ランダム分岐あたりはABテストで使えるなぁと思います(そこそこの母集団がないと偏りますが)。 曜日指定もできるようにしようか悩んだけどそれをすると祝日にまで対応しないといけない気がするのでやめときました(‘A`) と、いろいろデバッグはしてみたつもりですが結構ささーって書けたのでなんか怖い… バグがあれば報告してくれると喜びます( ゚д゚)

何か一言あれば!!

質問とかツッコミとかお気軽にコメントしてください。がんばって返します。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

9 件のコメント

  1. ピンバック: スマホに対応 | やっぱり、いたちいぬが好き

  2. ピンバック: [WordPress]ダッシュボードの使い勝手を向上させるカスタマイズTIPS 10|ウェビメモ

  3. 同じようなものを作成しようと調べていたら。
    高機能ですばらしいです。使わせていただきます。
    1点、日付・時刻の判定の際に、現状ですと標準時間での判定になってしまっていましたので、
    date_default_timezone_set(get_option(‘timezone_string’));
    でタイムゾーンを変更するか、
    date(‘Y’) などで現在の日時を取得している部分を
    date_i18n(‘Y’) に変更するとうまくいきました。

  4. date_default_timezone_set を使わない場合は
    日付判定部分も
    $ts1 =time();
    $ts1 =strtotime(get_option(‘gmt_offset’).’hours’);
    にする必要がありました。

    1. よっしー 投稿作成者

      はみ様
      こんばんは!
      ありがとうございます。
      ご指摘の通り、日付の処理がサーバーの環境によって変わりますね…
      WordPressで実装されている関数を利用するのが望ましいと思われるので修正いたします。

  5. こういうのを探してました!ウィジェットでもちゃんと動いていて素晴らしいですが、
    ▲▲▲
    のような「複雑な条件分岐」使い方をするとうまくいきませんでした。

  6. 半角でショートコード書いたら送信失敗しましたので再送です。
    こういうのを探してました!ウィジェットでもちゃんと動いていて素晴らしいですが、
    [if is_single=”1″]■■■[else is_single=”10″]●●●[else]▲▲▲[/if]
    のような「複雑な条件分岐」使い方をするとうまくいきませんでした。
    まぁあまり想定されてない使い方かもですが・・・。