WordPressの起動2 – WordPress起動時に読み込まれるPHPファイルの順番

define()関数とconst()関数による定数定義方法の違い

PHPの定数定義は、クラス外からグローバル関数として使用するdefine()関数が使われてきましたが、PHP5.3からはクラス内で使用するconst()が使えるようになっています。

class Person{
  private $name;
  const age = 15;    //ここはクラス内なのでdefine()は使えない
  //コンストラクタ関数
  function __construct($parm){
    $this->name = $parm;
  }
  //メソッド
  public function konnichiwa(){
    print('こんにちは'.$this->name.'['.age.']');
  }
}
//オブジェクトの生成
$hito = new Person('yama');
$hito->konnichiwa();

読み込まれるPHPファイルの順番

WordPressは、6つのファイルとwp関数が、順番に呼び出されて起動します。

  1. -index.php(WordPressのエントリーポイント)
  2. —–wp-blog-header.php
  3. ———wp-load.php(環境設定)
  4. ————-wp-config.php(DB接続情報)
  5. —————–wp-settings.php(WordPress初期設定のコア)
  6. ———wp関数(WordPressメイン関数実行)
  7. ———template-loader.php(テンプレート選択)

1. index.php

define('WP_USE_THEMES', true);
require( dirname( __FILE__ ) . '/wp-blog-header.php' );

仮にWordPressを別ディレクトリにインストール(WordPressのアドレスが違う場合)する際でも、index.phpはWordPressのエントリーポイントなので、必ずドメインルートに置く必要があり、2行目で/blog/wp-blog-header.phpみたいに、インストールディレクトリにあるファイルをインクルードするように修正します。

これはエントリーポイントをドメインルートに置くだけで、インストールディレクトリは自由に選択できるという柔軟性を実現しています。

dirname関数はファイルである自分が入っているディレクトリ、つまり親ディレクトリを示すのでカレントディレクトリになりますが、カレントディレクトリをさらにdirname関数の引数にすると、カレントディレクトリが入っているディレクトリ、すなわち1個親のディレクトリを指します。

__FILE__はPHP定義済み定数で自分自身のファイルまでのパス、dirname(__FILE__)自分自身がいるディレクトリ、つまり親ディレクトリまでのパスとなり、dirname(dirname(__FILE__))になると祖父ディレクトリまでのパスとなります。

echo __FILE__; //表示結果はD:\xammp\htdocs\index.php
echo dirname(__FILE__); //表示結果はD:\xammp\htdocs
echo dirname(dirname(__FILE__)); //表示結果はD:\xammp

2. wp-blog-header.php

if ( !isset($wp_did_header) ) {
    $wp_did_header = true;
    require_once( dirname(__FILE__) . '/wp-load.php' );
    wp();
    require_once( ABSPATH . WPINC . '/template-loader.php' );
}
  • 2行目:以降は変数$wp_did_headerが空の場合の処理であり、まずtureをセットする。この$wp_did_headerが何なのかイマイチはっきりしませんが、php.iniのregister_globalディレクティブがONだと、仮にURLのクエリストリングで「index.php?wp_did_header=dummy」とかやられたら、コード上で$wp_did_header変数にdummyという値が格納されてしまうのでマズイらしい。よって$wp_did_headerが空であることを前提として処理を継続させます。
  • 3行目:wp-load.phpを1回だけインクルード
  • 4行目:wp関数が実行される。
  • 5行目:wp-includes/template-loader.phpを1回だけインクルード

3. wp-load.php

define( 'ABSPATH', dirname(__FILE__) . '/' );
error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR );

if ( file_exists( ABSPATH . 'wp-config.php') ) {
    require_once( ABSPATH . 'wp-config.php' );
} elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
    require_once( dirname( ABSPATH ) . '/wp-config.php' );
  • 1行目:ABSPATHにwp-load.phpのカレントディレクトリまでのパスに末尾スラッシュをつけたもの、すなわちWordPressルートをセット
  • 2行目:error_reporting() 関数で出力する PHP エラーの種類を設定する。
  • 4行目:WordPressルートにwp-config.phpがある場合。wp-config.phpはWordPressのインストール時にDB名、ユーザー名、パスワードなどパラメータを設定することで自動生成されます。つまりwp-config.phpがあるということはWordPressのインストールが完了しているということです。
  • 5行目:WordPressルートのwp-config.phpを1回だけインクルード
  • 6行目:WordPressルートの1個上の親ディレクトリ、通常はドキュメントルートにwp-config.phpがありwp-setting.phpがない場合(関数の前に@があるのでエラーログを出力しない)
  • 7行目:ドキュメントルートのwp-config.phpを1回だけインクルード

