Config, environments, and secrets. Hard-coding database passwords in source files works until you push to GitHub and regret it.
Config as a returned array
<?php
// config/app.php
declare(strict_types=1);
return [
'debug' => false,
'name' => 'My App',
'url' => 'https://example.com',
];
$config = require dirname(__DIR__) . '/config/app.php';
$dbConfig = require dirname(__DIR__) . '/config/database.php';
.env for secrets (local)
Use a .env file for machine-specific values. Never commit it. Add .env to .gitignore. Commit .env.example with empty placeholders instead.
# .env.example
APP_DEBUG=true
APP_URL=http://localhost:8080
DB_HOST=127.0.0.1
DB_NAME=myapp
DB_USER=
DB_PASS=
Load .env with vlucas/phpdotenv (composer require vlucas/phpdotenv) or a tiny parser you write yourself.
$dotenv = DotenvDotenv::createImmutable(dirname(__DIR__));
$dotenv->safeLoad();
$host = $_ENV['DB_HOST'] ?? '127.0.0.1';
Environment-specific config
$env = $_ENV['APP_ENV'] ?? 'production';
$config = require __DIR__ . '/app.php';
$local = __DIR__ . "/app.$env.php";
if (file_exists($local)) {
$config = array_merge($config, require $local);
}
Secrets on production
- Set env vars in the hosting panel or server config – not in git
- Rotate keys if they leak
- Different DB credentials per environment
- Turn off debug in production (
APP_DEBUG=false)
What belongs in config
- Database DSN parts, mail SMTP host, API base URLs
- Feature flags (maintenance mode, registration open)
- Not business rules that change often – those live in code or the database
Treat secrets like house keys. Config files describe the app; .env holds the keys. Keep them out of version control.

