Update NotificationFactoryLocator and CHANGELOG

This commit is contained in:
Younes ENNAJI
2026-03-02 03:23:45 +00:00
parent 98336b98bf
commit f399bc912d
3 changed files with 77 additions and 1 deletions
+1
View File
@@ -17,6 +17,7 @@
- [Laravel] Add LivewireListener classes for all adapters and themes to enable Livewire event handling
* fix [Flasher] Fix FilterCriteria uninitialized property error when constructed with empty array
* fix [Flasher] Fix null comparison issues in PriorityCriteria, HopsCriteria, and DelayCriteria that relied on PHP's implicit null-to-0 coercion
* fix [Flasher] Add type validation for callable factory return values in NotificationFactoryLocator with descriptive error messages
## [v2.1.3](https://github.com/php-flasher/php-flasher/compare/v2.1.2...v2.1.3) - 2025-01-25
@@ -15,6 +15,7 @@ final class NotificationFactoryLocator implements NotificationFactoryLocatorInte
/**
* @throws FactoryNotFoundException
* @throws \InvalidArgumentException
*/
public function get(string $id): NotificationFactoryInterface
{
@@ -24,7 +25,20 @@ final class NotificationFactoryLocator implements NotificationFactoryLocatorInte
$factory = $this->factories[$id];
return \is_callable($factory) ? $factory() : $factory;
if (\is_callable($factory)) {
$factory = $factory();
if (!$factory instanceof NotificationFactoryInterface) {
throw new \InvalidArgumentException(\sprintf(
'Factory callable for "%s" must return an instance of %s, %s returned.',
$id,
NotificationFactoryInterface::class,
\get_debug_type($factory)
));
}
}
return $factory;
}
public function has(string $id): bool
@@ -53,4 +53,65 @@ final class NotificationFactoryLocatorTest extends TestCase
$this->assertTrue($notificationFactoryLocator->has('alias'));
}
public function testGetWithCallableFactory(): void
{
$factoryMock = \Mockery::mock(NotificationFactoryInterface::class);
$notificationFactoryLocator = new NotificationFactoryLocator();
$notificationFactoryLocator->addFactory('alias', fn () => $factoryMock);
$retrievedFactory = $notificationFactoryLocator->get('alias');
$this->assertSame($factoryMock, $retrievedFactory);
}
public function testGetWithCallableFactoryCalledEachTime(): void
{
$callCount = 0;
$notificationFactoryLocator = new NotificationFactoryLocator();
$notificationFactoryLocator->addFactory('alias', function () use (&$callCount) {
++$callCount;
return \Mockery::mock(NotificationFactoryInterface::class);
});
$notificationFactoryLocator->get('alias');
$notificationFactoryLocator->get('alias');
$this->assertSame(2, $callCount);
}
public function testGetWithCallableReturningInvalidTypeThrowsException(): void
{
$notificationFactoryLocator = new NotificationFactoryLocator();
$notificationFactoryLocator->addFactory('invalid', fn () => 'not a factory');
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Factory callable for "invalid" must return an instance of');
$notificationFactoryLocator->get('invalid');
}
public function testGetWithCallableReturningNullThrowsException(): void
{
$notificationFactoryLocator = new NotificationFactoryLocator();
$notificationFactoryLocator->addFactory('null_factory', fn () => null);
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Factory callable for "null_factory" must return an instance of');
$notificationFactoryLocator->get('null_factory');
}
public function testAddFactoryOverwritesExistingFactory(): void
{
$factory1 = \Mockery::mock(NotificationFactoryInterface::class);
$factory2 = \Mockery::mock(NotificationFactoryInterface::class);
$notificationFactoryLocator = new NotificationFactoryLocator();
$notificationFactoryLocator->addFactory('alias', $factory1);
$notificationFactoryLocator->addFactory('alias', $factory2);
$this->assertSame($factory2, $notificationFactoryLocator->get('alias'));
}
}