A real application has many settings: the database host, the site name, debug mode, mail server, cache time. Some are secret. Some differ between your laptop and the live server. Configuration management is the habit of keeping them out of your logic and in one organised place.
Code vs configuration
| Code | Configuration |
|---|---|
| Same everywhere | Different per environment |
| Reviewed and committed | Often secret, so not committed |
calculateTax() | TAX_RATE, DB_PASSWORD |
If changing a value means editing code in ten files, it should have been configuration.
Environments
Most projects run in at least three:
| Environment | Purpose | Typical settings |
|---|---|---|
| local | Your computer | Debug on, errors shown, test database |
| staging | A copy of the live site for testing | Debug off, real-like data |
| production | The live site | Debug off, errors logged, strong secrets |
The same code runs in all three. Only the configuration changes.
A config file that returns an array
<?php
return [
"name" => "Code Corner",
"debug" => false,
"timezone" => "Asia/Kolkata",
"per_page" => 10,
];
A small Config class
<?php
final class Config
{
public function __construct(private array $items) {}
public function get(string $key, mixed $default = null): mixed
{
$value = $this->items;
foreach (explode(".", $key) as $part) {
if (!is_array($value) || !array_key_exists($part, $value)) {
return $default;
}
$value = $value[$part];
}
return $value;
}
}
$config = new Config([
"app" => ["name" => "Code Corner", "debug" => false],
"db" => ["host" => "127.0.0.1", "port" => 3306],
]);
echo $config->get("app.name") . "\n";
echo $config->get("db.port") . "\n";
echo $config->get("mail.host", "not configured");
Code Corner
3306
not configured
The class reads nested values with a dotted key, and returns a default when a setting is missing.
Pass configuration in, do not reach out for it
<?php
final class Paginator
{
public function __construct(private int $perPage) {}
public function pages(int $total): int
{
return (int) ceil($total / $this->perPage);
}
}
$perPage = 10; // in the real app: $config->get("app.per_page")
echo (new Paginator($perPage))->pages(47);
5
The Paginator knows nothing about where the number came from. It is easy to test, and easy to reuse. This is dependency injection applied to settings.
Rules
- One place for each setting.
- Defaults that are safe (debug off, not on).
- Secrets from the environment, never committed (Environment Variables).
- Validate required settings at startup, and fail with a clear message if one is missing.
- Do not read
$_ENV,getenv()or config files all over the code. Read them once, in one place, and pass values down.
Fail early on missing settings
<?php
function requireSetting(array $settings, string $key): string
{
$value = $settings[$key] ?? "";
if ($value === "") {
throw new RuntimeException("Missing required setting: $key");
}
return $value;
}
try {
requireSetting(["DB_HOST" => "127.0.0.1"], "DB_NAME");
} catch (RuntimeException $e) {
echo $e->getMessage();
}
Missing required setting: DB_NAME
A clear error at startup is far better than a strange failure in the middle of a request.
Why this matters in WordPress and Laravel
wp-config.php constants and the wp_options table. Laravel keeps files in config/ that read values with env() from a .env file. Both separate code from settings, the same way.