https://github.com/daun/statamic-cache-directives
Parse conditional comments for dynamic fragments in cached Statamic pages
https://github.com/daun/statamic-cache-directives
Last synced: 26 days ago
JSON representation
Parse conditional comments for dynamic fragments in cached Statamic pages
- Host: GitHub
- URL: https://github.com/daun/statamic-cache-directives
- Owner: daun
- License: mit
- Created: 2026-07-05T15:24:15.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-07-06T10:18:19.000Z (about 1 month ago)
- Last Synced: 2026-07-10T19:19:18.818Z (27 days ago)
- Language: PHP
- Size: 63.5 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Statamic Cache Directives
Parse conditional HTML comment directives after Statamic has rendered a page, so cached pages can still include small dynamic fragments.
This package is a lightweight alternative to Statamic's [`nocache`](https://statamic.dev/tags/nocache) tag. It is useful for tiny auth-, role-, or request-dependent islands where you only need to keep or remove existing markup without the overhead of the nocache data/session pipeline.
It is implemented as a [static caching replacer](https://statamic.dev/advanced-topics/static-caching#replacers) for Statamic's half-measure static cache.
## Installation
Install the package via composer:
```bash
composer require daun/statamic-cache-directives
```
## Registration
Enable the replacer in `config/statamic/static_caching.php`.
```diff
+ use Daun\StatamicCacheDirectives\CacheDirectiveReplacer;
'replacers' => [
CsrfTokenReplacer::class,
NoCacheReplacer::class,
+ CacheDirectiveReplacer::class,
],
```
## Usage
Wrap markup in conditional comments. Matching blocks are kept when their expression evaluates to `true`; otherwise they are removed from the response.
```html
```
The second format follows the [downlevel-hidden syntax](https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/ms537512(v=vs.85)#syntax-of-conditional-comments) for conditional comments.
## Syntax
Expressions use [Symfony Expression Language](https://symfony.com/doc/current/reference/formats/expression_language.html) syntax.
### If
```html
Visible to signed-in users.
```
### Unless
```html
Visible to guests.
```
### Not
Use either `!` or `not`.
```html
Regular user content.
```
### And
Use `and` or `&&`.
```html
```
### Or
Use `or` or `||`.
```html
```
### Echo
Use `echo` to print a variable value. Echo directives can be standalone or block-style. Output is escaped for html contexts.
```
### Raw
Use `raw` to print a variable value without escaping. Use this only for values you fully control as printing untrusted data with `raw` is an XSS vector.
```html
```
### Combined expressions
Use parentheses to group subexpressions or override precedence.
```html
Visible unless signed in as a super admin.
```
### Nested data and object methods
Use `[]` for array keys and `.` for object properties or methods.
```html
```
Unknown variable names throw an `InvalidArgumentException`, so typos fail loudly.
## Built-in variables
These variables are available by default and can be used in expressions:
- `logged_in`: Current Statamic user is authenticated.
- `logged_out`: Current Statamic user is not authenticated.
- `cp_access`: Current Statamic user has control panel access.
- `super`: Current Statamic user is a super admin.
Authentication uses Statamic's configured control-panel guard: `config('statamic.users.guards.cp')`.
## Real-world examples
### Seed frontend auth state
```html
window.app = window.app || {};
window.app.authenticated = false;
window.app.authenticated = true;
```
### Swap account navigation without `nocache`
```html
Account
Log out
```
### Show edit links to super admins
```html
```
### Hide conversion prompts from signed-in users
```html
Save your favourites
Create an account to keep this list across devices.
Sign up
```
### Load control-panel JavaScript
```html
```
## Custom variables
Add custom variables during application boot using `variable()` or `variables()`. Values can be scalar values, arrays,
objects or closures. Closures are evaluated lazily when the variable is first encountered.
Use closures for request-dependent values such as auth, session, request data, or GeoIP lookups. In long-lived worker runtimes
such as FrankenPHP, Swoole, RoadRunner, or Laravel Octane, service providers can boot once per worker instead of once per request.
Passing a direct value would capture the value at boot; passing a closure resolves it for the current request.
```php
use Daun\StatamicCacheDirectives\CacheDirectiveReplacer;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
CacheDirectiveReplacer::variable('editor', fn () => auth()->user()?->hasRole('editor') ?? false);
CacheDirectiveReplacer::variable('member', fn () => auth()->user()?->isInGroup('members') ?? false);
}
}
```
For bulk registration, use `variables()`.
```php
use Daun\StatamicCacheDirectives\CacheDirectiveReplacer;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
CacheDirectiveReplacer::variables([
'in_uk' => fn () => app(GeoIp::class)->countryCode(request()->ip()) === 'UK',
'has_cart' => fn () => (bool) session('cart.items'),
]);
}
}
```
Then use those variables in comments:
```html
Create an account before checkout to save your order history.
This feature is only available in the UK.
```
## Disabling a response
If a response contains this marker anywhere, directive parsing is skipped for the **whole response**. Use it as a kill switch on routes that must never be processed (for example email or preview endpoints).
```html
```
## Ignoring a range
Wrap a section in ignore markers to leave it **verbatim** while the rest of the response is still parsed. Directives inside the range are not processed, and the wrapper comments are removed from the output. This is useful for fragments that legitimately contain conditional comments (for example Outlook `
```
> [!WARNING]
> Both markers control directive processing, so they must **never** originate from untrusted, user-controlled content. See [Security](#security) below.
## Security
This replacer scans the **entire** cached HTML response for directive comments. Cached pages frequently contain user-generated content (comments, reviews, usernames, profile fields, reflected search queries, form echoes). If any of that content can contain the strings below, it can subvert directive processing:
- **``** disables all directive processing for the page. An attacker who injects it can prevent auth-gated blocks such as `...` from being stripped, exposing that markup to every visitor.
- **`` … ``** leaves an arbitrary range unprocessed, which can likewise expose auth-gated markup wrapped inside it.
- **`` / ``** markers let injected content pair with, hide, or reveal surrounding directive blocks.
To stay safe, **strip or neutralize directive comments from user-controlled content before it is rendered into a cacheable page**. For example, remove `