10000 [DomCrawler] Add a way to filter direct children by Einenlum · Pull Request #28221 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DomCrawler] Add a way to filter direct children #28221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions src/Symfony/Component/DomCrawler/Crawler.php
< 8000 td id="diff-940b51ffa31dedac17aa49cd8e04e44aa8b3747782c7f9f27457b0510587c05dL700" data-line-number="700" class="blob-num blob-num-context js-linkable-line-number">
Original file line number Diff line number Diff line change
Expand Up @@ -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 */)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas shouldn't we also detect cases where getclass($this) !== __CLASS__ (plus the special handling of common mock libraries) to trigger a deprecation warning if they don't have the argument in the child class ? Otherwise, we don't have a continous migration path.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the source, we forgot such deprecations in many places.
Maybe merge as is (the continuous upgrade path would be just not adding this argument in v5)
and fix all the code base at once next?

{
$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());
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
}
}
31 changes: 31 additions & 0 deletions src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,37 @@ public function testChildren()
}
}

public function testFilteredChildren()
{
$html = <<<'HTML'
<!DOCTYPE html>
<html lang="en">
<body>
<div id="foo">
<div class="lorem">
<p class="lorem"></p>
</div>
<div class="lorem">
<span class="lorem"></span>
</div>
<span class="ipsum"></span>
</div>
</body>
</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]');
Expand Down
0