写一个 WordPress 插件听起来像是高手才能做的事,但实际上,只要了解了基本的框架和规范,任何人都可以从零开始写出一个可用的插件。插件不一定要很复杂,哪怕只是给文章增加一个自定义字段、在后台增加一个设置页面、或者在页面底部增加一段统计代码,都可以封装成插件。相比放在主题的 functions.php 中,插件的最大好处是和主题解耦——更换主题时功能不会丢失。

第一步:创建插件目录和主文件

在 /wp-content/plugins/ 下创建一个文件夹,命名应为小写字母和连字符,例如 my-custom-plugin。在里面创建一个主 PHP 文件,命名通常与目录相同,例如 my-custom-plugin.php

主文件的开头需要有一段插件头部注释,否则 WordPress 无法识别:

<?php
/**
 * Plugin Name: 我的自定义插件
 * Plugin URI:  https://你的网站.com
 * Description: 这是一个示例插件,用于学习插件开发
 * Version:     1.0.0
 * Author:      你的名字
 * Author URI:  https://你的网站.com
 * License:     GPL v2 or later
 * Text Domain: my-plugin
 */

添加这个头部后,插件就会出现在后台的插件列表中,可以被激活和停用。

第二步:定义安全常量

为了防止直接访问插件文件,通常在头部注释之后立即判断 ABSPATH

if (!defined('ABSPATH')) {
    exit;
}

同时,可以定义插件的路径常量和 URL 常量,方便后续引用:

define('MY_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MY_PLUGIN_URL', plugin_dir_url(__FILE__));

第三步:激活和停用钩子

很多插件需要在激活时执行一些初始化操作,比如创建数据库表、设置默认选项。可以用 register_activation_hook 实现:

register_activation_hook(__FILE__, 'my_plugin_activate');
function my_plugin_activate() {
    // 创建数据库表
    // 添加默认选项
    flush_rewrite_rules();
}

停用时的清理操作:

register_deactivation_hook(__FILE__, 'my_plugin_deactivate');
function my_plugin_deactivate() {
    // 清除定时任务
    // 不要删除用户数据,除非确实是卸载操作
    flush_rewrite_rules();
}

第四步:创建数据库表(如果需要)

如果你的插件需要存储结构化数据,比如一个访客统计表或者一个表单提交记录表,应该在激活时创建。创建表的函数通常使用 dbDelta

function my_plugin_create_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_plugin_data';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
        content text NOT NULL,
        PRIMARY KEY (id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}
register_activation_hook(__FILE__, 'my_plugin_create_table');

注意:dbDelta 很智能,它只会添加新增的列,不会删除已有的列。所以如果你修改了表结构,需要手动处理迁移逻辑。

第五步:主逻辑与钩子注册

插件的主要功能通常通过钩子实现。例如,你想在前端所有页面的底部添加一段自定义代码:

add_action('wp_footer', 'my_plugin_add_footer_code');
function my_plugin_add_footer_code() {
    echo '<div>这是插件添加的内容</div>';
}

或者你想在后台添加一个设置页面,通常在 admin_menu 钩子上添加:

add_action('admin_menu', 'my_plugin_add_admin_menu');
function my_plugin_add_admin_menu() {
    add_options_page(
        '我的插件设置',     // 页面标题
        '我的插件',         // 菜单标题
        'manage_options',  // 权限
        'my-plugin-settings', // 菜单 slug
        'my_plugin_settings_page' // 回调函数
    );
}

function my_plugin_settings_page() {
    ?>
    <div class="wrap">
        <h1>我的插件设置</h1>
        <form method="post" action="options.php">
            <?php settings_fields('my_plugin_settings_group'); ?>
            <table class="form-table">
                <tr>
                    <th>是否启用功能</th>
                    <td><input type="checkbox" name="my_plugin_enabled" value="1" <?php checked(get_option('my_plugin_enabled'), 1); ?> /></td>
                </tr>
            </table>
            <?php submit_button(); ?>
        </form>
    </div>
    <?php
}

同时需要注册这些设置项:

add_action('admin_init', 'my_plugin_register_settings');
function my_plugin_register_settings() {
    register_setting('my_plugin_settings_group', 'my_plugin_enabled');
}

第六步:使用类组织代码

对于稍微复杂一点的插件,建议用面向对象的方式组织。一个典型的插件类结构如下:

class My_Plugin {
    private static $instance = null;
    
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        add_action('wp_footer', array($this, 'footer_code'));
    }
    
    public function footer_code() {
        // 代码
    }
}

My_Plugin::get_instance();

单例模式可以避免多个实例冲突,这是 WordPress 插件开发中的常见模式。

第七步:卸载钩子

当用户决定删除插件时,应该清理掉插件创建的所有数据。创建 uninstall.php 文件放在插件根目录,内容如下:

<?php
if (!defined('WP_UNINSTALL_PLUGIN')) {
    exit;
}

global $wpdb;
$table_name = $wpdb->prefix . 'my_plugin_data';
$wpdb->query("DROP TABLE IF EXISTS $table_name");
delete_option('my_plugin_enabled');

WordPress 检测到 uninstall.php 时会自动在删除插件时调用。

插件开发是 WordPress 生态中最有价值的能力之一。学会开发插件之后,你就能把功能打包成可复用的模块,在不同项目之间轻松迁移,甚至发布到 WordPress 官方插件库供全球用户使用。