Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Idle Dom Laravel Package

wikimedia/idle-dom

IDLeDOM provides PHP interfaces and helper traits for the DOM as defined by Web IDL, enabling type-safe documents, nodes, and elements with interchangeable implementations. It helps libraries offer standards-compliant DOM behavior without binding to a single backend.

View on GitHub
Deep Wiki
Context7

Getting Started

wikimedia/idle-dom is not a DOM implementation — it is a set of PHP interfaces generated from the DOM WebIDL spec (plus a Helper trait that gives implementations the IDL-style magic property accessors). You almost never install it alone.

Minimum viable setup in a Laravel app:

composer require wikimedia/idle-dom wikimedia/dodo wikimedia/remex-html
  • wikimedia/idle-dom — the interfaces (IDLeDOM\Node, IDLeDOM\Element, IDLeDOM\Document, …)
  • wikimedia/dodo — the concrete implementation that actually does the work
  • wikimedia/remex-html — HTML5-spec-compliant tokenizer/tree builder, which is what makes the parse behave like a browser instead of like DOMDocument::loadHTML()

There is no service provider, no config, no facade. It's a library, not a Laravel package; nothing to register.

First use case — typed, spec-correct HTML parsing inside a service class:

use Dodo\Document;

$doc = Document::loadHTML('<p class="lead">Hello <b>world</b></p>');
$p   = $doc->getElementsByTagName('p')->item(0);

echo $p->textContent;              // "Hello world"
echo $p->getAttribute('class');    // "lead"

Where to look first: read the IDLeDOM interface files themselves (vendor/wikimedia/idle-dom/src/) — they are auto-generated straight from the spec and are the best documentation you'll get. Then read Dodo's README for the concrete factory/parsing helpers, and Parsoid (MediaWiki) for real-world usage patterns. The GitHub repo is only a mirror; the upstream code and issue tracker live on Gerrit/Phabricator, so don't expect PRs or issues on GitHub to be triaged.

Implementation Patterns

Type-hint the interfaces, not the implementation. This is the entire point of the package — decouple your application code from Dodo so a faster or stricter implementation can be swapped in later (or mocked in tests):

namespace App\Services;

use IDLeDOM\Element;

final class ClassCollector
{
    /** @return list<string> */
    public function collect(Element $root): array
    {
        $classes = [];
        foreach ($root->getElementsByTagName('*') as $el) {
            foreach (preg_split('/\s+/', $el->getAttribute('class') ?? '') as $c) {
                if ($c !== '') {
                    $classes[$c] = true;
                }
            }
        }
        return array_keys($classes);
    }
}

Wrap parsing in one place. A single factory/service keeps the Dodo dependency contained:

final class HtmlDocument
{
    public function parse(string $html): \IDLeDOM\Document
    {
        return \Dodo\Document::loadHTML($html);
    }
}

If your Dodo version doesn't expose loadHTML(), wire RemexHtml manually (Tokenizer → Dispatcher → TreeBuilder → DOMBuilder) once, in that same class.

IDL-style attributes via magic accessors. Implementations using IDLeDOM\Helper expose spec-named properties alongside the PHP method names:

$el->textContent;        // same as $el->getTextContent() in effect
$el->textContent = 'x';  // setter
$el->attributes;         // IDLeDOM\NamedNodeMap
$el->childNodes;         // IDLeDOM\NodeList (iterable, countable)
$el->parentElement;

Prefer the explicit get*/set* methods in application code — they're static-analysable, and Psalm/PHPStan understands them without a plugin.

Sanitisation / rewriting pass. A common Laravel use: accept rich-text from a WYSIWYG, strip dangerous attributes, re-serialise.

$doc = $this->html->parse($userHtml);

foreach (iterator_to_array($doc->getElementsByTagName('*')) as $el) {
    foreach (['onclick', 'onerror', 'style'] as $attr) {
        if ($el->hasAttribute($attr)) {
            $el->removeAttribute($attr);
        }
    }
}

$clean = $doc->saveHTML();   // or getInnerHTML() on the body element

Iterate over a snapshot (iterator_to_array or collecting into an array first) — getElementsByTagName returns a live list, and mutating while iterating will bite you.

Structured extraction into DTOs. Parse once, walk once, map to your own value objects — don't leak IDLeDOM\* types out of the service layer:

$items = [];
foreach ($doc->getElementsByTagName('article') as $article) {
    $items[] = new ArticleDto(
        title: trim($article->getElementsByTagName('h2')->item(0)?->textContent ?? ''),
        body:  $article->textContent,
    );
}

Queue jobs for heavy parsing. Parsing large documents is CPU-bound; dispatch a job and cache the extracted result rather than parsing on every web request.

Gotchas and Tips

  • Interfaces only. new IDLeDOM\Document() does not exist. Every "instantiate" path goes through Dodo (or your own implementation). Mixing native \DOMDocument objects into methods typed against IDLeDOM\Node will fatal — they are unrelated hierarchies.
  • childNodes / getElementsByTagName are live. Snapshot before mutating, and never rely on indices surviving an insertion.
  • textContent vs nodeValue vs innerHTML. textContent returns raw text (no entity decoding surprises, no tags); innerHTML serialises children. For HTML5 serialisation semantics, use Dodo's serialiser rather than hand-rolling string concatenation — attribute quoting and void elements are easy to get wrong.
  • Parser differences are the feature. DOMDocument::loadHTML() is HTML4-era and mangles modern markup (e.g. <template>, <figure> nesting, unquoted attributes in edge cases). If your output must match a browser, use RemexHtml/Dodo, not loadHTML().
  • Namespaces. For XHTML-ish input, getElementsByTagName won't match namespaced nodes; use getElementsByTagNameNS. Most Laravel HTML inputs are text/html so this only bites with SVG/MathML embedded content.
  • Error handling. Parse errors surface as IDLeDOM\DOMException (or RemexHtml errors, depending on configuration), not as false returns like the native API. Wrap in try/catch and decide whether to reject or repair malformed input.
  • Ecosystem is tiny (0 dependents, 1 star on the mirror). Documentation is the source code and Parsoid. Budget time to read the interfaces; don't expect Stack Overflow answers.
  • Gerrit, not GitHub. Patches go to gerrit.wikimedia.org; bugs go to Phabricator. Forking the GitHub mirror and opening a PR is a dead end. Pin versions in composer.json and be prepared to vendor a patch if you hit an upstream bug.
  • Performance. Dodo is pure PHP — noticeably slower and more memory-hungry than ext-dom. Fine for parsing a few documents per request or in a job; not fine for a loop over 10k documents. Cache aggressively (Cache::remember keyed on a content hash) and consider native DOMDocument/Symfony DomCrawler for trivial, well-formed extraction where spec fidelity doesn't matter.
  • Extension points. If you need to wrap the native ext-dom for speed, implement the IDLeDOM\* interfaces yourself and use IDLeDOM\Helper; — the trait supplies the magic property glue, so you only implement the real methods. That's the supported way to add a second backend.
  • Testing. Assert on serialised output strings (saveHTML() / getInnerHTML()), not on object graphs — object identity isn't stable between Dodo versions.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package