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

Disposable Email Bundle Laravel Package

nalabdou/disposable-email-bundle

Symfony bundle that flags disposable and temporary email addresses before they reach your users. Ships a validator constraint, a reusable checker service, and an updatable list of throwaway domains, so signups, forms, and APIs can reject fake inboxes with a single line of configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

nalabdou/disposable-email-bundle is a Symfony bundle, not a Laravel package, so treat v1.0.0 as "a well-structured PHP library with a Symfony delivery mechanism." The good news is that the release notes show a clean split between the framework-agnostic pieces (contracts, value objects, and exceptions, core services, whitelist providers, domain loaders, domain events) and the Symfony glue (bundle bootstrapping and DI layer, Symfony integrations, attribute-based configuration system). In Laravel you will use the former and ignore the latter.

composer require nalabdou/disposable-email-bundle

Because Laravel has no Symfony bundle auto-registration, the bundle's DependencyInjection layer will not run for you. First place to look after install:

  1. src/Contracts/ — the interfaces for the checker, domain loader, and whitelist provider. These are what you bind in your own service provider.
  2. src/ValueObjects/ — the email/domain value objects and the result type returned by a check. Type-hint against these in your validation rules.
  3. resources/config/ — the reference configuration (likely YAML). Port the keys you care about into config/disposable-email.php.
  4. src/Exceptions/ — you will translate these into ValidationException so they surface as 422s rather than 500s.

Minimal first use case — block disposable emails at registration:

// app/Providers/DisposableEmailServiceProvider.php
public function register(): void
{
    $this->app->singleton(DisposableEmailChecker::class, function ($app) {
        return new DisposableEmailChecker(
            loader: new FileDomainLoader(config('disposable-email.domain_file')),
            whitelist: new ConfigWhitelistProvider(config('disposable-email.whitelist', [])),
        );
    });
}
// in a FormRequest
public function rules(): array
{
    return [
        'email' => ['required', 'email', new NotDisposableEmail()],
    ];
}

If you are on a hybrid stack (Symfony app plus a Laravel admin panel) you can register the bundle in Symfony and reuse only the core services in Laravel — the split exists precisely for this.

Implementation Patterns

Wrap the checker in a Laravel validation rule. This is the 90% use case. Keep the rule thin: it translates the value object result into a Laravel ValidationException and returns a translatable message. Do not let Symfony exceptions leak into your HTTP layer.

Bind contracts, not concretes. The package ships a suite of domain loaders — swap them per environment. FileDomainLoader in local/CI, a cached/remote loader in production. config('disposable-email.loader') selecting an implementation keeps tests hermetic and avoids network calls in CI.

Cache the domain list aggressively, but not forever. Disposable domains rotate weekly. Register a scheduled job to warm/refresh the cached list and let the loader read from the cache store. A Cache::remember() around a loader call is usually enough; if the package's loader is immutable-per-instance, resolve it as a scoped binding rather than a singleton so long-lived workers (Octane, queue workers) pick up refreshed data.

Map domain events to Laravel events. The domain events added in 25d81cc are almost certainly dispatched through Symfony's EventDispatcherInterface. In Laravel, either inject a small adapter that forwards to event()/dispatch(), or listen for the package's event classes directly by resolving them through your own dispatcher binding. A DisposableEmailRejected event is the natural hook for fraud/analytics logging — you want the rejected domain, the email hash, and the request context (IP, route).

Whitelist providers are your false-positive valve. Corporate domains, .gov, and niche-but-legitimate providers get misclassified. Implement a WhitelistProvider that composes: a static config list + a database table of exemptions your support team can edit. Precedence matters — confirm whether the package checks the whitelist before or after the domain list, and test that ordering explicitly.

Layer checks by cost. Run the cheap syntactic/value-object checks on every request; run the disposable-domain check only on registration, invite acceptance, and password reset — not on login, where an existing user with a now-blacklisted domain must still get in. Gate it behind a feature flag so you can disable it in an incident.

Use PHP attributes where the package offers them. The attribute-based configuration system is the ergonomic win from this release: annotate DTOs or jobs with the package's attributes and resolve configuration reflectively. It works fine in Laravel (PHP 8 attributes are framework-neutral), but you must still register the corresponding reflection-based factory yourself.

Gotchas and Tips

  • No Laravel package discovery. The service provider is Symfony\Component\HttpKernel\Bundle\Bundle-shaped. Do not add it to config/app.php; it will fatal. Publish/port config manually from resources/config/.
  • Config file is YAML. If you vendor:publish, verify what lands in config/. Writing your own PHP config file is safer and gives you env() support the bundle config cannot express.
  • Two different event systems. Symfony's dispatcher is not Laravel's. Events dispatched inside the package will be silently dropped unless you bridge them. Check composer.json for symfony/event-dispatcher and whether the package accepts an injected dispatcher — if it does, inject a Laravel-backed adapter at bind time.
  • Case and normalisation. Test Gmail.com, [email protected], leading/trailing whitespace, and IDN/punycode (bücher.de vs xn--bcher-kva.de). Disposable lists are usually lowercase ASCII; if normalisation happens in the loader rather than the checker, you can get false negatives. Add a test asserting the value object normalises before lookup.
  • Subdomains. Decide explicitly whether mail.disposable.example matches disposable.example. Whether the package does suffix matching is a one-line test — write it before you trust the result.
  • Long-running processes. Singleton loaders plus cached domain files are stale in Octane and queue workers. Use scoped bindings or listen for the relevant Octane/worker lifecycle events to reset.
  • Exceptions vs. booleans. The exceptions in src/Exceptions/ are likely thrown for loader failures (missing file, unreachable source), not for "this email is disposable." Distinguish operational failure from a negative result — fail open (allow the registration, log loudly) on loader failure rather than blocking every signup when the domain list is unreachable.
  • Test doubles. The contracts directory means you can stub the loader and whitelist provider without any filesystem access. Do that in your own test suite — never hit a real remote list in tests, and never assert against a snapshot of the shipped domain file, which will change between releases.
  • v1.0.0 is young. It is the initial release with a full test suite and QA tooling, which is encouraging, but there is no deprecation history to read and no evidence yet of semantic-versioning discipline on the contracts. Pin the version ("^1.0") and keep the framework-agnostic surface behind your own NotDisposableEmail rule so an upstream contract change is a one-file fix rather than a codebase-wide sweep.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core