リクエストのライフサイクル

Browser
  │
  ├─ .htaccess          すべての .php リクエストを __flexagon.php に書き換え
  │
  ├─ __flexagon.php ──► flexagon.php ──► Bootstrap.php
  │                                        │
  │                                        ├─ 定数を定義(PROJECT_ROOT, PUBLIC_ROOT, _FLEXAGON_ROOT ...)
  │                                        ├─ オートローダーを登録
  │                                        ├─ _Const.php / _Global.php / _Config.php を読み込み
  │                                        └─ URL を解析 → _Global::$URL_PARAM
  │
  ├─ public/_entry.php      ① リクエスト開始時のフック(サイトタイトル、共通ヘッダーなど)
  ├─ [セッションの自動開始] ② $SESSION_AUTO_START が true の場合
  ├─ public/_prepare.php    ③ セッション開始後のフック(認可チェックなど)
  └─ public/_router.php     ④ ディスパッチ — デフォルト実装は TemplateLoader::content() を使用
                                 → public/<URL パス>.php を実行

3 つのフックは、リクエストのライフサイクルのそれぞれ異なる段階で実行されます。

ファイル 段階 用途
_entry.php セッション前 サイトのメタデータ、レスポンスヘッダー、ロケール
_prepare.php セッション後 認証・認可チェック、共通データの読み込み
_router.php ディスパッチ ページを実行します。独自のルーティングが必要な場合はこのファイルを置き換えます

URL マッピングのルール

リクエスト URL 実行されるファイル
/ public/index.php
/hello public/hello.php
/user/profile public/user/profile.php
/user/ public/user/index.php
/user/profile?id=3 public/user/profile.php

リクエストパスは、まず HttpUtil::normalizePath() によって正規化されます。NUL バイトを除去し、バックスラッシュを / に変換したうえで、空のセグメントや .、.. を取り除くことで、親ディレクトリへのトラバーサルを防ぎます。

続いて TemplateLoader が realpath() で最終的なパスを解決し、そのパスが public/(または _Template/)の内側にとどまっていることを確認したうえでのみ include します。その範囲外のパスは 404 として拒否されます。この保護は、Web サーバーによるパスの正規化だけに頼るのではなく、フレームワーク自身によって強制されます。

解析結果は _Global::$URL_PARAM(UrlParamsModel)に格納されます。

_Global::$URL_PARAM->filePath;       // 'user/profile'
_Global::$URL_PARAM->filePathArray;  // ['user', 'profile']
_Global::$URL_PARAM->filePathEnd;    // 'profile'
_Global::$URL_PARAM->params;         // '?id=3'

カスタムルーティング

ファイルマッピングを使わずにリクエストを独自にディスパッチするには、public/_router.php を置き換えます。

<?php
// public/_router.php
$path = _Global::$URL_PARAM->filePath;

if (str_starts_with($path, 'api/')) {
    header('Content-Type: application/json');
    include_once PUBLIC_ROOT . '/api/_dispatch.php';
} else {
    TemplateLoader::content();
}