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

Html5 Twig Component Bundle Laravel Package

vardumper/html5-twig-component-bundle

A Symfony bundle for building reusable HTML5-style Twig components. Define components as Twig templates and render them as custom elements with attributes and slots, making it easier to organize and reuse UI markup across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

vardumper/extended-htmldocument is a thin DOMDocument subclass that swaps libxml's HTML4 parser for masterminds/html5, giving you an HTML5-correct parse tree plus CSS-selector querying (via symfony/css-selector). In a Laravel app it is a parsing tool, not a browser: think "extract structured data from an HTML string I already have."

composer require vardumper/extended-htmldocument

Requirements are PHP 8.x, ext-dom, masterminds/html5, and symfony/css-selector; all are pulled in transitively, so there is no service provider or config to publish.

Where to look first, in order:

  1. vendor/vardumper/extended-htmldocument/src/ — the surface is one class (Vardumper\ExtendedHtmlDocument\HtmlDocument). Read it end to end; it is a few hundred lines and is the real documentation.
  2. composer.json of the package — confirm the masterminds/html5 constraint so you know which HTML5 spec revision you are parsing against.
  3. The package README for the exact factory/helper names. At 0.x these have moved between releases; verify loadHTML() / createFromString()-style entry points against src/ rather than trusting a blog post.

First use case: a queued job that pulls a supplier's product page or an inbound HTML email and extracts prices/SKUs, without dragging in headless Chrome.

use Vardumper\ExtendedHtmlDocument\HtmlDocument;

$doc = new HtmlDocument();
$doc->loadHTML($response->body());          // HTML5-correct parse tree

$price = $doc->querySelector('.product-price')?->nodeValue;
$skus  = $doc->querySelectorAll('ul.variants li[data-sku]');

Verify the class name and method signatures in src/ before writing more than a scratch script — that is the single highest-value five minutes you will spend on this package.

Implementation Patterns

Wrap it, don't sprinkle it. Bind a small extractor per source, so the parser never leaks into controllers or jobs:

final class SupplierPageExtractor
{
    public function extract(string $html): SupplierProductData
    {
        $doc = new HtmlDocument();
        $doc->loadHTML($this->normalise($html));

        return new SupplierProductData(
            title: trim($doc->querySelector('h1')?->nodeValue ?? ''),
            price: $this->toCents($doc->querySelector('.price')?->nodeValue),
            skus:  $this->collectSkus($doc->querySelectorAll('li[data-sku]')),
        );
    }

    private function normalise(string $html): string
    {
        // Fragment without <html>/<head>? Give the parser a charset hint.
        return str_starts_with(ltrim($html), '<!DOCTYPE')
            ? $html
            : '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>'.$html.'</body></html>';
    }
}

Inject it into a job, wrap the call in Http::retry(...)->get(...), and persist a DTO. Keep HTTP fetching outside the extractor so you can unit-test parsing against fixtures with zero network.

Iterate NodeLists deliberately. querySelectorAll() returns a DOMNodeList; convert once and map, rather than re-querying inside the loop:

$skus = collect(iterator_to_array($doc->querySelectorAll('li[data-sku]')))
    ->map(fn (DOMElement $li) => $li->getAttribute('data-sku'))
    ->filter()
    ->unique()
    ->values();

Assert HTML structure in feature tests. This is the underrated day-to-day win. Instead of brittle assertSee string matching on markup that shifts whitespace, parse the response body and assert on selectors:

$doc = new HtmlDocument();
$doc->loadHTML($this->get('/products')->getContent());

$this->assertCount(3, $doc->querySelectorAll('.product-card'));
$this->assertSame('Out of stock', trim($doc->querySelector('.stock-badge')?->nodeValue));

Pair this with a tests/Fixtures/ directory of real-world HTML snapshots (one per supplier/template) so parser regressions surface as failing tests rather than bad rows in the database.

Mutate, then re-serialise. The document is writable, which makes it useful for normalising third-party HTML before storing or re-rendering it — strip tracking pixels, rewrite relative src attributes, drop scripts:

foreach (iterator_to_array($doc->querySelectorAll('script, iframe.tracker')) as $node) {
    $node->parentNode->removeChild($node);
}

$clean = $doc->saveHTML();

Cheap structural validation. In a FormRequest or an import pipeline, use the parser to reject "HTML" that is actually plain text or truncated markup before it reaches your renderer.

Gotchas and Tips

  • This is 0.x software — pin it. composer require vardumper/extended-htmldocument:0.3.* and commit the lock file. There is no BC promise below 1.0; minor bumps have moved method names before. 0.3.4 is a patch release cut from an automated sync, so it should be a drop-in inside 0.3.x, but read the diff in src/ if anything selector-related misbehaves after composer update.
  • HTML5 parsing changes the tree you get back. masterminds/html5 inserts implied elements the way a browser does — <tbody> inside tables, <head>/<body> scaffolding, auto-closed <p> tags. XPath written against libxml's output will silently miss nodes. Always query with CSS selectors, or dump $doc->saveHTML() when a selector returns null.
  • Encoding is your job. Feed the parser UTF-8 and include a <meta charset="utf-8"> in any fragment you wrap. Mojibake in extracted text is almost always a missing charset hint, not a parser bug.
  • querySelector() returns null, querySelectorAll() returns an empty list. No exceptions. Guard every scalar extraction with ?->nodeValue and an explicit fallback, otherwise a template change upstream becomes a TypeError in a queue worker at 3am. Validate extracted DTOs before persisting.
  • Parsing is not sanitising. Never treat the output of saveHTML() as safe for {!! !!}. Removing <script> tags via selectors is not an XSS defence — use a dedicated sanitiser (e.g. mews/purifier) for anything user-supplied.
  • Memory scales with document size. The whole DOM lives in memory; a 10 MB page is not a 10 MB cost. Cap response size, or short-circuit with str_contains before parsing when you only need one field.
  • Malformed input can throw. Wrap loadHTML() in a try/catch and log the source URL plus a truncated body — the HTML5 parser is lenient but still rejects genuinely broken byte sequences, and the message alone won't tell you which supplier broke.
  • getElementById() actually works here. With native DOMDocument it requires a DTD to be loaded first; the HTML5 parser preserves id semantics, so this is one of the quieter reasons to prefer the package.
  • Vendor risk is real. This is a small, single-maintainer package. If your usage grows into XPath-heavy scraping or you need crawling/HTTP features, the escape hatch is small and clean: depend on masterminds/html5 + symfony/dom-crawler directly, or wrap this class behind your own HtmlExtractor interface now — then swapping implementations is a one-file change.
  • Extension point: the class extends DOMDocument, so you can subclass it to add project helpers (e.g. text(string $selector): ?string) without forking. Keep such a subclass in app/Support/, not in vendor/.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums