plugins_loaded → muplugins_loaded → 載入啟用插件 → init → wp_loaded → 模板載入 → wp_head / wp_footer 等 Hook 觸發
<?php
/**
* Plugin Name: My Awesome Plugin
* Plugin URI: https://example.com/plugin
* Description: 簡短說明,顯示於後台插件列表
* Version: 1.0.0
* Requires at least: 5.8
* Requires PHP: 7.4
* Author: Your Name
* Author URI: https://example.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-awesome-plugin
* Domain Path: /languages
*/
wp_plugins 緩存,顯示於後台。Text Domain 用於國際化 __('string', 'text-domain')。wp-settings.php 關鍵代碼// 簡化版核心邏輯
if ( $active_plugins = get_option('active_plugins') ) {
foreach ( $active_plugins as $plugin_file ) {
// $plugin_file 形如 'my-plugin/my-plugin.php' (相對於 WP_PLUGIN_DIR)
if ( validate_file($plugin_file) === 0 && file_exists(WP_PLUGIN_DIR . '/' . $plugin_file) ) {
include_once WP_PLUGIN_DIR . '/' . $plugin_file;
}
}
}
active_plugins 選項存儲啟用插件的相對路徑陣列(單站點)或序列化陣列(多站點)。mu-plugins (Must-Use) 目錄下檔案自動載入,無需啟用,優先於一般插件。include_once 確保重複啟用不報錯,但也意味著插件主檔案只能定義函數/類/掛 Hook,不可直接輸出內容。| 類型 | 函數 | 用途 | 回傳值 |
|---|---|---|---|
| Action | add_action() / do_action() | 在特定時間點執行副作用(寫 DB、送信、輸出 HTML) | 忽略 |
| Filter | add_filter() / apply_filters() | 修改/過濾資料(標題、內容、查詢參數) | 必須回傳修改後的值 |
// add_action( $hook, $callback, $priority = 10, $accepted_args = 1 )
add_action('wp_footer', 'my_output', 5, 0); // 較早執行 (priority 5 < 10)
add_filter('the_content', 'my_modify', 10, 2); // 接收 2 參數:$content, $post
<?php
// my-plugin.php
defined('ABSPATH') or die; // 防止直接存取
// 1. 早期初始化:載入文字域、定義常數
add_action('plugins_loaded', function() {
load_plugin_textdomain('my-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
define('MY_PLUGIN_VERSION', '1.0.0');
});
// 2. 核心功能掛載:init 階段(當前用戶已確認、主題函數可用)
add_action('init', function() {
// 註冊自訂文章類型、分類法、REST API 端點
register_post_type('book', [...]);
register_taxonomy('genre', 'book', [...]);
});
// 3. 前端資源佇列:wp_enqueue_scripts(僅前端)
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('my-plugin', plugin_dir_url(__FILE__) . 'assets/style.css');
wp_enqueue_script('my-plugin', plugin_dir_url(__FILE__) . 'assets/app.js', ['jquery'], null, true);
wp_localize_script('my-plugin', 'MyPluginAjax', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('my_plugin_nonce'),
]);
});
// 4. 後台選單:admin_menu
add_action('admin_menu', function() {
add_options_page('My Plugin', 'My Plugin', 'manage_options', 'my-plugin', 'my_plugin_settings_page');
});
// 5. AJAX 處理:wp_ajax_{action} / wp_ajax_nopriv_{action}
add_action('wp_ajax_my_action', 'my_ajax_handler');
add_action('wp_ajax_nopriv_my_action', 'my_ajax_handler'); // 未登入用戶
function my_ajax_handler() {
check_ajax_referer('my_plugin_nonce', 'nonce');
wp_send_json_success(['message' => 'OK']);
}
// 6. 內容修改:the_content filter
add_filter('the_content', function($content) {
if (is_singular('book')) {
$content .= '';
}
return $content; // 必須回傳
});
// 7. 輸出頁腳:wp_footer action
add_action('wp_footer', function() {
echo '';
});
muplugins_loadedplugins_loaded ← 插件主檔案多在此掛載早期邏輯load_textdomainauth_cookie_valid / set_current_userinit ← 註冊 CPT、Taxonomy、Rewrite Rules、REST Endpointswp_loaded ← 處理 POST/Redirect、表單提交parse_request / wp / template_redirectget_header → wp_headloop_start → the_post → the_content (Filter)get_footer → wp_footer (Action)shutdownQuery Monitor 外掛:查看 Hook 執行順序、耗時、SQL、HTTP 請求。add_action('all', function($hook) { error_log($hook); }); 記錄所有觸發的 Hook(慎用,極其噪雜)。wp cli hook list 列出所有 Hook 及掛載的回調。wpackagist-plugin/... 或私有倉庫。require vendor/autoload.php 並啟動 Service Provider。wp plugin activate my-plugin、wp eval 'var_dump(apply_filters("the_content", "test"));'。wp scaffold plugin-tests + phpunit,搭配 WordPress/WordPress-Coding-Standards。原文《淺析Wordpress的插件執行流程》重構,補充完整 Hook 生命週期、現代開發工具鏈、除錯技巧與最佳實踐。