博客

WordPress 钩子架构:动作与过滤器详解

每个优秀插件的基础。了解 PAGEnza AI 如何使用钩子和过滤器来创建可维护、可扩展的代码。

每个 WordPress 插件都做两件事之一:它在特定时刻运行代码(动作),或者在值被使用前修改它(过滤器)。就是这样。整个 WordPress 插件生态系统都建立在此区分之上。

理解区别——以及何时使用它们——是区分能正常工作的插件和会引起问题的插件的关键。

动作:在特定时间点执行操作

动作钩子是 WordPress(或其他插件)在特定时刻发出的信号。您将一个函数附加到该信号,当信号触发时,您的函数就会运行。

add_action( 'wp_enqueue_scripts', 'my_plugin_load_assets' );

function my_plugin_load_assets() {
    wp_enqueue_style( 'my-plugin', plugin_dir_url( __FILE__ ) . 'style.css' );
    wp_enqueue_script( 'my-plugin', plugin_dir_url( __FILE__ ) . 'script.js', ['jquery'], '1.0', true );
}

wp_enqueue_scripts 在 WordPress 准备为前端加载资源时触发。挂钩到它是在加载 CSS 和 JS 的正确方法——而不是直接将 <script> 标签放入模板。

您将经常使用的常见动作钩子:

  • init — 早期运行,适用于注册文章类型和分类法
  • wp_enqueue_scripts — 加载前端资源
  • admin_enqueue_scripts — 加载后台资源
  • save_post — 在文章保存时运行,适用于处理自定义字段
  • wp_ajax_{action} — 处理已认证用户的 AJAX 请求
  • wp_ajax_nopriv_{action} — 处理未登录用户的 AJAX 请求

过滤器:在值被使用前修改它

过滤器钩子会给您一个值,让您修改它,并期望您返回修改后的版本。然后 WordPress 使用您修改后的值而不是原始值。

add_filter( 'the_content', 'my_plugin_append_cta' );

function my_plugin_append_cta( $content ) {
    if ( ! is_single() ) {
        return $content;
    }
    $cta = '<div class="post-cta">准备尝试 PAGEnza? <a href="#">免费开始 →</a></div>';
    return $content . $cta;
}

关键规则:始终从过滤器返回某些内容。如果您忘记了 return,该值将变为 null,您将破坏任何使用它的东西。

常见的过滤器钩子:

  • the_content — 显示前的文章内容
  • the_title — 文章标题
  • wp_nav_menu_items — 导航菜单 HTML
  • upload_mimes — 允许的文件上传类型
  • plugin_action_links_{plugin-file} — 在插件列表页面显示的链接

PAGEnza AI 如何使用钩子

当插件生成器生成插件时,它会在构造函数中围绕一个类及其附加到钩子的方法来构建代码。这种模式可以避免全局函数名冲突并保持代码的组织性。

class My_Plugin {
    public function __construct() {
        add_action( 'init', [ $this, 'register_post_type' ] );
        add_action( 'wp_enqueue_scripts', [ $this, 'load_assets' ] );
        add_filter( 'the_content', [ $this, 'append_content' ] );
    }

    public function register_post_type() { /* ... */ }
    public function load_assets() { /* ... */ }
    public function append_content( $content ) {
        // always return
        return $content;
    }
}

new My_Plugin();

安全扫描特别检查 AJAX 处理程序是否在执行任何操作之前包含 nonce 检查和权限检查:

add_action( 'wp_ajax_my_action', [ $this, 'handle_ajax' ] );

public function handle_ajax() {
    // Security: verify nonce
    check_ajax_referer( 'my_nonce_action', 'nonce' );

    // Security: verify permissions
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( 'Insufficient permissions', 403 );
    }

    // Now safe to process
    $data = sanitize_text_field( $_POST['data'] ?? '' );
    wp_send_json_success( [ 'result' => $data ] );
}

缺少其中任何一个都会被标记为安全问题,并阻止插件发布。

优先级和钩子队列

当多个函数附加到同一个钩子时,它们会按照优先级(数字越小,运行越早)的顺序运行。默认优先级为 10。

add_action( 'init', 'runs_first', 5 );
add_action( 'init', 'runs_second', 10 );   // default
add_action( 'init', 'runs_last', 20 );

当您的插件需要在另一个插件的代码之前或之后运行时,这一点很重要。如果您正在修改另一个插件也修改的内容,优先级将决定谁获胜。

常见模式:如果您要覆盖主题的过滤器,请使用优先级 20 或更高,以确保在主题代码之后运行。

删除钩子

您可以删除别人添加的钩子——包括主题和插件钩子:

remove_action( 'wp_head', 'wp_generator' );  // 从 <head> 中移除 WP 版本
remove_filter( 'the_content', 'wpautop' );   // 移除自动段落包装

诀窍是:您只能在原始 add_action/add_filter 运行后调用 remove_action/remove_filter 来删除钩子。这通常意味着将您的删除操作挂钩到一个更晚的动作。