01 / Architecture

Concepts

Follow the request. Understand the boundaries. From the platform to the smallest component, every part has a responsibility.

A physical architectural model of geometric volumes in Shanghai
Structure at every scale. · Photo: Declan Sun / Unsplash

Performance targets

Hard targets that drive every architectural decision. They are budgets to measure against, not proven guarantees on every machine.

ScopeTargetIncludes
Kernel CPU, no I/O< 1 msBoot, config, routing, middleware, ACL, response dispatch
Full response, with I/O< 10 msEverything above plus database, cache reads and view render

How it is enforced: lazy composables, compile-time ACL expansion, no magic methods, compiled manifests read at boot, and local shared memory (APCu, OPcache) instead of network caches on the hot path.

Request lifecycle

The HTTP request loop and Composer Lifecycle are separate flows. This diagram follows a request from the public entry point through response dispatch and the next request.

flowchart TD
    incoming[HTTP request] --> front[public index.php]
    front --> clock[Record monotonic request start]
    clock --> maintenance{Maintenance file exists}
    maintenance -->|yes| maintenanceResponse[Run maintenance response]
    maintenance -->|no| probe{Path is healthz or ready}
    probe -->|yes| healthResponse[Plain text 200 response]
    probe -->|no| fastboot[internal fast-boot.php]
    fastboot --> autoload[Require Composer autoload]
    autoload --> httpRun[Webkernel Http run]
    httpRun --> config[Config boot and compiled configuration]
    config --> container[Container boot and request scope]
    container --> manifests[Load compiled view component and language manifests]
    manifests --> viewBoot[View boot]
    viewBoot --> i18nBoot[I18n boot and context flush]
    i18nBoot --> routeRun[Route run]
    routeRun --> match{Path and method match}
    match -->|route| handler[Dispatch handler and echo response]
    match -->|unknown path| notFound[404 response]
    match -->|wrong method| methodNotAllowed[405 response with Allow header]
    handler --> complete[Request complete]
    notFound --> complete
    methodNotAllowed --> complete
    maintenanceResponse --> complete
    healthResponse --> complete
    complete -. next request .-> incoming

Early exits in public/index.php: It records a monotonic start time, serves internal/maintenance.php when present, and answers /healthz and /ready with plain-text 200 responses before loading the framework. Ordinary requests continue to internal/fast-boot.php.

Framework boot: Webkernel\Http::run() calls Webkernel\Config\Config::boot(), loads compiled bindings into Webkernel\Container\Container::boot() and opens a request scope, then boots Webkernel\View\View::boot() from view/component manifests, boots Webkernel\I18n\I18n::boot() from the language manifest, flushes Webkernel\I18n\I18nContext, and calls Webkernel\Route\Route::run().

Route result: A matching route dispatches its handler and echoes the response. An unknown path returns 404; a known path with the wrong method returns 405 with an Allow header.

Fast boot: fast path and recovery path

flowchart TD
    entry[Fast boot entry] --> paths[Read config paths]
    paths --> pathValid{Configured autoload path is a string}
    pathValid -->|no| stopInvalid[Stop for invalid configuration]
    pathValid -->|yes| configured{Configured autoload file exists}
    configured -->|yes| requireConfigured[Require configured autoload and return]
    configured -->|no| candidates[Build safe vendor autoload candidates]
    candidates --> candidateExists{A candidate exists}
    candidateExists -->|yes| requireCandidate[Require candidate and return]
    candidateExists -->|no| isCli{PHP SAPI is CLI}
    isCli -->|no| stopHttp[Stop HTTP request and report missing dependencies]
    isCli -->|yes| composerPath{Composer is executable on PATH}
    composerPath -->|yes| install[Run Composer install]
    composerPath -->|no| urlFopen{allow url fopen is enabled}
    urlFopen -->|no| stopInvalid
    urlFopen -->|yes| download[Fetch Composer PHAR and published SHA256 over verified HTTPS]
    download --> checksum{Checksum and PHP PHAR header are valid}
    checksum -->|no| stopInvalid
    checksum -->|yes| install
    install --> safeProcess[Fixed argument array with shell bypass and no scripts]
    safeProcess --> temporary[Use runtime tmp and remove temporary PHAR]

