diff --git a/CHANGELOG.md b/CHANGELOG.md index 3844358f..6490febf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,11 @@ * fix [Flasher] Simplify FlasherPlugin::normalizeFlashBag() by replacing redundant array union with direct array_merge * fix [Flasher] Standardize exception message format in PresetNotFoundException to use brackets like other exceptions * fix [Flasher] Standardize exception message wording in CriteriaNotRegisteredException to use "not found" instead of "is not found" +* feature [DX] Add `@method` annotations to FlasherInterface and NotificationFactoryInterface for better IDE autocompletion +* feature [DX] Add Type::all() and Type::isValid() helper methods with PHPStan type narrowing +* feature [DX] Add `@throws` annotations to FlasherContainer methods for better exception documentation +* feature [DX] Add FlasherContainer::setContainer() method as convenient alias for testing +* feature [DX] Add PHPStan type alias `NotificationType` for valid notification types ## [v2.1.3](https://github.com/php-flasher/php-flasher/compare/v2.1.2...v2.1.3) - 2025-01-25 diff --git a/src/Prime/Container/FlasherContainer.php b/src/Prime/Container/FlasherContainer.php index 63ef6464..c0bff0a7 100644 --- a/src/Prime/Container/FlasherContainer.php +++ b/src/Prime/Container/FlasherContainer.php @@ -9,6 +9,11 @@ use Flasher\Prime\FlasherInterface; use Psr\Container\ContainerInterface; /** + * Service container for PHPFlasher. + * + * Provides a static access point to the flasher services. + * Must be initialized with a PSR-11 container or a closure that returns one. + * * @internal */ final class FlasherContainer @@ -19,17 +24,56 @@ final class FlasherContainer { } + /** + * Initialize the container with a PSR-11 container or a lazy-loading closure. + * + * @param ContainerInterface|\Closure(): ContainerInterface $container + */ public static function from(ContainerInterface|\Closure $container): void { self::$instance ??= new self($container); } + /** + * Alias for from() - sets the container instance. + * + * @param FlasherInterface $flasher The flasher instance to use + */ + public static function setContainer(FlasherInterface $flasher): void + { + self::$instance = new self(new class($flasher) implements ContainerInterface { + public function __construct(private readonly FlasherInterface $flasher) + { + } + + public function get(string $id): FlasherInterface + { + return $this->flasher; + } + + public function has(string $id): bool + { + return 'flasher' === $id; + } + }); + } + + /** + * Reset the container instance. + */ public static function reset(): void { self::$instance = null; } /** + * Create or retrieve a flasher service from the container. + * + * @param string $id The service identifier (e.g., 'flasher', 'flasher.toastr') + * + * @throws \InvalidArgumentException If the service is not found or invalid + * @throws \LogicException If the container has not been initialized + * * @phpstan-return ($id is 'flasher' ? \Flasher\Prime\FlasherInterface : * ($id is 'flasher.noty' ? \Flasher\Noty\Prime\NotyInterface : * ($id is 'flasher.notyf' ? \Flasher\Notyf\Prime\NotyfInterface : @@ -52,11 +96,24 @@ final class FlasherContainer return $factory; } + /** + * Check if a service exists in the container. + * + * @param string $id The service identifier + * + * @throws \LogicException If the container has not been initialized + */ public static function has(string $id): bool { return self::getContainer()->has($id); } + /** + * Get the underlying PSR-11 container. + * + * @throws \LogicException If the container has not been initialized + * @throws \InvalidArgumentException If the container closure returns an invalid type + */ public static function getContainer(): ContainerInterface { $container = self::getInstance()->container; diff --git a/src/Prime/Factory/NotificationFactoryInterface.php b/src/Prime/Factory/NotificationFactoryInterface.php index cfd31c66..0a2d0e70 100644 --- a/src/Prime/Factory/NotificationFactoryInterface.php +++ b/src/Prime/Factory/NotificationFactoryInterface.php @@ -4,12 +4,46 @@ declare(strict_types=1); namespace Flasher\Prime\Factory; +use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\NotificationBuilderInterface; +use Flasher\Prime\Stamp\StampInterface; /** + * Interface for notification factories that create notification builders. + * * @mixin \Flasher\Prime\Notification\NotificationBuilderInterface + * + * @method $this title(string $title) Set the notification title + * @method $this message(string $message) Set the notification message + * @method $this type(string $type) Set the notification type (success, error, info, warning) + * @method $this options(array $options, bool $append = true) Set notification options + * @method $this option(string $name, mixed $value) Set a single notification option + * @method $this priority(int $priority) Set the notification priority + * @method $this hops(int $amount) Set the number of requests the notification should persist + * @method $this keep() Keep the notification for one more request + * @method $this delay(int $delay) Set the delay in milliseconds before showing the notification + * @method $this translate(array $parameters = [], ?string $locale = null) Mark the notification for translation + * @method $this handler(string $handler) Set the notification handler/adapter + * @method $this context(array $context) Set additional context data + * @method $this when(bool|\Closure $condition) Conditionally show the notification + * @method $this unless(bool|\Closure $condition) Conditionally hide the notification + * @method $this with(StampInterface[]|StampInterface $stamps) Add stamps to the notification + * @method Envelope getEnvelope() Get the notification envelope + * @method Envelope success(string $message, array $options = [], ?string $title = null) Create a success notification + * @method Envelope error(string $message, array $options = [], ?string $title = null) Create an error notification + * @method Envelope info(string $message, array $options = [], ?string $title = null) Create an info notification + * @method Envelope warning(string $message, array $options = [], ?string $title = null) Create a warning notification + * @method Envelope flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null) Create a flash notification + * @method Envelope push() Push the notification to storage + * @method Envelope created(string|object|null $resource = null) Create a "resource created" notification + * @method Envelope updated(string|object|null $resource = null) Create a "resource updated" notification + * @method Envelope saved(string|object|null $resource = null) Create a "resource saved" notification + * @method Envelope deleted(string|object|null $resource = null) Create a "resource deleted" notification */ interface NotificationFactoryInterface { + /** + * Create a new notification builder instance. + */ public function createNotificationBuilder(): NotificationBuilderInterface; } diff --git a/src/Prime/FlasherInterface.php b/src/Prime/FlasherInterface.php index a0e88d03..f20883ae 100644 --- a/src/Prime/FlasherInterface.php +++ b/src/Prime/FlasherInterface.php @@ -5,12 +5,43 @@ declare(strict_types=1); namespace Flasher\Prime; use Flasher\Prime\Factory\NotificationFactoryInterface; +use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Response\Presenter\ArrayPresenter; +use Flasher\Prime\Stamp\StampInterface; /** + * Main entry point for creating flash notifications. + * * @mixin \Flasher\Prime\Notification\NotificationBuilder * * @phpstan-import-type ArrayPresenterType from ArrayPresenter + * + * @method $this title(string $title) Set the notification title + * @method $this message(string $message) Set the notification message + * @method $this type(string $type) Set the notification type (success, error, info, warning) + * @method $this options(array $options, bool $append = true) Set notification options + * @method $this option(string $name, mixed $value) Set a single notification option + * @method $this priority(int $priority) Set the notification priority + * @method $this hops(int $amount) Set the number of requests the notification should persist + * @method $this keep() Keep the notification for one more request + * @method $this delay(int $delay) Set the delay in milliseconds before showing the notification + * @method $this translate(array $parameters = [], ?string $locale = null) Mark the notification for translation + * @method $this handler(string $handler) Set the notification handler/adapter + * @method $this context(array $context) Set additional context data + * @method $this when(bool|\Closure $condition) Conditionally show the notification + * @method $this unless(bool|\Closure $condition) Conditionally hide the notification + * @method $this with(StampInterface[]|StampInterface $stamps) Add stamps to the notification + * @method Envelope getEnvelope() Get the notification envelope + * @method Envelope success(string $message, array $options = [], ?string $title = null) Create a success notification + * @method Envelope error(string $message, array $options = [], ?string $title = null) Create an error notification + * @method Envelope info(string $message, array $options = [], ?string $title = null) Create an info notification + * @method Envelope warning(string $message, array $options = [], ?string $title = null) Create a warning notification + * @method Envelope flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null) Create a flash notification + * @method Envelope push() Push the notification to storage + * @method Envelope created(string|object|null $resource = null) Create a "resource created" notification + * @method Envelope updated(string|object|null $resource = null) Create a "resource updated" notification + * @method Envelope saved(string|object|null $resource = null) Create a "resource saved" notification + * @method Envelope deleted(string|object|null $resource = null) Create a "resource deleted" notification */ interface FlasherInterface { diff --git a/src/Prime/Notification/NotificationBuilderInterface.php b/src/Prime/Notification/NotificationBuilderInterface.php index f9625b1c..9865342e 100644 --- a/src/Prime/Notification/NotificationBuilderInterface.php +++ b/src/Prime/Notification/NotificationBuilderInterface.php @@ -6,12 +6,37 @@ namespace Flasher\Prime\Notification; use Flasher\Prime\Stamp\StampInterface; +/** + * Builder interface for creating flash notifications. + * + * Provides a fluent API for constructing notifications with various + * properties like title, message, type, and options. + * + * @phpstan-import-type NotificationType from Type + */ interface NotificationBuilderInterface { + /** + * Set the notification title. + * + * @param string $title The title to display + */ public function title(string $title): static; + /** + * Set the notification message. + * + * @param string $message The message content + */ public function message(string $message): static; + /** + * Set the notification type. + * + * @param string $type The notification type (success, error, info, warning) + * + * @phpstan-param NotificationType|string $type + */ public function type(string $type): static; /** diff --git a/src/Prime/Notification/Type.php b/src/Prime/Notification/Type.php index 75c69db7..fda86013 100644 --- a/src/Prime/Notification/Type.php +++ b/src/Prime/Notification/Type.php @@ -4,10 +4,42 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; +/** + * Notification type constants. + * + * @phpstan-type NotificationType 'success'|'error'|'info'|'warning' + */ final class Type { + /** @var 'success' */ public const SUCCESS = 'success'; + + /** @var 'error' */ public const ERROR = 'error'; + + /** @var 'info' */ public const INFO = 'info'; + + /** @var 'warning' */ public const WARNING = 'warning'; + + /** + * Get all available notification types. + * + * @return list + */ + public static function all(): array + { + return [self::SUCCESS, self::ERROR, self::INFO, self::WARNING]; + } + + /** + * Check if a given type is valid. + * + * @phpstan-assert-if-true NotificationType $type + */ + public static function isValid(string $type): bool + { + return \in_array($type, self::all(), true); + } } diff --git a/tests/Prime/Notification/TypeTest.php b/tests/Prime/Notification/TypeTest.php new file mode 100644 index 00000000..50a0b82d --- /dev/null +++ b/tests/Prime/Notification/TypeTest.php @@ -0,0 +1,41 @@ +assertSame('success', Type::SUCCESS); + $this->assertSame('error', Type::ERROR); + $this->assertSame('info', Type::INFO); + $this->assertSame('warning', Type::WARNING); + } + + public function testAll(): void + { + $expected = ['success', 'error', 'info', 'warning']; + $this->assertSame($expected, Type::all()); + } + + public function testIsValidWithValidTypes(): void + { + $this->assertTrue(Type::isValid('success')); + $this->assertTrue(Type::isValid('error')); + $this->assertTrue(Type::isValid('info')); + $this->assertTrue(Type::isValid('warning')); + } + + public function testIsValidWithInvalidTypes(): void + { + $this->assertFalse(Type::isValid('invalid')); + $this->assertFalse(Type::isValid('')); + $this->assertFalse(Type::isValid('SUCCESS')); + $this->assertFalse(Type::isValid('notice')); + } +}