diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 489737909e586..ae7437a5d41a7 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -501,16 +501,28 @@ public function parents() /** * Returns the children nodes of the current selection. * + * @param string|null $selector An optional CSS selector to filter children + * * @return self * * @throws \InvalidArgumentException When current node is empty + * @throws \RuntimeException If the CssSelector Component is not available and $selector is provided */ - public function children() + public function children(/* string $selector = null */) { + $selector = 0 < \func_num_args() ? func_get_arg(0) : null; + if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } + if (null !== $selector) { + $converter = $this->createCssSelectorConverter(); + $xpath = $converter->toXPath($selector, 'child::'); + + return $this->filterRelativeXPath($xpath); + } + $node = $this->getNode(0)->firstChild; return $this->createSubCrawler($node ? $this->sibling($node) : array()); @@ -691,11 +703,7 @@ public function filterXPath($xpath) */ public function filter($selector) { - if (!class_exists(CssSelectorConverter::class)) { - throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.'); - } - - $converter = new CssSelectorConverter($this->isHtml); + $converter = $this->createCssSelectorConverter(); // The CssSelector already prefixes the selector with descendant-or-self:: return $this->filterRelativeXPath($converter->toXPath($selector)); @@ -1148,4 +1156,16 @@ private function createSubCrawler($nodes) return $crawler; } + + /** + * @throws \RuntimeException If the CssSelector Component is not available + */ + private function createCssSelectorConverter(): CssSelectorConverter + { + if (!class_exists(CssSelectorConverter::class)) { + throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.'); + } + + return new CssSelectorConverter($this->isHtml); + } } diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 9b2d1241949d1..294c2f6bf8f92 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -1004,6 +1004,37 @@ public function testChildren() } } + public function testFilteredChildren() + { + $html = <<<'HTML' + + + +
+
+

+
+
+ +
+ +
+ + +HTML; + + $crawler = new Crawler($html); + $foo = $crawler->filter('#foo'); + + $this->assertEquals(3, $foo->children()->count()); + $this->assertEquals(2, $foo->children('.lorem')->count()); + $this->assertEquals(2, $foo->children('div')->count()); + $this->assertEquals(2, $foo->children('div.lorem')->count()); + $this->assertEquals(1, $foo->children('span')->count()); + $this->assertEquals(1, $foo->children('span.ipsum')->count()); + $this->assertEquals(1, $foo->children('.ipsum')->count()); + } + public function testParents() { $crawler = $this->createTestCrawler()->filterXPath('//li[1]');