Skip to content

WordPress 钩子系统

钩子概述

WordPress 钩子是扩展 WordPress 功能的核心机制,允许你在特定时刻执行自定义代码。

┌─────────────────────────────────────────────────────────────┐
│                    WordPress 核心                           │
│  ┌─────────────────────────────────────────────────────┐  │
│  │              钩子触发点 (Actions/Filters)            │  │
│  │                                                     │  │
│  │    do_action('init')                               │  │
│  │    ┌──────────┐                                     │  │
│  │    │ Plugin 1 │ ← my_custom_function()             │  │
│  │    └──────────┘                                     │  │
│  │    ┌──────────┐                                     │  │
│  │    │ Plugin 2 │ ← another_function()                │  │
│  │    └──────────┘                                     │  │
│  └─────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

两种钩子类型

类型说明用途
Action(动作)在特定事件发生时执行添加功能、修改行为
Filter(过滤器)修改数据后返回格式化输出、转换数据

动作钩子 (Actions)

基本语法

php
<?php
// 添加动作钩子
add_action($hook, $callback, $priority = 10, $accepted_args = 1);

// 移除动作钩子
remove_action($hook, $callback, $priority = 10);

// 示例
add_action('init', 'my_custom_function');

function my_custom_function() {
    // 执行的代码
}
?>

参数说明

php
<?php
add_action(
    $hook           = 'init',           // 钩子名称
    $callback       = 'my_function',    // 回调函数
    $priority       = 10,               // 优先级(数字越小越先执行)
    $accepted_args  = 1                  // 接受的参数数量
);

// 优先级示例
add_action('the_content', 'prefix_content_1', 5);   // 先执行
add_action('the_content', 'prefix_content_2', 10);  // 后执行
add_action('the_content', 'prefix_content_3', 20);  // 最后执行
?>

常用动作钩子

php
<?php
// === 初始化阶段 ===
add_action('init', 'init_function');           // WordPress 初始化
add_action('widgets_init', 'widgets_init');     // 小工具初始化
add_action('parse_request', 'parse_request');   // 请求解析后
add_action('send_headers', 'send_headers');     // 发送头部前

// === 文章相关 ===
add_action('wp_insert_post', 'on_post_save');    // 文章保存时
add_action('publish_post', 'on_publish');        // 文章发布时
add_action('delete_post', 'on_delete');           // 文章删除时
add_action('transition_post_status', 'status_change', 10, 3);

// === 头部和底部 ===
add_action('wp_head', 'add_meta_tags');          // 在 <head> 中添加内容
add_action('wp_footer', 'add_footer_scripts');   // 在 </body> 前添加内容

// === 加载资源 ===
add_action('wp_enqueue_scripts', 'load_assets'); // 加载脚本和样式
add_action('admin_enqueue_scripts', 'load_admin_assets'); // 后台资源

// === 登录相关 ===
add_action('login_init', 'custom_login_init');   // 登录页初始化
add_action('wp_login', 'on_user_login');          // 用户登录时
add_action('wp_logout', 'on_user_logout');       // 用户登出时

// === AJAX ===
add_action('wp_ajax_my_action', 'handle_ajax');         // 仅登录用户
add_action('wp_ajax_nopriv_my_action', 'handle_ajax'); // 仅访客
?>

带参数的钩子

php
<?php
// 接收参数的钩子
add_action('publish_post', 'notify_on_publish', 10, 2);

function notify_on_publish($post_id, $post) {
    // 发送通知邮件
    $author = get_userdata($post->post_author);
    $message = sprintf(
        '%s 发布了新文章: %s',
        $author->display_name,
        $post->post_title
    );
    wp_mail('admin@example.com', '新文章发布', $message);
}

// transition_post_status 钩子(3个参数)
add_action('transition_post_status', 'on_status_change', 10, 3);

function on_status_change($new_status, $old_status, $post) {
    if ($new_status === 'publish' && $old_status !== 'publish') {
        // 文章从非发布状态变为发布状态
        do_something();
    }
}
?>

过滤器钩子 (Filters)

基本语法

php
<?php
// 添加过滤器
add_filter($hook, $callback, $priority = 10, $accepted_args = 1);

// 移除过滤器
remove_filter($hook, $callback, $priority = 10);

