moving away from gross globals
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# 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):
|
||||
```bash
|
||||
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 `require`s 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: `@novaconium` → `FRAMEWORKPATH/twig` (layout partials: `master.html.twig`, `nav.html.twig`, `head.html.twig`, etc.) and `@novacore` → `FRAMEWORKPATH/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`.
|
||||
Reference in New Issue
Block a user