WordPressのadd_filterで追加した無名関数を消す方法

ここ数ヶ月WordPressの案件のヘルプに入っていた。

緯度経度を使って現在地から近い順で並び替えたい要望があり実装、アクセス数の都合上GoogleMapAPIは使えなかったのでDBに処理を書くことにした。

そこで、タイトルに書いた問題が発生した、ググると日本語の記事がまだ見つからなかったので社内で共有した殴り書きを流用。

WordPressが初めてでディープなところに踏み込めたのは意外と嬉しかった。

WordPressでクエリの投げる直前に、任意のクエリを埋め込む時に使用するフック関数

add_filter('posts_join', 'add_query_join_lat');

これだと任意の引数が渡せない、解決するには現状思いつくのが無名関数を渡しスコープ外の変数を渡す方法

add_filter('posts_fields', function ($fields) use ($lat, $lng) {
    $km = 6371;
    $mile = 3959;
    $latlng_distance = ",(
        $km * acos(
            cos(radians($lat))
            * cos(radians(META_KEY_LAT.meta_value))
            * cos(radians(META_KEY_LNG.meta_value) - radians($lng))
            + sin(radians($lat))
            * sin(radians(META_KEY_LAT.meta_value))
        )
    ) AS distance";
    $fields .= $latlng_distance;
    return $fields;
});

このフック関数の悪い所は、addしたら全てのクエリ発行の時にこのフックが走ってしまうところで、解除するにはこんな感じで解除したい関数名を書く必要がある。

remove_filter('posts_join', 'add_query_join_lat');

ここで問題が発生、無名関数は名前がないから削除できない。

一度無名関数を変数に格納してやればいけるんじゃ?と思いやってみたが、変数に無名関数を渡しただけで名前はついていないのでやっぱり消せない。

$hoge = function() {echo 1;}

add_filter('posts_join', $hoge);

remove_filter('posts_join', "hoge");

ググってたらやっぱり同じ問題にぶち当たっている外人が沢山いた、結局使ったのがこれ。

https://stackoverflow.com/a/48480358

これだとプラグインで使用している無名関数も消えてしまうので、フックした関数名、無名関数で利用した引数の名前をチェックして削除するように改善

global $wp_filter;

foreach ($wp_filter as $filter_name => $filter_properties) {
    if($filter_name === "posts_fields") {
        foreach ($filter_properties->callbacks as $priority) {
            foreach($priority as $function) {
                if(is_object($function["function"]) == true) {
                    $tmp_function = new ReflectionFunction($function["function"]);

                    $static_keys = array_keys($tmp_function->getStaticVariables()); // useで指定した$lat, $lngが格納されている
                    $parameter_keys = array_keys($tmp_function->getParameters()); // 関数で使用する引数が格納されている
                    
                    $is_distance_function = (in_array('lat', $static_keys) && in_array('lng', $static_keys)) && in_array('fields', $parameter_keys);
                    
                    if($is_distance_function) unset($wp_filter[$filter_name]);
                }
            }
        }
    }
}