Routing
URLs map to classes automatically. No routing table. No configuration.
URL-to-Class Mapping
Zero Framework parses every URL into three parts: module, endpoint, and arguments.
The framework then:
- Converts the module segment from kebab-case to PascalCase
- Converts the endpoint segment from kebab-case to camelCase
- Loads
modules/{Module}/{Module}.php - Calls
Module::endpoint(...$args)using the splat operator
Conversion Examples
| URL | Class | Method Call |
|---|---|---|
/hello-world |
HelloWorld |
HelloWorld::index() |
/hello-world/say-hello |
HelloWorld |
HelloWorld::sayHello() |
/user-profile/edit/42 |
UserProfile |
UserProfile::edit('42') |
/auth/login |
Auth |
Auth::login() |
/api/users/5/posts |
Api |
Api::users('5', 'posts') |
When no endpoint is specified in the URL, index() is called. So /hello-world maps to HelloWorld::index().
Request Flow
The full request lifecycle follows four stages inside Application:
- registerAutoloaders() — Sets up PSR-4-like class loading for core and module namespaces
- parseRequest() — Parses the URL into module, endpoint, args, and detects JSON/AJAX requests
- defineConstants() — Loads
config.ini,constants.ini, and module-specific config - run() — Instantiates the module, processes attributes, and calls the endpoint method
Argument Handling
URL segments after the endpoint are unpacked as individual method parameters using PHP's splat operator:
// URL: /hello-world/say-hello/alice/bob
// Framework calls: $module->sayHello(...['alice', 'bob'])
// Which is equivalent to: $module->sayHello('alice', 'bob')
public function sayHello($name1, $name2 = null) {
// $name1 = 'alice'
// $name2 = 'bob'
}
If a method declares a required parameter and the URL doesn't provide it, PHP throws an ArgumentCountError which the framework catches and returns HTTP 400. Always use $param = null for optional arguments.
Recommended Pattern
// Required argument: /module/show/42
public function show($id) {
$item = $this->model->find($id);
if (!$item) {
throw new HTTPError(404, 'Item not found');
}
}
// Optional argument: /module/list or /module/list/active
public function list($filter = null) {
// $filter is 'active' or null
}
// Multiple arguments: /module/edit/5/settings
public function edit($id, $tab = null) {
// $id = '5', $tab = 'settings' or null
}
The Request Object
After parsing, all request data is available via static properties on \Zero\Core\Request:
| Property | Example Value | Description |
|---|---|---|
Request::$module | 'helloWorld' | camelCase module name |
Request::$Module | 'HelloWorld' | PascalCase module name |
Request::$endpoint | 'sayHello' | camelCase endpoint name |
Request::$args | ['alice', 'bob'] | Remaining URL segments |
Request::$method | 'GET' | HTTP method |
Request::$domain | 'stave.dev' | Domain name |
Request::$acceptsJSON | true | Client wants JSON? |
Request::$madeWithAJAX | false | XMLHttpRequest? |