WordPress工作原理之程式檔案執行順序

flowerszhong發表於2016-04-28

在瞭解WordPress掛載機制時,一直有一個疑惑,到底是WordPress的核心原始檔先執行還是主題檔案裡functions.php檔案先執行。為了解決這個問題,想了解WordPress的工作原理,它是如何生成網頁的,各程式檔案的執行順序是什麼,於是有了這篇文章。

WordPress所有的前端頁面生成都要經過根目錄下的index.php檔案(不是主題根目錄),這是通過Web伺服器的rewrite規則實現的。然後通過index.php檔案一步步引導WordPress環境啟動,再分析請求URL返回相應資料所組成的前臺頁面。以下將一步步分析原始碼檔案,以此來了解WordPress整體框架及工作原理。

第一步:載入index.php檔案

該檔案有效原始碼如下:

define(`WP_USE_THEMES`, true);

//定義是否載入主題檔案,true為載入;

require(`./wp-blog-header.php`);

//載入wp-blog-header.php檔案,該檔案用於啟動WordPress環境及模板;

第二步:載入wp-blog-header.php檔案

該檔案有效原始碼如下:

if ( !isset($wp_did_header) ) {
    # 判斷$wp_did_header變數是否已經設定,如果未設定則執行程式碼塊;
    $wp_did_header = true;
    # 見解析1;
    require_once( dirname(__FILE__) . `/wp-load.php` );
    # 見解析2;
    wp();
    # 見解析3;
    require_once( ABSPATH . WPINC . `/template-loader.php` );
    # 見解析4;
}

解析1: 對$wp_did_header進行賦值,這樣如果程式碼塊已經執行過,判斷就會失敗,程式碼塊就不會再執行。這種做法可以確保wp-blog-header.php檔案只執行一次(重複執行的話會出現函式名衝突、變數重置等,WordPress會精神分裂的!);

解析2: 載入WP根目錄下wp-load.php檔案,執行初始化工作,如初始化常量、環境、載入類庫和核心程式碼等完成WordPress環境啟動工作,如載入wp-includes目錄下functions.php(函式庫)、class-wp.php(類庫)、plugin.php(外掛)、pomo目錄(語言包)、query.php(資料請求)、theme.php(載入主題檔案)、post-template.php(文章模板)、comment.php(評論模板)、rewrite.php(URL重寫)等等。

解析3: 執行wp()函式,執行內容處理工作,如根據使用者的請求呼叫相關函式獲取和處理資料,為前端展示準備資料;

解析4: 載入根目錄絕對路徑下wp-includes目錄中template-loader.php檔案,執行主題應用工作,如根據使用者的請求載入主題模板。

WordPress之所以能將使用者請求的頁面生成出來,都是最後這三行核心程式碼起的作用。
wp-load.php會完成頁面生成所需要的所有環境、變數、API等,相當於做了好準備工作;wp()函式根據使用者請求的URL從資料庫中取出相應的資料內容備用;
template-loader.php把已經準備好的內容用主題所設定的樣式展現方式給拼接出來。這三項工作完成,就可以將使用者請求的頁面展現出來了。
我們姑且將這三項工作也認定為三個大步驟,以下將重點分析。

第三步:載入wp-load.php檔案(初始化)

該檔案初始化常量(如:定義絕對路徑、設定功能檔案及內容檔案路徑等)並載入wp-config.php檔案(本處不分析wp-config.php檔案不存在的情況),部分核心程式碼如下:

define( `ABSPATH`, dirname(__FILE__) . `/` );

# 定義常量ABSPATH為根目錄絕對地址;

require_once( ABSPATH . `wp-config.php` );

# 載入根目錄下wp-config.php檔案;

從程式碼看出,本檔案的主要作用就是載入wp-config.php檔案,故我們可以抽象的將之看作是wp-load.php初始化時的第一個小步驟,具體如下:

1. 載入wp-config.php檔案

