Caching & Minification
Three production concerns sit at the edge of the pipeline: avoiding recompilation of identical input, avoiding the cost of building a compiler on every request, and shrinking the generated CSS. All three are PHP-side helpers around the compiler.
Caching
CSS generation runs the whole pipeline on every call. For content that doesn't change between requests, the file-based cache writes the result to disk and serves it on subsequent calls.
Pass cache to tw::generate() — true for the default directory, or a path string for a custom one:
use TailwindPHP\tw;
// Cache to the default directory: sys_get_temp_dir() . '/tailwindphp'
$css = tw::generate([
'content' => '<div class="flex p-4">Hello</div>',
'cache' => true,
]);
// Cache to a custom directory
$css = tw::generate([
'content' => '<div class="flex p-4">Hello</div>',
'cache' => '/path/to/cache',
]);
// With a time-to-live (seconds)
$css = tw::generate([
'content' => '<div class="flex p-4">Hello</div>',
'cache' => true,
'cacheTtl' => 3600, // expire after 1 hour
]);
The cache directory is created automatically if it doesn't exist.
Cache key
The key is derived from the content, the CSS configuration, and the minify flag — hashed into the filename tailwind_<hash>.css. Any change to the markup, the CSS input, or whether minification is on produces a different key, and therefore a different file.
The CSS configuration in the key is the inline css plus the contents of the files named in importPaths. Two inputs are not part of it: files pulled in by a nested @import inside those files, and the TailwindPHP version. Clear the cache after editing a nested import or upgrading the package, or the old output keeps being served.
Time-to-live
cacheTtl is a lifetime in seconds. On a cache hit, the file's modification time is compared against the current time; if the file is older than cacheTtl, it is treated as a miss and recompiled. With no cacheTtl, entries never expire — clear them explicitly.
Clearing the cache
tw::clearCache() (or the clearCache() function) removes the tailwind_*.css files from a cache directory and returns the number of files deleted.
use TailwindPHP\tw;
use function TailwindPHP\clearCache;
$deleted = tw::clearCache(); // default directory
$deleted = tw::clearCache('/path/to/cache'); // custom directory
clearCache('/path/to/cache'); // function form
Compiler state cache
The output cache only helps when the same content comes back. When the content changes on every call, the expensive part is building the compiler itself: resolving @import "tailwindcss", parsing the bundled resources, and accumulating the theme. That phase depends only on the input CSS, so it can be stored and reused.
Pass stateCacheFile to tw::compile() (or new TailwindCompiler($css, $options)):
use TailwindPHP\tw;
$compiler = tw::compile($themeCss, [
'stateCacheFile' => __DIR__ . '/cache/tailwind-state.php',
]);
$css = $compiler->generateExact($html, true);
On the first construction the state is written to that path as a plain PHP file, which OPcache can share across requests; later constructions load it instead of parsing again. The output is byte-identical to an uncached compiler.
- The file is validated against an engine fingerprint (the size and mtime of TailwindPHP's sources and resources), a key derived from the input CSS and the
baseandimportSearchPathsoptions, and the size and mtime of every file the input imported. A mismatch rebuilds and rewrites it, so upgrading TailwindPHP or editing an imported file needs no manual step. - You own the path: use one file per distinct input CSS, in a directory the web server can write to. Writes are atomic (a temporary file plus
rename()). - Inputs that use a custom
importResolverbypass the cache, because the resolver's behavior cannot be fingerprinted. - The same cache is available around the lower-level
compile()array API asTailwindPHP\StateCache\compileCssCached($css, $cacheFile, $options).
tw::generate() does not take stateCacheFile; it builds a fresh compiler on every call. Use a compiler instance when construction cost matters.
Minification
Minification shrinks the output for production. The minify flag emits minified CSS directly during serialization, so it is both the fastest and the most precise path; tw::minify() runs the string-based minifier (src/_tailwindphp/CssMinifier.php) over an existing CSS string:
use TailwindPHP\tw;
// Minify during generation (serializer path, preferred)
$css = tw::generate([
'content' => '<div class="flex p-4">Hello</div>',
'minify' => true,
]);
// Or minify an existing string (string-based post-pass)
$css = tw::generate('<div class="flex p-4">Hello</div>');
$minified = tw::minify($css);
The two paths produce equivalent structure but not identical bytes. The serializer path preserves descendant :not() selectors that the string pass collapses, so prefer the minify flag for generated output and reserve tw::minify() for CSS that arrives as a string.
What it does
Both paths apply the same set of size reductions:
| Step | Effect |
|---|---|
| Remove comments | Strips /* … */ blocks |
| Collapse whitespace | Collapses runs of whitespace and removes it around { } ; : , and selector combinators |
| Shorten hex colors | #ffffff → #fff, #aabbcc → #abc |
| Remove zero units | 0px → 0 (preserves 0s/0ms time values) |
| Shorten font-weight | font-weight:normal → 400, font-weight:bold → 700 |
| Remove empty rules | Drops selectors with empty declaration blocks |
By design it does not merge duplicate selectors or combine shorthand properties — both make debugging harder and can affect the cascade.
CLI: --minify vs --optimize
The CLI exposes the serializer minify path through two flags:
# Optimize and minify (smallest output)
tailwindphp -i ./src/app.css -o ./dist/styles.css --minify # or -m
# Meant to optimize without minifying
tailwindphp -i ./src/app.css -o ./dist/styles.css --optimize
In the current release --optimize also minifies, so both commands write the same bytes (#9). The lightningcss-equivalent transforms run on every build either way, so plain output (no flag) is already the optimized, readable form.
For the rest of the public API — tw::generate(), tw::compile(), inspection methods, and input formats — see the API reference.