#
ドキュメント

Document

自分のための備忘録です。

パーマリンク構造

WordPressの表示フローは概ね以下のようになります。

パーマリンク生成

  1. 設定 > パーマリンクからリライトルールを設定
  2. WordPressはパーマリンクを作成
    • 新規投稿時や記事/アーカイブ表示時にget_permalink関数で1で設定したリライトルールに沿ってprettyパーマリンクを生成

投稿表示

  1. WordPressは↑のprettyパーマリンクをリライトルールに応じてuglyパーマリンクに変換
  2. uglyパーマリンクのquery_varsに応じて対象を抽出して表示する

Ref

概要

WordPressのパーマリンクは大きく2つに分けられます。

本記事ではPrettyパーマリンクがUglyパーマリンクに変換されるフローを記載しています。

※ Prettyパーマリンクの構造は基本的に設定 > パーマリンクから指定します。
※ パーマリンクはDBに格納されていません。↑の設定に応じて動的に作成されます。

リライトの流れ

  1. 設定 > パーマリンク で設定を変更
  2. 上記設定によってwp_options.option_namerewrite_rulesWP_Rewrite::rules)が決まる

アクセス

  1. Prettyパーマリンクでアクセス(例:https://example.com/category-sample/1
  2. PrettyパーマリンクをWP_Rewrite::rulesをもとにUglyパーマリンクに変換(例:https://example.com?p=1&category-name=category-sample&page=)
  3. Uglyパーマリンクのパブリッククエリをもとに投稿抽出クエリの条件であるWP_Query:query_varsが設定されて投稿抽出SQL発行

Webサイト表示の場合HTTPリクエストを受け取って起動したWordPressは、HTTPリクエストに含まれるクエリ文字列またはURLの一部をWordPressのリクエスト変数として解釈し、WordPressクエリ文字列を実行します。HTTPリクエストに含めることができるクエリ変数をパブリッククエリ変数といいます。

O'Reilly 詳解WordPress プライム・ストラテジー株式会社 p95

パブリック変数はwp-includes/class-wp.phpで定義されている。

<?php
/**
 * WordPress environment setup class.
 *
 * @package WordPress
 * @since 2.0.0
 */
class WP {
    /**
     * Public query variables.
     *
     * Long list of public query variables.
     *
     * @since 2.0.0
     * @var string[]
     */
    public $public_query_vars = array( 'm', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'pagename', 'page_id', 'error', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'favicon', 'taxonomy', 'term', 'cpage', 'post_type', 'embed' );

リライトルール

パブリッククエリをリライトルールに沿って解釈して、問合せを(SQL)発行して投稿を取得する。

アクセス時のパブリッククエリは、WP_Query::query_varsに格納される

wp-setting.phpの中に、WordPressによるURLの解釈、リライトのために重要な2つのオブジェクト、$wp_queryと$wp_rewriteが初期化されます。

Rewrite APIその1 「Rewriteとパーマリンク」(WordPressプラグイン開発のバイブルのボツ原稿から)

WordPressのリライトルールは、グローバル変数$wp_rewriteに格納されている。

DBテーブル

リライト関連の情報は、wp_optionsに格納されている。 以下に代表的なレコードを記載する。

option_name option_value
permalink_structure /%category%/%post_id%
rewrite_rules マッチ規則の正規表現がシリアライズされて保存

リライトルールを追加

ref. WordPress パーマリンク リライトルール

  • add_rewrite_rule
  • add_rewrite_tag
  • pre_post_link ・・・ get_permalinkの中でapplyされる
  • add permastruct
  • rewrite_rules_array
  • flush_rewrite_rules
    • リライトルールはflush_rewrite_ruleをコードで呼び出すか、管理画面のパーマリンク画面を開くと自動で反映される

プラグインによるリライトルールの追加時に、WordPressのデータベースにtransientキャッシュとして登録されているリライトルールを変更する処理を加えなければなりません。

Rewrite APIその1 「Rewriteとパーマリンク」(WordPressプラグイン開発のバイブルのボツ原稿から)

備考

wp_rewrite

WP_Rewrite Object
(
    [permalink_structure] => /%postname%/
    [use_trailing_slashes] => 1
    [author_base] => author
    [page_structure] => %pagename%
    [search_base] => search
    [comments_base] => comments
    [pagination_base] => page
    [comments_pagination_base] => comment-page
    [feed_base] => feed
    [comment_feed_structure] => comments/feed/%feed%
    [feed_structure] => feed/%feed%
    [front] => /
    [root] => 
    [index] => index.php
    [matches] => 
    [rules] => Array
        (
            [^wp-json/?$] => index.php?rest_route=/
            [^wp-json/(.*)?] => index.php?rest_route=/$matches[1]
            [^index.php/wp-json/?$] => index.php?rest_route=/
            [^index.php/wp-json/(.*)?] => index.php?rest_route=/$matches[1]
            [category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?category_name=$matches[1]&feed=$matches[2]
            [category/(.+?)/(feed|rdf|rss|rss2|atom)/?$] => index.php?category_name=$matches[1]&feed=$matches[2]
            [category/(.+?)/embed/?$] => index.php?category_name=$matches[1]&embed=true
            [category/(.+?)/page/?([0-9]{1,})/?$] => index.php?category_name=$matches[1]&paged=$matches[2]
            [category/(.+?)/?$] => index.php?category_name=$matches[1]
            [tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?tag=$matches[1]&feed=$matches[2]
            [tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?tag=$matches[1]&feed=$matches[2]
            [tag/([^/]+)/embed/?$] => index.php?tag=$matches[1]&embed=true
            [tag/([^/]+)/page/?([0-9]{1,})/?$] => index.php?tag=$matches[1]&paged=$matches[2]
            [tag/([^/]+)/?$] => index.php?tag=$matches[1]
            [type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_format=$matches[1]&feed=$matches[2]
            [type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?post_format=$matches[1]&feed=$matches[2]
            [type/([^/]+)/embed/?$] => index.php?post_format=$matches[1]&embed=true
            [type/([^/]+)/page/?([0-9]{1,})/?$] => index.php?post_format=$matches[1]&paged=$matches[2]
            [type/([^/]+)/?$] => index.php?post_format=$matches[1]
            [.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$] => index.php?feed=old
            [.*wp-app\.php(/.*)?$] => index.php?error=403
            [.*wp-register.php$] => index.php?register=true
            [feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]
            [(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]
            [embed/?$] => index.php?&embed=true
            [page/?([0-9]{1,})/?$] => index.php?&paged=$matches[1]
            [comments/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]&withcomments=1
            [comments/(feed|rdf|rss|rss2|atom)/?$] => index.php?&feed=$matches[1]&withcomments=1
            [comments/embed/?$] => index.php?&embed=true
            [search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?s=$matches[1]&feed=$matches[2]
            [search/(.+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?s=$matches[1]&feed=$matches[2]
            [search/(.+)/embed/?$] => index.php?s=$matches[1]&embed=true
            [search/(.+)/page/?([0-9]{1,})/?$] => index.php?s=$matches[1]&paged=$matches[2]
            [search/(.+)/?$] => index.php?s=$matches[1]
            [author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?author_name=$matches[1]&feed=$matches[2]
            [author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?author_name=$matches[1]&feed=$matches[2]
            [author/([^/]+)/embed/?$] => index.php?author_name=$matches[1]&embed=true
            [author/([^/]+)/page/?([0-9]{1,})/?$] => index.php?author_name=$matches[1]&paged=$matches[2]
            [author/([^/]+)/?$] => index.php?author_name=$matches[1]
            [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
            [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
            [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/embed/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&embed=true
            [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
            [([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
            [([0-9]{4})/([0-9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
            [([0-9]{4})/([0-9]{1,2})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
            [([0-9]{4})/([0-9]{1,2})/embed/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&embed=true
            [([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
            [([0-9]{4})/([0-9]{1,2})/?$] => index.php?year=$matches[1]&monthnum=$matches[2]
            [([0-9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
            [([0-9]{4})/(feed|rdf|rss|rss2|atom)/?$] => index.php?year=$matches[1]&feed=$matches[2]
            [([0-9]{4})/embed/?$] => index.php?year=$matches[1]&embed=true
            [([0-9]{4})/page/?([0-9]{1,})/?$] => index.php?year=$matches[1]&paged=$matches[2]
            [([0-9]{4})/?$] => index.php?year=$matches[1]
            [.?.+?/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
            [.?.+?/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
            [.?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [.?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [.?.+?/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
            [.?.+?/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
            [(.?.+?)/embed/?$] => index.php?pagename=$matches[1]&embed=true
            [(.?.+?)/trackback/?$] => index.php?pagename=$matches[1]&tb=1
            [(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?pagename=$matches[1]&feed=$matches[2]
            [(.?.+?)/(feed|rdf|rss|rss2|atom)/?$] => index.php?pagename=$matches[1]&feed=$matches[2]
            [(.?.+?)/page/?([0-9]{1,})/?$] => index.php?pagename=$matches[1]&paged=$matches[2]
            [(.?.+?)/comment-page-([0-9]{1,})/?$] => index.php?pagename=$matches[1]&cpage=$matches[2]
            [(.?.+?)(?:/([0-9]+))?/?$] => index.php?pagename=$matches[1]&page=$matches[2]
            [[^/]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
            [[^/]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
            [[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
            [[^/]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
            [([^/]+)/embed/?$] => index.php?name=$matches[1]&embed=true
            [([^/]+)/trackback/?$] => index.php?name=$matches[1]&tb=1
            [([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?name=$matches[1]&feed=$matches[2]
            [([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?name=$matches[1]&feed=$matches[2]
            [([^/]+)/page/?([0-9]{1,})/?$] => index.php?name=$matches[1]&paged=$matches[2]
            [([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?name=$matches[1]&cpage=$matches[2]
            [([^/]+)(?:/([0-9]+))?/?$] => index.php?name=$matches[1]&page=$matches[2]
            [[^/]+/([^/]+)/?$] => index.php?attachment=$matches[1]
            [[^/]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
            [[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
            [[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
            [[^/]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
        )

    [extra_rules] => Array
        (
        )

    [extra_rules_top] => Array
        (
            [^wp-json/?$] => index.php?rest_route=/
            [^wp-json/(.*)?] => index.php?rest_route=/$matches[1]
            [^index.php/wp-json/?$] => index.php?rest_route=/
            [^index.php/wp-json/(.*)?] => index.php?rest_route=/$matches[1]
        )

    [non_wp_rules] => Array
        (
        )

    [extra_permastructs] => Array
        (
            [category] => Array
                (
                    [with_front] => 1
                    [ep_mask] => 512
                    [paged] => 1
                    [feed] => 1
                    [forcomments] => 
                    [walk_dirs] => 1
                    [endpoints] => 1
                    [struct] => /category/%category%
                )

            [post_tag] => Array
                (
                    [with_front] => 1
                    [ep_mask] => 1024
                    [paged] => 1
                    [feed] => 1
                    [forcomments] => 
                    [walk_dirs] => 1
                    [endpoints] => 1
                    [struct] => /tag/%post_tag%
                )

            [post_format] => Array
                (
                    [with_front] => 1
                    [ep_mask] => 0
                    [paged] => 1
                    [feed] => 1
                    [forcomments] => 
                    [walk_dirs] => 1
                    [endpoints] => 1
                    [struct] => /type/%post_format%
                )

        )

    [endpoints] => Array
        (
        )

    [use_verbose_rules] => 
    [use_verbose_page_rules] => 1
    [rewritecode] => Array
        (
            [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%
        )

    [rewritereplace] => Array
        (
            [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] => ([^/]+)
        )

    [queryreplace] => Array
        (
            [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=
        )

    [feeds] => Array
        (
            [0] => feed
            [1] => rdf
            [2] => rss
            [3] => rss2
            [4] => atom
        )

)

WordPress パーマリンク リライトルール

// カスタム投稿タイプmanualのrewriteルールを追加
add_filter( 'rewrite_rules_array', function($rules) {
    $new_rules = [
        'manual/([0-9]+)/?$' => 'index.php?post_type=manual&p=$matches[1]',
    ];

    return $new_rules + $rules;
});


add_filter( 'pre_post_link', function($permalink, $post, $leavename) {
    if ( $post->ID !== $post->post_name ) {
        return '/%category%/%postname%';
    }
    //$permalink をカスタマイズ
    return $permalink;
}, 10, 3 );

add_filter( 'rewrite_rules_array', function($rules) {
    $new_rules = [
        'info/.+/([0-9a-zA-Z-_]+)$' => 'index.php?name=matches[1]',
    ];

    return $rules + $new_rules;
});