From f8d0c78bb0a4ab6f1c7240803a5eacb0b50a3a55 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 4 Feb 2025 07:31:50 +0100 Subject: [PATCH 1/8] :chore: Add database structure :closes: #7242 --- .../Database/Queries/PreloadFonts.php | 101 ++++++++++++++++ .../Database/Rows/PreloadFonts.php | 111 ++++++++++++++++++ .../Database/Schema/PreloadFonts.php | 110 +++++++++++++++++ .../Database/Table/PreloadFonts.php | 57 +++++++++ .../Media/PreloadFonts/ServiceProvider.php | 45 +++++++ 5 files changed, 424 insertions(+) create mode 100644 inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php create mode 100644 inc/Engine/Media/PreloadFonts/Database/Rows/PreloadFonts.php create mode 100644 inc/Engine/Media/PreloadFonts/Database/Schema/PreloadFonts.php create mode 100644 inc/Engine/Media/PreloadFonts/Database/Table/PreloadFonts.php create mode 100644 inc/Engine/Media/PreloadFonts/ServiceProvider.php diff --git a/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php new file mode 100644 index 0000000000..733e733ba8 --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php @@ -0,0 +1,101 @@ +get_db(); + + // Early bailout if no database interface is available. + if ( ! $db ) { + return false; + } + + /** + * Filters the interval (in months) to determine when a Preload Fonts(PLF) entry is considered 'old'. + * Old PLF entries are eligible for deletion. By default, a PLF entry is considered old if it hasn't been accessed in the last month. + * + * @param int $delete_interval The interval in months after which an PLF entry is considered old. Default is 1 month. + */ + $delete_interval = wpm_apply_filters_typed( 'integer', 'rocket_plf_cleanup_interval', 1 ); + + if ( $delete_interval <= 0 ) { + return false; + } + + $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/PreloadFonts/Database/Rows/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Rows/PreloadFonts.php new file mode 100644 index 0000000000..dd552abe5f --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/Database/Rows/PreloadFonts.php @@ -0,0 +1,111 @@ +id = (int) $this->id; + $this->url = (string) $this->url; + $this->is_mobile = (bool) $this->is_mobile; + $this->fonts = (string) $this->fonts; + $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 Preload Fonts value. + * + * @return bool Returns true if the object's status is 'completed' and the fonts value is not empty or '[]', false otherwise. + */ + public function has_preload_fonts() { + if ( 'completed' !== $this->status ) { + return false; + } + + if ( empty( $this->fonts ) ) { + return false; + } + + if ( '[]' === $this->fonts ) { + return false; + } + + return true; + } +} diff --git a/inc/Engine/Media/PreloadFonts/Database/Schema/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Schema/PreloadFonts.php new file mode 100644 index 0000000000..a8eef190b2 --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/Database/Schema/PreloadFonts.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' => 'fonts', + '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/PreloadFonts/Database/Table/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Table/PreloadFonts.php new file mode 100644 index 0000000000..417b97e2ee --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/Database/Table/PreloadFonts.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, + fonts 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))"; +} diff --git a/inc/Engine/Media/PreloadFonts/ServiceProvider.php b/inc/Engine/Media/PreloadFonts/ServiceProvider.php new file mode 100644 index 0000000000..8045fae4aa --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/ServiceProvider.php @@ -0,0 +1,45 @@ +provides, true ); + } + + /** + * Registers the classes in the container + * + * @return void + */ + public function register(): void { + $this->getContainer()->addShared( 'plf_table', PLFTable::class ); + $this->getContainer()->add( 'plf_query', PLFQuery::class ); + } +} From 04ecaa9c8ef5d68c498477baf69bd22e1907b239 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 4 Feb 2025 08:45:02 +0100 Subject: [PATCH 2/8] Add ajax controller for preload fonts #7242 --- .../Media/PreloadFonts/AJAX/Controller.php | 146 ++++++++++++++++++ .../Media/PreloadFonts/Context/Context.php | 27 ++++ .../Database/Queries/PreloadFonts.php | 2 +- .../Media/PreloadFonts/ServiceProvider.php | 25 ++- inc/Engine/WPRocketUninstall.php | 6 +- inc/Plugin.php | 3 +- 6 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 inc/Engine/Media/PreloadFonts/AJAX/Controller.php create mode 100644 inc/Engine/Media/PreloadFonts/Context/Context.php diff --git a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php new file mode 100644 index 0000000000..675d3a5fe3 --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php @@ -0,0 +1,146 @@ +query = $query; + $this->context = $context; + } + + + /** + * Add Preload fonts 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) [ 'preload_fonts' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $fonts = $results->fonts ?? []; + + $preload_fonts = []; + + /** + * Filters the maximum number of fonts 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_preload_fonts_number = wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_number', 20, $url, $fonts ); + if ( 0 >= $max_preload_fonts_number ) { + $max_preload_fonts_number = 1; + } + + foreach ( (array) $fonts as $font ) { + $preload_fonts[] = sanitize_text_field( wp_unslash( $font ) ); + --$max_preload_fonts_number; + } + + $row = $this->query->get_row( $url, $is_mobile ); + if ( ! empty( $row ) ) { + $payload['preload_fonts'] = '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['preload_fonts'] = 'error when adding the entry to the database'; + + return $payload; + } + + $payload['preload_fonts'] = $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 fonts 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 = [ + 'preload_fonts' => false, + ]; + + check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' ); + + if ( ! $this->context->is_allowed() ) { + $payload['preload_fonts'] = 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['preload_fonts'] = true; + } + + return $payload; + } +} diff --git a/inc/Engine/Media/PreloadFonts/Context/Context.php b/inc/Engine/Media/PreloadFonts/Context/Context.php new file mode 100644 index 0000000000..c5d34d0289 --- /dev/null +++ b/inc/Engine/Media/PreloadFonts/Context/Context.php @@ -0,0 +1,27 @@ +getContainer()->addShared( 'plf_table', PLFTable::class ); - $this->getContainer()->add( 'plf_query', PLFQuery::class ); + $this->getContainer()->addShared( 'preload_fonts_table', PreloadFontsTable::class ); + $this->getContainer()->add( 'preload_fonts_query', PreloadFontsQuery::class ); + $this->getContainer()->add( 'preload_fonts_context', Context::class ); + + $this->getContainer()->add( 'preload_fonts_ajax_controller', AJAXController::class ) + ->addArguments( + [ + $this->getContainer()->get( 'preload_fonts_query' ), + $this->getContainer()->get( 'preload_fonts_context' ), + ] + ); } } diff --git a/inc/Engine/WPRocketUninstall.php b/inc/Engine/WPRocketUninstall.php index 23d17995e9..b45b6b1bf3 100644 --- a/inc/Engine/WPRocketUninstall.php +++ b/inc/Engine/WPRocketUninstall.php @@ -5,6 +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\PreloadFonts\Database\Table\PreloadFonts; /** * Manages the deletion of WP Rocket data and files on uninstall. @@ -152,6 +153,7 @@ class WPRocketUninstall { * @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 PreloadFonts $preload_fonts_table Preload fonts table. */ public function __construct( $cache_path, @@ -159,7 +161,8 @@ public function __construct( $rucss_usedcss_table, $rocket_cache, $atf_table, - $lrc_table + $lrc_table, + $preload_fonts_table ) { $this->cache_path = trailingslashit( $cache_path ); $this->config_path = $config_path; @@ -168,6 +171,7 @@ public function __construct( $rocket_cache, $atf_table, $lrc_table, + $preload_fonts_table, ]; } diff --git a/inc/Plugin.php b/inc/Plugin.php index 1300cbc134..5a94c0bdaf 100644 --- a/inc/Plugin.php +++ b/inc/Plugin.php @@ -54,7 +54,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\PreloadFonts\ServiceProvider as PreloadFontsServiceProvider; /** * Plugin Manager. @@ -311,6 +311,7 @@ private function init_common_subscribers() { $this->container->addServiceProvider( new PerformanceHintsServiceProvider() ); $this->container->addServiceProvider( new LRCServiceProvider() ); $this->container->addServiceProvider( new MediaFontsServiceProvider() ); + $this->container->addServiceProvider( new PreloadFontsServiceProvider() ); $common_subscribers = [ 'license_subscriber', From 75a8bf57ec58f3b1b547f82b3cde442c9105ff0e Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 4 Feb 2025 16:00:45 +0100 Subject: [PATCH 3/8] Add test --- .../Media/PreloadFonts/AJAX/Controller.php | 2 +- .../PreloadFonts/AJAX/Controller/addData.php | 73 ++++++++++++ .../Engine/WPRocketUninstall/uninstall.php | 13 ++- .../PreloadFonts/AJAX/Controller/addData.php | 107 ++++++++++++++++++ 4 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php create mode 100644 tests/Unit/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php diff --git a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php index 675d3a5fe3..aec787bc7a 100644 --- a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php +++ b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php @@ -56,7 +56,7 @@ 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) [ 'preload_fonts' => [] ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - $fonts = $results->fonts ?? []; + $fonts = $results->preload_fonts ?? []; $preload_fonts = []; diff --git a/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php new file mode 100644 index 0000000000..31784881b3 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php @@ -0,0 +1,73 @@ + [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode( + [ + 'preload_fonts' => [ + "Roboto" => (object) [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + ], + ] + ] + ], + ), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'fonts' => json_encode( [ + "Roboto" => [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + ] + ], + "OpenSans" => [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Open+Sans", + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Open+Sans", + ], + ] + ], + ] ), + 'last_accessed' => '2024-01-01 00:00:00', + 'created_at' => '2024-01-01 00:00:00', + 'error_message' => '' + ], + 'result' => true, + 'message' => 'not allowed', + ], + ], +]; diff --git a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php index a98a466013..31eed9783c 100644 --- a/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php +++ b/tests/Integration/inc/Engine/WPRocketUninstall/uninstall.php @@ -144,8 +144,17 @@ public function testShouldDeleteAll() { $preload_table = $container->get( 'preload_caches_table' ); $atf_table = $container->get( 'atf_table' ); $lrc_table = $container->get( 'lrc_table' ); - - $uninstall = new WPRocketUninstall( $cache_path, $config_path, $rucss_usedcss_table, $preload_table, $atf_table, $lrc_table ); + $preload_fonts_table = $container->get( 'preload_fonts_table' ); + + $uninstall = new WPRocketUninstall( + $cache_path, + $config_path, + $rucss_usedcss_table, + $preload_table, + $atf_table, + $lrc_table, + $preload_fonts_table + ); $uninstall->uninstall(); diff --git a/tests/Unit/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php b/tests/Unit/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php new file mode 100644 index 0000000000..a13f64bab8 --- /dev/null +++ b/tests/Unit/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php @@ -0,0 +1,107 @@ +query = $this->createPartialMock( PreloadFonts::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'] ); + + $valid_source = $expected['valid_source'] ?? []; + + if(empty($valid_source)) { + Functions\when( 'sanitize_text_field' )->alias( + function ( $value ) { + return is_string( $value ) ? strip_tags( $value ) : $value; + } + ); + } else{ + Functions\when('sanitize_text_field')->alias( + function ($value) use ($valid_source) { + $arr_value = []; + if (!is_string($value)) { + foreach ($valid_source as $key => $replacement) { + $arr_value[] = strip_tags($replacement); + } + return (object) $arr_value; + } + + return strip_tags($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( [ 'preload_fonts' => $expected['message'] ], $this->controller->add_data() ); + } +} From 59b8f4ef688fe2864d90bd34823ef42f90d7ff49 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Tue, 4 Feb 2025 22:56:52 +0100 Subject: [PATCH 4/8] Fix unit test error --- .../Media/PreloadFonts/AJAX/Controller.php | 5 +- .../PreloadFonts/AJAX/Controller/addData.php | 81 +++++++++++-------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php index aec787bc7a..ca3f41a5ae 100644 --- a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php +++ b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php @@ -47,6 +47,7 @@ public function __construct( PreloadFontsQuery $query, ContextInterface $context public function add_data(): array { check_ajax_referer( 'rocket_beacon', 'rocket_beacon_nonce' ); $payload = []; + if ( ! $this->context->is_allowed() ) { $payload['preload_fonts'] = 'not allowed'; @@ -72,8 +73,8 @@ public function add_data(): array { $max_preload_fonts_number = 1; } - foreach ( (array) $fonts as $font ) { - $preload_fonts[] = sanitize_text_field( wp_unslash( $font ) ); + foreach ( (array) $fonts as $index => $font ) { + $preload_fonts[ $index ] = sanitize_text_field( wp_unslash( $font ) ); --$max_preload_fonts_number; } diff --git a/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php index 31784881b3..0358df5adc 100644 --- a/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php +++ b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php @@ -6,68 +6,79 @@ 'filter' => true, 'url' => 'http://example.org', 'is_mobile' => false, - 'results' => json_encode( - [ - 'preload_fonts' => [ - "Roboto" => (object) [ - 'variations' => [ - [ - 'weight' => "300", - "style" => "normal", - "url" => "https://fonts.googleapis.com/css?family=Roboto", - ], - [ - 'weight' => "400", - "style" => "normal", - "url" => "https://fonts.googleapis.com/css?family=Roboto", - ], + 'results' => json_encode([ + 'preload_fonts' => [ + 'Raleway' => [ + 'variations' => [ + [ + 'weight' => '300', + 'style' => 'normal', + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', ], - ] - ] - ], - ), + [ + 'weight' => '400', + "style" => 'normal', + "url" => 'https://fonts.googleapis.com/css?family=Roboto', + ], + ], + ], + ] + ]), ], 'expected' => [ 'item' => [ 'url' => 'http://example.org', 'is_mobile' => false, 'status' => 'completed', - 'fonts' => json_encode( [ - "Roboto" => [ + 'fonts' => json_encode( + [ + "Raleway" => (object) [ 'variations' => [ [ 'weight' => "300", "style" => "normal", - "url" => "https://fonts.googleapis.com/css?family=Roboto", + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', ], [ 'weight' => "400", "style" => "normal", "url" => "https://fonts.googleapis.com/css?family=Roboto", ], - ] - ], - "OpenSans" => [ + ], + ] + ], + ), + 'last_accessed' => '2025-02-02 00:00:00', + 'created_at' => '2025-02-02 00:00:00', + 'error_message' => '' + ], + 'result' => true, + 'message' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'error_message' => '', + 'fonts' => json_encode( + [ + "Raleway" => (object) [ 'variations' => [ [ 'weight' => "300", "style" => "normal", - "url" => "https://fonts.googleapis.com/css?family=Open+Sans", + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', ], [ 'weight' => "400", "style" => "normal", - "url" => "https://fonts.googleapis.com/css?family=Open+Sans", + "url" => "https://fonts.googleapis.com/css?family=Roboto", ], - ] - ], - ] ), - 'last_accessed' => '2024-01-01 00:00:00', - 'created_at' => '2024-01-01 00:00:00', - 'error_message' => '' + ], + ] + ], + ), + 'created_at' => '2025-02-02 00:00:00', + 'last_accessed' => '2025-02-02 00:00:00', ], - 'result' => true, - 'message' => 'not allowed', ], ], ]; From e46f9b5226a8cccb09d6df41f3aa38c44a83dd7c Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 5 Feb 2025 12:47:40 +0100 Subject: [PATCH 5/8] Add more test data --- .../PreloadFonts/AJAX/Controller/addData.php | 194 +++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php index 0358df5adc..df539fd184 100644 --- a/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php +++ b/tests/Fixtures/inc/Engine/Media/PreloadFonts/AJAX/Controller/addData.php @@ -1,7 +1,7 @@ [ + 'testShouldAddItemToDB' => [ 'config' => [ 'filter' => true, 'url' => 'http://example.org', @@ -81,4 +81,196 @@ ], ], ], + 'testShouldBailWhenNotAllowed' => [ + 'config' => [ + 'filter' => false, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode( + [ + 'preload_fonts' => [] + ], + ), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'fonts' => [], + '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([ + 'preload_fonts' => [ + 'Raleway' => [ + 'variations' => [ + [ + 'weight' => '300', + 'style' => 'normal', + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', + ], + [ + 'weight' => '400', + "style" => 'normal', + "url" => 'https://fonts.googleapis.com/css?family=Roboto', + ], + ], + ], + ] + ]), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'fonts' => json_encode( + [ + "Raleway" => (object) [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + ], + ] + ], + ), + '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([ + 'preload_fonts' => [ + 'Raleway' => [ + 'variations' => [ + [ + 'weight' => '300', + 'style' => 'normal', + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', + ], + [ + 'weight' => '400', + "style" => 'normal', + "url" => 'https://fonts.googleapis.com/css?family=Roboto', + ], + ], + ], + ] + ]), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => true, + 'status' => 'completed', + 'fonts' => json_encode( + [ + "Raleway" => (object) [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + ], + ] + ], + ), + 'last_accessed' => '2025-02-02 00:00:00', + 'created_at' => '2025-02-02 00:00:00', + 'error_message' => '' + ], + 'result' => true, + 'message' => [ + 'url' => 'http://example.org', + 'is_mobile' => true, + 'status' => 'completed', + 'error_message' => '', + 'fonts' => json_encode( + [ + "Raleway" => (object) [ + 'variations' => [ + [ + 'weight' => "300", + "style" => "normal", + 'url' => 'https://fonts.gstatic.com/s/raleway/v34/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2', + ], + [ + 'weight' => "400", + "style" => "normal", + "url" => "https://fonts.googleapis.com/css?family=Roboto", + ], + ], + ] + ], + ), + 'created_at' => '2025-02-02 00:00:00', + 'last_accessed' => '2025-02-02 00:00:00', + ], + ], + ], + 'testShouldNotAddItemToDBWhenNoData' => [ + 'config' => [ + 'filter' => true, + 'url' => 'http://example.org', + 'is_mobile' => false, + 'results' => json_encode( + [ + 'preload_fonts' => [] + ], + ), + ], + 'expected' => [ + 'item' => [ + 'url' => 'http://example.org', + 'is_mobile' => false, + 'status' => 'completed', + 'fonts' => '[]', + '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' => '', + 'fonts' => '[]', + 'created_at' => '2025-01-01 00:00:00', + 'last_accessed' => '2025-01-01 00:00:00', + ], + ], + ] ]; From ad9f878a4aad30928b85c8130ead623a27fde121 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 5 Feb 2025 18:06:51 +0100 Subject: [PATCH 6/8] Fixed php stan error --- .../Media/PreloadFonts/AJAX/Controller.php | 1 - .../Media/PreloadFonts/Context/Context.php | 19 ++++++++++++++++++- .../Media/PreloadFonts/ServiceProvider.php | 5 ++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php index ca3f41a5ae..751c1e0fb3 100644 --- a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php +++ b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php @@ -10,7 +10,6 @@ use WP_Rocket\Engine\Optimization\UrlTrait; class Controller implements ControllerInterface { - use UrlTrait; use AJAXControllerTrait; /** diff --git a/inc/Engine/Media/PreloadFonts/Context/Context.php b/inc/Engine/Media/PreloadFonts/Context/Context.php index c5d34d0289..951c013119 100644 --- a/inc/Engine/Media/PreloadFonts/Context/Context.php +++ b/inc/Engine/Media/PreloadFonts/Context/Context.php @@ -3,9 +3,26 @@ namespace WP_Rocket\Engine\Media\PreloadFonts\Context; +use WP_Rocket\Admin\Options_Data; use WP_Rocket\Engine\Common\Context\ContextInterface; class Context implements ContextInterface { + /** + * Instance of the Option_Data class. + * + * @var Options_Data + */ + private $options; + + /** + * Constructor. + * + * @param Options_Data $options Instance of the Option_Data class. + */ + public function __construct( Options_Data $options ) { + $this->options = $options; + } + /** * Determine if the action is allowed. * @@ -13,7 +30,7 @@ class Context implements ContextInterface { * @return bool */ public function is_allowed( array $data = [] ): bool { - if ( get_option( 'wp_rocket_no_licence' ) ) { + if ( $this->options->get( 'wp_rocket_no_licence' ) ) { return false; } diff --git a/inc/Engine/Media/PreloadFonts/ServiceProvider.php b/inc/Engine/Media/PreloadFonts/ServiceProvider.php index 4d97fb773e..d985bad794 100644 --- a/inc/Engine/Media/PreloadFonts/ServiceProvider.php +++ b/inc/Engine/Media/PreloadFonts/ServiceProvider.php @@ -43,9 +43,12 @@ public function provides( string $id ): bool { * @return void */ public function register(): void { + $options = $this->getContainer()->get( 'options' ); + $this->getContainer()->addShared( 'preload_fonts_table', PreloadFontsTable::class ); $this->getContainer()->add( 'preload_fonts_query', PreloadFontsQuery::class ); - $this->getContainer()->add( 'preload_fonts_context', Context::class ); + $this->getContainer()->add( 'preload_fonts_context', Context::class ) + ->addArgument( $options ); $this->getContainer()->add( 'preload_fonts_ajax_controller', AJAXController::class ) ->addArguments( From f5567f7394b4b0f2c247249e593c10925067ad73 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 5 Feb 2025 18:41:48 +0100 Subject: [PATCH 7/8] create deletion filter method --- .../Database/Queries/PreloadFonts.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php index 336e68c5c9..969f76752a 100644 --- a/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php +++ b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php @@ -81,13 +81,7 @@ public function delete_old_rows() { return false; } - /** - * Filters the interval (in months) to determine when a Preload Fonts(PLF) entry is considered 'old'. - * Old PLF entries are eligible for deletion. By default, a PLF entry is considered old if it hasn't been accessed in the last month. - * - * @param int $delete_interval The interval in months after which an PLF entry is considered old. Default is 1 month. - */ - $delete_interval = wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_cleanup_interval', 1 ); + $delete_interval = $this->deletion_interval(); if ( $delete_interval <= 0 ) { return false; @@ -98,4 +92,14 @@ public function delete_old_rows() { return $db->query( $query ); } + + private function deletion_interval() { + /** + * Filters the interval (in months) to determine when a Preload Fonts(PLF) entry is considered 'old'. + * Old PLF entries are eligible for deletion. By default, a PLF entry is considered old if it hasn't been accessed in the last month. + * + * @param int $delete_interval The interval in months after which an PLF entry is considered old. Default is 1 month. + */ + return wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_cleanup_interval', 1 ); + } } From 83ac02229f8bf29d13be9036c21ebf4f26ad7695 Mon Sep 17 00:00:00 2001 From: Khadreal Date: Wed, 5 Feb 2025 18:51:43 +0100 Subject: [PATCH 8/8] Fixed phpstan error --- inc/Engine/Media/PreloadFonts/AJAX/Controller.php | 15 +++++++++++++++ .../Database/Queries/PreloadFonts.php | 13 ++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php index 751c1e0fb3..2eeeda7aff 100644 --- a/inc/Engine/Media/PreloadFonts/AJAX/Controller.php +++ b/inc/Engine/Media/PreloadFonts/AJAX/Controller.php @@ -143,4 +143,19 @@ public function check_data(): array { return $payload; } + + /** + * Preload Fonts Deletion interval filter. + * + * @return int + */ + public static function deletion_interval(): int { + /** + * Filters the interval (in months) to determine when a Preload Fonts entry is considered 'old'. + * Old PLF entries are eligible for deletion. By default, a PLF entry is considered old if it hasn't been accessed in the last month. + * + * @param int $delete_interval The interval in months after which a preload fonts entry is considered old. Default is 1 month. + */ + return wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_cleanup_interval', 1 ); + } } diff --git a/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php index 969f76752a..72638eb27c 100644 --- a/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php +++ b/inc/Engine/Media/PreloadFonts/Database/Queries/PreloadFonts.php @@ -4,6 +4,7 @@ use WP_Rocket\Engine\Common\PerformanceHints\Database\Queries\AbstractQueries; use WP_Rocket\Engine\Common\PerformanceHints\Database\Queries\QueriesInterface; +use WP_Rocket\Engine\Media\PreloadFonts\AJAX\Controller; use WP_Rocket\Engine\Media\PreloadFonts\Database\Schema\PreloadFonts as PreloadFontsSchema; use WP_Rocket\Engine\Media\PreloadFonts\Database\Rows\PreloadFonts as PreloadFontsRows; class PreloadFonts extends AbstractQueries implements QueriesInterface { @@ -81,7 +82,7 @@ public function delete_old_rows() { return false; } - $delete_interval = $this->deletion_interval(); + $delete_interval = Controller::deletion_interval(); if ( $delete_interval <= 0 ) { return false; @@ -92,14 +93,4 @@ public function delete_old_rows() { return $db->query( $query ); } - - private function deletion_interval() { - /** - * Filters the interval (in months) to determine when a Preload Fonts(PLF) entry is considered 'old'. - * Old PLF entries are eligible for deletion. By default, a PLF entry is considered old if it hasn't been accessed in the last month. - * - * @param int $delete_interval The interval in months after which an PLF entry is considered old. Default is 1 month. - */ - return wpm_apply_filters_typed( 'integer', 'rocket_preload_fonts_cleanup_interval', 1 ); - } }