diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index c2be6d9348..c3954946ad 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -441,7 +441,7 @@ class Crawler extends \SplObjectStorage $nodes = array(); while ($node = $node->parentNode) { - if (1 === $node->nodeType) { + if (1 === $node->nodeType && '_root' !== $node->nodeName) { $nodes[] = $node; } } @@ -584,13 +584,15 @@ class Crawler extends \SplObjectStorage */ public function filterXPath($xpath) { - $crawler = new static(null, $this->uri); + $document = new \DOMDocument('1.0', 'UTF-8'); + $root = $document->appendChild($document->createElement('_root')); foreach ($this as $node) { - $domxpath = new \DOMXPath($node->ownerDocument); - $crawler->add($domxpath->query($xpath, $node)); + $root->appendChild($document->importNode($node, true)); } - return $crawler; + $domxpath = new \DOMXPath($document); + + return new static($domxpath->query($xpath), $this->uri); } /** diff --git a/src/Symfony/Component/DomCrawler/Field/FormField.php b/src/Symfony/Component/DomCrawler/Field/FormField.php index 27fb5f06a8..6412272c2e 100644 --- a/src/Symfony/Component/DomCrawler/Field/FormField.php +++ b/src/Symfony/Component/DomCrawler/Field/FormField.php @@ -52,7 +52,13 @@ abstract class FormField { $this->node = $node; $this->name = $node->getAttribute('name'); - $this->xpath = new \DOMXPath($node->ownerDocument); + + $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->initialize(); } diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index 03f6980b34..2649d6d33b 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -378,7 +378,9 @@ class Form extends Link implements \ArrayAccess { $this->fields = new FormFieldRegistry(); - $xpath = new \DOMXPath($this->node->ownerDocument); + $document = new \DOMDocument('1.0', 'UTF-8'); + $xpath = new \DOMXPath($document); + $root = $document->appendChild($document->createElement('_root')); // add submitted button if it has a valid name if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { @@ -388,33 +390,39 @@ class Form extends Link implements \ArrayAccess // temporarily change the name of the input node for the x coordinate $this->button->setAttribute('name', $name.'.x'); - $this->set(new Field\InputFormField($this->button)); + $this->set(new Field\InputFormField($document->importNode($this->button, true))); // temporarily change the name of the input node for the y coordinate $this->button->setAttribute('name', $name.'.y'); - $this->set(new Field\InputFormField($this->button)); + $this->set(new Field\InputFormField($document->importNode($this->button, true))); // restore the original name of the input node $this->button->setAttribute('name', $name); - } else { - $this->set(new Field\InputFormField($this->button)); + } + else { + $this->set(new Field\InputFormField($document->importNode($this->button, true))); } } // 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')); - - // 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)); + $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); foreach ($fieldNodes as $node) { $this->addField($node); } } else { - // 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); + // 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); 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 867b24613c..1c22398025 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -378,10 +378,8 @@ EOF $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'); + $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); } /**