.lex template to a plain PHP file and stores it in the .lexer/ directory. On subsequent requests the engine re-uses the compiled file, skipping the full parse-and-compile pipeline. When a source file changes, the engine detects the change via a dependency graph and recompiles automatically — you never need to clear the cache by hand during development.
Cache directory layout
The cache directory is always placed at{projectRoot}/.lexer/ when you use Lexer::fromConfig(). When you use the fluent API without a config file it defaults to {cwd}/.lexer/. Override it with ->cache(string $dir).
| Path | Contents |
|---|---|
.lexer/compiled/ | Compiled PHP files named {md5(cacheKey)}.php |
.lexer/ast/ | Serialised AST snapshots named {md5(cacheKey)}.ast |
.lexer/compiled/index.php | Precompiled view index used in production mode |
.lexer/view_dependencies.json | Dependency graph for automatic cache invalidation |
How compilation and caching work
First render
When you call
$lexer->render('home', $data) for the first time, the engine runs the full compilation pipeline:FileLoaderresolves'home'to an absolute path and generates a cache key (md5(path:mtime)).- The Lexer tokenises the source character-by-character (no regex structural parsing).
- The Parser converts tokens into a nested AST.
DependencyGraphwalks the AST and records every static dependency (#extends,#include, component tags) with its currentfilemtime.AstValidatorenforces sandbox rules if sandbox mode is active.OptimizePassmerges adjacent text nodes and removes empty nodes.- Code generation produces a PHP source string.
FileCachewrites the compiled PHP to.lexer/compiled/{md5}.phpatomically (write to temp file, then rename).- The compiled file is
include()-d in an isolated scope with$__envinjected.
Subsequent renders (dev mode)
On every render in dev mode the engine:
- Generates the cache key from the current
filemtimeof the source file. - Checks
DependencyGraph::isStale()— if any recorded dependency has a differentfilemtimethan when it was compiled, the compiled cache is cleared. - If the key matches an existing
.phpfile and the template is not stale, the file is served directly — no recompilation. - Otherwise the pipeline from step 1 above runs again.
Production mode
In production mode all source-file I/O is skipped entirely. The engine looks up the template path in
.lexer/compiled/index.php and serves the compiled file directly. See Production mode for the full workflow.The dependency graph
What is tracked
When a template is compiled, Lex walks its AST and records every static dependency it finds:| Source construct | Tracked as |
|---|---|
#extends('layouts.app') | Layout dependency |
#include('partials.header') | Include dependency |
#includeIf, #includeWhen, #includeFirst (string literal args) | Include dependencies |
<Card />, <Alert> component tags | Component dependencies |
What is not tracked
Dynamic include expressions — where the template name is a variable — cannot be statically resolved and are silently skipped:The view_dependencies.json format
The graph is persisted to .lexer/view_dependencies.json. Keys are absolute template paths; nested keys are absolute dependency paths; values are the Unix filemtime timestamps recorded at compile time.
Invalidation flow
Whenheader.lex is modified, the sequence is:
DependencyGraph API
You can query the graph directly from PHP for tooling, warm-up scripts, or debugging.getDeps(string $template): array
Return the recorded dependency map for $template — an associative array of absoluteDepPath => mtime.
getDependents(string $template): array
Return every template that lists $template as a dependency (reverse lookup). Useful for finding all templates that would be invalidated if a shared partial changes.
isStale(string $template): bool
Return true if at least one recorded dependency of $template has a different filemtime than when the template was last compiled. Returns false when no dependency data has been recorded.
all(): array
Return the full forward dependency map (templatePath => [depPath => mtime]). Useful for CLI tooling and inspection.
Clearing the cache
There are two ways to clear the compiled cache:- Delete the directory
- FileCache::flush()
Delete the entire
.lexer/ directory. On the next render request Lex creates a fresh cache from scratch.