From 43a771614c966e2db68a44ce7fa630b87e17b1db Mon Sep 17 00:00:00 2001 From: Robbert Klarenbeek Date: Wed, 5 Feb 2014 01:04:16 +0100 Subject: [PATCH] [DomCrawler] Fixed filterXPath() chaining --- src/Symfony/Component/DomCrawler/Crawler.php | 12 ++++---- .../Component/DomCrawler/Field/FormField.php | 8 +---- src/Symfony/Component/DomCrawler/Form.php | 30 +++++++------------ .../DomCrawler/Tests/CrawlerTest.php | 4 ++- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index c3954946ad83..c2be6d9348a4 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -441,7 +441,7 @@ public function parents() $nodes = array(); while ($node = $node->parentNode) { - if (1 === $node->nodeType && '_root' !== $node->nodeName) { + if (1 === $node->nodeType) { $nodes[] = $node; } } @@ -584,15 +584,13 @@ public function extract($attributes) */ public function filterXPath($xpath) { - $document = new \DOMDocument('1.0', 'UTF-8'); - $root = $document->appendChild($document->createElement('_root')); + $crawler = new static(null, $this->uri); foreach ($this as $node) { - $root->appendChild($document->importNode($node, true)); + $domxpath = new \DOMXPath($node->ownerDocument); + $crawler->add($domxpath->query($xpath, $node)); } - $domxpath = new \DOMXPath($document); - - return new static($domxpath->query($xpath), $this->uri); + return $crawler; } /** diff --git a/src/Symfony/Component/DomCrawler/Field/FormField.php b/src/Symfony/Component/DomCrawler/Field/FormField.php index 6412272c2e29..27fb5f06a8a8 100644 --- a/src/Symfony/Component/DomCrawler/Field/FormField.php +++ b/src/Symfony/Component/DomCrawler/Field/FormField.php @@ -52,13 +52,7 @@ public function __construct(\DOMNode $node) { $this->node = $node; $this->name = $node->getAttribute('name'); - - $this->document = new \DOMDocument('1.0', 'UTF-8'); - $this->node = $this->document->importNode($this->node, true); - - $root = $this->document->appendChild($this->document->createElement('_root')); - $root->appendChild($this->node); - $this->xpath = new \DOMXPath($this->document); + $this->xpath = new \DOMXPath($node->ownerDocument); $this->initialize(); } diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index 2649d6d33b30..03f6980b34e1 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -378,9 +378,7 @@ private function initialize() { $this->fields = new FormFieldRegistry(); - $document = new \DOMDocument('1.0', 'UTF-8'); - $xpath = new \DOMXPath($document); - $root = $document->appendChild($document->createElement('_root')); + $xpath = new \DOMXPath($this->node->ownerDocument); // add submitted button if it has a valid name if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { @@ -390,39 +388,33 @@ private function initialize() // temporarily change the name of the input node for the x coordinate $this->button->setAttribute('name', $name.'.x'); - $this->set(new Field\InputFormField($document->importNode($this->button, true))); + $this->set(new Field\InputFormField($this->button)); // temporarily change the name of the input node for the y coordinate $this->button->setAttribute('name', $name.'.y'); - $this->set(new Field\InputFormField($document->importNode($this->button, true))); + $this->set(new Field\InputFormField($this->button)); // restore the original name of the input node $this->button->setAttribute('name', $name); - } - else { - $this->set(new Field\InputFormField($document->importNode($this->button, true))); + } else { + $this->set(new Field\InputFormField($this->button)); } } // find form elements corresponding to the current form if ($this->node->hasAttribute('id')) { - // traverse through the whole document - $node = $document->importNode($this->node->ownerDocument->documentElement, true); - $root->appendChild($node); - // corresponding elements are either descendants or have a matching HTML5 form attribute $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); - $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId), $root); + + // do the xpath query without $this->node as the context node (i.e. traverse through the whole document) + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); foreach ($fieldNodes as $node) { $this->addField($node); } } else { - // parent form has no id, add descendant elements only - $node = $document->importNode($this->node, true); - $root->appendChild($node); - - // descendant elements with form attribute are not part of this form - $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $root); + // do the xpath query with $this->node as the context node, to only find descendant elements + // however, descendant elements with form attribute are not part of this form + $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); foreach ($fieldNodes as $node) { $this->addField($node); } diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 1c223980252f..867b24613c0a 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -378,8 +378,10 @@ public function testFilterXPath() $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler'); $crawler = $this->createTestCrawler()->filterXPath('//ul'); - $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); + + $crawler = $this->createTestCrawler(); + $this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained'); } /**