voku/simple_html_dom
voku/simple_html_dom is a fast PHP HTML DOM parser and jQuery-style selector library. It wraps DOMDocument and Symfony CssSelector to find, extract, and manipulate HTML with UTF-8 and partial invalid-HTML support. Requires PHP 7.1+.
A HTML DOM parser written in PHP - let you manipulate HTML in a very easy way! This is a fork of PHP Simple HTML DOM Parser project but instead of string manipulation we use DOMDocument and modern php classes like "Symfony CssSelector".
composer require voku/simple_html_dom
composer require voku/portable-utf8 # if you need e.g. UTF-8 fixed output
find*() calls now return live nodes scoped to the original DOM, so mutating nested results updates the source document.use voku\helper\HtmlDomParser;
require_once 'composer/autoload.php';
...
$dom = HtmlDomParser::str_get_html($str);
// or
$dom = HtmlDomParser::file_get_html($file);
$element = $dom->findOne('#css-selector'); // "$element" === instance of "SimpleHtmlDomInterface"
$elements = $dom->findMulti('.css-selector'); // "$elements" === instance of SimpleHtmlDomNodeInterface<int, SimpleHtmlDomInterface>
$elementOrFalse = $dom->findOneOrFalse('#css-selector'); // "$elementOrFalse" === instance of "SimpleHtmlDomInterface" or false
$elementsOrFalse = $dom->findMultiOrFalse('.css-selector'); // "$elementsOrFalse" === instance of SimpleHtmlDomNodeInterface<int, SimpleHtmlDomInterface> or false
...
Html5DomParserPHP 8.4 added \Dom\HTMLDocument, a parser that follows the HTML5 specification and therefore
recovers from broken markup the way a browser does. This library exposes it as its own class,
for the same reason PHP put it next to \DOMDocument instead of adding a mode to it: the
parsing rules are different, and that difference is visible in the result.
Html5DomParser extends HtmlDomParser and has the same API, so switching means changing the
class name and nothing else:
use voku\helper\Html5DomParser;
$dom = Html5DomParser::str_get_html('<table><tr><td>x</table><p>a<p>b');
$dom->html(); // '<table><tbody><tr><td>x</td></tr></tbody></table><p>a</p><p>b</p>'
// HtmlDomParser, unchanged, still returns:
// '<table><tr><td>x</td></tr></table><p>a</p><p>b</p>'
HtmlDomParser is not affected by any of this and stays the default parser of this library.
What you get with Html5DomParser: implied <tbody>, auto-closed <p> / <li> / <td>,
recovery from misnested formatting tags, tag names normalized to lower case, camel-case SVG
names (viewBox, feColorMatrix) restored, the encoding detected from the document like a
browser does, and elements that libxml would have dropped or moved.
What is different, and why it is a separate class:
and & come back as and & instead of staying entities.getDocument()->documentElement is
always <html>, even for a fragment. html() / innerHtml() still return the fragment.<input checked> gives getAttribute('checked') === ''. Test presence with
hasAttribute(), and expect checked="" in the serialized output.</body> is moved back into the body, like a browser does.useKeepBrokenHtml() works on top of it: the broken fragments are preserved verbatim, but
because they travel through the parser as text, HTML5 tree construction can move such a
fragment out of a <table> or out of the <head>. The fragment itself is never lost.tests/Html5DomParserCompatibilityTest.php is the HtmlDomParser test suite run against
Html5DomParser, so every one of these differences is pinned by a test, and everything else is
proven to be unchanged.
The result is bridged back into a \DOMDocument, which costs one extra serialize + parse and
more transient memory. Measure it for your own input:
php build/benchmark_html5_parser.php
On the fixtures of this repository the complete loadHtml() + query + html() round-trip is
currently faster than the libxml path (factor 0.72 - 0.95), because the HTML5 parser needs
none of the string preprocessing that path does; small synthetic fragments are slower
(factor ~1.3 - 1.5), and peak memory is higher in both cases.
Html5DomParser is a strict parser choice. It does not silently switch back to
HtmlDomParser, because that would make the class name lie about the parsing semantics. On an
application that also runs on PHP < 8.4, check support before selecting the class:
if (Html5DomParser::isHtml5ParserSupported()) {
$dom = Html5DomParser::str_get_html($html);
} else {
$dom = HtmlDomParser::str_get_html($html); // explicit application decision
}
Parsing throws a RuntimeException when the HTML5 backend is unavailable or when its normalized
tree cannot be represented by the legacy \DOMDocument XML bridge (for example an HTML-valid
attribute name that XML cannot represent). Use HtmlDomParser explicitly if legacy parsing is
the intended fallback. A successful Html5DomParser parse therefore always means HTML5 tree
construction actually happened.
github.com/voku/simple_html_dom/tree/master/example
github.com/voku/simple_html_dom/tree/master/README_API.md
For support and donations please visit Github | Issues | PayPal | Patreon.
For status updates and release announcements please visit Releases | Twitter | Patreon.
For professional support please contact me.
How can I help you explore Laravel packages today?