From 3245095e14ad7569d625fb0b2a94fc571bf78d7a Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Fri, 1 Sep 2023 16:40:25 -0400 Subject: [PATCH 1/7] Cache and send request events asyncronously Use the same process for hooking into the "shutdown" action and spin off async requests for sending each cached event to the telemetry server. --- src/Telemetry/Events/Event.php | 7 ++ src/Telemetry/Events/Event_Subscriber.php | 79 +++++++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/Telemetry/Events/Event.php b/src/Telemetry/Events/Event.php index 0b57272..693dd8d 100644 --- a/src/Telemetry/Events/Event.php +++ b/src/Telemetry/Events/Event.php @@ -21,6 +21,13 @@ */ class Event { + /** + * The hook name for sending events asyncronously. + * + * @since TBD + */ + public const AJAX_ACTION = 'stellarwp_telemetry_send_event'; + /** * An instance of the Telemetry class. * diff --git a/src/Telemetry/Events/Event_Subscriber.php b/src/Telemetry/Events/Event_Subscriber.php index 3e58585..aceaecc 100644 --- a/src/Telemetry/Events/Event_Subscriber.php +++ b/src/Telemetry/Events/Event_Subscriber.php @@ -9,6 +9,7 @@ namespace StellarWP\Telemetry\Events; +use StellarWP\Telemetry\Config; use StellarWP\Telemetry\Contracts\Abstract_Subscriber; /** @@ -28,19 +29,83 @@ class Event_Subscriber extends Abstract_Subscriber { * @return void */ public function register() { - add_action( 'stellarwp/telemetry/event', [ $this, 'send_event' ], 10, 2 ); + add_action( 'shutdown', [ $this, 'send_cached_events' ] ); + add_action( 'stellarwp/telemetry/' . Config::get_hook_prefix() . 'event', [ $this, 'cache_event' ], 10, 2 ); + add_action( 'wp_ajax_' . Event::AJAX_ACTION, [ $this, 'send_event' ], 10, 1 ); + add_action( 'wp_ajax_nopriv_' . Event::AJAX_ACTION, [ $this, 'send_event' ], 10, 1 ); } /** - * Sends an event request to the Telemetry server. + * Caches an event to be sent during shutdown. * - * @since 2.1.0 + * @since TBD + * + * @param string $name The name of the event. + * @param array $data The data sent along with the event. * - * @param string $name The name of the event to send. - * @param array $data Additional information to send with the event. + * @return void */ - public function send_event( string $name, array $data = [] ) { - $this->container->get( Event::class )->send( $name, $data ); + public function cache_event( $name, $data ) { + $events = []; + + if ( $this->container->has( 'events' ) ) { + $events = $this->container->get( 'events' ); + } + + $events[] = [ $name => $data ]; + + $this->container->bind( 'events', $events ); } + /** + * Sends the events that have been stored for the current request. + * + * @since TBD + * + * @return void + */ + public function send_cached_events() { + if ( ! $this->container->has( 'events' ) ) { + return; + } + + $url = admin_url( 'admin-ajax.php?XDEBUG_SESSION_START=1' ); + + $events = $this->container->get( 'events' ); + + // Kick off asyncronous requests for each cached event. + foreach ( $events as $key => $event ) { + // Remove each event as they are sent. + unset( $events[ $key ] ); + + wp_remote_post( + $url, + [ + 'blocking' => false, + 'sslverify' => false, + 'body' => [ + 'action' => Event::AJAX_ACTION, + 'event' => $event, + ], + ] + ); + } + + $this->container->bind( 'events', [] ); + } + + /** + * Send the event to the telemetry server. + * + * @since TBD + * + * @return void + */ + public function send_event() { + $event = filter_input( INPUT_POST, 'event', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); // phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter + $name = key( $event ); + $data = $event[ $name ]; + + $this->container->get( Event::class )->send( $name, $data ); + } } From 54e6e76e13a1ddcbc86182188065787174f65052 Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Fri, 1 Sep 2023 16:41:51 -0400 Subject: [PATCH 2/7] Add adminbar link to simplify event sending --- .../library-testing/library-testing.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dev/public/wp-content/plugins/library-testing/library-testing.php b/dev/public/wp-content/plugins/library-testing/library-testing.php index 87900fc..9c6e55d 100644 --- a/dev/public/wp-content/plugins/library-testing/library-testing.php +++ b/dev/public/wp-content/plugins/library-testing/library-testing.php @@ -31,3 +31,43 @@ function () { do_action( 'stellarwp/telemetry/optin', 'telemetry-library' ); } ); + +// If the 'Send Events' link was used, send some test events once. +add_action( + 'init', + function() { + if ( ! isset( $_GET['send-events'] ) ) { + return; + } + + do_action( 'stellarwp/telemetry/telemetry-prefix/event', 'opt-in', [ 'one' => 1 ] ); + do_action( 'stellarwp/telemetry/telemetry-prefix/event', 'create-post', [ 'post-title' => 'This is my first post!' ] ); + do_action( 'stellarwp/telemetry/telemetry-prefix/event', 'opt-out', [ 'one' => 1 ] ); + + wp_safe_redirect( remove_query_arg( 'send-events' ) ); + exit; + } +); + +/** + * Adds a helper link to the admin bar for sending a group of events. + * + * @param WP_Admin_Bar $admin_bar The adminbar class. + * + * @return void + */ +function add_event_send_link( $admin_bar ) { + global $wp; + + $admin_bar->add_menu( + [ + 'id' => 'send-events', + 'title' => 'Send Events', + 'href' => add_query_arg( [ 'send-events' => true ], home_url( $wp->request ) ), + 'meta' => [ + 'title' => __( 'Send Events' ), + ], + ] + ); +} +add_action( 'admin_bar_menu', 'add_event_send_link', 100, 1 ); From 895786efae938cfe4e3af6738c5c1aa3bdf234a2 Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Tue, 5 Sep 2023 10:13:23 -0400 Subject: [PATCH 3/7] Ignore nonce warning --- .../wp-content/plugins/library-testing/library-testing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/public/wp-content/plugins/library-testing/library-testing.php b/dev/public/wp-content/plugins/library-testing/library-testing.php index 9c6e55d..3b7b06a 100644 --- a/dev/public/wp-content/plugins/library-testing/library-testing.php +++ b/dev/public/wp-content/plugins/library-testing/library-testing.php @@ -36,7 +36,7 @@ function () { add_action( 'init', function() { - if ( ! isset( $_GET['send-events'] ) ) { + if ( ! isset( $_GET['send-events'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } From 6d14f5cecf3237b47a7f81e96523016d8e20abbb Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Tue, 5 Sep 2023 10:16:54 -0400 Subject: [PATCH 4/7] Remove leftover testing param --- src/Telemetry/Events/Event_Subscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Telemetry/Events/Event_Subscriber.php b/src/Telemetry/Events/Event_Subscriber.php index aceaecc..356889f 100644 --- a/src/Telemetry/Events/Event_Subscriber.php +++ b/src/Telemetry/Events/Event_Subscriber.php @@ -69,7 +69,7 @@ public function send_cached_events() { return; } - $url = admin_url( 'admin-ajax.php?XDEBUG_SESSION_START=1' ); + $url = admin_url( 'admin-ajax.php' ); $events = $this->container->get( 'events' ); From 5d8594b256e7f774e8798062a7a9992131dba3e5 Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Tue, 5 Sep 2023 10:23:37 -0400 Subject: [PATCH 5/7] Fix phpstan issues --- src/Telemetry/Events/Event_Subscriber.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Telemetry/Events/Event_Subscriber.php b/src/Telemetry/Events/Event_Subscriber.php index 356889f..bd078d3 100644 --- a/src/Telemetry/Events/Event_Subscriber.php +++ b/src/Telemetry/Events/Event_Subscriber.php @@ -102,9 +102,12 @@ public function send_cached_events() { * @return void */ public function send_event() { + // Get the passed event array. $event = filter_input( INPUT_POST, 'event', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); // phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter - $name = key( $event ); - $data = $event[ $name ]; + // Get the name of the event from the key of the array. + $name = key( $event ); + // Set the data for the event. + $data = (array) $event[ $name ]; $this->container->get( Event::class )->send( $name, $data ); } From e5640cb020420925b9d77b2335e2db22d5693ba6 Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Mon, 11 Sep 2023 16:53:42 -0400 Subject: [PATCH 6/7] Refactor to utilize new batch method of events API --- src/Telemetry/Events/Event.php | 24 ++++++++++ src/Telemetry/Events/Event_Subscriber.php | 56 ++++++++++------------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/Telemetry/Events/Event.php b/src/Telemetry/Events/Event.php index 693dd8d..da5ab1c 100644 --- a/src/Telemetry/Events/Event.php +++ b/src/Telemetry/Events/Event.php @@ -84,6 +84,30 @@ public function send( string $name, array $data = [] ) { return boolval( $response['status'] ); } + /** + * Send batched events. + * + * @since TBD + * + * @param array $events An array of stored events to send to the telemetry server. + * + * @return bool + */ + public function send_batch( array $events ) { + $data = [ + 'token' => $this->telemetry->get_token(), + 'events' => $events, + ]; + + $response = $this->telemetry->send( $data, $this->get_url() ); + + if ( ! isset( $response['status'] ) ) { + return false; + } + + return boolval( $response['status'] ); + } + /** * Gets the url used for sending events. * diff --git a/src/Telemetry/Events/Event_Subscriber.php b/src/Telemetry/Events/Event_Subscriber.php index bd078d3..aa6db88 100644 --- a/src/Telemetry/Events/Event_Subscriber.php +++ b/src/Telemetry/Events/Event_Subscriber.php @@ -30,9 +30,9 @@ class Event_Subscriber extends Abstract_Subscriber { */ public function register() { add_action( 'shutdown', [ $this, 'send_cached_events' ] ); - add_action( 'stellarwp/telemetry/' . Config::get_hook_prefix() . 'event', [ $this, 'cache_event' ], 10, 2 ); - add_action( 'wp_ajax_' . Event::AJAX_ACTION, [ $this, 'send_event' ], 10, 1 ); - add_action( 'wp_ajax_nopriv_' . Event::AJAX_ACTION, [ $this, 'send_event' ], 10, 1 ); + add_action( 'stellarwp/telemetry/' . Config::get_hook_prefix() . 'event', [ $this, 'cache_event' ], 10, 3 ); + add_action( 'wp_ajax_' . Event::AJAX_ACTION, [ $this, 'send_events' ], 10, 1 ); + add_action( 'wp_ajax_nopriv_' . Event::AJAX_ACTION, [ $this, 'send_events' ], 10, 1 ); } /** @@ -40,8 +40,8 @@ public function register() { * * @since TBD * - * @param string $name The name of the event. - * @param array $data The data sent along with the event. + * @param string $name The name of the event. + * @param array $data The data sent along with the event. * * @return void */ @@ -52,7 +52,11 @@ public function cache_event( $name, $data ) { $events = $this->container->get( 'events' ); } - $events[] = [ $name => $data ]; + $events[] = [ + 'name' => $name, + 'data' => wp_json_encode( $data ), + 'stellar_slug' => Config::get_stellar_slug(), + ]; $this->container->bind( 'events', $events ); } @@ -71,25 +75,17 @@ public function send_cached_events() { $url = admin_url( 'admin-ajax.php' ); - $events = $this->container->get( 'events' ); - - // Kick off asyncronous requests for each cached event. - foreach ( $events as $key => $event ) { - // Remove each event as they are sent. - unset( $events[ $key ] ); - - wp_remote_post( - $url, - [ - 'blocking' => false, - 'sslverify' => false, - 'body' => [ - 'action' => Event::AJAX_ACTION, - 'event' => $event, - ], - ] - ); - } + wp_remote_post( + $url, + [ + 'blocking' => false, + 'sslverify' => false, + 'body' => [ + 'action' => Event::AJAX_ACTION, + 'events' => $this->container->get( 'events' ), + ], + ] + ); $this->container->bind( 'events', [] ); } @@ -101,14 +97,10 @@ public function send_cached_events() { * * @return void */ - public function send_event() { + public function send_events() { // Get the passed event array. - $event = filter_input( INPUT_POST, 'event', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); // phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter - // Get the name of the event from the key of the array. - $name = key( $event ); - // Set the data for the event. - $data = (array) $event[ $name ]; + $events = filter_input( INPUT_POST, 'events', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); // phpcs:ignore WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter - $this->container->get( Event::class )->send( $name, $data ); + $this->container->get( Event::class )->send_batch( $events ); } } From 0e265546d829c745a9ceb11978ea6b6a93e8e585 Mon Sep 17 00:00:00 2001 From: Tanner Record Date: Mon, 11 Sep 2023 16:59:04 -0400 Subject: [PATCH 7/7] Fix phpstan issues --- src/Telemetry/Events/Event_Subscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Telemetry/Events/Event_Subscriber.php b/src/Telemetry/Events/Event_Subscriber.php index aa6db88..b101578 100644 --- a/src/Telemetry/Events/Event_Subscriber.php +++ b/src/Telemetry/Events/Event_Subscriber.php @@ -30,7 +30,7 @@ class Event_Subscriber extends Abstract_Subscriber { */ public function register() { add_action( 'shutdown', [ $this, 'send_cached_events' ] ); - add_action( 'stellarwp/telemetry/' . Config::get_hook_prefix() . 'event', [ $this, 'cache_event' ], 10, 3 ); + add_action( 'stellarwp/telemetry/' . Config::get_hook_prefix() . 'event', [ $this, 'cache_event' ], 10, 2 ); add_action( 'wp_ajax_' . Event::AJAX_ACTION, [ $this, 'send_events' ], 10, 1 ); add_action( 'wp_ajax_nopriv_' . Event::AJAX_ACTION, [ $this, 'send_events' ], 10, 1 ); }