Browse Source

Replace the deprecated HTML-ENTITIES conversion for PHP 8.2

html_to_dom_document() used mb_convert_encoding() with the HTML-ENTITIES
pseudo-encoding, which is deprecated as of PHP 8.2. Encode non-ASCII
characters with mb_encode_numericentity() instead. The resulting DOM is
identical, including invalid byte sequences being dropped.

Also stop passing an empty string to loadHTML(), which throws a
ValueError as of PHP 8.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main
Aaron Parecki 2 days ago
parent
commit
552cff1223
2 changed files with 33 additions and 2 deletions
  1. +15
    -2
      src/utils.php
  2. +18
    -0
      tests/unit/UtilsTest.php

+ 15
- 2
src/utils.php View File

@ -109,8 +109,21 @@ function html_to_dom_document($html) {
// Parse the source body as HTML // Parse the source body as HTML
$doc = new DOMDocument(); $doc = new DOMDocument();
libxml_use_internal_errors(true); # suppress parse errors and warnings libxml_use_internal_errors(true); # suppress parse errors and warnings
$body = mb_convert_encoding($html, 'HTML-ENTITIES', mb_detect_encoding($html));
@$doc->loadHTML($body, LIBXML_NOWARNING|LIBXML_NOERROR);
# loadHTML assumes ISO-8859-1 unless the document says otherwise, so encode
# everything outside ASCII as numeric entities. This used to be done with the
# HTML-ENTITIES pseudo-encoding, which is deprecated as of PHP 8.2.
# Invalid byte sequences are dropped, as they were before.
$substitute = mb_substitute_character();
mb_substitute_character('none');
try {
$encoding = mb_detect_encoding($html) ?: 'UTF-8';
$body = mb_encode_numericentity(mb_convert_encoding($html, 'UTF-8', $encoding), [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8');
} finally {
mb_substitute_character($substitute);
}
# loadHTML throws on an empty string as of PHP 8
if($body !== '')
@$doc->loadHTML($body, LIBXML_NOWARNING|LIBXML_NOERROR);
libxml_clear_errors(); libxml_clear_errors();
return $doc; return $doc;
} }

+ 18
- 0
tests/unit/UtilsTest.php View File

@ -98,6 +98,24 @@ class UtilsTest extends \PHPUnit\Framework\TestCase {
$this->assertEmpty(libxml_get_errors()); $this->assertEmpty(libxml_get_errors());
} }
public function testHTMLToDomDocumentPreservesNonASCII() {
$doc = p3k\html_to_dom_document('<p>Café 日本語 🚀</p>');
$this->assertEquals('Café 日本語 🚀', $doc->getElementsByTagName('p')->item(0)->textContent);
}
public function testHTMLToDomDocumentDropsInvalidBytes() {
$substitute = mb_substitute_character();
$doc = p3k\html_to_dom_document("<p>caf\xe9 ok</p>");
$this->assertEquals('caf ok', $doc->getElementsByTagName('p')->item(0)->textContent);
$this->assertEquals($substitute, mb_substitute_character());
}
public function testHTMLToDomDocumentEmptyString() {
$doc = p3k\html_to_dom_document('');
$this->assertEquals('DOMDocument', get_class($doc));
$this->assertNull($doc->documentElement);
}
public function testXMLToDomDocument() { public function testXMLToDomDocument() {
$doc = p3k\xml_to_dom_document('<html><head><title>Title</title></head><body>Hello World</body></html>'); $doc = p3k\xml_to_dom_document('<html><head><title>Title</title></head><body>Hello World</body></html>');
$this->assertEquals('DOMDocument', get_class($doc)); $this->assertEquals('DOMDocument', get_class($doc));

Loading…
Cancel
Save