Skip to main content

Request Lifecycle

Introduction

Nothing in the plugin runs on its own — WordPress calls it through hooks. Knowing which hook owns which stage tells you where new code belongs.

Boot sequence

Entry file — plugin headers and the ABSPATH guard, then it requires backend/bootstrap.php. No logic lives here.

bootstrap.php — checks vendor/autoload.php exists (and shows an admin notice if not), loads it, reads .env, then calls Plugin::load().

Plugin — a singleton. Its constructor registers the installer and defers everything else to plugins_loaded, because WordPress isn't ready earlier.

Providers — registered on init at priority 8, before the default 10, so routes exist by the time WordPress dispatches AJAX or REST.

Request dispatch

HookProvider builds a router only for the transport actually in play, so an AJAX request never parses REST routes:

$router = new Router(RequestType::AJAX, Config::VAR_PREFIX, '');
$router->setMiddlewares(Plugin::instance()->middlewares());

include $this->_pluginBackend . 'hooks' . DIRECTORY_SEPARATOR . 'ajax.php';
$router->register();

Including the route file is what registers routes: each Route:: call adds to the router, and register() binds them to WordPress hooks — wp_ajax_* actions for AJAX, register_rest_route() for REST.

Once a route matches, RouteRegister runs the middleware stack, resolves the controller method's arguments by reflection — the Request, then route parameters by name — calls it, and wraps whatever comes back into the response envelope.

Where to add code

You want toAdd it in
A new endpointbackend/hooks/ajax.php or api.php, plus a controller
A check across many routesA middleware, registered in Plugin::middlewares()
A WordPress hookA provider under app/Providers
A tableA migration under backend/db/Migrations
Logic too big for a controllerA service under app/Services
note

registerProviders() runs on init. Code that must run earlier — rewrite rules, the OAuth callback handler — is wired in the Plugin constructor instead, guarded so it only runs for non-AJAX, non-REST, non-cron requests.