Request Lifecycle

Every HTTP request to Pubvana passes through a defined sequence of steps. Understanding this sequence is critical when building plugins that need to intercept, modify, or extend request handling.

Full Lifecycle Sequence

1. Apache receives HTTP request
2. mod_rewrite rewrites to public/index.php
3. public/index.php bootstraps CI4
4. CI4 fires pre_system event
5. Filters run (before phase)
6. Router matches route
7. Controller::method() executes
8. ThemeService renders view
9. Response sent to browser

Step 1–2: Apache and mod_rewrite

The vhost DocumentRoot is public/. Apache's .htaccess in public/ rewrites all requests to index.php. Only public/ is web-accessible — app/, plugins/, themes/, and writable/ are outside the document root.

Step 3: CI4 Bootstrap

public/index.php defines FCPATH, sets the ENVIRONMENT from $_SERVER['CI_ENVIRONMENT'] (or .env), and calls CodeIgniterCodeIgniter::run(). This bootstraps the framework: loads app/Config/, initialises the request and response objects, and fires events.

Step 4: pre_system Event

The most important hook for plugin developers. Registered in app/Config/Events.php:

Events::on('pre_system', static function () {
    // 1. Boot all active plugins
    service('pluginManager')->boot();

    // 2. Sync locale from session
    $locale = session('locale');
    if ($locale && in_array($locale, config('App')->supportedLocales)) {
        service('request')->setLocale($locale);
    }

    // 3. Populate supportedLocales from settings
    config('App')->supportedLocales = setting('App.supportedLocales') ?? ['en'];
});

PluginManager::boot() at this stage:

  1. Queries the plugins table for is_active = 1 records
  2. Registers each plugin's PSR-4 namespace via spl_autoload_register
  3. Calls Plugin::register() on each plugin (event hooks, SDK init)
  4. Requires each plugin's Config/Routes.php into the CI4 router

Because this runs before routing, plugin routes are available to the router as if they were core routes.

Step 5: Filters (Before Phase)

Global Filters

FilterAction
honeypotAdds honeypot field; rejects bots
csrfValidates CSRF token on POST/PUT/DELETE
maintenanceIf maintenance mode on, redirects non-admins to maintenance page
session (Shield)Authenticates session-based users

Route-Specific Filters

Admin routes declare ['filter' => ['admin_auth', 'totp']]:

  • admin_auth — requires the user to be logged in and in the admin/superadmin group.
  • totp — if 2FA is enabled site-wide, requires a completed TOTP challenge in session.

Plugin CSRF exemptions (returned from getCsrfExemptions()) are injected into the CSRF filter's except list during PluginManager::boot().

Step 6: Route Matching

CI4's router matches the request URI against defined routes. Route resolution order:

  1. Core routes defined in app/Config/Routes.php
  2. Plugin routes (loaded at pre_system into the same router instance)
  3. Auto-routing (disabled — Pubvana uses explicit routes only)

Step 7: Controller Execution

The matched controller is instantiated. Pubvana controllers extend either:

  • AppControllersBaseController — for public-facing pages
  • AppControllersAdminBaseAdminController — for admin panel pages

Step 8: View Rendering

Public Views (ThemeService)

return $this->themeService->view('home', $data);

Admin Views (CI4 view())

return view('Plugins\MyPlugin\Views\admin\index', $data);

Diagram

Browser
  │
  ▼
Apache → mod_rewrite → public/index.php
  │
  ▼
CI4 bootstrap (Config/, Services)
  │
  ▼
pre_system event
  ├── PluginManager::boot()
  │     ├── Register namespaces (spl_autoload_register)
  │     ├── Plugin::register() × N
  │     └── Load Config/Routes.php × N
  ├── Locale sync
  └── supportedLocales population
  │
  ▼
Filters (before): honeypot → csrf → maintenance → session → admin_auth → totp
  │
  ▼
Router → Controller::method()
  │
  ▼
ThemeService::view() or view() [admin]
  │
  ▼
Filters (after)
  │
  ▼
Response → Browser