diff --git a/src/Websocket/Handler.php b/src/Websocket/Handler.php index abd4ea7..aea1775 100644 --- a/src/Websocket/Handler.php +++ b/src/Websocket/Handler.php @@ -521,14 +521,10 @@ class Handler implements MessageComponentInterface // Validator refresh failed, continue anyway } - // 9. Clear event dispatcher cache (for fresh event/listener bindings) - try { - if ($container->bound('events')) { - $container->forgetInstance('events'); - } - } catch (\Throwable $e) { - // Events refresh failed, continue anyway - } + // 9. The event dispatcher is deliberately KEPT: listeners are registered + // once by the service providers (Event::listen in boot()), which never + // run again in this child. A forgotten instance would be rebuilt empty + // and every event fired from the controller would reach no listener. // 10. Clear WebSocket ControllerResolver cache for fresh controller loading ControllerResolver::clearCache(); diff --git a/tests/HotReloadKeepsListenersTest.php b/tests/HotReloadKeepsListenersTest.php new file mode 100644 index 0000000..f731bf5 --- /dev/null +++ b/tests/HotReloadKeepsListenersTest.php @@ -0,0 +1,47 @@ +setHotReload(null); + + parent::tearDown(); + } + + private function setHotReload(?bool $on): void + { + $flag = new \ReflectionProperty(Handler::class, 'hotReload'); + $flag->setAccessible(true); + $flag->setValue(null, $on); + } + + public function test_listeners_registered_at_boot_survive_the_hot_reload() + { + $heard = 0; + Event::listen('shop.order-paid', function () use (&$heard) { + $heard++; + }); + + $this->setHotReload(true); + $reload = new \ReflectionMethod($this->wsHandler, 'hotReloadChild'); + $reload->setAccessible(true); + $reload->invoke($this->wsHandler); + + event('shop.order-paid'); + + $this->assertSame(1, $heard); + } +}