結論
remove_action() が効かないときの原因は、ほぼ次の4つに絞れます。
- 優先度が一致していない(追加側が 20 なのに、削除側は省略=10)
- 削除を書いた場所が早すぎる(追加される前に実行している)、または遅すぎる(フックがすでに実行された後)
- クラスメソッドを、インスタンスではなく文字列のクラス名で指定している
- クロージャ(無名関数)で追加されている
remove_action() は、外せなかったときに警告もエラーも出さず、戻り値 false を返すだけです。そのため、「書いたのに何も起きない」状態になりやすいのが特徴です。
原因・仕組み
remove_action() は remove_filter() の別名で、内部では「フック名・コールバック・優先度」の3つがすべて一致する登録を探して削除します(公式リファレンスにも、優先度は追加時と完全に一致させる旨の記載があります)。つまり「だいたい合っている」では外れません。
また、WordPress のフックは「登録された順に配列へ入れ、実行時にその配列を回す」仕組みです。登録されていないものは外せませんし、すでに実行済みのフックから外しても、その回の処理には間に合いません。
手順(サンプルコード)
1. 優先度を合わせる
function greet() { echo "hellon"; }
add_action( 'demo_hook', 'greet', 20 );
remove_action( 'demo_hook', 'greet' ); // 優先度10と解釈され、外れない(false)
remove_action( 'demo_hook', 'greet', 20 ); // 追加時と同じ20なので外れる(true)
相手のコードで優先度が分からないときは、has_action( 'demo_hook', 'greet' ) を使います。登録されていればその優先度の数値が、なければ false が返ります。
2. 登録の後・実行の前に外す
// 外したい側(テーマやプラグイン)
add_action( 'after_setup_theme', function () {
remove_action( 'wp_head', 'wp_generator' ); // wp_head が実行される前に外す
} );
追加元が plugins_loaded や init で登録している場合は、それより後のフック(もしくは同じフックで大きい優先度)で外します。
add_action( 'init', function () {
remove_action( 'template_redirect', 'plugin_redirect_func', 10 );
}, 99 );
3. クラスメソッドはインスタンスで指定する
class Plugin_X {
public function __construct() { add_action( 'demo_hook2', array( $this, 'run' ) ); }
public function run() { echo "runn"; }
}
$GLOBALS['plugin_x'] = new Plugin_X();
remove_action( 'demo_hook2', array( 'Plugin_X', 'run' ) ); // false:外れない
remove_action( 'demo_hook2', array( $GLOBALS['plugin_x'], 'run' ) ); // true:外れる
インスタンスをグローバル変数や静的プロパティで参照できない場合は、自分でそのコードを書き換えるのではなく、作者が用意しているフィルターや設定項目がないかを先に探します。
4. クロージャは基本的に外せない
クロージャは毎回別のオブジェクトになるため、同じ見た目の無名関数を書いても外れません。最終手段として remove_all_actions( 'フック名' )(優先度の指定も可)がありますが、他のコールバックも消えるため、影響範囲を確認してから使います。
動作確認(検証環境と結果)
WordPress 7.1.2(ja)、PHP 8.2.12 の CLI テスト環境で実行しました(C:Tempwp-testrun.php に上記のコードを渡して実行)。
- 登録前の
remove_actionを書いた後にadd_action(..., 20)→has_actionは20(まだ登録されている) - 優先度省略の
remove_action→false、has_actionは20のまま - 優先度 20 を指定 →
true、has_actionはfalse - クラス名文字列での指定 →
false、インスタンス指定 →true - 別のクロージャで外そうとした →
false、remove_all_actions→ 全削除 - 別のフック(
timing_a)のコールバックでdemo_lateの登録を、do_action( 'demo_late' )の前に外した → 外れて実行されなかった
詳細ログは testsremove-action-not-working.md に保存しています。
remove_action が効かない原因と対処の注意点
- 他人のコードに
remove_actionを使うと、プラグインの更新で優先度が変わった際に、気付かないまま外れなくなることがあります。更新後の確認を習慣にしてください。 has_action()で事前に確認してから外すと、デバッグしやすくなります。- 管理画面専用のフックなど、実行される場面が限られるものは、そのコンテキストの中で外します。
remove_action が効かない原因と対処でよくあるミス
functions.phpの最上部で、読み込み順の関係から、まだ追加されていないフックを外そうとする。- 追加側の優先度を確認せずに、既定値のまま外そうとする。
array( 'クラス名', 'メソッド' )で外そうとする(静的メソッドでない限り無効)。- すでに実行された後のフック(例:
wp_headの中からwp_headの登録を外す)に対して外そうとする。
remove_action が効かない原因と対処のチェックリスト
- [ ] 追加側のコードで、フック名・関数名・優先度を確認したか
- [ ]
has_action()が、削除前に数値を返すことを確認したか - [ ] 削除コードは、追加の後・フックの実行前に動くか
- [ ] クラスの場合は、インスタンスで指定しているか
- [ ]
remove_action()の戻り値を、デバッグ時に確認したか
remove_action が効かない原因と対処のFAQ(よくある質問)
Q. 戻り値が true なのに、まだ動いているのはなぜですか?
A. 同じ処理が別のフックや、別の優先度から二重に登録されている可能性があります。has_action() を複数のフックで確認してください。
Q. フィルターの場合も同じですか?
A. 同じです。remove_filter() を使い、考え方(優先度の一致・タイミング)は共通です。
Q. 子テーマで親テーマのフックを外すには?
A. 親テーマは先に読み込まれるため、子テーマの functions.php から after_setup_theme などで外します。
筆者の見解(remove_action が効かない原因と対処)
remove_action() が失敗しても何も言ってくれない点が、一番のつまずきどころだと考えます。まず has_action() で「今どの優先度で登録されているか」を表示して、数値を目で確認する方法が、遠回りに見えて最も早いと思います。なお、クロージャで書かれたコードは「外されないことを前提にした作り」とも読めるため、無理に外さず、設定項目やフィルターでの調整を探すのが穏当と私見では考えます。
remove_action が効かない原因と対処の関連項目
- remove_action() の使い方|説明・引数・注意点
- add_action() の使い方|説明・引数・注意点
- has_action() の使い方|説明・引数・注意点
- remove_all_actions() の使い方|説明・引数・注意点
- remove_filter() の使い方|説明・引数・注意点
