Skip to content

Hotfix for HTML form attribute handling #9158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

fabpot
Copy link
Member

@fabpot fabpot commented Sep 27, 2013

This PR was submitted on the symfony/DomCrawler read-only repository and moved automatically to the main Symfony repository (closes symfony/dom-crawler#15).

The following,

$form = $node->ownerDocument->getElementById($formId);

...is an unreliable way to retrieve the ID.

Even if there is an element with an id attribute the id won't be recognized unless the document was validated at the point it was read which in the case of setNode (for whatever reason) does not happen under some circumstances.

To give an example, given the following xml file

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <form id="testform1"></form>
</root>

And the following code,

<?php

ini_set('display_errors',1);
ini_set('display_startup_errors',1);
ini_set('html_errors', 1);
error_reporting(-1);

$doc = new DomDocument;
$doc->validateOnParse = true;
$doc->Load('test.xml');

echo 'Form Tag Exists? '.($doc->getElementById('testform1') === null ? 'Nope' : 'Yup');

The code will still output "Form Tag Exists? Nope" because of how picky the getElementById method is.

For more on the phenomenon see this stackoverflow answer: http://stackoverflow.com/questions/3391942/php-html-domdocument-getelementbyid-problems

I haven't been able to track down why the document in question doesn't work with getElementById, but somehow I'm assuming it has something to do with html5 not having a DTD (though for what it's worth ownerDocument in that particular case somehow gets reduced to just the form and the fields; which is weird in of itself). For reference the real world circumstances where this borks miserably is when using behat with goutte.

The following XPath version appears to reliably work,

$xp = new \DOMXPath($node->ownerDocument);
$form = $xp->query("//*[@id='{$formId}']")->item(0);

…rm tag with an id is reliably retrieved

The following,

$form = $node->ownerDocument->getElementById($formId);

...is an unreliable way to retrieve the ID.

Even if there is an element with an id attribute the id won't be recognized unless the document was validated at the point it was read which in the case of setNode (for whatever reason) does not happen under some circumstances.

To give an example, given the following xml file

	<?xml version="1.0" encoding="UTF-8"?>
	<root>
		<form id="testform1"></form>
	</root>

And the following code,

	<?php

	ini_set('display_errors',1);
	ini_set('display_startup_errors',1);
	ini_set('html_errors', 1);
	error_reporting(-1);

	$doc = new DomDocument;
	$doc->validateOnParse = true;
	$doc->Load('test.xml');

	echo 'Form Tag Exists? '.($doc->getElementById('testform1') === null ? 'Nope' : 'Yup');

The code will still output "Form Tag Exists? Nope" because of how picky the getElementById method is.

For more on the phenomenon see this stackoverflow answer: http://stackoverflow.com/questions/3391942/php-html-domdocument-getelementbyid-problems

I haven't been able to track down why the document in question doesn't work with getElementById, but somehow I'm assuming it has something to do with html5 not having a DTD (though for what it's worth ownerDocument in that particular case somehow gets reduced to just the form and the fields; which is weird in of itself). For reference the real world circumstances where this borks miserably is when using behat with goutte.

The following XPath version appears to reliably work,

	$xp = new \DOMXPath($node->ownerDocument);
	$form = $xp->query("//*[@id='{$formId}']")->item(0);
@fabpot
Copy link
Member Author

fabpot commented Sep 28, 2013

@srcspider Can you try with the latest version as I'm not able to reproduce it.

I've added this test:

    public function testFormWithId()
    {
        $dom = new \DOMDocument();
        $dom->loadXML('<?xml version="1.0" encoding="UTF-8"?><root><form id="form"><input type="submit" /></form></root>');

        // $nodes = $dom->getElementById('form');
        // $this->assertNotNull($nodes);
        $nodes = $dom->getElementsByTagName('input');

        $form = new Form($nodes->item(0), 'http://example.com');
    }

And even if the getElementById() does not work (commented code), the form works fine.

@srcspider
Copy link

