Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listen for hooks within set bounds #792

Open
wants to merge 27 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1f3098f
save
crstauf Jul 12, 2023
d8224a5
save
crstauf Jul 12, 2023
080e629
save
crstauf Jul 12, 2023
14fe5b5
save
crstauf Jul 12, 2023
72238c1
save
crstauf Jul 12, 2023
e9ec8a3
save
crstauf Jul 12, 2023
32aa91d
Merge branch 'develop' into fix-405-4
crstauf Jul 27, 2023
182ddfe
Merge branch 'develop' into fix-405-4
crstauf Aug 2, 2023
cdeb0c9
Merge branch 'develop' into fix-405-4
crstauf Aug 3, 2023
e9c0e2f
Merge branch 'develop' into fix-405-4
crstauf Aug 9, 2023
eb614ce
Merge branch 'develop' into fix-405-4
crstauf Sep 11, 2023
8498e38
Merge branch 'develop' into fix-405-4
crstauf Oct 17, 2023
c965f60
Merge branch 'develop' into fix-405-4
crstauf Oct 20, 2023
be5d968
Merge branch 'develop' into fix-405-4
crstauf Oct 30, 2023
bc5e649
Merge branch 'develop' into fix-405-4
crstauf Nov 6, 2023
47d60aa
Merge branch 'develop' into fix-405-4
crstauf Nov 11, 2023
e171bbc
Merge branch 'develop' into fix-405-4
crstauf Nov 13, 2023
11c0d69
Merge branch 'develop' into fix-405-4
crstauf Dec 20, 2023
bf8bda0
Merge branch 'develop' into fix-405-4
crstauf Feb 15, 2024
38cbfec
Merge branch 'develop' into fix-405-4
crstauf May 25, 2024
aac4c53
Merge branch 'develop' into fix-405-4
crstauf Jul 27, 2024
228662e
Merge branch 'develop' into fix-405-4
crstauf Jul 27, 2024
1860091
Merge branch 'develop' into fix-405-4
crstauf Aug 19, 2024
e2c7f53
Merge branch 'develop' into fix-405-4
crstauf Sep 25, 2024
a8558fc
Merge branch 'develop' into fix-405-4
crstauf Nov 18, 2024
560a798
Merge branch 'develop' into fix-405-4
crstauf Nov 27, 2024
d352f21
Merge branch 'develop' into fix-405-4
crstauf Nov 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions classes/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public function __construct( QM_Plugin $qm ) {
if ( ! defined( 'QM_EDITOR_COOKIE' ) ) {
define( 'QM_EDITOR_COOKIE', 'wp-query_monitor_editor_' . COOKIEHASH );
}
if ( ! defined( 'QM_MAX_DISCOVERED_HOOKS' ) ) {
define( 'QM_MAX_DISCOVERED_HOOKS', 100 );
}

add_action( 'init', array( $this, 'init' ) );

Expand Down
206 changes: 206 additions & 0 deletions collectors/hooks_discovered.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<?php
/**
* Hooks within bounds collector.
*
* @package query-monitor
*/

/**
* @extends QM_DataCollector<QM_Data_Hooks_Discovered>
*/
class QM_Collector_Hooks_Discovered extends QM_DataCollector {

public $id = 'hooks_discovered';

public function get_storage(): QM_Data {
return new QM_Data_Hooks_Discovered();
}

/**
* @return string
*/
public function name() {
return __( 'Discovered Hooks', 'query-monitor' );
}

/**
* @return void
*/
public function set_up() {
parent::set_up();

if ( defined( 'QM_DISABLED_HOOK_DISCOVERY' ) && constant( 'QM_DISABLED_HOOK_DISCOVERY' ) ) {
return;
}

add_action( 'qm/listen/start', array( $this, 'action_listener_start' ) );
add_action( 'qm/listen/stop', array( $this, 'action_listener_stop' ) );
add_action( 'shutdown', array( $this, 'action_shutdown' ) );
}

/**
* @return void
*/
public function tear_down() {
parent::tear_down();

remove_action( 'qm/listen/start', array( $this, 'action_listener_start' ) );
remove_action( 'qm/listen/stop', array( $this, 'action_listener_stop' ) );
remove_action( 'shutdown', array( $this, 'action_shutdown' ) );
}

/**
* @param string $id
* @return void
*/
public function action_listener_start( string $id ) {
if ( $this->is_active( $id ) ) {
return;
}

if ( is_array( $this->data->bounds ) && array_key_exists( $id, $this->data->bounds ) ) {
trigger_error( sprintf(
/* translators: %s: Hook discovery ID */
esc_html__( 'Hook discovery ID `%s` already exists', 'query-monitor' ),
esc_html( $id ),
), E_USER_NOTICE );

return;
}

$this->maybe_add_all_callback();

if ( ! is_array( $this->data->active ) ) {
$this->data->active = array();
}

if ( ! is_array( $this->data->hooks ) ) {
$this->data->hooks = array();
}

if ( ! is_array( $this->data->counts ) ) {
$this->data->counts = array();
}

if ( ! is_array( $this->data->bounds ) ) {
$this->data->bounds = array();
}

$this->data->active[ $id ] = 1;
$this->data->hooks[ $id ] = array();
$this->data->counts[ $id ] = 0;
$this->data->bounds[ $id ] = array(
'start' => new QM_Backtrace(),
'stop' => null,
);
}

/**
* @param string $id
* @return void
*/
public function action_listener_stop( string $id ) {
if ( ! $this->is_active( $id ) && ! array_key_exists( $id, $this->data->hooks ) ) {
trigger_error( sprintf(
/* translators: %s: Hook discovery ID */
esc_html__( 'Hook discovery starting bound for `%s` has not been set', 'query-monitor' ),
esc_html( $id )
), E_USER_NOTICE );

return;
}

unset( $this->data->active[ $id ] );
$this->data->bounds[ $id ]['stop'] = new QM_Backtrace();

if ( $this->is_active() ) {
return;
}

remove_filter( 'all', array( $this, 'filter_all' ) );
}

/**
* @param mixed $var
* @return mixed
*/
public function filter_all( $var ) {
if ( ! $this->is_active() ) {
remove_filter( 'all', array( $this, 'filter_all' ) );

return $var;
}

if ( in_array( current_action(), array(
'qm/listen/start',
'qm/listen/stop',
) ) ) {
return $var;
}

global $wp_actions;

foreach ( array_keys( $this->data->active ) as $id ) {
end( $this->data->hooks[ $id ] );
$last = current( $this->data->hooks[ $id ] );

if ( ! empty( $last ) && current_action() === $last['name'] ) {
$i = absint( key( $this->data->hooks[ $id ] ) );
$this->data->hooks[ $id ][ $i ]['fires']++;
} else {
$this->data->hooks[ $id ][] = array(
'name' => current_action(),
'is_action' => array_key_exists( current_action(), $wp_actions ),
'fires' => 1,
);
}

if ( constant( 'QM_MAX_DISCOVERED_HOOKS' ) < ++$this->data->counts[ $id ] ) {
$this->action_listener_stop( $id );
$this->data->bounds[ $id ]['terminated'] = true;
}

return $var;
}
}

/**
* @return void
*/
public function action_shutdown() {
if ( ! $this->is_active() ) {
return;
}

foreach ( array_keys( $this->data->active ) as $id ) {
$this->action_listener_stop( $id );
}
}

/**
* @param string $id
* @return bool
*/
protected function is_active( string $id = '' ) {
if ( empty( $id ) ) {
return ! empty( $this->data->active );
}

return is_array( $this->data->active ) && array_key_exists( $id, $this->data->active );
}

/**
* @return void
*/
protected function maybe_add_all_callback() {
if ( $this->is_active() ) {
return;
}

add_filter( 'all', array( $this, 'filter_all' ) );
}

}

# Load early to catch all hooks
QM_Collectors::add( new QM_Collector_Hooks_Discovered() );
28 changes: 28 additions & 0 deletions data/hooks_discovered.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);
/**
* Hooks data transfer object.
*
* @package query-monitor
*/

class QM_Data_Hooks_Discovered extends QM_Data {
/**
* @var array<string, int>
*/
public $active;

/**
* @var array<string, array<string, mixed>>
*/
public $bounds;

/**
* @var array<string, int>
*/
public $counts;

/**
* @var array<string, array<int, array<string, mixed>>>
*/
public $hooks;
}
8 changes: 8 additions & 0 deletions dispatchers/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,14 @@ protected function after_output() {
'label' => __( 'Allow the wp-content/db.php file symlink to be put into place during activation. Set to false to prevent the symlink creation.', 'query-monitor' ),
'default' => true,
),
'QM_DISABLED_HOOK_DISCOVERY' => array(
'label' => __( 'Prevent hook discovery, to safeguard against performance impact in production.', 'query-monitor' ),
'default' => false,
),
'QM_MAX_DISCOVERED_HOOKS' => array(
'label' => __( 'Maximum number of hooks to discover before terminating.', 'query-monitor' ),
'default' => 100,
),
);

/**
Expand Down
Loading