以下はwp-config.phpがない場合、つまりwp-load.phpは存在するがwp-config.phpがないということは、インストールはしたもののパラメータが未設定で自動生成されていない状態で、この場合以下が1回だけ実行されます。

} else {
    define( 'WPINC', 'wp-includes' );
    require_once( ABSPATH . WPINC . '/load.php' );

    wp_fix_server_vars();
    require_once( ABSPATH . WPINC . '/functions.php' );
    $path = wp_guess_url() . '/wp-admin/setup-config.php';

    if ( false === strpos( $_SERVER['REQUEST_URI'], 'setup-config' ) ) {
        header( 'Location: ' . $path );
        exit;
    }

    define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
    require_once( ABSPATH . WPINC . '/version.php' );

    wp_check_php_mysql_versions();
    wp_load_translations_early();

    // Die with an error message
    $die  = sprintf(
        /* translators: %s: wp-config.php */
        __( "There doesn't seem to be a %s file. I need this before we can get started." ),
        '<code>wp-config.php</code>'
    ) . '</p>';
    $die .= '<p>' . sprintf(
        /* translators: %s: Codex URL */
        __( "Need more help? <a href='%s'>We got it</a>." ),
        __( 'https://codex.wordpress.org/Editing_wp-config.php' )
    ) . '</p>';
    $die .= '<p>' . sprintf(
        /* translators: %s: wp-config.php */
        __( "You can create a %s file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file." ),
        '<code>wp-config.php</code>'
    ) . '</p>';
    $die .= '<p><a href="' . $path . '" class="button button-large">' . __( "Create a Configuration File" ) . '</a>';

    wp_die( $die, __( 'WordPress &rsaquo; Error' ) );
}

そもそもwp-config.phpはダウンロード時のWordPressのコアファイルの中には存在せず、インストール時に自動生成されますが、設定が終わっていないと以下のエラーを出力します。

ファイルが見つかりません。インストールを開始するにはwp-config.php ファイルが必要です。お困りでしたら「wp-config.php の編集」を参照してください。ウィザード形式で wp-config.phpファイルを作成することもできますが、すべてのサーバーにおいて正常に動作するわけではありません。最も安全な方法は手動でファイルを作成することです。

4. wp-config.php

インストール時に接続情報やユーザー情報を入れることで自動生成された環境設定ファイルをWordPressの起動時に読み込む。

define('DB_NAME', 'DB名');
define('DB_USER', 'DBユーザーID');
define('DB_PASSWORD', 'DBパスワード');
define('DB_HOST', 'DBホスト名');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
$table_prefix  = 'wp1_';
define ('WPLANG', 'ja');

if ( !defined('ABSPATH') )
    define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');
  • 12行目:ABSPATHが空の場合はWordPressルートをセットして、そこにあるwp-settings.phpを1回だけインクルード

5. wp-settings.php

WordPress全体で使うオブジェクトを生成し、スーパーグローバル変数に格納し、処理やクラス的なライブラリをインクルードする重要なファイルです。

  • 18行目:wp_initial_constants関数で、phpの実行メモリ上限をマルチサイトで64MB、シングルサイトで40MBに指定、ブログIDのデフォルトを1に指定、などなど。
  • 21行目:wp_check_php_mysql_versions関数で、PHPとMySQLがWordPressに対してバージョン不足の際にエラーを表示させる。
  • 24行目:php.iniのディレクティブであるマジッククォートはSQLインジェクション(SQL攻撃)防止のため、シングルクオート、ダブルクオート、バックスラッシュ、NULL文字を自動的にバックスラッシュでエスケープする機能ですが、副作用が多いためPHP5.4でデフォルトOFFされていますが、WordPress内でも無効化しています。
  • 31行目:php.iniのディレクティブであるregister_global=Onだと、GETかPOSTのクエリストリングで送信した値が、$_GET[‘変数’]や$_POST[‘変数’]で取得できるのはいいとしても、$変数自体にも値がセットされてしまっているため、もしプログラムで$変数の初期化を忘れてしまった場合に、URLの改ざんにより問題が起こる可能性があるため、Offと同じ状態にしましょうという関数がload.php内のwp_unregister_globals関数です。
