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

  1. Never commit .env with real secrets. Use .env.example as a template:
    DB_HOST=localhost
    DB_PORT=5432
    SECRET_KEY=change-me
    
  2. Use .env.local.php in production for zero-overhead resolution:
    composer dump-env prod
    
  3. Use system environment variables in Docker/CI:
    ENV DB_HOST=db.internal
    ENV DB_PORT=5432
    
  4. Type-cast at the boundary — use int:, float:, bool: prefixes to ensure correct types from the start.

  5. Keep .env minimal — only development defaults. Production config should come from the environment or .env.local.php.