2026年7月7日

2026年7月7日

WordPressのフック優先度エラーを解決する方法

はじめに

WordPressでadd_action()add_filter()を登録したのに他のプラグインのコールバックより先に実行されてしまう・remove_action()で削除しようとしても効かない・フィルターの戻り値が別のプラグインに上書きされる・add_action('init')で登録した処理がまだ準備できていないデータに依存してエラーになるといった問題は、WordPressのフックシステムとpriority(優先度)の仕組みを正しく理解することで解決できます。

症状・原因

  • remove_action()に渡すpriority・コールバックがadd_action()時と異なるため削除に失敗している
  • 匿名関数(クロージャ)で登録したフックはremove_action()で削除できない
  • フックのpriority(デフォルト10)が他のプラグインと衝突しており意図しない実行順になっている
  • add_action('plugins_loaded')で登録すべき処理をadd_action('init')で登録しており他プラグインのロードを待てていない

解決手順

ステップ1:フックの状態を診断する

# 特定フックに登録されているコールバックを確認
wp eval "
global \$wp_filter;
if (isset(\$wp_filter['init'])) {
    foreach (\$wp_filter['init']->callbacks as \$priority => \$cbs) {
        foreach (\$cbs as \$id => \$cb) {
            \$fn = is_string(\$cb['function']) ? \$cb['function']
                 : (is_array(\$cb['function']) ? get_class(\$cb['function'][0]) . '::' . \$cb['function'][1]
                 : '[closure]');
            echo \$priority . ': ' . \$fn . PHP_EOL;
        }
    }
}
"

# フックが登録されているか確認
wp eval "var_dump(has_action('save_post', 'my_callback_function'));"

# フックの優先度を確認
wp eval "var_dump(has_filter('the_content', 'wpautop'));"
# 返値: false(未登録)または int(優先度)

# 現在のフィルター実行状況
wp eval "echo doing_filter('the_content') ? 'Running' : 'Not running';"

ステップ2:フックの優先度を正しく設定する

// WordPress フック優先度の基本
// 数値が小さいほど先に実行される(デフォルト: 10)
// 負の値も使用可能

// ✅ 他のプラグインより前に実行(priority=1)
add_filter('the_content', 'my_early_filter', 1);

// ✅ 他のプラグインより後に実行(priority=99)
add_filter('the_content', 'my_late_filter', 99);

// WordPress コアの主要フックのデフォルトpriority
// wpautop: 10
// wptexturize: 10
// convert_smilies: 20
// shortcode_unautop: 10
// do_shortcode: 11

// ✅ wpautop より後にショートコードを処理したい場合
add_filter('the_content', 'do_shortcode', 11); // デフォルトで 11

// コールバックを名前付き関数で登録(remove_action で削除可能)
function my_init_setup(): void {
    // 初期化処理
    register_post_type('my_type', [...]);
}
add_action('init', 'my_init_setup', 10);

// 後から削除できる
remove_action('init', 'my_init_setup', 10); // priority 一致が必須

// クラスメソッドのフック登録と削除
class My_Plugin {
    public function __construct() {
        add_action('init', [$this, 'setup'], 10);
        add_filter('the_title', [$this, 'filter_title'], 10, 2);
    }

    public function setup(): void { /* ... */ }
    public function filter_title(string $title, int $post_id): string {
        return $title;
    }
}

$plugin = new My_Plugin();
// クラスメソッドの削除(インスタンスが必要)
remove_action('init', [$plugin, 'setup'], 10);

ステップ3:他のプラグインのフックを削除・上書きする

// 他のプラグインのフックを削除する
// ❌ 削除できない(クロージャは参照できない)
// remove_action('init', function() { ... }, 10);

// ✅ named function は削除できる
remove_action('init', 'other_plugin_init_function', 10);

// ✅ クラスメソッドのフックを削除(グローバルインスタンスが必要)
add_action('init', function(): void {
    global $other_plugin; // プラグインがグローバル変数を使っている場合
    if ($other_plugin instanceof Other_Plugin) {
        remove_action('wp_head', [$other_plugin, 'add_styles'], 10);
    }
}, 20); // priority=20 で other_plugin の init より後に実行

