Extending EventServiceProvider causes duplicate verification email notification #57836
Replies: 3 comments 1 reply
-
You can register events in any service provider. Preferably on the provider's
As a package consumer, one should avoid packages that try to replace user-land-level providers instead of providing their own providers, as it is harmful to a user's app and can hide malicious code or unexpected behavior changes on the framework. Imagine a package registers a subclass of the Built-in application-level service providers allow the framework to rapidly provide new features to developers, given their basic behavior is unchanged. Let's say a vulnerability is found in Laravel, and the fix requires changing an application-level service provider's method. Any application subclassing that service provider and overriding that method would still be vulnerable if that app's developer doesn't follow closely the changes in the framework's core.
Extending an application-level service provider is not recommended. And should be avoided. Your package can, and should in most cases, provide service providers that extend the Your package should not override application-level service providers. And especially never try to replace those automatically. If for some reason the package provides such features that replacing an application-level service provider would be needed, it should require explicit user action to replace any application-level service providers so a user is well aware of the changes and consequences. 3rd-party documentation is not to be considered a "recommendation" on how to do things. In contrast, official documentation is to be considered a "recommendation" on how to do things:
Reference: https://laravel.com/docs/12.x/packages#service-providers Nowhere in the official documentation states that a 3rd-party package should override an application-level service provider. Mind that the excerpt above, which recommends extending from the base Reference: https://laravel.com/docs/5.1/packages#service-providers And although Laravel 5.0's documentation does not have this explicit excerpt, it also does not suggest a package's service provider should override from an application-level service provider. Laravel 5.1 was released 10 years ago. I would consider that to be "a long-standing recommendation for third-party packages". |
Beta Was this translation helpful? Give feedback.
-
|
Oh boy, don't you love it when you create a thoughtful issue and get a snarky reply.
I'm not extending the user's app's
You're right there, for sure. However the first-party documentation on writing packages has always been sparse, which is why there are a lot of questions and answers around the web on this topic. Here's a code search across GitHub for repositories extending from the foundation event service provider—I guess we can agree this pattern is spread widely enough to take the problem seriously? |
Beta Was this translation helpful? Give feedback.
-
Most results are based on older Laravel versions where a
Of course it is not. A 3rd-party package replacing an application-level service provider under the hood is an anti-pattern and could be considered malicious. On previous versions, just those instances were registered on the containers, and their base providers never got registered. Since Laravel 11, with the simplified As I said, a user can subclass it as long as they replace any built-in service providers by overriding the The An approach I already saw was to add an empty array to the There a user can replace any provider they want with a bespoke subclass or comment out unneeded service providers for API or CLI-only applications. But again, as I said before, doing that requires explicit user action, so they are well aware of what is being done. A 3rd-party package should never try to replace an application-level service provider automatically.
Just don't extend the application level The issue is the double registration of an Register those events by using the <?php
namespace My\Package\Providers;
use My\Package\Events\MyEvent;
use My\Package\Listeners\MyListerner;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider
{
public function boot(Dispatcher $events): void
{
$events->listen([
MyEvent::class => MyListerner::class,
]);
}
}I am sorry if my wording seemed harsh. English is not my native language, and I truly didn't want to sound any snarky. I just wanted to help you in finding a solution and outlining how a package's service provider is expected to work. You are still yet to provide a reason for you to want to extend the But I hope the sample above can help you in some way. Have a nice day =) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Laravel Version
12.38
PHP Version
8.4
Database Driver & Version
—
Description
Extending the event service provider installs two listeners for the
Registeredevent, causing theSendEmailVerificationNotificationto be sent twice. Extending the provider is a long-standing recommendation for third-party packages to register package events, see e.g. LaravelPackage.A solution has previously been suggested in the discussion at #57390, namely to override the
configureEmailVerificationmethod with a blank function in the child provider:This works, but I'm convinced this is neither idiomatic to Laravel, an elegant solution, nor easy to discover.
As a package author, I expect a simple way to register events that doesn't cause duplicate emails being sent to end-users of applications using my packages; as a package consumer, I have little chance of preventing this, short of deep-diving into any packages I install and avoid using any packages using custom events.
Steps To Reproduce
Create a service provider extending the event service provider. For posterity, modify your
vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.phpto print the calling class in the booting callback, for example:$this->booted(function () { + \fwrite(\STDERR, "Configuring email verification listener in ".static::class."\n"); $this->configureEmailVerification(); });Then run the app, and you'll see:
Beta Was this translation helpful? Give feedback.
All reactions