redirect_canonical フィルタを使う時の注意点

ちょっとピコンときたので、WordPress 2.3から導入された機能「カノニカルURL」を使って、短縮urlを実現するプラグインを書いてみました。
まだまだβ段階もいいところなんですが、とりあえず公開しておきます。ご要望をお寄せください。
http://dl.getdropbox.com/u/110305/New/short-link-maker.zip

このプラグインを有効化すると、シングルページの <head> 内に <link rel="shortlink" href="https://dogmap.jp/〜" /> という META タグが吐き出されます。
また、この META タグの href に対してアクセスすると、正式なURLにリダイレクトされるようになります。
Twitter や、印刷した紙に短い URL を書きたいときなんかに使ってください。
ちなみに URL は、単純に post_ID を62進数に変換しただけのものなので、カテゴリ一覧やタグ一覧などでは使えません。
あくまでも post_ID を持っているページのみに対応しています。

さて、これを作る際に困ったのが redirect_canonical フィルタの使い方。
wp-includes/canonical.php を見ると分かりますが、いつもどおり add_filter() で単純に使うだけだとハマりますので注意してください。
# つうか、限りなく仕様不備のような気もする。

カノニカルURL機能を実現しているのは redirect_canonical() という関数です。
この関数の最後で $redirect_url = apply_filters('redirect_canonical', $redirect_url, $requested_url); とやって、'redirect_canonical' というフィルターを使えるようにしてくれているので、このフィルターをフックしてやれば、リクエストがあったURLとは違うURLにリダイレクトしてやることができます。
が、この redirect_canonical() 関数、apply_filter() を呼んだ後で、そのリダイレクト先のURLが正しいかどうかをチェックするために、再度自分を呼んでる(293行目)のです。

	if ( $redirect_url == $requested_url )
		return false;

	// Note that you can use the "redirect_canonical" filter to cancel a canonical redirect for whatever reason by returning FALSE
	$redirect_url = apply_filters('redirect_canonical', $redirect_url, $requested_url);

	if ( !$redirect_url || $redirect_url == $requested_url ) // yes, again -- in case the filter aborted the request
		return false;

	if ( $do_redirect ) {
		// protect against chained redirects
		if ( !redirect_canonical($redirect_url, false) ) {
			wp_redirect($redirect_url, 301);
			exit();
		} else {
			// Debug
			// die("1: $redirect_url<br />2: " . redirect_canonical( $redirect_url, false ) );
			return false;
		}
	} else {
		return $redirect_url;
	}

WordPress のカノニカルURL機能では、実在しないURLをリクエストされた時、似たようなURLをサイト内から探して候補を出します。
例えば https://dogmap.jp/pU という URL がリクエストされた場合、 WordPress は https://dogmap.jp/2007/09/05/put-scripts-at-the-bottom/ をリダイレクト先の候補にします。
しかし、本当は https://dogmap.jp/pU という URL は、post ID 1606 の https://dogmap.jp/2008/03/12/wordpress-233-feed/ にリダイレクトさせたいのです。
そこで 'redirect_canonical' というフィルターをフックして https://dogmap.jp/pU という URL がリクエストされた場合 https://dogmap.jp/2008/03/12/wordpress-233-feed/ をリダイレクト先に指定してやるのですが、2回目にフィルタを呼ばれたときに正常にフィルタ内の関数が動作しない

というわけで、以下のような実装にしました。

function redirect_shortlink($redirect_url, $requested_url){
	remove_filter('redirect_canonical', 'redirect_shortlink' );

	if (empty($requested_url)) return $redirect_url;

	$siteurl = trailingslashit(get_option('siteurl'));
	$pattern = '/^' . preg_quote($siteurl, '/') . '([\da-zA-Z]+)$/';
	if ( preg_match_all( $pattern, $requested_url, $matches ) ) {
		$link_id = $matches[1][0];
		$post_id = n_to_dec($link_id, 62);
		if ( $post_id !== FALSE ) $redirect_url = get_permalink($post_id);
		if ( empty($redirect_url) )
			$redirect_url = ( preg_match('/[\d]+/', $link_id)
				? get_permalink($link_id)
				: get_permalink(hexdec($link_id))
			);
		add_filter('redirect_canonical', create_function( '', "return '$redirect_url';"));
	}
	unset($matches);

	return $redirect_url;
}
add_filter('redirect_canonical', 'redirect_shortlink', 10, 2 );

redirect_canonical() 関数が二回目に呼び出されたときには、一度目に取得したリダイレクトURLを返すようにフィルターを変更しています。

3 thoughts on “redirect_canonical フィルタを使う時の注意点

      1. Ai

        なるほど。残念ですが、そのような理由であれば仕方ないですね。
        ありがとうございます。

        返信

コメントを残す

メールアドレスが公開されることはありません。

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