spatie / laravel-ray

Debug with Ray to fix problems faster in Laravel apps
https://myray.app
MIT License
291 stars 64 forks source link

Ray always listens to (wildcard) Events and this cannot be disabled #203

Closed niektenhoopen closed 3 years ago

niektenhoopen commented 3 years ago

Describe the bug In my applications I test if an Event has (array of) Listeners listening to it. Because Ray uses Event::listen('*', ... in the EventWatcher, it is also considered a Listener and therefore my tests fail.

I now have to remove Ray from my project before running my tests, which makes it practically unusable in this project.

Versions 1.20.1

PHP version: 7.4 Laravel version: 8.x

Additional information: My assertion:

    public function assertListenersAttachedToEvent(string $eventClassName, array $expectedListeners): void
    {
        $listeners = collect(app(Dispatcher::class)->getListeners($eventClassName))
            ->map(function (\Closure $listenerClosure): string {
                $reflection = new ReflectionFunction($listenerClosure);

                return $reflection->getStaticVariables()['listener'];
            })
            ->toArray();

        $this->assertEqualsCanonicalizing($expectedListeners, $listeners, sprintf('Event %s does not have (all) expected listeners attached to it', $eventClassName));
    }

And I use this in my tests:

        $this->assertListenersAttachedToEvent(RandomEvent::class, [
            RandomListener::class,
        ]);

Solution Is it possible to check if Ray is enabled before registering the wildcard Event listener? Now it's "disabled" but still actually still active in the background. That is not really "disabled" in my opinion.

freekmurze commented 3 years ago

Could you try adding Event::forget('*') before running your test?

We won't add this to the Ray class itself as some people might have an actual * listener that we shouldn't forget.

niektenhoopen commented 3 years ago

Yes, that works! It does feel a bit dirty but it works.

Personally I was thinking about checking if Ray is enabled before booting the EventWatcher but that probably messes up the production application of people leaving calls to ray() in production? Or am I mistaken?

I could probably also swap out the EventWatcher:

class OverwriteRayServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        $settings = app(Settings::class);

        if ($settings->enable !== true) {
            $this->app->singleton(EventWatcher::class, function ($app) {
                return new EmptyEventWatcher();
            });
        }
    }
}
use Spatie\LaravelRay\Watchers\Watcher;

class EmptyEventWatcher extends Watcher
{
    public function register(): void
    {
        // Do nothing
    }
}
freekmurze commented 3 years ago

Closing this for now, as there are a couple of ways to avoid the problem without having to change Laravel-ray's code.