debug config also drives Twig debug + bypasses static cache
When debug is true: enable Twig debug mode (dump(), auto_reload, DebugExtension), and stop caching sidecar-less pages — Renderer skips the cache write, the dev router skips serving cache files, and bootstrap clears stale cache on the first request that reaches PHP. Also update docs: new Debug mode section on the config page, cache- opt-out notes (debug + empty index.php sidecar) on the caching page, and correct the Sass build target to App/css/main.css in AGENTS.md and the styling page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011FZQHKxu24mvojrJDwhWVS
This commit is contained in:
@@ -191,7 +191,8 @@ except `public/cache/*` and `novaconium/contact-log.txt`.
|
|||||||
(`php novaconium/bin/<script>.php`) — distinct from
|
(`php novaconium/bin/<script>.php`) — distinct from
|
||||||
`bootstrap.php`/`autoload.php`/`config.php`, which are only `require`'d.
|
`bootstrap.php`/`autoload.php`/`config.php`, which are only `require`'d.
|
||||||
- CSS compiles from `novaconium/sass/main.sass` (indented syntax) to
|
- CSS compiles from `novaconium/sass/main.sass` (indented syntax) to
|
||||||
`public/css/main.css`:
|
`App/css/main.css` (project-owned; docker-compose mounts `App/css` to
|
||||||
`sass --load-path=App/sass --load-path=novaconium/sass/defaults --no-source-map novaconium/sass/main.sass public/css/main.css`
|
the container's `public/css`, and it's served at `/css/main.css`):
|
||||||
|
`sass --load-path=App/sass --load-path=novaconium/sass/defaults --no-source-map novaconium/sass/main.sass App/css/main.css`
|
||||||
— commit the regenerated CSS. See `/admin/docs/styling` for a Docker
|
— commit the regenerated CSS. See `/admin/docs/styling` for a Docker
|
||||||
fallback if `sass` isn't installed locally.
|
fallback if `sass` isn't installed locally.
|
||||||
|
|||||||
@@ -52,7 +52,17 @@ $matomoUrl = $config['matomo_url'] !== '' ? rtrim($config['matomo_url'], '/') .
|
|||||||
$adminAuthEnabled = (bool) $config['admin_auth_enabled'];
|
$adminAuthEnabled = (bool) $config['admin_auth_enabled'];
|
||||||
|
|
||||||
$cache = new Cache($config['cache_dir']);
|
$cache = new Cache($config['cache_dir']);
|
||||||
$renderer = new Renderer($config['pages_dirs'], $cache, $adminAuthEnabled, $matomoUrl, $config['matomo_site_id'], $config['site_name'], $config['content_index_enabled']);
|
|
||||||
|
// In debug mode nothing should be served from the static cache. The dev
|
||||||
|
// router already skips it; under Apache a pre-existing cache file would be
|
||||||
|
// served before PHP runs, so drop the whole cache on the first request
|
||||||
|
// that does reach PHP. Renderer also stops writing new cache files while
|
||||||
|
// debug is on, so this only ever runs against stale leftovers.
|
||||||
|
if ($config['debug']) {
|
||||||
|
$cache->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
$renderer = new Renderer($config['pages_dirs'], $cache, $adminAuthEnabled, $matomoUrl, $config['matomo_site_id'], $config['site_name'], $config['content_index_enabled'], $config['debug']);
|
||||||
|
|
||||||
// Every route under /admin/* — clear-cache, docs, users, and any admin
|
// Every route under /admin/* — clear-cache, docs, users, and any admin
|
||||||
// page a project adds later — is gated here, once, rather than in each
|
// page a project adds later — is gated here, once, rather than in each
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
|
|
||||||
<p>Two kinds of route are excluded from the cache unconditionally, regardless of whether they have a sidecar: every <a class="icon-link" href="/admin/docs/drafts">{{ icons.lock() }}draft page</a> and every <code>/admin/*</code> route. Both are gated by the <a class="icon-link" href="/admin/docs/admin-auth">{{ icons.lock() }}admin login</a>, and a cached copy would bypass that check entirely — <code>.htaccess</code> serves a cached file before PHP (and therefore any auth check) ever runs, so a cached admin or draft page would be served to anyone, unauthenticated, forever after the first authenticated view. See <a class="icon-link" href="/admin/docs/drafts">{{ icons.lock() }}Draft pages</a> for the full write-up.</p>
|
<p>Two kinds of route are excluded from the cache unconditionally, regardless of whether they have a sidecar: every <a class="icon-link" href="/admin/docs/drafts">{{ icons.lock() }}draft page</a> and every <code>/admin/*</code> route. Both are gated by the <a class="icon-link" href="/admin/docs/admin-auth">{{ icons.lock() }}admin login</a>, and a cached copy would bypass that check entirely — <code>.htaccess</code> serves a cached file before PHP (and therefore any auth check) ever runs, so a cached admin or draft page would be served to anyone, unauthenticated, forever after the first authenticated view. See <a class="icon-link" href="/admin/docs/drafts">{{ icons.lock() }}Draft pages</a> for the full write-up.</p>
|
||||||
|
|
||||||
|
<p>To opt a single page out of caching permanently, give it a sidecar — presence is all that's checked, so an <strong>empty <code>index.php</code></strong> is enough (<code><?php return [];</code> if you'd rather it read as deliberate). The page then re-renders through PHP/Twig on every request. Reach for this when a sidecar-less page's output actually varies (a current-time stamp, a random pick) but you don't otherwise need sidecar logic.</p>
|
||||||
|
|
||||||
|
<p>The whole mechanism is disabled while <a href="/admin/docs/config">debug mode</a> is on: sidecar-less pages are re-rendered on every request and nothing is written to or served from <code>public/cache/</code>. Set <code>'debug' => false</code> in <code>App/config.php</code> for production so static caching takes effect.</p>
|
||||||
|
|
||||||
<p>To force a single page to re-render, delete its file under <code>public/cache/</code>. To clear everything at once, there are two equivalent options:</p>
|
<p>To force a single page to re-render, delete its file under <code>public/cache/</code>. To clear everything at once, there are two equivalent options:</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -23,6 +23,24 @@ return [
|
|||||||
|
|
||||||
<p>Deleting <code>App/config.php</code> entirely is just as valid as leaving it in place returning an empty array — either way, every setting falls back to <code>novaconium/config.php</code>'s defaults.</p>
|
<p>Deleting <code>App/config.php</code> entirely is just as valid as leaving it in place returning an empty array — either way, every setting falls back to <code>novaconium/config.php</code>'s defaults.</p>
|
||||||
|
|
||||||
|
<h2>Debug mode</h2>
|
||||||
|
|
||||||
|
<p><code>debug</code> (default <code>true</code> in <code>novaconium/config.php</code>) is a single boolean that turns on everything you want while developing and nothing you want in production. Set <code>'debug' => false</code> in <code>App/config.php</code> before deploying:</p>
|
||||||
|
|
||||||
|
<pre><code><?php
|
||||||
|
// App/config.php
|
||||||
|
return [
|
||||||
|
'debug' => false,
|
||||||
|
];</code></pre>
|
||||||
|
|
||||||
|
<p>When <code>debug</code> is on:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>PHP errors are shown.</strong> <code>novaconium/bootstrap.php</code> sets <code>error_reporting(E_ALL)</code> and <code>display_errors</code> — notices, warnings, and fatals render in the response instead of being swallowed. With it off, nothing is changed, so you get whatever <code>php.ini</code> / the server config decides (typically: logged, not displayed).</li>
|
||||||
|
<li><strong>Twig debug mode is on.</strong> <code>Renderer</code> builds the Twig <code>Environment</code> with <code>debug</code> and <code>auto_reload</code> enabled and registers <code>DebugExtension</code>, so <code>{{ dump(some_var) }}</code> works in templates and edited templates take effect immediately.</li>
|
||||||
|
<li><strong>The static HTML cache is bypassed.</strong> Sidecar-less pages normally get frozen into <code>public/cache/</code> on first request (see <a href="/admin/docs/caching">Static caching</a>); in debug mode <code>Renderer</code> stops writing those files, the dev router (<code>public/router.php</code>) stops serving them, and <code>bootstrap.php</code> calls <code>$cache->clear()</code> on the first request that reaches PHP to flush any stale leftovers. Every page renders fresh on every request. Turn <code>debug</code> off and normal caching resumes as pages are re-requested.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Site name</h2>
|
<h2>Site name</h2>
|
||||||
|
|
||||||
<p><code>site_name</code> (default <code>'My Site'</code>) is used by the root layout as the default page <code><title></code> (when a page doesn't override the <code>title</code> block), <code>og:site_name</code>, and the footer copyright line:</p>
|
<p><code>site_name</code> (default <code>'My Site'</code>) is used by the root layout as the default page <code><title></code> (when a page doesn't override the <code>title</code> block), <code>og:site_name</code>, and the footer copyright line:</p>
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
{% block title %}Styling{% endblock %}
|
{% block title %}Styling{% endblock %}
|
||||||
|
|
||||||
{% block description %}Sass, indented syntax, compiled to public/css/main.css, with an overridable color palette.{% endblock %}
|
{% block description %}Sass, indented syntax, compiled to App/css/main.css, with an overridable color palette.{% endblock %}
|
||||||
|
|
||||||
{% block robots %}noindex, nofollow{% endblock %}
|
{% block robots %}noindex, nofollow{% endblock %}
|
||||||
|
|
||||||
{% block docs_content %}
|
{% block docs_content %}
|
||||||
<h1>Styling (Sass, indented syntax)</h1>
|
<h1>Styling (Sass, indented syntax)</h1>
|
||||||
|
|
||||||
<p>Source lives in <code>novaconium/sass/main.sass</code> (indented syntax, not SCSS). Compile it with <a href="https://sass-lang.com/dart-sass/">Dart Sass</a>, passing both Sass directories as load paths:</p>
|
<p>Source lives in <code>novaconium/sass/main.sass</code> (indented syntax, not SCSS). Compile it with <a href="https://sass-lang.com/dart-sass/">Dart Sass</a>, passing both Sass directories as load paths. The output goes to <code>App/css/main.css</code> — a project-owned directory that docker-compose mounts onto the container's <code>public/css</code>, served at <code>/css/main.css</code>:</p>
|
||||||
|
|
||||||
<pre><code>sass --load-path=App/sass --load-path=novaconium/sass/defaults novaconium/sass/main.sass public/css/main.css</code></pre>
|
<pre><code>sass --load-path=App/sass --load-path=novaconium/sass/defaults novaconium/sass/main.sass App/css/main.css</code></pre>
|
||||||
|
|
||||||
<p>There's no PHP-based Sass compiler in this project (PHP options like <code>scssphp</code> only understand SCSS syntax) — this is a manual/CI build step, not something the app does at runtime.</p>
|
<p>There's no PHP-based Sass compiler in this project (PHP options like <code>scssphp</code> only understand SCSS syntax) — this is a manual/CI build step, not something the app does at runtime.</p>
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ ENTRYPOINT ["sass"]</code></pre>
|
|||||||
|
|
||||||
<pre><code>docker build -t novaconium-sass -f Dockerfile.sass .
|
<pre><code>docker build -t novaconium-sass -f Dockerfile.sass .
|
||||||
docker run --rm -v "$(pwd):/usr/src/app" -w /usr/src/app novaconium-sass \
|
docker run --rm -v "$(pwd):/usr/src/app" -w /usr/src/app novaconium-sass \
|
||||||
--load-path=App/sass --load-path=novaconium/sass/defaults novaconium/sass/main.sass public/css/main.css</code></pre>
|
--load-path=App/sass --load-path=novaconium/sass/defaults novaconium/sass/main.sass App/css/main.css</code></pre>
|
||||||
|
|
||||||
<p>See <a href="https://git.4lt.ca/4lt/novaconium/src/branch/master/docs/Sass.md">git.4lt.ca/4lt/novaconium/docs/Sass.md</a> for this project's own write-up of the Docker approach in general; the Dockerfile and paths/flags above are this project's own, adjusted to use Dart Sass directly and this repo's current layout.</p>
|
<p>See <a href="https://git.4lt.ca/4lt/novaconium/src/branch/master/docs/Sass.md">git.4lt.ca/4lt/novaconium/docs/Sass.md</a> for this project's own write-up of the Docker approach in general; the Dockerfile and paths/flags above are this project's own, adjusted to use Dart Sass directly and this repo's current layout.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
|
use Twig\Extension\DebugExtension;
|
||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,11 +32,20 @@ final class Renderer
|
|||||||
string $matomoSiteId = '',
|
string $matomoSiteId = '',
|
||||||
string $siteName = 'My Site',
|
string $siteName = 'My Site',
|
||||||
bool $contentIndexEnabled = false,
|
bool $contentIndexEnabled = false,
|
||||||
|
private readonly bool $debug = false,
|
||||||
) {
|
) {
|
||||||
$loader = new FilesystemLoader($this->pagesDirs);
|
$loader = new FilesystemLoader($this->pagesDirs);
|
||||||
|
// 'debug' also drives PHP error display (see bootstrap.php). When on,
|
||||||
|
// it enables Twig's debug mode too: {{ dump() }} in templates plus
|
||||||
|
// auto_reload so template edits take effect without clearing cache.
|
||||||
$this->twig = new Environment($loader, [
|
$this->twig = new Environment($loader, [
|
||||||
'cache' => false,
|
'cache' => false,
|
||||||
|
'debug' => $debug,
|
||||||
|
'auto_reload' => $debug,
|
||||||
]);
|
]);
|
||||||
|
if ($debug) {
|
||||||
|
$this->twig->addExtension(new DebugExtension());
|
||||||
|
}
|
||||||
$this->twig->addGlobal('admin_auth_enabled', $adminAuthEnabled);
|
$this->twig->addGlobal('admin_auth_enabled', $adminAuthEnabled);
|
||||||
$this->twig->addGlobal('matomo_url', $matomoUrl);
|
$this->twig->addGlobal('matomo_url', $matomoUrl);
|
||||||
$this->twig->addGlobal('matomo_site_id', $matomoSiteId);
|
$this->twig->addGlobal('matomo_site_id', $matomoSiteId);
|
||||||
@@ -85,7 +95,11 @@ final class Renderer
|
|||||||
header('Content-Type: text/html; charset=utf-8');
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
echo $html;
|
echo $html;
|
||||||
|
|
||||||
if (!$hasSidecar && !$excludeFromCache) {
|
// In debug mode a sidecar-less page is re-rendered on every request
|
||||||
|
// rather than frozen into public/cache/ — so template/content edits
|
||||||
|
// show up immediately. bootstrap.php also skips serving any stale
|
||||||
|
// cached file while debug is on.
|
||||||
|
if (!$hasSidecar && !$excludeFromCache && !$this->debug) {
|
||||||
$this->cache->write($requestUri, $html);
|
$this->cache->write($requestUri, $html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -18,8 +18,18 @@ if ($uri !== '/' && (is_file($file) || is_dir($file))) {
|
|||||||
return false; // let the built-in server handle real files/dirs (css, cache)
|
return false; // let the built-in server handle real files/dirs (css, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirror .htaccess's "serve the pre-rendered static file, bypassing PHP"
|
||||||
|
// rule — but skip it when debug is on, so sidecar-less pages render fresh
|
||||||
|
// on every request (Renderer also stops writing the cache in that mode).
|
||||||
|
$novaconiumConfig = require __DIR__ . '/../novaconium/config.php';
|
||||||
|
$appConfigFile = __DIR__ . '/../App/config.php';
|
||||||
|
$config = is_file($appConfigFile)
|
||||||
|
? array_merge($novaconiumConfig, require $appConfigFile)
|
||||||
|
: $novaconiumConfig;
|
||||||
|
$debug = (bool) $config['debug'];
|
||||||
|
|
||||||
$cacheFile = __DIR__ . '/cache' . $uri . '/index.html';
|
$cacheFile = __DIR__ . '/cache' . $uri . '/index.html';
|
||||||
if (is_file($cacheFile)) {
|
if (!$debug && is_file($cacheFile)) {
|
||||||
readfile($cacheFile);
|
readfile($cacheFile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user