Merge pull request #82 from runcommand/23-split-stage-hook

Split `wp profile` into `wp profile stage` and `wp profile hook`
This commit is contained in:
Daniel Bachhuber 2016-10-04 07:06:40 -07:00 committed by GitHub
commit 8050917a52
6 changed files with 142 additions and 73 deletions

View file

@ -84,26 +84,55 @@ Et voila! You've identified some of the sources of slowness.
## Using
This package implements the following commands:
### wp profile stage
Profile each stage of the WordPress load process (bootstrap, main_query, template).
~~~
wp profile [--url=<url>] [--stage=<stage>] [--hook=<hook>] [--fields=<fields>] [--format=<format>]
wp profile stage [<stage>] [--url=<url>] [--fields=<fields>] [--format=<format>]
~~~
**OPTIONS**
[<stage>]
Drill down into a specific stage.
[--url=<url>]
Execute a request against a specified URL. Defaults to the home URL.
[--stage=<stage>]
Drill down into a specific stage.
[--fields=<fields>]
Display one or more fields.
[--format=<format>]
Render output in a particular format.
---
default: table
options:
- bootstrap
- main_query
- template
- table
- json
- yaml
- csv
---
[--hook=<hook>]
Drill down into a specific hook.
### wp profile hook
Profile key metrics for a WordPress hook (action or filter).
~~~
wp profile hook <hook> [--url=<url>] [--fields=<fields>] [--format=<format>]
~~~
**OPTIONS**
<hook>
WordPress hook (action or filter) to profile.
[--url=<url>]
Execute a request against a specified URL. Defaults to the home URL.
[--fields=<fields>]
Display one or more fields.

View file

