diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php index d30a8b1ddc..557c4c0cb3 100644 --- a/src/Symfony/Component/DomCrawler/Form.php +++ b/src/Symfony/Component/DomCrawler/Form.php @@ -366,46 +366,59 @@ class Form extends Link implements \ArrayAccess $this->node = $node; } + /** + * Adds form elements related to this form. + * + * Creates an internal copy of the submitted 'button' element and + * the form node or the entire document depending on whether we need + * to find non-descendant elements through HTML5 'form' attribute. + */ private function initialize() { $this->fields = new FormFieldRegistry(); $document = new \DOMDocument('1.0', 'UTF-8'); - $node = $document->importNode($this->node, true); - $button = $document->importNode($this->button, true); - $root = $document->appendChild($document->createElement('_root')); - $root->appendChild($node); - $root->appendChild($button); $xpath = new \DOMXPath($document); + $root = $document->appendChild($document->createElement('_root')); - // add descendant elements to the form - $fieldNodes = $xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root); - foreach ($fieldNodes as $node) { - $this->addField($node, $button); + // add submitted button if it has a valid name + if ($this->button->hasAttribute('name') && $this->button->getAttribute('name')) { + $this->set(new Field\InputFormField($document->importNode($this->button, true))); } - // find form elements corresponding to the current form by the HTML5 form attribute + // 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')); - $xpath = new \DOMXPath($this->node->ownerDocument); - $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s]', $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, $button); + $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); + foreach ($fieldNodes as $node) { + $this->addField($node); } } } - private function addField(\DOMNode $node, \DOMNode $button) + private function addField(\DOMNode $node) { if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { return; } $nodeName = $node->nodeName; - - if ($node === $button) { - $this->set(new Field\InputFormField($node)); - } elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) { + if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) { $this->set(new Field\ChoiceFormField($node)); } elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) { if ($this->has($node->getAttribute('name'))) { diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php index 43e4f372f9..bf1765943e 100644 --- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php @@ -90,21 +90,53 @@ class FormTest extends \PHPUnit_Framework_TestCase $dom = new \DOMDocument(); $dom->loadHTML(' -
+ + - '); - $nodes = $dom->getElementsByTagName('input'); + $inputElements = $dom->getElementsByTagName('input'); + $buttonElements = $dom->getElementsByTagName('button'); - $form = new Form($nodes->item(0), 'http://example.com'); - $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); + // Tests if submit buttons are correctly assigned to forms + $form1 = new Form($buttonElements->item(1), 'http://example.com'); + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); + + $form1 = new Form($inputElements->item(3), 'http://example.com'); + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form1->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); + + $form2 = new Form($buttonElements->item(0), 'http://example.com'); + $this->assertSame($dom->getElementsByTagName('form')->item(1), $form2->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); + + // Tests if form elements are correctly assigned to forms + $values1 = array( + 'apples' => array('1', '2'), + 'form_name' => 'form_1', + 'button_1' => 'Capture fields', + 'outer_field' => 'success' + ); + $values2 = array( + 'oranges' => array('1', '2', '3'), + 'form_name' => 'form_2', + 'button_2' => '', + ); + $this->assertEquals($values1, $form1->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly'); + $this->assertEquals($values2, $form2->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly'); - $form = new Form($nodes->item(1), 'http://example.com'); - $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), 'HTML5-compliant form attribute handled incorrectly'); } public function testMultiValuedFields()