//load.php内
function wp_unregister_GLOBALS() {
     //register_globalsディレクティブの設定をini_get()関数で取得しOFFならreturnで関数を抜ける
	if ( !ini_get( 'register_globals' ) )
		return;

        //クエリストリングのキーがGLOBALSで送信されてきた場合(/index.php?GLOBALS['yama']=123みたいな)
	if ( isset( $_REQUEST['GLOBALS'] ) )
          //「GLOBALSを上書きしようとしますよ」という警告メッセージを出力してPHPを終了
		die( 'GLOBALS overwrite attempt detected' );

	//削除NGの変数一覧
	$no_unset = array( 'GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES', 'table_prefix' );
     //グローバル変数
	$input = array_merge( $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset( $_SESSION ) &amp;&amp; is_array( $_SESSION ) ? $_SESSION : array() );
	foreach ( $input as $k => $v )
          //グローバル変数の値が削除NGの変数一覧でない場合には削除する
		if ( !in_array( $k, $no_unset ) && isset( $GLOBALS[$k] ) ) {
			unset( $GLOBALS[$k] );
		}
}

PHP5.4からregister_globalディレクティブはデフォルトでOFFですが、WordPress内でもOffにします。スーパーグローバル変数$GLOBALSは連想配列です。

//定数WPINCにwp-includesをセット
define( 'WPINC', 'wp-includes' );

//関数ファイルload.phpとdefault-constants.phpをインクルード
require( ABSPATH . WPINC . '/load.php' );
require( ABSPATH . WPINC . '/default-constants.php' );

//version.php内で値をセット
global $wp_version, $wp_db_version, $tinymce_version, $required_php_version, $required_mysql_version;

//バージョン情報ファイルversion.phpをインクルード
require( ABSPATH . WPINC . '/version.php' );

//wp_initial_constants()関数内で値をセット
global $blog_id;

//default-constants.php内の関数
wp_initial_constants();

//load.php内の関数でバージョン不足エラーを表示
wp_check_php_mysql_versions();

//マジッククウォートをオフ
@ini_set( 'magic_quotes_runtime', 0 );
@ini_set( 'magic_quotes_sybase',  0 );

//タイムゾーンを協定世界時にセット
date_default_timezone_set( 'UTC' );

//load.php内の関数でregister_globalsディレクティブの設定をOFFにする。
wp_unregister_GLOBALS();

//load.php内の関数
wp_fix_server_vars();

//load.php内の関数
wp_favicon_request();

// Check if we're in maintenance mode.
wp_maintenance();

// Start loading timer.
timer_start();

// Check if we're in WP_DEBUG mode.
wp_debug_mode();

// For an advanced caching plugin to use. Uses a static drop-in because you would only want one.
if ( WP_CACHE )
    WP_DEBUG ? include( WP_CONTENT_DIR . '/advanced-cache.php' ) : @include( WP_CONTENT_DIR . '/advanced-cache.php' );

// Define WP_LANG_DIR if not set.
wp_set_lang_dir();

// Load early WordPress files.
require( ABSPATH . WPINC . '/compat.php' );
require( ABSPATH . WPINC . '/functions.php' );
require( ABSPATH . WPINC . '/class-wp.php' );
require( ABSPATH . WPINC . '/class-wp-error.php' );
require( ABSPATH . WPINC . '/plugin.php' );
require( ABSPATH . WPINC . '/pomo/mo.php' );

// Include the wpdb class and, if present, a db.php database drop-in.
require_wp_db();

// wp-config.phpでセットしたプレフィックス
$GLOBALS['table_prefix'] = $table_prefix;
wp_set_wpdb_vars();

// Start the WordPress object cache, or an external object cache if the drop-in is present.
wp_start_object_cache();

// Attach the default filters.
require( ABSPATH . WPINC . '/default-filters.php' );

// Initialize multisite if enabled.
if ( is_multisite() ) {
    require( ABSPATH . WPINC . '/ms-blogs.php' );
    require( ABSPATH . WPINC . '/ms-settings.php' );
} elseif ( ! defined( 'MULTISITE' ) ) {
    define( 'MULTISITE', false );
}

register_shutdown_function( 'shutdown_action_hook' );

// Stop most of WordPress from being loaded if we just want the basics.
if ( SHORTINIT )
    return false;

// Load the L10n library.
require_once( ABSPATH . WPINC . '/l10n.php' );

// Run the installer if WordPress is not installed.
wp_not_installed();

