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( '

%s

', 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 ''; echo '
'; echo ''; echo ''; echo ''; echo '
'; // #qm-title echo '
'; echo ''; // #qm-panel-menu echo '
'; } protected function do_panel_menu_item( $id, array $menu ) { printf( '
  • ', 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 '

    '; 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 ''; echo '

    '; echo ''; 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 );