Skip to content

Commit

Permalink
perf(data-events): queue dispatches to execute on shutdown (#3616)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelpeixe authored Jan 8, 2025
1 parent 6d9a4fd commit 510a1a0
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 13 deletions.
64 changes: 54 additions & 10 deletions includes/data-events/class-data-events.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,22 @@ final class Data_Events {
*/
private static $global_handlers = [];

/**
* Dispatches queued for execution on shutdown.
*
* @var array[]
*/
private static $queued_dispatches = [];

/**
* Initialize hooks.
*/
public static function init() {
\add_action( 'wp_ajax_' . self::ACTION, [ __CLASS__, 'maybe_handle' ] );
\add_action( 'wp_ajax_nopriv_' . self::ACTION, [ __CLASS__, 'maybe_handle' ] );
\add_action( 'shutdown', [ __CLASS__, 'execute_queued_dispatches' ] );
}


/**
* Maybe handle an event.
*/
Expand All @@ -57,16 +64,24 @@ public static function maybe_handle() {
\wp_die();
}

$action_name = isset( $_POST['action_name'] ) ? \sanitize_text_field( \wp_unslash( $_POST['action_name'] ) ) : null;
if ( empty( $action_name ) || ! isset( self::$actions[ $action_name ] ) ) {
$dispatches = isset( $_POST['dispatches'] ) ? $_POST['dispatches'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

if ( empty( $dispatches ) || ! is_array( $dispatches ) ) {
\wp_die();
}

$timestamp = isset( $_POST['timestamp'] ) ? \sanitize_text_field( \wp_unslash( $_POST['timestamp'] ) ) : null;
$data = isset( $_POST['data'] ) ? $_POST['data'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$client_id = isset( $_POST['client_id'] ) ? \sanitize_text_field( \wp_unslash( $_POST['client_id'] ) ) : null;
foreach ( $dispatches as $dispatch ) {
$action_name = isset( $dispatch['action_name'] ) ? \sanitize_text_field( $dispatch['action_name'] ) : null;
if ( empty( $action_name ) || ! isset( self::$actions[ $action_name ] ) ) {
continue;
}

$timestamp = isset( $dispatch['timestamp'] ) ? \sanitize_text_field( $dispatch['timestamp'] ) : null;
$data = isset( $dispatch['data'] ) ? $dispatch['data'] : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$client_id = isset( $dispatch['client_id'] ) ? \sanitize_text_field( $dispatch['client_id'] ) : null;

self::handle( $action_name, $timestamp, $data, $client_id );
self::handle( $action_name, $timestamp, $data, $client_id );
}

\wp_die();
}
Expand Down Expand Up @@ -312,8 +327,26 @@ public static function dispatch( $action_name, $data, $use_client_id = true ) {
return $body;
}

self::$queued_dispatches[] = $body;

// If we're in shutdown, execute the dispatches immediately.
if ( did_action( 'shutdown' ) ) {
self::execute_queued_dispatches();
}
}

/**
* Execute queued dispatches.
*/
public static function execute_queued_dispatches() {
if ( empty( self::$queued_dispatches ) ) {
return;
}

$actions = array_column( self::$queued_dispatches, 'action_name' );

Logger::log(
sprintf( 'Dispatching action "%s".', $action_name ),
sprintf( 'Dispatching actions: "%s".', implode( ', ', $actions ) ),
self::LOGGER_HEADER
);

Expand All @@ -325,16 +358,27 @@ public static function dispatch( $action_name, $data, $use_client_id = true ) {
\admin_url( 'admin-ajax.php' )
);

return \wp_remote_post(
$request = \wp_remote_post(
$url,
[
'timeout' => 0.01,
'blocking' => false,
'body' => $body,
'body' => [ 'dispatches' => self::$queued_dispatches ],
'cookies' => $_COOKIE, // phpcs:ignore
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
]
);

/**
* Fires after dispatching queued actions.
*
* @param WP_Error|WP_HTTP_Response $request The request object.
* @param array $queued_dispatches The queued dispatches.
*/
\do_action( 'newspack_data_events_dispatched', $request, self::$queued_dispatches );

// Clear the queue in case of a retry.
self::$queued_dispatches = [];
}
}
Data_Events::init();
28 changes: 25 additions & 3 deletions tests/unit-tests/data-events.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,32 @@ public function test_dispatch() {

// Assert the hook was called once.
$this->assertEquals( 1, $call_count );
}

/**
* Test that executing queued dispatches triggers the dispatched action hook.
*/
public function test_execute_queued_dispatches() {
$action_name = 'test_action';
$data = [ 'test' => 'data' ];

$hook_request = null;
$hook_queued_dispatches = null;

$hook = function( $request, $queued_dispatches ) use ( &$hook_request, &$hook_queued_dispatches ) {
$hook_request = $request;
$hook_queued_dispatches = $queued_dispatches;
};
add_action( 'newspack_data_events_dispatched', $hook, 10, 2 );

Data_Events::register_action( $action_name );
Data_Events::dispatch( $action_name, $data );
Data_Events::execute_queued_dispatches();

// Assert it returns a WP_Http response.
$this->assertIsArray( $result );
$this->assertArrayHasKey( 'http_response', $result );
$this->assertIsArray( $hook_request );
$this->assertIsArray( $hook_queued_dispatches );
$this->assertEquals( $action_name, $hook_queued_dispatches[0]['action_name'] );
$this->assertEquals( $data, $hook_queued_dispatches[0]['data'] );
}

/**
Expand Down

0 comments on commit 510a1a0

Please sign in to comment.