Clean-room copy of the reusable engines from comiida, with all instance data, secrets, dependencies, and build output excluded: - app/ Astro theme skeleton (no comiida blog posts; hero image -> placeholder) - api/ SeedProject PHP framework (no vendor/.env/config.php) - content-pipeline/ engine only (scripts/admin/prompts; empty runtime state) - astroagent.config.json + app/.astroagent/skills Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYHWLHihq3v9nxNwoPCKSn
3.7 KiB
3.7 KiB
Core Framework — core/
Framework internals. Rarely modified. Contains the request lifecycle engine.
Files
| File | Purpose |
|---|---|
Bootstrap.php |
Bootstraps environment, loads config, starts routing |
Controller.php |
Base controller class — all UI controllers extend this (via AppController) |
View.php |
View renderer — handles render(), PartialView(), asset injection |
AltoRouter.php |
URL router used for complex custom routes |
Request Lifecycle
Request
→ public/index.php
→ config.php (loads .env, defines constants)
→ core/Bootstrap.php (environment setup, session, autoload)
→ AltoRouter / auto-routing
→ ControllerClass::method($param)
→ $this->view->render(...)
→ Response (HTML or JSON)
Auto-routing maps /controller/method/param directly to ControllerClass::method($param).
Controller (core/Controller.php)
Base class. Provides:
$this->view— View instance$this->JavaScript[]— array of JS paths to inject$this->Styles[]— array of CSS paths to injectredirect($url)— HTTP redirect helper
Do not modify this file. App-wide customisation belongs in app/Controllers/AppController.php.
AppController (app/Controllers/AppController.php)
Sits between core/Controller and all UI page controllers.
UI controllers extend AppController, not Controller directly.
Current responsibilities:
- Injects
$UserProfile(logged-in user's full profile viaUser::profile()) into every view automatically.
use statement scoping rule:
- Namespaces needed on every page →
AppController - Namespaces needed by one controller → that controller only
- Namespaces needed by one method → inline as fully-qualified class name
// app/Controllers/AppController.php
use App\Components\User;
class AppController extends Controller {
function __construct() {
parent::__construct();
if (!empty($_SESSION['login']['userid'])) {
$this->view->UserProfile = User::profile($_SESSION['login']['userid']);
}
}
}
View (core/View.php)
Properties:
- Any property set on
$this->viewbecomes a variable in the rendered template.
Methods:
render($viewPath, $wrapper = '', $useWelcome = true)— render a viewPartialView($partialName)— load and return a partial as a string
Render signatures used in practice:
$this->view->render(__CLASS__ . '/' . __FUNCTION__); // default admin wrapper
$this->view->render(__CLASS__ . '/' . __FUNCTION__, 'login'); // login wrapper
$this->view->render('clients/index', true, 'site'); // legacy style
Routing
Auto-Routing (default)
| URL | Maps to |
|---|---|
/clients |
Clients::index() |
/clients/show/123 |
Clients::show(123) |
/products/edit/456 |
Products::edit(456) |
AltoRouter (complex routes)
Define in public/routes.php or api/routes.php:
// Named route with typed parameter
$router->map('GET', '/users/[i:id]', 'Users#show', 'user_show');
// Multiple parameters
$router->map('GET', '/blog/[i:year]/[i:month]/[*:slug]', 'Blog#show', 'blog_post');
// POST
$router->map('POST', '/api/users/create', 'UsersAPI#create', 'api_user_create');
Match type tokens:
[i:id]— integer[a:action]— alphanumeric (A-Z, a-z, 0-9, -)[h:key]— hex[*:trailing]— catch-all (no slashes)[**:path]— catch-all including slashes
// Generate URL from named route
echo $router->generate('user_show', ['id' => 5]); // → /users/5