• カミナリツイッターカミナリtwitter
  • カミナリフェイスブックカミナリfacebook
  • カミナリインスタグラムカミナリinstagram

山陰の情報を発信するカミナリ

ナビ

24

APRIL

ウェブFRIDAY 2020 / 4 / 24

WordPressで実行されたフックをすべて記録してみよう

WordPressで実行されたフックをすべて記録するクラスを作りました。

プラグイン使用時に、「この保存処理がされた後のフックを知りたい」という際に、調べるために作成。記録結果はWordPressの管理者メールアドレスに送信されます。

オプションとして

・除外するフックを指定する
・指定した正規表現にマッチするフック名のみ記録する
・一度記録したフックは2回目以降記録しない

といった機能もあります。
functions.php 等、適当な場所に入れて使います。
記録結果は WordPress 設定の管理者メールアドレス宛に送信されます。

ソースはこんな感じです。

 

 

/**
 * WpRecordHook
 */
class WpRecordHook{
  private
    $_list = array(), // 実行済みフックのリスト
    $_excepts = array(),  // 記録から除外するフックの連想配列
    $_isOnce = false, // 1回記録したフックは記録しないオプション
    $_regex = '/./';  // 記録するフックのマッチパターン

  function __construct(){
    $this->_init();
  }

  /**
   * 初期化処理
   *
   * @return void
   */
  function _init(){
    if(strpos($_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php')===false){ // Heartbeat API による定期的なアクセスを除外する
      add_action('all', array($this, '_rec'));
      register_shutdown_function(array($this, '_send'));
    }
  }

  /**
   * 記録から除外するフック名を指定
   *
   * @param  array $excepts
   * @return void
   */
  function addExcepts(array $excepts){
    foreach($excepts as $n){
      $this->_excepts[$n] = true;
    }
  }

  /**
   * 記録するフックのマッチパターンを設定
   *
   * @param  string $regex
   * @return void
   */
  function setRegex($regex){
    $this->_regex = $regex;
  }

  /**
   * 同じフック名は一度しか記録しないモードを有効化
   * @return void
   */
  function setOnce(){
    $this->_isOnce = true;
  }

  /**
   * フックの記録
   *
   * @return void
   */
  function _rec(){
    $is_rec = false;
    $hook = current_filter();
    if(!isset($this->_excepts[$hook]) && preg_match($this->_regex, $hook)){
      if($this->_isOnce){
        if(array_search($hook, $this->_list, true)===false){
          $is_rec = true;
        }
      }else{
        $is_rec = true;
      }
    }
    if($is_rec){
      $this->_list[] = $hook;
    }
  }

  /**
   * 記録結果を管理者メール宛に送信
   *
   * @return void
   */
  function _send(){
    $output = print_r($this->_list, true);
    $to = get_option('admin_email');
    $subject = 'WordPress Hook list.';
    wp_mail($to, $subject, $output);
  }
}

 

使用例

1. すべてのフックを記録する

<?php
$wrh = new WpRecordHook();
?>

備考: すべて記録しますが、2000件以上記録されて「わかんねーよ!」ってなります。Gmailを使っていると勝手に省略されていることがあります。→ サンプルデータ

 

2. 一度記録したフックは記録しない

<?php
$wrh = new WpRecordHook();
$wrh->setOnce();
?>

備考:初めて観測したフックしか記録しませんので、重要性の高いフックを重点的に調べられます。

 

3. 特定のフックを除外する

<?php
$wrh = new WpRecordHook();
$wrh->addExcepts(array('gettext', 'gettext_with_context')); // 翻訳系の処理で起きる2つのフックを除外する
?>

備考:特に多い翻訳系のフックを除外することで記録を見やすくします(それでも多いですが)。

 

4. 正規表現を使って記録するフックを指定する

<?php
$wrh = new WpRecordHook();
$wrh->setRegex('/^smart-cf/'); // Smart Custom Fields 関連のフックのみ記録する
?>

備考:プラグイン系のフックでは同じ先頭文字列を使っていることが多いため、ソースを調べてアタリを付けてから指定すると調査しやすいです。

 

5. 複合的に設定

<?php
$wrh = new WpRecordHook(); $wrh->setOnce();
$wrh->addExcepts(array('gettext', 'gettext_with_context'));
$wrh->setRegex('/^smart-cf/');
?>

備考:複数オプションの同時使用も可能です。

 

仕組みの解説

‘all’ という特殊なフックを指定するとすべてのフックに処理を引っ掛けることができますので、それで引っ掛けて current_filter() を使って現在のフック名調べて記録しているだけです。

なにげに厄介なのは、WordPress の Heartbeat API と呼ばれる動作で、ブラウザとサーバーが定期的に Ajax通信をするというもので、その結果1度しかアクセスしていないのに2通以上の結果報告メールが届き、どのメールを見ればいいのかわからないという混乱が発生します。

Heartbeat時は wp-admin/admin-ajax.php へアクセスしているため、REQUEST_URI に含まれている場合は記録から除外するようにして対応しています。

松本博之隠しプロフィール画像

TEXT by

松本 博之まつもとひろゆき)

1978年、鳥取県米子市生まれ。
株式会社マジックワード WEBシステムの開発・運用を担当。

WordPressの実績多数。表面的な使用方法を把握するだけでなく、WordPressのソースコードを読み解いて対応できます。
国家資格の応用情報処理技術者をなんとなくとりました。

この人が書いた他の記事を読む