該檔案主要用於配置MySQL資料庫通訊資訊、設定資料庫表名字首、設定金鑰、設定語言及檔案絕對路徑等,部分核心程式碼如下(為省事就直接在程式碼後加#然後解釋含義了):

define(`DB_NAME`, `db_name`);

# 定義資料庫名db_name;

define(`DB_USER`, `db_username`);

# 定義資料庫使用者名稱db_username;

define(`DB_PASSWORD`, `db_password`);

# 定義資料庫密碼db_password;

define(`DB_HOST`, `db_host_location`);

# 定義資料庫主機地址,如localhost或其他IP;

define(`DB_CHARSET`, `utf8`);

# 定義資料表預設文字編碼,如utf8;

$table_prefix = `wp_`;

# 定義資料庫表字首,一般預設為wp_;

define(`WPLANG`, `zh_CN`);

# 定義WordPress語言,中文預設zh_CH,使用的漢化語言檔案為/wp-content/languages目錄下的zh_CH.mo檔案,該檔案為二進位制,檢視具體中文可見zh_CH.po檔案;

define(`WP_DEBUG`, false);

# 設定開發環境DEBUG,預設為false不開啟;

require_once(ABSPATH . `wp-settings.php`);

# 載入根目錄下wp-settings.php檔案;

程式碼中定義的資料庫常量主要用於資料請求時通訊資料庫,本檔案還有個主要作用就是載入了wp-settings.php檔案,而該檔案相當於啟動WordPress環境的總指揮,下面我們就將該檔案作為初始化的第二步來分析。

2. 載入wp-settings.php檔案

該檔案主要用於建立和定義常見變數、函式和類的庫來為WordPress執行做準備,也就是說WordPress執行過程中使用的大多數變數、函式和類等核心程式碼都是在這個檔案中定義的。這個檔案相當於一個總控制器,很多常量定義、函式定義等都是在其他檔案中完成,而該檔案的作用就是執行那些檔案或執行在那些檔案中已經定義好的函式。

該檔案原始碼分析內容較多,詳見”WordPress核心檔案wp-setting.php原始碼分析”。

第四步: 執行wp()函式(內容處理)

在這一階段,呼叫wp()函式對資料庫內容進行查詢,並將查詢的內容賦值給一些全域性變數,方便在模板中使用模板標籤獲取相應的資料並展示在前端。該函式原始碼如下:

function wp( $query_vars = `` ) {
    global $wp, $wp_query, $wp_the_query;
    # 對變數$wp,$wp_query,$wp_the_query進行全域性化;
    $wp->main( $query_vars );
    # 見解析1;
    if ( !isset($wp_the_query) )
        $wp_the_query = $wp_query;
    # 見解析2;
}

解析1:呼叫$wp->main(),即呼叫物件$wp的main()方法,該物件是class-wp.php檔案中WP類例項化得到的,該類主要用於啟動WordPress環境,main()方法原始碼分析詳見“WordPress核心類WP內main()方法原始碼分析”;

解析2:判斷$wp_the_query是否設定,若未設定將其賦值為$wp_query,該物件是query.php檔案中WP_Query類例項化得到的,該類作用強大,幾乎WP所需要的所有資料資訊都是由該類得到的,所以內容的準備工作基本都是這段程式碼來完成的,該類的具體分析見“”;

至此,WP根據請求準備相應資料的工作也已經完成,下面就需要載入模板並把這些資料展現到前臺去了。

第五步:載入template-loader.php檔案(主題應用)

該檔案根據使用者URL返回載入相應模板,其原始碼如下:

if ( defined(`WP_USE_THEMES`) && WP_USE_THEMES )
    do_action(`template_redirect`);
# 如果常量WP_USE_THEMES存在且值為真,則觸發掛載點(動作鉤子)template_redirect;
if ( is_robots() ) :
    do_action(`do_robots`);
    return;
elseif ( is_feed() ) :
    do_feed();
    return;
elseif ( is_trackback() ) :
    include( ABSPATH . `wp-trackback.php` );
    return;
endif;
# 判斷函式is_robots(), is_feed() 和 is_trackback()的返回結果,處理 feeds 和 trackbacks,即使沒有使用任何主題;
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_tax()            && $template = get_taxonomy_template()       ) :
    elseif ( is_front_page()     && $template = get_front_page_template()     ) :
    elseif ( is_home()           && $template = get_home_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_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;
    # 見解析1;
    if ( $template = apply_filters( `template_include`, $template ) )
        include( $template );
    return;
endif;
# 若template_include過濾鉤子上有掛載函式,則對$template進行應用,最終將內容呈現給使用者;

解析1:如果常量WP_USE_THEMES存在且值為真,則判斷頁面型別同時給$template變數賦相應值;其中,判斷頁面型別的函式如is_404()位於wp-includes目錄下query.php檔案,該函式返回物件$wp_query中is_404()方法,若is_404()為false則繼續往下判斷是否是其他頁面;若為true則給$template賦值為get_404_template(),該函式位於wp-includes目錄下template.php檔案,它返回get_query_template(`404`),而該函式將頁面型別傳入陣列$templates並應用呼叫函式locate_template($templates)且應用過濾器;locate_template()函式根據傳入陣列在主題中查詢到相應的檔案然後交給load_template()函式然後使用require載入,最終將使用者需要的頁面呈現出來;


相關文章