Skip to content
Nibiru docsv0.9.2

Bootstrap & Dispatcher

How a Nibiru request flows from index.php through the dispatcher to your controller.

Stable Reading time ~ 2 min Edit on GitHub

A Nibiru request takes one trip through three files: index.php, core/framework.php, and core/c/dispatcher.php. Reading them in that order tells you everything.

<?php
require_once 'core/framework.php';

That’s the whole file. index.php exists only to give the web server a target.

framework.php requires the framework’s classes in dependency order — settings, registry, router, engine, autoloader, all DB drivers, all 28 form types, view, controller, modules, auth, debug, display — and finishes with:

Nibiru\Dispatcher::getInstance()->run();

That single call is your application’s heartbeat.

The simplified flow:

public function run() {
date_default_timezone_set(Config::getInstance()->getConfig()
[View::NIBIRU_SETTINGS]['timezone']);
if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); / 1. (re)generate models from schema
}
Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller";
$controller = new $class();
if (array_key_exists('_action', $_REQUEST)) {
$action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction();
if (method_exists($controller, $action)) {
$controller->$action(); / 6. optional named action
}
$controller->pageAction();
} else {
$controller->navigationAction();
$controller->pageAction();
}
Display::getInstance()->display(); / 7. render Smarty
} else {
/ 8. soft 404 render the configured error controller
}
}

Every request goes through the same three steps if ?_action=foo is set:

  1. navigationAction() — populate menus, breadcrumbs.
  2. fooAction() — run the named action.
  3. pageAction() — last chance to assign template data.

Without _action, only steps 1 and 3 run.

This means stateless render-time logic belongs in pageAction(), and navigation data belongs in navigationAction(), even if it feels like duplicating effort. Two controllers in the same project will both have a navigationAction(); that’s correct.

If the matched controller file doesn’t exist, Nibiru renders a soft 404 — it returns a 200 OK and renders the controller named in [ENGINE] error.controller (default: error). This is intentional: it lets you serve nice error pages without server-level configuration.

If you want a real 404 Not Found HTTP code, set it in your error controller:

public function pageAction() {
http_response_code(404);
View::assign(['title' => 'Lost in the void']);
}

Two auto-loaders run before the controller is constructed:

  • loadModelFiles() scans application/model/ and includes every .php file there. Generated models are flat files, not namespaced packages, so this is a simple require_once loop.
  • loadModules() walks application/module/<name>/ and loads each module’s main class plus its trait, plugin and interface files. The Registry indexes each module’s settings INI at the same time.

Both use the configured paths in [SETTINGS] modules.path etc., so you can override them per environment.

Before the controller resolution, Router::route() runs handleSeoUrls() which detects URLs of the form /controller/<slug>/<id> (where the second segment is not a known action and the third is numeric). Those get rewritten internally to /controller/detail/ with $_REQUEST['id'] and $_REQUEST['slug'] populated. Read more in Routing.

Almost never. The cleaner extension points are:

  • Custom error controller — set [ENGINE] error.controller.
  • Pre-controller hooks — load a module that registers an observer, then attach it inside navigationAction().
  • Cron-style entry — run the framework headless via the nibiru CLI rather than index.php.