Pop PHP
Getting Started

Configuration

Config files return arrays of data — routes, credentials, services. Your application class's load() turns that data into wiring. Values in one, behavior in the other.

The Config Array#

The front controller reads one config file and hands the array to the constructor:

PHPpublic/index.php
<?php

$autoloader = include __DIR__ . '/../vendor/autoload.php';

$dotEnv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotEnv->safeLoad();

$app = new App\Application($autoloader, include __DIR__ . '/../app/config/app.http.php');
$app->load();
$app->run();

Bootstrap runs in the constructor and picks up a fixed set of top-level keys:

Key What bootstrap does with it
routes registers every entry with the router
services defines each entry in the service locator
events attaches each listener to the event manager
middleware registers each handler with the middleware manager
name, version sets the application's identity; name falls back to APP_NAME
prefix, src, psr-0 registers an autoloader prefix for the app or module
helper_functions set to false to skip loading the pop-utils helper functions

So a config array can wire an application on its own:

PHPapp/config/app.http.php
<?php

return [
    'routes'   => [
        '[/]' => [
            'controller' => 'App\Http\Controller\IndexController',
            'action'     => 'index'
        ],
        '*' => [
            'controller' => 'App\Http\Controller\IndexController',
            'action'     => 'error'
        ]
    ],
    'services' => [
        'session' => ['call' => 'Pop\Session\Session::getInstance']
    ]
];

Every other key is yours to use. pop:init adds http_options_headers for CORS and, with a database configured, a database key. Reach them with $app->config['http_options_headers'] or Pop\App::config('database').

Wiring in load()#

Opening connections, registering services and branching between HTTP and CLI go in load() on your application class. Call it before run() — the generated front controller does:

PHPapp/src/Application.php
<?php

namespace App;

class Application extends \Pop\Application
{

    public function load(): Application
    {
        if (isset($this->config['database'])) {
            $this->initDb($this->config['database']);
        }

        if ($this->router() !== null) {
            if ($this->router()->isHttp()) {
                $this->on('app.dispatch.pre', 'App\Http\Event\Options::send', 1);
            } else if ($this->router()->isCli()) {
                $this->on('app.dispatch.post', function() { echo PHP_EOL; });
            }
        }

        return $this;
    }

}

initDb() is an app-level convention that pop:init scaffolds, shown in full in Records & the ORM. The credentials stay data in app/config/database.php; the connection opens here.

Adding a database after your initial pop:init? Run ./kettle db:config, then add 'database' => include __DIR__ . '/database.php', to app.http.php so load() can find it.

Environment Variables#

Per-machine values live in .env, which vlucas/phpdotenv loads into $_ENV before anything else — public/index.php for web requests, kettle for commands. pop:init writes it, .gitignore excludes it, and .env.example is the copy you commit.

Variable Used for
APP_NAME the application name, when the config sets no name key
APP_ENV local, dev, testing, staging or production
APP_URL the application's base URL
MAINTENANCE_MODE true or false
MAINTENANCE_MODE_SECRET lets specific clients through while maintenance mode is on
DB_ADAPTER, DB_DATABASE, DB_USERNAME, DB_PASSWORD, DB_HOST, DB_TYPE read by app/config/database.php
QUEUE_ADAPTER, QUEUE_PRIORITY, QUEUE_LEASE read by the queue configuration

Read them through Pop\App::env(), which coerces the literals dotenv leaves as strings — true and (true) come back as true, false as false, null as null, empty as '':

PHP
use Pop\App;

$name = App::env('APP_NAME');
$mode = App::env('MAINTENANCE_MODE');       // bool, not the string 'true'
$tz   = App::env('APP_TIMEZONE', 'UTC');    // second argument is the default

The default applies when the key is absent. A key written with no value — DB_HOST= on its own line — is an empty string, so remove the line entirely for the default to take effect.

Per-Environment Branching#

One config array per runtime, with the environment as a value you branch on:

PHP
use Pop\App;

if (App::isLocal()) {
    // verbose errors, no cache
} else if (App::isProduction()) {
    // real mail transport, real payment gateway
}

isProduction() matches any APP_ENV starting with prod, and App::environment(['staging', 'production']) tests membership in a set. Every predicate is also an instance method — $this->isProduction() inside load(), which is usually where the branch belongs.

./kettle pop:env prints the current environment and ./kettle pop:env --set changes it. In production, kettle confirms before running anything destructive.

For maintenance mode, ./kettle pop:down --secret=letmein sets the flag and the secret, ./kettle pop:up clears it. Gate on App::isDown(), and App::isSecretRequest() lets a client holding the secret through.

Config Objects#

The constructor takes a Pop\Config\Config anywhere it takes an array, which adds dot-notation access and immutability:

PHP
use Pop\Config\Config;

$config = new Config(['database' => ['host' => 'localhost', 'port' => 5432]]);

$host = $config['database.host'];   // 'localhost'
$config->changesAllowed();          // false

Pass true as the second constructor argument for a writable one. Config::createFromData() also reads INI, JSON, XML and YAML — see Config Objects.

See Also#