Files
novaconium/CLAUDE.md
T
2026-07-03 21:05:39 -07:00

6.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

Novaconium is a PHP 8.1+ micro-framework distributed as a Composer package (4lt/novaconium, PSR-4 autoloaded under the Novaconium\ namespace from src/). This repo is the framework itself — there is no "app" here to run directly. Consuming projects composer require it and copy skeleton/novaconium/ into their own project to get an App/ directory (config, controllers, views, routes) plus a public/ entrypoint.

Because of this, most of the interesting behavior lives in two places:

  • src/ — the framework runtime (bootstrap, router, session, db, etc.)
  • controllers/ + views/ + twig/ — the framework's own built-in admin panel ("NOVACONIUM" control panel routes), shipped so consuming apps get login/dashboard/page-editing/messages for free.

skeleton/ is what gets copied into a new consuming project — treat it as a template, not live framework code.

Development

There is no local PHP/Composer install expected — Composer, PHP/Apache, and Sass all run in Docker containers (see docs/docker.md, docs/Composer.md, docs/Sass.md). There is no test suite or linter configured in this repo.

Compiling Sass (from repo root):

docker run --rm -v "$(pwd):/usr/src/app" -w /usr/src/app sass-container --style=compressed sass/novaconium.sass skeleton/novaconium/public/css/novaconium.css

For iterating against a real consuming project without re-running composer require on every change, use the fake autoloader dev pattern in docs/Dev-Fake_autoload.md (registers a spl_autoload_register shim pointing at a manually cloned copy of this repo under the app's vendor/4lt/novaconium).

Request lifecycle

Entry point in a consuming app is App/public/index.php (see skeleton/novaconium/public/index.php), which:

  1. Defines BASEPATH (app root) and FRAMEWORKPATH (BASEPATH/vendor/4lt/novaconium) — these two constants are used everywhere in src/ to resolve app vs. framework paths, and are assumed to already be defined by the time any framework code runs.
  2. Requires Composer's autoloader, then FRAMEWORKPATH/src/novaconium.php (the bootstrap).
  3. Calls the global makeitso() at the very end.

src/novaconium.php (the bootstrap) runs in this order and wires up several globals that framework code and app controllers rely on implicitly ($config, $log, $data, $session, $messages, $db, $post, $redirect, $router):

  1. Loads App/config.php (falls back to the skeleton's default config if the app hasn't created one).
  2. Loads src/functions.php (global helpers: dd(), makeitso()) and src/twig.php (global view() helper).
  3. Creates $log (Novaconium\Logger), $session (Novaconium\Session), $messages (Novaconium\MessageHandler, seeded from flashed session messages), $db (Novaconium\Database, only if config['database']['host'] is set), $post (Novaconium\Post, only if $_POST is non-empty), $redirect (Novaconium\Redirect).
  4. Builds $router (Novaconium\Router), whose constructor resolves the matched controller file, then immediately requires it — so the matched controller file executes inline, still inside bootstrap, with all the globals above already in scope.

Controllers are plain PHP scripts (not classes) that use the ambient globals directly (e.g. $session->get(...), $messages->error(...), $post->get(...), $db->getRow(...)) and typically end by calling the global view($templateName, $data) or setting $redirect->url(...) followed by makeitso() to end the request (writes session, closes DB, performs any pending redirect, flashes messages, then exit).

Router (src/Router.php)

Full behavioral spec is in docs/Router.md — read it before touching routing logic, since several quirks are intentionally preserved for backward compatibility, not bugs:

  • Only the first {param} in a route pattern is ever extracted; subsequent params in the same pattern are ignored.
  • For parameterized routes, the first route whose static prefix + segment count matches wins, even if that route has no handler for the current HTTP method — the router does not keep searching for a better match.
  • No wildcard/optional-segment support; segment counts must match exactly.

Routes are plain associative arrays keyed by path, mapping get/post to a controller string (see config/routes.php for the framework's own admin routes, skeleton/novaconium/App/routes.php for the app-level shape). App/routes.php routes are merged with (and can override) framework routes. Controller strings prefixed with NOVACONIUM resolve against FRAMEWORKPATH/controllers/; everything else resolves against BASEPATH/App/controllers/. Unresolved routes/files fall back to the app's 404.php, then the framework's controllers/404.php.

Views (Twig)

view($name, $data) (src/twig.php) loads templates from App/views/ by default, with two additional Twig namespaces registered: @novaconiumFRAMEWORKPATH/twig (layout partials: master.html.twig, nav.html.twig, head.html.twig, etc.) and @novacoreFRAMEWORKPATH/views (the framework's built-in admin pages: dashboard, pages, messages, settings). Apps can override any framework template by placing a same-named file under App/templates/ (override namespace). The framework's own controllers render via the @novacore/... namespace (e.g. view('@novacore/dashboard', $data)).

Other framework primitives (all under src/, namespace Novaconium)

  • Database — thin mysqli wrapper (query, getRow, getRows); always uses prepared statements but binds every parameter as a string type.
  • Session — thin $_SESSION wrapper; auto-initializes token/messages/formData/errors keys; flash() reads-then-deletes.
  • MessageHandler — categorized flash messages (error/warning/notice/success), persisted across requests via $session.
  • Post — sanitizes $_POST via FILTER_SANITIZE_FULL_SPECIAL_CHARS on construction; access via $post->get($key).
  • Redirect — buffers a target URL/status; only the last url() call before makeitso() takes effect; actual header() redirect happens inside makeitso().
  • Logger — level-gated file logger (DEBUG/INFO/WARNING/ERROR/NONE), configured via config['logfile']/config['loglevel'].
  • src/Services/ — newer, thinner service classes (Auth, TagManager) alongside the older top-level src/ classes; check here first before adding new cross-cutting logic.

Configuration

App-level config lives in App/config.php (see docs/ConfigurationFile.md and skeleton/novaconium/App/config.php for the shape): database (mysqli connection), base_url, secure_key (used to pepper password hashes via hash_hmac('sha3-512', ...), see controllers/authenticate.php), logfile, loglevel, plus optional matomo_url/matomo_id/fonts passed straight into Twig's global $data.