Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dotenv Laravel Package

josegonzalez/dotenv

josegonzalez/dotenv is a lightweight PHP library for loading environment variables from .env files into getenv(), $_ENV, and $_SERVER. It supports environment-specific files, sections, and overwrite or immutable modes, making configuration management simple in any PHP or Laravel project.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it alongside your existing Laravel app (it does not ship with Laravel — Laravel defaults to vlucas/phpdotenv):

composer require josegonzalez/dotenv

The package centers on a single class, josegonzalez\Dotenv\Loader, with a fluent API. The shortest viable boot is parse-then-apply:

use josegonzalez\Dotenv\Loader;

(new Loader(__DIR__ . '/.env'))
    ->parse()
    ->toEnv();

Where to look first: Loader::parse() and Loader::toArray(). Parse-only mode is the safest entry point because it inspects a file without touching $_ENV, $_SERVER, or putenv(). Use it to confirm the package finds and parses your file before you start mutating global state:

$values = (new Loader(base_path()))->parse()->toArray();
// ['APP_NAME' => 'My App', 'DB_HOST' => '127.0.0.1', ...]

First real use case: load an environment-specific file before Laravel boots. Because josegonzalez/dotenv lets you pick the filename explicitly, it fits naturally into bootstrap/env.php or the top of artisan:

// artisan, public/index.php, tests/bootstrap.php
$env = getenv('APP_ENV') ?: 'local';
(new Loader(__DIR__ . '/..'))->setFilename(".env.{$env}")->parse()->toEnv()->toServer();

If that runs cleanly, you have the 80% of the package you'll actually use.

Implementation Patterns

1. Pre-bootstrap env selector. The main reason a Laravel dev reaches for this package over the default loader: choose the file before the container exists. Wrap it as a small bootstrap file required at the top of artisan, public/index.php, and your PHPUnit bootstrap so CLI, web, and test paths all agree.

// bootstrap/env.php
$env = $_SERVER['APP_ENV'] ?? getenv('APP_ENV') ?: 'local';
(new Loader(__DIR__ . '/..'))
    ->setFilename(".env.{$env}")
    ->parse()
    ->toEnv()
    ->toServer();

2. Disable Laravel's loader. If you're going to own env loading, own it fully. Remove or replace Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables from the bootstrapper list (or short-circuit it in bootstrap/app.php) so two loaders don't race for the same keys.

3. Parse-only for validation. Write a small artisan command that checks required keys exist before deployment:

$values = (new Loader(base_path()))->parse()->toArray();
$missing = array_diff(['APP_KEY', 'DB_HOST', 'DB_DATABASE'], array_keys($values));
if ($missing) {
    $this->error('Missing: ' . implode(', ', $missing));
}

4. Redaction before logging or shelling out. Filters mutate the parsed map in place, so you can mask secrets before they leave the process:

(new Loader(base_path()))
    ->parse()
    ->filter('DB_PASSWORD', $redacted = '***')
    ->toEnv();

5. Value transformations. Filters also accept callables, which is handy for normalizing casing, trimming, or coercing types:

->filter('APP_ENV', fn ($v) => strtolower($v))
->filter('DB_PORT', fn ($v) => (int) $v)

6. Constants for legacy code. define() writes parsed values into PHP constants — useful when integrating with older libraries or scripts that expect APP_ENV-style constants rather than env vars:

(new Loader(base_path()))->parse()->define();

7. Multi-tenant or per-connection loading. Loop over tenant dirs and load each .env, being deliberate about overwriting:

foreach ($tenants as $tenant) {
    (new Loader(base_path("tenants/{$tenant->slug}")))
        ->setFilename('.env')
        ->parse()
        ->overload()   // see gotchas: use only in CLI/scripts
        ->toEnv();
}

8. Standalone scripts in CI/CD. Any deploy or maintenance script that doesn't want to boot the whole framework but does need env vars can load a specific file in three lines. This is the cleanest fit for the package in day-to-day work.

Integration tips: run loading before Application is built (not inside config files — env() returns null there after config:cache), keep filenames conventional (.env, .env.local, .env.staging, .env.testing), and prefer toEnv()->toServer() over putenv() so values are visible to env() and getenv()-style lookups consistently.

Gotchas and Tips

  • Two loaders, one source of truth. Laravel already loads .env via vlucas/phpdotenv. Running both is redundant at best and order-dependent at worst. Confirm which one is active before debugging mysterious missing keys.
  • Existing env wins by default. The loader does not clobber vars already set by Docker, Kubernetes, or Heroku. That's usually the behavior you want — use overload() only in CLI scripts where you explicitly need a file to override the process environment.
  • putenv() is legacy. Laravel deprecated putenv() in 5.8 because it isn't thread-safe and doesn't survive config:cache. Prefer toEnv()/toServer().
  • Cached config ignores .env. Once php artisan config:cache runs, none of this matters for the web path. Re-cache after changing env files, and remember env() outside config files returns null at runtime.
  • define() won't overwrite. Pre-defined constants are skipped silently. Same pattern applies to env/server arrays unless you call overload(). Don't expect a second load to "refresh" values.
  • Path resolution. Relative paths resolve against the current working directory, which is not the calling file. Always pass base_path() or __DIR__ — otherwise php artisan from a subdirectory silently looks in the wrong place.
  • Missing files. Verify how your version handles a missing file (exception vs. silent no-op); wrap bootstrap loading in a try/catch or check file_exists() first, and fail loudly with a clear message so deploys don't ship with an empty environment.
  • Filter ordering matters. filter() mutates the parsed map. Chain it before toEnv()/toServer()/toArray(), and remember later writes to the same key need to come before earlier consumers.
  • Interpolation and quoting. Values referencing other keys depend on parse order — forward references can resolve to empty strings. Multi-line secrets (private keys) need proper quoting; verify they survive round-trip.
  • Debugging technique. Compare ->parse()->toArray() with getenv('KEY') at the top of bootstrap/env.php; that isolates "did the file parse correctly" from "did something else overwrite it later."
  • Extension points. Filters accept callables, and the fluent Loader is easy to subclass or wrap in a small service class (EnvironmentLoader) for DI-friendly usage in commands and tests.
  • Maintenance risk. Last release 2023, ~277 stars, effectively one maintainer. It's fine for scripts, legacy integration, and internal tooling — but for new Laravel apps on modern PHP, weigh it against vlucas/phpdotenv, which is the ecosystem default and receives ongoing maintenance.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib