Configuration

Configuration flows one way. Non-secret settings live as constants in Config.php. Secrets and per-environment values come from environment variables. The entry point reads both, hands them to modules through Config::Provide, and one security check confirms nothing is missing before the site serves a single request.

Two kinds of configuration

The framework keeps two kinds of setting apart:

  • Non-secret settings, such as the site title or the module path, are constants in Config.php. This file is committed with the site.
  • Secrets and per-environment values, such as the database password or an API token, come from environment variables set by the container. They are never committed.

Both are read in one place, the entry point, and every module reads its settings from the Config module rather than touching getenv itself. That gives a site a single point where its environment is touched.

Config.php

Config.php sits above public_html so it is never served. It holds plain constants:

<?php

// Non-secret configuration for the site. Never web-served.

// Where the framework modules are mounted (see Running locally).
const APP_MODULE_PATH = '/var/www/framework';

// Show errors while developing.
const APP_DEBUG = true;

// The site.
const APP_SITE_URL    = 'http://localhost:8080';
const APP_SITE_TITLE  = 'My Site';
const APP_SITE_SECRET = 'change_me_site_secret';

// Default admin account, seeded on first run.
const APP_ADMIN_LOGIN    = 'admin';
const APP_ADMIN_EMAIL    = 'admin@example.com';
const APP_ADMIN_PASSWORD = 'admin';

Providing configuration in index.php

The entry point includes Config.php, brings in Security and Config first, reads the secrets from the environment, and then provides every module's settings up front, before the modules that consume them are included. Each call to Config::Provide gives one module all of its settings at once:

require_once __DIR__ . '/../Config.php';

$MODULE_PATH = $GLOBALS['MODULE_PATH'] = APP_MODULE_PATH;
$DEBUG       = $GLOBALS['DEBUG']       = APP_DEBUG;

require_once $MODULE_PATH . '/Security/index.php';
require_once $MODULE_PATH . '/Config/index.php';

// Secrets and per-environment values come from the container's environment.
$dbHost = getenv('DB_HOST') ?: null;
$dbPort = getenv('DB_PORT') ?: null;
$dbUser = getenv('DB_USER') ?: null;
$dbPass = getenv('DB_PASS') ?: null;
$dbName = getenv('DB_NAME') ?: null;

Config::Provide('Database', [
    'HOST' => $dbHost,
    'PORT' => $dbPort,
    'USER' => $dbUser,
    'PASS' => $dbPass,
    'NAME' => $dbName,
]);

Config::Provide('Web', [
    'WEB_SITE_URL'         => APP_SITE_URL,
    'WEB_SITE_TITLE'       => APP_SITE_TITLE,
    'WEB_SITE_DESCRIPTION' => '',
    'WEB_SITE_SECRET'      => APP_SITE_SECRET,
]);

Config::Provide('Admin', [
    'ADMIN_URL'              => '/admin',
    'ADMIN_DEFAULT_LOGIN'    => APP_ADMIN_LOGIN,
    'ADMIN_DEFAULT_EMAIL'    => APP_ADMIN_EMAIL,
    'ADMIN_DEFAULT_PASSWORD' => APP_ADMIN_PASSWORD,
]);

// ... include the modules that consume this configuration ...

Config::SecurityCheck();

Provide comes before include on purpose: a module reads and validates its settings as it loads, so the values have to be waiting for it.

How a module declares what it needs

A module states its own configuration in its index file. It declares each value it requires, with a type, and reads values back with Config::Get:

// in Shop/index.php
Config::Required('Shop', 'CURRENCY', Config::TYPE_STRING);

// anywhere in the Shop module
$currency = Config::Get('Shop', 'CURRENCY');

Because a module declares its own requirements, you can read the top of any module and see exactly what it expects to be given. A site wires the module up by providing those keys, and nothing else needs to know about them.

The security check

After every Config::Provide call, the entry point calls Config::SecurityCheck. It confirms that every value a module declared as required was provided, and that each value is the right type. If a setting is missing or the wrong type, the site stops at startup with a named error rather than failing deep inside a later request. A misconfigured site never serves a page.

The database connection

The database settings are provided first because the Database module needs them as it loads. In the Docker setup the container sets the matching environment variables to reach the database it created: host 127.0.0.1, database app_db, user app. Provide those through the Database config as shown above and the connection is ready by the time the first module runs a query.

That completes Getting started. Next: What a module is and provides.