Environment Variables
Built-in dotenv support with 3-level priority and type casting.
Table of contents
Overview
Wirebox has a built-in .env parser — no external packages like vlucas/phpdotenv or symfony/dotenv needed. Environment variables are resolved with a 3-level priority system.
Resolution Priority
Variables are resolved in order of priority (highest first):
| Priority | Source | Description |
|---|---|---|
| 1 (highest) | .env.local.php |
PHP array file. Generated by composer dump-env. Fastest. |
| 2 | $_ENV / getenv() |
Real system environment variables (Docker, CI, etc.) |
| 3 (lowest) | .env |
Parsed by built-in DotEnvParser. Development fallback. |
All files are resolved relative to the projectDir passed to ContainerBuilder.
If a variable is defined at multiple levels, the highest-priority source wins. For example, a system env var overrides .env, and .env.local.php overrides everything.
The .env File
Create a .env file in your project root for development defaults:
# Application
APP_NAME=Wirebox
APP_DEBUG=true
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
# Secrets
SECRET_KEY=dev-secret-key-change-in-production
Supported Syntax
# Comments start with #
APP_NAME=Wirebox
# Quoted values (preserves spaces)
GREETING="Hello World"
# Single quotes (literal, no interpolation)
PATTERN='${NOT_INTERPOLATED}'
# Variable interpolation (double quotes or unquoted)
BASE_PATH=/opt
FULL_PATH="${BASE_PATH}/app"
ALSO_WORKS=${BASE_PATH}/app
# Export prefix is stripped
export SECRET_KEY=abc123
# Empty values
EMPTY_VALUE=
Interpolation Rules
| Syntax | Behavior |
|---|---|
${VAR} in double quotes |
Interpolated |
${VAR} unquoted |
Interpolated |
${VAR} in single quotes |
Literal (not interpolated) |
Using Environment Variables
With #[Param] Attribute
The simplest way — inject directly into constructor parameters:
use AsceticSoft\Wirebox\Attribute\Param;
class AppConfig
{
public function __construct(
#[Param('APP_NAME')] private string $appName,
#[Param('APP_DEBUG')] private bool $debug,
#[Param('DB_PORT')] private int $port,
) {
}
}
Type casting is automatic based on the parameter’s type hint.
With parameter() on the Builder
Define named parameters with environment expressions:
$builder->parameter('db.host', '%env(DB_HOST)%');
$builder->parameter('db.port', '%env(int:DB_PORT)%');
$builder->parameter('app.debug', '%env(bool:APP_DEBUG)%');
Then access them in the container:
$host = $container->getParameter('db.host');
$port = $container->getParameter('db.port'); // int
Type Casting
In parameter() Expressions
Use the type: prefix inside %env(...)%:
$builder->parameter('port', '%env(int:DB_PORT)%'); // int
$builder->parameter('rate', '%env(float:RATE_LIMIT)%'); // float
$builder->parameter('debug', '%env(bool:APP_DEBUG)%'); // bool
$builder->parameter('host', '%env(DB_HOST)%'); // string (default)
| Cast | Expression | Input | Output |
|---|---|---|---|
string |
%env(DB_HOST)% |
"localhost" |
"localhost" |
int |
%env(int:DB_PORT)% |
"5432" |
5432 |
float |
%env(float:RATE)% |
"1.5" |
1.5 |
bool |
%env(bool:DEBUG)% |
"true" |
true |
Boolean Casting Rules
| Value | Result |
|---|---|
"true", "1", "yes", "on" |
true |
"false", "0", "no", "off", "" |
false |
With #[Param] Attribute
Type casting is based on the PHP type hint:
public function __construct(
#[Param('DB_PORT')] private int $port, // auto-cast to int
#[Param('RATE')] private float $rate, // auto-cast to float
#[Param('DEBUG')] private bool $debug, // auto-cast to bool
#[Param('HOST')] private string $host, // string (no cast)
)
Embedded Expressions
Environment expressions can be part of a larger string:
$builder->parameter('dsn', 'mysql:host=%env(DB_HOST)%;port=%env(DB_PORT)%');
// Result: "mysql:host=localhost;port=5432"
$builder->parameter('redis.url', 'redis://%env(REDIS_HOST)%:%env(REDIS_PORT)%');
// Result: "redis://127.0.0.1:6379"
When an env expression is embedded in a string, the result is always a string — even with a type cast prefix. Type casting only applies when the expression is the entire parameter value.
Production: .env.local.php
For production deployments, avoid parsing .env files at runtime. Generate a cached PHP array:
# Using Symfony's composer plugin
composer dump-env prod
This creates .env.local.php:
<?php
return [
'APP_NAME' => 'Wirebox',
'APP_DEBUG' => 'false',
'DB_HOST' => 'db.production.internal',
'DB_PORT' => '5432',
];
Since this is a plain PHP array, there’s zero parsing overhead — just a file include.
The .env.local.php file has the highest priority. It overrides both system environment variables and .env. This makes deployments predictable — what you dump is what you get.
Environment Variable Flow
Here’s how Wirebox resolves an env variable at runtime:
Request: %env(int:DB_PORT)%
│
▼
.env.local.php exists?
│
┌────┴────┐
Yes No
│ │
▼ ▼
Return $_ENV / getenv()
value has DB_PORT?
│
┌────┴────┐
Yes No
│ │
▼ ▼
Return Parse .env
value has DB_PORT?
│
┌────┴────┐
Yes No
│ │
▼ ▼
Return Throw
value Exception
Best Practices
- Never commit
.envwith real secrets. Use.env.exampleas a template:DB_HOST=localhost DB_PORT=5432 SECRET_KEY=change-me - Use
.env.local.phpin production for zero-overhead resolution:composer dump-env prod - Use system environment variables in Docker/CI:
ENV DB_HOST=db.internal ENV DB_PORT=5432 -
Type-cast at the boundary — use
int:,float:,bool:prefixes to ensure correct types from the start. - Keep
.envminimal — only development defaults. Production config should come from the environment or.env.local.php.