Skip to content

Instantly share code, notes, and snippets.

@gschoppe
Last active October 23, 2019 22:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gschoppe/42c6902b413939291bcc6b9cde9ab0bb to your computer and use it in GitHub Desktop.
Save gschoppe/42c6902b413939291bcc6b9cde9ab0bb to your computer and use it in GitHub Desktop.
This is a nifty little mu-plugin designed to assist in benchmarking plugins and snippets in WordPress. Enable the plugin from the 'settings' menu. NOTE: Not for use on Production Sites. Benchmark data WILL be shown to all viewers of the website.
<?php if(!defined('ABSPATH')) { die(); } // Include in all php files, to prevent direct execution
/*
Plugin Name: GS Benchmark
Plugin URI: https://gschoppe.com
Description: mu-plugin to assist in diagnosing performance issues
Version: 0.1.0
Author: Greg Schoppe
Author URI: https://gschoppe.com
*/
if( !class_exists('GSBenchmark') ) {
class GSBenchmark {
private $enabled = false;
private $start_time = 0;
private $hook_times = array();
private $hooks = array();
public static function Instance() {
static $instance = null;
if ($instance === null) {
$instance = new self();
}
return $instance;
}
private function __construct() {
$int_max = PHP_INT_MAX;
$int_min = -1 * $int_max;
$options = get_option('gs_benchmark');
if( !empty( $options['enabled'] ) ) {
$this->enabled = true;
}
if( !empty( $options['hooks'] ) ) {
$this->hooks = $options['hooks'];
}
add_action( 'admin_menu' , array( $this, 'register_admin_menu' ) );
add_action( 'admin_post_gs_benchmark_options', array( $this, 'save_options_page' ) );
if( $this->enabled ) {
add_action( 'muplugins_loaded', array( $this, 'get_start_time' ), $int_min );
if( $this->hooks && is_array( $this->hooks ) ) {
foreach( $this->hooks as $hook ) {
add_filter( $hook, array( $this, 'get_hook_start_time' ), $int_min );
add_filter( $hook, array( $this, 'get_hook_end_time' ), $int_max );
}
}
add_action( 'shutdown', array( $this, 'output_end_time' ), $int_max );
}
}
// Public Functions
public function register_admin_menu() {
add_options_page( 'GS Benchmark', 'GS Benchmark', 'manage_options', 'gs-benchmark', array( $this, 'render_options_page' ) );
}
public function render_options_page() {
$form_action = admin_url('admin-post.php');
?>
<h1>GS Benchmark Options</h1>
<form method="POST" action="<?php echo $form_action; ?>">
<input type="hidden" name="action" value="gs_benchmark_options">
<input type="hidden" name="redirect_url" value="<?php echo esc_attr( menu_page_url( 'gs-benchmark', false ) ); ?>">
<?php wp_nonce_field( 'gs_benchmark_options_save', 'wp_nonce' ); ?>
<label>
<input type="checkbox" name="enable_benchmark" value="true" <?php checked( $this->enabled, true ); ?>>
Enable Benchmarking
</label>
<br><br>
<label>
Benchmark Specific Hooks (optional, comma separated):<br>
<textarea name="benchmark_hooks" style="display: block; width: 100%;"><?php echo esc_attr( implode( ',', $this->hooks ) ); ?></textarea>
</label>
<?php submit_button( "Save Options" ); ?>
</form>
<?php
}
public function save_options_page() {
if( empty( $_POST['wp_nonce'] ) || !wp_verify_nonce( $_POST['wp_nonce'], 'gs_benchmark_options_save' ) ) {
echo "invalid request";
wp_die();
}
if( !current_user_can( 'manage_options' ) ) {
echo "you do not have permission to modify these options";
wp_die();
}
$enabled = false;
$hooks = array();
if( !empty( $_POST['enable_benchmark'] ) ) {
$enabled = true;
}
if( !empty( $_POST['benchmark_hooks'] ) ) {
$hooks = explode( ',', esc_attr( $_POST['benchmark_hooks'] ) );
$hooks = array_map( 'trim', $hooks );
}
update_option( 'gs_benchmark', array(
'enabled' => $enabled,
'hooks' => $hooks
) );
$url = admin_url( 'options-general.php?page=gs-benchmark' );
if( !empty( $_POST['redirect_url'] ) ) {
$url = $_POST['redirect_url'];
}
wp_redirect( $url );
die();
}
public function get_start_time() {
$this->start_time = microtime( true );
}
public function get_hook_start_time( $content = "" ) {
$hook_name = current_filter();
$this->hook_times[$hook_name] = array(
'start' => microtime( true ),
'end' => 0
);
return $content;
}
public function get_hook_end_time( $content = "" ) {
$hook_name = current_filter();
$this->hook_times[$hook_name]['end'] = microtime( true );
return $content;
}
public function output_end_time() {
$end_time = microtime( true );
$total = $end_time - $this->start_time;
$total_ms = round( $total * 1000, 3 );
$content = '<code class="gs-benchmark-output">';
$content .= "PAGE GENERATED: " . date( "M-D-y H:i:s" );
$content .= "<br>";
$content .= "TIME TO GENERATE PAGE: " . $total_ms . " milliseconds";
if( $this->hooks && is_array( $this->hooks ) ) {
$content .= "<br>";
$content .= "TIME TO PROCESS HOOKS:";
foreach( $this->hooks as $hook ) {
if( !isset( $this->hook_times[$hook] ) ) {
$content .= "<br>";
$content .= "`" . $hook . "`: NOT RUN";
continue;
}
$hook_data = $this->hook_times[$hook];
if( empty( $hook_data['start'] ) || empty( $hook_data['end'] ) ) {
$content .= "<br>";
$content .= "`" . $hook . "`: PROCESSING ERROR";
continue;
}
$total_hook_time = $hook_data['end'] - $hook_data['start'];
if( $total_hook_time < 0 ) {
$content .= "<br>";
$content .= "`" . $hook . "`: PROCESSING ERROR";
continue;
}
$total_hook_time = round( $total_hook_time * 1000, 3 );
$content .= "<br>";
$content .= "`" . $hook . "`: " . $total_hook_time . " milliseconds";
}
}
$content .= "</code>";
$content .= "<style>";
$content .= ".gs-benchmark-output {";
$content .= " position:fixed;";
$content .= " top: 50px;";
$content .= " right: 0;";
$content .= " background-color:#eee;";
$content .= " border:1px solid #ccc;";
$content .= " padding: 5px;";
$content .= " color: #f00;";
$content .= " z-index:99999999;";
$content .= " opacity:0.85;";
$content .= "}";
$content .= ".gs-benchmark-output:hover {";
$content .= " opacity:0;";
$content .= " pointer-events:none;";
$content .= "}";
$content .= "</style>";
echo $content;
}
}
GSBenchmark::Instance();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment