---
title: "Bootstrap & Dispatcher"
description: "Nibiruリクエストがindex.phpを経てディスパッチャに到達し、その後あなたのコントローラーにどのように流れることか。"
---

ニブルのリクエストは3つのファイルを1回通過します：`index.php`、`core/framework.php`、および`core/c/dispatcher.php`。それらをその順序で読み込むと、すべてがわかります。

## index.php
```php
<?php
require_once 'core/framework.php';
```
これが全体のファイルです。`index.php`はウェブサーバーにターゲットを提供するだけです。

## core/framework.php

`framework.php` は、依存関係の順序でフレームワークのクラスを読み込みます。設定、レジストリ、ルーター、エンジン、オートローダー、すべてのデータベースドライバー、すべての28種類のフォームタイプ、ビュー、コントローラー、モジュール、認証、デバッグ、表示 — そして最後に以下を実行します：
```php
Nibiru\Dispatcher::getInstance()->run();
```
その単一の呼び出しは、アプリケーションのハートビートです。

## Dispatcher::run()

簡略化された流れ:
```php
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
    }
}
```
## アクションシーケンス

すべてのリクエストが同じ3つのステップを経る場合、`?_action=foo` が設定されているときです。

1. `navigationAction()` — メニュー、パンくずを設定します。
2. `fooAction()` — 名前付きアクションを実行します。
3. `pageAction()` — テンプレートデータを最後に割り当てます。

`_action` 無しでは、ステップ 1 とステップ 3 のみが実行されます。

これは、**ステートレスなレンダリング時ロジックは `pageAction()` に含まれるべきであり**、**ナビゲーションデータは `navigationAction()` に含まれるべきである**ということを意味します。それが労力の重複に感じさせても、同じプロジェクト内の2つのコントローラーともに `navigationAction()` を持つのは正しいです。

## ソフト404

マッチしたコントローラー ファイルが存在しない場合、Nibiru は *ソフトウェア 404* をレンダリングします — 200 OK を返し、`[ENGINE] error.controller`（デフォルト: `error`）で指定された名前のコントローラーを表示します。これは意図的な動作です：これにより、サーバー レベルの構成なしに美しいエラーページを提供できます。

実際の `404 Not Found` HTTP コードを使用するには、エラーコントローラーで設定します。
```php
public function pageAction() {
    http_response_code(404);
    View::assign(['title' => 'Lost in the void']);
}
```
## Auto::loader()

コントローラーが構築される前に2つのオートローダーが実行されます：

- **`loadModelFiles()`** は `application/model/` をスキャンし、そのディレクトリ内のすべての `.php` ファイルをインクルードします。生成されたモデルは名前空間パッケージではなくフラットなファイルなので、これは単純な `require_once` ループです。
- **`loadModules()`** は `application/module/<name>/` を走査し、各モジュールのメインクラスとそのトレイト、プラグイン、インターフェースファイルを読み込みます。レジストリは同時に各モジュールの設定 INI をインデックスします。

両者は、`[SETTINGS] modules.path` 等の設定されたパスを使用しているため、環境ごとに上書きすることができます。

## SEO対応のURL処理

コントローラーの解決前に、`Router::route()` は `handleSeoUrls()` を実行します。このメソッドは `/controller/<slug>/<id>` 形式の URL（2 番目のセグメントが既知のアクションではなく、3 番目が数値である場合）を検出します。これらの URL は内部的に `/controller/detail/` にリライトされ、`$_REQUEST['id']` と `$_REQUEST['slug']` が設定されます。詳細については [Routing](/core/routing/) を参照してください。

## ディスパッチャーをオーバーライドする場合

ほとんどありません。クリーナーの拡張ポイントは次のとおりです：

- **カスタムエラーコントローラー** — `[ENGINE] error.controller` を設定します。
- **プリコントローラーフック** — オブザーバーを登録するモジュールを読み込み、その後 `navigationAction()` 内でアタッチします。
- **cron スタイルのエントリ** — `nibiru` CLI を使用してフレームワークをヘッドレスで実行し、代わりに `index.php` を使用します。
