Skip to content

Bootstrap

require StaticPHP\Core\Bootstrap::FILE is the whole of starting the framework. This page is what that one line sets in motion, in order.

Bootstrap::FILE and Bootstrap::AUTOLOAD, the path constants they resolve, and why the entry point is a constant rather than a run() method are covered in the front controller. The short version is that Bootstrap::AUTOLOAD brings up the path constants and the application autoloader and nothing else, while Bootstrap::FILE - the file this page describes - brings up everything.

Requiring Bootstrap::FILE runs the following top to bottom. Each step is listed with the reason it sits where it does, because the order is the interesting part - most of these steps depend on the one before it having already run.

  1. $microtime = microtime(true). First statement in the file, at global scope. Every number Timers reports is measured from here.

  2. require_once dirname(__FILE__) . '/Autoload.php'. Defines SP_PATH, APP_PATH, APP_MODULES_PATH, BASE_PATH and VENDOR_PATH, throws a RuntimeException if the front controller never defined PUBLIC_PATH, and registers the application autoloader. Nothing below can name an application file until this has run.

  3. Request::populateFromCli(). Under the cli SAPI this rewrites $_GET, $_POST, $_REQUEST and several $_SERVER keys from argv; under any other SAPI it returns immediately. It must precede step 4 because the application’s Config.php binds $config['request_uri'] and friends to $_SERVER entries by reference - rewriting the superglobals afterwards would leave the configuration describing a request that was never made. See Request.

  4. Config::load(['Config', 'Routing']). Reads APP_PATH/Config/Config.php and APP_PATH/Config/Routing.php into Config::$items.

  5. The client address is resolved, but only when the application left it alone:

    <?php
    if (Config::get('client_ip', null) === null) {
    Config::$items['client_ip'] = Router::clientIp();
    }

    It sits here because the configuration governing it - trust_proxy_headers, trusted_proxy_hops - has only just been read, and because everything below may want an address to log against. null means “work it out”, the way base_url does; anything the application assigned is kept, including the 1.x = & $_SERVER['REMOTE_ADDR'] binding. See proxy headers.

  6. Debug is decided, and now and date_time are written.

    <?php
    Config::$items['now'] = time();
    Config::$items['date_time'] = new ExtendedDateTime();
    Config::$items['debug'] = Config::resolveDebug();

    resolveDebug() returns true for a truthy $config['debug'], and otherwise calls $config['debug_check'] - the application’s own callable(): bool - and requires a strict true. It fails closed. The rules in full are on the Config API.

    error_reporting is then set to E_ALL when debug is on and E_ALL & ~E_DEPRECATED when it is off, and display_errors to (int) Config::$items['debug'] - the value computed just above, not the one the config file assigned. Every later step reads Config::$items['debug'], so nothing that cares about debug mode can run before this, and debug_check in particular runs before sessions, the database and routing exist.

  7. $config['autoload_configs'] is loaded. Each entry is split on / and read right to left: one part is a file in APP_PATH/Config, two parts are module/file, three are project/module/file. Entries that are not strings are skipped.

  8. The error handlers are registered. Load::helper(['ErrorHandlers'], 'Core', 'staticphp') requires src/Core/Helpers/ErrorHandlers.php, then:

    <?php
    set_error_handler('sp_error_handler', (!empty(Config::$items['debug']) ? E_ALL : E_ALL & ~E_DEPRECATED));
    set_exception_handler('sp_exception_handler');

    Everything above this line runs under PHP’s default handling; a failure in the application’s Config.php will not render through the framework’s error pages. See errors.

  9. The twig environment is built, unless $config['disable_twig'] === true or \Twig\Environment does not exist. The check is class_exists() rather than a probe for a file under VENDOR_PATH, so it survives twig moving its own internals around. Details below.

  10. $config['autoload_helpers'] is loaded, using the same one/two/three part naming as step 7, through Load::helper().

  11. Router::init(). Parses the url, finds a controller and calls it. Everything the request does happens inside this call. See the router.

There is no step after Router::init(). Control returns to the front controller, which by convention has nothing left to do.

What the bootstrap writes back into the configuration

Section titled “What the bootstrap writes back into the configuration”
Key Value
client_ip Router::clientIp(), only when the application left the key null
now time() at boot
date_time a StaticPHP\Utils\Models\ExtendedDateTime instance
debug the computed boolean from step 6
view_loader \Twig\Loader\FilesystemLoader, only when twig is in use
view_engine \Twig\Environment, only when twig is in use

Router::splitSegments() later defines the BASE_URL constant as well.

When it is built, the loader searches three directories in this order:

<?php
new \Twig\Loader\FilesystemLoader([
APP_MODULES_PATH,
APP_PATH,
SP_PATH . '/Core/Views',
]);

The environment gets cache => APP_PATH . '/Cache/Views/', or false when debug is on, and debug => Config::get('debug').

Registered on it:

Name Kind Signature
siteUrl filter ($url = '', $prefix = null, $current_prefix = true)
siteUrl function ($url = '', $prefix = null, $current_prefix = true)
startTimer function ()
stopTimer function ($name)
markTime function ($name)
debugOutput function ()

siteUrl forwards to Router::siteUrl(); the three timer functions forward to Timers; debugOutput returns Logger::debugOutput(). \StaticPHP\Utils\Models\Csrf::registerTwig() is then called, which adds the csrfToken(), csrfFieldName() and csrfField() helpers - registering them only makes the token reachable from a template, validating an incoming request is still the application’s job.

If twig is not installed, none of this exists and Load::view() renders plain php templates instead. See Load.

Step 2 above also registers a second spl_autoload_register() callback, in src/Core/Helpers/Autoload.php. It is what resolves the application tree, whose namespace roots are module names rather than a PSR-4 prefix - see the front controller for what it resolves and how, and running multiple applications for why it exists at all.