oscarotero/env
oscarotero/env is a simple PHP library to read environment variables and convert them to typed values (bool, int, float, arrays, etc.), with support for .env files, defaults, and nested variable expansion. Lightweight and dependency-free alternative to phpdotenv.
oscarotero/env is a single-class, zero-dependency reader for process environment variables with light type coercion. It is not a .env file loader — it reads what is already in the process environment (getenv() / $_ENV / $_SERVER).
Minimal steps:
composer require oscarotero/env
require __DIR__ . '/vendor/autoload.php';
use Env\Env;
$debug = Env::get('APP_DEBUG', false); // bool
$port = Env::get('PORT', 8080); // int
$name = Env::get('APP_NAME', 'app'); // string
Where to look first:
README.md — the whole API fits on one screen.src/Env.php — ~100 lines; worth skimming to confirm the exact type-constant names and the auto-conversion rules before you rely on them.public/index.php, artisan, a CLI script) — this is where environment variables actually reach PHP, and where Env::get() belongs.First use case: a framework-agnostic config.php that returns a typed array. Everything downstream consumes the array, not the environment, so the rest of the codebase is testable and free of getenv() calls.
Config-as-a-boundary. Put every Env::get() call in one or a handful of config files. Convert once, at boot, then pass typed values around. This mirrors Laravel's "only use env() inside config/" convention and keeps environment coupling to one layer.
// config/database.php
return [
'host' => Env::get('DB_HOST', '127.0.0.1'),
'port' => Env::get('DB_PORT', 3306),
'database' => Env::get('DB_DATABASE', 'app'),
'debug' => Env::get('DB_DEBUG', false),
];
Explicit types over implicit coercion. Auto-conversion of strings like "true", "false", "null", and numerics is convenient but implicit. For anything security- or correctness-sensitive (feature flags, timeouts, pool sizes), force the type explicitly using the package's type constants — e.g. Env::get('WORKERS', null, Env::TYPE_INT) — and treat a null result as a configuration error rather than relying on a default. Check src/Env.php for the exact constant names in your version.
Fail fast for required values. The library returns the default (usually null) for missing keys and does not validate. Wrap it:
function required(string $key): string
{
$value = Env::get($key);
if ($value === null || $value === '') {
throw new RuntimeException("Missing required env var: {$key}");
}
return $value;
}
Laravel integration. Laravel's own env() already coerces true/false/null/empty, so this package earns its place mainly in (a) non-Laravel or framework-agnostic packages, (b) code shared between Laravel and standalone CLI tools, and (c) places where you want a stricter, predictable coercion with a documented default. Inside Laravel, still confine calls to config/*.php and re-run php artisan config:cache after changing your reader logic.
Docker / CI workflow. Define variables in docker-compose.yml, a Kubernetes Secret, or CI settings; let the package do the typing inside the container. This keeps a clean split: infrastructure owns where values come from, PHP owns what type they are.
Testing. Set values before each test, and clean up afterwards:
setUp(): putenv('DB_PORT=5433'); $_ENV['DB_PORT'] = '5433';
tearDown(): putenv('DB_PORT'); unset($_ENV['DB_PORT']);
For anything larger, wrap Env::get() behind your own Config object and inject that — the static API is awkward to mock.
.env files. Pair it with vlucas/phpdotenv, symfony/dotenv, Laravel's bootstrap, or real container/host environment variables. A common mistake is installing this package expecting .env parsing.getenv() vs $_ENV vs $_SERVER. Different SAPIs populate different superglobals. PHP-FPM with clear_env = yes (a frequent default) strips the environment entirely — you must pass variables via env[...] in the pool config or read from $_SERVER. Verify with a quick var_dump(getenv('FOO'), $_ENV['FOO'] ?? null) in the actual runtime, not just your shell."0" becoming 0 (falsy), empty string vs. null, whitespace around values (" true "), and values like "yes"/"on" that are not converted to booleans. Keep env values canonical: true/false, plain integers, no quotes.config:cache runs, config/*.php is not re-evaluated, so changing an environment variable will not take effect on the next request even though Env::get() would return the new value. Any env-driven logic must live in config files, never in controllers or services.UPPER_SNAKE_CASE to avoid surprises.interface Config { public function get(string $key, mixed $default = null): mixed; }) and bind it once at boot..env locally (git-ignored) and real secrets managers in production. The library will happily return a credential; keeping it out of source control and logs is on you.How can I help you explore Laravel packages today?