Prevent adding non-DOMElement elements in DomCrawler

Many methods of the DomCrawler component are relying on the DOMElement
API, not only on the DOMNode API.
This commit is contained in:
Christophe Coevoet 2015-10-02 01:09:43 +02:00
parent 99745e12a7
commit 9f362a12f6
2 changed files with 35 additions and 8 deletions

View File

@ -308,10 +308,14 @@ class Crawler extends \SplObjectStorage
public function addNode(\DOMNode $node) public function addNode(\DOMNode $node)
{ {
if ($node instanceof \DOMDocument) { if ($node instanceof \DOMDocument) {
parent::attach($node->documentElement); $node = $node->documentElement;
} else {
parent::attach($node);
} }
if (!$node instanceof \DOMElement) {
throw new \InvalidArgumentException(sprintf('Nodes set in a Crawler must be DOMElement or DOMDocument instances, "%s" given.', get_class($node)));
}
parent::attach($node);
} }
// Serializing and unserializing a crawler creates DOM objects in a corrupted state. DOM elements are not properly serializable. // Serializing and unserializing a crawler creates DOM objects in a corrupted state. DOM elements are not properly serializable.
@ -974,7 +978,12 @@ class Crawler extends \SplObjectStorage
foreach ($this as $node) { foreach ($this as $node) {
$domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes);
$crawler->add($domxpath->query($xpath, $node));
foreach ($domxpath->query($xpath, $node) as $subNode) {
if ($subNode->nodeType === 1) {
$crawler->add($subNode);
}
}
} }
return $crawler; return $crawler;

View File

@ -20,7 +20,10 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
$crawler = new Crawler(); $crawler = new Crawler();
$this->assertCount(0, $crawler, '__construct() returns an empty crawler'); $this->assertCount(0, $crawler, '__construct() returns an empty crawler');
$crawler = new Crawler(new \DOMNode()); $doc = new \DOMDocument();
$node = $doc->createElement('test');
$crawler = new Crawler($node);
$this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); $this->assertCount(1, $crawler, '__construct() takes a node as a first argument');
} }
@ -37,6 +40,7 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
$crawler->add($this->createNodeList()); $crawler->add($this->createNodeList());
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList'); $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList');
$list = array();
foreach ($this->createNodeList() as $node) { foreach ($this->createNodeList() as $node) {
$list[] = $node; $list[] = $node;
} }
@ -56,12 +60,22 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
*/ */
public function testAddInvalidNode() public function testAddInvalidType()
{ {
$crawler = new Crawler(); $crawler = new Crawler();
$crawler->add(1); $crawler->add(1);
} }
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Nodes set in a Crawler must be DOMElement or DOMDocument instances, "DOMNode" given.
*/
public function testAddInvalidNode()
{
$crawler = new Crawler();
$crawler->add(new \DOMNode());
}
/** /**
* @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
*/ */
@ -267,6 +281,7 @@ EOF
*/ */
public function testAddNodes() public function testAddNodes()
{ {
$list = array();
foreach ($this->createNodeList() as $node) { foreach ($this->createNodeList() as $node) {
$list[] = $node; $list[] = $node;
} }
@ -290,7 +305,10 @@ EOF
public function testClear() public function testClear()
{ {
$crawler = new Crawler(new \DOMNode()); $doc = new \DOMDocument();
$node = $doc->createElement('test');
$crawler = new Crawler($node);
$crawler->clear(); $crawler->clear();
$this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler'); $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler');
} }
@ -526,7 +544,7 @@ EOF
public function testFilterXPathWithAttributeAxisAfterElementAxis() public function testFilterXPathWithAttributeAxisAfterElementAxis()
{ {
$this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis'); $this->assertCount(0, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis');
} }
public function testFilterXPathWithChildAxis() public function testFilterXPathWithChildAxis()