WordPressでリライトをコントロールしているのはWP_Rewriteというクラスで、$wp_rewriteというインスタンスが在ります。このWP_Rewriteによってリライトルールが生成され、みんな大好きパーマリンクを実現しています。

リライトルールはキーを正規表現、値をリライトするURLとする連想配列で保存されています。

memo/([0-9]+).html/?$ => index.php?post_type=memo&p=$matches[1]

$rules = $wp_rewrite->wp_rewrite_rules();
foreach ( $rules as $regex => $rule ) {
	echo $regex . " => " . $rule . '<br />';
}

こんな感じで展開して確認してみるといいです。

このルールをWP_Rewriteを使って生成しているわけですが、大きな流れとしては、

  • リライトタグの追加 : add_rewrite_tag()
  • ストラクチャーを追加 : add_permastruct()
  • リライトルールを作成 : generate_rewrite_rules()
  • リライトルールの追加 : rewrite_rules()

のような感じになります。
では順を追って確認していきます。

add_rewrite_tag()でリライトタグ(%hoge%的なやつ)を追加する

add_rewrite_tag( $tag, $regex, $query );
第1引数 タグ名 %任意の文字列%
第2引数 正規表現 1つのサブパターン
(例) ([^/]+) スラッシュ以外の文字の1回以上の繰り返し
第3引数 クエリ

リライトタグ%year%%post_id%%category%などでお馴染みの「%」で括られたタグです。後述のストラクチャーにおいて使用され、generate_rewrite_rules()add_rewrite_tag()の第2引数で渡した正規表現に置き換えられます。

このブログの場合は、

function mytheme_add_rewrite_tag() {
	global $wp_rewrite;
	$wp_rewrite->add_rewrite_tag( '%memo%', '(memo)', 'post_type=' );
	$wp_rewrite->add_rewrite_tag( '%blog%', '(blog)', 'post_type=' );
	$wp_rewrite->add_rewrite_tag( '%works%', '(works)', 'post_type=' );
}
add_action( 'init', 'mytheme_add_rewrite_tag', 0 );

が実行されています。
これにより、$rewritecode$rewritereplace$queryreplace、各プロパティの配列に要素が追加されます。

function mytheme_add_rewrite_tag() {
	global $wp_rewrite;
	$wp_rewrite->add_rewrite_tag( '%post_type%', '(memo|blog|works)', 'post_type=' );
}
add_action( 'init', 'mytheme_add_rewrite_tag', 0 );

これでも同じ結果が得られそうな気がしますが、なぜか上手くいきませんでした。
わかる方教えていただけるとうれしいです。

上記でフックしているinitは「WordPressの読み込みが完了し、ヘッダーが送信される前に実行する」とWordPress Codex 日本語版にありますが、それってつまりどのタイミング?っていうのはhttp://www.warna.info/archives/279/がとても参考になります。

では、どんな要素が追加されたのか展開して確認してみます。これもinitにフックさせて実行します。

echo '<p><strong>$wp_rewrite->rewritecode</strong><br />';
foreach ( $wp_rewrite->rewritecode as $key => $val ) {
	echo $key . ' => ' . $val . '<br />';
}
echo '</p><p><strong>$wp_rewrite->rewritereplace</strong><br />';
foreach ( $wp_rewrite->rewritereplace as $key => $val ) {
	echo $key . ' => ' . $val . '<br />';
}
echo '</p><p><strong>$wp_rewrite->queryreplace</strong><br />';
foreach ( $wp_rewrite->queryreplace as $key => $val ) {
	echo $key . ' => ' . $val . '<br />';
}
echo '</p>';

上記の実行結果は以下。

実行結果1

$wp_rewrite->rewritecode
0 => %year%
1 => %monthnum%
2 => %day%
3 => %hour%
4 => %minute%
5 => %second%
6 => %postname%
7 => %post_id%
8 => %author%
9 => %pagename%
10 => %search%
11 => %category%
12 => %post_tag%
13 => %post_format%
14 => %memo%
15 => %blog%
16 => %works%

$wp_rewrite->rewritereplace
0 => ([0-9]{4})
1 => ([0-9]{1,2})
2 => ([0-9]{1,2})
3 => ([0-9]{1,2})
4 => ([0-9]{1,2})
5 => ([0-9]{1,2})
6 => ([^/]+)
7 => ([0-9]+)
8 => ([^/]+)
9 => ([^/]+?)
10 => (.+)
11 => (.+?)
12 => ([^/]+)
13 => ([^/]+)
14 => (memo)
15 => (blog)
16 => (works)

