stave.dev

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.

/module
/endpoint
/arg1/arg2/...argN
?query=string

The framework then:

  1. Converts the module segment from kebab-case to PascalCase
  2. Converts the endpoint segment from kebab-case to camelCase
  3. Loads modules/{Module}/{Module}.php
  4. 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')
Default Endpoint

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()
parseRequest()
defineConstants()
run()
  1. registerAutoloaders() — Sets up PSR-4-like class loading for core and module namespaces
  2. parseRequest() — Parses the URL into module, endpoint, args, and detects JSON/AJAX requests
  3. defineConstants() — Loads config.ini, constants.ini, and module-specific config
  4. 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'
}
Required vs. Optional Arguments

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::$acceptsJSONtrueClient wants JSON?
Request::$madeWithAJAXfalseXMLHttpRequest?