You can't expect the form to work with getElementById() failing when the form input could very well be outside the form (and is in 90% of my use cases)

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <form id="form1" method="GET">
        <input type="hidden" name="submitted" value="form1"/>
    </form>
    <input form="form1" type="submit"/>
</root>

@fabpot
Copy link
Member Author

fabpot commented Sep 30, 2013

@srcspider I still don't get it. If you try to use the code you mentioned as HTML, it works perfectly fine. And if you are using XHTML, it's not valid.

@srcspider
Copy link

@fabpot The point was not that the code is invalid html (with regard to xml, it only looks like that because I just altered your example to illustrate the difference).

The example is in response to what you previously said,

And even if the getElementById() does not work (commented code), the form works fine.

That only applies to example like the one you gave where the form encapsulates the fields. In examples like the one I gave you where the field is outside the form and linked to the form via the form attribute if getElementById() fails obviously the form fails.

@fabpot
Copy link
Member Author

fabpot commented Oct 1, 2013

I have tried with the submit button outside of the form and it definitely works if the doctype is HTML5.

@fabpot
Copy link
Member Author

fabpot commented Oct 1, 2013

So, I still don't have any valid example that actually does not work. The code you give is wrong as you cannot use a submit button outside of a form in XHTML.

@srcspider
Copy link

@fabpot

So, I still don't have any valid example that actually does not work. The code you give is wrong as you cannot use a submit button outside of a form in XHTML.

I'll take it you're referring to the 4.1 XHTML specification and don't support HTML5, since that's a valid XML and there is no rule that submit buttons can't have a form attribute association. Specificiation: http://www.w3.org/html/wg/drafts/html/master/forms.html#attr-fae-form

I can't give you a perfect example since I'm too high on the dependency chain to actually have the full reason why your system, while used by various other projects, fails (and dont ask me to ask them, I did, they pointed the finger at you). I can only tell you what part of it fails, why it fails and how you can patch it so it doesn't fail anymore, not the exact reasons and pieces why it reaches the fail state.

I also don't use symphony, and have dropped the parts in the tools that use this system, so it's not worth my time to dig for days to find you a perfect example.

The example I've given you is a "test" on how to see under what circumstances it fail, not a test for the actual bug in your system. Here's a single block version in case reading the xml from another file was confusing:

<?php

$doc = new DomDocument;
$doc->validateOnParse = true;

$doc->loadXML
    (
        '<?xml version="1.0" encoding="UTF-8"?>
        <root>
            <form id="testform1">
                <input type="submit" />
            </form>
        </root>
        '
    );

// the getElementById is important!
if ($doc->getElementById('testform1') === null)
{
    throw new \Exception('Failed');
}

@OskarStark
Copy link
Contributor

is this PR already merged? @fabpot

@xabbuh xabbuh added DomCrawler and removed Form labels Jun 14, 2015
@xabbuh
Copy link
Member

xabbuh commented Jun 16, 2015

No, it's not merged yet and from what I see this won't happen if we don't have a failing test case for this issue.

@jakzal
Copy link
Contributor

jakzal commented Jul 7, 2015

@OskarStark have you experienced this issue yourself?

@OskarStark
Copy link
Contributor

@jakzal no, but the ticket is very old, thought that this one could be closed

@xabbuh
Copy link
Member

xabbuh commented Jul 7, 2015

I agree. Someone can open a new pull request if they are able to provide a proper test case.

@xabbuh xabbuh closed this Jul 7, 2015
@OskarStark
Copy link
Contributor

@xabbuh thank you

joshtrichards added a commit to joshtrichards/symfony that referenced this pull request Apr 26, 2024
joshtrichards added a commit to joshtrichards/symfony that referenced this pull request Apr 27, 2024
joshtrichards added a commit to joshtrichards/symfony that referenced this pull request Apr 27, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Apr 27, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Apr 27, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Apr 27, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Aug 9, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Aug 9, 2024
phil-davis pushed a commit to phil-davis/symfony that referenced this pull request Aug 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants