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('[email protected]', '新文章发布', $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
方法二:搜索引擎搜索
site:developer.wordpress.org "add_action"方法三:搜索插件/主题源代码
bash
grep -r "add_action\|add_filter" wp-includes/
grep -r "do_action\|apply_filters" wp-includes/钩子最佳实践
- 使用前缀命名函数 - 避免与其他插件冲突
- 指定优先级 - 确保执行顺序正确
- 移除时指定优先级 - 与添加时保持一致
- 使用类封装 - 更清晰的代码结构
- 记录钩子使用 - 便于维护和调试
- 避免在钩子中执行繁重操作 - 影响性能
