mirror of
https://hk.gh-proxy.com/https://github.com/wp-cli/profile-command.git
synced 2025-08-18 06:11:25 +08:00
Push current changes
This commit is contained in:
parent
f248ed3625
commit
552276d06e
17 changed files with 1106 additions and 195 deletions
|
@ -23,20 +23,6 @@ Feature: Profile a specific hook
|
|||
| smilies_init() | 2 | 0 |
|
||||
| feed_links() | 8 | 0 |
|
||||
|
||||
@less-than-php-7 @require-wp-4.0
|
||||
Scenario: Profile an intermediate stage hook
|
||||
Given a WP install
|
||||
|
||||
When I run `wp profile hook wp_head:before --fields=callback,cache_hits,cache_misses`
|
||||
Then STDOUT should be a table containing rows:
|
||||
| callback | cache_hits | cache_misses |
|
||||
| locate_template() | 0 | 0 |
|
||||
| load_template() | 0 | 0 |
|
||||
And STDOUT should not contain:
|
||||
"""
|
||||
WP_CLI\Profile\Profiler->wp_tick_profile_begin()
|
||||
"""
|
||||
|
||||
@require-wp-4.0
|
||||
Scenario: Profile a hook before the template is loaded
|
||||
Given a WP install
|
||||
|
@ -129,43 +115,3 @@ Feature: Profile a specific hook
|
|||
"""
|
||||
Warning: Called 1
|
||||
"""
|
||||
|
||||
@less-than-php-7 @require-wp-4.0
|
||||
Scenario: Profile the mu_plugins:before hook
|
||||
Given a WP install
|
||||
And a wp-content/mu-plugins/awesome-file.php file:
|
||||
"""
|
||||
<?php
|
||||
function awesome_func() {
|
||||
// does nothing
|
||||
}
|
||||
awesome_func();
|
||||
"""
|
||||
|
||||
When I run `wp profile hook muplugins_loaded:before --fields=callback`
|
||||
Then STDOUT should contain:
|
||||
"""
|
||||
wp-content/mu-plugins/awesome-file.php
|
||||
"""
|
||||
|
||||
@less-than-php-7 @require-wp-4.0
|
||||
Scenario: Profile the :after hooks
|
||||
Given a WP install
|
||||
|
||||
When I run `wp profile hook wp_loaded:after`
|
||||
Then STDOUT should contain:
|
||||
"""
|
||||
do_action()
|
||||
"""
|
||||
|
||||
When I run `wp profile hook wp:after`
|
||||
Then STDOUT should contain:
|
||||
"""
|
||||
do_action_ref_array()
|
||||
"""
|
||||
|
||||
When I run `wp profile hook wp_footer:after`
|
||||
Then STDOUT should contain:
|
||||
"""
|
||||
do_action()
|
||||
"""
|
||||
|
|
65
src/Collector.php
Normal file
65
src/Collector.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile;
|
||||
|
||||
/**
|
||||
* The collector is responsible for collect a specific subset of data across the
|
||||
* requested scope.
|
||||
*/
|
||||
interface Collector {
|
||||
|
||||
/**
|
||||
* Register the collector with the WordPress lifecycle.
|
||||
*
|
||||
* @param Scope $scope Scope for which to collect the data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Scope $scope );
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_hook( $hook );
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to stop the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_hook( $hook );
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_callback( $hook, $callback, $index );
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_callback( $hook, $callback, $index );
|
||||
/**
|
||||
* Report the collected data.
|
||||
*
|
||||
* @return Report
|
||||
*/
|
||||
public function report();
|
||||
}
|
100
src/Collector/DatabaseQueries.php
Normal file
100
src/Collector/DatabaseQueries.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Collector;
|
||||
|
||||
use RuntimeException;
|
||||
use WP_CLI\Profile\Collector;
|
||||
use WP_CLI\Profile\Report;
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect database queries for the requested scope.
|
||||
*/
|
||||
final class DatabaseQueries implements Collector {
|
||||
|
||||
/**
|
||||
* Internal storage for collected data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* Register the collector with the WordPress lifecycle.
|
||||
*
|
||||
* @param Scope $scope Scope for which to collect the data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Scope $scope ) {
|
||||
switch ( $scope->get_type() ) {
|
||||
case Scope::TYPE_ALL_HOOKS:
|
||||
break;
|
||||
case Scope::TYPE_HOOK:
|
||||
break;
|
||||
case Scope::TYPE_STAGE:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
"Trying to register unsupported scope {$scope->get_type()} in DatabaseQueries collector"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_hook( $hook ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to stop the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_hook( $hook ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_callback( $hook, $callback, $index ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_callback( $hook, $callback, $index ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the collected data.
|
||||
*
|
||||
* @return Report
|
||||
*/
|
||||
public function report() {
|
||||
return new Report\DatabaseQueries( $this->data );
|
||||
}
|
||||
}
|
193
src/Collector/ExecutionTime.php
Normal file
193
src/Collector/ExecutionTime.php
Normal file
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Collector;
|
||||
|
||||
use RuntimeException;
|
||||
use WP_CLI;
|
||||
use WP_CLI\Profile\Collector;
|
||||
use WP_CLI\Profile\Report;
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect execution time for the requested scope.
|
||||
*/
|
||||
final class ExecutionTime implements Collector {
|
||||
|
||||
/**
|
||||
* Internal storage for collected data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = [
|
||||
'hook' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Previous hook that was collected.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $previous_hook;
|
||||
|
||||
/**
|
||||
* Register the collector with the WordPress lifecycle.
|
||||
*
|
||||
* @param Scope $scope Scope for which to collect the data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Scope $scope ) {
|
||||
switch ( $scope->get_type() ) {
|
||||
case Scope::TYPE_ALL_HOOKS:
|
||||
break;
|
||||
case Scope::TYPE_HOOK:
|
||||
break;
|
||||
case Scope::TYPE_STAGE:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
"Trying to register unsupported scope {$scope->get_type()} in ExecutionTime collector"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_hook( $hook ) {
|
||||
$this->record_hook_metric(
|
||||
$hook,
|
||||
'start_time',
|
||||
microtime( true )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to stop the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_hook( $hook ) {
|
||||
$this->record_hook_metric(
|
||||
$hook,
|
||||
'end_time',
|
||||
microtime( true )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_callback( $hook, $callback, $index ) {
|
||||
$this->record_callback_metric(
|
||||
$hook,
|
||||
$callback,
|
||||
'start_time',
|
||||
microtime( true )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_callback( $hook, $callback, $index ) {
|
||||
$this->record_callback_metric(
|
||||
$hook,
|
||||
$callback,
|
||||
'end_time',
|
||||
microtime( true )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the collected data.
|
||||
*
|
||||
* @return Report
|
||||
*/
|
||||
public function report() {
|
||||
var_dump( $this->data );
|
||||
return new Report\ExecutionTime( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a metric for a hook.
|
||||
*
|
||||
* @param string $hook Hook to collect the metric fo.
|
||||
* @param string $metric Metric to collect.
|
||||
* @param mixed $value Value to collect.
|
||||
*/
|
||||
private function record_hook_metric( $hook, $metric, $value ) {
|
||||
$this->add_array_keys_as_needed( $this->data, [ 'hooks', $hook ] );
|
||||
|
||||
$this->data['hooks'][ $hook ][ $metric ] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a metric for a callback.
|
||||
*
|
||||
* @param string $hook Hook to collect the metric fo.
|
||||
* @param callable $callback Callback to collect the metric for.
|
||||
* @param string $metric Metric to collect.
|
||||
* @param mixed $value Value to collect.
|
||||
*/
|
||||
private function record_callback_metric( $hook, $callback, $metric, $value ) {
|
||||
if ( is_callable( $callback ) ) {
|
||||
$callback = $this->get_callback_hash( $callback );
|
||||
}
|
||||
|
||||
if ( is_array( $callback ) ) {
|
||||
$callback = $this->get_array_hash( $callback );
|
||||
}
|
||||
|
||||
$this->add_array_keys_as_needed(
|
||||
$this->data,
|
||||
[ 'hooks', $hook, 'callbacks', $callback ]
|
||||
);
|
||||
|
||||
$this->data['hooks'][ $hook ]['callbacks'][ $callback ][ $metric ] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add missing array keys as needed for several levels in one go.
|
||||
*
|
||||
* @param array $array Array to set the missing keys for.
|
||||
* @param array<string> $keys Array of keys to add if missing.
|
||||
*/
|
||||
private function add_array_keys_as_needed( &$array, $keys ) {
|
||||
while ( count( $keys ) > 0) {
|
||||
$key = array_shift( $keys );
|
||||
|
||||
if ( ! isset( $array[ $key ]) || ! is_array( $array[ $key ] ) ) {
|
||||
$array[ $key ] = [];
|
||||
}
|
||||
|
||||
$array =& $array[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
private function get_callback_hash( callable $callback ) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
private function get_array_hash( array $callback ) {
|
||||
var_dump( array_keys( $callback ) );
|
||||
}
|
||||
}
|
100
src/Collector/HttpRequests.php
Normal file
100
src/Collector/HttpRequests.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Collector;
|
||||
|
||||
use RuntimeException;
|
||||
use WP_CLI\Profile\Collector;
|
||||
use WP_CLI\Profile\Report;
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect HTTP requests for the requested scope.
|
||||
*/
|
||||
final class HttpRequests implements Collector {
|
||||
|
||||
/**
|
||||
* Internal storage for collected data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* Register the collector with the WordPress lifecycle.
|
||||
*
|
||||
* @param Scope $scope Scope for which to collect the data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Scope $scope ) {
|
||||
switch ( $scope->get_type() ) {
|
||||
case Scope::TYPE_ALL_HOOKS:
|
||||
break;
|
||||
case Scope::TYPE_HOOK:
|
||||
break;
|
||||
case Scope::TYPE_STAGE:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
"Trying to register unsupported scope {$scope->get_type()} in HttpRequests collector"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_hook( $hook ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to stop the collection for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_hook( $hook ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_callback( $hook, $callback, $index ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the collection for a given hook and callback.
|
||||
*
|
||||
* @param string $hook Hook to start the collection for.
|
||||
* @param callable $callback Callback to start the collection for.
|
||||
* @param int $index Index of the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stop_callback( $hook, $callback, $index ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the collected data.
|
||||
*
|
||||
* @return Report
|
||||
*/
|
||||
public function report() {
|
||||
return new Report\HttpRequests( $this->data );
|
||||
}
|
||||
}
|
|
@ -240,6 +240,67 @@ class Command {
|
|||
$formatter->display_items( $loggers, true, $order, $orderby );
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile key metrics for WordPress hooks (actions and filters).
|
||||
*
|
||||
* In order to profile callbacks on a specific hook, the action or filter
|
||||
* will need to execute during the course of the request.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [<hook>]
|
||||
* : Drill into key metrics of callbacks on a specific WordPress hook.
|
||||
*
|
||||
* [--all]
|
||||
* : Profile callbacks for all WordPress hooks.
|
||||
*
|
||||
* [--spotlight]
|
||||
* : Filter out logs with zero-ish values from the set.
|
||||
*
|
||||
* [--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
|
||||
* ---
|
||||
*
|
||||
* [--order=<order>]
|
||||
* : Ascending or Descending order.
|
||||
* ---
|
||||
* default: ASC
|
||||
* options:
|
||||
* - ASC
|
||||
* - DESC
|
||||
* ---
|
||||
*
|
||||
* [--orderby=<orderby>]
|
||||
* : Order by fields.
|
||||
*
|
||||
* @when before_wp_load
|
||||
* @subcommand new-hook
|
||||
*/
|
||||
public function new_hook( $args, $assoc_args ) {
|
||||
|
||||
$focus = Utils\get_flag_value( $assoc_args, 'all', isset( $args[0] ) ? $args[0] : null );
|
||||
|
||||
$order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' );
|
||||
$orderby = Utils\get_flag_value( $assoc_args, 'orderby', null );
|
||||
|
||||
$profiler = new NewProfiler( new Scope\AllHooks() );
|
||||
$profiler->profile();
|
||||
$profiler->report();
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile arbitrary code execution.
|
||||
*
|
||||
|
|
260
src/NewProfiler.php
Normal file
260
src/NewProfiler.php
Normal file
|
@ -0,0 +1,260 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile;
|
||||
|
||||
use WP_CLI;
|
||||
use WP_Hook;
|
||||
|
||||
final class NewProfiler {
|
||||
|
||||
const DEFAULT_COLLECTORS = [
|
||||
Collector\ExecutionTime::class,
|
||||
Collector\DatabaseQueries::class,
|
||||
Collector\HttpRequests::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Array of instantiated collectors.
|
||||
*
|
||||
* @var array<Collector>
|
||||
*/
|
||||
private $collectors = [];
|
||||
|
||||
/**
|
||||
* Scope to profile.
|
||||
*
|
||||
* @var Scope
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
/**
|
||||
* Current depth of the hook.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $hook_depth = 0;
|
||||
|
||||
/**
|
||||
* Instantiate a profiler instance.
|
||||
*
|
||||
* @param Scope $scope Scope to profile.
|
||||
*/
|
||||
public function __construct( Scope $scope ) {
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a profile run.
|
||||
*/
|
||||
public function profile() {
|
||||
$this->collectors = $this->register_collectors();
|
||||
|
||||
WP_CLI::add_wp_hook( 'all', [ $this, 'wp_hook_begin' ] );
|
||||
|
||||
$this->run_wordpress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling verbosity at the beginning of every action and filter.
|
||||
*/
|
||||
public function wp_hook_begin() {
|
||||
$hook = current_filter();
|
||||
if ( $this->scope->includes_hook( $hook ) ) {
|
||||
$this->start_hook_collectors( $hook );
|
||||
}
|
||||
|
||||
if ( 0 === $this->hook_depth
|
||||
&& ! is_null( $this->previous_filter_callbacks ) ) {
|
||||
$this->set_hook_callbacks( $this->previous_filter, $this->previous_filter_callbacks );
|
||||
$this->previous_filter_callbacks = null;
|
||||
}
|
||||
|
||||
if ( 0 === $this->hook_depth
|
||||
&& $this->scope->includes_hook( $hook ) ) {
|
||||
$this->wrap_hook_callbacks( $hook );
|
||||
}
|
||||
|
||||
$this->hook_depth++;
|
||||
|
||||
WP_CLI::add_wp_hook( $hook, [ $this, 'wp_hook_end' ], PHP_INT_MAX );
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling verbosity at the end of every action and filter.
|
||||
*/
|
||||
public function wp_hook_end( $filter_value = null ) {
|
||||
$hook = current_filter();
|
||||
|
||||
$this->stop_hook_collectors( $hook );
|
||||
|
||||
$this->hook_depth--;
|
||||
|
||||
return $filter_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an aggregated report of all collectors.
|
||||
*
|
||||
* @return Report Aggregated report of all collectors.
|
||||
*/
|
||||
public function report() {
|
||||
$reports = [];
|
||||
|
||||
foreach ( $this->collectors as $collector ) {
|
||||
$reports[] = $collector->report();
|
||||
}
|
||||
|
||||
return new Report\Aggregated( $reports );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a set of collectors based on their classes.
|
||||
*
|
||||
* @return array<Collector> Array of collectors that were registered.
|
||||
*/
|
||||
private function register_collectors() {
|
||||
$collectors = [];
|
||||
|
||||
foreach ( self::DEFAULT_COLLECTORS as $collector_class ) {
|
||||
$collector = $this->instantiate_collector_class( $collector_class );
|
||||
$collector->register( $this->scope );
|
||||
$collectors[] = $collector;
|
||||
}
|
||||
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a collector class.
|
||||
*
|
||||
* @param string $collector_class Collector class to instantiate.
|
||||
*
|
||||
* @return Collector Instantiated collector.
|
||||
*/
|
||||
private function instantiate_collector_class( $collector_class ) {
|
||||
return new $collector_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs through the entirety of the WP bootstrap process.
|
||||
*/
|
||||
private function run_wordpress() {
|
||||
// WordPress already ran once.
|
||||
if ( function_exists( 'add_filter' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WP_CLI::get_runner()->load_wordpress();
|
||||
|
||||
wp();
|
||||
|
||||
define( 'WP_USE_THEMES', true );
|
||||
|
||||
// Template is normally loaded in global scope, so we need to replicate
|
||||
foreach ( $GLOBALS as $key => $value ) {
|
||||
global ${$key}; // phpcs:ignore
|
||||
// PHPCompatibility.PHP.ForbiddenGlobalVariableVariable.NonBareVariableFound -- Syntax is updated to compatible with php 5 and 7.
|
||||
}
|
||||
|
||||
ob_start();
|
||||
require_once ABSPATH . WPINC . '/template-loader.php';
|
||||
ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap hook callbacks with a timer.
|
||||
*/
|
||||
private function wrap_hook_callbacks( $hook ) {
|
||||
$callbacks = $this->get_hook_callbacks( $hook );
|
||||
|
||||
if ( false === $callbacks ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $callbacks as $priority => $priority_callbacks ) {
|
||||
foreach ( $priority_callbacks as $index => $callback ) {
|
||||
$callbacks[ $priority ][ $index ] = [
|
||||
'function' => function ( ...$args ) use ( $hook, $callback, $index ) {
|
||||
var_dump( $index );
|
||||
$this->start_callback_collectors( $hook, $callback, $index );
|
||||
$value = $callback['function']( ...$args );
|
||||
$this->stop_callback_collectors( $hook, $callback, $index );
|
||||
return $value;
|
||||
},
|
||||
'accepted_args' => $callback['accepted_args'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->set_hook_callbacks( $hook, $callbacks );
|
||||
}
|
||||
|
||||
private function start_hook_collectors( $hook ) {
|
||||
foreach ( $this->collectors as $collector ) {
|
||||
$collector->start_hook( $hook );
|
||||
}
|
||||
}
|
||||
|
||||
private function stop_hook_collectors( $hook ) {
|
||||
foreach ( $this->collectors as $collector ) {
|
||||
$collector->stop_hook( $hook );
|
||||
}
|
||||
}
|
||||
|
||||
private function start_callback_collectors( $hook, $callback, $index ) {
|
||||
foreach ( $this->collectors as $collector ) {
|
||||
$collector->start_callback( $hook, $callback, $index );
|
||||
}
|
||||
}
|
||||
|
||||
private function stop_callback_collectors( $hook, $callback, $index ) {
|
||||
foreach ( $this->collectors as $collector ) {
|
||||
$collector->stop_callback( $hook, $callback, $index );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the callbacks for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to get the callbacks for.
|
||||
* @return array|false Array of callbacks, or false if the hook is unknown.
|
||||
*/
|
||||
private function get_hook_callbacks( $hook ) {
|
||||
global $wp_filter;
|
||||
|
||||
if ( ! isset( $wp_filter[ $hook ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$callbacks = $wp_filter[ $hook ] instanceof WP_Hook
|
||||
? $wp_filter[ $hook ]->callbacks
|
||||
: $wp_filter[ $hook ];
|
||||
|
||||
if ( is_array( $callbacks ) ) {
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the callbacks for a given hook.
|
||||
*
|
||||
* @param string $hook Hook to set the callbacks for.
|
||||
* @param mixed $callbacks Callbacks to set for the hook.
|
||||
*/
|
||||
private function set_hook_callbacks( $hook, $callbacks ) {
|
||||
global $wp_filter;
|
||||
|
||||
if ( ! isset( $wp_filter[ $hook ] ) && class_exists( 'WP_Hook' ) ) {
|
||||
$wp_filter[ $hook ] = new WP_Hook();
|
||||
}
|
||||
|
||||
if ( $wp_filter[ $hook ] instanceof WP_Hook ) {
|
||||
$wp_filter[ $hook ]->callbacks = $callbacks;
|
||||
} else {
|
||||
$wp_filter[ $hook ] = $callbacks;
|
||||
}
|
||||
}
|
||||
}
|
215
src/Profiler.php
215
src/Profiler.php
|
@ -8,34 +8,34 @@ class Profiler {
|
|||
|
||||
private $type;
|
||||
private $focus;
|
||||
private $loggers = array();
|
||||
private $stage_hooks = array(
|
||||
'bootstrap' => array(
|
||||
private $loggers = [];
|
||||
private $stage_hooks = [
|
||||
'bootstrap' => [
|
||||
'muplugins_loaded',
|
||||
'plugins_loaded',
|
||||
'setup_theme',
|
||||
'after_setup_theme',
|
||||
'init',
|
||||
'wp_loaded',
|
||||
),
|
||||
'main_query' => array(
|
||||
],
|
||||
'main_query' => [
|
||||
'parse_request',
|
||||
'send_headers',
|
||||
'pre_get_posts',
|
||||
'the_posts',
|
||||
'wp',
|
||||
),
|
||||
'template' => array(
|
||||
],
|
||||
'template' => [
|
||||
'template_redirect',
|
||||
'template_include',
|
||||
'wp_head',
|
||||
'loop_start',
|
||||
'loop_end',
|
||||
'wp_footer',
|
||||
),
|
||||
);
|
||||
],
|
||||
];
|
||||
|
||||
private $current_stage_hooks = array();
|
||||
private $current_stage_hooks = [];
|
||||
private $running_hook = null;
|
||||
private $previous_filter = null;
|
||||
private $previous_filter_callbacks = null;
|
||||
|
@ -74,105 +74,19 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Run the profiler against WordPress
|
||||
* Run the profiler against WordPress.
|
||||
*/
|
||||
public function run() {
|
||||
WP_CLI::add_wp_hook(
|
||||
'muplugins_loaded',
|
||||
function() {
|
||||
$url = WP_CLI::get_runner()->config['url'];
|
||||
if ( ! empty( $url ) ) {
|
||||
WP_CLI::set_url( trailingslashit( $url ) );
|
||||
} else {
|
||||
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 );
|
||||
}
|
||||
}
|
||||
);
|
||||
if ( 'hook' === $this->type
|
||||
&& ':before' === substr( $this->focus, -7, 7 ) ) {
|
||||
$stage_hooks = array();
|
||||
foreach ( $this->stage_hooks as $hooks ) {
|
||||
$stage_hooks = array_merge( $stage_hooks, $hooks );
|
||||
}
|
||||
$end_hook = substr( $this->focus, 0, -7 );
|
||||
$key = array_search( $end_hook, $stage_hooks, true );
|
||||
if ( isset( $stage_hooks[ $key - 1 ] ) ) {
|
||||
$start_hook = $stage_hooks[ $key - 1 ];
|
||||
WP_CLI::add_wp_hook( $start_hook, array( $this, 'wp_tick_profile_begin' ), 9999 );
|
||||
} else {
|
||||
WP_CLI::add_hook( 'after_wp_config_load', array( $this, 'wp_tick_profile_begin' ) );
|
||||
}
|
||||
WP_CLI::add_wp_hook( $end_hook, array( $this, 'wp_tick_profile_end' ), -9999 );
|
||||
} elseif ( 'hook' === $this->type
|
||||
&& ':after' === substr( $this->focus, -6, 6 ) ) {
|
||||
$start_hook = substr( $this->focus, 0, -6 );
|
||||
WP_CLI::add_wp_hook( $start_hook, array( $this, 'wp_tick_profile_begin' ), 9999 );
|
||||
} else {
|
||||
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->configure_wordpress_context();
|
||||
$this->enable_savequeries();
|
||||
WP_CLI::add_wp_hook( 'all', [ $this, 'wp_hook_begin' ] );
|
||||
WP_CLI::add_wp_hook( 'pre_http_request', [ $this, 'wp_request_begin' ] );
|
||||
WP_CLI::add_wp_hook( 'http_api_debug', [ $this, 'wp_request_end' ] );
|
||||
$this->load_wordpress_with_template();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiling function calls on the end of this filter
|
||||
*/
|
||||
public function wp_tick_profile_begin( $value = null ) {
|
||||
|
||||
if ( version_compare( PHP_VERSION, '7.0.0' ) >= 0 ) {
|
||||
WP_CLI::error( 'Profiling intermediate hooks is broken in PHP 7, see https://bugs.php.net/bug.php?id=72966' );
|
||||
}
|
||||
|
||||
// Disable opcode optimizers. These "optimize" calls out of the stack
|
||||
// and hide calls from the tick handler and backtraces.
|
||||
// Copied from P3 Profiler
|
||||
if ( extension_loaded( 'xcache' ) ) {
|
||||
@ini_set( 'xcache.optimizer', false ); // phpcs:ignore
|
||||
// WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server.
|
||||
} elseif ( extension_loaded( 'apc' ) ) {
|
||||
@ini_set( 'apc.optimization', 0 ); // phpcs:ignore
|
||||
// WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server.
|
||||
apc_clear_cache();
|
||||
} elseif ( extension_loaded( 'eaccelerator' ) ) {
|
||||
@ini_set( 'eaccelerator.optimizer', 0 ); // phpcs:ignore
|
||||
// WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server.
|
||||
if ( function_exists( 'eaccelerator_optimizer' ) ) {
|
||||
@eaccelerator_optimizer( false ); // phpcs:ignore
|
||||
// WordPress.PHP.NoSilencedErrors.Discouraged -- disabling eaccelerator on runtime can faild
|
||||
}
|
||||
} elseif ( extension_loaded( 'Zend Optimizer+' ) ) {
|
||||
@ini_set( 'zend_optimizerplus.optimization_level', 0 ); // phpcs:ignore
|
||||
// WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server.
|
||||
}
|
||||
|
||||
register_tick_function( array( $this, 'handle_function_tick' ) );
|
||||
declare( ticks = 1 );
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop profiling function calls at the beginning of this filter
|
||||
*/
|
||||
public function wp_tick_profile_end( $value = null ) {
|
||||
unregister_tick_function( array( $this, 'handle_function_tick' ) );
|
||||
$this->tick_callback = null;
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling verbosity at the beginning of every action and filter
|
||||
* Profiling verbosity at the beginning of every action and filter.
|
||||
*/
|
||||
public function wp_hook_begin() {
|
||||
|
||||
|
@ -195,10 +109,10 @@ class Profiler {
|
|||
}
|
||||
}
|
||||
$this->loggers[ $current_filter ] = new Logger(
|
||||
array(
|
||||
[
|
||||
'hook' => $current_filter,
|
||||
'callback_count' => $callback_count,
|
||||
)
|
||||
]
|
||||
);
|
||||
$this->loggers[ $current_filter ]->start();
|
||||
}
|
||||
|
@ -217,11 +131,11 @@ class Profiler {
|
|||
|
||||
$this->filter_depth++;
|
||||
|
||||
WP_CLI::add_wp_hook( $current_filter, array( $this, 'wp_hook_end' ), 9999 );
|
||||
WP_CLI::add_wp_hook( $current_filter, [ $this, 'wp_hook_end' ], 9999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap current filter callbacks with a timer
|
||||
* Wrap current filter callbacks with a timer.
|
||||
*/
|
||||
private function wrap_current_filter_callbacks( $current_filter ) {
|
||||
|
||||
|
@ -234,13 +148,13 @@ class Profiler {
|
|||
|
||||
foreach ( $callbacks as $priority => $priority_callbacks ) {
|
||||
foreach ( $priority_callbacks as $i => $the_ ) {
|
||||
$callbacks[ $priority ][ $i ] = array(
|
||||
'function' => function() use ( $the_, $i ) {
|
||||
$callbacks[ $priority ][ $i ] = [
|
||||
'function' => function () use ( $the_, $i ) {
|
||||
if ( ! isset( $this->loggers[ $i ] ) ) {
|
||||
$this->loggers[ $i ] = new Logger(
|
||||
array(
|
||||
[
|
||||
'callback' => $the_['function'],
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
$this->loggers[ $i ]->start();
|
||||
|
@ -249,14 +163,14 @@ class Profiler {
|
|||
return $value;
|
||||
},
|
||||
'accepted_args' => $the_['accepted_args'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
self::set_filter_callbacks( $current_filter, $callbacks );
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling verbosity at the end of every action and filter
|
||||
* Profiling verbosity at the end of every action and filter.
|
||||
*/
|
||||
public function wp_hook_end( $filter_value = null ) {
|
||||
|
||||
|
@ -276,7 +190,7 @@ class Profiler {
|
|||
$pseudo_hook = "{$this->current_stage_hooks[$key]}:after";
|
||||
$this->running_hook = $pseudo_hook;
|
||||
}
|
||||
$this->loggers[ $pseudo_hook ] = new Logger( array( 'hook' => $pseudo_hook ) );
|
||||
$this->loggers[ $pseudo_hook ] = new Logger( [ 'hook' => $pseudo_hook ] );
|
||||
$this->loggers[ $pseudo_hook ]->start();
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +201,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle the tick of a function
|
||||
* Handle the tick of a function.
|
||||
*/
|
||||
public function handle_function_tick() {
|
||||
global $wpdb, $wp_object_cache;
|
||||
|
@ -297,7 +211,7 @@ class Profiler {
|
|||
|
||||
$callback_hash = md5( serialize( $this->tick_callback . $this->tick_location ) ); // phpcs:ignore
|
||||
if ( ! isset( $this->loggers[ $callback_hash ] ) ) {
|
||||
$this->loggers[ $callback_hash ] = array(
|
||||
$this->loggers[ $callback_hash ] = [
|
||||
'callback' => $this->tick_callback,
|
||||
'location' => $this->tick_location,
|
||||
'time' => 0,
|
||||
|
@ -306,7 +220,7 @@ class Profiler {
|
|||
'cache_hits' => 0,
|
||||
'cache_misses' => 0,
|
||||
'cache_ratio' => null,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
$this->loggers[ $callback_hash ]['time'] += $time;
|
||||
|
@ -340,7 +254,7 @@ class Profiler {
|
|||
|
||||
$location = '';
|
||||
$callback = '';
|
||||
if ( in_array( strtolower( $frame['function'] ), array( 'include', 'require', 'include_once', 'require_once' ), true ) ) {
|
||||
if ( in_array( strtolower( $frame['function'] ), [ 'include', 'require', 'include_once', 'require_once' ], true ) ) {
|
||||
$callback = $frame['function'] . " '" . $frame['args'][0] . "'";
|
||||
} elseif ( isset( $frame['object'] ) && method_exists( $frame['object'], $frame['function'] ) ) {
|
||||
$callback = get_class( $frame['object'] ) . '->' . $frame['function'] . '()';
|
||||
|
@ -373,7 +287,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Profiling request time for any active Loggers
|
||||
* Profiling request time for any active Loggers.
|
||||
*/
|
||||
public function wp_request_begin( $filter_value = null ) {
|
||||
foreach ( Logger::$active_loggers as $logger ) {
|
||||
|
@ -383,7 +297,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Profiling request time for any active Loggers
|
||||
* Profiling request time for any active Loggers.
|
||||
*/
|
||||
public function wp_request_end( $filter_value = null ) {
|
||||
foreach ( Logger::$active_loggers as $logger ) {
|
||||
|
@ -393,7 +307,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Runs through the entirety of the WP bootstrap process
|
||||
* Runs through the entirety of the WP bootstrap process.
|
||||
*/
|
||||
private function load_wordpress_with_template() {
|
||||
|
||||
|
@ -403,7 +317,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
if ( 'stage' === $this->type && true === $this->focus ) {
|
||||
$hooks = array();
|
||||
$hooks = [];
|
||||
foreach ( $this->stage_hooks as $stage_hook ) {
|
||||
$hooks = array_merge( $hooks, $stage_hook );
|
||||
}
|
||||
|
@ -414,7 +328,7 @@ class Profiler {
|
|||
if ( 'bootstrap' === $this->focus ) {
|
||||
$this->set_stage_hooks( $this->stage_hooks['bootstrap'] );
|
||||
} elseif ( ! $this->focus ) {
|
||||
$logger = new Logger( array( 'stage' => 'bootstrap' ) );
|
||||
$logger = new Logger( [ 'stage' => 'bootstrap' ] );
|
||||
$logger->start();
|
||||
}
|
||||
}
|
||||
|
@ -423,9 +337,6 @@ class Profiler {
|
|||
$this->loggers[ $this->running_hook ]->stop();
|
||||
$this->running_hook = null;
|
||||
}
|
||||
if ( 'hook' === $this->type && 'wp_loaded:after' === $this->focus ) {
|
||||
$this->wp_tick_profile_end();
|
||||
}
|
||||
if ( 'stage' === $this->type && ! $this->focus ) {
|
||||
$logger->stop();
|
||||
$this->loggers[] = $logger;
|
||||
|
@ -436,7 +347,7 @@ class Profiler {
|
|||
if ( 'main_query' === $this->focus ) {
|
||||
$this->set_stage_hooks( $this->stage_hooks['main_query'] );
|
||||
} elseif ( ! $this->focus ) {
|
||||
$logger = new Logger( array( 'stage' => 'main_query' ) );
|
||||
$logger = new Logger( [ 'stage' => 'main_query' ] );
|
||||
$logger->start();
|
||||
}
|
||||
}
|
||||
|
@ -445,9 +356,6 @@ class Profiler {
|
|||
$this->loggers[ $this->running_hook ]->stop();
|
||||
$this->running_hook = null;
|
||||
}
|
||||
if ( 'hook' === $this->type && 'wp:after' === $this->focus ) {
|
||||
$this->wp_tick_profile_end();
|
||||
}
|
||||
if ( 'stage' === $this->type && ! $this->focus ) {
|
||||
$logger->stop();
|
||||
$this->loggers[] = $logger;
|
||||
|
@ -466,7 +374,7 @@ class Profiler {
|
|||
if ( 'template' === $this->focus ) {
|
||||
$this->set_stage_hooks( $this->stage_hooks['template'] );
|
||||
} elseif ( ! $this->focus ) {
|
||||
$logger = new Logger( array( 'stage' => 'template' ) );
|
||||
$logger = new Logger( [ 'stage' => 'template' ] );
|
||||
$logger->start();
|
||||
}
|
||||
}
|
||||
|
@ -477,9 +385,6 @@ class Profiler {
|
|||
$this->loggers[ $this->running_hook ]->stop();
|
||||
$this->running_hook = null;
|
||||
}
|
||||
if ( 'hook' === $this->type && 'wp_footer:after' === $this->focus ) {
|
||||
$this->wp_tick_profile_end();
|
||||
}
|
||||
if ( 'stage' === $this->type && ! $this->focus ) {
|
||||
$logger->stop();
|
||||
$this->loggers[] = $logger;
|
||||
|
@ -488,7 +393,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a human-readable name from a callback
|
||||
* Get a human-readable name from a callback.
|
||||
*/
|
||||
private static function get_name_location_from_callback( $callback ) {
|
||||
$location = '';
|
||||
|
@ -510,11 +415,11 @@ class Profiler {
|
|||
if ( $reflection ) {
|
||||
$location = $reflection->getFileName() . ':' . $reflection->getStartLine();
|
||||
}
|
||||
return array( $name, $location );
|
||||
return [ $name, $location ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short location from the full location
|
||||
* Get the short location from the full location.
|
||||
*
|
||||
* @param string $location
|
||||
* @return string
|
||||
|
@ -536,17 +441,17 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the hooks for the current stage
|
||||
* Set the hooks for the current stage.
|
||||
*/
|
||||
private function set_stage_hooks( $hooks ) {
|
||||
$this->current_stage_hooks = $hooks;
|
||||
$pseudo_hook = "{$hooks[0]}:before";
|
||||
$this->loggers[ $pseudo_hook ] = new Logger( array( 'hook' => $pseudo_hook ) );
|
||||
$this->loggers[ $pseudo_hook ] = new Logger( [ 'hook' => $pseudo_hook ] );
|
||||
$this->loggers[ $pseudo_hook ]->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the callbacks for a given filter
|
||||
* Get the callbacks for a given filter.
|
||||
*
|
||||
* @param string
|
||||
* @return array|false
|
||||
|
@ -570,7 +475,7 @@ class Profiler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the callbacks for a given filter
|
||||
* Set the callbacks for a given filter.
|
||||
*
|
||||
* @param string $filter
|
||||
* @param mixed $callbacks
|
||||
|
@ -589,4 +494,32 @@ class Profiler {
|
|||
}
|
||||
}
|
||||
|
||||
protected function configure_wordpress_context() {
|
||||
WP_CLI::add_wp_hook(
|
||||
'muplugins_loaded',
|
||||
static function () {
|
||||
$url = WP_CLI::get_runner()->config['url'];
|
||||
if ( ! empty( $url ) ) {
|
||||
WP_CLI::set_url( trailingslashit( $url ) );
|
||||
} else {
|
||||
WP_CLI::set_url( home_url( '/' ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function enable_savequeries() {
|
||||
WP_CLI::add_hook(
|
||||
'after_wp_config_load',
|
||||
static 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 );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
10
src/Report.php
Normal file
10
src/Report.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile;
|
||||
|
||||
/**
|
||||
* Report for the collected data.
|
||||
*/
|
||||
interface Report {
|
||||
|
||||
}
|
9
src/Report/Aggregated.php
Normal file
9
src/Report/Aggregated.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Report;
|
||||
|
||||
use WP_CLI\Profile\Report;
|
||||
|
||||
final class Aggregated implements Report {
|
||||
|
||||
}
|
12
src/Report/DatabaseQueries.php
Normal file
12
src/Report/DatabaseQueries.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Report;
|
||||
|
||||
use WP_CLI\Profile\Report;
|
||||
|
||||
/**
|
||||
* Report collected database queries.
|
||||
*/
|
||||
final class DatabaseQueries implements Report {
|
||||
|
||||
}
|
12
src/Report/ExecutionTime.php
Normal file
12
src/Report/ExecutionTime.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Report;
|
||||
|
||||
use WP_CLI\Profile\Report;
|
||||
|
||||
/**
|
||||
* Report collected execution time.
|
||||
*/
|
||||
final class ExecutionTime implements Report {
|
||||
|
||||
}
|
12
src/Report/HttpRequests.php
Normal file
12
src/Report/HttpRequests.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Report;
|
||||
|
||||
use WP_CLI\Profile\Report;
|
||||
|
||||
/**
|
||||
* Report collected HTTP requests.
|
||||
*/
|
||||
final class HttpRequests implements Report {
|
||||
|
||||
}
|
29
src/Scope.php
Normal file
29
src/Scope.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile;
|
||||
|
||||
/**
|
||||
* Scope for which to collect data.
|
||||
*/
|
||||
interface Scope {
|
||||
|
||||
const TYPE_ALL_HOOKS = 'all_hooks';
|
||||
const TYPE_HOOK = 'hook';
|
||||
const TYPE_STAGE = 'stage';
|
||||
|
||||
/**
|
||||
* Get the type of the scope.
|
||||
*
|
||||
* @return string Type of the scope.
|
||||
*/
|
||||
public function get_type();
|
||||
|
||||
/**
|
||||
* Check if the scope includes a given hook.
|
||||
*
|
||||
* @param string $hook Hook to check.
|
||||
*
|
||||
* @return bool Whether the hook is included in the scope.
|
||||
*/
|
||||
public function includes_hook( $hook );
|
||||
}
|
31
src/Scope/AllHooks.php
Normal file
31
src/Scope/AllHooks.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Scope;
|
||||
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect data across all hooks.
|
||||
*/
|
||||
final class AllHooks implements Scope {
|
||||
|
||||
/**
|
||||
* Check if the scope includes a given hook.
|
||||
*
|
||||
* @param string $hook Hook to check.
|
||||
*
|
||||
* @return bool Whether the hook is included in the scope.
|
||||
*/
|
||||
public function includes_hook( $hook ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the scope.
|
||||
*
|
||||
* @return string Type of the scope.
|
||||
*/
|
||||
public function get_type() {
|
||||
return Scope::TYPE_ALL_HOOKS;
|
||||
}
|
||||
}
|
47
src/Scope/Hook.php
Normal file
47
src/Scope/Hook.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Scope;
|
||||
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect data scoped based on a provided hook.
|
||||
*/
|
||||
final class Hook implements Scope {
|
||||
|
||||
/**
|
||||
* Hook to scope the data collection by.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $hook;
|
||||
|
||||
/**
|
||||
* Instantiate a Hook object.
|
||||
*
|
||||
* @param string $hook Hook to scope the data collection by.
|
||||
*/
|
||||
public function __construct( $hook ) {
|
||||
$this->hook = $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the scope includes a given hook.
|
||||
*
|
||||
* @param string $hook Hook to check.
|
||||
*
|
||||
* @return bool Whether the hook is included in the scope.
|
||||
*/
|
||||
public function includes_hook( $hook ) {
|
||||
return $hook === $this->hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the scope.
|
||||
*
|
||||
* @return string Type of the scope.
|
||||
*/
|
||||
public function get_type() {
|
||||
return Scope::TYPE_HOOK;
|
||||
}
|
||||
}
|
91
src/Scope/Stage.php
Normal file
91
src/Scope/Stage.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace WP_CLI\Profile\Scope;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use WP_CLI\Profile\Scope;
|
||||
|
||||
/**
|
||||
* Collect data scoped based on a provided stage.
|
||||
*/
|
||||
final class Stage implements Scope {
|
||||
|
||||
const BOOTSTRAP = 'bootstrap';
|
||||
const MAIN_QUERY = 'main_query';
|
||||
const TEMPLATE = 'template';
|
||||
|
||||
const STAGE_HOOKS = [
|
||||
self::BOOTSTRAP => [
|
||||
'muplugins_loaded',
|
||||
'plugins_loaded',
|
||||
'setup_theme',
|
||||
'after_setup_theme',
|
||||
'init',
|
||||
'wp_loaded',
|
||||
],
|
||||
self::MAIN_QUERY => [
|
||||
'parse_request',
|
||||
'send_headers',
|
||||
'pre_get_posts',
|
||||
'the_posts',
|
||||
'wp',
|
||||
],
|
||||
self::TEMPLATE => [
|
||||
'template_redirect',
|
||||
'template_include',
|
||||
'wp_head',
|
||||
'loop_start',
|
||||
'loop_end',
|
||||
'wp_footer',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Stage to scope the data collection by.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $stage;
|
||||
|
||||
/**
|
||||
* Instantiate a Stage object.
|
||||
*
|
||||
* @param string $stage Stage to scope the data collection by.
|
||||
*/
|
||||
public function __construct( $stage ) {
|
||||
if ( ! array_key_exists( $stage, self::STAGE_HOOKS ) ) {
|
||||
throw new InvalidArgumentException( "Invalid stage {$stage}" );
|
||||
}
|
||||
|
||||
$this->stage = $stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stage to scope the collection of data by.
|
||||
*
|
||||
* @return string Stage to scope the data collection by.
|
||||
*/
|
||||
public function get_stage() {
|
||||
return $this->stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the scope includes a given hook.
|
||||
*
|
||||
* @param string $hook Hook to check.
|
||||
*
|
||||
* @return bool Whether the hook is included in the scope.
|
||||
*/
|
||||
public function includes_hook( $hook ) {
|
||||
return array_key_exists( $hook, self::STAGE_HOOKS[ $this->stage ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the scope.
|
||||
*
|
||||
* @return string Type of the scope.
|
||||
*/
|
||||
public function get_type() {
|
||||
return Scope::TYPE_STAGE;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue