You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Breaking Change in Php8.6 Nightly DOMDocument->loadHTML on Win11 #24300
But I expected this output instead (all other environments):
<?xml encoding="UTF-8"><p>foo</p>
This is the output from all Php8.2-8.5 for both Windows and Unix, and I believe it is also the output from Php8.6 nightly for Unix. I do not have a Unix Php8.6 testbed, but this seems to be the result at Github using 8.6 nightly.
This is actually not caused by a code change in PHP, but by a dependency update of libxml. DOMDocument uses libxml to parse HTML documents, which isn't compliant to the HTML spec. Lately, the libxml maintainers have made it more spec compliant (but still not fully).
So the old hack you were using breaks on newer libxml versions. This is actually already broken for Linux distros that ship a more modern libxml version. And the hack was also never actually safe to begin with.
The only solution is to ditch DOMDocument and switch to the newer Dom\HTMLDocument class that was introduced in PHP 8.4 to deal with all these kinds of troubles.
Thank you for the explanation. Is there a specific change in libxml that you can point me to so that I can explain the behavior change to my users?
I need to support 8.3 (and for a while 8.2), so HTMLDocument isn't a good choice for me. It may be possible, but if the present code is using an old hack, this will certainly be introducing a new one in its place.
Or perhaps I'm missing something. I believe the present code needed the xml tag to make sure our (user-supplied) UTF-8 string wasn't parsed as ISO-8859-1. Is there a better way to do that with DOMDocument?
I need to support 8.3 (and for a while 8.2), so HTMLDocument isn't a good choice for me. It may be possible, but if the present code is using an old hack, this will certainly be introducing a new one in its place.
I see.
Or perhaps I'm missing something. I believe the present code needed the xml tag to make sure our (user-supplied) UTF-8 string wasn't parsed as ISO-8859-1. Is there a better way to do that with DOMDocument?
Probably, prepending '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' is better than prepending the xml declaration.
That used to correctly give warning messages. Now it doesn't - it just throws everything out without any messages and without any indication of failure. How are we supposed to tell what's going on?
It depends on what you expect to happen.
The behaviour that libxml had previously was specific to whatever version your client was using and was closer to parsing html version 4 than modern html.
The newer libxml parser is closer to html5 and its warning/error model.
There's nothing we can do at the php side.
The only real solution is to use the new dom classes on php 8.4 and higher: https://3v4l.org/#v853
For lower php versions, clients should stick to older libxml versions, but note those libxml versions come with unbackported security issues.
Thank you @DanielRuf. I think pre-pending the string with a UTF-8 BOM may also work. At any rate, I am well on my way to a solution. It's just hard to figure out what else may now be different aside from what I've already found.
Here's another difference I found. Just adding this for documentary purposes. This is a genuine bug-fix for libxml; our code just happens to depend on the bug (sigh). When we pass the string <p>foo</p><p>bar</p> to loadHTML() and then iterate through all the child nodes, what the old libxml generates is:
nodeName = p, nodeValue = foobar // this is clearly wrong
nodeName = #text, nodeValue = foo
nodeName = p, nodeValue = bar
nodeName = #text, nodeValue = bar
As further evidence, when we do saveHTML() in this case, what we see is the obviously wrong <p>foo<p>bar</p></p>.
Pre-pending with UTF-8 BOM or using @DanielRuf suggestion does not affect this. However, prepending the xml tag results in:
nodeName = xml, nodeValue = encoding="UTF-8"
nodeName = p, nodeValue = foo // this is now correct
nodeName = #text, nodeValue = foo
nodeName = p, nodeValue = bar
nodeName = #text, nodeValue = bar
As I said, this particular change in libxml is a bug-fix after all. We can continue to prepend the xml tag when we know we're using a libxml version below a certain threshold.
We can continue to prepend the xml tag when we know we're using a libxml version below a certain threshold.
I thought also using a libxml version check (either using LIBXML_VERSION or LIBXML_LOADED_VERSION) but this is something that some want to avoid like PHP_VERSION_ID checks.
Activity
ndossche commentedon Oct 13, 2026
This is actually not caused by a code change in PHP, but by a dependency update of libxml. DOMDocument uses libxml to parse HTML documents, which isn't compliant to the HTML spec. Lately, the libxml maintainers have made it more spec compliant (but still not fully).
So the old hack you were using breaks on newer libxml versions. This is actually already broken for Linux distros that ship a more modern libxml version. And the hack was also never actually safe to begin with.
The only solution is to ditch DOMDocument and switch to the newer Dom\HTMLDocument class that was introduced in PHP 8.4 to deal with all these kinds of troubles.
oleibman commentedon Oct 13, 2026
Thank you for the explanation. Is there a specific change in libxml that you can point me to so that I can explain the behavior change to my users?
I need to support 8.3 (and for a while 8.2), so HTMLDocument isn't a good choice for me. It may be possible, but if the present code is using an old hack, this will certainly be introducing a new one in its place.
Or perhaps I'm missing something. I believe the present code needed the
xmltag to make sure our (user-supplied) UTF-8 string wasn't parsed as ISO-8859-1. Is there a better way to do that with DOMDocument?ndossche commentedon Oct 13, 2026
The changes landed in multiple commits, a full breakdown is provided here: https://gitlab.gnome.org/GNOME/libxml2/-/work_items/211
I see.
Probably, prepending
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'is better than prepending the xml declaration.oleibman commentedon Oct 13, 2026
This seems like a complete disaster to me.
That used to correctly give warning messages. Now it doesn't - it just throws everything out without any messages and without any indication of failure. How are we supposed to tell what's going on?
ndossche commentedon Oct 14, 2026
It depends on what you expect to happen.
The behaviour that libxml had previously was specific to whatever version your client was using and was closer to parsing html version 4 than modern html.
The newer libxml parser is closer to html5 and its warning/error model.
There's nothing we can do at the php side.
The only real solution is to use the new dom classes on php 8.4 and higher: https://3v4l.org/#v853
For lower php versions, clients should stick to older libxml versions, but note those libxml versions come with unbackported security issues.
DanielRuf commentedon Oct 15, 2026
So I guess the ne DOM class is not directly supported on older PHP versions and there are no polyfills.
I did some research and came to this solution:
Not beautiful but it works for me.
ndossche commentedon Oct 15, 2026
This is correct, it is too complicated and too much functionality to reasonably polyfill.
Your solution should indeed work.
oleibman commentedon Oct 15, 2026
Thank you @DanielRuf. I think pre-pending the string with a UTF-8 BOM may also work. At any rate, I am well on my way to a solution. It's just hard to figure out what else may now be different aside from what I've already found.
oleibman commentedon Oct 16, 2026
Here's another difference I found. Just adding this for documentary purposes. This is a genuine bug-fix for libxml; our code just happens to depend on the bug (sigh). When we pass the string
<p>foo</p><p>bar</p>toloadHTML()and then iterate through all the child nodes, what the old libxml generates is:As further evidence, when we do
saveHTML()in this case, what we see is the obviously wrong<p>foo<p>bar</p></p>.Pre-pending with UTF-8 BOM or using @DanielRuf suggestion does not affect this. However, prepending the
xmltag results in:As I said, this particular change in libxml is a bug-fix after all. We can continue to prepend the
xmltag when we know we're using a libxml version below a certain threshold.DanielRuf commentedon Oct 16, 2026
I thought also using a libxml version check (either using
LIBXML_VERSIONorLIBXML_LOADED_VERSION) but this is something that some want to avoid likePHP_VERSION_IDchecks.