phug/formatter
A formatting extension for Phug, the PHP Pug template engine. It pretty-prints and normalizes Pug source with configurable options, helping keep templates readable and consistent in Laravel and PHP projects.
phug/formatter is the compile-time backend of the Phug stack (Pug/Jade for PHP). It is not a standalone HTML pretty-printer: it takes the AST produced by phug/parser (node classes live in phug/ast) and emits the PHP template code that the renderer later executes. Everything that determines what a tag actually looks like in the compiled file — attribute merging, boolean-attribute collapsing, escaping, self-closing rules, and indentation — lives here. The 2 stars / 0 dependents numbers simply reflect that nobody requires it directly; it arrives transitively through phug/phug.
Install the stack, not the leaf:
composer require phug/phug # pulls formatter + ast + parser + compiler + lexer + renderer
# only if you are building a custom pipeline:
composer require phug/formatter
Read these four places first, in order:
| Path | Why |
|---|---|
src/Phug/Formatter.php |
Entry point, option handling, format registry |
src/Phug/Formatter/Format/ |
One class per node kind (TagFormat, AttributeFormat, TextFormat, …) |
src/Phug/Formatter/Element/ |
Intermediate elements the formats consume |
src/Phug/Formatter/Extension/ |
The sanctioned way to hook in without forking |
The first thing most developers ever do with this package is flip pretty:
$renderer = new \Phug\Renderer([
'pretty' => true,
'cache_dir' => __DIR__.'/cache',
]);
echo $renderer->render('div.card: p Hello');
Then set pretty => false, re-render, and diff. Read the generated file in cache_dir side by side with the diff — that is the fastest way to build a mental model of what the formatter is responsible for.
Method names below track the 1.x line. The package is small; if a call is missing in your version,
grepfor it insrc/before assuming the approach is wrong.
Configure through the renderer, never by hand-instantiating the formatter. The compiler builds it internally and passes options down. Injecting your own is possible ($compiler->setFormatter(...) or the formatter option) but you inherit the wiring — only do it when you need a subclass or extra extensions.
Two-stage debugging. Stop between stages to localise bugs:
$ast = $renderer->getCompiler()->getParser()->parse($pug); // structure wrong? formatter is innocent
$php = $renderer->getCompiler()->compile($pug); // bad PHP here? it is the formatter
If the PHP looks right but the page is wrong, you are chasing a compiler/renderer bug.
Customising output: extend a Format, register it for an Element. This is the core extension pattern and the reason the package is split into Format and Element namespaces.
use Phug\Formatter\Format\TagFormat;
use Phug\Formatter\Element\TagElement;
class VoidTagXmlFormat extends TagFormat
{
// open the parent first: the hook may be handleTag(), __invoke(), or a trait method
protected function handleTag(TagElement $element)
{
$code = parent::handleTag($element);
// post-process the generated PHP string, e.g. force <br /> instead of <br>
return $code;
}
}
$formatter->setFormat(TagElement::class, VoidTagXmlFormat::class);
Prefer a Formatter extension over a global subclass. Extensions are composable and are how the built-in attributes module plugs in. Mirror any class in src/Phug/Formatter/Extension/ — same getOptions() / format registration shape — then attach it via the renderer's addExtension(). This keeps you upgrade-safe.
Attribute semantics are the highest-traffic customisation. class accepts arrays and merges with static classes; null/false values drop the attribute; true collapses to a boolean attribute; &attributes($array) splats and lets later values win. If your markup differs from expectation, fix the attribute element/format, not the templates.
Escaping is decided here, not at runtime. #{...} vs !{...} and the pattern / pattern_escape options control the echo statement the formatter writes. When output is double-escaped or raw when it shouldn't be, look at those options before touching template code.
Test compiled output, not rendered output. Golden-file tests over compile() catch formatter regressions precisely and keep failures readable.
phug/ast, phug/parser, phug/compiler, and phug/formatter move in lockstep and share internal element contracts. Require phug/phug:^1.x and let Composer co-install; never mix major versions.pretty or any formatter option does not invalidate previously compiled templates in cache_dir. Clear it (or the framework's view cache) after every option change or upgrade — this accounts for a large share of "the option does nothing" reports.pretty mangles inline content. Newlines get inserted between inline elements and around text, which visibly breaks <pre>, <textarea>, and a > span layouts. Keep pretty off in production and test with your real templates, not a hello-world.indent_char, indent_size) are a separate concern. grep -n indent src/Phug/Formatter.php to confirm the exact key for your version before filing a bug.&attributes ordering surprises. Merge order is "later wins", which means a splat placed before an explicit attribute will not override it. Reorder, don't patch the formatter.cache_dir; if it reads correctly, your problem is downstream. Re-compile after edits — stale cache will lie to you.src/Phug/Formatter/Event/ for the formatting/stringify hooks) → subclass a Format → vendor patch.composer.lock, wrap every customisation in regression tests, and prefer composition (extensions, event hooks) over forking so you can take upstream fixes.How can I help you explore Laravel packages today?