From 804710125e8b9f1a3cc5cf43cb6150164f1ca050 Mon Sep 17 00:00:00 2001 From: Alex Cicovic <23142906+acicovic@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:46:37 +0200 Subject: [PATCH 1/3] Fix integration test errors on PHP 8.5 --- .../ContentHelperPostListStatsTest.php | 2 +- .../Integrations/IntegrationsTest.php | 2 +- tests/Integration/TestCase.php | 4 +-- tests/Traits/TestsReflection.php | 27 ++++++++++++------- tests/Unit/Integrations/IntegrationsTest.php | 5 +++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php b/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php index 5b7d3407ce..7ce13efc8a 100644 --- a/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php +++ b/tests/Integration/ContentHelper/ContentHelperPostListStatsTest.php @@ -1297,7 +1297,7 @@ private function get_parsely_stats_response( // Replace the original API with the mock, using reflection. $api_reflection = new ReflectionProperty( $obj, 'content_api' ); - $api_reflection->setAccessible( true ); + self::make_accessible( $api_reflection ); $api_reflection->setValue( $obj, $api ); return $obj->get_parsely_stats_response(); diff --git a/tests/Integration/Integrations/IntegrationsTest.php b/tests/Integration/Integrations/IntegrationsTest.php index 780a65ad7d..cb1cd278e6 100644 --- a/tests/Integration/Integrations/IntegrationsTest.php +++ b/tests/Integration/Integrations/IntegrationsTest.php @@ -43,7 +43,7 @@ function ( $integrations ) { // Use Reflection to look inside the collection. $reflector_property = ( new ReflectionClass( $integrations ) )->getProperty( 'integrations' ); - $reflector_property->setAccessible( true ); + self::make_accessible( $reflector_property ); /** * Variable. * diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 1f0ede3471..a4f6ff60ae 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -653,7 +653,7 @@ public function get_private_property( string $class_name, string $property_name $reflector = new ReflectionClass( $class_name ); $property = $reflector->getProperty( $property_name ); - $property->setAccessible( true ); + self::make_accessible( $property ); return $property; } @@ -669,7 +669,7 @@ public function get_private_method( string $class_name, string $method ): Reflec $reflector = new ReflectionClass( $class_name ); $method = $reflector->getMethod( $method ); - $method->setAccessible( true ); + self::make_accessible( $method ); return $method; } diff --git a/tests/Traits/TestsReflection.php b/tests/Traits/TestsReflection.php index 4c1484bbe8..845cf94361 100644 --- a/tests/Traits/TestsReflection.php +++ b/tests/Traits/TestsReflection.php @@ -15,6 +15,19 @@ use ReflectionProperty; trait TestsReflection { + /** + * Makes a ReflectionMethod or ReflectionProperty accessible on PHP below + * 8.1, where setAccessible() is required. It is a no-op since PHP 8.1 + * and deprecated since PHP 8.5. + * + * @param ReflectionMethod|ReflectionProperty $reflection The reflection object. + */ + public static function make_accessible( object $reflection ): void { + if ( PHP_VERSION_ID < 80100 ) { + $reflection->setAccessible( true ); + } + } + /** * Gets a method from a class. This should be used when trying to access a * private method for testing. @@ -26,7 +39,7 @@ trait TestsReflection { */ public static function get_method( string $method_name, $class_name = Parsely::class ): \ReflectionMethod { $method = ( new \ReflectionClass( $class_name ) )->getMethod( $method_name ); - $method->setAccessible( true ); + self::make_accessible( $method ); return $method; } @@ -44,7 +57,7 @@ public static function get_method( string $method_name, $class_name = Parsely::c */ public static function get_property( string $property_name, $class_name = Parsely::class ) { $property = ( new \ReflectionClass( $class_name ) )->getProperty( $property_name ); - $property->setAccessible( true ); + self::make_accessible( $property ); return $property; } @@ -53,9 +66,6 @@ public static function get_property( string $property_name, $class_name = Parsel * Overrides the value of a private property on a given object. This is * useful when mocking the internals of a class. * - * Note that the property will no longer be private after setAccessible is - * called. - * * @since 3.17.0 Changed the method signature. * * @param object $obj The object instance on which to set the value. @@ -66,7 +76,7 @@ public static function get_property( string $property_name, $class_name = Parsel */ public static function set_private_property( $obj, string $property_name, $value ): void { $property = ( new \ReflectionClass( $obj ) )->getProperty( $property_name ); - $property->setAccessible( true ); + self::make_accessible( $property ); $property->setValue( $obj, $value ); } @@ -74,9 +84,6 @@ public static function set_private_property( $obj, string $property_name, $value * Overrides the value of a protected property on a given object. This is * useful when mocking the internals of a class. * - * Note that the property will no longer be protected after setAccessible is - * called. - * * @since 3.17.0 * * @param object $obj The object instance on which to set the value. @@ -88,7 +95,7 @@ public static function set_private_property( $obj, string $property_name, $value public static function set_protected_property( $obj, string $property_name, $value ): void { $reflection = new \ReflectionClass( $obj ); $property = $reflection->getProperty( $property_name ); - $property->setAccessible( true ); + self::make_accessible( $property ); $property->setValue( $obj, $value ); } } diff --git a/tests/Unit/Integrations/IntegrationsTest.php b/tests/Unit/Integrations/IntegrationsTest.php index 962c48e6dd..bdd5cfa663 100644 --- a/tests/Unit/Integrations/IntegrationsTest.php +++ b/tests/Unit/Integrations/IntegrationsTest.php @@ -12,6 +12,7 @@ use Parsely\Integrations\Integration; use Parsely\Integrations\Integrations; use Parsely\Parsely; +use Parsely\Tests\Traits\TestsReflection; use ReflectionClass; use Yoast\WPTestUtils\BrainMonkey\TestCase; @@ -19,6 +20,8 @@ * Unit Tests for the Integrations collection. */ final class IntegrationsTest extends TestCase { + use TestsReflection; + /** * Internal variable. * @@ -49,7 +52,7 @@ public function test_an_integration_can_be_registered_to_a_new_Integrations_obje // Use Reflection to look inside the collection. $reflector = new ReflectionClass( $integrations ); $reflector_property = $reflector->getProperty( 'integrations' ); - $reflector_property->setAccessible( true ); + self::make_accessible( $reflector_property ); /** * Variable. * From b35ad839b04bb7f1c85d7c5a7814a7ed419ef193 Mon Sep 17 00:00:00 2001 From: Alex Cicovic <23142906+acicovic@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:26:07 +0200 Subject: [PATCH 2/3] Fix integration test errors on PHP 8.5 - Attempt 2 --- .../HeadlineTestingScriptsTest.php | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/tests/Integration/HeadlineTestingScriptsTest.php b/tests/Integration/HeadlineTestingScriptsTest.php index c38caf1e53..f6926736de 100644 --- a/tests/Integration/HeadlineTestingScriptsTest.php +++ b/tests/Integration/HeadlineTestingScriptsTest.php @@ -30,16 +30,6 @@ final class HeadlineTestingScriptsTest extends TestCase { */ private static $headline_testing; - /** - * One-line script string used in assertions. - * - * @since 3.21.0 - * - * @var string $one_line_script_string Holds the one-line script string. - */ - private static $one_line_script_string = 'src="https://experiments.parsely.com/vip-experiments.js?apiKey=demoaccount.parsely.com&ver=' - . PARSELY_VERSION . '" id="parsely-headline-testing-one-line-js"'; - /** * Advanced script string used in assertions. * @@ -262,7 +252,7 @@ public function test_markup_with_one_line_script_installation_method(): void { $output = (string) ob_get_clean(); // Markup should contain the one-line script and not the advanced script. - self::assertStringContainsString( self::$one_line_script_string, $output ); + $this->assert_one_line_script_in_output( $output ); self::assertStringNotContainsString( self::$advanced_script_string, $output ); } @@ -307,10 +297,7 @@ public function test_markup_with_one_line_script_and_additional_option(): void { $output = (string) ob_get_clean(); // Markup should contain the one-line script with the option's attribute. - self::assertStringContainsString( - self::$one_line_script_string . ' data-enable-live-updates="true"', - $output - ); + $this->assert_one_line_script_in_output( $output, array( 'data-enable-live-updates' => 'true' ) ); self::assertStringNotContainsString( self::$advanced_script_string, $output ); } @@ -410,7 +397,7 @@ public function test_markup_with_advanced_script_installation_method(): void { // Markup should contain the advanced script and not the one-line script. self::assertStringContainsString( self::$advanced_script_string, $output ); - self::assertStringNotContainsString( self::$one_line_script_string, $output ); + $this->assert_one_line_script_not_in_output( $output ); } /** @@ -460,7 +447,7 @@ public function test_markup_with_advanced_script_and_additional_option(): void { // Markup should contain the advanced script with the option's config. self::assertStringContainsString( self::$advanced_script_string, $output ); self::assertStringContainsString( 'enableLiveUpdates: true', $output ); - self::assertStringNotContainsString( self::$one_line_script_string, $output ); + $this->assert_one_line_script_not_in_output( $output ); } /** @@ -484,4 +471,42 @@ private function enable_headline_testing( array $settings = array() ): void { ) ); } + + /** + * Asserts that the one-line headline testing script tag is present in the + * given HTML output. + * + * Uses a regex with lookaheads to verify each attribute independently, + * making the assertion robust against WordPress changes to attribute order + * or HTML entity encoding variants (e.g. & vs &). + * + * @since 3.22.1 + * + * @param string $output The HTML output to search. + * @param array $extra_attrs Optional extra attributes to assert on the tag. + */ + private function assert_one_line_script_in_output( string $output, array $extra_attrs = array() ): void { + $version = preg_quote( PARSELY_VERSION, '~' ); + $lookaheads = '(?=[^>]*\bid="parsely-headline-testing-one-line-js")' + . '(?=[^>]*\bsrc="https://experiments\.parsely\.com/vip-experiments\.js' + . '\?apiKey=demoaccount\.parsely\.com(?:&|&)ver=' . $version . '")'; + + foreach ( $extra_attrs as $attr => $value ) { + $lookaheads .= '(?=[^>]*\b' . preg_quote( $attr, '~' ) . '="' . preg_quote( $value, '~' ) . '")'; + } + + self::assertMatchesRegularExpression( '~ Date: Wed, 4 Mar 2026 14:30:38 +0200 Subject: [PATCH 3/3] make_accessible(): Add since to DocBlock --- tests/Traits/TestsReflection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Traits/TestsReflection.php b/tests/Traits/TestsReflection.php index 845cf94361..7ef1571289 100644 --- a/tests/Traits/TestsReflection.php +++ b/tests/Traits/TestsReflection.php @@ -20,6 +20,8 @@ trait TestsReflection { * 8.1, where setAccessible() is required. It is a no-op since PHP 8.1 * and deprecated since PHP 8.5. * + * @since 3.22.1 + * * @param ReflectionMethod|ReflectionProperty $reflection The reflection object. */ public static function make_accessible( object $reflection ): void {