Reorganized code to designate events as input and records as results.
This commit is contained in:
1 parent
67d686f601
commit
ce7cf8eade
11 files changed
+159
-199
No files matched your search
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Rarst\Laps\Record;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Rarst\Laps\Bootable_Provider_Interface;
|
||||
use Rarst\Laps\Events\Core_Events;
|
||||
use Rarst\Laps\Events\Laps_Events;
|
||||
use Rarst\Laps\Laps;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\Stopwatch\StopwatchEvent;
|
||||
|
||||
class Hook_Record_Collector implements ServiceProviderInterface, Bootable_Provider_Interface, Record_Collector_Interface {
|
||||
|
||||
/** @var Stopwatch $stopwatch */
|
||||
protected $stopwatch;
|
||||
|
||||
protected $events = [];
|
||||
|
||||
public function register( Container $pimple ) {
|
||||
|
||||
}
|
||||
|
||||
public function boot( Laps $laps ) {
|
||||
|
||||
$this->stopwatch = $laps['stopwatch'];
|
||||
|
||||
$this->stopwatch->start( 'Plugins Load', 'plugin' );
|
||||
|
||||
$events = new Core_Events();
|
||||
$this->add_events( $events->get() );
|
||||
|
||||
add_action( 'after_setup_theme', [ $this, 'after_setup_theme' ], 15 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook events by name and priority from array.
|
||||
*
|
||||
* @param array $stops
|
||||
*/
|
||||
public function add_events( $stops ) {
|
||||
|
||||
$this->events = array_merge( $this->events, $stops );
|
||||
|
||||
foreach ( $stops as $hook_name => $data ) {
|
||||
|
||||
foreach ( array_keys( $data ) as $priority ) {
|
||||
|
||||
add_action( $hook_name, [ $this, 'tick' ], $priority );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When theme is done possibly add vendor-specific events.
|
||||
*/
|
||||
public function after_setup_theme() {
|
||||
|
||||
foreach ( [ 'THA', 'Hybrid', 'Genesis', 'Yoast' ] as $vendor ) {
|
||||
|
||||
$class = "Rarst\\Laps\\Events\\{$vendor}_Events";
|
||||
/** @var Laps_Events $events */
|
||||
$events = new $class;
|
||||
$this->add_events( $events->get() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark action for the event on Stopwatch.
|
||||
*
|
||||
* @param mixed $input pass through if added to filter
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function tick( $input = null ) {
|
||||
|
||||
global $wp_filter;
|
||||
|
||||
$filter_name = current_filter();
|
||||
$filter_instance = $wp_filter[ $filter_name ];
|
||||
$priority = $filter_instance instanceof \WP_Hook ? $filter_instance->current_priority() : key( $filter_instance );
|
||||
|
||||
// See https://core.trac.wordpress.org/ticket/41185 on broken priority, but more general sanity check.
|
||||
if ( empty( $this->events[ $filter_name ][ $priority ] ) ) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
$event = wp_parse_args(
|
||||
$this->events[ $filter_name ][ $priority ],
|
||||
[
|
||||
'action' => 'start',
|
||||
'category' => null,
|
||||
]
|
||||
);
|
||||
|
||||
if ( 'stop' === $event['action'] && ! $this->stopwatch->isStarted( $event['event'] ) ) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
$this->stopwatch->{$event['action']}( $event['event'], $event['category'] );
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
public function get_records() {
|
||||
|
||||
if ( $this->stopwatch->isStarted( 'Toolbar' ) ) {
|
||||
$this->stopwatch->stop( 'Toolbar' );
|
||||
}
|
||||
|
||||
$events = $this->stopwatch->getSectionEvents( '__root__' );
|
||||
|
||||
foreach ( array_keys( $events ) as $name ) {
|
||||
if ( $this->stopwatch->isStarted( $name ) ) {
|
||||
unset( $events[ $name ] );
|
||||
}
|
||||
}
|
||||
|
||||
return array_map( [ $this, 'transform' ], array_keys( $events ), $events );
|
||||
}
|
||||
|
||||
protected function transform( $name, StopwatchEvent $event ) {
|
||||
|
||||
$duration = $event->getDuration();
|
||||
$memory = $event->getMemory() / 1024 / 1024;
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'description' => "{$name} - {$duration} ms - {$memory} MB",
|
||||
'origin' => $event->getOrigin(),
|
||||
'duration' => $duration,
|
||||
'memory' => $memory,
|
||||
'category' => $event->getCategory(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Rarst\Laps\Record;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Rarst\Laps\Bootable_Provider_Interface;
|
||||
use Rarst\Laps\Laps;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\Stopwatch\StopwatchEvent;
|
||||
|
||||
class Http_Record_Collector implements ServiceProviderInterface, Bootable_Provider_Interface, Record_Collector_Interface {
|
||||
|
||||
/** @var Stopwatch $stopwatch */
|
||||
protected $stopwatch;
|
||||
|
||||
public function register( Container $pimple ) {
|
||||
|
||||
}
|
||||
|
||||
public function boot( Laps $laps ) {
|
||||
|
||||
$this->stopwatch = $laps['stopwatch'];
|
||||
|
||||
add_action( 'pre_http_request', [ $this, 'pre_http_request' ], 10, 3 );
|
||||
add_action( 'http_api_debug', [ $this, 'http_api_debug' ], 10, 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture start time of HTTP request
|
||||
*
|
||||
* @param boolean $false
|
||||
* @param array $args
|
||||
* @param string $url
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function pre_http_request( $false, $args, $url ) {
|
||||
|
||||
$this->stopwatch->start( $url, 'http' );
|
||||
|
||||
return $false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture end time of HTTP request
|
||||
*
|
||||
* @param array|\WP_Error $response
|
||||
* @param string $type
|
||||
* @param object $class
|
||||
* @param array $args
|
||||
* @param string $url
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function http_api_debug( $response, $type, $class, $args, $url ) {
|
||||
|
||||
$this->stopwatch->stop( $url );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function get_records() {
|
||||
|
||||
$events = $this->stopwatch->getSectionEvents( '__root__' );
|
||||
|
||||
return array_map( [ $this, 'transform' ], array_keys( $events ), $events );
|
||||
}
|
||||
|
||||
protected function transform( $name, StopwatchEvent $event ) {
|
||||
|
||||
$duration = $event->getDuration();
|
||||
$memory = $event->getMemory() / 1024 / 1024;
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'description' => $name,
|
||||
'origin' => $event->getOrigin(),
|
||||
'duration' => $duration,
|
||||
'memory' => $memory,
|
||||
'category' => $event->getCategory(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Rarst\Laps\Record;
|
||||
|
||||
interface Record_Collector_Interface {
|
||||
|
||||
public function get_records();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Rarst\Laps\Record;
|
||||
|
||||
class Recursive_Record_Iterator extends \ArrayIterator implements \RecursiveIterator {
|
||||
|
||||
protected $children = [];
|
||||
|
||||
public function __construct( array $records, $flags = 0 ) {
|
||||
|
||||
usort( $records, [ $this, 'sort_origin' ] );
|
||||
$end = 0;
|
||||
|
||||
foreach ( $records as $key => $event ) {
|
||||
|
||||
if ( $event['origin'] < $end ) {
|
||||
unset( $records[ $key ] );
|
||||
$this->children[] = $event;
|
||||
continue;
|
||||
}
|
||||
|
||||
$end = $event['origin'] + $event['duration'];
|
||||
}
|
||||
|
||||
parent::__construct( $records, $flags );
|
||||
}
|
||||
|
||||
protected function sort_origin( $record_a, $record_b ) {
|
||||
|
||||
if ( $record_a['origin'] === $record_b['origin'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ( $record_a['origin'] < $record_b['origin'] ) ? - 1 : 1;
|
||||
}
|
||||
|
||||
public function hasChildren() {
|
||||
|
||||
return ! empty( $this->children );
|
||||
}
|
||||
|
||||
public function getChildren() {
|
||||
|
||||
return new static( $this->children );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Rarst\Laps\Record;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Rarst\Laps\Bootable_Provider_Interface;
|
||||
use Rarst\Laps\Laps;
|
||||
|
||||
class Sql_Record_Collector implements ServiceProviderInterface, Bootable_Provider_Interface, Record_Collector_Interface {
|
||||
|
||||
protected $query_starts = [];
|
||||
|
||||
public function register( Container $pimple ) {
|
||||
|
||||
}
|
||||
|
||||
public function boot( Laps $laps ) {
|
||||
|
||||
if ( $this->is_savequeries() ) {
|
||||
add_filter( 'query', [ $this, 'query' ], 20 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture SQL queries start times
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function query( $query ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $this->query_starts ) && ! empty( $wpdb->queries ) ) {
|
||||
$this->query_starts[ count( $wpdb->queries ) ] = microtime( true ) * 1000;
|
||||
} else {
|
||||
$this->query_starts[] = microtime( true ) * 1000;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function get_records() {
|
||||
|
||||
if ( ! $this->is_savequeries() ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$query_data = [];
|
||||
$last_query_end = 0;
|
||||
|
||||
foreach ( $wpdb->queries as $key => list( $sql, $duration ) ) {
|
||||
$query_start = isset( $this->query_starts[ $key ] ) ? $this->query_starts[ $key ] : $last_query_end;
|
||||
$sql = trim( $sql );
|
||||
$category = 'query-read';
|
||||
|
||||
if ( 0 === stripos( $sql, 'INSERT' ) || 0 === stripos( $sql, 'UPDATE' ) ) {
|
||||
$category = 'query-write';
|
||||
}
|
||||
|
||||
$duration *= 1000;
|
||||
$last_query_end = $query_start + $duration;
|
||||
|
||||
$name = $sql;
|
||||
$description = $name;
|
||||
$origin = $query_start;
|
||||
|
||||
$query_data[] = compact( 'name', 'description', 'origin', 'duration', 'category' );
|
||||
}
|
||||
|
||||
return $query_data;
|
||||
}
|
||||
|
||||
protected function is_savequeries() {
|
||||
|
||||
return defined( 'SAVEQUERIES' ) && SAVEQUERIES;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user