[DomCrawler] Add a way to filter direct children

This commit is contained in:
Einenlum 2018-08-17 19:43:46 +02:00 committed by Nicolas Grekas
parent f0168e3ed6
commit f634afdb6f
2 changed files with 57 additions and 6 deletions

View File

@ -501,16 +501,28 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* 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 @@ class Crawler implements \Countable, \IteratorAggregate
*/
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 @@ class Crawler implements \Countable, \IteratorAggregate
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);
}
}

View File

@ -1004,6 +1004,37 @@ HTML;
}
}
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]');