Skip to content

Configuration

Marko uses PHP files for configuration — not YAML, not JSON, not .env directly. PHP config files give you type safety, IDE autocompletion, and a single source of truth.

Configuration lives in PHP files that return arrays:

config/database.php
<?php
declare(strict_types=1);
return [
'default' => env('DB_CONNECTION', 'pgsql'),
'connections' => [
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'localhost'),
'port' => (int) env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'marko'),
'username' => env('DB_USERNAME', 'marko'),
'password' => env('DB_PASSWORD', ''),
],
],
];

Use the ConfigRepositoryInterface with dot notation:

<?php
declare(strict_types=1);
use Marko\Config\ConfigRepositoryInterface;
class DatabaseService
{
public function __construct(
private readonly ConfigRepositoryInterface $configRepository,
) {}
public function getHost(): string
{
return $this->configRepository->getString('database.connections.pgsql.host');
}
}

Never worry about type coercion. Every accessor enforces the return type:

MethodReturns
getString(key)string
getInt(key)int
getFloat(key)float
getBool(key)bool
getArray(key)array
get(key)mixed

All typed accessors throw if the value doesn’t match the expected type. No silent coercion.

Environment variables belong in .env. They should only be referenced from config files, never directly in application code:

.env
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=marko
APP_DEBUG=true
config/app.php
return [
'debug' => (bool) env('APP_DEBUG', false),
];
$debug = $this->configRepository->getBool('app.debug');

This keeps environment variables in one place and makes your application testable with config overrides.

When multiple modules provide the same config key, higher-priority modules win:

vendor/marko/cache/config/cache.php → Base defaults
modules/acme/cache/config/cache.php → Third-party overrides
app/my-app/config/cache.php → Your overrides (wins)