$wp_rewrite->queryreplace
0 => year=
1 => monthnum=
2 => day=
3 => hour=
4 => minute=
5 => second=
6 => name=
7 => p=
8 => author_name=
9 => pagename=
10 => s=
11 => category_name=
12 => tag=
13 => post_format=
14 => post_type=
15 => post_type=
16 => post_type=

10番までは初期値、11~13番はたぶんデフォルトで追加される値で、末尾の3つ、14~15番が新たに追加されました。
単純に第1引数が$rewritecode、第2引数が$rewritereplace、第3引数が$queryreplaceに追加されるだけですね。ちなみに、第1引数がすでに存在するリライトタグ名だった場合は$rewritereplace$queryreplaceが上書きされます。

add_permastruct()でストラクチャーを追加する

add_permastruct( $name, $struct, $args = array() );
第1引数 ストラクチャー名 ユニークな文字列
第2引数 ストラクチャー パーマリンクのURL構造。リライトタグを使って表現します。
(例) /%memo%/%post_id%.html
第3引数 パラメータ

デフォルトは以下の通り。
'with_front' => true,
'ep_mask' => EP_NONE,
'paged' => true,
'feed' => true,
'forcomments' => false,
'walk_dirs' => true,
'endpoints' => true,

単に論理値(true or false)で指定した場合、with_frontの値として扱われデフォルトにマージされます。
「設定>パーマリンク設定」のカスタム構造において、例えば「/archives/%post_id%.html」など何らかの文字列を入れている場合、with_fronttrueだと第2引数で渡したストラクチャーの前にその文字列が入ってしまうので、ここはfalseを渡しておいたほうが無難です。

このブログでは、

function mytheme_add_permastruct() {
	global $wp_rewrite;
	$wp_rewrite->add_permastruct( 'memo_single', '/%memo%/%post_id%.html', false );
	$wp_rewrite->add_permastruct( 'blog_single', '/%blog%/%post_id%.html', false );
	$wp_rewrite->add_permastruct( 'works_single', '/%works%/%post_id%.html', false );
}
add_action( 'init', 'mytheme_add_permastruct', 0 );

が実行されています。

では、今回もどんな要素が追加されたのか展開して確認してみます。

echo '<p><strong>$wp_rewrite->extra_permastructs</strong><br />';
foreach ( $wp_rewrite->extra_permastructs as $key => $val ) {
	echo $key . ' => ' . $val['struct'] . '<br />';
}
echo '</p>';

上記の実行結果は以下。

$wp_rewrite->extra_permastructs
category => /category/%category%
post_tag => /tag/%post_tag%
post_format => /type/%post_format%
memo_single => /%memo%/%post_id%.html
blog_single => /%blog%/%post_id%.html
works_single => /%works%/%post_id%.html

赤字が今回追加されたストラクチャーです。※上記のコードで赤字になるわけではないのであしからず。

リライトルールの生成

これで準備は整いました。あとはflush_rules()を実行するか、パーマリンク設定を更新すればリライトルールが書き換えられます。

実際にどんな処理が行われているかというと、add_permastruct()の第2引数で渡したストラクチャーに含まれるリライトタグを、そのタグと同じインデックスの正規表現クエリに置き換えて生成します。

例えばストラクチャーが「/%memo%/%post_id%.html」の場合、「%memo%」は正規表現「(memo)」に置き換えられ、「%post_id%」は正規表現「([0-9]+)」に置き換えられます(※実行結果1参照)。

/(memo)/([0-9]+).html

これにページ分割した場合の正規表現が加えられて、

(memo)/([0-9]+).html(/[0-9]+)?/?$

これがリライト対象のURLになります。
次にこれをリライトするURLのクエリが必要になるわけですが、ストラクチャーからリライトタグを抜き出し、順番につなげて生成されます。「%memo%」は「post_type=」、「%post_id%」は「p=」なので(※実行結果1参照)、

post_type=$matches[1]&p=$matches[2]

のようになります。$matchesサブパターンに一致した文字列が順に入ります。
これもページ分割クエリが加えられて、

index.php?post_type=$matches[1]&p=$matches[2]&page=$matches[3]

実際はこのようになります。

例えば、

http://www.example.com/hoge/71.html

にアクセスがあると、

http://www.example.com/index.php?post_type=hoge&p=71

の内容を表示してくれるようになるわけです。