// 示例
add_filter('the_content', 'modify_content');

function modify_content($content) {
    // 修改内容
    return $content;
}
?>

常用过滤器

php
<?php
// === 内容过滤器 ===
add_filter('the_content', 'filter_content');           // 文章内容
add_filter('the_excerpt', 'filter_excerpt');           // 文章摘要
add_filter('the_title', 'filter_title');               // 文章标题
add_filter('the_permalink', 'filter_permalink');       // 文章链接

// === 摘要和截断 ===
add_filter('excerpt_length', 'custom_excerpt_length');
add_filter('excerpt_more', 'custom_excerpt_more');

function custom_excerpt_length($length) {
    return 30;  // 改为 30 字
}

function custom_excerpt_more($more) {
    return '...';  // 改为省略号
}

// === 导航菜单 ===
add_filter('walker_nav_menu_start_el', 'nav_item_output', 10, 4);

// === 编辑器 ===
add_filter('tiny_mce_before_init', 'custom_tinymce');
add_filter('mce_buttons', 'add_tinymce_buttons');

// === 用户相关 ===
add_filter('the_author', 'display_author_name');
add_filter('get_avatar', 'custom_avatar_html', 10, 5);

// === RSS / Feed ===
add_filter('the_content_feed', 'add_feed_content');
add_filter('the_excerpt_rss', 'add_feed_excerpt');

// === 查询相关 ===
add_filter('query_vars', 'add_query_vars');
add_filter('posts_where', 'modify_query_where', 10, 2);
add_filter('posts_join', 'modify_query_join', 10, 2);

// === 标题标签 ===
add_filter('wp_title', 'custom_wp_title', 10, 3);
add_filter('document_title_parts', 'custom_document_title');
?>

内容过滤器示例

php
<?php
/**
 * 在文章内容前后添加内容
 */
add_filter('the_content', 'add_content_around');

function add_content_around($content) {
    // 只在单文章页面添加
    if (is_single()) {
        $before = '<div class="article-notice">';
        $before .= '<strong>温馨提示:</strong> ';
        $before .= '本文由作者原创,欢迎分享!</div>';
        
        $after = '<div class="article-footer">';
        $after .= '<p>觉得有帮助?<a href="#">分享给朋友</a></p>';
        $after .= '</div>';
        
        return $before . $content . $after;
    }
    
    return $content;
}

/**
 * 自动为文章添加关键词链接
 */
add_filter('the_content', 'auto_link_keywords');

function auto_link_keywords($content) {
    $keywords = array(
        'WordPress' => 'https://wordpress.org/',
        'PHP' => 'https://php.net/',
        'MySQL' => 'https://mysql.com/',
    );
    
    foreach ($keywords as $keyword => $url) {
        $content = str_replace(
            $keyword,
            '<a href="' . esc_url($url) . '">' . $keyword . '</a>',
            $content
        );
    }
    
    return $content;
}

/**
 * 修改摘录长度
 */
add_filter('excerpt_length', function($length) {
    if (is_category('tutorials')) {
        return 50;  // 教程分类 50 字
    }
    return 25;  // 其他 25 字
});
?>

创建自定义钩子

创建动作钩子

php
<?php
/**
 * 创建自定义动作钩子
 */
do_action('my_plugin_after_save', $post_id, $post);

// 插件用户可以这样使用:
add_action('my_plugin_after_save', 'handle_my_plugin_save', 10, 2);

function handle_my_plugin_save($post_id, $post) {
    // 处理保存后的逻辑
}
?>

创建带参数的动作钩子

php
<?php
/**
 * 创建带参数的钩子
 */
do_action('my_plugin_send_notification', 
    $user_id, 
    $message, 
    $type
);

// 用户使用
add_action('my_plugin_send_notification', 'handle_notification', 10, 3);

function handle_notification($user_id, $message, $type) {
    if ($type === 'email') {
        wp_mail(get_userdata($user_id)->user_email, '通知', $message);
    } elseif ($type === 'sms') {
        // 发送短信
    }
}
?>

创建过滤器钩子

php
<?php
/**
 * 创建自定义过滤器钩子
 */
$message = apply_filters('my_plugin_notification_message', 
    $message, 
    $user_id
);

