Pular para o conteúdo principal

Configuration Deep Dive

Advanced configuration topics including environment inheritance, config loading, credentials management, and multi-environment setup.

Table of Contents

Overview

Configuration is powered by byjg/config:

  • Environment Inheritance: test and staging inherit from dev; prod inherits from staging
  • Flat, numbered files: config/{env}/01-infrastructure.php06-external.php, loaded in filename order
  • .env params: each environment has a credentials.env; config/.env provides local machine overrides
  • Caching: staging and prod cache the resolved container between requests

Location: config/

Environment Hierarchy

The environments and their inheritance are defined in ByJG\Gluo\Config\BaseConfigBootstrap (byjg/gluo-core). Your project's config/ConfigBootstrap.php just extends it:

Development (dev)
├── Test (test) # Inherits from dev
└── Staging (staging) # Inherits from dev, cached
└── Production (prod) # Inherits from staging (then dev), cached

How Inheritance Works

This is the actual definition in gluo-core:

// ByJG\Gluo\Config\BaseConfigBootstrap (vendor/byjg/gluo-core)
$dev = Environment::create('dev');
$test = Environment::create('test')
->inheritFrom($dev);
$staging = Environment::create('staging')
->inheritFrom($dev)
->withCache(new FileSystemCacheEngine());
$prod = Environment::create('prod')
->inheritFrom($staging, $dev)
->withCache(new FileSystemCacheEngine());

An environment only needs to define what differs from its parent: a key defined in config/prod/ overrides the same key inherited from staging/dev.

Inheritance Example

config/
├── dev/
│ └── credentials.env # DBDRIVER_CONNECTION=mysql://...@mysql-container/localdev
├── staging/
│ └── credentials.env # Override: staging database and secrets
└── prod/
└── credentials.env # Override: production database and secrets

Result:

  • dev uses its own connection
  • test inherits everything from dev it does not override
  • staging overrides the connection; everything else falls back to dev
  • prod starts from staging and overrides what differs

Configuration Files

Each environment directory contains flat, numbered PHP files — the numbering controls load order, and later definitions win:

config/
├── ConfigBootstrap.php # Bootstrap (extends gluo-core BaseConfigBootstrap)
├── .env # Local machine overrides (gitignored; see .env.sample)
└── {environment}/
├── credentials.env # Connection strings, JWT secret, mail, CORS
├── 01-infrastructure.php # Database, cache, logging, ORM init
├── 02-security.php # JWT, password policy, users service
├── 03-api.php # Route list, middleware, HTTP handler
├── 04-repositories.php # Repository DI bindings
├── 05-services.php # Service DI bindings
└── 06-external.php # Mail and other external services

byjg/config loads, for the active environment (and its parents): every *.php file (DI bindings and params), every *.env file (plain params), and finally config/.env for local overrides.

Environment-Specific Configs

Values that vary per environment live in each environment's credentials.env and are consumed in the PHP config files via Param::get():

config/dev/credentials.env
DBDRIVER_CONNECTION=mysql://root:mysqlp455w0rd@mysql-container/localdev
JWT_SECRET=ZGV2LS1qd3Qtc2VjcmV0...
CORS_SERVERS=.*
config/dev/01-infrastructure.php (excerpt)
use ByJG\AnyDataset\Db\Factory;
use ByJG\Config\DependencyInjection as DI;
use ByJG\Config\Param;

return [
DbDriverInterface::class => DI::bind(Factory::class)
->withFactoryMethod("getDbRelationalInstance", [Param::get('DBDRIVER_CONNECTION')])
->toSingleton(),
];

To change the database in another environment, override only the key:

config/prod/credentials.env
DBDRIVER_CONNECTION=mysql://produser:[email protected]/myapp

Reading OS Environment Variables

To pull values from the operating-system environment (Docker/Kubernetes secrets), register them in your bootstrap (see below) with withOSEnvironment(). The default bootstrap already exposes TAG_VERSION and TAG_COMMIT.

Credentials Management

Per-environment credentials.env

Each environment ships a credentials.env with its connection strings and JWT_SECRET. composer create-project regenerates a unique JWT secret for every environment.

Local overrides: config/.env

File: config/.env (not committed — it is in .gitignore; a documented example lives in config/.env.sample)

Use it for developer-machine specifics you don't want in version control:

config/.env
# Override database connection for local development
DBDRIVER_CONNECTION=mysql://root:[email protected]/localdev

# Override JWT secret for local testing
JWT_SECRET=local-dev-secret-key
Keep secrets out of version control

Real production secrets belong in OS environment variables (via withOSEnvironment()) or in a secrets manager — not committed to credentials.env.

Configuration Bootstrap

File: config/ConfigBootstrap.php

The project bootstrap is intentionally tiny — the environment set, inheritance, and caching live in gluo-core, and improvements arrive with composer update:

<?php

use ByJG\Gluo\Config\BaseConfigBootstrap;

return new class extends BaseConfigBootstrap {
};

Customizing the Definition

Override configureDefinition() to add OS environment variables, extra config directories, or custom environments:

return new class extends BaseConfigBootstrap {
#[\Override]
protected function configureDefinition(Definition $definition): void
{
parent::configureDefinition($definition); // keeps TAG_VERSION / TAG_COMMIT

// Expose more OS environment variables as params
$definition->withOSEnvironment(['DATABASE_URL', 'REDIS_HOST']);
}
};

Best Practices

1. Use OS Environment Variables for Production Secrets

// Good - registered OS variable, injected by the platform
$definition->withOSEnvironment(['DATABASE_URL', 'JWT_SECRET']);

// Bad - production secret committed in credentials.env

2. Override Only What Differs

Rely on inheritance: keep the complete configuration in dev, and let test, staging, and prod define only their deltas. Small environment dirs are a feature, not an omission.

3. Use Param::get() for Values, DI::bind() for Services

return [
// A value resolved at injection time
DbDriverInterface::class => DI::bind(Factory::class)
->withFactoryMethod("getDbRelationalInstance", [Param::get('DBDRIVER_CONNECTION')])
->toSingleton(),

// A service with injected constructor
DatabaseExecutor::class => DI::bind(DatabaseExecutor::class)
->withInjectedConstructor()
->toSingleton(),
];

4. Keep the Layer Numbering

# Good - layered by concern, loaded in order
config/dev/01-infrastructure.php
config/dev/02-security.php
config/dev/03-api.php

# Bad - everything in one file
config/dev/app.php

Later files can reference bindings from earlier ones — repositories (04) build on the database (01), services (05) build on repositories.

5. Environment-Specific Caching

staging and prod already cache the resolved definition with FileSystemCacheEngine (see BaseConfigBootstrap). After changing config in those environments, clear the cached container (the cache lives in the system temp dir).