did_footer = true;
}
/**
* Helper function. Should the authentication cookie be secure?
*
* @return bool Should the authentication cookie be secure?
*/
public static function secure_cookie() {
return ( is_ssl() && ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) ) );
}
public function ajax_on() {
if ( ! current_user_can( 'view_query_monitor' ) || ! check_ajax_referer( 'qm-auth-on', 'nonce', false ) ) {
wp_send_json_error();
}
$expiration = time() + ( 2 * DAY_IN_SECONDS );
$secure = self::secure_cookie();
$cookie = wp_generate_auth_cookie( get_current_user_id(), $expiration, 'logged_in' );
setcookie( QM_COOKIE, $cookie, $expiration, COOKIEPATH, COOKIE_DOMAIN, $secure, false );
wp_send_json_success();
}
public function ajax_off() {
if ( ! self::user_verified() || ! check_ajax_referer( 'qm-auth-off', 'nonce', false ) ) {
wp_send_json_error();
}
$expiration = time() - 31536000;
setcookie( QM_COOKIE, ' ', $expiration, COOKIEPATH, COOKIE_DOMAIN );
wp_send_json_success();
}
public function ajax_editor_set() {
if ( ! current_user_can( 'view_query_monitor' ) || ! check_ajax_referer( 'qm-editor-set', 'nonce', false ) ) {
wp_send_json_error();
}
$expiration = time() + ( 2 * YEAR_IN_SECONDS );
$secure = self::secure_cookie();
$editor = wp_unslash( $_POST['editor'] );
setcookie( QM_EDITOR_COOKIE, $editor, $expiration, COOKIEPATH, COOKIE_DOMAIN, $secure, false );
wp_send_json_success( $editor );
}
public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
if ( ! self::user_can_view() ) {
return;
}
$title = __( 'Query Monitor', 'query-monitor' );
$wp_admin_bar->add_node( array(
'id' => 'query-monitor',
'title' => esc_html( $title ),
'href' => '#qm-overview',
) );
$wp_admin_bar->add_node( array(
'parent' => 'query-monitor',
'id' => 'query-monitor-placeholder',
'title' => esc_html( $title ),
'href' => '#qm-overview',
) );
}
public function init() {
if ( ! self::user_can_view() ) {
return;
}
if ( ! file_exists( $this->qm->plugin_path( 'assets/query-monitor.css' ) ) ) {
add_action( 'admin_notices', array( $this, 'build_warning' ) );
}
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
add_action( 'login_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_assets' ), -9999 );
add_action( 'gp_head', array( $this, 'manually_print_assets' ), 11 );
parent::init();
}
public function manually_print_assets() {
wp_print_scripts( array(
'query-monitor',
) );
wp_print_styles( array(
'query-monitor',
) );
}
public function build_warning() {
printf(
'
',
sprintf(
/* translators: 1: CLI command to run, 2: plugin directory name */
esc_html__( 'Asset files for Query Monitor need to be built. Run %1$s from the %2$s directory.', 'query-monitor' ),
'npm i && npm run build',
sprintf(
'%s',
esc_html( QM_Util::standard_dir( untrailingslashit( $this->qm->plugin_path() ), '' ) )
)
)
);
}
public function enqueue_assets() {
global $wp_locale, $wp_version;
$deps = array(
'jquery',
);
if ( defined( 'QM_NO_JQUERY' ) && QM_NO_JQUERY ) {
$deps = array();
}
$css = 'query-monitor';
if ( method_exists( 'Dark_Mode', 'is_using_dark_mode' ) && is_user_logged_in() ) {
if ( Dark_Mode::is_using_dark_mode() ) {
$css .= '-dark';
}
} elseif ( defined( 'QM_DARK_MODE' ) && QM_DARK_MODE ) {
$css .= '-dark';
}
wp_enqueue_style(
'query-monitor',
$this->qm->plugin_url( "assets/{$css}.css" ),
array( 'dashicons' ),
$this->qm->plugin_ver( "assets/{$css}.css" )
);
wp_enqueue_script(
'query-monitor',
$this->qm->plugin_url( 'assets/query-monitor.js' ),
$deps,
$this->qm->plugin_ver( 'assets/query-monitor.js' ),
false
);
wp_localize_script(
'query-monitor',
'qm_number_format',
$wp_locale->number_format
);
wp_localize_script(
'query-monitor',
'qm_l10n',
array(
'ajax_error' => __( 'PHP Errors in Ajax Response', 'query-monitor' ),
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'auth_nonce' => array(
'on' => wp_create_nonce( 'qm-auth-on' ),
'off' => wp_create_nonce( 'qm-auth-off' ),
'editor-set' => wp_create_nonce( 'qm-editor-set' ),
),
'fatal_error' => __( 'PHP Fatal Error', 'query-monitor' ),
)
);
/**
* Fires when assets for QM's HTML have been enqueued.
*
* @since 3.6.0
*
* @param \QM_Dispatcher_Html $this The HTML dispatcher.
*/
do_action( 'qm/output/enqueued-assets', $this );
}
public function dispatch() {
if ( ! $this->should_dispatch() ) {
return;
}
$switched_locale = function_exists( 'switch_to_locale' ) && switch_to_locale( get_user_locale() );
$this->before_output();
foreach ( $this->outputters as $id => $output ) {
$timer = new QM_Timer();
$timer->start();
printf(
"\n" . '' . "\n" . '' . "\n",
esc_html( $id )
);
$output->output();
printf(
"\n" . '
' . "\n" . '' . "\n",
esc_html( $id )
);
$output->set_timer( $timer->stop() );
}
$this->after_output();
if ( $switched_locale ) {
restore_previous_locale();
}
}
protected function before_output() {
require_once $this->qm->plugin_path( 'output/Html.php' );
foreach ( glob( $this->qm->plugin_path( 'output/html/*.php' ) ) as $file ) {
require_once $file;
}
$this->outputters = $this->get_outputters( 'html' );
/**
* Filters the menu items shown in Query Monitor's admin toolbar menu.
*
* @since 3.0.0
*
* @param array $menus Array of menus.
*/
$this->admin_bar_menu = apply_filters( 'qm/output/menus', array() );
/**
* Filters the menu items shown in the panel navigation menu in Query Monitor's output.
*
* @since 3.0.0
*
* @param array $admin_bar_menu Array of menus.
*/
$this->panel_menu = apply_filters( 'qm/output/panel_menus', $this->admin_bar_menu );
foreach ( $this->outputters as $output_id => $output ) {
$collector = $output->get_collector();
if ( ( ! empty( $collector->concerned_filters ) || ! empty( $collector->concerned_actions ) ) && isset( $this->panel_menu[ 'qm-' . $output_id ] ) ) {
$this->panel_menu[ 'qm-' . $output_id ]['children'][ 'qm-' . $output_id . '-concerned_hooks' ] = array(
'href' => esc_attr( '#' . $collector->id() . '-concerned_hooks' ),
'title' => __( 'Hooks in Use', 'query-monitor' ),
);
}
}
$class = array(
'qm-no-js',
);
if ( did_action( 'wp_head' ) ) {
$class[] = sprintf( 'qm-theme-%s', get_template() );
$class[] = sprintf( 'qm-theme-%s', get_stylesheet() );
}
if ( ! is_admin_bar_showing() ) {
$class[] = 'qm-peek';
}
$json = array(
'menu' => $this->js_admin_bar_menu(),
'ajax_errors' => array(), # @TODO move this into the php_errors collector
);
echo '' . "\n\n";
echo '' . "\n\n";
echo '';
echo '
';
echo '
';
echo '
' . esc_html__( 'Query Monitor', 'query-monitor' ) . ' ';
echo '
';
echo '';
printf(
'%2$s ',
'#qm-overview',
esc_html__( 'Overview', 'query-monitor' )
);
foreach ( $this->panel_menu as $menu ) {
printf(
'%2$s ',
esc_attr( $menu['href'] ),
esc_html( $menu['title'] )
);
if ( ! empty( $menu['children'] ) ) {
foreach ( $menu['children'] as $child ) {
printf(
'└ %2$s ',
esc_attr( $child['href'] ),
esc_html( $child['title'] )
);
}
}
}
printf(
'%2$s ',
'#qm-settings',
esc_html__( 'Settings', 'query-monitor' )
);
echo ' ';
echo '
';
echo '
';
echo '
';
echo '
';
echo '
'; // #qm-title
echo '
';
echo ''; // #qm-panel-menu
echo '
';
}
protected function do_panel_menu_item( $id, array $menu ) {
printf(
'
%2$s ',
esc_attr( $menu['href'] ),
esc_html( $menu['title'] )
);
if ( ! empty( $menu['children'] ) ) {
echo '';
foreach ( $menu['children'] as $child_id => $child ) {
$this->do_panel_menu_item( $child_id, $child );
}
echo ' ';
}
echo '';
}
protected function after_output() {
$state = self::user_verified() ? 'on' : 'off';
$editor = self::editor_cookie();
$text = array(
'on' => __( 'Clear authentication cookie', 'query-monitor' ),
'off' => __( 'Set authentication cookie', 'query-monitor' ),
);
echo '
';
echo '
' . esc_html__( 'Settings', 'query-monitor' ) . ' ';
echo '
';
echo '
';
echo '' . esc_html__( 'Authentication', 'query-monitor' ) . ' ';
echo '' . esc_html__( 'You can set an authentication cookie which allows you to view Query Monitor output when you’re not logged in, or when you’re logged in as a different user.', 'query-monitor' ) . '
';
echo '' . esc_html( $text[ $state ] ) . '
';
echo ' ' . esc_html__( 'Authentication cookie is set', 'query-monitor' ) . '
';
echo ' ';
echo '
';
echo '
';
echo '
';
echo '' . esc_html__( 'Editor', 'query-monitor' ) . ' ';
echo '' . esc_html__( 'You can set your editor here, so that when you click on stack trace links the file opens in your editor.', 'query-monitor' ) . '
';
echo '';
echo '';
$editors = array(
'Default/Xdebug' => '',
'Atom' => 'atom',
'Netbeans' => 'netbeans',
'PhpStorm' => 'phpstorm',
'Sublime Text' => 'sublime',
'TextMate' => 'textmate',
'Visual Studio Code' => 'vscode',
);
foreach ( $editors as $name => $value ) {
echo '' . esc_html( $name ) . ' ';
}
echo ' ';
echo '
';
echo '' . esc_html__( 'Set editor cookie', 'query-monitor' ) . ' ';
echo '
';
echo ' ' . esc_html__( 'Saved! Reload to apply changes.', 'query-monitor' ) . '
';
echo ' ';
echo '
';
echo '
';
$constants = array(
'QM_DARK_MODE' => array(
'label' => __( 'Enable dark mode for Query Monitor\'s interface.', 'query-monitor' ),
'default' => false,
),
'QM_DB_EXPENSIVE' => array(
'label' => __( 'If an individual database query takes longer than this time to execute, it\'s considered "slow" and triggers a warning.', 'query-monitor' ),
'default' => 0.05,
),
'QM_DISABLED' => array(
'label' => __( 'Disable Query Monitor entirely.', 'query-monitor' ),
'default' => false,
),
'QM_DISABLE_ERROR_HANDLER' => array(
'label' => __( 'Disable the handling of PHP errors.', 'query-monitor' ),
'default' => false,
),
'QM_ENABLE_CAPS_PANEL' => array(
'label' => __( 'Enable the Capability Checks panel.', 'query-monitor' ),
'default' => false,
),
'QM_HIDE_CORE_ACTIONS' => array(
'label' => __( 'Hide WordPress core on the Hooks & Actions panel.', 'query-monitor' ),
'default' => false,
),
'QM_HIDE_SELF' => array(
'label' => __( 'Hide Query Monitor itself from various panels. Set to false if you want to see how Query Monitor hooks into WordPress.', 'query-monitor' ),
'default' => true,
),
'QM_NO_JQUERY' => array(
'label' => __( 'Don\'t specify jQuery as a dependency of Query Monitor. If jQuery isn\'t enqueued then Query Monitor will still operate, but with some reduced functionality.', 'query-monitor' ),
'default' => false,
),
'QM_SHOW_ALL_HOOKS' => array(
'label' => __( 'In the Hooks & Actions panel, show every hook that has an action or filter attached (instead of every action hook that fired during the request).', 'query-monitor' ),
'default' => false,
),
);
echo '
';
echo '' . esc_html__( 'Configuration', 'query-monitor' ) . ' ';
echo '';
printf(
/* translators: %s: Name of the config file */
esc_html__( 'The following PHP constants can be defined in your %s file in order to control the behavior of Query Monitor:', 'query-monitor' ),
'wp-config.php'
);
echo '
';
echo '';
foreach ( $constants as $name => $constant ) {
echo '' . esc_html( $name ) . ' ';
echo '';
printf(
esc_html( $constant['label'] ),
'' . esc_html( $constant['default'] ) . ''
);
$default_value = $constant['default'];
if ( is_bool( $default_value ) ) {
$default_value = ( $default_value ? 'true' : 'false' );
}
echo '';
printf(
/* translators: %s: Default value for a PHP constant */
esc_html__( 'Default value: %s', 'query-monitor' ),
'' . esc_html( $default_value ) . ''
);
echo ' ';
if ( defined( $name ) ) {
$current_value = constant( $name );
if ( is_bool( $current_value ) ) {
$current_value = QM_Collector::format_bool_constant( $name );
}
}
if ( defined( $name ) && ( constant( $name ) !== $constant['default'] ) ) {
echo '';
printf(
/* translators: %s: Current value for a PHP constant */
esc_html__( 'Current value: %s', 'query-monitor' ),
'' . esc_html( $current_value ) . ''
);
echo ' ';
}
echo ' ';
}
echo ' ';
echo ' ';
echo '
';
echo '
'; // #qm-settings
/**
* Fires after settings but before the panel closing tag.
*
* @since 3.1.0
*
* @param QM_Dispatcher_Html $this The HTML dispatcher instance.
* @param QM_Output_Html[] $this->outputters Array of outputters.
*/
do_action( 'qm/output/after', $this, $this->outputters );
echo '
'; // #qm-panels
echo '
'; // #qm-wrapper
echo '
'; // #query-monitor-main
echo '' . "\n\n";
echo '' . "\n\n";
}
public static function size( $var ) {
$start_memory = memory_get_usage();
try {
$var = unserialize( serialize( $var ) ); // phpcs:ignore
} catch ( Exception $e ) {
return $e;
}
return memory_get_usage() - $start_memory - ( PHP_INT_SIZE * 8 );
}
public function js_admin_bar_menu() {
/**
* Filters the CSS class names used on Query Monitor's admin toolbar menu.
*
* @since 2.7.0
*
* @param array $menu_classes Array of menu classes.
*/
$class = implode( ' ', apply_filters( 'qm/output/menu_class', array() ) );
if ( false === strpos( $class, 'qm-' ) ) {
$class .= ' qm-all-clear';
}
/**
* Filters the title used in Query Monitor's admin toolbar menu.
*
* @since 2.7.0
*
* @param array $output_title List of titles.
*/
$title = implode( ' ', apply_filters( 'qm/output/title', array() ) );
if ( empty( $title ) ) {
$title = esc_html__( 'Query Monitor', 'query-monitor' );
}
$admin_bar_menu = array(
'top' => array(
'title' => sprintf(
'QM %s ',
$title
),
'classname' => $class,
),
'sub' => array(),
);
foreach ( $this->admin_bar_menu as $menu ) {
$admin_bar_menu['sub'][ $menu['id'] ] = $menu;
}
return $admin_bar_menu;
}
public function is_active() {
if ( ! self::user_can_view() ) {
return false;
}
if ( ! $this->did_footer ) {
return false;
}
// Don't dispatch if this is an async request and not a customizer preview:
if ( QM_Util::is_async() && ( ! function_exists( 'is_customize_preview' ) || ! is_customize_preview() ) ) {
return false;
}
// Don't dispatch if the minimum required actions haven't fired:
if ( is_admin() ) {
if ( ! did_action( 'admin_init' ) ) {
return false;
}
} else {
if ( ! ( did_action( 'wp' ) || did_action( 'login_init' ) || did_action( 'gp_head' ) ) ) {
return false;
}
}
// Don't dispatch during an iframed request, eg the plugin info modal or an upgrader action:
if ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) {
return false;
}
/** Back-compat filter. Please use `qm/dispatch/html` instead */
if ( ! apply_filters( 'qm/process', true, is_admin_bar_showing() ) ) {
return false;
}
return true;
}
}
function register_qm_dispatcher_html( array $dispatchers, QM_Plugin $qm ) {
$dispatchers['html'] = new QM_Dispatcher_Html( $qm );
return $dispatchers;
}
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_html', 10, 2 );