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

Reader Laravel Package

phug/reader

Phug Reader is a PHP library for reading Pug templates and turning them into a structured syntax tree. It serves as the parsing foundation for the Phug template engine, enabling developers to inspect, transform, and compile Pug source reliably in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it as a normal Composer dependency — the reader has no service provider or framework integration:

composer require phug/reader

You now have the two-stage front end of the Phug pipeline (phug/lexer → phug/parser) behind one call. The typical entry point is a single parse call that turns a Pug source string into a node tree:

use Phug\Reader;

$document = (new Reader())->parse(
    "div.container\n  h1.title Hello\n  p= \$name",
    __DIR__ . '/views/page.pug' // pass the real path, see Gotchas
);

var_dump($document->getName());        // "document"
var_dump($document->getChildren());    // block/nodes…

Where to look first, in order:

  1. phug/lexer and phug/parser — this package is a thin convenience layer over them. Any behaviour you cannot explain lives in those two repos, not here.
  2. The Phug\Parser\Node\* classes — that's your working vocabulary (DocumentNode, ElementNode, TextNode, AttributeNode, ConditionalNode, MixinCallNode, IncludeNode, …).
  3. The package's own test suite — with 4 stars and no dependents, tests are the de-facto documentation.

The first thing most people actually want this for is not rendering HTML (that's phug/renderer), but inspecting a template: building a small script that parses every .pug file in resources/views and prints the tag tree. That single script proves the package works in your environment and is immediately extendable into a class/ID inventory, a lint pass, or a migration report.

Implementation Patterns

Parse once, walk many times. Lexing + parsing is the expensive half of the Phug pipeline. In any tool that inspects the same template repeatedly (linters, generators, watch loops), parse into an AST up front, keep it in memory keyed by realpath, and run all your analyses against the tree. Only reach for phug/compiler or phug/renderer when you actually need output.

Recursive visitor as the default shape. Node traversal is uniform: getChildren() plus a getName() switch. A generic collector covers most day-to-day needs:

function walk(NodeInterface $node, callable $fn): void
{
    $fn($node);
    foreach ($node->getChildren() as $child) {
        walk($child, $fn);
    }
}

$classes = [];
walk($document, function ($node) use (&$classes) {
    if ($node->getName() === 'element' && $node->hasAttribute('class')) {
        $classes[] = (string) $node->getAttribute('class')->getValue();
    }
});

Use it as a gatekeeper, not a renderer. A very common production workflow: parse the template in CI to fail fast on syntax errors with file/line context, then let the normal renderer handle the happy path. Parsing is cheaper than rendering and produces much more precise diagnostics than "the page 500'd".

Combine with phug/compiler for targeted compilation. If you only need to render a fragment (say, a mixin extracted from a large layout), parse to find the node, then hand that subtree — not the whole file — to the compiler. This is the clean way to build "render one component" endpoints.

Dependency/inventory pass before deploys. Walk the tree for include, extends, and mixin-call nodes to produce an asset manifest (which partials does this view touch?) for cache warming, cache busting, or precompiling. Note that resolution itself is not the reader's job (see Gotchas).

Wrap it in a small domain service. Bind a TemplateInspector class into your container rather than calling new Reader() in controllers or commands. It gives you one place to inject options, memoise ASTs, and swap in a custom parser subclass later.

Options pass-through is your configuration surface. Whatever you pass as the third argument is forwarded down the lexer/parser chain — this is how you turn on/off features (pretty-print handling, custom keywords, etc.) without forking. Keep a single options array in config and spread it everywhere, so a Pug dialect change is a one-line edit.

Testing: because the tree is a plain object graph, assert on getName()/attribute values rather than on rendered strings. Fixture .pug files plus snapshot-style assertions on a flattened node list are far more stable than HTML diffs.

Gotchas and Tips

  • It parses; it does not compile, render, or resolve. include, extends, and mixin definitions appear as nodes with unresolved paths/names. If you need the real file graph, use phug/renderer's dependency handling or write your own resolver against your view root.
  • Always pass a real filename. Without it, parser exceptions lose their source context and you get line numbers with no file — miserable in CI logs. Use __FILE__-style absolute paths, not 'template.pug'.
  • Indentation failures are lexer failures. Mixed tabs/spaces, a stray CRLF, or a UTF-8 BOM at the top of the file produces confusing "unexpected indent"-style errors. Normalise file contents (str_replace("\r\n", "\n"), strip BOM) before parsing when reading from disk, uploads, or a DB.
  • Two exception families. Lexer errors and parser errors are distinct namespaces; catching only one will let the other escape. Catch the shared base (or both) at the boundary of your tool and re-throw with your own file context.
  • getName() returns strings, and the vocabulary is version-sensitive. instanceof against Phug\Parser\Node\* classes is more refactor-safe than string matching. Snapshot your node-name usage when bumping versions — Phug's minor releases have historically renamed/tuned node internals.
  • Attribute values are themselves nodes, often with interpolation segments. Casting to string works for the simple case; for class="a #{b}" you need to walk the attribute's children. If you only need the raw source, the token/attribute helpers are friendlier than reconstructing from the tree.
  • Pin the version and read the release notes. 4 stars, 0 dependents, and a future-dated release mean you are effectively the only person testing your combination of PHP version + lexer + parser. Use a caret constraint on a known-good minor, commit the lockfile, and vendor-audit before upgrading.
  • Debugging: when a node looks wrong, drop below the reader — instantiate the lexer alone and dump the token stream. The gap between "token is fine" and "node is wrong" localises the bug to the parser stage in seconds, which the reader layer hides.
  • Extension points are on the lexer/parser, not the reader. Custom syntax means subclassing and registering keywords/token handlers on Phug\Parser/Phug\Lexer (e.g. addKeyword, addTokenHandler) and wiring those classes in through the options array. Treat any such customisation as fork-level code: it is not covered by this package's tests.
  • No memoisation, no shared state. Each parse() call is self-contained and independent — good for correctness (no cross-request bleed), but a watch loop that re-parses 500 templates will feel it. Add your own AST cache keyed on file mtime.
  • PHP version drift: Phug's older releases target PHP 7.x-era signatures (loose typing, nullable returns). On PHP 8.1+ expect deprecation noise from the dependency chain; run with error_reporting(E_ALL) once on a staging branch before rolling into production.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager