外掛是 WordPress 擴充功能的主要方式。當功能不應該跟著主題一起消失,就應該考慮寫成外掛。本章從最小外掛開始,介紹外掛檔頭、啟用、Hook 與內容過濾範例。
能建立可啟用的外掛,理解 Action 與 Filter,並用外掛修改文章內容。
11.1 WordPress 外掛入門
外掛至少需要一個 PHP 檔案與外掛檔頭。放在 wp-content/plugins 後,WordPress 會讀取檔頭並顯示在外掛列表。
<?php
/*
Plugin Name: WP2026 Demo Plugin
Description: 課程示範外掛。
Version: 1.0.0
Author: minhuangyuntech
*/
外掛與主題的分工
主題負責外觀,外掛負責功能。若功能與網站資料、商業邏輯、後台管理或 API 有關,通常應放在外掛。
建議的外掛資料夾結構
當外掛只有一個小功能時,一個 PHP 檔案就足夠;但只要開始加入後台頁面、前台樣式、短代碼或 AJAX,就應該拆分檔案,避免主檔變得難以維護。
wp2026-demo-plugin/
├── wp2026-demo-plugin.php
├── includes/
│ ├── class-assets.php
│ ├── class-shortcodes.php
│ └── class-admin-page.php
├── assets/
│ ├── css/
│ └── js/
└── languages/
主檔通常負責檢查安全常數、定義版本與路徑常數、載入必要檔案,並啟動外掛。
if (!defined('ABSPATH')) {
exit;
}
define('WP2026_PLUGIN_VERSION', '1.0.0');
define('WP2026_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WP2026_PLUGIN_URL', plugin_dir_url(__FILE__));
啟用、停用與移除
外掛可在啟用、停用或移除時執行特定工作。例如建立預設選項、清除排程事件或刪除暫存資料。
register_activation_hook(__FILE__, 'wp2026_activate_plugin');
function wp2026_activate_plugin() {
add_option('wp2026_enabled', 'yes');
}
register_deactivation_hook(__FILE__, 'wp2026_deactivate_plugin');
function wp2026_deactivate_plugin() {
wp_clear_scheduled_hook('wp2026_daily_event');
}
若要在外掛刪除時清除資料,應建立 uninstall.php,但要小心不要誤刪使用者仍想保留的資料。
外掛安全基本原則
- 檔案開頭檢查
ABSPATH,避免被直接執行。 - 後台操作檢查
current_user_can()。 - 表單與 AJAX 操作檢查 nonce。
- 所有輸入先 sanitize,所有輸出再 escape。
- 資料庫查詢使用 WordPress API 或
$wpdb->prepare()。
11.2 Hooks 簡介與應用
Hook 是 WordPress 的擴充機制。Action 用來在特定時間點執行動作,Filter 用來接收資料、修改後再回傳。
Action 範例
add_action('wp_footer', 'wp2026_footer_note');
function wp2026_footer_note() {
echo '<div class="wp2026-note">感謝閱讀 WP2026</div>';
}
Action 的重點是「在某個時機做某件事」。常見 action 包含:
init:註冊 CPT、taxonomy 或初始化功能。wp_enqueue_scripts:載入前台樣式與腳本。admin_menu:加入後台選單。save_post:文章儲存時處理自訂欄位。
Filter 範例
add_filter('the_content', 'wp2026_append_message');
function wp2026_append_message($content) {
if (is_single()) {
$content .= '<p>本文由 WP2026 外掛加入。</p>';
}
return $content;
}
Filter 的重點是「接收資料、修改資料、回傳資料」。如果忘記回傳,原本內容可能消失。Filter callback 的參數數量可透過 add_filter() 第四個參數指定。
add_filter('the_title', 'wp2026_prefix_title', 10, 2);
function wp2026_prefix_title($title, $post_id) {
if (is_admin()) {
return $title;
}
return 'WP2026:' . $title;
}
優先順序與移除 Hook
add_action() 與 add_filter() 的第三個參數是優先順序,數字越小越早執行,預設是 10。若要移除 hook,必須使用相同 callback 與 priority。
add_filter('the_content', 'wp2026_append_message', 20);
remove_filter('the_content', 'wp2026_append_message', 20);
在大型專案中,清楚記錄 hook 的 priority 能避免多個外掛互相覆蓋輸出。
11.3 使用外掛過濾文章的內容範例
過濾文章內容時要注意只在合適頁面執行,並避免影響後台、摘要或 REST API 回應。
加入條件與安全輸出
function wp2026_append_course_cta($content) {
if (!is_singular('post') || !in_the_loop() || !is_main_query()) {
return $content;
}
$cta = '<aside class="course-cta">繼續學習下一章</aside>';
return $content . $cta;
}
add_filter('the_content', 'wp2026_append_course_cta');
把 HTML 組合抽成函式
當附加內容變複雜時,不建議直接在 filter callback 中串接一大段 HTML。可以把 HTML 產生邏輯抽成另一個函式,讓主流程更清楚。
function wp2026_render_course_cta() {
$next_url = home_url('/lessons/');
return sprintf(
'<aside class="course-cta"><a href="%s">%s</a></aside>',
esc_url($next_url),
esc_html('查看所有課程')
);
}
避免重複加入內容
某些頁面建構器或特殊模板可能多次呼叫 the_content。如果你的外掛不能重複輸出,可使用靜態變數保護。
function wp2026_append_once($content) {
static $added = false;
if ($added) {
return $content;
}
$added = true;
return $content . wp2026_render_course_cta();
}
本章練習
- 建立一個可啟用外掛。
- 在文章結尾加入課程提示區塊。
- 限制提示只出現在單篇文章頁。
- 停用外掛並確認提示消失。