From 5fffe8f78bb7be16ceee230dfd6fc4a2eec008a8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Jun 2025 16:08:14 +0200 Subject: [PATCH 01/13] Allow Symfony ^8.0 --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index e145984..b2e6761 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,13 @@ ], "require": { "php": ">=8.2", - "symfony/dom-crawler": "^6.4|^7.0" + "symfony/dom-crawler": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, From 53f90664a264ea20cdcfe187192a806466e95c76 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Jun 2025 17:50:55 +0200 Subject: [PATCH 02/13] Bump Symfony 8 to PHP >= 8.4 --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index b2e6761..b9e526f 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": ">=8.2", - "symfony/dom-crawler": "^6.4|^7.0|^8.0" + "php": ">=8.4", + "symfony/dom-crawler": "^7.4|^8.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/css-selector": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, From 89c4b4394a1ccb2ec652f299e63969d512606995 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 18:31:05 +0200 Subject: [PATCH 03/13] Enforce return types on all components --- AbstractBrowser.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index 1269fcb..e294c44 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -413,13 +413,11 @@ public function request(string $method, string $uri, array $parameters = [], arr * * @psalm-param TRequest $request * - * @return object - * * @psalm-return TResponse * * @throws \RuntimeException When processing returns exit code */ - protected function doRequestInProcess(object $request) + protected function doRequestInProcess(object $request): object { $deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec'); putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$deprecationsFile); @@ -452,11 +450,9 @@ protected function doRequestInProcess(object $request) * * @psalm-param TRequest $request * - * @return object - * * @psalm-return TResponse */ - abstract protected function doRequest(object $request); + abstract protected function doRequest(object $request): object; /** * Returns the script to execute when the request must be insulated. @@ -465,11 +461,9 @@ abstract protected function doRequest(object $request); * * @param object $request An origin request instance * - * @return string - * * @throws LogicException When this abstract class is not implemented */ - protected function getScript(object $request) + protected function getScript(object $request): string { throw new LogicException('To insulate requests, you need to override the getScript() method.'); } @@ -477,11 +471,9 @@ protected function getScript(object $request) /** * Filters the BrowserKit request to the origin one. * - * @return object - * * @psalm-return TRequest */ - protected function filterRequest(Request $request) + protected function filterRequest(Request $request): object { return $request; } @@ -490,10 +482,8 @@ protected function filterRequest(Request $request) * Filters the origin response to the BrowserKit one. * * @psalm-param TResponse $response - * - * @return Response */ - protected function filterResponse(object $response) + protected function filterResponse(object $response): Response { return $response; } From 8c73fd07ced6fb3c4b293d83ee6b7746f6cfb453 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Tue, 24 Jun 2025 19:24:06 -0300 Subject: [PATCH 04/13] [BrowserKit] Add `isFirstPage()` and `isLastPage()` methods to History --- CHANGELOG.md | 5 +++++ History.php | 20 ++++++++++++++++++-- Tests/AbstractBrowserTest.php | 4 ++++ Tests/HistoryTest.php | 30 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b05e307..2437bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Add `isFirstPage()` and `isLastPage()` methods to the History class for checking navigation boundaries + 6.4 --- diff --git a/History.php b/History.php index 8fe4f2b..5926b40 100644 --- a/History.php +++ b/History.php @@ -50,6 +50,22 @@ public function isEmpty(): bool return 0 === \count($this->stack); } + /** + * Returns true if the stack is on the first page. + */ + public function isFirstPage(): bool + { + return $this->position < 1; + } + + /** + * Returns true if the stack is on the last page. + */ + public function isLastPage(): bool + { + return $this->position > \count($this->stack) - 2; + } + /** * Goes back in the history. * @@ -57,7 +73,7 @@ public function isEmpty(): bool */ public function back(): Request { - if ($this->position < 1) { + if ($this->isFirstPage()) { throw new LogicException('You are already on the first page.'); } @@ -71,7 +87,7 @@ public function back(): Request */ public function forward(): Request { - if ($this->position > \count($this->stack) - 2) { + if ($this->isLastPage()) { throw new LogicException('You are already on the last page.'); } diff --git a/Tests/AbstractBrowserTest.php b/Tests/AbstractBrowserTest.php index dd7f8e4..096c07c 100644 --- a/Tests/AbstractBrowserTest.php +++ b/Tests/AbstractBrowserTest.php @@ -701,6 +701,8 @@ public function testBack() $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->back() keeps files'); $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->back() keeps $_SERVER'); $this->assertSame($content, $client->getRequest()->getContent(), '->back() keeps content'); + $this->assertTrue($client->getHistory()->isFirstPage()); + $this->assertFalse($client->getHistory()->isLastPage()); } public function testForward() @@ -741,6 +743,8 @@ public function testBackAndFrowardWithRedirects() $client->forward(); $this->assertSame('http://www.example.com/redirected', $client->getRequest()->getUri(), '->forward() goes forward in the history skipping redirects'); + $this->assertTrue($client->getHistory()->isLastPage()); + $this->assertFalse($client->getHistory()->isFirstPage()); } public function testReload() diff --git a/Tests/HistoryTest.php b/Tests/HistoryTest.php index 8f3cfae..1e1acb2 100644 --- a/Tests/HistoryTest.php +++ b/Tests/HistoryTest.php @@ -99,4 +99,34 @@ public function testForward() $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->forward() returns the next request in the history'); } + + public function testIsFirstPage() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + $history->add(new Request('http://www.example1.com/', 'get')); + $history->back(); + + $this->assertFalse($history->isLastPage()); + $this->assertTrue($history->isFirstPage()); + } + + public function testIsLastPage() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + $history->add(new Request('http://www.example1.com/', 'get')); + + $this->assertTrue($history->isLastPage()); + $this->assertFalse($history->isFirstPage()); + } + + public function testIsFirstAndLastPage() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + + $this->assertTrue($history->isLastPage()); + $this->assertTrue($history->isFirstPage()); + } } From 10daaadea5baef40f4823f52af994257c1b181f6 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Mon, 30 Jun 2025 00:17:16 -0300 Subject: [PATCH 05/13] [BrowserKit] Add PHPUnit constraints: `BrowserHistoryIsOnFirstPage` and `BrowserHistoryIsOnLastPage` --- CHANGELOG.md | 1 + .../BrowserHistoryIsOnFirstPage.php | 37 +++++++++++++++++++ .../Constraint/BrowserHistoryIsOnLastPage.php | 37 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 Test/Constraint/BrowserHistoryIsOnFirstPage.php create mode 100644 Test/Constraint/BrowserHistoryIsOnLastPage.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 2437bbc..d078c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `isFirstPage()` and `isLastPage()` methods to the History class for checking navigation boundaries + * Add PHPUnit constraints: `BrowserHistoryIsOnFirstPage` and `BrowserHistoryIsOnLastPage` 6.4 --- diff --git a/Test/Constraint/BrowserHistoryIsOnFirstPage.php b/Test/Constraint/BrowserHistoryIsOnFirstPage.php new file mode 100644 index 0000000..be5be9a --- /dev/null +++ b/Test/Constraint/BrowserHistoryIsOnFirstPage.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\BrowserKit\AbstractBrowser; + +final class BrowserHistoryIsOnFirstPage extends Constraint +{ + public function toString(): string + { + return 'is on the first page'; + } + + protected function matches($other): bool + { + if (!$other instanceof AbstractBrowser) { + throw new \LogicException('Can only test on an AbstractBrowser instance.'); + } + + return $other->getHistory()->isFirstPage(); + } + + protected function failureDescription($other): string + { + return 'the Browser history '.$this->toString(); + } +} diff --git a/Test/Constraint/BrowserHistoryIsOnLastPage.php b/Test/Constraint/BrowserHistoryIsOnLastPage.php new file mode 100644 index 0000000..38658a0 --- /dev/null +++ b/Test/Constraint/BrowserHistoryIsOnLastPage.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\BrowserKit\AbstractBrowser; + +final class BrowserHistoryIsOnLastPage extends Constraint +{ + public function toString(): string + { + return 'is on the last page'; + } + + protected function matches($other): bool + { + if (!$other instanceof AbstractBrowser) { + throw new \LogicException('Can only test on an AbstractBrowser instance.'); + } + + return $other->getHistory()->isLastPage(); + } + + protected function failureDescription($other): string + { + return 'the Browser history '.$this->toString(); + } +} From 67faa9c48ca6b7b1d4346c504d35a1f722e2efbb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 8 Jul 2025 11:08:29 +0200 Subject: [PATCH 06/13] Various CS fixes --- AbstractBrowser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index 1269fcb..68cc417 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -461,10 +461,10 @@ abstract protected function doRequest(object $request); /** * Returns the script to execute when the request must be insulated. * - * @psalm-param TRequest $request - * * @param object $request An origin request instance * + * @psalm-param TRequest $request + * * @return string * * @throws LogicException When this abstract class is not implemented From 60f6518f77d4f6b4533f7bb91953354e52fd55f7 Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sat, 12 Jul 2025 15:55:19 +0200 Subject: [PATCH 07/13] optimize `in_array` calls --- AbstractBrowser.php | 2 +- HttpBrowser.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index 68cc417..8d30ed3 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -567,7 +567,7 @@ public function followRedirect(): Crawler $request = $this->internalRequest; - if (\in_array($this->internalResponse->getStatusCode(), [301, 302, 303])) { + if (\in_array($this->internalResponse->getStatusCode(), [301, 302, 303], true)) { $method = 'GET'; $files = []; $content = null; diff --git a/HttpBrowser.php b/HttpBrowser.php index 4f04442..c7ae5f6 100644 --- a/HttpBrowser.php +++ b/HttpBrowser.php @@ -64,7 +64,7 @@ protected function doRequest(object $request): Response */ private function getBodyAndExtraHeaders(Request $request, array $headers): array { - if (\in_array($request->getMethod(), ['GET', 'HEAD']) && !isset($headers['content-type'])) { + if (\in_array($request->getMethod(), ['GET', 'HEAD'], true) && !isset($headers['content-type'])) { return ['', []]; } From e68f766164ca16c668dae5e4dd120bc17bb99a6f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 9 Oct 2024 11:06:51 +0200 Subject: [PATCH 08/13] run tests using PHPUnit 11.5 --- phpunit.xml.dist | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 747ed25..2272dfe 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,10 +1,11 @@ @@ -18,7 +19,7 @@ - + ./ @@ -27,5 +28,9 @@ ./Tests ./vendor - + + + + + From 484063bd005fa550313fb68682044133139e0ed7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 31 Jul 2025 14:36:46 +0200 Subject: [PATCH 09/13] replace PHPUnit annotations with attributes --- Tests/AbstractBrowserTest.php | 10 ++++------ Tests/CookieJarTest.php | 5 ++--- Tests/CookieTest.php | 9 +++------ Tests/HttpBrowserTest.php | 9 +++------ Tests/ResponseTest.php | 5 ++--- 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/Tests/AbstractBrowserTest.php b/Tests/AbstractBrowserTest.php index 096c07c..87290de 100644 --- a/Tests/AbstractBrowserTest.php +++ b/Tests/AbstractBrowserTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\BrowserKit\Tests; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; use Symfony\Component\BrowserKit\CookieJar; use Symfony\Component\BrowserKit\Exception\BadMethodCallException; @@ -650,9 +652,7 @@ public function testFollowRedirectDropPostMethod() } } - /** - * @dataProvider getTestsForMetaRefresh - */ + #[DataProvider('getTestsForMetaRefresh')] public function testFollowMetaRefresh(string $content, string $expectedEndingUrl, bool $followMetaRefresh = true) { $client = $this->getBrowser(); @@ -776,9 +776,7 @@ public function testRestart() $this->assertSame([], $client->getCookieJar()->all(), '->restart() clears the cookies'); } - /** - * @runInSeparateProcess - */ + #[RunInSeparateProcess] public function testInsulatedRequests() { $client = $this->getBrowser(); diff --git a/Tests/CookieJarTest.php b/Tests/CookieJarTest.php index 2e456b8..fc2a6f3 100644 --- a/Tests/CookieJarTest.php +++ b/Tests/CookieJarTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\BrowserKit\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\BrowserKit\Cookie; use Symfony\Component\BrowserKit\CookieJar; @@ -114,9 +115,7 @@ public function testUpdateFromSetCookieWithMultipleCookies() $this->assertEquals($timestamp, $phpCookie->getExpiresTime()); } - /** - * @dataProvider provideAllValuesValues - */ + #[DataProvider('provideAllValuesValues')] public function testAllValues($uri, $values) { $cookieJar = new CookieJar(); diff --git a/Tests/CookieTest.php b/Tests/CookieTest.php index 5d42ceb..a8a6f52 100644 --- a/Tests/CookieTest.php +++ b/Tests/CookieTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\BrowserKit\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\BrowserKit\Cookie; use Symfony\Component\BrowserKit\Exception\InvalidArgumentException; @@ -36,9 +37,7 @@ public function testToString() $this->assertEquals('foo=bar; expires=Thu, 01 Jan 1970 00:00:02 GMT; path=/; secure; httponly; samesite=lax', (string) $cookie); } - /** - * @dataProvider getTestsForToFromString - */ + #[DataProvider('getTestsForToFromString')] public function testToFromString($cookie, $url = null) { $this->assertEquals($cookie, (string) Cookie::fromString($cookie, $url)); @@ -65,9 +64,7 @@ public function testFromStringIgnoreSecureFlag() $this->assertFalse(Cookie::fromString('foo=bar; secure', 'http://example.com/')->isSecure()); } - /** - * @dataProvider getExpireCookieStrings - */ + #[DataProvider('getExpireCookieStrings')] public function testFromStringAcceptsSeveralExpiresDateFormats($cookie) { $this->assertEquals(1596185377, Cookie::fromString($cookie)->getExpiresTime()); diff --git a/Tests/HttpBrowserTest.php b/Tests/HttpBrowserTest.php index 3a2547d..322f1c5 100644 --- a/Tests/HttpBrowserTest.php +++ b/Tests/HttpBrowserTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\BrowserKit\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\BrowserKit\CookieJar; use Symfony\Component\BrowserKit\History; use Symfony\Component\BrowserKit\HttpBrowser; @@ -26,9 +27,7 @@ public function getBrowser(array $server = [], ?History $history = null, ?Cookie return new TestHttpClient($server, $history, $cookieJar); } - /** - * @dataProvider validContentTypes - */ + #[DataProvider('validContentTypes')] public function testRequestHeaders(array $requestArguments, array $expectedArguments) { $client = $this->createMock(HttpClientInterface::class); @@ -186,9 +185,7 @@ public function testMultiPartRequestWithAdditionalParametersOfTheSameName() ]); } - /** - * @dataProvider forwardSlashesRequestPathProvider - */ + #[DataProvider('forwardSlashesRequestPathProvider')] public function testMultipleForwardSlashesRequestPath(string $requestPath) { $client = $this->createMock(HttpClientInterface::class); diff --git a/Tests/ResponseTest.php b/Tests/ResponseTest.php index f68d0b5..37b4b46 100644 --- a/Tests/ResponseTest.php +++ b/Tests/ResponseTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\BrowserKit\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\BrowserKit\Exception\JsonException; use Symfony\Component\BrowserKit\Response; @@ -90,9 +91,7 @@ public function testToArray() ], $response->toArray(), '->toArray returns an array representation of json content'); } - /** - * @dataProvider provideInvalidJson - */ + #[DataProvider('provideInvalidJson')] public function testToArrayThrowsErrorOnInvalidJson(string $data) { $response = new Response($data); From 2cbbb9e8bf74022f9ece031399b752bf2cfa64c6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 4 Aug 2025 09:53:42 +0200 Subject: [PATCH 10/13] CS fixes --- AbstractBrowser.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index 8d30ed3..fefd7c1 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -19,6 +19,7 @@ use Symfony\Component\DomCrawler\Form; use Symfony\Component\DomCrawler\Link; use Symfony\Component\Process\PhpProcess; +use Symfony\Component\Process\Process; /** * Simulates a browser. @@ -114,7 +115,7 @@ public function getMaxRedirects(): int */ public function insulate(bool $insulated = true): void { - if ($insulated && !class_exists(\Symfony\Component\Process\Process::class)) { + if ($insulated && !class_exists(Process::class)) { throw new LogicException('Unable to isolate requests as the Symfony Process Component is not installed. Try running "composer require symfony/process".'); } From 7f5f0fb313cd77fa32a934ce0d59c7f2ba7c9ad2 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 10 Aug 2025 00:28:14 +0200 Subject: [PATCH 11/13] chore: heredoc indentation as of PHP 7.3 https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc --- Tests/TestClient.php | 8 ++++---- Tests/TestHttpClient.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/TestClient.php b/Tests/TestClient.php index dc27e3b..b70e002 100644 --- a/Tests/TestClient.php +++ b/Tests/TestClient.php @@ -47,11 +47,11 @@ protected function getScript(object $request): string $path = $r->getFileName(); return <<nextScript); -EOF; + echo serialize($this->nextScript); + EOF; } } diff --git a/Tests/TestHttpClient.php b/Tests/TestHttpClient.php index 3d0a354..9de38eb 100644 --- a/Tests/TestHttpClient.php +++ b/Tests/TestHttpClient.php @@ -70,11 +70,11 @@ protected function getScript(object $request): string $path = $r->getFileName(); return <<nextScript); -EOF; + echo serialize($this->nextScript); + EOF; } } From abec2fa9e2027d0d63cd8df46d1994bb7b74edc7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 20 Aug 2025 17:25:58 +0200 Subject: [PATCH 12/13] [DomCrawler] Use the native HTM5 parser on PHP 8.4 --- AbstractBrowser.php | 7 +++++++ CHANGELOG.md | 1 + composer.json | 1 + 3 files changed, 9 insertions(+) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index fefd7c1..bcb3b4b 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -46,6 +46,7 @@ abstract class AbstractBrowser /** @psalm-var TResponse */ protected object $response; protected Crawler $crawler; + /** @deprecated since Symfony 7.4, to be removed in Symfony 8 */ protected bool $useHtml5Parser = true; protected bool $insulated = false; protected ?string $redirect; @@ -204,10 +205,16 @@ public function getCrawler(): Crawler /** * Sets whether parsing should be done using "masterminds/html5". * + * @deprecated since Symfony 7.4, Symfony 8 will unconditionally use the native HTML5 parser + * * @return $this */ public function useHtml5Parser(bool $useHtml5Parser): static { + if (\PHP_VERSION_ID >= 80400) { + trigger_deprecation('symfony/browser-kit', '7.4', 'Method "%s()" is deprecated. Symfony 8 will unconditionally use the native HTML5 parser.', __METHOD__); + } + $this->useHtml5Parser = $useHtml5Parser; return $this; diff --git a/CHANGELOG.md b/CHANGELOG.md index d078c10..ad57c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `isFirstPage()` and `isLastPage()` methods to the History class for checking navigation boundaries * Add PHPUnit constraints: `BrowserHistoryIsOnFirstPage` and `BrowserHistoryIsOnLastPage` + * Deprecate `AbstractBrowser::useHtml5Parser()`; Symfony 8 will unconditionally use the native HTML5 parser 6.4 --- diff --git a/composer.json b/composer.json index b2e6761..a123577 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/dom-crawler": "^6.4|^7.0|^8.0" }, "require-dev": { From 33746d804655b7b9cf950cfcde6d5d69fcff2ca1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 21 Aug 2025 09:45:18 +0200 Subject: [PATCH 13/13] [DomCrawler] Always parse according to HTML5 rules thanks to the native DOM parser --- AbstractBrowser.php | 22 +--------------------- CHANGELOG.md | 5 +++++ 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/AbstractBrowser.php b/AbstractBrowser.php index 76b00a3..9f0675b 100644 --- a/AbstractBrowser.php +++ b/AbstractBrowser.php @@ -46,8 +46,6 @@ abstract class AbstractBrowser /** @psalm-var TResponse */ protected object $response; protected Crawler $crawler; - /** @deprecated since Symfony 7.4, to be removed in Symfony 8 */ - protected bool $useHtml5Parser = true; protected bool $insulated = false; protected ?string $redirect; protected bool $followRedirects = true; @@ -202,24 +200,6 @@ public function getCrawler(): Crawler return $this->crawler ?? throw new BadMethodCallException(\sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } - /** - * Sets whether parsing should be done using "masterminds/html5". - * - * @deprecated since Symfony 7.4, Symfony 8 will unconditionally use the native HTML5 parser - * - * @return $this - */ - public function useHtml5Parser(bool $useHtml5Parser): static - { - if (\PHP_VERSION_ID >= 80400) { - trigger_deprecation('symfony/browser-kit', '7.4', 'Method "%s()" is deprecated. Symfony 8 will unconditionally use the native HTML5 parser.', __METHOD__); - } - - $this->useHtml5Parser = $useHtml5Parser; - - return $this; - } - /** * Returns the current BrowserKit Response instance. */ @@ -507,7 +487,7 @@ protected function createCrawlerFromContent(string $uri, string $content, string return null; } - $crawler = new Crawler(null, $uri, null, $this->useHtml5Parser); + $crawler = new Crawler(null, $uri, null); $crawler->addContent($content, $type); return $crawler; diff --git a/CHANGELOG.md b/CHANGELOG.md index ad57c95..eb4b427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +8.0 +--- + + * Remove `AbstractBrowser::useHtml5Parser()`; the native HTML5 parser is used unconditionally + 7.4 ---