WordPress のソースコードを読んで
REST API の処理を追いかけてみる
WordPress の REST API を使ってやりたいことがあったので公式ドキュメントを読んだり検索をしていたけれど、なかなか欲しい情報にたどり着けず。するとふと「ソースコードを読んでみようかな」という考えが浮かんだので実践してみました。
なかなか楽しかったので、ざーっとしたまとめを掲載します。
なお WordPress 始動からの流れは以下がとても参考になります。特に前者は、被引用回数が多く各所で紹介されている有名記事といった印象です。
- WordPressの実行フローを視覚化してみる | Simple Colors
- Rewrite APIその1 「Rewriteとパーマリンク」(WordPressプラグイン開発のバイブルのボツ原稿から) – Shinichi Nishikawa's
- クエリ概要 – WordPress Codex 日本語版
準備
今回読んだのは WordPress 4.9.1 のソースコード。リリースアーカイブのページから zip をゲットです。
あとは展開してまるっと Sublime Text で開けたら、端から順に愚直に読んでいきましょう。
千里の道も /index.php から
WordPress は “全ての道は/index.php
へ通ず” なので、/index.php
さえ開ければあとは芋づる式です。なお/index.php
へたどり着くまでの流れは冒頭で紹介した西川さんのブログに明るいです。
私のメモ書きママですが、/index.php
からの流れをざーっと書くと…。
/index.php /wp-blog-header.php /wp-load.php /wp-config.php /wp-settings.php /wp-includes/load.php /wp-includes/default-constants.php /wp-includes/plugin.php /wp-includes/version.php wp_initial_constants() wp_check_php_mysql_versions() wp_unregister_GLOBALS() wp_fix_server_vars() wp_favicon_request() wp_maintenance() timer_start() wp_debug_mode() /wp-includes/compat.php /wp-includes/class-wp-list-util.php /wp-includes/functions.php /wp-includes/class-wp-matchesmapregex.php /wp-includes/class-wp.php /wp-includes/class-wp-error.php /wp-includes/pomo/mo.php require_wp_db() wp_set_wpdb_vars() wp_start_object_cache() /wp-includes/default-filters.php /wp-includes/l10n.php /wp-includes/class-wp-locale.php /wp-includes/class-wp-locale-switcher.php /wp-includes/class-wp-walker.php /wp-includes/class-wp-ajax-response.php /wp-includes/formatting.php /wp-includes/capabilities.php /wp-includes/class-wp-roles.php /wp-includes/class-wp-role.php /wp-includes/class-wp-user.php /wp-includes/class-wp-query.php /wp-includes/query.php /wp-includes/date.php /wp-includes/theme.php /wp-includes/class-wp-theme.php /wp-includes/template.php /wp-includes/user.php /wp-includes/class-wp-user-query.php /wp-includes/class-wp-session-tokens.php /wp-includes/class-wp-user-meta-session-tokens.php /wp-includes/meta.php /wp-includes/class-wp-meta-query.php /wp-includes/class-wp-metadata-lazyloader.php /wp-includes/general-template.php /wp-includes/link-template.php /wp-includes/author-template.php /wp-includes/post.php /wp-includes/class-walker-page.php /wp-includes/class-walker-page-dropdown.php /wp-includes/class-wp-post-type.php /wp-includes/class-wp-post.php /wp-includes/post-template.php /wp-includes/revision.php /wp-includes/post-formats.php /wp-includes/post-thumbnail-template.php /wp-includes/category.php /wp-includes/class-walker-category.php /wp-includes/class-walker-category-dropdown.php /wp-includes/category-template.php /wp-includes/comment.php /wp-includes/class-wp-comment.php /wp-includes/class-wp-comment-query.php /wp-includes/class-walker-comment.php /wp-includes/comment-template.php /wp-includes/rewrite.php /wp-includes/class-wp-rewrite.php /wp-includes/feed.php /wp-includes/bookmark.php /wp-includes/bookmark-template.php /wp-includes/kses.php /wp-includes/cron.php /wp-includes/deprecated.php /wp-includes/script-loader.php /wp-includes/taxonomy.php /wp-includes/class-wp-taxonomy.php /wp-includes/class-wp-term.php /wp-includes/class-wp-term-query.php /wp-includes/class-wp-tax-query.php /wp-includes/update.php /wp-includes/canonical.php /wp-includes/shortcodes.php /wp-includes/embed.php /wp-includes/class-wp-embed.php /wp-includes/class-oembed.php /wp-includes/class-wp-oembed-controller.php /wp-includes/media.php /wp-includes/http.php /wp-includes/class-http.php /wp-includes/class-wp-http-streams.php /wp-includes/class-wp-http-curl.php /wp-includes/class-wp-http-proxy.php /wp-includes/class-wp-http-cookie.php /wp-includes/class-wp-http-encoding.php /wp-includes/class-wp-http-response.php /wp-includes/class-wp-http-requests-response.php /wp-includes/class-wp-http-requests-hooks.php /wp-includes/widgets.php /wp-includes/class-wp-widget.php /wp-includes/class-wp-widget-factory.php /wp-includes/nav-menu.php /wp-includes/nav-menu-template.php /wp-includes/admin-bar.php /wp-includes/rest-api.php /wp-includes/rest-api/class-wp-rest-server.php /wp-includes/rest-api/class-wp-rest-response.php /wp-includes/rest-api/class-wp-rest-request.php /wp-includes/rest-api/endpoints/class-wp-rest-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php /wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php /wp-includes/rest-api/fields/class-wp-rest-meta-fields.php /wp-includes/rest-api/fields/class-wp-rest-comment-meta-fields.php /wp-includes/rest-api/fields/class-wp-rest-post-meta-fields.php /wp-includes/rest-api/fields/class-wp-rest-term-meta-fields.php /wp-includes/rest-api/fields/class-wp-rest-user-meta-fields.php wp_plugin_directory_constants() do_action( 'muplugins_loaded' ) wp_cookie_constants() wp_ssl_constants() /wp-includes/vars.php create_initial_taxonomies() create_initial_post_types() wp_start_scraping_edited_file_errors() register_theme_directory( get_theme_root() ) wp_register_plugin_realpath( $plugin ) include_once( $plugin ) /wp-includes/pluggable.php /wp-includes/pluggable-deprecated.php wp_set_internal_encoding() do_action( 'plugins_loaded' ) wp_functionality_constants() wp_magic_quotes() do_action( 'sanitize_comment_cookies' ) $GLOBALS['wp_the_query'] = new WP_Query() $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'] $GLOBALS['wp_rewrite'] = new WP_Rewrite() $GLOBALS['wp'] = new WP() $GLOBALS['wp_widget_factory'] = new WP_Widget_Factory() $GLOBALS['wp_roles'] = new WP_Roles() do_action( 'setup_theme' ) wp_templating_constants( ) load_default_textdomain() $GLOBALS['wp_locale'] = new WP_Locale() $GLOBALS['wp_locale_switcher'] = new WP_Locale_Switcher() $GLOBALS['wp_locale_switcher']->init() do_action( 'after_setup_theme' ) $GLOBALS['wp']->init() do_action( 'init' ) do_action( 'wp_loaded' ) wp() /wp-includes/template-loader.php
とにかく目を引くのが/wp-settings.php
。およそ100のファイルを読み込んでいます。途中にこんなコメントがあってちょっと笑ってしまいました。
// Load most of WordPress.
ファイル名の通りセッティングが主で、後半も後半で思い出したようにインスタンス生成を連発。そして最後にdo_action('init')
を実行しています。見慣れたアクション名を発見した時はなんだかホッとしました。
/wp-settings.php
までは正直ほとんど何もしていないので、次のwp()
が WordPress の肝だろうと予想。途中 rest-api の文字が散見されたのでそれらも合わせてさらに読み進めます。
WP REST API 簡単まとめ
相変わらず前置きが長い、話が長いので、今回の目的の REST API に直接関係しそうな箇所だけのまとめに向かいましょう。
ポイントは2つ。
add_action( 'init', 'rest_api_init' )
add_action( 'parse_request', 'rest_api_loaded' )
いずれも/wp-includes/default-filters.php
に書かれています。
それでは見てみましょう。
/wp-settings.php /wp-includes/default-filters.php add_action( 'init', 'rest_api_init' ) add_action( 'rest_api_init', 'rest_api_default_filters', 10, 1 ) add_action( 'rest_api_init', 'register_initial_settings', 10 ) add_action( 'rest_api_init', 'create_initial_rest_routes', 99 ) add_action( 'parse_request', 'rest_api_loaded' ) add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ) $GLOBALS['wp_the_query'] = new WP_Query() $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'] $GLOBALS['wp_rewrite'] = new WP_Rewrite() $GLOBALS['wp'] = new WP() do_action( 'init' ) rest_api_init() rest_api_register_rewrites() $wp->add_query_var( 'rest_route' ) wp() $wp->main() $wp->init() $wp->parse_request($query_args) do_action_ref_array( 'parse_request', array( &$this ) ) rest_api_loaded() $server = rest_get_server() do_action( 'rest_api_init', $wp_rest_server ) rest_api_default_filters() register_initial_settings() create_initial_rest_routes() return new WP_REST_Server() $server->serve_request( $route ) $request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path ) return new WP_REST_Response( $response ) $result = $server->check_authentication() return apply_filters( 'rest_authentication_errors', null ) rest_cookie_check_errors() $result = $server->dispatch( $request ) $result = rest_ensure_response( $result ) return new WP_REST_Response( $result ) $result = $server->response_to_data( $result, isset( $_GET['_embed'] ) ) $result = wp_json_encode( $result ) echo $result return null die()
先にあげた2つのadd_action()
によって WordPress コアの流れに介入していることがわかります。逆に言えば、これがなければコアに影響しないわけで、REST API が WordPress 4.4 まではプラグインとして提供されていた様子が伺いしれます。
まずinit
のアクションに引っ掛けて API のエンドポイントを追加。add_rewrite_rule()
を使っています。
ちなみにデフォルトのエンドポイントには/wp-json/
という接頭語がつきますよね。これがなんとなくモヤモヤしていたのですが、apply_filters( 'rest_url_prefix', 'wp-json' )
という箇所があったので、簡単に加工できそうです。
ここまでは/wp-settings.php
。
続けてwp()
の内部に入ります。REST API はparse_request
のアクションで介入しています。このアクションはWP::parse_request()
の最後の最後で実行されるので、REST API が動きだす時点で、URLを解析してクエリ変数をセットするところまで完了しています。このへんの話は日本語codexに明るいです。
ついに REST API の本丸に辿り着いたように思います。長い旅も終わりが見えてきました。終わりが見えてきましたが、残念ながら終わる前に終わりです。ここまで読んでくれた方には申し訳ないですが、私と皆さんが乗った船は泥舟だったようです。
parse_request
のアクションに引っ掛けたあとの3つのクラス、WP_REST_Server
, WP_REST_Response
, WP_REST_Request
は、私には越えられない嵐で沈没してしまいました。
なんか色々したあとWP_REST_Server::dispatch()
して、echo $result
するんですよ、きっと。
ごめんなさい。
おわり。