prinsfrank/markdown-dom
prinsfrank/markdown-dom is a PHP package that converts Markdown into a DOM structure, letting you traverse, query, and manipulate parsed Markdown programmatically before rendering. Useful for custom transformations, content processing, and DOM-based workflows.
rel="nofollow"/target="_blank"), lazy-loaded and CDN-rewritten images, responsive table wrappers, content excerpts for index pages, and multiple output formats (HTML + plain text + search index) from a single parse.markdown-dom defines the DOM and a MarkdownParser contract; it ships no parser of its own. The practical dependency is an adapter such as markdown-dom-league-common, which wraps league/commonmark — already a transitive dependency of Laravel's Markdown mail. So the real decision is "adopt a thin node abstraction on top of a parser we already run," which is a much smaller commitment than adopting a new parser.Str::markdown() in one line. This package earns its place only on surfaces that need structural traversal. Roadmap sequencing should reflect that: pilot on low-blast-radius surfaces (internal docs, changelog pages, comment bodies) behind an application-owned MarkdownRenderer interface, and promote to default only after output parity against the incumbent on a real content corpus.illuminate/* ranges or constrain framework upgrades. That is a plus for longevity, but every bit of wiring becomes ours to own, so the PM cost is a small, permanent internal abstraction rather than a one-off integration.ext-dom. DOM construction and serialization are materially heavier in CPU and memory than string-based rendering. This is irrelevant if rendering happens once at write time with cached HTML, and potentially relevant if user content is rendered on every request — a product/infra decision about where rendering sits in the request path.html_input setting, and the DOM faithfully represents whatever the parser produced. For any user-authored content, sanitization must be an explicit, separately-resourced work item — not inferred from the parser.Consider it when:
league/commonmark rather than building your own AST handling.Look elsewhere when:
Str::markdown() is one line, already uses league/commonmark, and adds no dependency — use it.league/commonmark or an equivalent established parser.Gating questions before any adoption decision: Which Markdown dialect and extension set does it target? Is the custom autoloader on the Composer consumer path or only in the dev harness (i.e., could the v0.2.1 bug ever have reached us)? Is DOMDocument configured against XXE and entity expansion for untrusted input? What is the maintainer's release cadence and BC stance for 0.x? Do we need anything beyond HTML output?
To executives (one paragraph): This is a small, MIT-licensed PHP library that turns Markdown into a structured document tree instead of an HTML string, which lets us build content features we currently can't do without brittle string manipulation — auto-generated tables of contents, safer external links, image handling, list-page excerpts, and multiple output formats from a single parse. It does not replace our Markdown parser; it sits on top of the one Laravel already ships with, so the incremental technical commitment is small. The trade-off is maturity: version 0.2.1, one maintainer, no external adopters yet. Recommendation is a contained pilot on internal, low-risk surfaces behind our own interface — roughly half a day to integrate, one to two more days for test coverage and sanitization — with a decision to broaden at 1.0 or once output parity is demonstrated. If the pilot disappoints, swapping back is a one-class change.
To engineering: markdown-dom is the DOM abstraction, not a parser — a fresh install that "does nothing" is a missing adapter (markdown-dom-league-common) or an unbound MarkdownParser interface. Bind the parser in a service provider and route rendering through a single Blade component so {!! !!} stays confined to one audited place. Parse on save and persist the HTML; if rendering on read is unavoidable, cache keyed on the Markdown plus serialized parser config and bust on deploy. Do the high-value work in a node visitor — link hardening, image lazy-loading/rewriting, heading collection for TOCs, block-level excerpts — and express extensions through league/commonmark config rather than post-processing HTML. Assert against the tree in tests, not rendered strings, and add a golden-file suite over 50–100 real documents before promoting this past the pilot. Sanitization is ours to implement: set html_input => 'strip' and allow_unsafe_links => false, and add a DOM whitelist or HTMLPurifier for user content.
Release note (v0.2.1): A single patch fixing an off-by-one error in the package's bundled custom autoloader (PR #18). No new features, no deprecations, no breaking changes, no migration steps. Anyone on 0.2.0 should upgrade — a broken autoloader surfaces as intermittent class-not-found errors that masquerade as Composer cache problems — then run composer dump-autoload and re-check any vendored or copied classes. It slightly improves the maintenance signal and mildly flags that the package still carries hand-rolled loading logic; it does not alter the adoption assessment. Pin the version (0.2.1 or a narrow range) rather than a bare ^0.2.
How can I help you explore Laravel packages today?