@ -17,7 +17,8 @@
},
"extra": {
"commands": [
"profile"
"profile stage",
"profile hook"
],
"readme": {
"shields": [

View file

@ -3,7 +3,7 @@ Feature: Profile a specific hook
Scenario: Profile a hook before the template is loaded
Given a WP install
When I run `wp profile --hook=plugins_loaded --fields=callback`
When I run `wp profile hook plugins_loaded --fields=callback`
Then STDOUT should be a table containing rows:
| callback |
And STDERR should be empty
@ -11,7 +11,7 @@ Feature: Profile a specific hook
Scenario: Profile a hook without any callbacks
Given a WP install
When I run `wp profile --hook=setup_theme --fields=callback`
When I run `wp profile hook setup_theme --fields=callback`
Then STDOUT should be a table containing rows:
| callback |
| total |
@ -20,7 +20,7 @@ Feature: Profile a specific hook
Scenario: Profile a hook that has actions with output
Given a WP install
When I run `wp profile --hook=wp_head --fields=callback`
When I run `wp profile hook wp_head --fields=callback`
Then STDOUT should be a table containing rows:
| callback |
And STDOUT should not contain:

View file

@ -1,9 +1,19 @@
Feature: Profile the template render stage
Scenario: Profiler loads a summary table
Given a WP install
When I run `wp profile stage --fields=stage`
Then STDOUT should be a table containing rows:
| stage |
| bootstrap |
| main_query |
| template |
Scenario: Profiler loads a table with the correct hooks
Given a WP install
When I run `wp profile --stage=template --fields=hook`
When I run `wp profile stage template --fields=hook`
Then STDOUT should be a table containing rows:
| hook |
| |

View file

@ -1,15 +1,5 @@
Feature: Basic profile usage
Scenario: Profiler loads a summary table
Given a WP install
When I run `wp profile --fields=stage`
Then STDOUT should be a table containing rows:
| stage |
| bootstrap |
| main_query |
| template |
Scenario: Error when SAVEQUERIES is defined to false
Given an empty directory
And WP files
@ -23,7 +13,7 @@ Feature: Basic profile usage
When I run `wp core install --url='https://localhost' --title='Test' --admin_user=wpcli --admin_email=admin@example.com --admin_password=1`
Then the return code should be 0
When I try `wp profile`
When I try `wp profile stage`
Then STDERR should be:
"""
Error: 'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php
@ -32,7 +22,7 @@ Feature: Basic profile usage
Scenario: Profile a hook without any callbacks
Given a WP install
When I run `wp profile --hook=setup_theme --fields=callback,time`
When I run `wp profile hook setup_theme --fields=callback,time`
Then STDOUT should be a table containing rows:
| callback | time |
| total | |

View file

@ -8,34 +8,25 @@ use WP_CLI\Utils;
class Command {
private $loggers = array();
private $focus_stage;
private $focus_stage = null;
private $stage_hooks = array();
private $focus_hook;
private $focus_hook = null;
private $current_filter_callbacks = array();
private $focus_query_offset = 0;
private static $exception_message = "Need to bail, because can't restore the hooks";
/**
* Quickly identify what's slow with WordPress.
* Profile each stage of the WordPress load process (bootstrap, main_query, template).
*
* ## OPTIONS
*
* [<stage>]
* : Drill down into a specific stage.
*
* [--url=<url>]
* : Execute a request against a specified URL. Defaults to the home URL.
*
* [--stage=<stage>]
* : Drill down into a specific stage.
* ---
* options:
* - bootstrap
* - main_query
* - template
* ---
*
* [--hook=<hook>]
* : Drill down into a specific hook.
*
* [--fields=<fields>]
* : Display one or more fields.
*
@ -52,29 +43,19 @@ class Command {
*
* @when before_wp_load
*/
public function __invoke( $args, $assoc_args ) {
public function stage( $args, $assoc_args ) {
global $wpdb;
$this->focus_stage = Utils\get_flag_value( $assoc_args, 'stage' );
$this->focus_hook = Utils\get_flag_value( $assoc_args, 'hook' );
if ( ! isset( WP_CLI::get_runner()->config['url'] ) ) {
WP_CLI::add_wp_hook( 'muplugins_loaded', function(){
WP_CLI::set_url( home_url( '/' ) );
});
if ( isset( $args[0] ) ) {
$this->focus_stage = $args[0];
}
WP_CLI::add_hook( 'after_wp_config_load', function() {
if ( defined( 'SAVEQUERIES' ) && ! SAVEQUERIES ) {
WP_CLI::error( "'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php" );
}
if ( ! defined( 'SAVEQUERIES' ) ) {
define( 'SAVEQUERIES', true );
}
});
WP_CLI::add_wp_hook( 'all', array( $this, 'wp_hook_begin' ) );
WP_CLI::add_wp_hook( 'pre_http_request', array( $this, 'wp_request_begin' ) );
WP_CLI::add_wp_hook( 'http_api_debug', array( $this, 'wp_request_end' ) );
$this->load_wordpress_with_template();
$valid_stages = array( 'bootstrap', 'main_query', 'template' );
if ( $this->focus_stage && ! in_array( $this->focus_stage, $valid_stages, true ) ) {
WP_CLI::error( 'Invalid stage. Must be one of: ' . implode( ', ', $valid_stages ) );
}
$this->run_profiler();
if ( $this->focus_stage ) {
$fields = array(
@ -88,18 +69,6 @@ class Command {
'request_time',
'request_count',
);
} else if ( $this->focus_hook ) {
$fields = array(
'callback',
'time',
'query_time',
'query_count',
'cache_ratio',
'cache_hits',
'cache_misses',
'request_time',
'request_count',
);
} else {
$fields = array(
'stage',
@ -119,6 +88,53 @@ class Command {
$formatter->display_items( $this->loggers );
}
/**
* Profile key metrics for a WordPress hook (action or filter).
*
* ## OPTIONS
*
* <hook>
* : WordPress hook (action or filter) to profile.
*
* [--url=<url>]
* : Execute a request against a specified URL. Defaults to the home URL.
*
* [--fields=<fields>]
* : Display one or more fields.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - json
* - yaml
* - csv
* ---
*
* @when before_wp_load
*/
public function hook( $args, $assoc_args ) {
$this->focus_hook = $args[0];
$this->run_profiler();
$fields = array(
'callback',
'time',
'query_time',
'query_count',
'cache_ratio',
'cache_hits',
'cache_misses',
'request_time',
'request_count',
);
$formatter = new Formatter( $assoc_args, $fields );
$formatter->display_items( $this->loggers );
}
/**
* Profiling verbosity at the beginning of every action and filter
*/
@ -145,7 +161,7 @@ class Command {
WP_CLI::add_wp_hook( $current_filter, array( $this, 'wp_hook_end' ), 9999 );
}
/**
* Wrap current filter callbacks with a timer
*/
@ -247,6 +263,29 @@ class Command {
return $filter_value;
}
/**
* Run the profiler against WordPress
*/
private function run_profiler() {
if ( ! isset( WP_CLI::get_runner()->config['url'] ) ) {
WP_CLI::add_wp_hook( 'muplugins_loaded', function(){
WP_CLI::set_url( home_url( '/' ) );
});
}
WP_CLI::add_hook( 'after_wp_config_load', function() {
if ( defined( 'SAVEQUERIES' ) && ! SAVEQUERIES ) {
WP_CLI::error( "'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php" );
}
if ( ! defined( 'SAVEQUERIES' ) ) {
define( 'SAVEQUERIES', true );
}
});
WP_CLI::add_wp_hook( 'all', array( $this, 'wp_hook_begin' ) );
WP_CLI::add_wp_hook( 'pre_http_request', array( $this, 'wp_request_begin' ) );
WP_CLI::add_wp_hook( 'http_api_debug', array( $this, 'wp_request_end' ) );
$this->load_wordpress_with_template();
}
/**
* Runs through the entirety of the WP bootstrap process
*/