diff --git a/inc/Engine/Optimization/DynamicLists/DynamicLists.php b/inc/Engine/Optimization/DynamicLists/DynamicLists.php index 8ffbc97681..1734730889 100644 --- a/inc/Engine/Optimization/DynamicLists/DynamicLists.php +++ b/inc/Engine/Optimization/DynamicLists/DynamicLists.php @@ -302,4 +302,15 @@ public function get_exclude_js_templates(): array { return $lists->exclude_js_template ?? []; } + + /** + * Get the lazy rendered exclusions. + * + * @return array + */ + public function get_lrc_exclusions(): array { + $lists = $this->providers['defaultlists']->data_manager->get_lists(); + + return $lists->lazy_rendering_exclusions ?? []; + } } diff --git a/inc/Engine/Optimization/DynamicLists/Subscriber.php b/inc/Engine/Optimization/DynamicLists/Subscriber.php index afb9425a1e..9948d47b05 100644 --- a/inc/Engine/Optimization/DynamicLists/Subscriber.php +++ b/inc/Engine/Optimization/DynamicLists/Subscriber.php @@ -43,6 +43,7 @@ public static function get_subscribed_events() { 'rocket_exclude_js' => 'add_js_exclude_files', 'rocket_plugins_to_deactivate' => 'add_incompatible_plugins_to_deactivate', 'rocket_staging_list' => 'add_staging_exclusions', + 'rocket_lrc_exclusions' => 'add_lrc_exclusions', ]; } @@ -213,4 +214,15 @@ public function add_incompatible_plugins_to_deactivate( $plugins = [] ): array { public function add_staging_exclusions( $stagings = [] ): array { return array_merge( (array) $stagings, (array) $this->dynamic_lists->get_stagings() ); } + + /** + * Add the LRC exclusions to the array + * + * @param array $exclusions Array of LRC exclusions. + * + * @return array + */ + public function add_lrc_exclusions( $exclusions ): array { + return array_merge( (array) $exclusions, $this->dynamic_lists->get_lrc_exclusions() ); + } } diff --git a/inc/Engine/Optimization/LazyRenderContent/Frontend/Controller.php b/inc/Engine/Optimization/LazyRenderContent/Frontend/Controller.php index 6815b6f3cd..739124ceb4 100644 --- a/inc/Engine/Optimization/LazyRenderContent/Frontend/Controller.php +++ b/inc/Engine/Optimization/LazyRenderContent/Frontend/Controller.php @@ -145,10 +145,29 @@ public function add_hashes( $html ) { $processor = wpm_apply_filters_typed( 'string', 'rocket_lrc_processor', 'dom' ); $this->processor->set_processor( $processor ); + $this->processor->get_processor()->set_exclusions( $this->get_exclusions() ); return $this->processor->get_processor()->add_hashes( $html ); } + /** + * Get the list of patterns to exclude from hash injection. + * + * @return string[] + */ + private function get_exclusions(): array { + /** + * Filters the list of patterns to exclude from hash injection. + * + * @since 3.17.0.2 + * + * @param string[] $exclusions The list of patterns to exclude from hash injection. + */ + $exclusions = wpm_apply_filters_typed( 'string[]', 'rocket_lrc_exclusions', [] ); + + return $exclusions; + } + /** * Add custom data like the List of elements to be considered for optimization. * diff --git a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Dom.php b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Dom.php index f846a1f462..546e8872cf 100644 --- a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Dom.php +++ b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Dom.php @@ -7,7 +7,6 @@ use WP_Rocket\Logger\Logger; class Dom implements ProcessorInterface { - use HelperTrait; /** @@ -28,6 +27,46 @@ class Dom implements ProcessorInterface { */ private $max_hashes; + /** + * Array of patterns to exclude from hash injection. + * + * @since 3.17.0.2 + * + * @var array + */ + private $exclusions = []; + + /** + * Sets the exclusions list + * + * @param string[] $exclusions The list of patterns to exclude from hash injection. + * + * @return void + */ + public function set_exclusions( $exclusions ): void { + $this->exclusions = $exclusions; + } + + /** + * Gets the exclusions pattern + * + * @return string + */ + private function get_exclusions_pattern(): string { + if ( empty( $this->exclusions ) ) { + return ''; + } + + $exclusions = array_map( + function ( $exclusion ) { + return preg_quote( $exclusion, '/' ); + }, + $this->exclusions + ); + + return implode( '|', $exclusions ); + } + /** * Add hashes to the HTML elements * @@ -107,6 +146,16 @@ private function add_hash_to_element( $element, $depth, $html ) { $child_html = $child->ownerDocument->saveHTML( $child ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $opening_tag_html = strstr( $child_html, '>', true ) . '>'; + $exclusions_pattern = $this->get_exclusions_pattern(); + + if ( + ! empty( $exclusions_pattern ) + && + preg_match( '/(' . $exclusions_pattern . ')/i', $opening_tag_html ) + ) { + continue; + } + $hash = md5( $opening_tag_html . $this->count ); ++$this->count; diff --git a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/ProcessorInterface.php b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/ProcessorInterface.php index 1a071cee7b..5b2b3bf637 100644 --- a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/ProcessorInterface.php +++ b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/ProcessorInterface.php @@ -12,4 +12,13 @@ interface ProcessorInterface { * @return string */ public function add_hashes( $html ); + + /** + * Sets the exclusions list + * + * @param string[] $exclusions The list of patterns to exclude from hash injection. + * + * @return void + */ + public function set_exclusions( array $exclusions ): void; } diff --git a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Regex.php b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Regex.php index 97252455fd..56487bb3a4 100644 --- a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Regex.php +++ b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/Regex.php @@ -27,6 +27,26 @@ class Regex implements ProcessorInterface { */ private $max_hashes; + /** + * Array of patterns to exclude from hash injection. + * + * @since 3.17.0.2 + * + * @var array + */ + private $exclusions; + + /** + * Sets the exclusions list + * + * @param string[] $exclusions The list of patterns to exclude from hash injection. + * + * @return void + */ + public function set_exclusions( $exclusions ): void { + $this->exclusions = $exclusions; + } + /** * Add hashes to the HTML elements * diff --git a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/SimpleHtmlDom.php b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/SimpleHtmlDom.php index 746a9300a7..d7a3f8cdf7 100644 --- a/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/SimpleHtmlDom.php +++ b/inc/Engine/Optimization/LazyRenderContent/Frontend/Processor/SimpleHtmlDom.php @@ -30,6 +30,26 @@ class SimpleHtmlDom implements ProcessorInterface { */ private $max_hashes; + /** + * Array of patterns to exclude from hash injection. + * + * @since 3.17.0.2 + * + * @var array + */ + private $exclusions; + + /** + * Sets the exclusions list + * + * @param string[] $exclusions The list of patterns to exclude from hash injection. + * + * @return void + */ + public function set_exclusions( $exclusions ): void { + $this->exclusions = $exclusions; + } + /** * Add hashes to the HTML elements * diff --git a/tests/Fixtures/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php b/tests/Fixtures/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php new file mode 100644 index 0000000000..c2d9beafc4 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php @@ -0,0 +1,32 @@ + [ + 'config' => [ + 'dynamic_lists' => (object) [ + 'lazy_rendering_exclusions' => [ + 'a' + ] + ], + 'exclusions' => ['p', 'div'] + ], + 'expected' => [ + 'p', + 'div', + 'a' + ], + ], + 'shouldReturnUpdatedArrayWhenOriginalEmpty' => [ + 'config' => [ + 'dynamic_lists' => (object) [ + 'lazy_rendering_exclusions' => [ + ] + ], + 'exclusions' => ['p', 'div'] + ], + 'expected' => [ + 'p', + 'div', + ], + ], +]; diff --git a/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php b/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php index 4398813895..7ba4a5967f 100644 --- a/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php +++ b/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php @@ -55,6 +55,30 @@ 'expected' => [ 'html' => file_get_contents( WP_ROCKET_TESTS_FIXTURES_DIR . '/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/long_expected_150_hashes.php' ), ] - ] + ], + 'shouldNotAddHashesToExclusions' => [ + 'config' => [ + 'row' => [ + 'url' => 'http://example.org/', + 'is_mobile' => 0, + 'below_the_fold' => json_encode( + [ + "93548b90aa8f4989f7198144479055dc", + "7b16eca0652d4703f83ba63e304f2030", + "737184bbad8e65d0172a89cc68a46107", + "8a4ef50742cf3456f9db6425e16930dc" + ] + ), + 'status' => 'completed' + ], + 'html' => file_get_contents( WP_ROCKET_TESTS_FIXTURES_DIR . '/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/original.php' ), + 'exclusions' => [ + 'footer' + ], + ], + 'expected' => [ + 'html' => file_get_contents( WP_ROCKET_TESTS_FIXTURES_DIR . '/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/expected_exclusions.php' ), + ] + ], ] ]; diff --git a/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/expected_exclusions.php b/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/expected_exclusions.php new file mode 100644 index 0000000000..97a25999e9 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/html/expected_exclusions.php @@ -0,0 +1,67 @@ + + + + + + + + + + +
400px +
800 px +































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































+ 1800 px
Testing something
+
+
+ +
+
+
+
+ This is a class with margin-top set to 3000px +
+
+
+
+ +
+
+
+ This is a class with margin-top set to 1800px +
+
+
+ + + + diff --git a/tests/Integration/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php b/tests/Integration/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php new file mode 100644 index 0000000000..79ba017701 --- /dev/null +++ b/tests/Integration/inc/Engine/Optimization/DynamicLists/Subscriber/addLrcExclusions.php @@ -0,0 +1,46 @@ +unregisterAllCallbacksExcept( 'rocket_lrc_exclusions', 'add_lrc_exclusions', 10 ); + } + + public function tear_down() { + remove_filter( 'pre_transient_wpr_dynamic_lists', [$this, 'set_dynamic_list'] ); + + $this->restoreWpHook( 'rocket_lrc_exclusions' ); + + parent::tear_down(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldReturnExpected( $config, $expected ) { + $this->dynamic_lists = $config['dynamic_lists']; + add_filter( 'pre_transient_wpr_dynamic_lists', [$this, 'set_dynamic_list'], 12 ); + + $this->assertSame( + $expected, + wpm_apply_filters_typed('array', 'rocket_lrc_exclusions', $config['exclusions'] ) + ); + } + + public function set_dynamic_list() { + return $this->dynamic_lists; + } +} diff --git a/tests/Integration/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php b/tests/Integration/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php index 412f15a940..699cf7caf5 100644 --- a/tests/Integration/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php +++ b/tests/Integration/inc/Engine/Optimization/LazyRenderContent/Frontend/Subscriber/add_hashes.php @@ -30,6 +30,7 @@ public function set_up() { $this->max_hashes = null; $this->unregisterAllCallbacksExcept( 'rocket_performance_hints_buffer', 'add_hashes', 16 ); + } public function tear_down() { @@ -47,12 +48,16 @@ public function testShouldWorkAsExpected( $config, $expected ) { self::addLrc( $config['row'] ); add_filter( 'rocket_lrc_optimization', '__return_true' ); + add_filter( 'rocket_lrc_exclusions', function() use ($config) { + return $config['exclusions'] ?? []; + }); + if ( isset( $config['max_hashes'] ) ) { $this->max_hashes = $config['max_hashes']; add_filter( 'rocket_lrc_max_hashes', [ $this, 'set_lrc_max_hashes' ] ); } - + $this->assertSame( $expected['html'],