// Load most of WordPress.
require( ABSPATH . WPINC . '/class-wp-walker.php' );
require( ABSPATH . WPINC . '/class-wp-ajax-response.php' );
require( ABSPATH . WPINC . '/formatting.php' );
require( ABSPATH . WPINC . '/capabilities.php' );
require( ABSPATH . WPINC . '/class-wp-roles.php' );
require( ABSPATH . WPINC . '/class-wp-role.php' );
require( ABSPATH . WPINC . '/class-wp-user.php' );
require( ABSPATH . WPINC . '/query.php' );
require( ABSPATH . WPINC . '/date.php' );
require( ABSPATH . WPINC . '/theme.php' );
require( ABSPATH . WPINC . '/class-wp-theme.php' );
require( ABSPATH . WPINC . '/template.php' );
require( ABSPATH . WPINC . '/user.php' );
require( ABSPATH . WPINC . '/class-wp-user-query.php' );
require( ABSPATH . WPINC . '/session.php' );
require( ABSPATH . WPINC . '/meta.php' );
require( ABSPATH . WPINC . '/class-wp-meta-query.php' );
require( ABSPATH . WPINC . '/general-template.php' );
require( ABSPATH . WPINC . '/link-template.php' );
require( ABSPATH . WPINC . '/author-template.php' );
require( ABSPATH . WPINC . '/post.php' );
require( ABSPATH . WPINC . '/class-walker-page.php' );
require( ABSPATH . WPINC . '/class-walker-page-dropdown.php' );
require( ABSPATH . WPINC . '/class-wp-post.php' );
require( ABSPATH . WPINC . '/post-template.php' );
require( ABSPATH . WPINC . '/revision.php' );
require( ABSPATH . WPINC . '/post-formats.php' );
require( ABSPATH . WPINC . '/post-thumbnail-template.php' );
require( ABSPATH . WPINC . '/category.php' );
require( ABSPATH . WPINC . '/class-walker-category.php' );
require( ABSPATH . WPINC . '/class-walker-category-dropdown.php' );
require( ABSPATH . WPINC . '/category-template.php' );
require( ABSPATH . WPINC . '/comment.php' );
require( ABSPATH . WPINC . '/class-wp-comment.php' );
require( ABSPATH . WPINC . '/class-wp-comment-query.php' );
require( ABSPATH . WPINC . '/class-walker-comment.php' );
require( ABSPATH . WPINC . '/comment-template.php' );
require( ABSPATH . WPINC . '/rewrite.php' );
require( ABSPATH . WPINC . '/class-wp-rewrite.php' );
require( ABSPATH . WPINC . '/feed.php' );
require( ABSPATH . WPINC . '/bookmark.php' );
require( ABSPATH . WPINC . '/bookmark-template.php' );
require( ABSPATH . WPINC . '/kses.php' );
require( ABSPATH . WPINC . '/cron.php' );
require( ABSPATH . WPINC . '/deprecated.php' );
require( ABSPATH . WPINC . '/script-loader.php' );
require( ABSPATH . WPINC . '/taxonomy.php' );
require( ABSPATH . WPINC . '/class-wp-term.php' );
require( ABSPATH . WPINC . '/class-wp-tax-query.php' );
require( ABSPATH . WPINC . '/update.php' );
require( ABSPATH . WPINC . '/canonical.php' );
require( ABSPATH . WPINC . '/shortcodes.php' );
require( ABSPATH . WPINC . '/embed.php' );
require( ABSPATH . WPINC . '/class-wp-embed.php' );
require( ABSPATH . WPINC . '/class-wp-oembed-controller.php' );
require( ABSPATH . WPINC . '/media.php' );
require( ABSPATH . WPINC . '/http.php' );
require( ABSPATH . WPINC . '/class-http.php' );
require( ABSPATH . WPINC . '/class-wp-http-streams.php' );
require( ABSPATH . WPINC . '/class-wp-http-curl.php' );
require( ABSPATH . WPINC . '/class-wp-http-proxy.php' );
require( ABSPATH . WPINC . '/class-wp-http-cookie.php' );
require( ABSPATH . WPINC . '/class-wp-http-encoding.php' );
require( ABSPATH . WPINC . '/class-wp-http-response.php' );
require( ABSPATH . WPINC . '/widgets.php' );
require( ABSPATH . WPINC . '/class-wp-widget.php' );
require( ABSPATH . WPINC . '/class-wp-widget-factory.php' );
require( ABSPATH . WPINC . '/nav-menu.php' );
require( ABSPATH . WPINC . '/nav-menu-template.php' );
require( ABSPATH . WPINC . '/admin-bar.php' );
require( ABSPATH . WPINC . '/rest-api.php' );
require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
require( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' );
require( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' );

// Load multisite-specific files.
if ( is_multisite() ) {
    require( ABSPATH . WPINC . '/ms-functions.php' );
    require( ABSPATH . WPINC . '/ms-default-filters.php' );
    require( ABSPATH . WPINC . '/ms-deprecated.php' );
}

// Define constants that rely on the API to obtain the default value.
// Define must-use plugin directory constants, which may be overridden in the sunrise.php drop-in.
wp_plugin_directory_constants();

$GLOBALS['wp_plugin_paths'] = array();

// Load must-use plugins.
foreach ( wp_get_mu_plugins() as $mu_plugin ) {
    include_once( $mu_plugin );
}
unset( $mu_plugin );

// Load network activated plugins.
if ( is_multisite() ) {
    foreach ( wp_get_active_network_plugins() as $network_plugin ) {
        wp_register_plugin_realpath( $network_plugin );
        include_once( $network_plugin );
    }
    unset( $network_plugin );
}

/**
 * Fires once all must-use and network-activated plugins have loaded.
 *
 * @since 2.8.0
 */
do_action( 'muplugins_loaded' );

if ( is_multisite() )
    ms_cookie_constants(  );

// Define constants after multisite is loaded.
wp_cookie_constants();

// Define and enforce our SSL constants
wp_ssl_constants();

// Create common globals.
require( ABSPATH . WPINC . '/vars.php' );

// Make taxonomies and posts available to plugins and themes.
// @plugin authors: warning: these get registered again on the init hook.
create_initial_taxonomies();
create_initial_post_types();

// Register the default theme directory root
register_theme_directory( get_theme_root() );

// Load active plugins.
foreach ( wp_get_active_and_valid_plugins() as $plugin ) {
    wp_register_plugin_realpath( $plugin );
    include_once( $plugin );
}
unset( $plugin );

// Load pluggable functions.
require( ABSPATH . WPINC . '/pluggable.php' );
require( ABSPATH . WPINC . '/pluggable-deprecated.php' );

// Set internal encoding.
wp_set_internal_encoding();

// Run wp_cache_postload() if object cache is enabled and the function exists.
if ( WP_CACHE && function_exists( 'wp_cache_postload' ) )
    wp_cache_postload();

/**
 * Fires once activated plugins have loaded.
 *
 * Pluggable functions are also available at this point in the loading order.
 *
 * @since 1.5.0
 */
do_action( 'plugins_loaded' );

// Define constants which affect functionality if not already defined.
wp_functionality_constants();

// Add magic quotes and set up $_REQUEST ( $_GET + $_POST )
wp_magic_quotes();

/**
 * Fires when comment cookies are sanitized.
 *
 * @since 2.0.11
 */
do_action( 'sanitize_comment_cookies' );

/**
 * WordPress Query object
 * @global WP_Query $wp_the_query
 * @since 2.0.0
 */
$GLOBALS['wp_the_query'] = new WP_Query();

/**
 * Holds the reference to @see $wp_the_query
 * Use this global for WordPress queries
 * @global WP_Query $wp_query
 * @since 1.5.0
 */
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];

/**
 * Holds the WordPress Rewrite object for creating pretty URLs
 * @global WP_Rewrite $wp_rewrite
 * @since 1.5.0
 */
$GLOBALS['wp_rewrite'] = new WP_Rewrite();

/**
 * WordPress Object
 * @global WP $wp
 * @since 2.0.0
 */
$GLOBALS['wp'] = new WP();

/**
 * WordPress Widget Factory Object
 * @global WP_Widget_Factory $wp_widget_factory
 * @since 2.8.0
 */
$GLOBALS['wp_widget_factory'] = new WP_Widget_Factory();

/**
 * WordPress User Roles
 * @global WP_Roles $wp_roles
 * @since 2.0.0
 */
$GLOBALS['wp_roles'] = new WP_Roles();

//setup_themeフックを点火しテーマを読み込む
do_action( 'setup_theme' );

// Define the template related constants.
wp_templating_constants(  );

// Load the default text localization domain.
load_default_textdomain();

$locale = get_locale();
$locale_file = WP_LANG_DIR . "/$locale.php";
if ( ( 0 === validate_file( $locale ) ) && is_readable( $locale_file ) )
    require( $locale_file );
unset( $locale_file );

// Pull in locale data after loading text domain.
require_once( ABSPATH . WPINC . '/locale.php' );

/**
 * WordPress Locale object for loading locale domain date and various strings.
 * @global WP_Locale $wp_locale
 * @since 2.1.0
 */
$GLOBALS['wp_locale'] = new WP_Locale();

// Load the functions for the active theme, for both parent and child theme if applicable.
if ( ! wp_installing() || 'wp-activate.php' === $pagenow ) {
    if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
        include( STYLESHEETPATH . '/functions.php' );
    if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
        include( TEMPLATEPATH . '/functions.php' );
}

//after_setup_themeフックを点火
do_action( 'after_setup_theme' );

// Set up current user.
$GLOBALS['wp']->init();

//initフックを点火
do_action( 'init' );

// Check site status
if ( is_multisite() ) {
    if ( true !== ( $file = ms_site_check() ) ) {
        require( $file );
        die();
    }
    unset($file);
}

//wp_loadedフックを点火しコアモジュール、プラグイン、テーマを起動する。
do_action( 'wp_loaded' );
  • 272行目から294行目までは$wp_the_query, $wp_query, $wpという重要なインスタンスを生成し、スーパーグローバル変数$GROBALSに格納しているところです。
  • 272行目:WP_QUeryクラスから$wp_the_queryオブジェクトを生成し$GROBALSに格納
  • 280行目:WP_QUeryクラスから$wp_queryオブジェクトを生成し$GROBALSに格納
  • 294行目:WPクラスから$wpオブジェクトを生成し$GROBALSに格納

wp-settings.phpには、プラグイン開発で必要になるフックが以下の順に埋め込んであります。

  1. do_action( ‘muplugins_loaded’ );
  2. do_action( ‘plugins_loaded’ );
  3. do_action( ‘sanitize_comment_cookies’ );//setup_themeフックを点火しテーマを読み込む
  4. do_action( ‘setup_theme’ );//after_setup_themeフックを点火
  5. do_action( ‘after_setup_theme’ );//wp_loadedフックを点火しコアモジュール、プラグイン、テーマを起動する。
  6. do_action( ‘wp_loaded’ );

6. wp()関数を実行

wp-blog-header.phpでwp-load.php⇒wp-config.php⇒wp-settings.phpと読みこまれた後に、wp関数が実行されますが、やっていることは主に以下の3つです(Codex クエリ概要)。

  1. URLをパース(文法に従って分析)しクエリを特定
  2. 条件分岐タグで使用されるすべてのis_変数を設定
  3. クエリに基づき$wp_query->get_posts()関数で投稿データを$posts配列に格納

WPクラスからWP_Queryクラスに関数が以下のように呼ばれて繋がっていきます。

  1. wp()
  2. —$wp->main()
  3. ——–$wp->parse_request():URLパースとクエリ特定
  4. ——–$wp->query_posts()
  5. ————$wp_query->query()
  6. —————-$wp_query->get_posts():投稿データ取得
  7. ——————–$wp_query->parse_query():is_変数を設定

まずwp-settings.phpで生成したグローバル変数$wp, $wp_query, $wp_the_queryを関数内で使用するためにグローバル宣言しています。

wp-includes/functions.phpの中にwp関数が定義されており、ここでWPクラスのmainメソッドに引数$query_varsを渡して実行します。

function wp( $query_vars = '' ) {
    global $wp, $wp_query, $wp_the_query;
    $wp->main( $query_vars );

    if ( !isset($wp_the_query) )
    $wp_the_query = $wp_query;
}

WPクラス自体はwp-includes/class-wp.phpに定義されており、ここにmainメソッドがあります。

class WP {
    public function main($query_args = '') {
	$this->init();  // 現在のユーザーが存在するか判断しデータを取得
	$this->parse_request($query_args);  //URLをパースしてクエリ特定
	$this->send_headers();  //ヘッダーを変更できるようにする
	$this->query_posts();  //クエリ変数を元にループをセットアップ
	$this->handle_404();  //求められたURLが見つからない場合404をセット
	$this->register_globals();  //Set up the WordPress Globals.
	do_action_ref_array( 'wp', array( &$this ) );  // 特定のアクションフックに登録された関数を実行
    }
}

URLをパース(文法に従って分析)しクエリを特定

wp-includes/class-wp.phpのWPクラス内のparse_requestメソッドでパース処理を行ない、URLにマッチしたクエリを特定します。

class WP {
    public function parse_request($extra_query_vars = '') {
	global $wp_rewrite;

        //クエリ特定
    }
}

条件分岐タグで使用されるすべてのis_変数を設定

query.phpのWP_Queryクラスにparse_queryメソッドがありますが、名前にparseがついているにもかかわらず、パース処理はすでにparse_requestメソッドで完了しており、ここではパース処理は行なわれず、条件分岐タグで使用するis_変数への値をセットします。

class WP_Query {
    public function parse_query( $query = '' ) {
        if ( ! empty( $query ) ) {
            $this-&gt;init();
            $this-&gt;query = $this-&gt;query_vars = wp_parse_args( $query );
        } elseif ( ! isset( $this-&gt;query ) ) {
            $this-&gt;query = $this-&gt;query_vars;
        }
    }
}

クエリに基づき$wp_query->get_posts関数で投稿データを$posts配列に格納

$wp->mainメソッドの中で6個のWPクラス自身のメソッドを実行していますが、実際にクエリのタイプを確定し投稿を取得し、$wp_query->postsに格納しているのは$wp->query_postsメソッドです。

class WP{
    public function query_posts() {
	global $wp_the_query;
	$this->build_query_string();
	$wp_the_query->query($this->query_vars);
    }
}

wp-settings.phpでWP_Queryクラスから生成したグローバル変数$wp_the_queryを、関数内で使用するためにグローバル宣言し$wp_the_query->queryメソッドを実行します。

ここがWPクラスとWP_Queryクラスが連結するところで、WPクラスの$wpオブジェクトからWP_Queryクラスのメソッドを$wp_queryオブジェクトを通して実行しています。

class WP_Query{
    public function query( $query ) {
	$this->init();
	$this->query = $this->query_vars = wp_parse_args( $query );
	return $this->get_posts();  //投稿IDを基に投稿のデータを取得
    }
}

wp-includes/query.phpにあるWP_Queryクラスのget_postsメソッドで、要求された投稿をデータベースから取得し、グローバル変数$postsに格納しています。

class WP_Query {
    public function get_posts() {
        global $wpdb;
        $this->parse_query();  //条件分岐タグで使用するis_変数への値をセット 
        do_action_ref_array( 'pre_get_posts', array( &$this ) ); //アクションフック
                
        //長~い省略
        return $this->posts;  //最後に$postsが完成
    }
}

 

7. template_loader.php

WordPressのテンプレート階層とは、テンプレートファイルを表示するルールであり、それを実現するのがtemplate_loader.phpです。

  • 8行目:HTTP通信メソッドのGETはページや画像などのデータにアクセスしたときで、POSTはフォームでデータを送信したとき であり、HEADはウェブ巡回ツールが ページの更新日付などの情報だけを取得したときのメソッドになります。

apply_filters( ‘exit_on_http_head’, true )がよく分かっていないのですが、exit_on_http_headはHEADメソッドに対してコンテンツを生成を認めるかどうかというフックのようです。要はHEADメソッドならExit終了ということ。

■exit_on_http_head
Hook: Filter whether to allow ‘HEAD’ requests to generate content.
■apply_filters ( ‘exit_on_http_head’, bool $exit );
Filter whether to allow ‘HEAD’ requests to generate content.
Provides a significant performance bump by exiting before the page content loads for ‘HEAD’ requests.
if ( defined('WP_USE_THEMES') && WP_USE_THEMES )
	/*WP_USE_THEMESはテーマが使われているか否かを判定。使われていればアクションフックを点火*/
	do_action( 'template_redirect' );

/**
比較演算子===は等しく型も同じ
 */
if ( 'HEAD' === $_SERVER['REQUEST_METHOD'] && apply_filters( 'exit_on_http_head', true ) )
	exit();

// 検索エンジンのクローラーからのアクセスの場合、アクションフックdo_robotsを実行
if ( is_robots() ) :
	do_action( 'do_robots' );
	return;
//RSSフィードのリクエストの場合、フィード用テンプレートを読み込み
elseif ( is_feed() ) :
	do_feed();
	return;
// トラックバックエンドポイントリクエストであれば、WordPressホームのwp-trackback.phpを読み込む
elseif ( is_trackback() ) :
	include( ABSPATH . 'wp-trackback.php' );
	return;
//埋め込み(embed)投稿ページならembed-template.phpを読み込む
elseif ( is_embed() ) :
	$template = ABSPATH . WPINC . '/embed-template.php';
	$template = apply_filters( 'embed_template', $template );
	include ( $template );
	return;
endif;

if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
	$template = false;
	if     ( is_404()            && $template = get_404_template()            ) :
	elseif ( is_search()         && $template = get_search_template()         ) :
	elseif ( is_front_page()     && $template = get_front_page_template()     ) :
	elseif ( is_home()           && $template = get_home_template()           ) :
	elseif ( is_post_type_archive() && $template = get_post_type_archive_template() ) :
	elseif ( is_tax()            && $template = get_taxonomy_template()       ) :
	elseif ( is_attachment()     && $template = get_attachment_template()     ) :
		remove_filter('the_content', 'prepend_attachment');
	elseif ( is_single()         && $template = get_single_template()         ) :
	elseif ( is_page()           && $template = get_page_template()           ) :
	elseif ( is_singular()       && $template = get_singular_template()       ) :
	elseif ( is_category()       && $template = get_category_template()       ) :
	elseif ( is_tag()            && $template = get_tag_template()            ) :
	elseif ( is_author()         && $template = get_author_template()         ) :
	elseif ( is_date()           && $template = get_date_template()           ) :
	elseif ( is_archive()        && $template = get_archive_template()        ) :
	elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
	elseif ( is_paged()          && $template = get_paged_template()          ) :
	else :
		$template = get_index_template();
	endif;
	/**
	 * Filter the path of the current template before including it.
	 *
	 * @since 3.0.0
	 *
	 * @param string $template The path of the template to include.
	 */
	if ( $template = apply_filters( 'template_include', $template ) )
		include( $template );
	return;
endif;
  • 61行目: apply_filters( ‘template_include’, $template )

8. テンプレートファイル

WordPressループの中のthe_postsテンプレートタグにより、$wp_query->postsから現在の投稿を順次に読み込んでいきます。

まずはいつものWordPressループの中で何気なく実行される関数・・・

<?php 
if(have_posts()): 
    while(have_posts()): the_post(); 
?>
 
<?php 
    endwhile;
endif;
?>

このhave_posts()関数やthe_post()関数を実行する$wp_queryオブジェクトは、wp-includes/query.phpの中に定義されているWP_Queryクラスから生成されており、このメソッドを実行しています。

function have_posts() {
	global $wp_query;
	return $wp_query->have_posts();
}

function the_post() {
	global $wp_query;
	$wp_query->the_post();
}

//ちなみに条件分岐関数もここにある
function is_home() {
	global $wp_query;

	if ( ! isset( $wp_query ) ) {
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
		return false;
	}

	return $wp_query->is_home();
}

WP_Queryクラスの中にhave_post()やthe_post()やis_home()などのメソッドが定義されています。

class WP_Query {
    //記事が存在するかどうか判断するメソッド
    public function have_posts() {
		if ( $this->current_post + 1 < $this->post_count ) {
			return true;
		} elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
			do_action_ref_array( 'loop_end', array( &$this ) );
			// Do some cleaning up after the loop
			$this->rewind_posts();
		}
		$this->in_the_loop = false;
		return false;
    }

    //現在の投稿を取得するメソッド
    public function the_post() {
        global $post;
        $this->in_the_loop = true;

        if ( $this->current_post == -1 ) // loop has just started
            /**
             * Fires once the loop is started.
             *
             * @since 2.0.0
             *
             * @param WP_Query &$this The WP_Query instance (passed by reference).
             */
            do_action_ref_array( 'loop_start', array( &$this ) );

        $post = $this->next_post();
        $this->setup_postdata( $post );
    }

    //トップページかどうかの判断をする変数の定義
    public $is_home = false;
    
    //トップページかどうか判断するメソッド
    public function is_home() {
        return (bool) $this->is_home;
    }
}

つまりWordPressループはWP_Queryクラスからオブジェクトを生成することにより、いくつものバリエーションのループを生成することができます。

<?php
$wp_query2 = new WP_Query( $args );
if ( $wp_query2->have_posts() ) {
	while ( $wp_query2->have_posts() ) {
		$wp_query2->the_post();
	}
?>

<?php
    endwhile;
endif;
?>

これでメインクエリのhave_posts関数でデータの有無を判断し、the_post関数でグローバル変数$postに格納された投稿を取得することがわかりました。