Normal request: When internal/dependencies/autoload.php exists, fast-boot requires it and returns. It does not search for Composer or start another process.

Recovery conditions: Composer recovery is available only to CLI. An HTTP request with no autoloader stops and does not run Composer. Composer's fallback vendor-dir rejects absolute paths and .. segments; the configured autoload path is application-owned configuration. Composer is taken from PATH first; otherwise the downloaded PHAR is checked against Composer's published SHA-256 and its PHP/PHAR header before execution.

Process safeguards: Composer receives fixed arguments through proc_open() with shell bypassed and --no-scripts. Its COMPOSER_HOME is cleared, TMPDIR points to runtime/tmp, and a temporary PHAR is removed after the attempt.

Composer Lifecycle: events, checks, and actions

Composer Lifecycle is a build/install-time flow; it does not run as part of an HTTP request.

flowchart TD
    composerEvent[Composer script event] --> plugin[LCPlugin subscribes to events]
    plugin --> handler[LCPluginScriptEventHandler]
    handler --> reader[LCDeclarationReader reads root and installed packages]
    reader --> validator[LCDeclarationValidator validates extra webkernel schema]
    validator --> dispatcher[LCEventDispatcher resolves validates orders and invokes]
    dispatcher --> phase{Composer script event}
    phase -->|pre install or pre update| dependencyCheck[EventDependencyCheck]
    dependencyCheck --> checks[LCCheckRunner evaluates PHP version conditions]
    phase -->|post autoload dump| compile[Codebase CompileDeclarations]
    compile --> basePath[EventSetBasePath refreshes project base path]
    basePath --> postDump[EventPostAutoloadDump]
    postDump --> checks
    checks --> actionRunner[LCActionRunner validates action declarations]
    actionRunner --> actionOrder[Root actions then Codebase then other packages alphabetically]
    actionOrder --> action[Instantiate each LifecycleActionContract and call handle]
    phase -->|other supported script event| callback[Run declared listener callbacks]
    packageEvent[Composer package install update or uninstall] --> operationHandler[LCPackageOperationEventHandler]
    operationHandler --> invalidate[Invalidate declaration cache and report package path and outcome]

Script events accepted: pre-install-cmd, post-install-cmd, pre-update-cmd, post-update-cmd, pre-autoload-dump, post-autoload-dump, post-root-package-install, and post-create-project-cmd. Listeners may be an invokable class or a Class::method callback. Lifecycle resolves package-local names from a single PSR-4 prefix, validates classes and public methods, then invokes Codebase callbacks first and other packages alphabetically.

Checks: Run for install/update pre-events and post-autoload-dump. Their condition grammar is limited to numeric PHP_VERSION_ID comparisons; Lifecycle parses it directly and does not evaluate arbitrary PHP. A failed check can report info or warning and continue, or use danger to stop Composer.

Actions: Run after autoload generation. Each declared action must implement LifecycleActionContract, be instantiable, and have no required constructor arguments. Packages run in this order: root package, webkernel/codebase, then other package names alphabetically; each package keeps its declaration order. Failures are reported and make the post-autoload event fail.

Package operation events: Pre/post install, update, and uninstall events invalidate Lifecycle's in-memory declaration cache and report recognized Webkernel package paths. They are plugin subscriptions, not keys for extra.webkernel.lifecycle.events.

Authorization status: Routes can store middleware and permission metadata, but this router path currently records those values without enforcing them. Do not treat those declarations as an active access-control check.

Domain hierarchy

Applications are multi-panel and modular, with permissions enforced down to individual components and actions.

Platform

Root level, managed by App Owners. Holds global configuration.

System panel

Platform-scoped admin for modules, owners and telemetry.

Modules

Functional domains shipped as Composer packages, with their own panels.

Panels and clusters

Workspaces typed `platform` or `module`, grouping resources.

Resources and pages

A resource is the CRUD unit for one model and owns its pages.

Components

Tables, forms, widgets and custom views inside pages.