Parsing and Rendering
Not all markup starts as element calls. Templates, rich-text editor output, and responses from other services arrive as strings. Parsing turns such a string into nodes, so you can change parts of it with the same API you use for elements built from scratch. Rendering then turns the tree back into a string. Changes can be made to a fragment that you did not write, without manipulating the string by hand with regular expressions.
Minimal Setup
HTML5Factory::parse() parses a string and returns the first top-level node. Use parseAsFragment() when the string holds several nodes:
use Windwalker\DOM\HTML5Factory;
$node = HTML5Factory::parse('<p class="lead">Intro</p>');
$fragment = HTML5Factory::parseAsFragment('<li>1</li><li>2</li>');Both methods return nodes that belong to the factory's shared document, so they can be appended to elements created by the same factory.
Showcase
The following example parses a navigation list, adds a class to each link, marks the list as sticky, and renders the result:
use Windwalker\DOM\HTML5Factory;
$html = <<<HTML
<ul class="nav">
<li><a href="/home">Home</a></li>
<li><a href="/docs">Docs</a></li>
</ul>
HTML;
$nav = HTML5Factory::parse($html);
foreach ($nav->querySelectorAll('a') as $link) {
$link->addClass('nav-link');
}
$nav->addClass('is-sticky');
echo $nav->render();The output keeps the original structure and whitespace, with the new classes added:
<ul class="nav is-sticky">
<li><a href="/home" class="nav-link">Home</a></li>
<li><a href="/docs" class="nav-link">Docs</a></li>
</ul>The query uses the native querySelectorAll() method from PHP's DOM extension. It searches the descendants of the node, not the node itself.
Parsing
parse() returns a single node, and the rest of the string is discarded. Leading text is returned as a text node, so the result can differ from what you expect:
$node = HTML5Factory::parse('Hello <b>world</b>');
get_class($node); // Dom\TextUse parseAsFragment() for any string with more than one top-level node or with leading text. The fragment contains all of them, in order:
$fragment = HTML5Factory::parseAsFragment(
'Hello <b>world</b> and <i>you</i>'
);
$fragment->childNodes->length; // 4A fragment can be appended to an element, which moves its children into the element:
$list = HTML5Factory::element('ul');
$list->appendChild(
HTML5Factory::parseAsFragment('<li>1</li><li>2</li>')
);Parsing follows the HTML5 rules, so malformed markup is corrected in the same way a browser would correct it.
WARNING
Parsing does not sanitize markup. Scripts, event attributes such as onerror, and javascript: URLs are kept in the tree and in the rendered output. If the markup comes from a user, sanitize it with a dedicated HTML sanitizer before parsing or rendering it.
Full Documents
HTML5Factory::createFromString() parses a complete HTML document, including <html>, <head>, and <body>. It returns a Dom\HTMLDocument, which is separate from the shared document:
use Windwalker\DOM\HTML5Factory;
$doc = HTML5Factory::createFromString(
'<html><body><p id="intro">Hi</p></body></html>'
);
$doc->getElementById('intro')->textContent; // "Hi"Use HTML5Factory::document() for elements that you build in code, and createFromString() only when you need a whole document.
Querying
Elements offer two query styles:
querySelector()andquerySelectorAll()use the native DOM API. Every element from this package supports them without extra packages.getCrawler()returns a SymfonyCrawlerfor the element. It needssymfony/dom-crawler, which is a development dependency of this package and is not installed automatically in a project.
$nav->querySelector('a.nav-link')?->textContent; // "Home"
// Requires symfony/dom-crawler.
$nav->getCrawler()->filter('li')->count(); // 2Rendering
Every element can be rendered with render(). Casting it to a string calls the same method:
echo $nav;
echo $nav->render();To render a node that is not an element, such as a text node or a fragment child, pass it to HTML5Factory::saveHtml():
$fragment = HTML5Factory::parseAsFragment('<b>1</b> and <i>2</i>');
echo HTML5Factory::saveHtml($fragment->firstChild);
// <b>1</b>saveHtml() also accepts a Dom\HTMLDocument, and renders the whole document, including the <html> tags.
Moving a Node to Another Document
with() copies an element into another document and returns the copy. The original element is not changed:
$element = HTML5Factory::element('p', [], 'Hi');
$other = HTML5Factory::createFromString('<html><body></body></html>');
$copy = $element->with($other);
$copy->ownerDocument === $other; // trueUse with() when a node must belong to a document that you created yourself. Appending a node from another document to a tree does the same copy automatically.