From 979b9371fa41535e9c6b0facd16389f63e5ab193 Mon Sep 17 00:00:00 2001 From: techmahedy Date: Fri, 30 Oct 2026 20:56:56 +0600 Subject: [PATCH] Introduce a signal bus contract --- src/Phaseolies/Database/Database.php | 6 +- src/Phaseolies/Helpers/helpers.php | 2 +- src/Phaseolies/Launchers/FacadeLauncher.php | 7 +- .../Signals/Contracts/SignalBusInterface.php | 75 +++++++++++++++++++ src/Phaseolies/Signals/SignalBus.php | 3 +- src/Phaseolies/Support/Facades/Signal.php | 8 +- tests/Signals/SignalBusTest.php | 33 ++++++++ 7 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 src/Phaseolies/Signals/Contracts/SignalBusInterface.php diff --git a/src/Phaseolies/Database/Database.php b/src/Phaseolies/Database/Database.php index 7db7417a..43b01c16 100644 --- a/src/Phaseolies/Database/Database.php +++ b/src/Phaseolies/Database/Database.php @@ -37,7 +37,7 @@ class Database /** * Signals waiting for a transaction to settle, per connection and nesting level * - * @var array>> + * @var array>> */ protected static $pendingSignals = []; @@ -245,12 +245,12 @@ public function rollBack(): void /** * Hold a signal until the open transaction on a connection settles * - * @param \Phaseolies\Signals\SignalBus $bus + * @param \Phaseolies\Signals\Contracts\SignalBusInterface $bus * @param object $signal * @param string|null $connection * @return bool */ - public static function stageSignal(\Phaseolies\Signals\SignalBus $bus, object $signal, ?string $connection = null): bool + public static function stageSignal(\Phaseolies\Signals\Contracts\SignalBusInterface $bus, object $signal, ?string $connection = null): bool { $connection = $connection ?: config('database.default'); $level = static::$transactions[$connection] ?? 0; diff --git a/src/Phaseolies/Helpers/helpers.php b/src/Phaseolies/Helpers/helpers.php index 0014b577..4251dd46 100644 --- a/src/Phaseolies/Helpers/helpers.php +++ b/src/Phaseolies/Helpers/helpers.php @@ -96,7 +96,7 @@ function defer(callable $callback) * Get the signal bus, or emit a signal when one is given * * @param object|null $signal - * @return \Phaseolies\Signals\SignalBus|void + * @return \Phaseolies\Signals\Contracts\SignalBusInterface|void */ function signal(?object $signal = null) { diff --git a/src/Phaseolies/Launchers/FacadeLauncher.php b/src/Phaseolies/Launchers/FacadeLauncher.php index 4a562367..29fc90ba 100644 --- a/src/Phaseolies/Launchers/FacadeLauncher.php +++ b/src/Phaseolies/Launchers/FacadeLauncher.php @@ -11,6 +11,7 @@ use Phaseolies\Support\Storage\StorageFileService; use Phaseolies\Support\Storage\Contracts\StorageInterface; use Phaseolies\Signals\SignalBus; +use Phaseolies\Signals\Contracts\SignalBusInterface; use Phaseolies\Support\Session; use Phaseolies\Session\Contracts\SessionInterface; use Phaseolies\Support\Mail\MailService; @@ -131,9 +132,11 @@ public function register() // This handles db related jobs $this->app->singleton('db', Database::class); - // Bind the 'signal' service to a singleton instance of the SignalBus class. + // Bind the signal bus contract to a singleton implementation and + // retain the existing 'signal' service name as an alias. // This handles signals that can wait for a database transaction to settle - $this->app->singleton('signal', SignalBus::class); + $this->app->singleton(SignalBusInterface::class, SignalBus::class); + $this->app->alias(SignalBusInterface::class, 'signal'); // Bind the 'app' service to a singleton instance of the Application class. // This handles app related jobs diff --git a/src/Phaseolies/Signals/Contracts/SignalBusInterface.php b/src/Phaseolies/Signals/Contracts/SignalBusInterface.php new file mode 100644 index 00000000..991adf77 --- /dev/null +++ b/src/Phaseolies/Signals/Contracts/SignalBusInterface.php @@ -0,0 +1,75 @@ +assertInstanceOf(SignalBusInterface::class, $this->bus); + } + + public function testApplicationBindsTheSignalContractAndLegacyServiceNameToTheSameSingleton(): void + { + $app = $this->getMockBuilder(Application::class)->disableOriginalConstructor()->onlyMethods([])->getMock(); + (new ReflectionClass(Container::class))->getConstructor()->invoke($app); + $previous = Container::getInstance(); + Container::setInstance($app); + $originalAppUrl = getenv('APP_URL'); + + try { + putenv('APP_URL=http://localhost'); + (new FacadeLauncher($app))->register(); + + $contract = $app->get(SignalBusInterface::class); + + $this->assertInstanceOf(SignalBus::class, $contract); + $this->assertSame($contract, $app->get('signal')); + $this->assertSame($contract, signal()); + } finally { + Container::setInstance($previous); + $originalAppUrl === false ? putenv('APP_URL') : putenv("APP_URL={$originalAppUrl}"); + } + } + public function testHigherPriorityListenersRunFirst(): void { $order = [];