From f204d60177578657838355eaec1bf3a8aa7dff73 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Mon, 17 Feb 2025 14:23:29 +0100 Subject: [PATCH 01/12] :feat: Add preconnect external domains db structure :closes: #7248 --- .../Queries/PreconnectExternalDomains.php | 89 ++++++++++++++ .../Row/PreconnectExternalDomains.php | 112 ++++++++++++++++++ .../Schema/PreconnectExternalDomains.php | 110 +++++++++++++++++ .../Table/PreconnectExternalDomains.php | 57 +++++++++ 4 files changed, 368 insertions(+) create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Database/Schema/PreconnectExternalDomains.php create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php new file mode 100644 index 0000000000..3a9e2ac836 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php @@ -0,0 +1,89 @@ +get_db(); + + // Early bailout if no database interface is available. + if ( ! $db ) { + return false; + } + + $delete_interval = $this->cleanup_interval; + + $prefixed_table_name = $db->prefix . $this->table_name; + $query = "DELETE FROM `$prefixed_table_name` WHERE status = 'failed' OR `last_accessed` <= date_sub(now(), interval $delete_interval month)"; + + return $db->query( $query ); + } +} diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php new file mode 100644 index 0000000000..45bf8e4973 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php @@ -0,0 +1,112 @@ +id = (int) $this->id; + $this->url = (string) $this->url; + $this->is_mobile = (bool) $this->is_mobile; + $this->domains = (string) $this->domains; + $this->status = (string) $this->status; + $this->error_message = (string) $this->error_message; + $this->modified = empty( $this->modified ) ? 0 : strtotime( (string) $this->modified ); + $this->last_accessed = empty( $this->last_accessed ) ? 0 : strtotime( (string) $this->last_accessed ); + $this->created_at = empty( $this->created_at ) ? 0 : strtotime( (string) $this->created_at ); + } + + /** + * Checks if the object has a valid Preconnect external domains value. + * + * @return bool Returns true if the object's status is 'completed' and the domains value is not empty or '[]', false otherwise. + */ + public function has_preconnect_external_domains() { + if ( 'completed' !== $this->status ) { + return false; + } + + if ( empty( $this->domains ) ) { + return false; + } + + if ( '[]' === $this->domains ) { + return false; + } + + return true; + } +} diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Schema/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Schema/PreconnectExternalDomains.php new file mode 100644 index 0000000000..56406338d1 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Schema/PreconnectExternalDomains.php @@ -0,0 +1,110 @@ + 'id', + 'type' => 'bigint', + 'length' => '20', + 'unsigned' => true, + 'extra' => 'auto_increment', + 'primary' => true, + 'sortable' => true, + ], + + // URL column. + [ + 'name' => 'url', + 'type' => 'varchar', + 'length' => '2000', + 'default' => '', + 'cache_key' => true, + 'searchable' => true, + 'sortable' => true, + ], + + // IS_MOBILE column. + [ + 'name' => 'is_mobile', + 'type' => 'tinyint', + 'length' => '1', + 'default' => 0, + 'cache_key' => true, + 'searchable' => true, + 'sortable' => true, + ], + + // Below the fold column. + [ + 'name' => 'domains', + 'type' => 'longtext', + 'default' => '', + 'cache_key' => false, + 'searchable' => true, + 'sortable' => true, + ], + + // error_message column. + [ + 'name' => 'error_message', + 'type' => 'longtext', + 'default' => null, + 'cache_key' => false, + 'searchable' => true, + 'sortable' => true, + ], + + // STATUS column. + [ + 'name' => 'status', + 'type' => 'varchar', + 'length' => '255', + 'default' => null, + 'cache_key' => true, + 'searchable' => true, + 'sortable' => false, + ], + + // MODIFIED column. + [ + 'name' => 'modified', + 'type' => 'timestamp', + 'default' => '0000-00-00 00:00:00', + 'created' => true, + 'date_query' => true, + 'sortable' => true, + ], + + // LAST_ACCESSED column. + [ + 'name' => 'last_accessed', + 'type' => 'timestamp', + 'default' => '0000-00-00 00:00:00', + 'created' => true, + 'date_query' => true, + 'sortable' => true, + ], + + // CREATED_AT column. + [ + 'name' => 'created_at', + 'type' => 'timestamp', + 'default' => null, + 'created' => true, + 'date_query' => true, + 'sortable' => true, + ], + ]; +} diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php new file mode 100644 index 0000000000..4f34da47e5 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php @@ -0,0 +1,57 @@ + value array of versions => methods. + * + * @var array + */ + protected $upgrades = []; + + /** + * Table schema data. + * + * @var string + */ + protected $schema_data = " + id bigint(20) unsigned NOT NULL AUTO_INCREMENT, + url varchar(2000) NOT NULL default '', + is_mobile tinyint(1) NOT NULL default 0, + domains longtext default '', + error_message longtext default '', + status varchar(255) NOT NULL default '', + modified timestamp NOT NULL default '0000-00-00 00:00:00', + last_accessed timestamp NOT NULL default '0000-00-00 00:00:00', + created_at timestamp NULL, + PRIMARY KEY (id), + KEY url (url(150), is_mobile), + KEY modified (modified), + KEY last_accessed (last_accessed), + INDEX `status_index` (`status`(191))"; +} From f57d4b1867fc1bfa2c2c455bfb122e9565a233de Mon Sep 17 00:00:00 2001 From: Khadreal Date: Mon, 17 Feb 2025 15:12:02 +0100 Subject: [PATCH 02/12] :chore: Add ajax controller --- .../AJAX/Controller.php | 144 ++++++++++++++++++ .../Row/PreconnectExternalDomains.php | 2 +- 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php diff --git a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php new file mode 100644 index 0000000000..b1d899b336 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php @@ -0,0 +1,144 @@ +query = $query; + $this->context = $context; + } + + /** + * Add Preconnect external domains data to the database + * + * @return array + */ + public function add_data(): array { + check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' ); + $payload = []; + + if ( ! $this->context->is_allowed() ) { + $payload['preload_fonts'] = 'not allowed'; + + return $payload; + } + + $url = isset( $_POST['url'] ) ? untrailingslashit( esc_url_raw( wp_unslash( $_POST['url'] ) ) ) : ''; + $is_mobile = isset( $_POST['is_mobile'] ) ? filter_var( wp_unslash( $_POST['is_mobile'] ), FILTER_VALIDATE_BOOLEAN ) : false; + $results = isset( $_POST['results'] ) ? json_decode( wp_unslash( $_POST['results'] ) ) : (object) [ 'preconnect_external_domains' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $domains = $results->preconnect_external_domains ?? []; + + $preconnect_domains = []; + + /** + * Filters the maximum number of preconnect external domains being saved into the database. + * + * @param int $max_number Maximum number to allow. + * @param string $url Current page url. + * @param string[]|array $hashes Current list of preload fonts. + */ + $max_preconnect_domains_number = wpm_apply_filters_typed( 'integer', 'rocket_preconnect_external_domains_number', 20, $url, $preload_fonts ); + if ( 0 >= $max_preconnect_domains_number ) { + $max_preconnect_domains_number = 1; + } + + foreach ( (array) $domains as $index => $domain ) { + $preconnect_domains[ $index ] = sanitize_text_field( wp_unslash( $domain ) ); + --$max_preconnect_domains_number; + } + + $row = $this->query->get_row( $url, $is_mobile ); + if ( ! empty( $row ) ) { + $payload['preconnect_external_domains'] = 'item already in the database'; + + return $payload; + } + + $status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : ''; + list( $status_code, $status_message ) = $this->get_status_code_message( $status ); + + $item = [ + 'url' => $url, + 'is_mobile' => $is_mobile, + 'status' => $status_code, + 'error_message' => $status_message, + 'fonts' => wp_json_encode( $preload_fonts ), + 'created_at' => current_time( 'mysql', true ), + 'last_accessed' => current_time( 'mysql', true ), + ]; + + $result = $this->query->add_item( $item ); + + if ( ! $result ) { + $payload['preconnect_external_domains'] = 'error when adding the entry to the database'; + + return $payload; + } + + $payload['preconnect_external_domains'] = $item; + + return $payload; + } + + /** + * Checks if there is existing data for the current URL and device type from the beacon script. + * + * This method is called via AJAX. It checks if there is existing preconnect domains data for the current URL and device type. + * If the data exists, it returns a JSON success response with true. If the data does not exist, it returns a JSON success response with false. + * If the context is not allowed, it returns a JSON error response with false. + * + * @return array + */ + public function check_data(): array { + $payload = [ + 'preconnect_external_domains' => false, + ]; + + check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' ); + + if ( ! $this->context->is_allowed() ) { + $payload['preconnect_external_domains'] = true; + + return $payload; + } + + $is_mobile = isset( $_POST['is_mobile'] ) ? filter_var( wp_unslash( $_POST['is_mobile'] ), FILTER_VALIDATE_BOOLEAN ) : false; + $url = isset( $_POST['url'] ) ? untrailingslashit( esc_url_raw( wp_unslash( $_POST['url'] ) ) ) : ''; + + $row = $this->query->get_row( $url, $is_mobile ); + + if ( ! empty( $row ) ) { + $payload['preconnect_external_domains'] = true; + } + + return $payload; + } +} diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php index 45bf8e4973..ae3086c347 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Row/PreconnectExternalDomains.php @@ -5,7 +5,7 @@ use WP_Rocket\Dependencies\BerlinDB\Database\Row; -class PreconnectExternalDomains extends Row{ +class PreconnectExternalDomains extends Row { /** * Row ID * From 72829ddb5b458912b8c3931d8e75802c65927441 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Mon, 17 Feb 2025 18:58:11 +0100 Subject: [PATCH 03/12] Add subscriber, table to uninstallation process # Conflicts: # inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php --- .../Frontend/Subscriber.php | 19 ++++++ .../ServiceProvider.php | 65 +++++++++++++++++++ inc/Engine/WPRocketUninstall.php | 19 +++--- inc/Plugin.php | 1 + uninstall.php | 15 +++-- 5 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Frontend/Subscriber.php create mode 100644 inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php diff --git a/inc/Engine/Media/PreconnectExternalDomains/Frontend/Subscriber.php b/inc/Engine/Media/PreconnectExternalDomains/Frontend/Subscriber.php new file mode 100644 index 0000000000..99d0642996 --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Frontend/Subscriber.php @@ -0,0 +1,19 @@ +provides, true ); + } + + + /** + * Registers the classes in the container + * + * @return void + */ + public function register(): void { + $this->getContainer()->add( 'preconnect_query', Query::class ); + $this->getContainer()->addShared( 'preconnect_table', PreconnectTable::class ); + + $this->getContainer()->get( 'preconnect_table' ); + + $this->getContainer()->add( 'preconnect_ajax_controller', AJAXController::class ) + ->addArguments( + [ + $this->getContainer()->get( 'preconnect_query' ), + $this->getContainer()->get( 'preconnect_context' ), + ] + ); + + $this->getContainer()->addShared( 'preconnect_frontend_subscriber', FrontendSubscriber::class ); + } +} diff --git a/inc/Engine/WPRocketUninstall.php b/inc/Engine/WPRocketUninstall.php index 23d17995e9..38a385a08e 100644 --- a/inc/Engine/WPRocketUninstall.php +++ b/inc/Engine/WPRocketUninstall.php @@ -5,7 +5,7 @@ use WP_Rocket\Engine\Preload\Database\Tables\Cache; use WP_Rocket\Engine\Media\AboveTheFold\Database\Tables\AboveTheFold; use WP_Rocket\Engine\Optimization\LazyRenderContent\Database\Table\LazyRenderContent; - +use WP_Rocket\Engine\Media\PreconnectExternalDomains\Database\Table\PreconnectExternalDomains; /** * Manages the deletion of WP Rocket data and files on uninstall. */ @@ -146,12 +146,13 @@ class WPRocketUninstall { /** * Constructor. * - * @param string $cache_path Path to the cache folder. - * @param string $config_path Path to the config folder. - * @param UsedCSS $rucss_usedcss_table RUCSS used_css table. - * @param Cache $rocket_cache Preload rocket_cache table. - * @param AboveTheFold $atf_table Above the fold table. - * @param LazyRenderContent $lrc_table Lazy Render content table. + * @param string $cache_path Path to the cache folder. + * @param string $config_path Path to the config folder. + * @param UsedCSS $rucss_usedcss_table RUCSS used_css table. + * @param Cache $rocket_cache Preload rocket_cache table. + * @param AboveTheFold $atf_table Above the fold table. + * @param LazyRenderContent $lrc_table Lazy Render content table. + * @param PreconnectExternalDomains $preload_domains_table Preload External Domains content table. */ public function __construct( $cache_path, @@ -159,7 +160,8 @@ public function __construct( $rucss_usedcss_table, $rocket_cache, $atf_table, - $lrc_table + $lrc_table, + $preload_domains_table ) { $this->cache_path = trailingslashit( $cache_path ); $this->config_path = $config_path; @@ -168,6 +170,7 @@ public function __construct( $rocket_cache, $atf_table, $lrc_table, + $preload_domains_table, ]; } diff --git a/inc/Plugin.php b/inc/Plugin.php index 7c7ec851a2..bfd83d3123 100644 --- a/inc/Plugin.php +++ b/inc/Plugin.php @@ -389,6 +389,7 @@ private function init_common_subscribers() { 'media_fonts_frontend_subscriber', 'media_fonts_admin_subscriber', 'media_fonts_clean_subscriber', + 'preconnect_frontend_subscriber', ]; $host_type = HostResolver::get_host_service(); diff --git a/uninstall.php b/uninstall.php index 55e48390f5..322b4688a0 100755 --- a/uninstall.php +++ b/uninstall.php @@ -30,18 +30,21 @@ require_once __DIR__ . '/inc/Engine/Common/PerformanceHints/Database/Table/AbstractTable.php'; require_once __DIR__ . '/inc/Engine/Media/AboveTheFold/Database/Tables/AboveTheFold.php'; require_once __DIR__ . '/inc/Engine/Optimization/LazyRenderContent/Database/Table/LazyRenderContent.php'; +require_once __DIR__ . '/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php'; -$rocket_rucss_usedcss_table = new WP_Rocket\Engine\Optimization\RUCSS\Database\Tables\UsedCSS(); -$rocket_cache_table = new WP_Rocket\Engine\Preload\Database\Tables\Cache(); -$rocket_atf_table = new WP_Rocket\Engine\Media\AboveTheFold\Database\Tables\AboveTheFold(); -$rocket_lrc_table = new WP_Rocket\Engine\Optimization\LazyRenderContent\Database\Table\LazyRenderContent(); -$rocket_uninstall = new WPRocketUninstall( +$rocket_rucss_usedcss_table = new WP_Rocket\Engine\Optimization\RUCSS\Database\Tables\UsedCSS(); +$rocket_cache_table = new WP_Rocket\Engine\Preload\Database\Tables\Cache(); +$rocket_atf_table = new WP_Rocket\Engine\Media\AboveTheFold\Database\Tables\AboveTheFold(); +$rocket_lrc_table = new WP_Rocket\Engine\Optimization\LazyRenderContent\Database\Table\LazyRenderContent(); +$rocket_preload_domains_table = new WP_Rocket\Engine\Media\PreconnectExternalDomains\Database\Table\PreconnectExternalDomains(); +$rocket_uninstall = new WPRocketUninstall( WP_ROCKET_CACHE_ROOT_PATH, WP_ROCKET_CONFIG_PATH, $rocket_rucss_usedcss_table, $rocket_cache_table, $rocket_atf_table, - $rocket_lrc_table + $rocket_lrc_table, + $rocket_preload_domains_table ); $rocket_uninstall->uninstall(); From 2356bb11fa46dabe769b5be343e562c9ab85a801 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 18 Feb 2025 12:44:03 +0100 Subject: [PATCH 04/12] Add trait, update test --- .../PerformanceHints/Cron/CronTrait.php | 30 +++++++++++++++++++ .../Database/Queries/AbstractQueries.php | 20 +++++++++++++ .../Database/Queries/QueriesInterface.php | 9 ++++++ .../AJAX/Controller.php | 6 ++-- .../Engine/WPRocketUninstall/uninstall.php | 3 +- 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 inc/Engine/Common/PerformanceHints/Cron/CronTrait.php diff --git a/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php b/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php new file mode 100644 index 0000000000..3e98df6c97 --- /dev/null +++ b/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php @@ -0,0 +1,30 @@ +queries; + } + + return $this->queries->set_cleanup_interval( $delete_interval ); + } +} diff --git a/inc/Engine/Common/PerformanceHints/Database/Queries/AbstractQueries.php b/inc/Engine/Common/PerformanceHints/Database/Queries/AbstractQueries.php index b74e6b2fae..974a23d4b3 100644 --- a/inc/Engine/Common/PerformanceHints/Database/Queries/AbstractQueries.php +++ b/inc/Engine/Common/PerformanceHints/Database/Queries/AbstractQueries.php @@ -6,6 +6,13 @@ use WP_Rocket\Dependencies\BerlinDB\Database\Query; class AbstractQueries extends Query { + /** + * Cleanup interval. + * + * @var int + */ + public $cleanup_interval; + /** * Table status. * @@ -163,4 +170,17 @@ public function get_rows_by_url( string $url ) { return $query; } + + /** + * Set cleanup interval + * + * @param int $interval The interval duration, usually default to 1. + * + * @return object + */ + public function set_cleanup_interval( int $interval ): object { + $this->cleanup_interval = $interval; + + return $this; + } } diff --git a/inc/Engine/Common/PerformanceHints/Database/Queries/QueriesInterface.php b/inc/Engine/Common/PerformanceHints/Database/Queries/QueriesInterface.php index dd362f1b52..5371013848 100644 --- a/inc/Engine/Common/PerformanceHints/Database/Queries/QueriesInterface.php +++ b/inc/Engine/Common/PerformanceHints/Database/Queries/QueriesInterface.php @@ -14,4 +14,13 @@ interface QueriesInterface { * @return bool|int Returns a boolean or integer value. The exact return value depends on the implementation. */ public function delete_old_rows(); + + /** + * Sets the cleanup interval. + * + * This method sets the interval at which the cleanup process should run. + * + * @param int $interval The interval in seconds. + */ + public function set_cleanup_interval( int $interval ); } diff --git a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php index b1d899b336..36ada76994 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php +++ b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php @@ -46,7 +46,7 @@ public function add_data(): array { $payload = []; if ( ! $this->context->is_allowed() ) { - $payload['preload_fonts'] = 'not allowed'; + $payload['preconnect_external_domains'] = 'not allowed'; return $payload; } @@ -65,7 +65,7 @@ public function add_data(): array { * @param string $url Current page url. * @param string[]|array $hashes Current list of preload fonts. */ - $max_preconnect_domains_number = wpm_apply_filters_typed( 'integer', 'rocket_preconnect_external_domains_number', 20, $url, $preload_fonts ); + $max_preconnect_domains_number = wpm_apply_filters_typed( 'integer', 'rocket_preconnect_external_domains_number', 20, $url, $domains ); if ( 0 >= $max_preconnect_domains_number ) { $max_preconnect_domains_number = 1; } @@ -90,7 +90,7 @@ public function add_data(): array { 'is_mobile' => $is_mobile, 'status' => $status_code, 'error_message' => $status_message, - 'fonts' => wp_json_encode( $preload_fonts ), + 'fonts' => wp_json_encode( $preconnect_domains ), 'created_at' => current_time( 'mysql', true ), 'last_accessed' => current_time( 'mysql', true ), ]; diff --git a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php index a98a466013..0ad3a22ee2 100644 --- a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php +++ b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php @@ -144,8 +144,9 @@ public function testShouldDeleteAll() { $preload_table = $container->get( 'preload_caches_table' ); $atf_table = $container->get( 'atf_table' ); $lrc_table = $container->get( 'lrc_table' ); + $preconnect_table = $container->get( 'preconnect_table' ); - $uninstall = new WPRocketUninstall( $cache_path, $config_path, $rucss_usedcss_table, $preload_table, $atf_table, $lrc_table ); + $uninstall = new WPRocketUninstall( $cache_path, $config_path, $rucss_usedcss_table, $preload_table, $atf_table, $lrc_table, $preconnect_table ); $uninstall->uninstall(); From aed491f6314735156b1467d9777a2490f4324efb Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 18 Feb 2025 14:01:59 +0100 Subject: [PATCH 05/12] Add unit test --- .../AJAX/Controller.php | 12 +- .../AJAX/Controller/addData.php | 180 ++++++++++++++++++ .../AJAX/Controller/addData.php | 93 +++++++++ 3 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php create mode 100644 tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php diff --git a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php index 36ada76994..11fecd7266 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php +++ b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php @@ -19,7 +19,7 @@ class Controller implements ControllerInterface { private $query; /** - * PreloadFonts Context. + * PreconnectExternalDomains Context. * * @var ContextInterface */ @@ -53,8 +53,8 @@ public function add_data(): array { $url = isset( $_POST['url'] ) ? untrailingslashit( esc_url_raw( wp_unslash( $_POST['url'] ) ) ) : ''; $is_mobile = isset( $_POST['is_mobile'] ) ? filter_var( wp_unslash( $_POST['is_mobile'] ), FILTER_VALIDATE_BOOLEAN ) : false; - $results = isset( $_POST['results'] ) ? json_decode( wp_unslash( $_POST['results'] ) ) : (object) [ 'preconnect_external_domains' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - $domains = $results->preconnect_external_domains ?? []; + $results = isset( $_POST['results'] ) ? json_decode( wp_unslash( $_POST['results'] ) ) : (object) [ 'domains' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $domains = $results->domains ?? []; $preconnect_domains = []; @@ -63,7 +63,7 @@ public function add_data(): array { * * @param int $max_number Maximum number to allow. * @param string $url Current page url. - * @param string[]|array $hashes Current list of preload fonts. + * @param string[]|array $hashes Current list of preconnect external domains. */ $max_preconnect_domains_number = wpm_apply_filters_typed( 'integer', 'rocket_preconnect_external_domains_number', 20, $url, $domains ); if ( 0 >= $max_preconnect_domains_number ) { @@ -71,7 +71,7 @@ public function add_data(): array { } foreach ( (array) $domains as $index => $domain ) { - $preconnect_domains[ $index ] = sanitize_text_field( wp_unslash( $domain ) ); + $preconnect_domains[ $index ] = sanitize_url( wp_unslash( $domain ) ); --$max_preconnect_domains_number; } @@ -90,7 +90,7 @@ public function add_data(): array { 'is_mobile' => $is_mobile, 'status' => $status_code, 'error_message' => $status_message, - 'fonts' => wp_json_encode( $preconnect_domains ), + 'domains' => wp_json_encode( $preconnect_domains ), 'created_at' => current_time( 'mysql', true ), 'last_accessed' => current_time( 'mysql', true ), ]; diff --git a/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php new file mode 100644 index 0000000000..5d24d8e2b9 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php @@ -0,0 +1,180 @@ + [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode([ + 'domains' => [ + 'https://example-domain-1.com/', + 'https://example-domain-2.com/', + ] + ]), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'domains' => json_encode( + [ + 'https://example-domain-1.com/', + 'https://example-domain-2.com/', + ], + ), + 'created_at' => '2025-02-18 00:00:00', + 'last_accessed' => '2025-02-18 00:00:00', + 'error_message' => '' + ], + 'result' => true, + 'message' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'error_message' => '', + 'domains' => json_encode( + [ + 'https://example-domain-1.com/', + 'https://example-domain-2.com/', + ], + ), + 'created_at' => '2025-02-18 00:00:00', + 'last_accessed' => '2025-02-18 00:00:00', + ], + ], + ], + 'testShouldBailWhenNotAllowed' => [ + 'config' => [ + 'filter' => false, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode( + [ + 'domains' => [] + ], + ), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'domains' => [], + 'created_at' => '2025-02-02 00:00:00', + 'last_accessed' => '2025-02-02 00:00:00', + ], + 'result' => false, + 'message' => 'not allowed', + ], + ], + 'testShouldBailoutWhenDBError' => [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode([ + 'domains' => [ + 'https://fonts.googleapis.org', + 'https://fonts.googleapis.com', + ] + ]), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'domains' => json_encode( + [ + 'https://fonts.googleapis.org', + 'https://fonts.googleapis.com', + ], + ), + 'last_accessed' => '2025-02-02 00:00:00', + 'created_at' => '2025-02-02 00:00:00', + 'error_message' => '' + ], + 'result' => false, + 'message' => 'error when adding the entry to the database', + ], + ], + 'testShouldAddItemToDBWhenMobile' => [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => true, + 'results' => json_encode([ + 'domains' => [ + 'https://example-domain.org', + 'https://example-domain.ng', + ] + ]), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => true, + 'status' => 'completed', + 'domains' => json_encode( + [ + 'https://example-domain.org', + 'https://example-domain.ng', + ], + ), + 'last_accessed' => '2025-02-18 00:00:00', + 'created_at' => '2025-02-18 00:00:00', + 'error_message' => '' + ], + 'result' => true, + 'message' => [ + 'url' => 'http://example.org', + 'is_mobile' => true, + 'status' => 'completed', + 'error_message' => '', + 'domains' => json_encode( + [ + 'https://example-domain.org', + 'https://example-domain.ng', + ], + ), + 'created_at' => '2025-02-18 00:00:00', + 'last_accessed' => '2025-02-18 00:00:00', + ], + ], + ], + 'testShouldNotAddItemToDBWhenNoData' => [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode( + [ + 'domains' => [] + ], + ), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'domains' => '[]', + 'last_accessed' => '2025-01-01 00:00:00', + 'error_message' => '', + 'created_at' => '2025-01-01 00:00:00', + ], + 'result' => true, + 'message' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'error_message' => '', + 'domains' => '[]', + 'created_at' => '2025-01-01 00:00:00', + 'last_accessed' => '2025-01-01 00:00:00', + ], + ], + ] +]; diff --git a/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php new file mode 100644 index 0000000000..1bec1e4587 --- /dev/null +++ b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/addData.php @@ -0,0 +1,93 @@ +query = $this->createPartialMock( PreconnectExternalDomains::class, [ 'add_item' ] ); + $this->context = Mockery::mock( Context::class ); + $this->controller = new Controller( $this->query, $this->context ); + $this->temp_post = $_POST; + + + $this->stubEscapeFunctions(); + } + + protected function tearDown(): void { + unset( $_POST ); + $_POST = $this->temp_post; + + parent::tearDown(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldReturnExpected( $config, $expected ) { + $this->stubEscapeFunctions(); + $this->stubTranslationFunctions(); + + $_POST = [ + 'url' => addslashes( $config['url'] ), + 'is_mobile' => addslashes( $config['is_mobile'] ), + 'results' => addslashes( $config['results'] ), + 'status' => addslashes( $config['status'] ?? 'success' ), + ]; + + Functions\expect( 'check_ajax_referer' ) + ->once() + ->with( 'rocket_beacon', 'rocket_beacon_nonce' ) + ->andReturn( true ); + + $this->context->shouldReceive( 'is_allowed' ) + ->atMost() + ->once() + ->andReturn( $config['filter'] ); + + Functions\when('sanitize_url')->alias(function($url) { + return filter_var($url, FILTER_SANITIZE_URL) ?: ''; + }); + + Functions\when( 'sanitize_text_field' )->alias( + function ( $value ) { + return is_string( $value ) ? strip_tags( $value ) : $value; + } + ); + + Functions\when( 'current_time' ) + ->justReturn( $expected['item']['last_accessed'] ); + + $this->query->method( 'add_item' ) + ->with( $expected['item'] ) + ->willReturn( $expected['result'] ); + + Functions\when( 'wp_unslash' )->alias( + function ( $value ) { + return is_string( $value ) ? stripslashes( $value ) : $value; + } + ); + + $this->stubWpParseUrl(); + + $this->assertSame( [ 'preconnect_external_domains' => $expected['message'] ], $this->controller->add_data() ); + } +} From b127b5a8753dc5c8ad239092a70dbfa38cf48036 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 18 Feb 2025 15:11:01 +0100 Subject: [PATCH 06/12] Update integration test setup --- tests/Integration/DBTrait.php | 34 +++++++++++++++++++ tests/Integration/bootstrap.php | 5 +++ .../Engine/WPRocketUninstall/uninstall.php | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/Integration/DBTrait.php b/tests/Integration/DBTrait.php index 20c80aed50..f72b7e81bc 100644 --- a/tests/Integration/DBTrait.php +++ b/tests/Integration/DBTrait.php @@ -59,6 +59,13 @@ public static function addLrc( array $resource ) { return $lrc_query->add_item( $resource ); } + public static function addPreconnectExternalDomainsTable(array $resource) { + $container = apply_filters( 'rocket_container', null ); + $preload_fonts_query = $container->get( 'preconnect_external_domains_table' ); + + return $preload_fonts_query->add_item( $resource ); + } + public static function installFresh() { $container = apply_filters( 'rocket_container', null ); @@ -76,6 +83,9 @@ public static function installFresh() { $lrc_table = $container->get( 'lrc_table' ); $lrc_table->install(); + + $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' ); + $preconnect_external_domains_table->install(); } public static function installUsedCssTable() { @@ -87,6 +97,15 @@ public static function installUsedCssTable() { } } + public static function installPreloadFontsTable() { + $container = apply_filters( 'rocket_container', null ); + $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' ); + + if ( ! $preconnect_external_domains_table->exists() ) { + $preconnect_external_domains_table->install(); + } + } + public static function installPreloadCacheTable() { $container = apply_filters( 'rocket_container', null ); $preload_cache_table = $container->get( 'preload_caches_table' ); @@ -136,6 +155,20 @@ public static function uninstallAll() { if ( $lrc_table->exists() ) { $lrc_table->uninstall(); } + + $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' ); + if ( $preconnect_external_domains_table->exists() ) { + $preconnect_external_domains_table->uninstall(); + } + } + + public static function uninstallPreconnectDomainsTable() { + $container = apply_filters( 'rocket_container', null ); + $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' ); + + if ( $preconnect_external_domains_table->exists() ) { + $preconnect_external_domains_table->uninstall(); + } } public static function uninstallUsedCssTable() { @@ -178,6 +211,7 @@ public static function removeDBHooks() { $container->get( 'preload_caches_table' ), $container->get( 'atf_table' ), $container->get( 'lrc_table' ), + $container->get( 'preconnect_external_domains_table' ), ]; foreach ( $tables as $table ) { diff --git a/tests/Integration/bootstrap.php b/tests/Integration/bootstrap.php index a105c30749..c0a5d5515e 100644 --- a/tests/Integration/bootstrap.php +++ b/tests/Integration/bootstrap.php @@ -20,6 +20,7 @@ function () { // Disable ATF & LRC optimizations to prevent DB requests (unrelated to other tests). add_filter( 'rocket_above_the_fold_optimization', '__return_false' ); add_filter( 'rocket_lrc_optimization', '__return_false' ); + add_filter( 'rocket_preconnect_external_domains_optimization', '__return_false' ); if ( BootstrapManager::isGroup( 'TranslatePress' ) ) { require WP_ROCKET_TESTS_FIXTURES_DIR . '/classes/TRP_Translate_Press.php'; @@ -266,6 +267,7 @@ function () { if ( BootstrapManager::isGroup( 'PerformanceHints' ) ) { add_filter( 'rocket_above_the_fold_optimization', '__return_true' ); add_filter( 'rocket_lrc_optimization', '__return_true' ); + add_filter( 'rocket_preconnect_external_domains_optimization', '__return_true' ); } // Load the plugin. @@ -286,6 +288,9 @@ function() { $lrc_table = $container->get( 'lrc_table' ); $lrc_table->uninstall(); + + $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' ); + $preconnect_external_domains_table->uninstall(); } ); diff --git a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php index 0ad3a22ee2..d42bbf2e40 100644 --- a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php +++ b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php @@ -144,7 +144,7 @@ public function testShouldDeleteAll() { $preload_table = $container->get( 'preload_caches_table' ); $atf_table = $container->get( 'atf_table' ); $lrc_table = $container->get( 'lrc_table' ); - $preconnect_table = $container->get( 'preconnect_table' ); + $preconnect_table = $container->get( 'preconnect_external_domains_table' ); $uninstall = new WPRocketUninstall( $cache_path, $config_path, $rucss_usedcss_table, $preload_table, $atf_table, $lrc_table, $preconnect_table ); From 49f32e7b9dd0b338ad510a8f919fae2f17a61b62 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 18 Feb 2025 15:12:00 +0100 Subject: [PATCH 07/12] Update feature name # Conflicts: # inc/Engine/Common/PerformanceHints/Activation/ServiceProvider.php # inc/Engine/Common/PerformanceHints/ServiceProvider.php # inc/Engine/Media/PreconnectExternalDomains/Factory.php # inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php --- .../PerformanceHints/Cron/CronTrait.php | 30 ------------------- .../Table/PreconnectExternalDomains.php | 2 +- .../ServiceProvider.php | 22 +++++++------- 3 files changed, 12 insertions(+), 42 deletions(-) delete mode 100644 inc/Engine/Common/PerformanceHints/Cron/CronTrait.php diff --git a/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php b/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php deleted file mode 100644 index 3e98df6c97..0000000000 --- a/inc/Engine/Common/PerformanceHints/Cron/CronTrait.php +++ /dev/null @@ -1,30 +0,0 @@ -queries; - } - - return $this->queries->set_cleanup_interval( $delete_interval ); - } -} diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php index 4f34da47e5..b43fde7b9c 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Table/PreconnectExternalDomains.php @@ -3,7 +3,7 @@ namespace WP_Rocket\Engine\Media\PreconnectExternalDomains\Database\Table; -use WP_Rocket\Engine\Common\Database\Tables\AbstractTable; +use WP_Rocket\Engine\Common\PerformanceHints\Database\Table\AbstractTable; class PreconnectExternalDomains extends AbstractTable { /** diff --git a/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php b/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php index e2ecf96c78..d2ad00ee54 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php +++ b/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php @@ -21,12 +21,12 @@ class ServiceProvider extends AbstractServiceProvider { * @var array */ protected $provides = [ - 'preconnect_factory', - 'preconnect_query', - 'preconnect_context', - 'preconnect_ajax_controller', + 'preconnect_external_domains_factory', + 'preconnect_external_domains_query', + 'preconnect_external_domains_context', + 'preconnect_external_domains_ajax_controller', 'preconnect_frontend_subscriber', - 'preconnect_table', + 'preconnect_external_domains_table', ]; /** @@ -47,16 +47,16 @@ public function provides( string $id ): bool { * @return void */ public function register(): void { - $this->getContainer()->add( 'preconnect_query', Query::class ); - $this->getContainer()->addShared( 'preconnect_table', PreconnectTable::class ); + $this->getContainer()->add( 'preconnect_external_domains_query', Query::class ); + $this->getContainer()->addShared( 'preconnect_external_domains_table', PreconnectTable::class ); - $this->getContainer()->get( 'preconnect_table' ); + $this->getContainer()->get( 'preconnect_external_domains_table' ); - $this->getContainer()->add( 'preconnect_ajax_controller', AJAXController::class ) + $this->getContainer()->add( 'preconnect_external_domains_ajax_controller', AJAXController::class ) ->addArguments( [ - $this->getContainer()->get( 'preconnect_query' ), - $this->getContainer()->get( 'preconnect_context' ), + $this->getContainer()->get( 'preconnect_external_domains_query' ), + $this->getContainer()->get( 'preconnect_external_domains_context' ), ] ); From 7362b32a362167324a3a66ef0e8c387f2255f39c Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 18 Feb 2025 17:27:38 +0100 Subject: [PATCH 08/12] fixed php stan issue --- phpstan-baseline.neon | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6e027d3e20..f241077311 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -742,27 +742,27 @@ parameters: - message: "#^Usage of apply_filters\\(\\) is discouraged\\. Use wpm_apply_filters_typed\\(\\) instead\\.$#" - count: 18 + count: 21 path: tests/Integration/AjaxTestCase.php - message: "#^Usage of apply_filters\\(\\) is discouraged\\. Use wpm_apply_filters_typed\\(\\) instead\\.$#" - count: 18 + count: 21 path: tests/Integration/ApiTestCase.php - message: "#^Usage of apply_filters\\(\\) is discouraged\\. Use wpm_apply_filters_typed\\(\\) instead\\.$#" - count: 18 + count: 21 path: tests/Integration/FilesystemTestCase.php - message: "#^Usage of apply_filters\\(\\) is discouraged\\. Use wpm_apply_filters_typed\\(\\) instead\\.$#" - count: 18 + count: 21 path: tests/Integration/RESTVfsTestCase.php - message: "#^Usage of apply_filters\\(\\) is discouraged\\. Use wpm_apply_filters_typed\\(\\) instead\\.$#" - count: 18 + count: 21 path: tests/Integration/TestCase.php - From b693ee147990b7bd6281a12070788091081a43fb Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 19 Feb 2025 10:26:38 +0100 Subject: [PATCH 09/12] Add missing context class, subscriber class --- .../AJAX/Controller.php | 2 +- .../Context/Context.php | 46 +++++++++++++++++++ .../ServiceProvider.php | 6 ++- inc/Plugin.php | 3 +- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 inc/Engine/Media/PreconnectExternalDomains/Context/Context.php diff --git a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php index 11fecd7266..59e3447db2 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php +++ b/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller.php @@ -28,7 +28,7 @@ class Controller implements ControllerInterface { /** * Constructor * - * @param PreconnectQuery $query PLFQuery instance. + * @param PreconnectQuery $query Preconnect External Domains Query instance. * @param ContextInterface $context Context interface. */ public function __construct( PreconnectQuery $query, ContextInterface $context ) { diff --git a/inc/Engine/Media/PreconnectExternalDomains/Context/Context.php b/inc/Engine/Media/PreconnectExternalDomains/Context/Context.php new file mode 100644 index 0000000000..7acd7089cc --- /dev/null +++ b/inc/Engine/Media/PreconnectExternalDomains/Context/Context.php @@ -0,0 +1,46 @@ +options = $options; + } + + /** + * Determine if the action is allowed. + * + * @param array $data Data to pass to the context. + * @return bool + */ + public function is_allowed( array $data = [] ): bool { + if ( $this->options->get( 'wp_rocket_no_licence', 0 ) ) { + return false; + } + + /** + * Filters to manage above the fold optimization + * + * @param bool $allow True to allow, false otherwise. + */ + return wpm_apply_filters_typed( 'boolean', 'rocket_preconnect_external_domains_optimization', true ); + } +} diff --git a/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php b/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php index d2ad00ee54..5c017f72cd 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php +++ b/inc/Engine/Media/PreconnectExternalDomains/ServiceProvider.php @@ -21,7 +21,6 @@ class ServiceProvider extends AbstractServiceProvider { * @var array */ protected $provides = [ - 'preconnect_external_domains_factory', 'preconnect_external_domains_query', 'preconnect_external_domains_context', 'preconnect_external_domains_ajax_controller', @@ -52,6 +51,11 @@ public function register(): void { $this->getContainer()->get( 'preconnect_external_domains_table' ); + $this->getContainer()->add( 'preconnect_external_domains_context', Context::class ) + ->addArgument( + $this->getContainer()->get( 'options' ) + ); + $this->getContainer()->add( 'preconnect_external_domains_ajax_controller', AJAXController::class ) ->addArguments( [ diff --git a/inc/Plugin.php b/inc/Plugin.php index bfd83d3123..4ab13f47c6 100644 --- a/inc/Plugin.php +++ b/inc/Plugin.php @@ -55,7 +55,7 @@ use WP_Rocket\Engine\Common\PerformanceHints\ServiceProvider as PerformanceHintsServiceProvider; use WP_Rocket\Engine\Optimization\LazyRenderContent\ServiceProvider as LRCServiceProvider; use WP_Rocket\Engine\Media\Fonts\ServiceProvider as MediaFontsServiceProvider; - +use WP_Rocket\Engine\Media\PreconnectExternalDomains\ServiceProvider as PreconnectExternalDomainsServiceProvider; /** * Plugin Manager. */ @@ -292,6 +292,7 @@ private function init_common_subscribers() { $this->container->addServiceProvider( new LRCServiceProvider() ); $this->container->addServiceProvider( new MediaFontsServiceProvider() ); $this->container->addServiceProvider( new ThirdPartyServiceProvider() ); + $this->container->addServiceProvider( new PreconnectExternalDomainsServiceProvider() ); $common_subscribers = [ 'license_subscriber', From 35c6fb420934c4bc316ffac226cb48ac876ccb8a Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 19 Feb 2025 10:51:24 +0100 Subject: [PATCH 10/12] Add unit test for context --- .../Context/isAllowed.php | 25 +++++++++++ .../Context/isAllowed.php | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php create mode 100644 tests/Unit/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php diff --git a/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php new file mode 100644 index 0000000000..3053377364 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php @@ -0,0 +1,25 @@ + [ + 'config' => [ + 'licence' => true, + 'filter' => true, + ], + 'expected' => false, + ], + 'testShouldReturnFalseWhenFilterFalse' => [ + 'config' => [ + 'licence' => false, + 'filter' => false, + ], + 'expected' => false, + ], + 'testShouldReturnTrueWhenLicenseAndFilterTrue' => [ + 'config' => [ + 'licence' => false, + 'filter' => true, + ], + 'expected' => true, + ], +]; diff --git a/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php new file mode 100644 index 0000000000..1409afa979 --- /dev/null +++ b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/Context/isAllowed.php @@ -0,0 +1,41 @@ +options = Mockery::mock( Options_Data::class ); + + $this->context = new Context( $this->options ); + } + + /** + * @dataProvider configTestData + */ + public function testShouldReturnExpected( $config, $expected ) { + $this->options->expects()->get( 'wp_rocket_no_licence', 0 )->andReturn($config['licence']); + + Filters\expectApplied( 'rocket_preconnect_external_domains_optimization' ) + ->andReturn( $config['filter'] ); + + $this->assertSame( + $expected, + $this->context->is_allowed() + ); + } +} From 0b196d337e416b48f9c815dc0fe2ed9ec8925a65 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 19 Feb 2025 11:09:30 +0100 Subject: [PATCH 11/12] Add more test to improve codacy score --- .../AJAX/Controller/checkData.php | 53 ++++++++++++ .../AJAX/Controller/checkData.php | 83 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php create mode 100644 tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php diff --git a/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php new file mode 100644 index 0000000000..f260aa5f77 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php @@ -0,0 +1,53 @@ + [ + 'config' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'filter' => false, + 'row' => (object) [ + 'domains' => json_encode( [ + (object) [ + 'http://exampled-domain.ng/', + 'http://exampled-domain.com', + ], + ] ), + ], + ], + 'expected' => [ + 'result' => true, + 'message' => true + ], + ], + 'testShouldReturnSuccess' => [ + 'config' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'filter' => true, + 'row' => (object) [ + 'domains' => json_encode( [ + (object) [ + 'http://exampled-domain.ng/', + 'http://exampled-domain.com', + ], + ] ), + ], + ], + 'expected' => [ + 'result' => true, + 'message' => true + ], + ], + 'testShouldReturnError' => [ + 'config' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'row' => false, + 'filter' => true, + ], + 'expected' => [ + 'result' => false, + 'message' => false + ], + ], +]; diff --git a/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php new file mode 100644 index 0000000000..1de77e4b36 --- /dev/null +++ b/tests/Unit/inc/Engine/Media/PreconnectExternalDomains/AJAX/Controller/checkData.php @@ -0,0 +1,83 @@ +query = $this->createPartialMock( PreconnectExternalDomains::class, [ 'get_row' ] ); + $this->context = Mockery::mock( Context::class ); + $this->controller = new Controller( $this->query, $this->context ); + $this->temp_post = $_POST; + + + $this->stubEscapeFunctions(); + } + + protected function tearDown(): void { + unset( $_POST ); + $_POST = $this->temp_post; + + parent::tearDown(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldReturnExpected( $config, $expected ) { + $this->stubEscapeFunctions(); + $this->stubTranslationFunctions(); + + $_POST = [ + 'url' => addslashes( $config['url'] ), + 'is_mobile' => addslashes( $config['is_mobile'] ), + ]; + + $this->context->shouldReceive( 'is_allowed' ) + ->atMost() + ->once() + ->andReturn( $config['filter'] ); + + Functions\expect( 'check_ajax_referer' ) + ->once() + ->with( 'rocket_beacon', 'rocket_beacon_nonce' ) + ->andReturn( true ); + + Functions\when('esc_url_raw')->alias( + function( $url ){ + return $url; + } + ); + + Functions\when( 'wp_unslash' )->alias( + function ( $value ) { + return is_string( $value ) ? stripslashes( $value ) : $value; + } + ); + + $this->query->method( 'get_row' ) + ->with( $config['url'], $config['is_mobile'] ) + ->willReturn( $config['row'] ); + + $this->assertSame( [ 'preconnect_external_domains' => $expected['message'] ], $this->controller->check_data() ); + } +} From b7d7412d52161982b89577fdfac1671a2fda9e64 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 19 Feb 2025 18:13:58 +0100 Subject: [PATCH 12/12] clean up, add row and schema table --- .../Database/Queries/PreconnectExternalDomains.php | 7 +++++-- tests/Integration/DBTrait.php | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php b/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php index 3a9e2ac836..0347b46073 100644 --- a/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php +++ b/inc/Engine/Media/PreconnectExternalDomains/Database/Queries/PreconnectExternalDomains.php @@ -1,9 +1,12 @@ add_item( $resource ); } - public static function addPreconnectExternalDomainsTable(array $resource) { + public static function addPreconnectExternalDomains(array $resource) { $container = apply_filters( 'rocket_container', null ); - $preload_fonts_query = $container->get( 'preconnect_external_domains_table' ); + $preconnect_external_domains = $container->get( 'preconnect_external_domains_query' ); - return $preload_fonts_query->add_item( $resource ); + return $preconnect_external_domains->add_item( $resource ); } public static function installFresh() { @@ -97,7 +97,7 @@ public static function installUsedCssTable() { } } - public static function installPreloadFontsTable() { + public static function installPreconnectExternalDomainsTable() { $container = apply_filters( 'rocket_container', null ); $preconnect_external_domains_table = $container->get( 'preconnect_external_domains_table' );