Skip to content

Instantly share code, notes, and snippets.

@mikeschinkel
Created August 14, 2010 01:46
Show Gist options
  • Select an option

  • Save mikeschinkel/523831 to your computer and use it in GitHub Desktop.

Select an option

Save mikeschinkel/523831 to your computer and use it in GitHub Desktop.
<?php
/*
Plugin Name: Instrument Hooks for WordPress
Description: Instruments Hooks for a Page. Outputs during the Shutdown Hook.
Version: 0.1
Author: Mike Schinkel
Author URI: http://mikeschinkel.com
*/
if (isset($_GET['instrument']) && $_GET['instrument']=='hooks') {
add_action('shutdown','instrument_hooks');
function instrument_hooks() {
global $wpdb;
$hooks = $wpdb->get_results("SELECT * FROM wp_hook_list ORDER BY first_call");
$html = array();
$html[] = '<style>#instrumented-hook-list table,#instrumented-hook-list th,#instrumented-hook-list td {border:1px solid gray;padding:2px 5px;}</style>
<div align="center" id="instrumented-hook-list">
<table>
<tr>
<th>First Call</th>
<th>Hook Name</th>
<th>Hook Type</th>
<th>Arg Count</th>
<th>Called By</th>
<th>Line #</th>
<th>File Name</th>
</tr>';
foreach($hooks as $hook) {
$html[] = "<tr>
<td>{$hook->first_call}</td>
<td>{$hook->hook_name}</td>
<td>{$hook->hook_type}</td>
<td>{$hook->arg_count}</td>
<td>{$hook->called_by}</td>
<td>{$hook->line_num}</td>
<td>{$hook->file_name}</td>
</tr>";
}
$html[] = '</table></div>';
echo implode("\n",$html);
}
add_action('all','record_hook_usage');
function record_hook_usage($hook){
global $wpdb;
static $in_hook = false;
static $first_call = 1;
static $doc_root;
$callstack = debug_backtrace();
if (!$in_hook) {
$in_hook = true;
if ($first_call==1) {
$doc_root = $_SERVER['DOCUMENT_ROOT'];
$results = $wpdb->get_results("SHOW TABLE STATUS LIKE 'wp_hook_list'");
if (count($results)==1) {
$wpdb->query("TRUNCATE TABLE wp_hook_list");
} else {
$wpdb->query("CREATE TABLE wp_hook_list (
called_by varchar(96) NOT NULL,
hook_name varchar(96) NOT NULL,
hook_type varchar(15) NOT NULL,
first_call int(11) NOT NULL,
arg_count tinyint(4) NOT NULL,
file_name varchar(128) NOT NULL,
line_num smallint NOT NULL,
PRIMARY KEY (first_call,hook_name))"
);
}
}
$args = func_get_args();
$arg_count = count($args)-1;
$hook_type = str_replace('do_','',
str_replace('apply_filters','filter',
str_replace('_ref_array','[]',
$callstack[3]['function'])));
$file_name = addslashes(str_replace($doc_root,'',$callstack[3]['file']));
$line_num = $callstack[3]['line'];
$called_by = $callstack[4]['function'];
$wpdb->query("INSERT wp_hook_list
(first_call,called_by,hook_name,hook_type,arg_count,file_name,line_num)
VALUES ($first_call,'$called_by()','$hook','$hook_type',$arg_count,'$file_name',$line_num)");
$first_call++;
$in_hook = false;
}
}
}
@arafalov

Copy link
Copy Markdown

Just tested this. Looks good. A small problem on windows - the file names do not escape backslashes so the path runs all together (cwampwww...)

@mikeschinkel

Copy link
Copy Markdown
Author

Send a patch?

@arafalov

Copy link
Copy Markdown

I am not setup for that right now. Sorry. So, just a bug report for now.

@mikeschinkel

Copy link
Copy Markdown
Author

Can you explain the solution? I'm not sure what you are suggesting.

@arafalov

Copy link
Copy Markdown

Oh, sorry I wasn't clear.
Basically, when the instrumentation table is displayed it, the file name does not take into account the fact that Windows file names use backslashes.
So, if the file path is: C:\Wamp\www\network, it will show up as C:wampwww[\n]etwork, where [\n] is now new line.

I think changing line 77 to use addslashes does the trick:
$file_name = addslashes(str_replace($doc_root,'',$callstack[3]['file']));

@mikeschinkel

Copy link
Copy Markdown
Author

Perfect, thanks! I've edited. Can you confirm it works for you?

@arafalov

Copy link
Copy Markdown

Seems to work fine. Thank you.

@GeertDD

GeertDD commented Nov 19, 2011

Copy link
Copy Markdown

You should do an isset or empty check for $_GET['instrument'] to prevent undefined variable errors.

@mikeschinkel

Copy link
Copy Markdown
Author

@GeertDD - Sure, done.

@juancaser

Copy link
Copy Markdown

perfect just what im looking for...

made small customisation where i could filter which hook type i need to view

https://gist.github.com/thejuanwhocode/7ddb83d2a1fb0c78d359

$_GET['type']=filter

@ve3

ve3 commented Dec 23, 2023

Copy link
Copy Markdown

Tested with WordPress 6.5-alpha-57224 but when I added ?instrument=hooks to the URL, table is shown but no list and everything seems to be stopped for unknown reason. No errors show up or no in debug.log.

I try to wrap if (isset($_GET['instrument']) && $_GET['instrument']=='hooks') {...} with only add_action('all','record_hook_usage'); part and it seems to work (DB inserted) but table result did not show.
I come back to normal page and result is showing full.

I have no idea why it stopped inside record_hook_usage() without any errors.

@mikeschinkel

Copy link
Copy Markdown
Author

@ve3 — I have not worked with WordPress for over 4 years so can’t tell you what’s wrong.

My best suggestion is to get PhpStorm, and use it’s debugger to figure out why it does not work.

@ve3

ve3 commented Dec 24, 2023

Copy link
Copy Markdown

Thanks @mikeschinkel for your work. May I post a link to my gist here? It's a simple plugin that can be trace actions/filters hooks on enabled trace option.

The difference than other plugins is most of them can list actions and filters of current page but when the page is redirect or act by normal user you cannot know anything about it. My simple plugin can work on any request as long as enable trace hook option is turn on.

@mikeschinkel

Copy link
Copy Markdown
Author

Sure.

@ve3

ve3 commented Dec 24, 2023

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment