first
This commit is contained in:
134
wp-content/plugins/query-monitor/classes/Activation.php
Normal file
134
wp-content/plugins/query-monitor/classes/Activation.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Plugin activation handler.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Activation extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
# Filters
|
||||
add_filter( 'pre_update_option_active_plugins', array( $this, 'filter_active_plugins' ) );
|
||||
add_filter( 'pre_update_site_option_active_sitewide_plugins', array( $this, 'filter_active_sitewide_plugins' ) );
|
||||
|
||||
# Activation and deactivation
|
||||
register_activation_hook( $file, array( $this, 'activate' ) );
|
||||
register_deactivation_hook( $file, array( $this, 'deactivate' ) );
|
||||
|
||||
# Parent setup:
|
||||
parent::__construct( $file );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $sitewide
|
||||
* @return void
|
||||
*/
|
||||
public function activate( $sitewide = false ) {
|
||||
$db = WP_CONTENT_DIR . '/db.php';
|
||||
$create_symlink = defined( 'QM_DB_SYMLINK' ) ? QM_DB_SYMLINK : true;
|
||||
|
||||
if ( $create_symlink && ! file_exists( $db ) && function_exists( 'symlink' ) ) {
|
||||
@symlink( $this->plugin_path( 'wp-content/db.php' ), $db ); // phpcs:ignore
|
||||
}
|
||||
|
||||
if ( $sitewide ) {
|
||||
update_site_option( 'active_sitewide_plugins', get_site_option( 'active_sitewide_plugins' ) );
|
||||
} else {
|
||||
update_option( 'active_plugins', get_option( 'active_plugins' ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deactivate() {
|
||||
$admins = QM_Util::get_admins();
|
||||
|
||||
// Remove legacy capability handling:
|
||||
if ( $admins ) {
|
||||
$admins->remove_cap( 'view_query_monitor' );
|
||||
}
|
||||
|
||||
# Only delete db.php if it belongs to Query Monitor
|
||||
if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && class_exists( 'QM_DB', false ) ) {
|
||||
unlink( WP_CONTENT_DIR . '/db.php' ); // phpcs:ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $plugins
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function filter_active_plugins( $plugins ) {
|
||||
|
||||
// this needs to run on the cli too
|
||||
|
||||
if ( empty( $plugins ) ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
$f = preg_quote( basename( $this->plugin_base() ), '/' );
|
||||
$qm = preg_grep( '/' . $f . '$/', $plugins );
|
||||
$notqm = preg_grep( '/' . $f . '$/', $plugins, PREG_GREP_INVERT );
|
||||
|
||||
if ( false === $qm || false === $notqm ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$qm,
|
||||
$notqm
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $plugins
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public function filter_active_sitewide_plugins( $plugins ) {
|
||||
|
||||
if ( empty( $plugins ) ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
$f = $this->plugin_base();
|
||||
|
||||
if ( isset( $plugins[ $f ] ) ) {
|
||||
|
||||
unset( $plugins[ $f ] );
|
||||
|
||||
return array_merge( array(
|
||||
$f => time(),
|
||||
), $plugins );
|
||||
|
||||
} else {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Activation( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
558
wp-content/plugins/query-monitor/classes/Backtrace.php
Normal file
558
wp-content/plugins/query-monitor/classes/Backtrace.php
Normal file
@ -0,0 +1,558 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Function call backtrace container.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'QM_Backtrace' ) ) {
|
||||
class QM_Backtrace {
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_class = array(
|
||||
'wpdb' => true,
|
||||
'hyperdb' => true,
|
||||
'LudicrousDB' => true,
|
||||
'QueryMonitor' => true,
|
||||
'W3_Db' => true,
|
||||
'Debug_Bar_PHP' => true,
|
||||
'WP_Hook' => true,
|
||||
'Altis\Cloud\DB' => true,
|
||||
'Yoast\WP\Lib\ORM' => true,
|
||||
'Perflab_SQLite_DB' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
protected static $ignore_method = array();
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_func = array(
|
||||
'include_once' => true,
|
||||
'require_once' => true,
|
||||
'include' => true,
|
||||
'require' => true,
|
||||
'call_user_func_array' => true,
|
||||
'call_user_func' => true,
|
||||
'trigger_error' => true,
|
||||
'_doing_it_wrong' => true,
|
||||
'_deprecated_argument' => true,
|
||||
'_deprecated_constructor' => true,
|
||||
'_deprecated_file' => true,
|
||||
'_deprecated_function' => true,
|
||||
'_deprecated_hook' => true,
|
||||
'dbDelta' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, int|string>
|
||||
*/
|
||||
protected static $show_args = array(
|
||||
'do_action' => 1,
|
||||
'apply_filters' => 1,
|
||||
'do_action_ref_array' => 1,
|
||||
'apply_filters_ref_array' => 1,
|
||||
'do_action_deprecated' => 1,
|
||||
'apply_filters_deprecated' => 1,
|
||||
'get_query_template' => 1,
|
||||
'resolve_block_template' => 1,
|
||||
'get_template_part' => 2,
|
||||
'get_extended_template_part' => 2,
|
||||
'ai_get_template_part' => 2,
|
||||
'load_template' => 'dir',
|
||||
'dynamic_sidebar' => 1,
|
||||
'get_header' => 1,
|
||||
'get_sidebar' => 1,
|
||||
'get_footer' => 1,
|
||||
'get_transient' => 1,
|
||||
'set_transient' => 1,
|
||||
'class_exists' => 2,
|
||||
'current_user_can' => 3,
|
||||
'user_can' => 4,
|
||||
'current_user_can_for_blog' => 4,
|
||||
'author_can' => 4,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_hook = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected static $filtered = false;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed[]>
|
||||
*/
|
||||
protected $args = array();
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $trace;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
*/
|
||||
protected $filtered_trace = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $calling_line = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $calling_file = '';
|
||||
|
||||
/**
|
||||
* @var QM_Component|null
|
||||
*/
|
||||
protected $component = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
*/
|
||||
protected $top_frame = null;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed[]> $args
|
||||
* @param mixed[] $trace
|
||||
*/
|
||||
public function __construct( array $args = array(), array $trace = null ) {
|
||||
$this->trace = $trace ?? debug_backtrace( 0 );
|
||||
|
||||
$this->args = array_merge( array(
|
||||
'ignore_class' => array(),
|
||||
'ignore_method' => array(),
|
||||
'ignore_func' => array(),
|
||||
'ignore_hook' => array(),
|
||||
'show_args' => array(),
|
||||
), $args );
|
||||
|
||||
foreach ( $this->trace as & $frame ) {
|
||||
if ( ! isset( $frame['args'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $frame['function'], self::$show_args[ $frame['function'] ] ) ) {
|
||||
$show = self::$show_args[ $frame['function'] ];
|
||||
|
||||
if ( ! is_int( $show ) ) {
|
||||
$show = 1;
|
||||
}
|
||||
|
||||
$frame['args'] = array_slice( $frame['args'], 0, $show );
|
||||
|
||||
} else {
|
||||
unset( $frame['args'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $frame
|
||||
* @return void
|
||||
*/
|
||||
public function push_frame( array $frame ) {
|
||||
$this->top_frame = $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_stack() {
|
||||
|
||||
$trace = $this->get_filtered_trace();
|
||||
$stack = array_column( $trace, 'display' );
|
||||
|
||||
return $stack;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function get_caller() {
|
||||
|
||||
$trace = $this->get_filtered_trace();
|
||||
|
||||
return reset( $trace );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Component
|
||||
*/
|
||||
public function get_component() {
|
||||
if ( isset( $this->component ) ) {
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
$components = array();
|
||||
$frames = $this->get_filtered_trace();
|
||||
|
||||
if ( $this->top_frame ) {
|
||||
array_unshift( $frames, $this->top_frame );
|
||||
}
|
||||
|
||||
foreach ( $frames as $frame ) {
|
||||
$component = self::get_frame_component( $frame );
|
||||
|
||||
if ( $component ) {
|
||||
if ( 'plugin' === $component->type ) {
|
||||
// If the component is a plugin then it can't be anything else,
|
||||
// so short-circuit and return early.
|
||||
$this->component = $component;
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
$components[ $component->type ] = $component;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( QM_Util::get_file_dirs() as $type => $dir ) {
|
||||
if ( isset( $components[ $type ] ) ) {
|
||||
$this->component = $components[ $type ];
|
||||
return $this->component;
|
||||
}
|
||||
}
|
||||
|
||||
$component = new QM_Component();
|
||||
$component->type = 'unknown';
|
||||
$component->name = __( 'Unknown', 'query-monitor' );
|
||||
$component->context = 'unknown';
|
||||
|
||||
return $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine the component responsible for a given frame.
|
||||
*
|
||||
* @param mixed[] $frame A single frame from a trace.
|
||||
* @phpstan-param array{
|
||||
* class?: class-string,
|
||||
* function?: string,
|
||||
* file?: string,
|
||||
* } $frame
|
||||
* @return QM_Component|null An object representing the component, or null if
|
||||
* the component cannot be determined.
|
||||
*/
|
||||
public static function get_frame_component( array $frame ) {
|
||||
try {
|
||||
|
||||
if ( isset( $frame['class'], $frame['function'] ) ) {
|
||||
if ( ! class_exists( $frame['class'], false ) ) {
|
||||
return null;
|
||||
}
|
||||
if ( ! method_exists( $frame['class'], $frame['function'] ) ) {
|
||||
return null;
|
||||
}
|
||||
$ref = new ReflectionMethod( $frame['class'], $frame['function'] );
|
||||
$file = $ref->getFileName();
|
||||
} elseif ( isset( $frame['function'] ) && function_exists( $frame['function'] ) ) {
|
||||
$ref = new ReflectionFunction( $frame['function'] );
|
||||
$file = $ref->getFileName();
|
||||
} elseif ( isset( $frame['file'] ) ) {
|
||||
$file = $frame['file'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! $file ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return QM_Util::get_file_component( $file );
|
||||
|
||||
} catch ( ReflectionException $e ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_trace() {
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the `::get_filtered_trace()` method instead.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_display_trace() {
|
||||
return $this->get_filtered_trace();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
* @phpstan-return list<array{
|
||||
* file: string,
|
||||
* line: int,
|
||||
* display: string,
|
||||
* }>
|
||||
*/
|
||||
public function get_filtered_trace() {
|
||||
|
||||
if ( ! isset( $this->filtered_trace ) ) {
|
||||
|
||||
$trace = array_map( array( $this, 'filter_trace' ), $this->trace );
|
||||
$trace = array_values( array_filter( $trace ) );
|
||||
|
||||
if ( empty( $trace ) && ! empty( $this->trace ) ) {
|
||||
$lowest = $this->trace[0];
|
||||
$file = QM_Util::standard_dir( $lowest['file'], '' );
|
||||
$lowest['calling_file'] = $lowest['file'];
|
||||
$lowest['calling_line'] = $lowest['line'];
|
||||
$lowest['function'] = $file;
|
||||
$lowest['display'] = $file;
|
||||
$lowest['id'] = $file;
|
||||
unset( $lowest['class'], $lowest['args'], $lowest['type'] );
|
||||
|
||||
// When a PHP error is triggered which doesn't have a stack trace, for example a
|
||||
// deprecated error, QM will blame itself due to its error handler. This prevents that.
|
||||
if ( false === strpos( $file, 'query-monitor/collectors/php_errors.php' ) ) {
|
||||
$trace[0] = $lowest;
|
||||
}
|
||||
}
|
||||
|
||||
$this->filtered_trace = $trace;
|
||||
|
||||
}
|
||||
|
||||
return $this->filtered_trace;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $stack
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function get_filtered_stack( array $stack ) {
|
||||
$trace = new self( array(), array() );
|
||||
$return = array();
|
||||
|
||||
foreach ( $stack as $i => $item ) {
|
||||
$frame = array(
|
||||
'function' => $item,
|
||||
);
|
||||
|
||||
if ( false !== strpos( $item, '->' ) ) {
|
||||
list( $class, $function ) = explode( '->', $item );
|
||||
$frame = array(
|
||||
'class' => $class,
|
||||
'type' => '->',
|
||||
'function' => $function,
|
||||
);
|
||||
}
|
||||
|
||||
if ( false !== strpos( $item, '::' ) ) {
|
||||
list( $class, $function ) = explode( '::', $item );
|
||||
$frame = array(
|
||||
'class' => $class,
|
||||
'type' => '::',
|
||||
'function' => $function,
|
||||
);
|
||||
}
|
||||
|
||||
$frame['args'] = array();
|
||||
|
||||
if ( $trace->filter_trace( $frame ) ) {
|
||||
$return[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the `ignore_class`, `ignore_method`, `ignore_func`, and `ignore_hook` arguments instead.
|
||||
*
|
||||
* @param int $num
|
||||
* @return self
|
||||
*/
|
||||
public function ignore( $num ) {
|
||||
for ( $i = 0; $i < $num; $i++ ) {
|
||||
unset( $this->trace[ $i ] );
|
||||
}
|
||||
$this->trace = array_values( $this->trace );
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $frame
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
public function filter_trace( array $frame ) {
|
||||
|
||||
if ( ! self::$filtered && function_exists( 'did_action' ) && did_action( 'plugins_loaded' ) ) {
|
||||
|
||||
/**
|
||||
* Filters which classes to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_class Array of class names to ignore. The array keys are class names to ignore,
|
||||
* the array values are whether to ignore the class (usually true).
|
||||
*/
|
||||
self::$ignore_class = apply_filters( 'qm/trace/ignore_class', self::$ignore_class );
|
||||
|
||||
/**
|
||||
* Filters which class methods to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, array<string, bool>> $ignore_method Array of method names to ignore. The top level array keys are
|
||||
* class names, the second level array keys are method names, and
|
||||
* the array values are whether to ignore the method (usually true).
|
||||
*/
|
||||
self::$ignore_method = apply_filters( 'qm/trace/ignore_method', self::$ignore_method );
|
||||
|
||||
/**
|
||||
* Filters which functions to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_func Array of function names to ignore. The array keys are function names to ignore,
|
||||
* the array values are whether to ignore the function (usually true).
|
||||
*/
|
||||
self::$ignore_func = apply_filters( 'qm/trace/ignore_func', self::$ignore_func );
|
||||
|
||||
/**
|
||||
* Filters which action and filter names to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_hook Array of hook names to ignore. The array keys are hook names to ignore,
|
||||
* the array values are whether to ignore the hook (usually true).
|
||||
*/
|
||||
self::$ignore_hook = apply_filters( 'qm/trace/ignore_hook', self::$ignore_hook );
|
||||
|
||||
/**
|
||||
* Filters the number of argument values to show for the given function name when constructing user-facing
|
||||
* call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string,int|string> $show_args The number of argument values to show for the given function name. The
|
||||
* array keys are function names, the array values are either integers or
|
||||
* "dir" to specifically treat the function argument as a directory path.
|
||||
*/
|
||||
self::$show_args = apply_filters( 'qm/trace/show_args', self::$show_args );
|
||||
|
||||
self::$filtered = true;
|
||||
|
||||
}
|
||||
|
||||
$return = $frame;
|
||||
$ignore_class = array_filter( array_merge( self::$ignore_class, $this->args['ignore_class'] ) );
|
||||
$ignore_method = array_filter( array_merge( self::$ignore_method, $this->args['ignore_method'] ) );
|
||||
$ignore_func = array_filter( array_merge( self::$ignore_func, $this->args['ignore_func'] ) );
|
||||
$ignore_hook = array_filter( array_merge( self::$ignore_hook, $this->args['ignore_hook'] ) );
|
||||
$show_args = array_merge( self::$show_args, $this->args['show_args'] );
|
||||
|
||||
$hook_functions = array(
|
||||
'apply_filters' => true,
|
||||
'do_action' => true,
|
||||
'apply_filters_ref_array' => true,
|
||||
'do_action_ref_array' => true,
|
||||
'apply_filters_deprecated' => true,
|
||||
'do_action_deprecated' => true,
|
||||
);
|
||||
|
||||
if ( ! isset( $frame['function'] ) ) {
|
||||
$frame['function'] = '(unknown)';
|
||||
}
|
||||
|
||||
if ( isset( $frame['class'] ) ) {
|
||||
if ( isset( $ignore_class[ $frame['class'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( isset( $ignore_method[ $frame['class'] ][ $frame['function'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( 0 === strpos( $frame['class'], 'QM' ) ) {
|
||||
$return = null;
|
||||
} else {
|
||||
$return['id'] = $frame['class'] . $frame['type'] . $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['class'] . $frame['type'] . $frame['function'] ) . '()';
|
||||
}
|
||||
} else {
|
||||
if ( isset( $ignore_func[ $frame['function'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( isset( $show_args[ $frame['function'] ] ) ) {
|
||||
$show = $show_args[ $frame['function'] ];
|
||||
|
||||
if ( 'dir' === $show ) {
|
||||
if ( isset( $frame['args'][0] ) ) {
|
||||
$arg = QM_Util::standard_dir( $frame['args'][0], '' );
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . "('{$arg}')";
|
||||
}
|
||||
} else {
|
||||
if ( isset( $hook_functions[ $frame['function'] ], $frame['args'][0] ) && is_string( $frame['args'][0] ) && isset( $ignore_hook[ $frame['args'][0] ] ) ) {
|
||||
$return = null;
|
||||
} else {
|
||||
$args = array();
|
||||
for ( $i = 0; $i < $show; $i++ ) {
|
||||
if ( isset( $frame['args'] ) && array_key_exists( $i, $frame['args'] ) ) {
|
||||
if ( is_string( $frame['args'][ $i ] ) ) {
|
||||
$args[] = '\'' . $frame['args'][ $i ] . '\'';
|
||||
} else {
|
||||
$args[] = QM_Util::display_variable( $frame['args'][ $i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . '(' . implode( ',', $args ) . ')';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . '()';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $return ) {
|
||||
|
||||
$return['calling_file'] = $this->calling_file;
|
||||
$return['calling_line'] = $this->calling_line;
|
||||
|
||||
if ( ! isset( $return['file'] ) ) {
|
||||
$return['file'] = $this->calling_file;
|
||||
}
|
||||
|
||||
if ( ! isset( $return['line'] ) ) {
|
||||
$return['line'] = $this->calling_line;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $frame['line'] ) ) {
|
||||
$this->calling_line = $frame['line'];
|
||||
}
|
||||
if ( isset( $frame['file'] ) ) {
|
||||
$this->calling_file = $frame['file'];
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
add_action( 'init', 'QueryMonitor::symlink_warning' );
|
||||
|
||||
}
|
75
wp-content/plugins/query-monitor/classes/CLI.php
Normal file
75
wp-content/plugins/query-monitor/classes/CLI.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Plugin CLI command.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_CLI extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
|
||||
# Register command
|
||||
WP_CLI::add_command( 'qm enable', array( $this, 'enable' ) );
|
||||
|
||||
# Parent setup:
|
||||
parent::__construct( $file );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable QM by creating the symlink for db.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enable() {
|
||||
$drop_in = WP_CONTENT_DIR . '/db.php';
|
||||
|
||||
if ( file_exists( $drop_in ) ) {
|
||||
$contents = file_get_contents( $drop_in );
|
||||
|
||||
if ( false !== $contents && false !== strpos( $contents, 'class QM_DB' ) ) {
|
||||
WP_CLI::success( "Query Monitor's wp-content/db.php is already in place" );
|
||||
exit( 0 );
|
||||
} else {
|
||||
WP_CLI::error( 'Unknown wp-content/db.php is already in place' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( defined( 'QM_DB_SYMLINK' ) && ! QM_DB_SYMLINK ) {
|
||||
WP_CLI::warning( 'Creation of symlink prevented by QM_DB_SYMLINK constant.' );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'symlink' ) ) {
|
||||
WP_CLI::error( 'The symlink function is not available' );
|
||||
}
|
||||
|
||||
if ( symlink( $this->plugin_path( 'wp-content/db.php' ), $drop_in ) ) {
|
||||
WP_CLI::success( 'wp-content/db.php symlink created' );
|
||||
exit( 0 );
|
||||
} else {
|
||||
WP_CLI::error( 'Failed to create wp-content/db.php symlink' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file = null ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_CLI( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
389
wp-content/plugins/query-monitor/classes/Collector.php
Normal file
389
wp-content/plugins/query-monitor/classes/Collector.php
Normal file
@ -0,0 +1,389 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Collector' ) ) {
|
||||
abstract class QM_Collector {
|
||||
|
||||
/**
|
||||
* @var QM_Timer|null
|
||||
*/
|
||||
protected $timer;
|
||||
|
||||
/**
|
||||
* @var QM_Data
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected static $hide_qm = null;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_actions = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_filters = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_constants = array();
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $tracked_hooks = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->data = $this->get_storage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
final public function id() {
|
||||
return "qm-{$this->id}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
protected function log_type( $type ) {
|
||||
if ( isset( $this->data->types[ $type ] ) ) {
|
||||
$this->data->types[ $type ]++;
|
||||
} else {
|
||||
$this->data->types[ $type ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Component $component
|
||||
* @param float $ltime
|
||||
* @param string|int $type
|
||||
* @return void
|
||||
*/
|
||||
protected function log_component( $component, $ltime, $type ) {
|
||||
if ( ! isset( $this->data->component_times[ $component->name ] ) ) {
|
||||
$this->data->component_times[ $component->name ] = array(
|
||||
'component' => $component->name,
|
||||
'ltime' => 0,
|
||||
'types' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->component_times[ $component->name ]['ltime'] += $ltime;
|
||||
|
||||
if ( isset( $this->data->component_times[ $component->name ]['types'][ $type ] ) ) {
|
||||
$this->data->component_times[ $component->name ]['types'][ $type ]++;
|
||||
} else {
|
||||
$this->data->component_times[ $component->name ]['types'][ $type ] = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public static function timer_stop_float() {
|
||||
return microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $constant
|
||||
* @return string
|
||||
*/
|
||||
public static function format_bool_constant( $constant ) {
|
||||
// @TODO this should be in QM_Util
|
||||
|
||||
if ( ! defined( $constant ) ) {
|
||||
/* translators: Undefined PHP constant */
|
||||
return __( 'undefined', 'query-monitor' );
|
||||
} elseif ( is_string( constant( $constant ) ) && ! is_numeric( constant( $constant ) ) ) {
|
||||
return constant( $constant );
|
||||
} elseif ( ! constant( $constant ) ) {
|
||||
return 'false';
|
||||
} else {
|
||||
return 'true';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
*/
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Fallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
final public function discard_data() {
|
||||
$this->data = $this->get_storage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return void
|
||||
*/
|
||||
final public function set_id( $id ) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
final public function process_concerns() {
|
||||
global $wp_filter;
|
||||
|
||||
$tracked = array();
|
||||
$id = $this->id;
|
||||
|
||||
/**
|
||||
* Filters the concerned actions for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $actions Array of action names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_actions = apply_filters( "qm/collect/concerned_actions/{$id}", $this->get_concerned_actions() );
|
||||
|
||||
/**
|
||||
* Filters the concerned filters for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $filters Array of filter names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_filters = apply_filters( "qm/collect/concerned_filters/{$id}", $this->get_concerned_filters() );
|
||||
|
||||
/**
|
||||
* Filters the concerned options for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $options Array of option names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_options = apply_filters( "qm/collect/concerned_options/{$id}", $this->get_concerned_options() );
|
||||
|
||||
/**
|
||||
* Filters the concerned constants for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $constants Array of constant names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_constants = apply_filters( "qm/collect/concerned_constants/{$id}", $this->get_concerned_constants() );
|
||||
|
||||
foreach ( $concerned_actions as $action ) {
|
||||
if ( has_action( $action ) ) {
|
||||
$this->concerned_actions[ $action ] = QM_Hook::process( $action, 'action', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $action;
|
||||
}
|
||||
|
||||
foreach ( $concerned_filters as $filter ) {
|
||||
if ( has_filter( $filter ) ) {
|
||||
$this->concerned_filters[ $filter ] = QM_Hook::process( $filter, 'filter', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $filter;
|
||||
}
|
||||
|
||||
$option_filters = array(
|
||||
// Should this include the pre_delete_ and pre_update_ filters too?
|
||||
'pre_option_%s',
|
||||
'default_option_%s',
|
||||
'option_%s',
|
||||
'pre_site_option_%s',
|
||||
'default_site_option_%s',
|
||||
'site_option_%s',
|
||||
);
|
||||
|
||||
foreach ( $concerned_options as $option ) {
|
||||
foreach ( $option_filters as $option_filter ) {
|
||||
$filter = sprintf(
|
||||
$option_filter,
|
||||
$option
|
||||
);
|
||||
if ( has_filter( $filter ) ) {
|
||||
$this->concerned_filters[ $filter ] = QM_Hook::process( $filter, 'filter', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $filter;
|
||||
}
|
||||
}
|
||||
|
||||
$this->concerned_actions = array_filter( $this->concerned_actions, array( $this, 'filter_concerns' ) );
|
||||
$this->concerned_filters = array_filter( $this->concerned_filters, array( $this, 'filter_concerns' ) );
|
||||
|
||||
foreach ( $concerned_constants as $constant ) {
|
||||
if ( defined( $constant ) ) {
|
||||
$this->concerned_constants[ $constant ] = constant( $constant );
|
||||
}
|
||||
}
|
||||
|
||||
sort( $tracked );
|
||||
|
||||
$this->tracked_hooks = $tracked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $concerns
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_concerns( $concerns ) {
|
||||
return ! empty( $concerns['actions'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WP_User $user_object
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function format_user( WP_User $user_object ) {
|
||||
$user = get_object_vars( $user_object->data );
|
||||
unset(
|
||||
$user['user_pass'],
|
||||
$user['user_activation_key']
|
||||
);
|
||||
$user['roles'] = $user_object->roles;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function enabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function hide_qm() {
|
||||
if ( ! defined( 'QM_HIDE_SELF' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( null === self::$hide_qm ) {
|
||||
self::$hide_qm = QM_HIDE_SELF;
|
||||
}
|
||||
|
||||
return self::$hide_qm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $item
|
||||
* @phpstan-param array{
|
||||
* component: QM_Component,
|
||||
* } $item
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_remove_qm( array $item ) {
|
||||
return ( 'query-monitor' !== $item['component']->context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $items
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_dupe_items( $items ) {
|
||||
return ( count( $items ) > 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function post_process() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {}
|
||||
|
||||
/**
|
||||
* @return QM_Timer|null
|
||||
*/
|
||||
public function get_timer() {
|
||||
return $this->timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Timer $timer
|
||||
* @return void
|
||||
*/
|
||||
public function set_timer( QM_Timer $timer ) {
|
||||
$this->timer = $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
340
wp-content/plugins/query-monitor/classes/Collector_Assets.php
Normal file
340
wp-content/plugins/query-monitor/classes/Collector_Assets.php
Normal file
@ -0,0 +1,340 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Enqueued scripts and styles collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Assets>
|
||||
*/
|
||||
abstract class QM_Collector_Assets extends QM_DataCollector {
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Assets();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
add_action( 'admin_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
add_action( 'wp_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
add_action( 'admin_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'wp_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'login_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'embed_head', array( $this, 'action_head' ), 9999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_action( 'admin_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
remove_action( 'wp_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
remove_action( 'admin_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'wp_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'login_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'embed_head', array( $this, 'action_head' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_dependency_type();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_head() {
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
/** @var WP_Dependencies $dependencies */
|
||||
$dependencies = $GLOBALS[ "wp_{$type}" ];
|
||||
|
||||
$this->data->header = $dependencies->done;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_print_footer_scripts() {
|
||||
if ( empty( $this->data->header ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
/** @var WP_Dependencies $dependencies */
|
||||
$dependencies = $GLOBALS[ "wp_{$type}" ];
|
||||
|
||||
$this->data->footer = array_diff( $dependencies->done, $this->data->header );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( empty( $this->data->header ) && empty( $this->data->footer ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data->is_ssl = is_ssl();
|
||||
$this->data->host = wp_unslash( $_SERVER['HTTP_HOST'] );
|
||||
$this->data->default_version = get_bloginfo( 'version' );
|
||||
$this->data->port = (string) parse_url( $this->data->host, PHP_URL_PORT );
|
||||
|
||||
$positions = array(
|
||||
'missing',
|
||||
'broken',
|
||||
'header',
|
||||
'footer',
|
||||
);
|
||||
|
||||
$this->data->counts = array(
|
||||
'missing' => 0,
|
||||
'broken' => 0,
|
||||
'header' => 0,
|
||||
'footer' => 0,
|
||||
'total' => 0,
|
||||
);
|
||||
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
foreach ( array( 'header', 'footer' ) as $position ) {
|
||||
if ( empty( $this->data->{$position} ) ) {
|
||||
$this->data->{$position} = array();
|
||||
}
|
||||
}
|
||||
|
||||
/** @var WP_Dependencies $raw */
|
||||
$raw = $GLOBALS[ "wp_{$type}" ];
|
||||
$broken = array_values( array_diff( $raw->queue, $raw->done ) );
|
||||
$missing = array_values( array_diff( $raw->queue, array_keys( $raw->registered ) ) );
|
||||
|
||||
// A broken asset is one which has been deregistered without also being dequeued
|
||||
if ( ! empty( $broken ) ) {
|
||||
foreach ( $broken as $key => $handle ) {
|
||||
/** @var _WP_Dependency|false $item */
|
||||
$item = $raw->query( $handle );
|
||||
if ( $item ) {
|
||||
$broken = array_merge( $broken, self::get_broken_dependencies( $item, $raw ) );
|
||||
} else {
|
||||
unset( $broken[ $key ] );
|
||||
$missing[] = $handle;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $broken ) ) {
|
||||
$this->data->broken = array_unique( $broken );
|
||||
}
|
||||
}
|
||||
|
||||
// A missing asset is one which has been enqueued with dependencies that don't exist
|
||||
if ( ! empty( $missing ) ) {
|
||||
$this->data->missing = array_unique( $missing );
|
||||
foreach ( $this->data->missing as $handle ) {
|
||||
$raw->add( $handle, false );
|
||||
$key = array_search( $handle, $raw->done, true );
|
||||
if ( false !== $key ) {
|
||||
unset( $raw->done[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$all_dependencies = array();
|
||||
$all_dependents = array();
|
||||
|
||||
$missing_dependencies = array();
|
||||
|
||||
foreach ( $positions as $position ) {
|
||||
if ( empty( $this->data->{$position} ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string $handle */
|
||||
foreach ( $this->data->{$position} as $handle ) {
|
||||
/** @var _WP_Dependency|false $dependency */
|
||||
$dependency = $raw->query( $handle );
|
||||
|
||||
if ( ! $dependency ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$all_dependencies = array_merge( $all_dependencies, $dependency->deps );
|
||||
$dependents = $this->get_dependents( $dependency, $raw );
|
||||
$all_dependents = array_merge( $all_dependents, $dependents );
|
||||
|
||||
list( $host, $source, $local, $port ) = $this->get_dependency_data( $dependency );
|
||||
|
||||
if ( empty( $dependency->ver ) ) {
|
||||
$ver = '';
|
||||
} else {
|
||||
$ver = $dependency->ver;
|
||||
}
|
||||
|
||||
$warning = ! in_array( $handle, $raw->done, true );
|
||||
|
||||
if ( $source instanceof WP_Error ) {
|
||||
$display = $source->get_error_message();
|
||||
} else {
|
||||
$display = ltrim( preg_replace( '#https?://' . preg_quote( $this->data->host, '#' ) . '#', '', remove_query_arg( 'ver', $source ) ), '/' );
|
||||
}
|
||||
|
||||
$dependencies = $dependency->deps;
|
||||
|
||||
foreach ( $dependencies as $dep ) {
|
||||
if ( ! $raw->query( $dep ) ) {
|
||||
// A missing dependency is a dependency on an asset that doesn't exist
|
||||
$missing_dependencies[ $dep ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->assets[ $position ][ $handle ] = array(
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'source' => $source,
|
||||
'local' => $local,
|
||||
'ver' => $ver,
|
||||
'warning' => $warning,
|
||||
'display' => $display,
|
||||
'dependents' => $dependents,
|
||||
'dependencies' => $dependencies,
|
||||
);
|
||||
|
||||
$this->data->counts[ $position ]++;
|
||||
$this->data->counts['total']++;
|
||||
}
|
||||
}
|
||||
|
||||
unset( $this->data->{$position} );
|
||||
|
||||
$all_dependencies = array_unique( $all_dependencies );
|
||||
sort( $all_dependencies );
|
||||
$this->data->dependencies = $all_dependencies;
|
||||
|
||||
$all_dependents = array_unique( $all_dependents );
|
||||
sort( $all_dependents );
|
||||
$this->data->dependents = $all_dependents;
|
||||
|
||||
$this->data->missing_dependencies = $missing_dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $item
|
||||
* @param WP_Dependencies $dependencies
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected static function get_broken_dependencies( _WP_Dependency $item, WP_Dependencies $dependencies ) {
|
||||
$broken = array();
|
||||
|
||||
foreach ( $item->deps as $handle ) {
|
||||
$dep = $dependencies->query( $handle );
|
||||
if ( $dep instanceof _WP_Dependency ) {
|
||||
$broken = array_merge( $broken, self::get_broken_dependencies( $dep, $dependencies ) );
|
||||
} else {
|
||||
$broken[] = $item->handle;
|
||||
}
|
||||
}
|
||||
|
||||
return $broken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $dependency
|
||||
* @param WP_Dependencies $dependencies
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_dependents( _WP_Dependency $dependency, WP_Dependencies $dependencies ) {
|
||||
$dependents = array();
|
||||
$handles = array_unique( array_merge( $dependencies->queue, $dependencies->done ) );
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$item = $dependencies->query( $handle );
|
||||
if ( $item instanceof _WP_Dependency ) {
|
||||
if ( in_array( $dependency->handle, $item->deps, true ) ) {
|
||||
$dependents[] = $handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort( $dependents );
|
||||
|
||||
return $dependents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $dependency
|
||||
* @return mixed[]
|
||||
* @phpstan-return array{
|
||||
* 0: string,
|
||||
* 1: string|WP_Error,
|
||||
* 2: bool,
|
||||
* 3: string,
|
||||
* }
|
||||
*/
|
||||
public function get_dependency_data( _WP_Dependency $dependency ) {
|
||||
/** @var QM_Data_Assets */
|
||||
$data = $this->get_data();
|
||||
$loader = rtrim( $this->get_dependency_type(), 's' );
|
||||
$src = $dependency->src;
|
||||
$host = '';
|
||||
$scheme = '';
|
||||
$port = '';
|
||||
|
||||
if ( null === $dependency->ver ) {
|
||||
$ver = '';
|
||||
} else {
|
||||
$ver = $dependency->ver ?: $this->data->default_version;
|
||||
}
|
||||
|
||||
if ( ! empty( $src ) && ! empty( $ver ) ) {
|
||||
$src = add_query_arg( 'ver', $ver, $src );
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/class.wp-scripts.php */
|
||||
$source = apply_filters( "{$loader}_loader_src", $src, $dependency->handle );
|
||||
|
||||
if ( is_string( $source ) ) {
|
||||
$host = (string) parse_url( $source, PHP_URL_HOST );
|
||||
$scheme = (string) parse_url( $source, PHP_URL_SCHEME );
|
||||
$port = (string) parse_url( $source, PHP_URL_PORT );
|
||||
}
|
||||
|
||||
$http_host = $data->host;
|
||||
$http_port = $data->port;
|
||||
|
||||
if ( empty( $host ) && ! empty( $http_host ) ) {
|
||||
$host = $http_host;
|
||||
$port = $http_port;
|
||||
}
|
||||
|
||||
if ( $scheme && $data->is_ssl && ( 'https' !== $scheme ) && ( 'localhost' !== $host ) ) {
|
||||
$source = new WP_Error( 'qm_insecure_content', __( 'Insecure content', 'query-monitor' ), array(
|
||||
'src' => $source,
|
||||
) );
|
||||
}
|
||||
|
||||
if ( $source instanceof WP_Error ) {
|
||||
$error_data = $source->get_error_data();
|
||||
if ( $error_data && isset( $error_data['src'] ) ) {
|
||||
$host = (string) parse_url( $error_data['src'], PHP_URL_HOST );
|
||||
}
|
||||
} elseif ( empty( $source ) ) {
|
||||
$source = '';
|
||||
$host = '';
|
||||
}
|
||||
|
||||
$local = ( $http_host === $host );
|
||||
|
||||
return array( $host, $source, $local, $port );
|
||||
}
|
||||
|
||||
}
|
112
wp-content/plugins/query-monitor/classes/Collectors.php
Normal file
112
wp-content/plugins/query-monitor/classes/Collectors.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Container for data collectors.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Collectors' ) ) {
|
||||
/**
|
||||
* @implements \IteratorAggregate<string, QM_Collector>
|
||||
*/
|
||||
class QM_Collectors implements IteratorAggregate {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Collector>
|
||||
*/
|
||||
private $items = array();
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $processed = false;
|
||||
|
||||
/**
|
||||
* @return ArrayIterator<string, QM_Collector>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return new ArrayIterator( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Collector $collector
|
||||
* @return void
|
||||
*/
|
||||
public static function add( QM_Collector $collector ) {
|
||||
$collectors = self::init();
|
||||
|
||||
$collector->set_up();
|
||||
|
||||
$collectors->items[ $collector->id ] = $collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a collector instance.
|
||||
*
|
||||
* @param string $id The collector ID.
|
||||
* @return QM_Collector|null The collector object.
|
||||
*/
|
||||
public static function get( $id ) {
|
||||
$collectors = self::init();
|
||||
|
||||
return $collectors->items[ $id ] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function init() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Collectors();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( $this->processed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this as $collector ) {
|
||||
$collector->tear_down();
|
||||
|
||||
$timer = new QM_Timer();
|
||||
$timer->start();
|
||||
|
||||
$collector->process();
|
||||
$collector->process_concerns();
|
||||
|
||||
$collector->set_timer( $timer->stop() );
|
||||
}
|
||||
|
||||
foreach ( $this as $collector ) {
|
||||
$collector->post_process();
|
||||
}
|
||||
|
||||
$this->processed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function cease() {
|
||||
$collectors = self::init();
|
||||
|
||||
$collectors->processed = true;
|
||||
|
||||
/** @var QM_Collector $collector */
|
||||
foreach ( $collectors as $collector ) {
|
||||
$collector->tear_down();
|
||||
$collector->discard_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
wp-content/plugins/query-monitor/classes/Component.php
Normal file
23
wp-content/plugins/query-monitor/classes/Component.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Class representing a component.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Component {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $context;
|
||||
}
|
64
wp-content/plugins/query-monitor/classes/DB.php
Normal file
64
wp-content/plugins/query-monitor/classes/DB.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database class used by the database dropin.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_DB extends wpdb {
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $time_start;
|
||||
|
||||
/**
|
||||
* Performs a MySQL database query, using current database connection.
|
||||
*
|
||||
* @see wpdb::query()
|
||||
*
|
||||
* @param string $query Database query
|
||||
* @return int|bool Boolean true for CREATE, ALTER, TRUNCATE and DROP queries. Number of rows
|
||||
* affected/selected for all other queries. Boolean false on error.
|
||||
*/
|
||||
public function query( $query ) {
|
||||
if ( $this->show_errors ) {
|
||||
$this->hide_errors();
|
||||
}
|
||||
|
||||
$result = parent::query( $query );
|
||||
$i = $this->num_queries - 1;
|
||||
|
||||
if ( did_action( 'qm/cease' ) ) {
|
||||
// It's not possible to prevent the parent class from logging queries because it reads
|
||||
// the `SAVEQUERIES` constant and I don't want to override more methods than necessary.
|
||||
$this->queries = array();
|
||||
}
|
||||
|
||||
if ( ! isset( $this->queries[ $i ] ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->queries[ $i ]['trace'] = new QM_Backtrace();
|
||||
|
||||
if ( ! isset( $this->queries[ $i ][3] ) ) {
|
||||
$this->queries[ $i ][3] = $this->time_start;
|
||||
}
|
||||
|
||||
if ( $this->last_error ) {
|
||||
$code = 'qmdb';
|
||||
|
||||
// This needs to remain in place to account for a user still on PHP 5. Don't want to kill their site.
|
||||
if ( $this->dbh instanceof mysqli ) {
|
||||
$code = mysqli_errno( $this->dbh );
|
||||
}
|
||||
|
||||
$this->queries[ $i ]['result'] = new WP_Error( $code, $this->last_error );
|
||||
} else {
|
||||
$this->queries[ $i ]['result'] = $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
67
wp-content/plugins/query-monitor/classes/Data.php
Normal file
67
wp-content/plugins/query-monitor/classes/Data.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data transfer object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @implements ArrayAccess<string,mixed>
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
abstract class QM_Data implements \ArrayAccess {
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $types = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* component: string,
|
||||
* ltime: float,
|
||||
* types: array<array-key, int>,
|
||||
* }>
|
||||
*/
|
||||
public $component_times = array();
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetSet( $offset, $value ) {
|
||||
if ( is_string( $offset ) ) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetExists( $offset ) {
|
||||
return is_string( $offset ) && isset( $this->$offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetUnset( $offset ) {
|
||||
// @TODO might be able to no-op this
|
||||
if ( is_string( $offset ) ) {
|
||||
unset( $this->$offset );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetGet( $offset ) {
|
||||
return ( is_string( $offset ) && isset( $this->$offset ) ) ? $this->$offset : null;
|
||||
}
|
||||
}
|
25
wp-content/plugins/query-monitor/classes/DataCollector.php
Normal file
25
wp-content/plugins/query-monitor/classes/DataCollector.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data collector for structured data.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @phpstan-template T of QM_Data
|
||||
*/
|
||||
abstract class QM_DataCollector extends QM_Collector {
|
||||
/**
|
||||
* @var QM_Data
|
||||
* @phpstan-var T
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
* @phpstan-return T
|
||||
*/
|
||||
final public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
239
wp-content/plugins/query-monitor/classes/Dispatcher.php
Normal file
239
wp-content/plugins/query-monitor/classes/Dispatcher.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Dispatcher' ) ) {
|
||||
abstract class QM_Dispatcher {
|
||||
|
||||
/**
|
||||
* Outputter instances.
|
||||
*
|
||||
* @var array<string, QM_Output> Array of outputters.
|
||||
*/
|
||||
protected $outputters = array();
|
||||
|
||||
/**
|
||||
* Query Monitor plugin instance.
|
||||
*
|
||||
* @var QM_Plugin Plugin instance.
|
||||
*/
|
||||
protected $qm;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $ceased = false;
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
$this->qm = $qm;
|
||||
|
||||
if ( ! defined( 'QM_COOKIE' ) ) {
|
||||
define( 'QM_COOKIE', 'wp-query_monitor_' . COOKIEHASH );
|
||||
}
|
||||
if ( ! defined( 'QM_EDITOR_COOKIE' ) ) {
|
||||
define( 'QM_EDITOR_COOKIE', 'wp-query_monitor_editor_' . COOKIEHASH );
|
||||
}
|
||||
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function is_active();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
final public function should_dispatch() {
|
||||
|
||||
$e = error_get_last();
|
||||
|
||||
# Don't dispatch if a fatal has occurred:
|
||||
if ( ! empty( $e ) && ( $e['type'] & QM_ERROR_FATALS ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users to disable this dispatcher.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$this->id`, refers to the dispatcher ID.
|
||||
*
|
||||
* Possible filter names include:
|
||||
*
|
||||
* - `qm/dispatch/html`
|
||||
* - `qm/dispatch/ajax`
|
||||
* - `qm/dispatch/redirect`
|
||||
* - `qm/dispatch/rest`
|
||||
* - `qm/dispatch/wp_die`
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $true Whether or not the dispatcher is enabled.
|
||||
*/
|
||||
if ( ! apply_filters( "qm/dispatch/{$this->id}", true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->is_active();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function cease() {
|
||||
$this->ceased = true;
|
||||
|
||||
add_filter( "qm/dispatch/{$this->id}", '__return_false' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes and fetches the outputters for this dispatcher.
|
||||
*
|
||||
* @param string $outputter_id The outputter ID.
|
||||
* @return array<string, QM_Output> Array of outputters.
|
||||
*/
|
||||
public function get_outputters( $outputter_id ) {
|
||||
$collectors = QM_Collectors::init();
|
||||
$collectors->process();
|
||||
|
||||
/**
|
||||
* Allows users to filter what outputs.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$outputter_id`, refers to the outputter ID.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array<string, QM_Output> $outputters Array of outputters.
|
||||
* @param QM_Collectors $collectors List of collectors.
|
||||
*/
|
||||
$this->outputters = apply_filters( "qm/outputter/{$outputter_id}", array(), $collectors );
|
||||
|
||||
return $this->outputters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( ! self::user_can_view() ) {
|
||||
do_action( 'qm/cease' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
|
||||
define( 'DONOTCACHEPAGE', 1 );
|
||||
}
|
||||
|
||||
add_action( 'send_headers', 'nocache_headers' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_output() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function user_can_view() {
|
||||
|
||||
if ( ! did_action( 'plugins_loaded' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( current_user_can( 'view_query_monitor' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return self::user_verified();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function user_verified() {
|
||||
if ( isset( $_COOKIE[QM_COOKIE] ) ) { // phpcs:ignore
|
||||
return self::verify_cookie( wp_unslash( $_COOKIE[QM_COOKIE] ) ); // phpcs:ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function editor_cookie() {
|
||||
if ( defined( 'QM_EDITOR_COOKIE' ) && isset( $_COOKIE[QM_EDITOR_COOKIE] ) ) { // phpcs:ignore
|
||||
return $_COOKIE[QM_EDITOR_COOKIE]; // phpcs:ignore
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify_cookie( $value ) {
|
||||
$old_user_id = wp_validate_auth_cookie( $value, 'logged_in' );
|
||||
if ( $old_user_id ) {
|
||||
return user_can( $old_user_id, 'view_query_monitor' );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to switch to the given locale.
|
||||
*
|
||||
* This is a wrapper around `switch_to_locale()` which is safe to call at any point, even
|
||||
* before the `$wp_locale_switcher` global is initialised or if the function does not exist.
|
||||
*
|
||||
* @param string $locale The locale.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public static function switch_to_locale( $locale ) {
|
||||
global $wp_locale_switcher;
|
||||
|
||||
if ( function_exists( 'switch_to_locale' ) && ( $wp_locale_switcher instanceof WP_Locale_Switcher ) ) {
|
||||
return switch_to_locale( $locale );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to restore the previous locale.
|
||||
*
|
||||
* This is a wrapper around `restore_previous_locale()` which is safe to call at any point, even
|
||||
* before the `$wp_locale_switcher` global is initialised or if the function does not exist.
|
||||
*
|
||||
* @return string|false Locale on success, false on error.
|
||||
*/
|
||||
public static function restore_previous_locale() {
|
||||
global $wp_locale_switcher;
|
||||
|
||||
if ( function_exists( 'restore_previous_locale' ) && ( $wp_locale_switcher instanceof WP_Locale_Switcher ) ) {
|
||||
return restore_previous_locale();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
71
wp-content/plugins/query-monitor/classes/Dispatchers.php
Normal file
71
wp-content/plugins/query-monitor/classes/Dispatchers.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Container for dispatchers.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @implements \IteratorAggregate<string, QM_Dispatcher>
|
||||
*/
|
||||
class QM_Dispatchers implements IteratorAggregate {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Dispatcher>
|
||||
*/
|
||||
private $items = array();
|
||||
|
||||
/**
|
||||
* @return ArrayIterator<string, QM_Dispatcher>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return new ArrayIterator( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Dispatcher $dispatcher
|
||||
* @return void
|
||||
*/
|
||||
public static function add( QM_Dispatcher $dispatcher ) {
|
||||
$dispatchers = self::init();
|
||||
$dispatchers->items[ $dispatcher->id ] = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return QM_Dispatcher|false
|
||||
*/
|
||||
public static function get( $id ) {
|
||||
$dispatchers = self::init();
|
||||
|
||||
return $dispatchers->items[ $id ] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function cease() {
|
||||
$dispatchers = self::init();
|
||||
|
||||
/** @var QM_Dispatcher $dispatcher */
|
||||
foreach ( $dispatchers as $dispatcher ) {
|
||||
$dispatcher->cease();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function init() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Dispatchers();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
77
wp-content/plugins/query-monitor/classes/Hook.php
Normal file
77
wp-content/plugins/query-monitor/classes/Hook.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Hook processor.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Hook {
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param array<string, WP_Hook> $wp_filter
|
||||
* @param bool $hide_qm
|
||||
* @param bool $hide_core
|
||||
* @return array<int, array<string, mixed>>
|
||||
* @phpstan-param 'action'|'filter' $type
|
||||
* @phpstan-return array{
|
||||
* name: string,
|
||||
* type: 'action'|'filter',
|
||||
* actions: list<array{
|
||||
* priority: int,
|
||||
* callback: array<string, mixed>,
|
||||
* }>,
|
||||
* parts: list<string>,
|
||||
* components: array<string, string>,
|
||||
* }
|
||||
*/
|
||||
public static function process( $name, string $type, array $wp_filter, $hide_qm = false, $hide_core = false ) {
|
||||
|
||||
$actions = array();
|
||||
$components = array();
|
||||
|
||||
if ( isset( $wp_filter[ $name ] ) ) {
|
||||
|
||||
# http://core.trac.wordpress.org/ticket/17817
|
||||
$action = $wp_filter[ $name ];
|
||||
|
||||
foreach ( $action as $priority => $callbacks ) {
|
||||
|
||||
foreach ( $callbacks as $cb ) {
|
||||
|
||||
$callback = QM_Util::populate_callback( $cb );
|
||||
|
||||
if ( isset( $callback['component'] ) ) {
|
||||
if (
|
||||
( $hide_qm && 'query-monitor' === $callback['component']->context )
|
||||
|| ( $hide_core && 'core' === $callback['component']->context )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$components[ $callback['component']->name ] = $callback['component']->name;
|
||||
}
|
||||
|
||||
$actions[] = array(
|
||||
'priority' => $priority,
|
||||
'callback' => $callback,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$parts = array_values( array_filter( (array) preg_split( '#[_/.-]#', $name ) ) );
|
||||
|
||||
return array(
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'actions' => $actions,
|
||||
'parts' => $parts,
|
||||
'components' => $components,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
64
wp-content/plugins/query-monitor/classes/Output.php
Normal file
64
wp-content/plugins/query-monitor/classes/Output.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract output handler.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Output' ) ) {
|
||||
abstract class QM_Output {
|
||||
|
||||
/**
|
||||
* Collector instance.
|
||||
*
|
||||
* @var QM_Collector Collector.
|
||||
*/
|
||||
protected $collector;
|
||||
|
||||
/**
|
||||
* Timer instance.
|
||||
*
|
||||
* @var QM_Timer|null Timer.
|
||||
*/
|
||||
protected $timer;
|
||||
|
||||
public function __construct( QM_Collector $collector ) {
|
||||
$this->collector = $collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get_output();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function output() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Collector
|
||||
*/
|
||||
public function get_collector() {
|
||||
return $this->collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Timer|null
|
||||
*/
|
||||
final public function get_timer() {
|
||||
return $this->timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Timer $timer
|
||||
* @return void
|
||||
*/
|
||||
final public function set_timer( QM_Timer $timer ) {
|
||||
$this->timer = $timer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
65
wp-content/plugins/query-monitor/classes/PHP.php
Normal file
65
wp-content/plugins/query-monitor/classes/PHP.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP version compatibility functionality.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_PHP' ) ) {
|
||||
class QM_PHP {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $minimum_version = '7.4.0';
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function version_met() {
|
||||
return version_compare( PHP_VERSION, self::$minimum_version, '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function php_version_nope() {
|
||||
printf(
|
||||
'<div id="qm-php-nope" class="notice notice-error is-dismissible"><p>%s</p></div>',
|
||||
wp_kses(
|
||||
sprintf(
|
||||
/* translators: 1: Required PHP version number, 2: Current PHP version number, 3: URL of PHP update help page */
|
||||
__( 'The Query Monitor plugin requires PHP version %1$s or higher. This site is running PHP version %2$s. <a href="%3$s">Learn about updating PHP</a>.', 'query-monitor' ),
|
||||
self::$minimum_version,
|
||||
PHP_VERSION,
|
||||
'https://wordpress.org/support/update-php/'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function vendor_nope() {
|
||||
printf(
|
||||
'<div id="qm-built-nope" class="notice notice-error"><p>%s</p></div>',
|
||||
sprintf(
|
||||
/* translators: 1: CLI command to run, 2: plugin directory name */
|
||||
esc_html__( 'Dependencies for Query Monitor need to be installed. Run %1$s from the %2$s directory.', 'query-monitor' ),
|
||||
'<code>composer install --no-dev</code>',
|
||||
sprintf(
|
||||
'<code>%s</code>',
|
||||
esc_html( dirname( dirname( __FILE__ ) ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
109
wp-content/plugins/query-monitor/classes/Plugin.php
Normal file
109
wp-content/plugins/query-monitor/classes/Plugin.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract plugin wrapper.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Plugin' ) ) {
|
||||
abstract class QM_Plugin {
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $plugin = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $file = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for for a file/dir within this plugin.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string URL
|
||||
*/
|
||||
final public function plugin_url( $file = '' ) {
|
||||
return $this->_plugin( 'url', $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for a file/dir within this plugin.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string Filesystem path
|
||||
*/
|
||||
final public function plugin_path( $file = '' ) {
|
||||
return $this->_plugin( 'path', $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version number for the given plugin file.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string Version
|
||||
*/
|
||||
final public function plugin_ver( $file ) {
|
||||
return QM_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current plugin's basename, eg. 'my_plugin/my_plugin.php'.
|
||||
*
|
||||
* @return string Basename
|
||||
*/
|
||||
final public function plugin_base() {
|
||||
return $this->_plugin( 'base' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates and returns the current plugin info.
|
||||
*
|
||||
* @param string $item
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
private function _plugin( $item, $file = '' ) {
|
||||
if ( ! array_key_exists( $item, $this->plugin ) ) {
|
||||
switch ( $item ) {
|
||||
case 'url':
|
||||
$this->plugin[ $item ] = plugin_dir_url( $this->file );
|
||||
break;
|
||||
case 'path':
|
||||
$this->plugin[ $item ] = plugin_dir_path( $this->file );
|
||||
break;
|
||||
case 'base':
|
||||
$this->plugin[ $item ] = plugin_basename( $this->file );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->plugin[ $item ] . ltrim( $file, '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Icon name.
|
||||
* @return string Icon HTML.
|
||||
*/
|
||||
public static function icon( $name ) {
|
||||
if ( 'blank' === $name ) {
|
||||
return '<span class="qm-icon qm-icon-blank"></span>';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<svg class="qm-icon qm-icon-%1$s" aria-hidden="true" width="20" height="20" viewBox="0 0 20 20"><use href="#qm-icon-%1$s" /></svg>',
|
||||
esc_attr( $name )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
158
wp-content/plugins/query-monitor/classes/QM.php
Normal file
158
wp-content/plugins/query-monitor/classes/QM.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* A convenience class for wrapping certain user-facing functionality.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM {
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function emergency( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `emergency` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/emergency', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function alert( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `alert` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/alert', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function critical( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `critical` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/critical', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function error( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `error` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/error', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function warning( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `warning` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/warning', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function notice( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `notice` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/notice', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function info( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `info` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/info', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function debug( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `debug` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/debug', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param QM_Collector_Logger::* $level
|
||||
* @return void
|
||||
*/
|
||||
public static function log( $level, $message, array $context = array() ) {
|
||||
/** @var QM_Collector_Logger */
|
||||
$logger = QM_Collectors::get( 'logger' );
|
||||
$logger->log( $level, $message, $context );
|
||||
}
|
||||
}
|
281
wp-content/plugins/query-monitor/classes/QueryMonitor.php
Normal file
281
wp-content/plugins/query-monitor/classes/QueryMonitor.php
Normal file
@ -0,0 +1,281 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* The main Query Monitor plugin class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QueryMonitor extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
|
||||
# Actions
|
||||
add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
|
||||
add_action( 'init', array( $this, 'action_init' ) );
|
||||
add_action( 'members_register_caps', array( $this, 'action_register_members_caps' ) );
|
||||
add_action( 'members_register_cap_groups', array( $this, 'action_register_members_groups' ) );
|
||||
add_action( 'qm/cease', array( $this, 'action_cease' ) );
|
||||
|
||||
# Filters
|
||||
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
|
||||
add_filter( 'ure_built_in_wp_caps', array( $this, 'filter_ure_caps' ) );
|
||||
add_filter( 'ure_capabilities_groups_tree', array( $this, 'filter_ure_groups' ) );
|
||||
add_filter( 'network_admin_plugin_action_links_query-monitor/query-monitor.php', array( $this, 'filter_plugin_action_links' ) );
|
||||
add_filter( 'plugin_action_links_query-monitor/query-monitor.php', array( $this, 'filter_plugin_action_links' ) );
|
||||
add_filter( 'plugin_row_meta', array( $this, 'filter_plugin_row_meta' ), 10, 2 );
|
||||
|
||||
# Load and register built-in collectors:
|
||||
$collectors = array();
|
||||
$files = glob( $this->plugin_path( 'collectors/*.php' ) );
|
||||
if ( $files ) {
|
||||
foreach ( $files as $file ) {
|
||||
$key = basename( $file, '.php' );
|
||||
$collectors[ $key ] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow filtering of built-in collector files.
|
||||
*
|
||||
* @since 2.14.0
|
||||
*
|
||||
* @param array<string, string> $collectors Array of file paths to be loaded, keyed by the base
|
||||
* name of the file.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/built-in-collectors', $collectors ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $actions
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function filter_plugin_action_links( array $actions ) {
|
||||
return array_merge( array(
|
||||
'settings' => '<a href="#qm-settings">' . esc_html__( 'Settings', 'query-monitor' ) . '</a>',
|
||||
'add-ons' => '<a href="https://github.com/johnbillion/query-monitor/wiki/Query-Monitor-Add-on-Plugins">' . esc_html__( 'Add-ons', 'query-monitor' ) . '</a>',
|
||||
), $actions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the array of row meta for each plugin in the Plugins list table.
|
||||
*
|
||||
* @param array<int, string> $plugin_meta An array of the plugin's metadata.
|
||||
* @param string $plugin_file Path to the plugin file relative to the plugins directory.
|
||||
* @return array<int, string> Updated array of the plugin's metadata.
|
||||
*/
|
||||
public function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
|
||||
if ( 'query-monitor/query-monitor.php' !== $plugin_file ) {
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
$plugin_meta[] = sprintf(
|
||||
'<a href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>',
|
||||
'https://github.com/sponsors/johnbillion',
|
||||
esc_html_x( 'Sponsor', 'verb', 'query-monitor' )
|
||||
);
|
||||
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a user's capabilities so they can be altered at runtime.
|
||||
*
|
||||
* This is used to:
|
||||
* - Grant the 'view_query_monitor' capability to the user if they have the ability to manage options.
|
||||
*
|
||||
* This does not get called for Super Admins.
|
||||
*
|
||||
* @param array<string, bool> $user_caps Array of key/value pairs where keys represent a capability name and boolean values
|
||||
* represent whether the user has that capability.
|
||||
* @param array<int, string> $required_caps Required primitive capabilities for the requested capability.
|
||||
* @param mixed[] $args {
|
||||
* Arguments that accompany the requested capability check.
|
||||
*
|
||||
* @type string $0 Requested capability.
|
||||
* @type int $1 Concerned user ID.
|
||||
* @type mixed ...$2 Optional second and further parameters.
|
||||
* }
|
||||
* @phpstan-param array{
|
||||
* 0: string,
|
||||
* 1: int,
|
||||
* } $args
|
||||
* @param WP_User $user Concerned user object.
|
||||
* @return array<string, bool> Concerned user's capabilities.
|
||||
*/
|
||||
public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
|
||||
if ( 'view_query_monitor' !== $args[0] ) {
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'view_query_monitor', $user_caps ) ) {
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
if ( ! is_multisite() && user_can( $args[1], 'manage_options' ) ) {
|
||||
$user_caps['view_query_monitor'] = true;
|
||||
}
|
||||
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_plugins_loaded() {
|
||||
// Hide QM itself from output by default:
|
||||
if ( ! defined( 'QM_HIDE_SELF' ) ) {
|
||||
define( 'QM_HIDE_SELF', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the collectors that are being added.
|
||||
*
|
||||
* @since 2.11.2
|
||||
*
|
||||
* @param array<int, QM_Collector> $collectors Array of collector instances.
|
||||
* @param QueryMonitor $instance QueryMonitor instance.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/collectors', array(), $this ) as $collector ) {
|
||||
QM_Collectors::add( $collector );
|
||||
}
|
||||
|
||||
# Load dispatchers:
|
||||
foreach ( (array) glob( $this->plugin_path( 'dispatchers/*.php' ) ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the dispatchers that are being added.
|
||||
*
|
||||
* @since 2.11.2
|
||||
*
|
||||
* @param array<int, QM_Dispatcher> $dispatchers Array of dispatcher instances.
|
||||
* @param QueryMonitor $instance QueryMonitor instance.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/dispatchers', array(), $this ) as $dispatcher ) {
|
||||
QM_Dispatchers::add( $dispatcher );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_init() {
|
||||
load_plugin_textdomain( 'query-monitor', false, dirname( $this->plugin_base() ) . '/languages' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function symlink_warning() {
|
||||
$db = WP_CONTENT_DIR . '/db.php';
|
||||
trigger_error( sprintf(
|
||||
/* translators: %s: Symlink file location */
|
||||
esc_html__( 'The symlink at %s is no longer pointing to the correct location. Please remove the symlink, then deactivate and reactivate Query Monitor.', 'query-monitor' ),
|
||||
'<code>' . esc_html( $db ) . '</code>'
|
||||
), E_USER_WARNING );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Query Monitor user capability group for the Members plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/members/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function action_register_members_groups() {
|
||||
members_register_cap_group( 'query_monitor', array(
|
||||
'label' => __( 'Query Monitor', 'query-monitor' ),
|
||||
'caps' => array(
|
||||
'view_query_monitor',
|
||||
),
|
||||
'icon' => 'dashicons-admin-tools',
|
||||
'priority' => 30,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the View Query Monitor user capability for the Members plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/members/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function action_register_members_caps() {
|
||||
members_register_cap( 'view_query_monitor', array(
|
||||
'label' => _x( 'View Query Monitor', 'Human readable label for the user capability required to view Query Monitor.', 'query-monitor' ),
|
||||
'group' => 'query_monitor',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Query Monitor user capability group for the User Role Editor plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/user-role-editor/
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $groups Array of existing groups.
|
||||
* @return array<string, array<string, mixed>> Updated array of groups.
|
||||
*/
|
||||
public function filter_ure_groups( array $groups ) {
|
||||
$groups['query_monitor'] = array(
|
||||
'caption' => esc_html__( 'Query Monitor', 'query-monitor' ),
|
||||
'parent' => 'custom',
|
||||
'level' => 2,
|
||||
);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the View Query Monitor user capability for the User Role Editor plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/user-role-editor/
|
||||
*
|
||||
* @param array<string, array<int, string>> $caps Array of existing capabilities.
|
||||
* @return array<string, array<int, string>> Updated array of capabilities.
|
||||
*/
|
||||
public function filter_ure_caps( array $caps ) {
|
||||
$caps['view_query_monitor'] = array(
|
||||
'custom',
|
||||
'query_monitor',
|
||||
);
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_cease() {
|
||||
// iterate collectors, call tear_down
|
||||
// discard all collected data
|
||||
QM_Collectors::cease();
|
||||
|
||||
// remove dispatchers or prevent them from doing anything
|
||||
QM_Dispatchers::cease();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file = null ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QueryMonitor( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
183
wp-content/plugins/query-monitor/classes/Timer.php
Normal file
183
wp-content/plugins/query-monitor/classes/Timer.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Timer that collects timing and memory usage.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Timer {
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>|null
|
||||
* @phpstan-var array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }|null
|
||||
*/
|
||||
protected $end = null;
|
||||
|
||||
/**
|
||||
* @var QM_Backtrace
|
||||
*/
|
||||
protected $trace;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }>
|
||||
*/
|
||||
protected $laps = array();
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function start( array $data = null ) {
|
||||
$this->trace = new QM_Backtrace();
|
||||
$this->start = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function stop( array $data = null ) {
|
||||
|
||||
$this->end = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @param string $name
|
||||
* @return self
|
||||
*/
|
||||
public function lap( array $data = null, $name = null ) {
|
||||
|
||||
$lap = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
if ( ! isset( $name ) ) {
|
||||
$i = sprintf(
|
||||
/* translators: %s: Timing lap number */
|
||||
__( 'Lap %s', 'query-monitor' ),
|
||||
number_format_i18n( count( $this->laps ) + 1 )
|
||||
);
|
||||
} else {
|
||||
$i = $name;
|
||||
}
|
||||
|
||||
$this->laps[ $i ] = $lap;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_laps() {
|
||||
|
||||
$laps = array();
|
||||
$prev = $this->start;
|
||||
|
||||
foreach ( $this->laps as $lap_id => $lap ) {
|
||||
|
||||
$lap['time_used'] = $lap['time'] - $prev['time'];
|
||||
$lap['memory_used'] = $lap['memory'] - $prev['memory'];
|
||||
|
||||
$laps[ $lap_id ] = $lap;
|
||||
$prev = $lap;
|
||||
|
||||
}
|
||||
|
||||
return $laps;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_time() {
|
||||
return $this->end['time'] - $this->start['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_memory() {
|
||||
return $this->end['memory'] - $this->start['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_start_time() {
|
||||
return $this->start['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_start_memory() {
|
||||
return $this->start['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_end_time() {
|
||||
return $this->end['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_end_memory() {
|
||||
return $this->end['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Backtrace
|
||||
*/
|
||||
public function get_trace() {
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function end( array $data = null ) {
|
||||
return $this->stop( $data );
|
||||
}
|
||||
|
||||
}
|
672
wp-content/plugins/query-monitor/classes/Util.php
Normal file
672
wp-content/plugins/query-monitor/classes/Util.php
Normal file
@ -0,0 +1,672 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* General utilities class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Util' ) ) {
|
||||
class QM_Util {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Component>
|
||||
*/
|
||||
protected static $file_components = array();
|
||||
|
||||
/**
|
||||
* @var array<string, string|null>
|
||||
*/
|
||||
protected static $file_dirs = array();
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $abspath = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $contentpath = null;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* @param string $size
|
||||
* @return float
|
||||
*/
|
||||
public static function convert_hr_to_bytes( $size ) {
|
||||
|
||||
# Annoyingly, wp_convert_hr_to_bytes() is defined in a file that's only
|
||||
# loaded in the admin area, so we'll use our own version.
|
||||
# See also http://core.trac.wordpress.org/ticket/17725
|
||||
|
||||
$bytes = (float) $size;
|
||||
|
||||
if ( $bytes ) {
|
||||
$last = strtolower( substr( $size, -1 ) );
|
||||
$pos = strpos( ' kmg', $last, 1 );
|
||||
if ( $pos ) {
|
||||
$bytes *= pow( 1024, $pos );
|
||||
}
|
||||
$bytes = round( $bytes );
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @param string $path_replace
|
||||
* @return string
|
||||
*/
|
||||
public static function standard_dir( $dir, $path_replace = null ) {
|
||||
|
||||
$dir = self::normalize_path( $dir );
|
||||
|
||||
if ( is_string( $path_replace ) ) {
|
||||
if ( ! self::$abspath ) {
|
||||
self::$abspath = self::normalize_path( ABSPATH );
|
||||
self::$contentpath = self::normalize_path( dirname( WP_CONTENT_DIR ) . '/' );
|
||||
}
|
||||
$dir = str_replace( array(
|
||||
self::$abspath,
|
||||
self::$contentpath,
|
||||
), $path_replace, $dir );
|
||||
}
|
||||
|
||||
return $dir;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function normalize_path( $path ) {
|
||||
if ( function_exists( 'wp_normalize_path' ) ) {
|
||||
$path = wp_normalize_path( $path );
|
||||
} else {
|
||||
$path = str_replace( '\\', '/', $path );
|
||||
$path = str_replace( '//', '/', $path );
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
public static function get_file_dirs() {
|
||||
if ( empty( self::$file_dirs ) ) {
|
||||
|
||||
/**
|
||||
* Filters the absolute directory paths that correlate to components.
|
||||
*
|
||||
* Note that this filter is applied before QM adds its built-in list of components. This is
|
||||
* so custom registered components take precedence during component detection.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_context/{$type}`
|
||||
* - `qm/component_name/{$type}`
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param array<string, string|null> $dirs Array of absolute directory paths keyed by component identifier.
|
||||
*/
|
||||
self::$file_dirs = apply_filters( 'qm/component_dirs', self::$file_dirs );
|
||||
|
||||
self::$file_dirs['plugin'] = WP_PLUGIN_DIR;
|
||||
self::$file_dirs['mu-vendor'] = WPMU_PLUGIN_DIR . '/vendor';
|
||||
self::$file_dirs['go-plugin'] = WPMU_PLUGIN_DIR . '/shared-plugins';
|
||||
self::$file_dirs['mu-plugin'] = WPMU_PLUGIN_DIR;
|
||||
self::$file_dirs['vip-plugin'] = get_theme_root() . '/vip/plugins';
|
||||
|
||||
if ( defined( 'WPCOM_VIP_CLIENT_MU_PLUGIN_DIR' ) ) {
|
||||
self::$file_dirs['vip-client-mu-plugin'] = WPCOM_VIP_CLIENT_MU_PLUGIN_DIR;
|
||||
}
|
||||
|
||||
if ( defined( '\Altis\ROOT_DIR' ) ) {
|
||||
self::$file_dirs['altis-vendor'] = \Altis\ROOT_DIR . '/vendor';
|
||||
}
|
||||
|
||||
self::$file_dirs['theme'] = null;
|
||||
self::$file_dirs['stylesheet'] = get_stylesheet_directory();
|
||||
self::$file_dirs['template'] = get_template_directory();
|
||||
self::$file_dirs['other'] = WP_CONTENT_DIR;
|
||||
self::$file_dirs['core'] = ABSPATH;
|
||||
self::$file_dirs['unknown'] = null;
|
||||
|
||||
foreach ( self::$file_dirs as $type => $dir ) {
|
||||
if ( null === $dir ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::$file_dirs[ $type ] = self::standard_dir( $dir );
|
||||
}
|
||||
}
|
||||
|
||||
return self::$file_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine the component responsible for a given file name.
|
||||
*
|
||||
* @param string $file An absolute file path.
|
||||
* @return QM_Component An object representing the component.
|
||||
*/
|
||||
public static function get_file_component( $file ) {
|
||||
$file = self::standard_dir( $file );
|
||||
$type = '';
|
||||
|
||||
if ( isset( self::$file_components[ $file ] ) ) {
|
||||
return self::$file_components[ $file ];
|
||||
}
|
||||
|
||||
foreach ( self::get_file_dirs() as $type => $dir ) {
|
||||
// this slash makes paths such as plugins-mu match mu-plugin not plugin
|
||||
if ( $dir && ( 0 === strpos( $file, trailingslashit( $dir ) ) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$context = $type;
|
||||
|
||||
switch ( $type ) {
|
||||
case 'altis-vendor':
|
||||
$plug = str_replace( \Altis\ROOT_DIR . '/vendor/', '', $file );
|
||||
$plug = explode( '/', $plug, 3 );
|
||||
$plug = $plug[0] . '/' . $plug[1];
|
||||
/* translators: %s: Dependency name */
|
||||
$name = sprintf( __( 'Dependency: %s', 'query-monitor' ), $plug );
|
||||
break;
|
||||
case 'plugin':
|
||||
case 'mu-plugin':
|
||||
case 'mu-vendor':
|
||||
$plug = str_replace( '/vendor/', '/', $file );
|
||||
$plug = plugin_basename( $plug );
|
||||
if ( strpos( $plug, '/' ) ) {
|
||||
$plug = explode( '/', $plug );
|
||||
$plug = reset( $plug );
|
||||
} else {
|
||||
$plug = basename( $plug );
|
||||
}
|
||||
if ( 'plugin' !== $type ) {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'MU Plugin: %s', 'query-monitor' ), $plug );
|
||||
} else {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'Plugin: %s', 'query-monitor' ), $plug );
|
||||
}
|
||||
$context = $plug;
|
||||
break;
|
||||
case 'go-plugin':
|
||||
case 'vip-plugin':
|
||||
case 'vip-client-mu-plugin':
|
||||
$plug = str_replace( self::$file_dirs[ $type ], '', $file );
|
||||
$plug = trim( $plug, '/' );
|
||||
if ( strpos( $plug, '/' ) ) {
|
||||
$plug = explode( '/', $plug );
|
||||
$plug = reset( $plug );
|
||||
} else {
|
||||
$plug = basename( $plug );
|
||||
}
|
||||
if ( 'vip-client-mu-plugin' === $type ) {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'VIP Client MU Plugin: %s', 'query-monitor' ), $plug );
|
||||
} else {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'VIP Plugin: %s', 'query-monitor' ), $plug );
|
||||
}
|
||||
$context = $plug;
|
||||
break;
|
||||
case 'stylesheet':
|
||||
if ( is_child_theme() ) {
|
||||
$name = __( 'Child Theme', 'query-monitor' );
|
||||
} else {
|
||||
$name = __( 'Theme', 'query-monitor' );
|
||||
}
|
||||
$type = 'theme';
|
||||
break;
|
||||
case 'template':
|
||||
$name = __( 'Parent Theme', 'query-monitor' );
|
||||
$type = 'theme';
|
||||
break;
|
||||
case 'other':
|
||||
// Anything else that's within the content directory should appear as
|
||||
// `wp-content/{dir}` or `wp-content/{file}`
|
||||
$name = self::standard_dir( $file );
|
||||
$name = str_replace( dirname( self::$file_dirs['other'] ), '', $name );
|
||||
$parts = explode( '/', trim( $name, '/' ) );
|
||||
$name = $parts[0] . '/' . $parts[1];
|
||||
$context = $file;
|
||||
break;
|
||||
case 'core':
|
||||
$name = __( 'WordPress Core', 'query-monitor' );
|
||||
break;
|
||||
case 'unknown':
|
||||
default:
|
||||
$name = __( 'Unknown', 'query-monitor' );
|
||||
|
||||
/**
|
||||
* Filters the type of a custom or unknown component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_name/{$type}`
|
||||
* - `qm/component_context/{$type}`
|
||||
*
|
||||
* @since 3.8.1
|
||||
*
|
||||
* @param string $type The component type.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
* @param string $name The component name.
|
||||
* @param string $context The context for the component.
|
||||
*/
|
||||
$type = apply_filters( "qm/component_type/{$type}", $type, $file, $name, $context );
|
||||
|
||||
/**
|
||||
* Filters the name of a custom or unknown component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_type/{$type}`
|
||||
* - `qm/component_context/{$type}`
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $name The component name.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
*/
|
||||
$name = apply_filters( "qm/component_name/{$type}", $name, $file );
|
||||
|
||||
/**
|
||||
* Filters the context for a custom or unknown component. The context is usually a
|
||||
* representation of its type more specific to the individual component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_type/{$type}`
|
||||
* - `qm/component_name/{$type}`
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param string $context The context for the component.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
* @param string $name The component name.
|
||||
*/
|
||||
$context = apply_filters( "qm/component_context/{$type}", $context, $file, $name );
|
||||
break;
|
||||
}
|
||||
|
||||
$component = new QM_Component();
|
||||
$component->type = $type;
|
||||
$component->name = $name;
|
||||
$component->context = $context;
|
||||
|
||||
self::$file_components[ $file ] = $component;
|
||||
|
||||
return self::$file_components[ $file ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $callback
|
||||
* @return array<string, mixed>
|
||||
* @phpstan-return array{
|
||||
* name?: string,
|
||||
* file?: string|false,
|
||||
* line?: string|false,
|
||||
* error?: WP_Error,
|
||||
* component?: QM_Component,
|
||||
* }
|
||||
*/
|
||||
public static function populate_callback( array $callback ) {
|
||||
|
||||
if ( is_string( $callback['function'] ) && ( false !== strpos( $callback['function'], '::' ) ) ) {
|
||||
$callback['function'] = explode( '::', $callback['function'] );
|
||||
}
|
||||
|
||||
if ( isset( $callback['class'] ) ) {
|
||||
$callback['function'] = array(
|
||||
$callback['class'],
|
||||
$callback['function'],
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if ( is_array( $callback['function'] ) ) {
|
||||
if ( is_object( $callback['function'][0] ) ) {
|
||||
$class = get_class( $callback['function'][0] );
|
||||
$access = '->';
|
||||
} else {
|
||||
$class = $callback['function'][0];
|
||||
$access = '::';
|
||||
}
|
||||
|
||||
$callback['name'] = self::shorten_fqn( $class . $access . $callback['function'][1] ) . '()';
|
||||
$ref = new ReflectionMethod( $class, $callback['function'][1] );
|
||||
} elseif ( is_object( $callback['function'] ) ) {
|
||||
if ( $callback['function'] instanceof Closure ) {
|
||||
$ref = new ReflectionFunction( $callback['function'] );
|
||||
$filename = $ref->getFileName();
|
||||
|
||||
if ( $filename ) {
|
||||
$file = self::standard_dir( $filename, '' );
|
||||
if ( 0 === strpos( $file, '/' ) ) {
|
||||
$file = basename( $filename );
|
||||
}
|
||||
$callback['name'] = sprintf(
|
||||
/* translators: A closure is an anonymous PHP function. 1: Line number, 2: File name */
|
||||
__( 'Closure on line %1$d of %2$s', 'query-monitor' ),
|
||||
$ref->getStartLine(),
|
||||
$file
|
||||
);
|
||||
} else {
|
||||
/* translators: A closure is an anonymous PHP function */
|
||||
$callback['name'] = __( 'Unknown closure', 'query-monitor' );
|
||||
}
|
||||
} else {
|
||||
// the object should have a __invoke() method
|
||||
$class = get_class( $callback['function'] );
|
||||
$callback['name'] = self::shorten_fqn( $class ) . '->__invoke()';
|
||||
$ref = new ReflectionMethod( $class, '__invoke' );
|
||||
}
|
||||
} else {
|
||||
$callback['name'] = self::shorten_fqn( $callback['function'] ) . '()';
|
||||
$ref = new ReflectionFunction( $callback['function'] );
|
||||
}
|
||||
|
||||
$callback['file'] = $ref->getFileName();
|
||||
$callback['line'] = $ref->getStartLine();
|
||||
|
||||
// https://github.com/facebook/hhvm/issues/5856
|
||||
$name = trim( $ref->getName() );
|
||||
|
||||
if ( '__lambda_func' === $name || 0 === strpos( $name, 'lambda_' ) ) {
|
||||
if ( $callback['file'] && preg_match( '|(?P<file>.*)\((?P<line>[0-9]+)\)|', $callback['file'], $matches ) ) {
|
||||
$callback['file'] = $matches['file'];
|
||||
$callback['line'] = $matches['line'];
|
||||
$file = trim( self::standard_dir( $callback['file'], '' ), '/' );
|
||||
/* translators: 1: Line number, 2: File name */
|
||||
$callback['name'] = sprintf( __( 'Anonymous function on line %1$d of %2$s', 'query-monitor' ), $callback['line'], $file );
|
||||
} else {
|
||||
// https://github.com/facebook/hhvm/issues/5807
|
||||
unset( $callback['line'], $callback['file'] );
|
||||
$callback['name'] = $name . '()';
|
||||
$callback['error'] = new WP_Error( 'unknown_lambda', __( 'Unable to determine source of lambda function', 'query-monitor' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $callback['file'] ) ) {
|
||||
$callback['component'] = self::get_file_component( $callback['file'] );
|
||||
} else {
|
||||
$callback['component'] = new QM_Component();
|
||||
$callback['component']->type = 'php';
|
||||
$callback['component']->name = 'PHP';
|
||||
$callback['component']->context = '';
|
||||
}
|
||||
} catch ( ReflectionException $e ) {
|
||||
|
||||
$callback['error'] = new WP_Error( 'reflection_exception', $e->getMessage() );
|
||||
|
||||
}
|
||||
|
||||
unset( $callback['function'], $callback['class'] );
|
||||
|
||||
return $callback;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ajax() {
|
||||
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_async() {
|
||||
if ( self::is_ajax() ) {
|
||||
return true;
|
||||
}
|
||||
if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && 'xmlhttprequest' === strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) ) { // phpcs:ignore
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WP_Role|false
|
||||
*/
|
||||
public static function get_admins() {
|
||||
if ( is_multisite() ) {
|
||||
return false;
|
||||
} else {
|
||||
return get_role( 'administrator' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_multi_network() {
|
||||
return ( function_exists( 'is_multi_network' ) && is_multi_network() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $client
|
||||
* @return array<string, int>
|
||||
* @phpstan-return array{
|
||||
* major: int,
|
||||
* minor: int,
|
||||
* patch: int,
|
||||
* }
|
||||
*/
|
||||
public static function get_client_version( $client ) {
|
||||
|
||||
$client = intval( $client );
|
||||
|
||||
$hello = $client % 10000;
|
||||
|
||||
$major = intval( floor( $client / 10000 ) );
|
||||
$minor = intval( floor( $hello / 100 ) );
|
||||
$patch = intval( $hello % 100 );
|
||||
|
||||
return compact( 'major', 'minor', 'patch' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @return string
|
||||
*/
|
||||
public static function get_query_type( $sql ) {
|
||||
// Trim leading whitespace and brackets
|
||||
$sql = ltrim( $sql, ' \t\n\r\0\x0B(' );
|
||||
|
||||
if ( 0 === strpos( $sql, '/*' ) ) {
|
||||
// Strip out leading comments such as `/*NO_SELECT_FOUND_ROWS*/` before calculating the query type
|
||||
$sql = preg_replace( '|^/\*[^\*/]+\*/|', '', $sql );
|
||||
}
|
||||
|
||||
$words = preg_split( '/\b/', trim( $sql ), 2, PREG_SPLIT_NO_EMPTY );
|
||||
$type = 'Unknown';
|
||||
|
||||
if ( is_array( $words ) && isset( $words[0] ) ) {
|
||||
$type = strtoupper( $words[0] );
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return string|float|int
|
||||
*/
|
||||
public static function display_variable( $value ) {
|
||||
if ( is_string( $value ) ) {
|
||||
return $value;
|
||||
} elseif ( $value === null ) {
|
||||
return 'null';
|
||||
} elseif ( is_bool( $value ) ) {
|
||||
return ( $value ) ? 'true' : 'false';
|
||||
} elseif ( is_scalar( $value ) ) {
|
||||
return $value;
|
||||
} elseif ( is_object( $value ) ) {
|
||||
$class = get_class( $value );
|
||||
|
||||
switch ( true ) {
|
||||
|
||||
case ( $value instanceof WP_Post ):
|
||||
case ( $value instanceof WP_User ):
|
||||
$class = sprintf( '%s (ID: %s)', $class, $value->ID );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Term ):
|
||||
$class = sprintf( '%s (term_id: %s)', $class, $value->term_id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Comment ):
|
||||
$class = sprintf( '%s (comment_ID: %s)', $class, $value->comment_ID );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Error ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->get_error_code() );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Role ):
|
||||
case ( $value instanceof WP_Post_Type ):
|
||||
case ( $value instanceof WP_Taxonomy ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->name );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Network ):
|
||||
$class = sprintf( '%s (id: %s)', $class, $value->id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Site ):
|
||||
$class = sprintf( '%s (blog_id: %s)', $class, $value->blog_id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Theme ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->get_stylesheet() );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return $class;
|
||||
} else {
|
||||
return gettype( $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortens a fully qualified name to reduce the length of the names of long namespaced symbols.
|
||||
*
|
||||
* This initialises portions that do not form the first or last portion of the name. For example:
|
||||
*
|
||||
* Inpsyde\Wonolog\HookListener\HookListenersRegistry->hook_callback()
|
||||
*
|
||||
* becomes:
|
||||
*
|
||||
* Inpsyde\W\H\HookListenersRegistry->hook_callback()
|
||||
*
|
||||
* @param string $fqn A fully qualified name.
|
||||
* @return string A shortened version of the name.
|
||||
*/
|
||||
public static function shorten_fqn( $fqn ) {
|
||||
if ( substr_count( $fqn, '\\' ) < 3 ) {
|
||||
return $fqn;
|
||||
}
|
||||
|
||||
return preg_replace_callback( '#\\\\[a-zA-Z0-9_\\\\]{4,}\\\\#', function( array $matches ) {
|
||||
preg_match_all( '#\\\\([a-zA-Z0-9_])#', $matches[0], $m );
|
||||
return '\\' . implode( '\\', $m[1] ) . '\\';
|
||||
}, $fqn );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for JSON encoding data and formatting it in a consistent manner.
|
||||
*
|
||||
* @param mixed $data The data to be JSON encoded.
|
||||
* @return string The JSON encoded data.
|
||||
*/
|
||||
public static function json_format( $data ) {
|
||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.json_unescaped_slashesFound
|
||||
$json_options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES;
|
||||
|
||||
$json = json_encode( $data, $json_options );
|
||||
|
||||
if ( false === $json ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the site editor URL for a given template or template part name.
|
||||
*
|
||||
* @param string $template The site template name, for example `twentytwentytwo//header-small-dark`.
|
||||
* @param string $type The template type, either 'wp_template_part' or 'wp_template'.
|
||||
* @return string The admin URL for editing the site template.
|
||||
*/
|
||||
public static function get_site_editor_url( string $template, string $type = 'wp_template_part' ): string {
|
||||
return add_query_arg(
|
||||
array(
|
||||
'postType' => $type,
|
||||
'postId' => urlencode( $template ),
|
||||
'canvas' => 'edit',
|
||||
),
|
||||
admin_url( 'site-editor.php' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param mixed $data
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_stringy( $data ) {
|
||||
return ( is_string( $data ) || ( is_object( $data ) && method_exists( $data, '__toString' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
* @param string $field
|
||||
* @return void
|
||||
*/
|
||||
public static function sort( array &$array, $field ) {
|
||||
usort( $array, function( array $a, array $b ) use ( $field ): int {
|
||||
return $a[ $field ] <=> $b[ $field ];
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
* @param string $field
|
||||
* @return void
|
||||
*/
|
||||
public static function rsort( array &$array, $field ) {
|
||||
usort( $array, function( array $a, array $b ) use ( $field ): int {
|
||||
return $b[ $field ] <=> $a[ $field ];
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
78
wp-content/plugins/query-monitor/classes/debug_bar.php
Normal file
78
wp-content/plugins/query-monitor/classes/debug_bar.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Mock 'Debug Bar' plugin class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class Debug_Bar {
|
||||
/**
|
||||
* @var array<int, Debug_Bar_Panel>
|
||||
*/
|
||||
public $panels = array();
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'wp_head', array( $this, 'ensure_ajaxurl' ), 1 );
|
||||
|
||||
$this->enqueue();
|
||||
$this->init_panels();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_register_style( 'debug-bar', false, array(
|
||||
'query-monitor',
|
||||
) );
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_register_script( 'debug-bar', false, array(
|
||||
'query-monitor',
|
||||
) );
|
||||
|
||||
/**
|
||||
* Fires after scripts have been enqueued. This mimics the same action fired in the Debug Bar plugin.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
do_action( 'debug_bar_enqueue_scripts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init_panels() {
|
||||
/**
|
||||
* Filters the debug bar panel list. This mimics the same filter called in the Debug Bar plugin.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<int, Debug_Bar_Panel> $panels Array of Debug Bar panel instances.
|
||||
*/
|
||||
$this->panels = apply_filters( 'debug_bar_panels', array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function ensure_ajaxurl() {
|
||||
$dispatcher = QM_Dispatchers::get( 'html' );
|
||||
|
||||
if ( $this->panels && $dispatcher && $dispatcher::user_can_view() ) {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var ajaxurl = '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>';
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function Debug_Bar() {
|
||||
self::__construct();
|
||||
}
|
||||
|
||||
}
|
95
wp-content/plugins/query-monitor/classes/debug_bar_panel.php
Normal file
95
wp-content/plugins/query-monitor/classes/debug_bar_panel.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Mock 'Debug Bar' panel class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
abstract class Debug_Bar_Panel {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $_title = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $_visible = true;
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct( $title = '' ) {
|
||||
$this->title( $title );
|
||||
|
||||
if ( $this->init() === false ) {
|
||||
$this->set_visible( false );
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'debug_bar_classes', array( $this, 'debug_bar_classes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the panel.
|
||||
*
|
||||
* @return false|void
|
||||
*/
|
||||
public function init() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function prerender() {}
|
||||
|
||||
/**
|
||||
* Renders the panel.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render() {}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return $this->_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $visible
|
||||
* @return void
|
||||
*/
|
||||
public function set_visible( $visible ) {
|
||||
$this->_visible = $visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $title
|
||||
* @return string|void
|
||||
*/
|
||||
public function title( $title = null ) {
|
||||
if ( ! isset( $title ) ) {
|
||||
return $this->_title;
|
||||
}
|
||||
$this->_title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $classes
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function debug_bar_classes( $classes ) {
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
* @return void
|
||||
*/
|
||||
public function Debug_Bar_Panel( $title = '' ) {
|
||||
self::__construct( $title );
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user