// $wp_filter を直接操作(最終手段)
add_action('init', function(): void {
    global $wp_filter;
    if (!isset($wp_filter['wp_head'])) {
        return;
    }
    foreach ($wp_filter['wp_head']->callbacks as $priority => $callbacks) {
        foreach ($callbacks as $key => $callback) {
            // 特定のクラスのコールバックを削除
            if (is_array($callback['function']) &&
                $callback['function'][0] instanceof Bad_Plugin_Class) {
                unset($wp_filter['wp_head']->callbacks[$priority][$key]);
            }
        }
    }
}, 99);

// フィルターを一時的に無効化(remove → 処理 → 再登録)
function get_raw_content(int $post_id): string {
    remove_filter('the_content', 'wpautop');
    remove_filter('the_content', 'wptexturize');

    $content = apply_filters('the_content', get_post_field('post_content', $post_id));

    add_filter('the_content', 'wpautop');
    add_filter('the_content', 'wptexturize');

    return $content;
}

ステップ4:フック実行タイミングを正しく選ぶ

// WordPress フック実行順(主要なもの)
// muplugins_loaded  → MUプラグイン読み込み後
// plugins_loaded    → 全プラグイン読み込み後(他プラグインのクラスが使える)
// setup_theme       → テーマ読み込み前
// after_setup_theme → テーマ読み込み後(add_image_size等)
// init              → WordPress完全初期化後(post_type/taxonomy登録)
// wp_loaded         → init後、全フック登録完了後
// wp                → クエリ解決後
// template_redirect → テンプレート選択前
// wp_head           → <head>出力時
// wp_footer         → </body>直前

// ✅ 他プラグインのクラスを使う場合は plugins_loaded を使う
add_action('plugins_loaded', function(): void {
    if (class_exists('WooCommerce')) {
        // WooCommerce が読み込まれていることが保証される
        add_filter('woocommerce_product_tabs', 'my_custom_tab');
    }
});

// ✅ REST APIリクエスト時のみ処理する
add_action('rest_api_init', function(): void {
    // REST API専用の処理(通常リクエストには影響しない)
    remove_filter('the_content', 'wpautop');
});

// ✅ フロントエンドのみ処理する
add_action('wp_enqueue_scripts', function(): void {
    // is_admin() が false の時のみ実行される
    wp_enqueue_style('my-style', get_template_directory_uri() . '/style.css');
});

ステップ5:カスタムフックを作成してプラグイン間連携する

// カスタムアクションフックの作成(他のプラグインが拡張できる)
function my_plugin_process_data(array $data): void {
    // 処理前にカスタムフックを実行
    do_action('my_plugin_before_process', $data);

    // 処理
    $result = process($data);

    // データをフィルタリング(他のプラグインが変更可能)
    $result = apply_filters('my_plugin_result', $result, $data);

    // 処理後にカスタムフックを実行
    do_action('my_plugin_after_process', $result, $data);
}

// 他のプラグインが拡張できるフィルター
add_filter('my_plugin_result', function(array $result, array $data): array {
    // 結果を加工して返す
    $result['extra'] = 'added_by_extension';
    return $result;
}, 10, 2);

// フックの引数数を正しく設定(accepted_args)
add_filter('the_title', function(string $title, int $post_id): string {
    return $title;
}, 10, 2); // ✅ 第4引数に引数の数を指定(デフォルト1)

注意事項

  • remove_action()remove_filter()add_action()add_filter()と完全に同じpriority・コールバックを指定しないと削除できません。コールバックが匿名関数(クロージャ)の場合は削除できないため、削除が必要な処理は必ず名前付き関数またはクラスメソッドで登録してください
  • フックのpriority競合を解決するにはvar_dump(has_filter('hook_name', 'callback'))で現在のpriorityを確認し、それより大きな値(後に実行)または小さな値(先に実行)を選んでください

まとめ

WordPressフック優先度エラーの解決は①global $wp_filterで登録済みコールバックを確認・has_action()でフック登録状態とpriorityを確認・doing_filter()で現在実行中のフックを確認、②priorityは小さいほど先に実行・デフォルト10を基準に前後を選択・クロージャはremove_action()で削除できないため名前付き関数を使用、③remove_action()はadd時と同じpriority必須・$wp_filterを直接操作して削除する最終手段・フィルターを一時無効化する場合はremove→処理→再登録、④plugins_loadedで他プラグインのクラス依存処理・after_setup_themeでテーマ依存処理・rest_api_initでREST API専用処理、⑤do_action/apply_filtersでカスタムフックを作成・accepted_argsで引数数を正しく設定・プラグイン間連携には公開フックを経由するの手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