/**
 * 注册新的文章类型时允许过滤
 */
function create_portfolio_post_type() {
    $args = apply_filters('portfolio_post_type_args', array(
        'labels' => array('name' => '作品集'),
        'public' => true,
        'supports' => array('title', 'editor'),
    ));
    
    register_post_type('portfolio', $args);
}
add_action('init', 'create_portfolio_post_type');

// 用户可以这样修改:
add_filter('portfolio_post_type_args', 'modify_portfolio_args');

function modify_portfolio_args($args) {
    $args['has_archive'] = true;
    $args['rewrite'] = array('slug' => 'my-portfolio');
    return $args;
}
?>

常用钩子优先级

优先级 1-4:     系统级钩子(很少使用)
优先级 5-9:     高优先级(前置处理)
优先级 10:      默认优先级
优先级 11-19:   正常优先级
优先级 20+:     低优先级(后置处理)

移除钩子

php
<?php
// 移除特定钩子
remove_action('the_content', 'wpautop', 10);

// 移除所有相同钩子
remove_all_actions('init');

// 注意:必须在相同或更早的优先级添加移除
// 错误示例:
add_action('init', 'my_init');          // priority = 10
remove_action('init', 'my_init', 5);    // wrong! priority = 5

// 正确示例:
add_action('init', 'my_init', 10);
remove_action('init', 'my_init', 10);   // same priority
?>

完整示例:主题功能扩展

php
<?php
/**
 * functions.php - 使用钩子扩展功能
 */

// 1. 添加主题支持
add_action('after_setup_theme', 'my_theme_setup');

function my_theme_setup() {
    add_theme_support('post-thumbnails');
    add_theme_support('title-tag');
    add_theme_support('html5', array('search-form', 'comment-form'));
}

// 2. 加载资源
add_action('wp_enqueue_scripts', 'my_theme_scripts');

function my_theme_scripts() {
    wp_enqueue_style('main', get_stylesheet_uri());
    
    wp_enqueue_script(
        'main',
        get_template_directory_uri() . '/js/main.js',
        array('jquery'),
        '1.0.0',
        true
    );
    
    // 本地化脚本
    wp_localize_script('main', 'myTheme', array(
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('my_nonce'),
    ));
}

// 3. 添加自定义功能
add_action('wp_footer', 'add_footer_code');

function add_footer_code() {
    echo '<!-- 自定义页脚代码 -->';
}

// 4. 过滤内容
add_filter('the_content', 'wrap_content_in_div');

function wrap_content_in_div($content) {
    if (is_single()) {
        return '<div class="article-content">' . $content . '</div>';
    }
    return $content;
}

// 5. 修改查询
add_action('pre_get_posts', 'modify_main_query');

function modify_main_query($query) {
    if (!is_admin() && $query->is_main_query()) {
        if (is_home()) {
            $query->set('posts_per_page', 10);
        }
    }
}

// 6. 添加自定义 Meta Box
add_action('add_meta_boxes', 'add_custom_meta_box');

function add_custom_meta_box() {
    add_meta_box(
        'custom_info',
        '自定义信息',
        'render_meta_box',
        'post',
        'side'
    );
}

// 7. 保存 Meta Box 数据
add_action('save_post', 'save_custom_meta');

function save_custom_meta($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    if (!current_user_can('edit_post', $post_id)) return;
    
    if (isset($_POST['custom_field'])) {
        update_post_meta($post_id, 'custom_field', sanitize_text_field($_POST['custom_field']));
    }
}
?>

查找可用钩子

方法一:WordPress Codex

访问 WordPress Hooks Database

方法二:搜索引擎搜索

site:developer.wordpress.org "add_action"

方法三:搜索插件/主题源代码

bash
grep -r "add_action\|add_filter" wp-includes/
grep -r "do_action\|apply_filters" wp-includes/

钩子最佳实践

  1. 使用前缀命名函数 - 避免与其他插件冲突
  2. 指定优先级 - 确保执行顺序正确
  3. 移除时指定优先级 - 与添加时保持一致
  4. 使用类封装 - 更清晰的代码结构
  5. 记录钩子使用 - 便于维护和调试
  6. 避免在钩子中执行繁重操作 - 影响性能

基于 WordPress官方文档 构建