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

Env Providers Laravel Package

sven/env-providers

Env Providers lets Laravel apps register service providers conditionally by environment, so local, staging, and production-only providers can live outside config/app.php and load automatically based on APP_ENV.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require sven/env-providers

There is nothing to publish — no config file, no migrations, no auto-discovered service provider. The package is a single trait, so it only does something once you use it.

Where to look first

  1. vendor/svenluijten/env-providers/src/LoadsProvidersFromEnvironment.php — it is a few dozen lines; read it before wiring it in so you know exactly what it calls ($this->app->environment() + $this->app->register()).
  2. Your APP_ENV value — this is the single input that drives everything. Check with php artisan about or php artisan tinker --execute="dump(app()->environment());".
  3. bootstrap/providers.php (Laravel 11+) or config/app.php (Laravel ≤10) — the provider you attach the trait to must itself be registered normally.

Minimal wiring

Attach the trait to an existing provider and declare the environment → providers map:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Sven\EnvProviders\LoadsProvidersFromEnvironment;

class AppServiceProvider extends ServiceProvider
{
    use LoadsProvidersFromEnvironment;

    protected function providers(): array
    {
        return [
            'local' => [
                \Barryvdh\Debugbar\ServiceProvider::class,
                \Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
            ],
            'staging' => [
                \Laravel\Telescope\TelescopeServiceProvider::class,
            ],
        ];
    }
}

If your provider already defines its own register(), keep it and invoke the trait's loader from inside it instead of relying on the trait's own register().

First real use case

Debug tooling in local/testing only: Debugbar, IDE Helper, query loggers, mail-catcher UIs. This is the cleanest win, because these packages are noisy in production and you almost never want them registered there. Verify with:

php artisan tinker --execute="dump(array_keys(app()->getLoadedProviders()));"

Implementation Patterns

One dedicated provider, not the trait everywhere. Create App\Providers\EnvironmentServiceProvider whose only job is the map. Sprinkling the trait across AppServiceProvider, AuthServiceProvider, etc. makes registration order hard to reason about.

Group by environment, but keep a "shared" bucket. The trait matches one environment key at a time, so a provider needed in both local and staging has to be listed twice or handled with an explicit check:

protected function providers(): array
{
    return [
        'local'   => [DebugbarServiceProvider::class, ...$this->shared()],
        'staging' => [TelescopeServiceProvider::class, ...$this->shared()],
    ];
}

private function shared(): array
{
    return [\Sentry\Laravel\ServiceProvider::class];
}

Drive the map from config, not hardcoded strings. Environment names drift (dev vs local, prod vs production). Pulling the map out of config/ or computing it lets you keep the list testable:

protected function providers(): array
{
    return config('providers.environments', []);
}

Pair it with config-level gating. If a provider is gated on environment but its config is gated on something else (a feature flag, a SENTRY_LARAVEL_DSN being present), you can end up registering a provider that has no usable configuration. Keep the two conditions in the same place:

'production' => array_filter([
    config('services.sentry.dsn') ? \Sentry\Laravel\ServiceProvider::class : null,
]),

Testing environments. phpunit.xml usually sets APP_ENV=testing, which is a different key from local. Anything you want in the test suite must be listed under testing explicitly — a common source of "works in php artisan serve, fails in tests."

Per-command environments. Artisan's --env flag changes the resolved environment for that invocation (php artisan migrate --env=local). Handy for running a one-off command with debug providers attached — but be aware it changes which of your mapped providers load.

Integrating with Laravel 11's bootstrap/providers.php. List the base provider there as usual; the trait's contents stay invisible to Laravel's discovery, which is exactly what you want (they're registered imperatively at runtime).

Gotchas and Tips

Register in register(), never in boot(). $this->app->register() calls the provider's register() immediately, but its boot() will not be invoked if the application has already finished bootstrapping. Loading providers from a boot() method — or worse, from middleware — leaves you with registered-but-never-booted providers, and under Octane those registrations leak across requests.

Auto-discovered packages cannot be gated this way. If Composer discovery already registered the provider, $this->app->register() deduplicates by class name and silently no-ops. To keep an auto-discovered package out of an environment you must use "extra": {"laravel": {"dont-discover": [...]}} in composer.json, not this package.

config:cache freezes the environment. app.env comes from env('APP_ENV') at cache-build time. Cache config in CI with APP_ENV=local and ship it, and production will happily load your Debugbar provider. Always build the config cache on the target environment (or with the correct APP_ENV), and re-run php artisan config:clear when debugging "wrong providers loaded."

Exact-string matching. APP_ENV="Local" or a trailing space in .env will not match 'local', and the package will fail silently — no exception, just missing providers. When something isn't loading, dump(app()->environment()) first, before touching the map.

Registration order matters. Providers registered this way are appended after everything already in the bootstrapper list. If a conditional provider rebinds a core contract (mailer, filesystem, exception handler), it will win over an earlier binding — that may be what you want, but it's implicit. Put the trait-bearing provider late in bootstrap/providers.php if order is load-bearing.

Don't let the map go recursive. Registering the trait-bearing provider itself from inside its own map will not recurse (deduplication saves you), but it's confusing and order-dependent. Keep the map free of providers already listed in bootstrap/providers.php.

No environment wildcards. The lookup is a plain array key match on the current environment. If you want "everything except production," be explicit:

$env = app()->environment();
$providers = $env === 'production' ? [] : config('providers.non_production');

Debugging checklist, in order: app()->environment() → array_keys(app()->getLoadedProviders()) → php artisan about --only=environment → confirm the config cache isn't stale → confirm the package isn't auto-discovered.

Maintenance risk is low but non-zero. 76 stars, a single-maintainer package, last release October 2024, zero dependents. The upside is that it is ~30 lines of glue: if it ever goes stale or you need wildcard/array-key matching, copying the trait into app/Support/ and extending it is a five-minute job. Evaluate honestly whether

if ($this->app->environment('local')) {
    $this->app->register(DebugbarServiceProvider::class);
}

already covers your case before adding a dependency — the package earns its place when the map grows past a handful of providers or when the environment list is owned by config rather than code.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin