Routing
PointArt uses PHP attributes to define routes directly on controller classes and methods — no configuration files, no route registration boilerplate.
Namespaces
PointStart\Attributes — Router, Route, HttpMethod, RequestParamPointStart\Core — App, RouteHandler, Renderer
Request Lifecycle
HTTP Request
│
▼
index.php ← single entry point
│
▼
App::boot() ← creates Container + Router, dispatches
│
├── Container ← resolves Controller + its dependencies
│
├── Router ← matches URL + HTTP method to a Controller method
│
▼
Controller method ← business logic, calls Model statics
│
▼
Renderer::render() ← extracts vars, requires .php view file
│
▼
HTML Response
Controllers
Place controllers in app/components/. They are auto-scanned on the first request. Mark a class with #[Router] and its methods with #[Route].
#[Router(name: 'user', path: '/user')]
class UserController {
#[Route('/list', HttpMethod::GET)]
public function index(): string {
$users = User::findAll();
return Renderer::render('user.list', ['users' => $users]);
}
#[Route('/show/{id}', HttpMethod::GET)]
public function show(int $id): string {
$user = User::find($id);
if ($user === null) {
return Renderer::render('user.notfound');
}
return Renderer::render('user.show', ['user' => $user]);
}
#[Route('/create', HttpMethod::POST)]
public function create(
#[RequestParam] string $name,
#[RequestParam] string $email
): string {
$user = new User();
$user->name = $name;
$user->email = $email;
$user->save();
return Renderer::render('user.show', ['user' => $user]);
}
}
#[Router]
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | URL prefix applied to every route in the class (e.g. '/user'). Default: '' |
name | string | No | Logical name for the controller. Default: '' |
#[Route]
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Route path, relative to the controller prefix. Supports {param} placeholders |
method | HttpMethod | No | HttpMethod::GET or HttpMethod::POST. Default: GET |
csrfExempt | bool | No | Skip CSRF validation for this route (e.g. webhooks, public APIs). Default: false |
CSRF exemption
CSRF protection is enabled for all POST routes by default. Set
csrfExempt: true to bypass it for a specific route — useful for webhook receivers or public API endpoints that authenticate another way.
#[Route('/webhook', HttpMethod::POST, csrfExempt: true)]
public function webhook(): array { ... }
See Configuration → CSRF for full details.
Method Parameters
The framework injects values into your method parameters from three sources:
| Source | How to Declare | Example |
|---|---|---|
| URL path segment | Typed parameter matching {name} in the route |
int $id for /show/{id} |
Query string ($_GET) |
Typed parameter with a default value | string $name = '' for ?name=foo |
| POST body / file upload | #[RequestParam] on the parameter |
#[RequestParam] string $email |
Route Pattern Matching
The router first tries an exact match, then falls back to pattern matching for routes with {param} placeholders:
// Registered route: /product/show/{id}
// Request: /product/show/42
// Result: $id = 42 → passed to the controller method
#[RequestParam]
No parameters. Tells the framework to inject the value from $_POST or $_FILES for this method parameter. Without it, only path parameters and $_GET values are injected.
Return Types
| Return Value | Response |
|---|---|
string | Echoed as HTML |
array or object | JSON-encoded with Content-Type: application/json |
Next: Dependency Injection →