From 0631e5953ee38d6f76324bafca93dd88d0bf7f71 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sun, 9 Mar 2025 00:28:06 +0000 Subject: [PATCH] docs: enhance code documentation with comprehensive PHPDoc comments --- .github/FUNDING.yml | 1 - .shared/.github/FUNDING.yml | 1 - composer.json | 1 - composer.lock | 141 ++++---- phpstan.neon.dist | 7 +- rector.php | 3 - src/Laravel/.github/FUNDING.yml | 1 - src/Laravel/Command/InstallCommand.php | 54 +++- src/Laravel/Component/FlasherComponent.php | 27 ++ .../EventListener/LivewireListener.php | 57 +++- src/Laravel/EventListener/OctaneListener.php | 18 ++ src/Laravel/Facade/Flasher.php | 28 ++ src/Laravel/FlasherServiceProvider.php | 127 ++++++++ src/Laravel/Http/Request.php | 22 ++ src/Laravel/Http/Response.php | 23 ++ src/Laravel/Middleware/FlasherMiddleware.php | 28 ++ src/Laravel/Middleware/SessionMiddleware.php | 28 ++ .../stubs/ApplicationTestingHooks.stub | 10 + src/Laravel/Phpstan/stubs/Repository.stub | 18 +- src/Laravel/Resources/config.php | 9 + src/Laravel/Storage/SessionBag.php | 32 ++ src/Laravel/Support/PluginServiceProvider.php | 81 +++++ src/Laravel/Template/BladeTemplateEngine.php | 26 ++ src/Laravel/Translation/Translator.php | 45 ++- src/Laravel/Translation/lang/ar/messages.php | 12 + src/Laravel/Translation/lang/de/messages.php | 12 + src/Laravel/Translation/lang/en/messages.php | 12 + src/Laravel/Translation/lang/es/messages.php | 12 + src/Laravel/Translation/lang/fr/messages.php | 12 + src/Laravel/Translation/lang/pt/messages.php | 12 + src/Laravel/Translation/lang/ru/messages.php | 12 + src/Laravel/Translation/lang/zh/messages.php | 12 + src/Noty/Laravel/.github/FUNDING.yml | 1 - src/Noty/Laravel/Facade/Noty.php | 28 ++ .../Laravel/FlasherNotyServiceProvider.php | 16 + src/Noty/Prime/.github/FUNDING.yml | 1 - src/Noty/Prime/.phpstorm.meta.php | 21 ++ src/Noty/Prime/Noty.php | 18 ++ src/Noty/Prime/NotyBuilder.php | 220 +++++++++++-- src/Noty/Prime/NotyInterface.php | 11 + src/Noty/Prime/NotyPlugin.php | 39 ++- src/Noty/Prime/functions.php | 9 + src/Noty/Prime/helpers.php | 9 + src/Noty/Symfony/.github/FUNDING.yml | 1 - src/Noty/Symfony/FlasherNotySymfonyBundle.php | 16 + src/Notyf/Laravel/.github/FUNDING.yml | 1 - src/Notyf/Laravel/Facade/Notyf.php | 28 ++ .../Laravel/FlasherNotyfServiceProvider.php | 16 + src/Notyf/Prime/.github/FUNDING.yml | 1 - src/Notyf/Prime/.phpstorm.meta.php | 22 +- src/Notyf/Prime/Notyf.php | 18 ++ src/Notyf/Prime/NotyfBuilder.php | 47 ++- src/Notyf/Prime/NotyfInterface.php | 11 + src/Notyf/Prime/NotyfPlugin.php | 41 ++- src/Notyf/Prime/functions.php | 9 + src/Notyf/Prime/helpers.php | 9 + src/Notyf/Symfony/.github/FUNDING.yml | 1 - .../Symfony/FlasherNotyfSymfonyBundle.php | 16 + src/Prime/.github/FUNDING.yml | 1 - src/Prime/.phpstorm.meta.php | 27 ++ src/Prime/Asset/AssetManager.php | 59 +++- src/Prime/Asset/AssetManagerInterface.php | 33 +- src/Prime/Configuration.php | 58 ++-- src/Prime/Container/FlasherContainer.php | 69 +++- .../EventDispatcher/Event/FilterEvent.php | 41 ++- .../Event/NotificationEvents.php | 31 +- .../EventDispatcher/Event/PersistEvent.php | 20 +- .../Event/PostPersistEvent.php | 14 +- .../EventDispatcher/Event/PostRemoveEvent.php | 21 +- .../EventDispatcher/Event/PostUpdateEvent.php | 14 +- .../Event/PresentationEvent.php | 24 +- .../EventDispatcher/Event/RemoveEvent.php | 30 +- .../EventDispatcher/Event/ResponseEvent.php | 31 ++ .../Event/StoppableEventInterface.php | 18 ++ .../EventDispatcher/Event/UpdateEvent.php | 20 +- src/Prime/EventDispatcher/EventDispatcher.php | 41 ++- .../EventDispatcherInterface.php | 38 ++- .../EventListener/AddToStorageListener.php | 51 +++ .../EventListener/ApplyPresetListener.php | 40 ++- .../AttachDefaultStampsListener.php | 32 +- .../EventListener/EnvelopeRemovalListener.php | 30 +- .../EventListener/EventListenerInterface.php | 35 +- .../NotificationLoggerListener.php | 52 ++- .../EventListener/TranslationListener.php | 54 +++- .../CriteriaNotRegisteredException.php | 16 +- .../Exception/FactoryNotFoundException.php | 16 +- .../Exception/PresenterNotFoundException.php | 16 +- .../Exception/PresetNotFoundException.php | 18 +- src/Prime/Factory/FlasherFactory.php | 17 + src/Prime/Factory/FlasherFactoryInterface.php | 7 + src/Prime/Factory/NotificationFactory.php | 41 ++- .../Factory/NotificationFactoryInterface.php | 17 + .../Factory/NotificationFactoryLocator.php | 48 +++ .../NotificationFactoryLocatorInterface.php | 26 ++ src/Prime/Flasher.php | 59 +++- src/Prime/FlasherInterface.php | 62 +++- .../Http/Csp/ContentSecurityPolicyHandler.php | 108 ++++++- .../ContentSecurityPolicyHandlerInterface.php | 27 +- src/Prime/Http/Csp/NonceGenerator.php | 16 + .../Http/Csp/NonceGeneratorInterface.php | 17 + src/Prime/Http/RequestExtension.php | 41 ++- src/Prime/Http/RequestExtensionInterface.php | 23 ++ src/Prime/Http/RequestInterface.php | 67 +++- src/Prime/Http/ResponseExtension.php | 53 ++- src/Prime/Http/ResponseExtensionInterface.php | 19 +- src/Prime/Http/ResponseInterface.php | 69 +++- src/Prime/Notification/Envelope.php | 127 +++++++- src/Prime/Notification/FlasherBuilder.php | 98 +++++- src/Prime/Notification/Notification.php | 56 +++- .../Notification/NotificationBuilder.php | 21 ++ .../NotificationBuilderInterface.php | 295 ++++++++++++++++- .../NotificationBuilderMethods.php | 154 +++++++++ .../Notification/NotificationInterface.php | 72 ++++- .../NotificationMethodAliases.php | 98 ++++++ .../NotificationStorageMethods.php | 166 ++++++++-- src/Prime/Notification/Type.php | 28 ++ src/Prime/Plugin/FlasherPlugin.php | 75 ++++- src/Prime/Plugin/Plugin.php | 61 ++++ src/Prime/Plugin/PluginInterface.php | 92 +++++- .../Response/Presenter/ArrayPresenter.php | 20 ++ .../Response/Presenter/HtmlPresenter.php | 44 ++- .../Response/Presenter/PresenterInterface.php | 21 ++ .../Response/Resource/ResourceManager.php | 44 ++- .../Resource/ResourceManagerInterface.php | 20 ++ src/Prime/Response/Response.php | 91 +++++- src/Prime/Response/ResponseManager.php | 66 +++- .../Response/ResponseManagerInterface.php | 34 +- src/Prime/Stamp/ContextStamp.php | 25 +- src/Prime/Stamp/CreatedAtStamp.php | 46 ++- src/Prime/Stamp/DelayStamp.php | 19 ++ src/Prime/Stamp/HopsStamp.php | 21 ++ src/Prime/Stamp/HtmlStamp.php | 28 ++ src/Prime/Stamp/IdStamp.php | 37 ++- src/Prime/Stamp/OrderableStampInterface.php | 23 ++ src/Prime/Stamp/PluginStamp.php | 27 +- src/Prime/Stamp/PresentableStampInterface.php | 18 +- src/Prime/Stamp/PresenterStamp.php | 24 ++ src/Prime/Stamp/PresetStamp.php | 26 +- src/Prime/Stamp/PriorityStamp.php | 40 ++- src/Prime/Stamp/StampInterface.php | 13 + src/Prime/Stamp/TranslationStamp.php | 25 +- src/Prime/Stamp/UnlessStamp.php | 19 ++ src/Prime/Stamp/WhenStamp.php | 19 ++ src/Prime/Storage/Bag/ArrayBag.php | 23 ++ src/Prime/Storage/Bag/BagInterface.php | 20 +- src/Prime/Storage/Bag/StaticBag.php | 25 ++ .../Filter/Criteria/CriteriaInterface.php | 19 +- .../Storage/Filter/Criteria/DelayCriteria.php | 38 +++ .../Filter/Criteria/FilterCriteria.php | 29 +- .../Storage/Filter/Criteria/HopsCriteria.php | 38 +++ .../Storage/Filter/Criteria/LimitCriteria.php | 28 ++ .../Filter/Criteria/OrderByCriteria.php | 36 +++ .../Filter/Criteria/PresenterCriteria.php | 29 ++ .../Filter/Criteria/PriorityCriteria.php | 38 +++ .../Filter/Criteria/RangeExtractor.php | 21 +- .../Filter/Criteria/StampsCriteria.php | 41 +++ src/Prime/Storage/Filter/Filter.php | 29 ++ src/Prime/Storage/Filter/FilterFactory.php | 53 ++- .../Storage/Filter/FilterFactoryInterface.php | 24 +- src/Prime/Storage/Filter/FilterInterface.php | 26 +- src/Prime/Storage/Storage.php | 49 +++ src/Prime/Storage/StorageInterface.php | 35 +- src/Prime/Storage/StorageManager.php | 63 +++- src/Prime/Storage/StorageManagerInterface.php | 41 ++- src/Prime/Template/PHPTemplateEngine.php | 27 ++ .../Template/TemplateEngineInterface.php | 23 +- src/Prime/Test/Constraint/Notification.php | 55 +++- .../Test/Constraint/NotificationCount.php | 35 +- .../Test/Constraint/NotificationMessage.php | 40 ++- .../Test/Constraint/NotificationOption.php | 49 ++- .../Test/Constraint/NotificationOptions.php | 40 ++- .../Test/Constraint/NotificationTitle.php | 39 ++- .../Test/Constraint/NotificationType.php | 39 ++- src/Prime/Test/FlasherAssert.php | 234 +++++++------- src/Prime/Translation/EchoTranslator.php | 20 +- src/Prime/Translation/Language.php | 39 ++- src/Prime/Translation/Language/Arabic.php | 22 +- src/Prime/Translation/Language/Chinese.php | 19 +- src/Prime/Translation/Language/English.php | 18 +- src/Prime/Translation/Language/French.php | 19 +- src/Prime/Translation/Language/German.php | 22 +- src/Prime/Translation/Language/Portuguese.php | 22 +- src/Prime/Translation/Language/Russian.php | 22 +- src/Prime/Translation/Language/Spanish.php | 22 +- src/Prime/Translation/Messages.php | 23 +- src/Prime/Translation/TranslatorInterface.php | 26 +- src/Prime/functions.php | 37 ++- src/Prime/helpers.php | 41 ++- src/SweetAlert/Laravel/.github/FUNDING.yml | 1 - src/SweetAlert/Laravel/Facade/SweetAlert.php | 34 ++ .../FlasherSweetAlertServiceProvider.php | 30 ++ src/SweetAlert/Laravel/LivewireListener.php | 30 ++ src/SweetAlert/Prime/.github/FUNDING.yml | 1 - src/SweetAlert/Prime/.phpstorm.meta.php | 22 +- src/SweetAlert/Prime/SweetAlert.php | 18 ++ src/SweetAlert/Prime/SweetAlertBuilder.php | 165 ++++++++-- src/SweetAlert/Prime/SweetAlertInterface.php | 11 + src/SweetAlert/Prime/SweetAlertPlugin.php | 43 ++- src/SweetAlert/Prime/functions.php | 21 +- src/SweetAlert/Prime/helpers.php | 26 +- src/SweetAlert/Symfony/.github/FUNDING.yml | 1 - .../FlasherSweetAlertSymfonyBundle.php | 16 + src/Symfony/.github/FUNDING.yml | 1 - src/Symfony/Attribute/AsFlasherFactory.php | 25 ++ src/Symfony/Attribute/AsFlasherPresenter.php | 26 ++ src/Symfony/Command/InstallCommand.php | 97 +++++- src/Symfony/Component/FlasherComponent.php | 34 +- .../Compiler/EventListenerCompilerPass.php | 17 + .../Compiler/PresenterCompilerPass.php | 17 + src/Symfony/EventListener/FlasherListener.php | 33 ++ src/Symfony/EventListener/SessionListener.php | 32 ++ .../Factory/NotificationFactoryLocator.php | 33 +- src/Symfony/FlasherSymfonyBundle.php | 32 ++ src/Symfony/Http/Request.php | 22 ++ src/Symfony/Http/Response.php | 17 + src/Symfony/Profiler/FlasherDataCollector.php | 53 +++ src/Symfony/Storage/FallbackSession.php | 17 +- .../Storage/FallbackSessionInterface.php | 11 +- src/Symfony/Storage/SessionBag.php | 47 +++ src/Symfony/Support/PluginBundle.php | 56 +++- src/Symfony/Support/PluginBundleInterface.php | 24 ++ src/Symfony/Template/TwigTemplateEngine.php | 26 ++ src/Symfony/Translation/Translator.php | 38 +++ src/Symfony/Twig/FlasherTwigExtension.php | 24 ++ src/Toastr/Laravel/.github/FUNDING.yml | 1 - src/Toastr/Laravel/Facade/Toastr.php | 30 ++ .../Laravel/FlasherToastrServiceProvider.php | 20 ++ src/Toastr/Prime/.github/FUNDING.yml | 1 - src/Toastr/Prime/.phpstorm.meta.php | 22 ++ src/Toastr/Prime/Toastr.php | 18 ++ src/Toastr/Prime/ToastrBuilder.php | 301 ++++++++++++++++-- src/Toastr/Prime/ToastrInterface.php | 11 + src/Toastr/Prime/ToastrPlugin.php | 50 ++- src/Toastr/Prime/functions.php | 9 + src/Toastr/Prime/helpers.php | 21 +- src/Toastr/Symfony/.github/FUNDING.yml | 1 - .../Symfony/FlasherToastrSymfonyBundle.php | 20 ++ 237 files changed, 7732 insertions(+), 766 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/.shared/.github/FUNDING.yml b/.shared/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/.shared/.github/FUNDING.yml +++ b/.shared/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/composer.json b/composer.json index 58774d9c..e9612c1e 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,6 @@ "phpunit/phpunit": "^10.5.26", "rector/rector": "^1.2.8", "rector/swiss-knife": "^1.0.0", - "spatie/ray": "^1.41.2", "symplify/monorepo-builder": "^11.2.22" }, "autoload": { diff --git a/composer.lock b/composer.lock index e8f2daee..055b338f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9847ba2abb14c12d2e0b0d55e8251f79", + "content-hash": "d5de08e384654736feea63f1b01ffba1", "packages": [ { "name": "brick/math", @@ -3162,31 +3162,31 @@ }, { "name": "symfony/cache", - "version": "v7.0.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "5e2376f726d61541b9617512e374808d12c1dc22" + "reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/5e2376f726d61541b9617512e374808d12c1dc22", - "reference": "5e2376f726d61541b9617512e374808d12c1dc22", + "url": "https://api.github.com/repos/symfony/cache/zipball/ac2d25f97b17eec6e19760b6b9962a4f7c44356a", + "reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.3.6|^7.0" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/dependency-injection": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/var-dumper": "<6.4" + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" }, "provide": { "psr/cache-implementation": "2.0|3.0", @@ -3195,15 +3195,15 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3238,7 +3238,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.0.0" + "source": "https://github.com/symfony/cache/tree/v6.4.0" }, "funding": [ { @@ -3254,7 +3254,7 @@ "type": "tidelift" } ], - "time": "2023-11-25T15:40:35+00:00" + "time": "2023-11-24T19:28:07+00:00" }, { "name": "symfony/cache-contracts", @@ -3857,24 +3857,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.0.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e" + "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e", - "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d76d2632cfc2206eecb5ad2b26cd5934082941b6", + "reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/dependency-injection": "<5.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -3883,13 +3883,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3917,7 +3917,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.0" }, "funding": [ { @@ -3933,7 +3933,7 @@ "type": "tidelift" } ], - "time": "2023-07-27T16:29:09+00:00" + "time": "2023-07-27T06:52:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6454,16 +6454,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.0.3", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a7a061abbf6fe3d4a79032cbc5149a4d65a10234" + "reference": "cf0220fc7607476fd0d001ab3ed9e830d1fdda56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a7a061abbf6fe3d4a79032cbc5149a4d65a10234", - "reference": "a7a061abbf6fe3d4a79032cbc5149a4d65a10234", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cf0220fc7607476fd0d001ab3ed9e830d1fdda56", + "reference": "cf0220fc7607476fd0d001ab3ed9e830d1fdda56", "shasum": "" }, "require": { @@ -6517,7 +6517,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.0.0" }, "funding": [ { @@ -6533,7 +6533,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2023-11-27T12:39:18+00:00" }, { "name": "symfony/var-exporter", @@ -8028,16 +8028,16 @@ }, { "name": "overtrue/phplint", - "version": "9.5.3", + "version": "9.5.6", "source": { "type": "git", "url": "https://github.com/overtrue/phplint.git", - "reference": "584063a687abca041551e0b14c47fe9fc574b193" + "reference": "3fdc395a816d1401091062c7a3bfb31252aed6c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/overtrue/phplint/zipball/584063a687abca041551e0b14c47fe9fc574b193", - "reference": "584063a687abca041551e0b14c47fe9fc574b193", + "url": "https://api.github.com/repos/overtrue/phplint/zipball/3fdc395a816d1401091062c7a3bfb31252aed6c5", + "reference": "3fdc395a816d1401091062c7a3bfb31252aed6c5", "shasum": "" }, "require": { @@ -8045,14 +8045,14 @@ "ext-dom": "*", "ext-json": "*", "ext-mbstring": "*", - "php": "^8.2", - "symfony/cache": "^7.0", - "symfony/console": "^7.0", - "symfony/event-dispatcher": "^7.0", - "symfony/finder": "^7.0", - "symfony/options-resolver": "^7.0", - "symfony/process": "^7.0", - "symfony/yaml": "^7.0" + "php": "^8.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/event-dispatcher": "^6.4 || ^7.0", + "symfony/finder": "^6.4 || ^7.0", + "symfony/options-resolver": "^6.4 || ^7.0", + "symfony/process": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4", @@ -8109,7 +8109,7 @@ ], "support": { "issues": "https://github.com/overtrue/phplint/issues", - "source": "https://github.com/overtrue/phplint/tree/9.5.3" + "source": "https://github.com/overtrue/phplint/tree/9.5.6" }, "funding": [ { @@ -8117,7 +8117,7 @@ "type": "github" } ], - "time": "2024-10-11T07:13:25+00:00" + "time": "2025-01-15T05:41:15+00:00" }, { "name": "phar-io/manifest", @@ -10313,16 +10313,16 @@ }, { "name": "spatie/ray", - "version": "1.41.2", + "version": "1.41.1", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc" + "reference": "051a0facb1d2462fafef87ff77eb74d6f2d12944" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/c44f8cfbf82c69909b505de61d8d3f2d324e93fc", - "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc", + "url": "https://api.github.com/repos/spatie/ray/zipball/051a0facb1d2462fafef87ff77eb74d6f2d12944", + "reference": "051a0facb1d2462fafef87ff77eb74d6f2d12944", "shasum": "" }, "require": { @@ -10333,7 +10333,7 @@ "spatie/backtrace": "^1.1", "spatie/macroable": "^1.0|^2.0", "symfony/stopwatch": "^4.0|^5.1|^6.0|^7.0", - "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3" + "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0" }, "require-dev": { "illuminate/support": "6.x|^8.18|^9.0", @@ -10349,11 +10349,6 @@ "bin/remove-ray.sh" ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, "autoload": { "files": [ "src/helpers.php" @@ -10382,7 +10377,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.41.2" + "source": "https://github.com/spatie/ray/tree/1.41.1" }, "funding": [ { @@ -10394,24 +10389,24 @@ "type": "other" } ], - "time": "2024-04-24T14:21:46+00:00" + "time": "2024-01-25T10:15:50+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.0.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f" + "reference": "22301f0e7fdeaacc14318928612dee79be99860e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", - "reference": "700ff4096e346f54cb628ea650767c8130f1001f", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22301f0e7fdeaacc14318928612dee79be99860e", + "reference": "22301f0e7fdeaacc14318928612dee79be99860e", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -10445,7 +10440,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.0" }, "funding": [ { @@ -10461,7 +10456,7 @@ "type": "tidelift" } ], - "time": "2023-08-08T10:20:21+00:00" + "time": "2023-08-08T10:16:24+00:00" }, { "name": "symfony/polyfill-iconv", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 2f52d410..b1a25cf5 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,16 +8,15 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon -rules: - - Spatie\Ray\PHPStan\RemainingRayCallRule - parameters: - level: 9 + level: max paths: - src/ - tests/ + editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' + tmpDir: .cache/phpstan ignoreErrors: diff --git a/rector.php b/rector.php index e9158530..9c1e8e32 100644 --- a/rector.php +++ b/rector.php @@ -14,7 +14,4 @@ return Rector\Config\RectorConfig::configure() Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_110, Rector\PHPUnit\Set\PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES, Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_CODE_QUALITY, - ]) - ->withRules([ - Spatie\Ray\Rector\RemoveRayCallRector::class, ]); diff --git a/src/Laravel/.github/FUNDING.yml b/src/Laravel/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Laravel/.github/FUNDING.yml +++ b/src/Laravel/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Laravel/Command/InstallCommand.php b/src/Laravel/Command/InstallCommand.php index f8657526..92e48e80 100644 --- a/src/Laravel/Command/InstallCommand.php +++ b/src/Laravel/Command/InstallCommand.php @@ -15,15 +15,41 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Finder\Finder; +/** + * InstallCommand - Artisan command for installing PHPFlasher resources. + * + * This command provides a CLI interface for installing PHPFlasher resources + * including assets (JS and CSS files) and configuration files. It discovers + * all registered PHPFlasher plugins and installs their resources. + * + * Design patterns: + * - Command: Implements the command pattern for Artisan CLI integration + * - Discovery: Automatically discovers and processes registered plugins + */ final class InstallCommand extends Command { + /** + * Command description. + * + * @var string + */ protected $description = 'Installs all PHPFlasher resources to the public and config directories.'; + /** + * Creates a new InstallCommand instance. + * + * @param AssetManagerInterface $assetManager Manager for handling PHPFlasher assets + */ public function __construct(private readonly AssetManagerInterface $assetManager) { parent::__construct(); } + /** + * Configure the command. + * + * Sets the command name, description, help text, and options. + */ protected function configure(): void { $this @@ -34,6 +60,20 @@ final class InstallCommand extends Command ->addOption('symlink', 's', InputOption::VALUE_NONE, 'Symlink PHPFlasher assets instead of copying them.'); } + /** + * Execute the command. + * + * Installs PHPFlasher resources by: + * 1. Displaying a fancy banner + * 2. Processing each registered plugin + * 3. Publishing assets and config files + * 4. Creating a manifest file + * + * @param InputInterface $input Command input + * @param OutputInterface $output Command output + * + * @return int Command exit code (0 for success, non-zero for failure) + */ protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln(''); @@ -122,7 +162,13 @@ final class InstallCommand extends Command } /** - * @return string[] + * Publish assets from a plugin to the public directory. + * + * @param PluginInterface $plugin The plugin to publish assets from + * @param string $publicDir The target public directory + * @param bool $useSymlinks Whether to symlink or copy assets + * + * @return string[] Array of published file paths */ private function publishAssets(PluginInterface $plugin, string $publicDir, bool $useSymlinks): array { @@ -156,6 +202,12 @@ final class InstallCommand extends Command return $files; } + /** + * Publish a plugin's configuration file. + * + * @param PluginInterface $plugin The plugin to publish configuration for + * @param string $configFile The source configuration file path + */ private function publishConfig(PluginInterface $plugin, string $configFile): void { if (!file_exists($configFile)) { diff --git a/src/Laravel/Component/FlasherComponent.php b/src/Laravel/Component/FlasherComponent.php index 33f5ea1c..7454fd48 100644 --- a/src/Laravel/Component/FlasherComponent.php +++ b/src/Laravel/Component/FlasherComponent.php @@ -6,12 +6,39 @@ namespace Flasher\Laravel\Component; use Illuminate\View\Component; +/** + * FlasherComponent - Blade component for rendering notifications. + * + * This class provides a Blade component interface for rendering PHPFlasher + * notifications in Laravel views. It can be used with Laravel's component syntax: + * + * + * Design patterns: + * - View Component: Implements Laravel's view component pattern + * - Adapter: Adapts the Flasher render method to Laravel's component interface + */ final class FlasherComponent extends Component { + /** + * Creates a new FlasherComponent instance. + * + * @param string $criteria JSON-encoded filtering criteria for notifications + * @param string $context JSON-encoded rendering context + */ public function __construct(public string $criteria = '', public string $context = '') { } + /** + * Renders the component. + * + * This method decodes the JSON criteria and context, then delegates to + * the Flasher service to render the notifications as HTML. + * + * @return string Rendered HTML content + * + * @throws \JsonException If JSON decoding fails + */ public function render(): string { /** @var array $criteria */ diff --git a/src/Laravel/EventListener/LivewireListener.php b/src/Laravel/EventListener/LivewireListener.php index 40b9f549..4dca259b 100644 --- a/src/Laravel/EventListener/LivewireListener.php +++ b/src/Laravel/EventListener/LivewireListener.php @@ -13,8 +13,28 @@ use Livewire\Component; use Livewire\LivewireManager; use Livewire\Mechanisms\HandleComponents\ComponentContext; +/** + * LivewireListener - Integrates PHPFlasher with Livewire component lifecycle. + * + * This listener ensures that PHPFlasher notifications can be displayed during + * Livewire component updates without requiring a full page load. It dispatches + * notifications as Livewire events that are handled by the front-end JavaScript. + * + * Design patterns: + * - Observer: Listens to Livewire component lifecycle events + * - Adapter: Adapts PHPFlasher notifications to Livewire's event system + * - Security-aware: Ensures CSP compliance with proper nonce handling + */ final readonly class LivewireListener { + /** + * Creates a new LivewireListener instance. + * + * @param LivewireManager $livewire The Livewire manager + * @param FlasherInterface $flasher The PHPFlasher service + * @param ContentSecurityPolicyHandlerInterface $cspHandler The CSP handler for security + * @param \Closure $request Closure to get the current request + */ public function __construct( private LivewireManager $livewire, private FlasherInterface $flasher, @@ -23,6 +43,15 @@ final readonly class LivewireListener ) { } + /** + * Handle a Livewire component dehydration event. + * + * This method is invoked during Livewire's component rendering process + * and dispatches any pending notifications as Livewire events. + * + * @param Component $component The Livewire component being rendered + * @param ComponentContext $context The Livewire component context + */ public function __invoke(Component $component, ComponentContext $context): void { if ($this->shouldSkip($context)) { @@ -38,7 +67,14 @@ final readonly class LivewireListener } /** - * @param array{envelopes: Envelope[]} $data + * Dispatches notifications as Livewire events. + * + * This method adds the notifications data to the Livewire component's + * dispatched events, which will be processed by the front-end. + * + * @param Component $component The Livewire component + * @param ComponentContext $context The Livewire component context + * @param array{envelopes: Envelope[]} $data The notification data */ private function dispatchNotifications(Component $component, ComponentContext $context, array $data): void { @@ -53,13 +89,30 @@ final readonly class LivewireListener $context->addEffect('dispatches', $dispatches); } + /** + * Determines if notification processing should be skipped. + * + * Skips processing in the following cases: + * - Not a Livewire request + * - During component mounting (initial render) + * - When a redirect is in progress + * + * @param ComponentContext $context The Livewire component context + * + * @return bool True if notification processing should be skipped + */ private function shouldSkip(ComponentContext $context): bool { return !$this->livewire->isLivewireRequest() || $context->mounting || isset($context->effects['redirect']); } /** - * @return array + * Creates the security context for rendering notifications. + * + * This method generates CSP nonces to ensure scripts loaded by PHPFlasher + * comply with Content Security Policy. + * + * @return array The context with CSP nonces */ private function createContext(): array { diff --git a/src/Laravel/EventListener/OctaneListener.php b/src/Laravel/EventListener/OctaneListener.php index 273138d8..6216815e 100644 --- a/src/Laravel/EventListener/OctaneListener.php +++ b/src/Laravel/EventListener/OctaneListener.php @@ -7,8 +7,26 @@ namespace Flasher\Laravel\EventListener; use Flasher\Prime\EventDispatcher\EventListener\NotificationLoggerListener; use Laravel\Octane\Events\RequestReceived; +/** + * OctaneListener - Resets notification logger between Octane requests. + * + * This listener ensures that notifications from previous requests don't + * leak into new requests when running Laravel Octane, which keeps workers + * alive between requests. + * + * Design patterns: + * - Observer: Observes Octane request events + * - State Reset: Cleans up state between stateful worker requests + */ final readonly class OctaneListener { + /** + * Handle the Octane RequestReceived event. + * + * Resets the notification logger to ensure clean state for the new request. + * + * @param RequestReceived $event The Octane request received event + */ public function handle(RequestReceived $event): void { /** @var NotificationLoggerListener $listener */ diff --git a/src/Laravel/Facade/Flasher.php b/src/Laravel/Facade/Flasher.php index 3c8d9798..c90c2f8b 100644 --- a/src/Laravel/Facade/Flasher.php +++ b/src/Laravel/Facade/Flasher.php @@ -10,6 +10,29 @@ use Flasher\Prime\Stamp\StampInterface; use Illuminate\Support\Facades\Facade; /** + * Flasher - Laravel Facade for the PHPFlasher service. + * + * This facade provides a static interface to PHPFlasher's functionality within Laravel, + * following Laravel's facade pattern. It offers comprehensive IDE autocompletion for all + * PHPFlasher methods by providing method signatures in PHPDoc comments. + * + * Design patterns: + * - Facade: Provides a simplified, static interface to a complex subsystem + * - Proxy: Acts as a proxy to the underlying PHPFlasher service + * + * Usage examples: + * ```php + * // Simple notification + * Flasher::success('Operation completed successfully'); + * + * // Chained configuration + * Flasher::type('success') + * ->title('Success') + * ->message('Record saved') + * ->option('timeout', 5000) + * ->push(); + * ``` + * * @method static NotificationBuilder title(string $message) * @method static NotificationBuilder message(string $message) * @method static NotificationBuilder type(string $message) @@ -48,6 +71,11 @@ use Illuminate\Support\Facades\Facade; */ final class Flasher extends Facade { + /** + * Get the registered name of the component. + * + * @return string The name of the facade's service binding ('flasher') + */ protected static function getFacadeAccessor(): string { return 'flasher'; diff --git a/src/Laravel/FlasherServiceProvider.php b/src/Laravel/FlasherServiceProvider.php index 3e23a54a..c7affe61 100644 --- a/src/Laravel/FlasherServiceProvider.php +++ b/src/Laravel/FlasherServiceProvider.php @@ -42,8 +42,28 @@ use Illuminate\View\Compilers\BladeCompiler; use Laravel\Octane\Events\RequestReceived; use Livewire\LivewireManager; +/** + * FlasherServiceProvider - Main service provider for Laravel integration. + * + * This class serves as the entry point for integrating PHPFlasher with Laravel. + * It registers all necessary services, components, commands, and middleware + * to provide a seamless Laravel experience. + * + * Design patterns: + * - Service Provider: Implements Laravel's service provider pattern for registration + * - Adapter: Adapts Laravel-specific components to PHPFlasher interfaces + * - Bridge: Connects the framework-agnostic PHPFlasher core to Laravel + */ final class FlasherServiceProvider extends PluginServiceProvider { + /** + * Register PHPFlasher services with the Laravel container. + * + * This method follows Laravel's service provider registration phase by: + * 1. Creating the core plugin + * 2. Registering configuration + * 3. Registering core services and adapters + */ public function register(): void { $this->plugin = $this->createPlugin(); @@ -60,6 +80,17 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->registerAssetManager(); } + /** + * Boot PHPFlasher services after all providers are registered. + * + * This method follows Laravel's service provider boot phase by: + * 1. Setting up the service container bridge + * 2. Registering commands + * 3. Loading translations + * 4. Registering middleware + * 5. Registering Blade directives and components + * 6. Setting up Livewire integration + */ public function boot(): void { FlasherContainer::from(static fn () => Container::getInstance()); @@ -71,11 +102,22 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->registerLivewire(); } + /** + * Create the PHPFlasher core plugin instance. + * + * @return FlasherPlugin The core PHPFlasher plugin + */ public function createPlugin(): FlasherPlugin { return new FlasherPlugin(); } + /** + * Register the main Flasher service with Laravel's container. + * + * This service is the main entry point for all PHPFlasher functionality + * and is made available via the 'flasher' service binding. + */ private function registerFlasher(): void { $this->app->singleton('flasher', static function (Application $app) { @@ -93,6 +135,12 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->app->bind(FlasherInterface::class, 'flasher'); } + /** + * Register the factory locator service. + * + * The factory locator is responsible for locating and providing + * notification factory instances. + */ private function registerFactoryLocator(): void { $this->app->singleton('flasher.factory_locator', static function () { @@ -100,6 +148,12 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the response manager service. + * + * The response manager is responsible for rendering notifications + * into different formats (HTML, JSON, etc.). + */ private function registerResponseManager(): void { $this->app->singleton('flasher.response_manager', static function (Application $app) { @@ -111,6 +165,11 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the template engine adapter for Blade. + * + * This adapter allows PHPFlasher to render templates using Laravel's Blade engine. + */ private function registerTemplateEngine(): void { $this->app->singleton('flasher.template_engine', static function (Application $app) { @@ -120,6 +179,12 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the resource manager service. + * + * The resource manager is responsible for managing assets (JS, CSS) + * needed by notifications. + */ private function registerResourceManager(): void { $this->app->singleton('flasher.resource_manager', static function (Application $app) { @@ -134,6 +199,12 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the storage manager service. + * + * The storage manager is responsible for storing and retrieving + * notifications from storage (session in Laravel's case). + */ private function registerStorageManager(): void { $this->app->singleton('flasher.storage_manager', static function (Application $app) { @@ -148,6 +219,12 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the event dispatcher and event listeners. + * + * The event dispatcher is responsible for dispatching events during + * the notification lifecycle. + */ private function registerEventDispatcher(): void { $this->app->singleton('flasher.notification_logger_listener', fn () => new NotificationLoggerListener()); @@ -173,6 +250,11 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the Artisan commands for PHPFlasher. + * + * Commands are only registered when running in console mode. + */ private function registerCommands(): void { if (!$this->app->runningInConsole()) { @@ -190,6 +272,11 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->commands(InstallCommand::class); } + /** + * Register PHPFlasher information with Laravel's about command. + * + * This adds PHPFlasher information to the output of the `php artisan about` command. + */ private function registerAboutCommand(): void { if (!class_exists(AboutCommand::class)) { @@ -214,12 +301,22 @@ final class FlasherServiceProvider extends PluginServiceProvider ]); } + /** + * Register PHPFlasher middleware with Laravel. + * + * Middleware includes session processing and response modification. + */ private function registerMiddlewares(): void { $this->registerSessionMiddleware(); $this->registerFlasherMiddleware(); } + /** + * Register the response middleware. + * + * This middleware injects notification assets into responses. + */ private function registerFlasherMiddleware(): void { if (!$this->getConfig('inject_assets')) { @@ -239,6 +336,11 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->pushMiddlewareToGroup(FlasherMiddleware::class); } + /** + * Register the session middleware. + * + * This middleware processes flash messages from the session. + */ private function registerSessionMiddleware(): void { if (!$this->getConfig('flash_bag')) { @@ -257,6 +359,11 @@ final class FlasherServiceProvider extends PluginServiceProvider $this->pushMiddlewareToGroup(SessionMiddleware::class); } + /** + * Push middleware to the web middleware group. + * + * @param string $middleware The middleware class name + */ private function pushMiddlewareToGroup(string $middleware): void { $this->callAfterResolving(HttpKernel::class, function (HttpKernel $kernel) use ($middleware) { @@ -264,6 +371,11 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the Content Security Policy handler. + * + * This service handles CSP headers when injecting assets. + */ private function registerCspHandler(): void { $this->app->singleton('flasher.csp_handler', static function () { @@ -271,6 +383,11 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register the asset manager service. + * + * The asset manager is responsible for managing asset paths and manifests. + */ private function registerAssetManager(): void { $this->app->singleton('flasher.asset_manager', static function () { @@ -281,6 +398,11 @@ final class FlasherServiceProvider extends PluginServiceProvider }); } + /** + * Register Blade directives and components. + * + * @param BladeCompiler $blade The Blade compiler instance + */ private function registerBladeDirectives(BladeCompiler $blade): void { $blade->directive('flasher_render', function (string $expression = '') { @@ -294,6 +416,11 @@ final class FlasherServiceProvider extends PluginServiceProvider $blade->component(FlasherComponent::class, 'flasher'); } + /** + * Register Livewire integration. + * + * This sets up listeners for Livewire component lifecycle events. + */ private function registerLivewire(): void { if (class_exists(LivewireManager::class) && !$this->app->bound('livewire')) { diff --git a/src/Laravel/Http/Request.php b/src/Laravel/Http/Request.php index 25167c2e..c205a620 100644 --- a/src/Laravel/Http/Request.php +++ b/src/Laravel/Http/Request.php @@ -8,8 +8,25 @@ use Flasher\Prime\Http\RequestInterface; use Illuminate\Contracts\Session\Session; use Illuminate\Http\Request as LaravelRequest; +/** + * Request - Adapter for Laravel HTTP requests. + * + * This adapter implements PHPFlasher's RequestInterface for Laravel HTTP requests, + * providing a consistent interface for request inspection and session interaction + * regardless of the underlying framework. + * + * Design patterns: + * - Adapter: Adapts framework-specific request objects to PHPFlasher's interface + * - Decorator: Adds PHPFlasher-specific functionality to request objects + * - Composition: Uses composition to delegate to the underlying request object + */ final readonly class Request implements RequestInterface { + /** + * Creates a new Request adapter. + * + * @param LaravelRequest $request The underlying Laravel request object + */ public function __construct(private LaravelRequest $request) { } @@ -73,6 +90,11 @@ final readonly class Request implements RequestInterface $session?->forget($type); } + /** + * Gets the session from the request, with graceful handling of missing sessions. + * + * @return Session|null The session or null if not available + */ private function getSession(): ?Session { try { diff --git a/src/Laravel/Http/Response.php b/src/Laravel/Http/Response.php index b0a1ac77..c24717e7 100644 --- a/src/Laravel/Http/Response.php +++ b/src/Laravel/Http/Response.php @@ -9,8 +9,25 @@ use Illuminate\Http\Response as LaravelResponse; use Symfony\Component\HttpFoundation\JsonResponse as SymfonyJsonResponse; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +/** + * Response - Adapter for Laravel/Symfony HTTP responses. + * + * This adapter implements PHPFlasher's ResponseInterface for Laravel and Symfony + * HTTP responses, providing a consistent interface for response manipulation + * regardless of the underlying framework. + * + * Design patterns: + * - Adapter: Adapts framework-specific response objects to PHPFlasher's interface + * - Decorator: Adds PHPFlasher-specific functionality to response objects + * - Composition: Uses composition to delegate to the underlying response object + */ final readonly class Response implements ResponseInterface { + /** + * Creates a new Response adapter. + * + * @param SymfonyResponse $response The underlying Symfony/Laravel response object + */ public function __construct(private SymfonyResponse $response) { } @@ -57,6 +74,12 @@ final readonly class Response implements ResponseInterface return $this->response->getContent() ?: ''; } + /** + * {@inheritdoc} + * + * This implementation preserves the original content in Laravel responses, + * ensuring compatibility with Laravel's view system and JSON responses. + */ public function setContent(string $content): void { $original = null; diff --git a/src/Laravel/Middleware/FlasherMiddleware.php b/src/Laravel/Middleware/FlasherMiddleware.php index 06fa8161..b1ac7277 100644 --- a/src/Laravel/Middleware/FlasherMiddleware.php +++ b/src/Laravel/Middleware/FlasherMiddleware.php @@ -10,12 +10,40 @@ use Flasher\Prime\Http\ResponseExtensionInterface; use Illuminate\Http\Request as LaravelRequest; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +/** + * FlasherMiddleware - Middleware for injecting PHPFlasher assets into responses. + * + * This middleware processes outgoing HTTP responses to inject PHPFlasher's + * JavaScript and CSS assets when needed. It's typically added to Laravel's + * web middleware group. + * + * Design patterns: + * - Pipeline: Participates in Laravel's middleware pipeline + * - Decorator: Decorates HTTP responses with PHPFlasher assets + * - Adapter: Adapts Laravel requests/responses to PHPFlasher interfaces + */ final readonly class FlasherMiddleware { + /** + * Creates a new FlasherMiddleware instance. + * + * @param ResponseExtensionInterface $responseExtension Service for extending responses with notifications + */ public function __construct(private ResponseExtensionInterface $responseExtension) { } + /** + * Handle an incoming request. + * + * Processes the response after the application has generated it, + * injecting PHPFlasher assets and notifications as needed. + * + * @param LaravelRequest $request The incoming request + * @param \Closure $next The next middleware handler + * + * @return mixed The processed response + */ public function handle(LaravelRequest $request, \Closure $next): mixed { $response = $next($request); diff --git a/src/Laravel/Middleware/SessionMiddleware.php b/src/Laravel/Middleware/SessionMiddleware.php index 79ccda72..366cddc8 100644 --- a/src/Laravel/Middleware/SessionMiddleware.php +++ b/src/Laravel/Middleware/SessionMiddleware.php @@ -10,12 +10,40 @@ use Flasher\Prime\Http\RequestExtensionInterface; use Illuminate\Http\Request as LaravelRequest; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +/** + * SessionMiddleware - Middleware for processing session flash messages. + * + * This middleware processes Laravel's session flash messages and converts them + * to PHPFlasher notifications, allowing seamless integration with existing code + * that uses Laravel's built-in flash messaging. + * + * Design patterns: + * - Pipeline: Participates in Laravel's middleware pipeline + * - Adapter: Adapts Laravel flash messages to PHPFlasher notifications + * - Transformer: Transforms one message format to another + */ final readonly class SessionMiddleware { + /** + * Creates a new SessionMiddleware instance. + * + * @param RequestExtensionInterface $requestExtension Service for processing request flash messages + */ public function __construct(private RequestExtensionInterface $requestExtension) { } + /** + * Handle an incoming request. + * + * Processes the request and response to convert Laravel flash messages + * to PHPFlasher notifications. + * + * @param LaravelRequest $request The incoming request + * @param \Closure $next The next middleware handler + * + * @return mixed The processed response + */ public function handle(LaravelRequest $request, \Closure $next): mixed { $response = $next($request); diff --git a/src/Laravel/Phpstan/stubs/ApplicationTestingHooks.stub b/src/Laravel/Phpstan/stubs/ApplicationTestingHooks.stub index 88b01d85..d531c38c 100644 --- a/src/Laravel/Phpstan/stubs/ApplicationTestingHooks.stub +++ b/src/Laravel/Phpstan/stubs/ApplicationTestingHooks.stub @@ -2,9 +2,19 @@ namespace Orchestra\Testbench\Concerns; +/** + * ApplicationTestingHooks - PHPStan stub for Orchestra Testbench. + * + * This stub file enhances PHPStan's static analysis for PHPFlasher by + * providing type information for Orchestra Testbench's testing hooks. + * + * Design pattern: Type Annotation - Provides type information for static analysis + */ trait ApplicationTestingHooks { /** + * The Laravel application instance for testing. + * * @var \Illuminate\Foundation\Application */ protected $app; diff --git a/src/Laravel/Phpstan/stubs/Repository.stub b/src/Laravel/Phpstan/stubs/Repository.stub index 43d7eb7c..be97ede6 100644 --- a/src/Laravel/Phpstan/stubs/Repository.stub +++ b/src/Laravel/Phpstan/stubs/Repository.stub @@ -5,6 +5,14 @@ declare(strict_types=1); namespace Illuminate\Contracts\Config; /** + * Repository - PHPStan stub for Laravel's Config Repository. + * + * This stub file enhances PHPStan's static analysis for PHPFlasher by + * providing detailed type information for Laravel's configuration repository, + * particularly for PHPFlasher's configuration structure. + * + * Design pattern: Type Annotation - Provides detailed type information for static analysis + * * @phpstan-type ConfigType array{ * default: string, * main_script: string, @@ -36,7 +44,15 @@ namespace Illuminate\Contracts\Config; interface Repository { /** - * @param string[]|string $key + * Get a configuration value. + * + * This stub provides detailed return type information for PHPFlasher's + * configuration structure, enabling better static analysis. + * + * @param string[]|string $key The configuration key + * @param mixed $default Default value if key doesn't exist + * + * @return mixed The configuration value with precise type information for PHPFlasher keys * * @phpstan-return ($key is 'flasher' ? ConfigType : * ($key is 'flasher.default' ? string : diff --git a/src/Laravel/Resources/config.php b/src/Laravel/Resources/config.php index 58e6922d..c32fe53f 100644 --- a/src/Laravel/Resources/config.php +++ b/src/Laravel/Resources/config.php @@ -4,6 +4,15 @@ declare(strict_types=1); use Flasher\Prime\Configuration; +/* + * Default PHPFlasher configuration for Laravel. + * + * This configuration file defines the default settings for PHPFlasher when + * used within a Laravel application. It uses the Configuration class from + * the core PHPFlasher library to establish type-safe configuration. + * + * @return array PHPFlasher configuration + */ return Configuration::from([ // Default notification library (e.g., 'flasher', 'toastr', 'noty', 'notyf', 'sweetalert') 'default' => 'flasher', diff --git a/src/Laravel/Storage/SessionBag.php b/src/Laravel/Storage/SessionBag.php index 82cf5aaa..9aed595d 100644 --- a/src/Laravel/Storage/SessionBag.php +++ b/src/Laravel/Storage/SessionBag.php @@ -8,14 +8,39 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Storage\Bag\BagInterface; use Illuminate\Session\SessionManager; +/** + * SessionBag - Laravel session storage for PHPFlasher notifications. + * + * This class implements PHPFlasher's storage interface using Laravel's session + * system, providing persistence for notifications across requests. + * + * Design patterns: + * - Adapter: Adapts Laravel's session system to PHPFlasher's storage interface + * - Repository: Provides CRUD operations for notification storage + */ final readonly class SessionBag implements BagInterface { + /** + * Session key for storing notification envelopes. + */ public const ENVELOPES_NAMESPACE = 'flasher::envelopes'; + /** + * Creates a new SessionBag instance. + * + * @param SessionManager $session Laravel's session manager + */ public function __construct(private SessionManager $session) { } + /** + * {@inheritdoc} + * + * Retrieves all stored notification envelopes from the session. + * + * @return Envelope[] The stored notification envelopes + */ public function get(): array { /** @var Envelope[] $envelopes */ @@ -24,6 +49,13 @@ final readonly class SessionBag implements BagInterface return $envelopes; } + /** + * {@inheritdoc} + * + * Stores notification envelopes in the session. + * + * @param Envelope[] $envelopes The notification envelopes to store + */ public function set(array $envelopes): void { $this->session->put(self::ENVELOPES_NAMESPACE, $envelopes); diff --git a/src/Laravel/Support/PluginServiceProvider.php b/src/Laravel/Support/PluginServiceProvider.php index b44915d1..e5b987f0 100644 --- a/src/Laravel/Support/PluginServiceProvider.php +++ b/src/Laravel/Support/PluginServiceProvider.php @@ -11,12 +11,44 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Foundation\CachesConfiguration; use Illuminate\Support\ServiceProvider; +/** + * PluginServiceProvider - Base service provider for PHPFlasher plugins. + * + * This abstract class provides the foundation for all PHPFlasher plugin + * service providers in Laravel. It handles the registration of plugin + * configuration, factories, and services. + * + * Design patterns: + * - Template Method: Defines the skeleton of registration process with customizable steps + * - Factory: Creates plugin instances via createPlugin method + * - Service Provider: Implements Laravel's service provider pattern + * + * Plugin providers should extend this class and implement the createPlugin method. + */ abstract class PluginServiceProvider extends ServiceProvider { + /** + * The plugin instance. + */ protected PluginInterface $plugin; + /** + * Create the plugin instance. + * + * Child classes must implement this method to provide their specific plugin. + * + * @return PluginInterface The plugin instance + */ abstract public function createPlugin(): PluginInterface; + /** + * Register services with the Laravel container. + * + * This method: + * 1. Creates the plugin instance + * 2. Registers plugin configuration + * 3. Calls the afterRegister hook for customization + */ public function register(): void { $this->plugin = $this->createPlugin(); @@ -25,17 +57,37 @@ abstract class PluginServiceProvider extends ServiceProvider $this->afterRegister(); } + /** + * Bootstrap services after all providers are registered. + * + * This method: + * 1. Registers the plugin factory + * 2. Calls the afterBoot hook for customization + */ public function boot(): void { $this->registerFactory(); $this->afterBoot(); } + /** + * Get the plugin's configuration file path. + * + * @return string The absolute path to the configuration file + */ public function getConfigurationFile(): string { return rtrim($this->getResourcesDir(), '/').'/config.php'; } + /** + * Get a configuration value with optional default. + * + * @param string|null $key The configuration key to retrieve, or null for all config + * @param mixed $default The default value to return if the key doesn't exist + * + * @return mixed The configuration value + */ protected function getConfig(?string $key = null, mixed $default = null): mixed { /** @var Repository $config */ @@ -44,6 +96,11 @@ abstract class PluginServiceProvider extends ServiceProvider return $key ? $config->get('flasher.'.$key, $default) : $config->get('flasher'); } + /** + * Get the plugin's resources directory path. + * + * @return string The absolute path to the resources directory + */ protected function getResourcesDir(): string { $r = new \ReflectionClass($this); @@ -51,6 +108,12 @@ abstract class PluginServiceProvider extends ServiceProvider return pathinfo($r->getFileName() ?: '', \PATHINFO_DIRNAME).'/Resources/'; } + /** + * Register the plugin's configuration. + * + * This method merges the plugin's default configuration with any user-defined + * configuration and registers it with Laravel's config repository. + */ protected function registerConfiguration(): void { if ($this->app instanceof CachesConfiguration && $this->app->configurationIsCached()) { @@ -73,14 +136,32 @@ abstract class PluginServiceProvider extends ServiceProvider $config->set($key, $this->plugin->normalizeConfig($current)); } + /** + * Hook method executed after registration. + * + * Child classes can override this method to add custom registration logic. + */ protected function afterRegister(): void { } + /** + * Hook method executed after boot. + * + * Child classes can override this method to add custom boot logic. + */ protected function afterBoot(): void { } + /** + * Register the plugin's notification factory. + * + * This method: + * 1. Registers the factory as a singleton + * 2. Registers any aliases for the factory + * 3. Adds the factory to the factory locator + */ protected function registerFactory(): void { $this->app->singleton($this->plugin->getServiceId(), function (Application $app) { diff --git a/src/Laravel/Template/BladeTemplateEngine.php b/src/Laravel/Template/BladeTemplateEngine.php index 6f646461..5dc6f178 100644 --- a/src/Laravel/Template/BladeTemplateEngine.php +++ b/src/Laravel/Template/BladeTemplateEngine.php @@ -7,12 +7,38 @@ namespace Flasher\Laravel\Template; use Flasher\Prime\Template\TemplateEngineInterface; use Illuminate\View\Factory; +/** + * BladeTemplateEngine - Laravel Blade adapter for PHPFlasher templates. + * + * This class provides an implementation of PHPFlasher's template engine interface + * using Laravel's Blade templating system. It allows PHPFlasher to render templates + * using all of Blade's features. + * + * Design patterns: + * - Adapter: Adapts Laravel's Blade engine to PHPFlasher's template interface + * - Bridge: Connects PHPFlasher's templating needs with Laravel's templating system + */ final readonly class BladeTemplateEngine implements TemplateEngineInterface { + /** + * Creates a new BladeTemplateEngine instance. + * + * @param Factory $blade Laravel's view factory + */ public function __construct(private Factory $blade) { } + /** + * {@inheritdoc} + * + * Renders a template using Laravel's Blade engine. + * + * @param string $name The template name or path + * @param array $context The template variables + * + * @return string The rendered template + */ public function render(string $name, array $context = []): string { return $this->blade->make($name, $context)->render(); diff --git a/src/Laravel/Translation/Translator.php b/src/Laravel/Translation/Translator.php index ed9850b5..4c758271 100644 --- a/src/Laravel/Translation/Translator.php +++ b/src/Laravel/Translation/Translator.php @@ -7,12 +7,43 @@ namespace Flasher\Laravel\Translation; use Flasher\Prime\Translation\TranslatorInterface; use Illuminate\Translation\Translator as LaravelTranslator; +/** + * Translator - Laravel adapter for PHPFlasher translations. + * + * This class provides an implementation of PHPFlasher's translator interface + * using Laravel's translation system. It enables PHPFlasher notifications + * to be localized using Laravel's language files and translation workflow. + * + * Design patterns: + * - Adapter: Adapts Laravel's translator to PHPFlasher's interface + * - Decorator: Adds PHPFlasher-specific behavior to Laravel's translator + */ final readonly class Translator implements TranslatorInterface { + /** + * Creates a new Translator instance. + * + * @param LaravelTranslator $translator Laravel's translator service + */ public function __construct(private LaravelTranslator $translator) { } + /** + * {@inheritdoc} + * + * Translates a message using Laravel's translation system. + * Tries multiple namespaces in this order: + * 1. flasher::messages.{id} + * 2. messages.{id} + * 3. {id} directly + * + * @param string $id The translation key + * @param array $parameters The parameters for variable substitution + * @param string|null $locale The locale to use, or null for default + * + * @return string The translated string + */ public function translate(string $id, array $parameters = [], ?string $locale = null): string { $parameters = $this->formatParameters($parameters); @@ -30,6 +61,13 @@ final readonly class Translator implements TranslatorInterface return $translation; } + /** + * {@inheritdoc} + * + * Gets the current locale from Laravel's translator. + * + * @return string The current locale code + */ public function getLocale(): string { return $this->translator->getLocale(); @@ -38,9 +76,12 @@ final readonly class Translator implements TranslatorInterface /** * Formats the parameters by stripping the colon prefix from keys for Laravel's translator. * - * @param array $parameters + * This ensures compatibility between PHPFlasher's parameter format (:parameter) + * and Laravel's parameter format (parameter). * - * @return array + * @param array $parameters The parameters with potential colon prefixes + * + * @return array The formatted parameters */ private function formatParameters(array $parameters): array { diff --git a/src/Laravel/Translation/lang/ar/messages.php b/src/Laravel/Translation/lang/ar/messages.php index 084fd9ee..d6fc12e8 100644 --- a/src/Laravel/Translation/lang/ar/messages.php +++ b/src/Laravel/Translation/lang/ar/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * Arabic translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns Arabic + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('ar'); diff --git a/src/Laravel/Translation/lang/de/messages.php b/src/Laravel/Translation/lang/de/messages.php index 57908c7e..554bfcdd 100644 --- a/src/Laravel/Translation/lang/de/messages.php +++ b/src/Laravel/Translation/lang/de/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * German translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns German + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('de'); diff --git a/src/Laravel/Translation/lang/en/messages.php b/src/Laravel/Translation/lang/en/messages.php index ca96f4c5..89e4df39 100644 --- a/src/Laravel/Translation/lang/en/messages.php +++ b/src/Laravel/Translation/lang/en/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * English translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns English + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('en'); diff --git a/src/Laravel/Translation/lang/es/messages.php b/src/Laravel/Translation/lang/es/messages.php index f7fe97e6..8535c287 100644 --- a/src/Laravel/Translation/lang/es/messages.php +++ b/src/Laravel/Translation/lang/es/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * Spanish translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns Spanish + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('es'); diff --git a/src/Laravel/Translation/lang/fr/messages.php b/src/Laravel/Translation/lang/fr/messages.php index 3a8aff96..d1b3b5dd 100644 --- a/src/Laravel/Translation/lang/fr/messages.php +++ b/src/Laravel/Translation/lang/fr/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * French translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns French + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('fr'); diff --git a/src/Laravel/Translation/lang/pt/messages.php b/src/Laravel/Translation/lang/pt/messages.php index 688f1ef9..7d456167 100644 --- a/src/Laravel/Translation/lang/pt/messages.php +++ b/src/Laravel/Translation/lang/pt/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * Portuguese translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns Portuguese + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('pt'); diff --git a/src/Laravel/Translation/lang/ru/messages.php b/src/Laravel/Translation/lang/ru/messages.php index 24a9e214..3da53300 100644 --- a/src/Laravel/Translation/lang/ru/messages.php +++ b/src/Laravel/Translation/lang/ru/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * Russian translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns Russian + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('ru'); diff --git a/src/Laravel/Translation/lang/zh/messages.php b/src/Laravel/Translation/lang/zh/messages.php index 091e1c65..ac38f190 100644 --- a/src/Laravel/Translation/lang/zh/messages.php +++ b/src/Laravel/Translation/lang/zh/messages.php @@ -2,4 +2,16 @@ declare(strict_types=1); +/** + * Chinese translations for PHPFlasher. + * + * This file serves as a bridge between Laravel's translation system and + * PHPFlasher's core translation repository. It simply returns Chinese + * translations from the core Messages class. + * + * Design pattern: Bridge - Connects Laravel's translation system to PHPFlasher's + * centralized message store. + * + * @return array Key-value pairs of message identifiers and translations + */ return Flasher\Prime\Translation\Messages::get('zh'); diff --git a/src/Noty/Laravel/.github/FUNDING.yml b/src/Noty/Laravel/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Noty/Laravel/.github/FUNDING.yml +++ b/src/Noty/Laravel/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Noty/Laravel/Facade/Noty.php b/src/Noty/Laravel/Facade/Noty.php index bde56121..b121823a 100644 --- a/src/Noty/Laravel/Facade/Noty.php +++ b/src/Noty/Laravel/Facade/Noty.php @@ -10,6 +10,29 @@ use Flasher\Prime\Stamp\StampInterface; use Illuminate\Support\Facades\Facade; /** + * Noty - Laravel facade for Noty notifications. + * + * This facade provides a static interface to Noty's functionality within Laravel, + * following Laravel's facade pattern. It offers comprehensive IDE autocompletion + * for all Noty builder methods. + * + * Design patterns: + * - Facade: Provides a simplified, static interface to a complex subsystem + * - Proxy: Acts as a proxy to the underlying Noty service + * + * Usage examples: + * ```php + * // Simple notification + * Noty::success('Operation completed successfully'); + * + * // Chained configuration + * Noty::layout('topRight') + * ->theme('mint') + * ->timeout(5000) + * ->progressBar(true) + * ->success('Record saved'); + * ``` + * * @method static NotyBuilder success(string $message, array $options = array()) * @method static NotyBuilder error(string $message, array $options = array()) * @method static NotyBuilder warning(string $message, array $options = array()) @@ -48,6 +71,11 @@ use Illuminate\Support\Facades\Facade; */ final class Noty extends Facade { + /** + * Get the registered name of the component. + * + * @return string The service container binding key for Noty + */ protected static function getFacadeAccessor(): string { return 'flasher.noty'; diff --git a/src/Noty/Laravel/FlasherNotyServiceProvider.php b/src/Noty/Laravel/FlasherNotyServiceProvider.php index 4322dcae..2ae8d477 100644 --- a/src/Noty/Laravel/FlasherNotyServiceProvider.php +++ b/src/Noty/Laravel/FlasherNotyServiceProvider.php @@ -7,8 +7,24 @@ namespace Flasher\Noty\Laravel; use Flasher\Laravel\Support\PluginServiceProvider; use Flasher\Noty\Prime\NotyPlugin; +/** + * FlasherNotyServiceProvider - Laravel service provider for Noty integration. + * + * This service provider registers the Noty plugin with Laravel's service container. + * It extends the base plugin service provider to inherit common registration logic + * while providing the Noty-specific plugin implementation. + * + * Design patterns: + * - Service Provider: Implements Laravel's service provider pattern + * - Factory Method: Creates the plugin instance + */ final class FlasherNotyServiceProvider extends PluginServiceProvider { + /** + * Creates the Noty plugin instance. + * + * @return NotyPlugin The Noty plugin instance + */ public function createPlugin(): NotyPlugin { return new NotyPlugin(); diff --git a/src/Noty/Prime/.github/FUNDING.yml b/src/Noty/Prime/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Noty/Prime/.github/FUNDING.yml +++ b/src/Noty/Prime/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Noty/Prime/.phpstorm.meta.php b/src/Noty/Prime/.phpstorm.meta.php index 12d17435..9363ea90 100644 --- a/src/Noty/Prime/.phpstorm.meta.php +++ b/src/Noty/Prime/.phpstorm.meta.php @@ -1,10 +1,30 @@ \Flasher\Noty\Prime\NotyInterface::class])); override(\Flasher\Prime\FlasherInterface::create(), map(['noty' => \Flasher\Noty\Prime\NotyInterface::class])); override(\Flasher\Prime\Container\FlasherContainer::create(), map(['flasher.noty' => \Flasher\Noty\Prime\NotyInterface::class])); diff --git a/src/Noty/Prime/Noty.php b/src/Noty/Prime/Noty.php index 738b9f15..1691d558 100644 --- a/src/Noty/Prime/Noty.php +++ b/src/Noty/Prime/Noty.php @@ -7,10 +7,28 @@ namespace Flasher\Noty\Prime; use Flasher\Prime\Factory\NotificationFactory; /** + * Noty - Factory implementation for Noty.js notifications. + * + * This class implements the notification factory for Noty.js, creating + * specialized notification builders configured for Noty's specific features. + * It serves as the primary entry point for creating Noty notifications. + * + * Design patterns: + * - Factory: Creates specialized notification builders + * - Bridge: Connects PHPFlasher's notification system to Noty.js + * - Composition: Delegates to NotyBuilder for construction details + * * @mixin \Flasher\Noty\Prime\NotyBuilder */ final class Noty extends NotificationFactory implements NotyInterface { + /** + * {@inheritdoc} + * + * Creates a new Noty-specific notification builder. + * + * @return NotyBuilder A builder configured for Noty.js notifications + */ public function createNotificationBuilder(): NotyBuilder { return new NotyBuilder('noty', $this->storageManager); diff --git a/src/Noty/Prime/NotyBuilder.php b/src/Noty/Prime/NotyBuilder.php index 16241c4e..ea2b896e 100644 --- a/src/Noty/Prime/NotyBuilder.php +++ b/src/Noty/Prime/NotyBuilder.php @@ -8,6 +8,17 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\NotificationBuilder; /** + * NotyBuilder - Builder implementation for Noty.js notifications. + * + * This class provides a fluent interface for configuring Noty.js notifications. + * It extends the core notification builder with Noty-specific options and + * features, such as layout, theme, animation, and more. + * + * Design patterns: + * - Builder: Provides a fluent interface for constructing complex objects + * - Fluent Interface: Methods return $this for method chaining + * - Type Safety: Uses PHPStan annotations for compile-time type checking + * * @phpstan-type NotificationType "success"|"info"|"warning"|"error"|"alert"|"information" * @phpstan-type OptionsType array{ * layout?: "top"|"topLeft"|"topCenter"|"topRight"|"center"|"centerLeft"|"centerRight"|"bottom"|"bottomLeft"|"bottomCenter"|"bottomRight", @@ -40,7 +51,11 @@ use Flasher\Prime\Notification\NotificationBuilder; final class NotyBuilder extends NotificationBuilder { /** - * @phpstan-param NotificationType $type + * Sets the notification type with Noty-specific type checking. + * + * @phpstan-param NotificationType $type The notification type + * + * @return static The builder instance */ public function type(string $type): static { @@ -48,7 +63,13 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates a success notification with Noty-specific options. + * + * @param string $message The notification message + * @param OptionsType $options Noty-specific options + * @param string|null $title The notification title + * + * @return Envelope The notification envelope */ public function success(string $message, array $options = [], ?string $title = null): Envelope { @@ -56,7 +77,13 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates an error notification with Noty-specific options. + * + * @param string $message The notification message + * @param OptionsType $options Noty-specific options + * @param string|null $title The notification title + * + * @return Envelope The notification envelope */ public function error(string $message, array $options = [], ?string $title = null): Envelope { @@ -64,7 +91,13 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates an info notification with Noty-specific options. + * + * @param string $message The notification message + * @param OptionsType $options Noty-specific options + * @param string|null $title The notification title + * + * @return Envelope The notification envelope */ public function info(string $message, array $options = [], ?string $title = null): Envelope { @@ -72,7 +105,13 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates a warning notification with Noty-specific options. + * + * @param string $message The notification message + * @param OptionsType $options Noty-specific options + * @param string|null $title The notification title + * + * @return Envelope The notification envelope */ public function warning(string $message, array $options = [], ?string $title = null): Envelope { @@ -80,8 +119,17 @@ final class NotyBuilder extends NotificationBuilder } /** - * @phpstan-param NotificationType $type - * @phpstan-param OptionsType $options + * Creates a notification of the specified type with Noty-specific options. + * + * @phpstan-param NotificationType|null $type The notification type + * @phpstan-param OptionsType $options Noty-specific options + * + * @param string|null $type The notification type + * @param string|null $message The notification message + * @param array $options Noty-specific options + * @param string|null $title The notification title + * + * @return Envelope The notification envelope */ public function flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope { @@ -89,7 +137,12 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Sets notification options with Noty-specific type checking. + * + * @param OptionsType $options The notification options + * @param bool $append Whether to append or replace existing options + * + * @return static The builder instance */ public function options(array $options, bool $append = true): static { @@ -97,11 +150,18 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets a specific notification option with Noty-specific type checking. + * * @template T of OptionsType * @template K of key-of * * @phpstan-param K $name * @phpstan-param T[K] $value + * + * @param string $name The option name + * @param mixed $value The option value + * + * @return static The builder instance */ public function option(string $name, mixed $value): static { @@ -109,7 +169,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets the notification text (alias for message). + * * This string can contain HTML too. But be careful and don't pass user inputs to this parameter. + * + * @param string $text The notification text + * + * @return self The builder instance */ public function text(string $text): self { @@ -117,7 +183,13 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates an alert notification. + * + * @param string|null $message The notification message + * @param string|null $title The notification title + * @param OptionsType $options Noty-specific options + * + * @return self The builder instance */ public function alert(?string $message = null, ?string $title = null, array $options = []): self { @@ -139,9 +211,15 @@ final class NotyBuilder extends NotificationBuilder } /** - * @phpstan-param OptionsType['layout'] $layout + * Sets the notification layout. + * + * @phpstan-param OptionsType['layout'] $layout The layout position * * - ClassName generator uses this value → noty_layout__${layout} + * + * @param string $layout The layout position + * + * @return self The builder instance */ public function layout(string $layout): self { @@ -151,9 +229,15 @@ final class NotyBuilder extends NotificationBuilder } /** - * @phpstan-param OptionsType['theme'] $theme + * Sets the notification theme. + * + * @phpstan-param OptionsType['theme'] $theme The theme name * * ClassName generator uses this value → noty_theme__${theme} + * + * @param string $theme The theme name + * + * @return self The builder instance */ public function theme(string $theme): self { @@ -163,7 +247,14 @@ final class NotyBuilder extends NotificationBuilder } /** - * false, 1000, 3000, 3500, etc. Delay for closing event in milliseconds (ms). Set 'false' for sticky notifications. + * Sets the notification timeout. + * + * false, 1000, 3000, 3500, etc. Delay for closing event in milliseconds (ms). + * Set 'false' for sticky notifications. + * + * @param false|int $timeout The timeout in milliseconds or false for sticky + * + * @return self The builder instance */ public function timeout(false|int $timeout): self { @@ -173,7 +264,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets whether to display a progress bar. + * * true, false - Displays a progress bar if timeout is not false. + * + * @param bool $progressBar Whether to display a progress bar + * + * @return self The builder instance */ public function progressBar(bool $progressBar = false): self { @@ -183,9 +280,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets how the notification can be closed. + * * click, button. * - * @param string|string[] $closeWith + * @param string|string[] $closeWith Close methods + * + * @return self The builder instance */ public function closeWith(string|array $closeWith): self { @@ -195,11 +296,16 @@ final class NotyBuilder extends NotificationBuilder } /** - * @param "open"|"close" $option - * @param "noty_effects_open"|"noty_effects_close"|string $effect + * Sets animation effects for opening or closing. * - * If string, assumed to be CSS class name. If null, no animation at all. If function, runs the function. (v3.0.1+) + * @param "open"|"close" $option Which animation to set + * @param "noty_effects_open"|"noty_effects_close"|string $effect The animation effect + * + * If string, assumed to be CSS class name. If null, no animation at all. + * If function, runs the function. (v3.0.1+) * You can use animate.css class names or your custom css animations as well. + * + * @return self The builder instance */ public function animation(string $option, string $effect): self { @@ -213,11 +319,18 @@ final class NotyBuilder extends NotificationBuilder } /** - * @phpstan-param "sources"|"volume"|"conditions" $option + * Sets sound options for the notification. + * + * @phpstan-param "sources"|"volume"|"conditions" $option The sound option to set * @phpstan-param ($option is "sources" ? string[] : * ($option is "volume" ? int : * ($option is "conditions" ? string[] : - * mixed))) $value + * mixed))) $value The option value + * + * @param string $option The sound option to set + * @param mixed $value The option value + * + * @return self The builder instance */ public function sounds(string $option, mixed $value): self { @@ -231,8 +344,15 @@ final class NotyBuilder extends NotificationBuilder } /** - * @phpstan-param "conditions"|string $option - * @phpstan-param ($option is "conditions" ? string[] : mixed) $value + * Sets document title options for the notification. + * + * @phpstan-param "conditions"|string $option The document title option + * @phpstan-param ($option is "conditions" ? string[] : mixed) $value The option value + * + * @param string $option The document title option + * @param mixed $value The option value + * + * @return self The builder instance */ public function docTitle(string $option, mixed $value): self { @@ -245,6 +365,13 @@ final class NotyBuilder extends NotificationBuilder return $this; } + /** + * Sets whether the notification is modal. + * + * @param bool $modal Whether the notification is modal + * + * @return self The builder instance + */ public function modal(bool $modal = true): self { $this->option('modal', $modal); @@ -253,7 +380,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets the notification ID. + * * You can use this id with querySelectors. Generated automatically if false. + * + * @param bool|string $id The notification ID or false to generate automatically + * + * @return self The builder instance */ public function id(bool|string $id): self { @@ -263,7 +396,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets whether to force the notification. + * * DOM insert method depends on this parameter. If false uses append, if true uses prepend. + * + * @param bool $force Whether to force the notification + * + * @return self The builder instance */ public function force(bool $force = true): self { @@ -272,6 +411,13 @@ final class NotyBuilder extends NotificationBuilder return $this; } + /** + * Sets the notification queue. + * + * @param string $queue The queue name + * + * @return self The builder instance + */ public function queue(string $queue): self { $this->option('queue', $queue); @@ -280,8 +426,14 @@ final class NotyBuilder extends NotificationBuilder } /** - * If true closes all visible notifications and shows itself. If string(queueName) closes all visible notification - * on this queue and shows itself. + * Sets whether the notification kills other notifications. + * + * If true closes all visible notifications and shows itself. + * If string(queueName) closes all visible notification on this queue and shows itself. + * + * @param bool|string $killer Whether to kill other notifications + * + * @return self The builder instance */ public function killer(bool|string $killer): self { @@ -291,7 +443,14 @@ final class NotyBuilder extends NotificationBuilder } /** - * Custom container selector string. Like '.my-custom-container'. Layout parameter will be ignored. + * Sets the notification container. + * + * Custom container selector string. Like '.my-custom-container'. + * Layout parameter will be ignored. + * + * @param false|string $container The container selector or false to use default + * + * @return self The builder instance */ public function container(false|string $container): self { @@ -301,9 +460,13 @@ final class NotyBuilder extends NotificationBuilder } /** + * Sets the notification buttons. + * * An array of Noty.button, for creating confirmation dialogs. * - * @param string[] $buttons + * @param string[] $buttons The buttons configuration + * + * @return self The builder instance */ public function buttons(array $buttons): self { @@ -313,7 +476,14 @@ final class NotyBuilder extends NotificationBuilder } /** - * If true Noty uses PageVisibility API to handle timeout. To ensure that users do not miss their notifications. + * Sets whether to use visibility control. + * + * If true Noty uses PageVisibility API to handle timeout. + * To ensure that users do not miss their notifications. + * + * @param bool $visibilityControl Whether to use visibility control + * + * @return self The builder instance */ public function visibilityControl(bool $visibilityControl): self { diff --git a/src/Noty/Prime/NotyInterface.php b/src/Noty/Prime/NotyInterface.php index 2a866d45..58501f5b 100644 --- a/src/Noty/Prime/NotyInterface.php +++ b/src/Noty/Prime/NotyInterface.php @@ -7,6 +7,17 @@ namespace Flasher\Noty\Prime; use Flasher\Prime\Factory\NotificationFactoryInterface; /** + * NotyInterface - Contract for Noty notification factories. + * + * This interface defines the contract for Noty notification factories. + * It extends the core notification factory interface to ensure compatibility + * with PHPFlasher's notification system, while allowing IDE completion for + * Noty-specific methods through the mixin annotation. + * + * Design patterns: + * - Interface Segregation: Provides a specific interface for Noty functionality + * - Contract: Defines a contract for creating Noty notifications + * * @mixin \Flasher\Noty\Prime\NotyBuilder */ interface NotyInterface extends NotificationFactoryInterface diff --git a/src/Noty/Prime/NotyPlugin.php b/src/Noty/Prime/NotyPlugin.php index 5c76dbf9..5ad51994 100644 --- a/src/Noty/Prime/NotyPlugin.php +++ b/src/Noty/Prime/NotyPlugin.php @@ -6,25 +6,56 @@ namespace Flasher\Noty\Prime; use Flasher\Prime\Plugin\Plugin; +/** + * NotyPlugin - Plugin definition for Noty.js integration with PHPFlasher. + * + * This class defines the core plugin configuration for the Noty.js notification + * library integration. It specifies the required JavaScript and CSS assets, + * factory class, and service identifiers for dependency injection containers. + * + * Design patterns: + * - Plugin: Implements the plugin pattern for extending core functionality + * - Registry: Registers the plugin's assets and identifiers with the core system + * - Metadata: Provides metadata about the plugin's requirements + */ final class NotyPlugin extends Plugin { + /** + * {@inheritdoc} + * + * Returns the plugin's unique identifier. + */ public function getAlias(): string { return 'noty'; } + /** + * {@inheritdoc} + * + * Returns the factory class responsible for creating Noty notifications. + */ public function getFactory(): string { return Noty::class; } + /** + * {@inheritdoc} + * + * Returns the service alias for dependency injection containers. + */ public function getServiceAliases(): string { return NotyInterface::class; } /** - * @return string[] + * {@inheritdoc} + * + * Returns the required JavaScript files for Noty.js integration. + * + * @return string[] Array of script paths */ public function getScripts(): array { @@ -35,7 +66,11 @@ final class NotyPlugin extends Plugin } /** - * @return string[] + * {@inheritdoc} + * + * Returns the required CSS files for Noty.js styling. + * + * @return string[] Array of stylesheet paths */ public function getStyles(): array { diff --git a/src/Noty/Prime/functions.php b/src/Noty/Prime/functions.php index 0fbf4cf4..70b667c3 100644 --- a/src/Noty/Prime/functions.php +++ b/src/Noty/Prime/functions.php @@ -8,6 +8,15 @@ use Flasher\Prime\Container\FlasherContainer; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\Type; +/* + * Namespace-specific helper functions for Noty notifications. + * + * This file provides a namespace-specific helper function that simplifies + * the creation of Noty notifications from code that uses imports. + * + * Design pattern: Facade - Provides a simplified interface to a complex subsystem + */ + if (!\function_exists('Flasher\Noty\Prime\noty')) { /** * Creates a Noty notification or returns the Noty factory. diff --git a/src/Noty/Prime/helpers.php b/src/Noty/Prime/helpers.php index ecebdc46..c6369a3c 100644 --- a/src/Noty/Prime/helpers.php +++ b/src/Noty/Prime/helpers.php @@ -7,6 +7,15 @@ use Flasher\Prime\Container\FlasherContainer; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\Type; +/* + * Global helper functions for Noty notifications. + * + * This file provides a global helper function that simplifies + * the creation of Noty notifications from any PHP code. + * + * Design pattern: Facade - Provides a simplified interface to a complex subsystem + */ + if (!function_exists('noty')) { /** * Creates a Noty notification or returns the Noty factory. diff --git a/src/Noty/Symfony/.github/FUNDING.yml b/src/Noty/Symfony/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Noty/Symfony/.github/FUNDING.yml +++ b/src/Noty/Symfony/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Noty/Symfony/FlasherNotySymfonyBundle.php b/src/Noty/Symfony/FlasherNotySymfonyBundle.php index f490840d..a13d120f 100644 --- a/src/Noty/Symfony/FlasherNotySymfonyBundle.php +++ b/src/Noty/Symfony/FlasherNotySymfonyBundle.php @@ -8,8 +8,24 @@ use Flasher\Noty\Prime\NotyPlugin; use Flasher\Prime\Plugin\PluginInterface; use Flasher\Symfony\Support\PluginBundle; +/** + * FlasherNotySymfonyBundle - Symfony bundle for Noty integration. + * + * This bundle registers the Noty plugin with Symfony's service container. + * It extends the base plugin bundle to inherit common registration logic + * while providing the Noty-specific plugin implementation. + * + * Design patterns: + * - Bundle: Implements Symfony's bundle pattern for packaging functionality + * - Factory Method: Creates the plugin instance + */ final class FlasherNotySymfonyBundle extends PluginBundle // Symfony\Component\HttpKernel\Bundle\Bundle { + /** + * Creates the Noty plugin instance. + * + * @return PluginInterface The Noty plugin instance + */ public function createPlugin(): PluginInterface { return new NotyPlugin(); diff --git a/src/Notyf/Laravel/.github/FUNDING.yml b/src/Notyf/Laravel/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Notyf/Laravel/.github/FUNDING.yml +++ b/src/Notyf/Laravel/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Notyf/Laravel/Facade/Notyf.php b/src/Notyf/Laravel/Facade/Notyf.php index c12a9260..90a4e5a4 100644 --- a/src/Notyf/Laravel/Facade/Notyf.php +++ b/src/Notyf/Laravel/Facade/Notyf.php @@ -10,6 +10,29 @@ use Flasher\Prime\Stamp\StampInterface; use Illuminate\Support\Facades\Facade; /** + * Notyf - Laravel facade for Notyf notifications. + * + * This facade provides a static interface to Notyf's functionality within Laravel, + * following Laravel's facade pattern. It offers comprehensive IDE autocompletion + * for all Notyf builder methods. + * + * Design patterns: + * - Facade: Provides a simplified, static interface to a complex subsystem + * - Proxy: Acts as a proxy to the underlying Notyf service + * + * Usage examples: + * ```php + * // Simple notification + * Notyf::success('Operation completed successfully'); + * + * // Chained configuration + * Notyf::duration(3000) + * ->dismissible(true) + * ->position('x', 'right') + * ->position('y', 'top') + * ->success('Record saved'); + * ``` + * * @method static NotyfBuilder success(string $message, array $options = array()) * @method static NotyfBuilder error(string $message, array $options = array()) * @method static NotyfBuilder warning(string $message, array $options = array()) @@ -34,6 +57,11 @@ use Illuminate\Support\Facades\Facade; */ final class Notyf extends Facade { + /** + * Get the registered name of the component. + * + * @return string The service container binding key for Notyf + */ protected static function getFacadeAccessor(): string { return 'flasher.notyf'; diff --git a/src/Notyf/Laravel/FlasherNotyfServiceProvider.php b/src/Notyf/Laravel/FlasherNotyfServiceProvider.php index d43ca3a4..aa80d9ea 100644 --- a/src/Notyf/Laravel/FlasherNotyfServiceProvider.php +++ b/src/Notyf/Laravel/FlasherNotyfServiceProvider.php @@ -7,8 +7,24 @@ namespace Flasher\Notyf\Laravel; use Flasher\Laravel\Support\PluginServiceProvider; use Flasher\Notyf\Prime\NotyfPlugin; +/** + * FlasherNotyfServiceProvider - Laravel service provider for Notyf integration. + * + * This service provider registers the Notyf plugin with Laravel's service container. + * It extends the base plugin service provider to inherit common registration logic + * while providing the Notyf-specific plugin implementation. + * + * Design patterns: + * - Service Provider: Implements Laravel's service provider pattern + * - Factory Method: Creates the plugin instance + */ final class FlasherNotyfServiceProvider extends PluginServiceProvider { + /** + * Creates the Notyf plugin instance. + * + * @return NotyfPlugin The Notyf plugin instance + */ public function createPlugin(): NotyfPlugin { return new NotyfPlugin(); diff --git a/src/Notyf/Prime/.github/FUNDING.yml b/src/Notyf/Prime/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Notyf/Prime/.github/FUNDING.yml +++ b/src/Notyf/Prime/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Notyf/Prime/.phpstorm.meta.php b/src/Notyf/Prime/.phpstorm.meta.php index d2ece8f0..552285b2 100644 --- a/src/Notyf/Prime/.phpstorm.meta.php +++ b/src/Notyf/Prime/.phpstorm.meta.php @@ -1,15 +1,35 @@ \Flasher\Notyf\Prime\NotyfInterface::class])); override(\Flasher\Prime\FlasherInterface::create(), map(['notyf' => \Flasher\Notyf\Prime\NotyfInterface::class])); override(\Flasher\Prime\Container\FlasherContainer::create(), map(['flasher.notyf' => \Notyf\Notyf\Prime\NotyfInterface::class])); - diff --git a/src/Notyf/Prime/Notyf.php b/src/Notyf/Prime/Notyf.php index 66d5faf7..a3375ff5 100644 --- a/src/Notyf/Prime/Notyf.php +++ b/src/Notyf/Prime/Notyf.php @@ -7,10 +7,28 @@ namespace Flasher\Notyf\Prime; use Flasher\Prime\Factory\NotificationFactory; /** + * Notyf - Factory implementation for Notyf.js notifications. + * + * This class implements the notification factory for Notyf.js, creating + * specialized notification builders configured for Notyf's specific features. + * It serves as the primary entry point for creating Notyf notifications. + * + * Design patterns: + * - Factory: Creates specialized notification builders + * - Bridge: Connects PHPFlasher's notification system to Notyf.js + * - Composition: Delegates to NotyfBuilder for construction details + * * @mixin \Flasher\Notyf\Prime\NotyfBuilder */ final class Notyf extends NotificationFactory implements NotyfInterface { + /** + * {@inheritdoc} + * + * Creates a new Notyf-specific notification builder. + * + * @return NotyfBuilder A builder configured for Notyf.js notifications + */ public function createNotificationBuilder(): NotyfBuilder { return new NotyfBuilder('notyf', $this->storageManager); diff --git a/src/Notyf/Prime/NotyfBuilder.php b/src/Notyf/Prime/NotyfBuilder.php index 9ea750e4..46f23f2a 100644 --- a/src/Notyf/Prime/NotyfBuilder.php +++ b/src/Notyf/Prime/NotyfBuilder.php @@ -7,6 +7,18 @@ namespace Flasher\Notyf\Prime; use Flasher\Prime\Notification\NotificationBuilder; /** + * NotyfBuilder - Builder implementation for Notyf.js notifications. + * + * This class provides a fluent interface for configuring Notyf.js notifications. + * It extends the core notification builder with Notyf-specific options and + * features, focusing on Notyf's minimalist approach with fewer options than other + * notification libraries. + * + * Design patterns: + * - Builder: Provides a fluent interface for constructing complex objects + * - Fluent Interface: Methods return $this for method chaining + * - Type Safety: Uses PHPStan annotations for compile-time type checking + * * @phpstan-type NotificationType "success"|"info"|"warning"|"error" * @phpstan-type OptionsType array{ * duration?: int, @@ -22,7 +34,13 @@ use Flasher\Prime\Notification\NotificationBuilder; final class NotyfBuilder extends NotificationBuilder { /** + * Sets the notification display duration. + * * Number of milliseconds before hiding the notification. Use 0 for infinite duration. + * + * @param int $duration Duration in milliseconds + * + * @return self The builder instance */ public function duration(int $duration): self { @@ -32,7 +50,11 @@ final class NotyfBuilder extends NotificationBuilder } /** - * Whether to show the notification with a ripple effect. + * Sets whether to show the notification with a ripple effect. + * + * @param bool $ripple Whether to enable ripple effect + * + * @return self The builder instance */ public function ripple(bool $ripple = true): self { @@ -42,14 +64,18 @@ final class NotyfBuilder extends NotificationBuilder } /** + * Sets the notification position in the viewport. + * * Viewport location where notifications are rendered. * - * @param "x"|"y" $position specifies the axis: 'x' for horizontal, 'y' for vertical + * @param "x"|"y" $position Specifies the axis: 'x' for horizontal, 'y' for vertical * @param "left"|"center"|"right"|"top"|"bottom" $value Position value, dependent on the axis: - * - If $position is 'x', $value must be 'left', 'center' or 'right'. - * - If $position is 'y', $value must be 'top', 'center' or 'bottom'. + * - If $position is 'x', $value must be 'left', 'center' or 'right' + * - If $position is 'y', $value must be 'top', 'center' or 'bottom' * * @phpstan-param ($position is 'x' ? "left"|"center"|"right" : "top"|"center"|"bottom") $value + * + * @return self The builder instance */ public function position(string $position, string $value): self { @@ -62,7 +88,13 @@ final class NotyfBuilder extends NotificationBuilder } /** + * Sets whether to allow users to dismiss the notification. + * * Whether to allow users to dismiss the notification with a button. + * + * @param bool $dismissible Whether to make the notification dismissible + * + * @return self The builder instance */ public function dismissible(bool $dismissible): self { @@ -71,6 +103,13 @@ final class NotyfBuilder extends NotificationBuilder return $this; } + /** + * Sets the background color of the notification. + * + * @param string $background CSS color value for the notification background + * + * @return self The builder instance + */ public function background(string $background): self { $this->option('background', $background); diff --git a/src/Notyf/Prime/NotyfInterface.php b/src/Notyf/Prime/NotyfInterface.php index b638e7d4..73829ca7 100644 --- a/src/Notyf/Prime/NotyfInterface.php +++ b/src/Notyf/Prime/NotyfInterface.php @@ -7,6 +7,17 @@ namespace Flasher\Notyf\Prime; use Flasher\Prime\Factory\NotificationFactoryInterface; /** + * NotyfInterface - Contract for Notyf notification factories. + * + * This interface defines the contract for Notyf notification factories. + * It extends the core notification factory interface to ensure compatibility + * with PHPFlasher's notification system, while allowing IDE completion for + * Notyf-specific methods through the mixin annotation. + * + * Design patterns: + * - Interface Segregation: Provides a specific interface for Notyf functionality + * - Contract: Defines a contract for creating Notyf notifications + * * @mixin \Flasher\Notyf\Prime\NotyfBuilder */ interface NotyfInterface extends NotificationFactoryInterface diff --git a/src/Notyf/Prime/NotyfPlugin.php b/src/Notyf/Prime/NotyfPlugin.php index 1e8e8c3e..042e991c 100644 --- a/src/Notyf/Prime/NotyfPlugin.php +++ b/src/Notyf/Prime/NotyfPlugin.php @@ -6,25 +6,57 @@ namespace Flasher\Notyf\Prime; use Flasher\Prime\Plugin\Plugin; +/** + * NotyfPlugin - Plugin definition for Notyf.js integration with PHPFlasher. + * + * This class defines the core plugin configuration for the Notyf.js notification + * library integration. It specifies the required JavaScript and CSS assets, + * factory class, and service identifiers for dependency injection containers. + * + * Design patterns: + * - Plugin: Implements the plugin pattern for extending core functionality + * - Registry: Registers the plugin's assets and identifiers with the core system + * - Metadata: Provides metadata about the plugin's requirements + */ final class NotyfPlugin extends Plugin { + /** + * {@inheritdoc} + * + * Returns the plugin's unique identifier. + */ public function getAlias(): string { return 'notyf'; } + /** + * {@inheritdoc} + * + * Returns the factory class responsible for creating Notyf notifications. + */ public function getFactory(): string { return Notyf::class; } + /** + * {@inheritdoc} + * + * Returns the service alias for dependency injection containers. + */ public function getServiceAliases(): string { return NotyfInterface::class; } /** - * @return string[] + * {@inheritdoc} + * + * Returns the required JavaScript files for Notyf.js integration. + * Compared to other libraries, Notyf has a minimal footprint with just one script. + * + * @return string[] Array of script paths */ public function getScripts(): array { @@ -34,7 +66,12 @@ final class NotyfPlugin extends Plugin } /** - * @return string[] + * {@inheritdoc} + * + * Returns the required CSS files for Notyf.js styling. + * Notyf has a minimal footprint with just one stylesheet. + * + * @return string[] Array of stylesheet paths */ public function getStyles(): array { diff --git a/src/Notyf/Prime/functions.php b/src/Notyf/Prime/functions.php index c6d2986f..2d5d06d0 100644 --- a/src/Notyf/Prime/functions.php +++ b/src/Notyf/Prime/functions.php @@ -8,6 +8,15 @@ use Flasher\Prime\Container\FlasherContainer; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\Type; +/* + * Namespace-specific helper functions for Notyf notifications. + * + * This file provides a namespace-specific helper function that simplifies + * the creation of Notyf notifications from code that uses imports. + * + * Design pattern: Facade - Provides a simplified interface to a complex subsystem + */ + if (!\function_exists('Flasher\Notyf\Prime\notyf')) { /** * Creates a Notyf notification or returns the Notyf factory. diff --git a/src/Notyf/Prime/helpers.php b/src/Notyf/Prime/helpers.php index 6f2d68cf..e3af9a19 100644 --- a/src/Notyf/Prime/helpers.php +++ b/src/Notyf/Prime/helpers.php @@ -7,6 +7,15 @@ use Flasher\Prime\Container\FlasherContainer; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\Type; +/* + * Global helper functions for Notyf notifications. + * + * This file provides a global helper function that simplifies + * the creation of Notyf notifications from any PHP code. + * + * Design pattern: Facade - Provides a simplified interface to a complex subsystem + */ + if (!function_exists('notyf')) { /** * Creates a Notyf notification or returns the Notyf factory. diff --git a/src/Notyf/Symfony/.github/FUNDING.yml b/src/Notyf/Symfony/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Notyf/Symfony/.github/FUNDING.yml +++ b/src/Notyf/Symfony/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Notyf/Symfony/FlasherNotyfSymfonyBundle.php b/src/Notyf/Symfony/FlasherNotyfSymfonyBundle.php index 549ffff0..3f0b19e3 100644 --- a/src/Notyf/Symfony/FlasherNotyfSymfonyBundle.php +++ b/src/Notyf/Symfony/FlasherNotyfSymfonyBundle.php @@ -8,8 +8,24 @@ use Flasher\Notyf\Prime\NotyfPlugin; use Flasher\Prime\Plugin\PluginInterface; use Flasher\Symfony\Support\PluginBundle; +/** + * FlasherNotyfSymfonyBundle - Symfony bundle for Notyf integration. + * + * This bundle registers the Notyf plugin with Symfony's service container. + * It extends the base plugin bundle to inherit common registration logic + * while providing the Notyf-specific plugin implementation. + * + * Design patterns: + * - Bundle: Implements Symfony's bundle pattern for packaging functionality + * - Factory Method: Creates the plugin instance + */ final class FlasherNotyfSymfonyBundle extends PluginBundle // Symfony\Component\HttpKernel\Bundle\Bundle { + /** + * Creates the Notyf plugin instance. + * + * @return PluginInterface The Notyf plugin instance + */ public function createPlugin(): PluginInterface { return new NotyfPlugin(); diff --git a/src/Prime/.github/FUNDING.yml b/src/Prime/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/Prime/.github/FUNDING.yml +++ b/src/Prime/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/Prime/.phpstorm.meta.php b/src/Prime/.phpstorm.meta.php index 5f11fb88..360caf40 100644 --- a/src/Prime/.phpstorm.meta.php +++ b/src/Prime/.phpstorm.meta.php @@ -1,13 +1,36 @@ \Flasher\Prime\Factory\FlasherFactory::class, 'theme.' => \Flasher\Prime\Factory\FlasherFactory::class])); override(\Flasher\Prime\Container\FlasherContainer::create(), map(['flasher' => \Flasher\Prime\Factory\FlasherFactory::class, 'theme.' => \Flasher\Prime\Factory\FlasherFactory::class])); override(\Flasher\Prime\FlasherInterface::use(), map(['flasher' => \Flasher\Prime\Factory\FlasherFactory::class, 'theme.' => \Flasher\Prime\Factory\FlasherFactory::class])); +// Register and utilize notification type constants for better code completion registerArgumentsSet('types', 'success', 'error', 'warning', 'info'); expectedArguments(\Flasher\Prime\Notification\NotificationBuilderInterface::type(), 0, argumentsSet('types')); expectedArguments(\Flasher\Prime\Notification\NotificationBuilderInterface::addFlash(), 0, argumentsSet('types')); @@ -17,7 +40,11 @@ expectedArguments(flash(), 1, argumentsSet('types')); expectedArguments(\Flasher\Prime\flash(), 1, argumentsSet('types')); expectedReturnValues(\Flasher\Prime\Notification\NotificationInterface::getType(), argumentsSet('types')); +// Define expected arguments for handlers expectedArguments(\Flasher\Prime\Notification\NotificationBuilderInterface::handler(), 0, 'flasher', 'toastr', 'noty', 'notyf', 'sweetalert'); +// Define expected arguments for render formats expectedArguments(\Flasher\Prime\FlasherInterface::render(), 0, 'html', 'json', 'array'); + +// Define expected arguments for common option keys expectedArguments(\Flasher\Prime\Notification\FlasherBuilder::option(), 0, 'timeout', 'timeouts', 'fps', 'position', 'direction', 'rtl', 'style', 'escapeHtml'); diff --git a/src/Prime/Asset/AssetManager.php b/src/Prime/Asset/AssetManager.php index 3927e17d..1bc47e9e 100644 --- a/src/Prime/Asset/AssetManager.php +++ b/src/Prime/Asset/AssetManager.php @@ -4,17 +4,45 @@ declare(strict_types=1); namespace Flasher\Prime\Asset; +/** + * AssetManager - Manages asset files and their versioning via a manifest file. + * + * This class provides functionality for handling asset versioning through a manifest + * file that maps original asset paths to versioned paths with hash parameters. + * The asset versioning helps with cache busting by appending a content hash to the URL. + * + * Design patterns: + * - Resource Manager: Centralized management of web asset resources + * - Cache Buster: Handles versioning of assets to ensure browser cache invalidation + */ final class AssetManager implements AssetManagerInterface { /** + * Cached manifest entries mapping original paths to versioned paths. + * * @var array */ private array $entries = []; - public function __construct(private readonly string $publicDir, private readonly string $manifestPath) - { + /** + * Creates a new AssetManager instance. + * + * @param string $publicDir The public directory where assets are located + * @param string $manifestPath The path to the manifest file + */ + public function __construct( + private readonly string $publicDir, + private readonly string $manifestPath, + ) { } + /** + * {@inheritdoc} + * + * This implementation first checks for an exact match in the manifest, + * then tries with directory separators normalized, and falls back to the + * original path if no match is found. + */ public function getPath(string $path): string { $entriesData = $this->getEntriesData(); @@ -27,6 +55,16 @@ final class AssetManager implements AssetManagerInterface return array_map(fn (string $path) => $this->getPath($path), $paths); } + /** + * {@inheritdoc} + * + * This method: + * 1. Processes each file to calculate its content hash + * 2. Creates a mapping from relative paths to versioned paths + * 3. Writes the mapping to the manifest file as JSON + * + * @throws \RuntimeException If unable to write to the manifest file + */ public function createManifest(array $files): void { foreach ($files as $file) { @@ -51,7 +89,12 @@ final class AssetManager implements AssetManagerInterface /** * Loads and returns the entries from the manifest file. * - * @return array the manifest entries + * This method lazily loads the manifest data and caches it for subsequent calls. + * If no manifest file exists or it cannot be parsed, an empty array is returned. + * + * @return array The manifest entries mapping original to versioned paths + * + * @throws \InvalidArgumentException If the manifest file contains invalid JSON */ private function getEntriesData(): array { @@ -73,6 +116,16 @@ final class AssetManager implements AssetManagerInterface return $this->entries = $entries; // @phpstan-ignore-line } + /** + * Computes a hash for a file based on its contents. + * + * This method normalizes line endings to ensure consistent hashing + * across different platforms. + * + * @param string $path The path to the file + * + * @return string The MD5 hash of the file contents or empty string if file cannot be read + */ private function computeHash(string $path): string { $contents = file_get_contents($path); diff --git a/src/Prime/Asset/AssetManagerInterface.php b/src/Prime/Asset/AssetManagerInterface.php index 0afd9cf4..8cc8bd15 100644 --- a/src/Prime/Asset/AssetManagerInterface.php +++ b/src/Prime/Asset/AssetManagerInterface.php @@ -4,28 +4,49 @@ declare(strict_types=1); namespace Flasher\Prime\Asset; +/** + * AssetManagerInterface - Contract for asset management services. + * + * This interface defines the essential operations for managing web assets, + * particularly focusing on path resolution and manifest generation for versioning. + * + * Design pattern: Strategy - Defines a common interface for different asset + * management strategies that can be used interchangeably. + */ interface AssetManagerInterface { /** * Resolves the given path to its hashed version if available in the manifest. * - * @param string $path the original file path + * This method should look up the original path in a manifest of versioned assets + * and return the versioned path if found. Otherwise, it should return the original path. * - * @return string the resolved file path or the original path if not found in the manifest + * @param string $path The original file path + * + * @return string The resolved file path with version hash or the original path if not found */ public function getPath(string $path): string; /** - * @param string[] $paths + * Resolves multiple paths to their hashed versions if available. * - * @return string[] + * This is a batch version of getPath() that processes multiple paths at once. + * + * @param string[] $paths Array of original file paths + * + * @return string[] Array of resolved file paths in the same order */ public function getPaths(array $paths): array; /** - * Generates a json manifest from given files. + * Generates a JSON manifest from given files. * - * @param string[] $files array of file paths + * This method should create a mapping between original file paths and + * versioned paths (typically with content hashes) and save it to a manifest file. + * + * @param string[] $files Array of file paths to include in the manifest + * + * @throws \RuntimeException If the manifest cannot be created */ public function createManifest(array $files): void; } diff --git a/src/Prime/Configuration.php b/src/Prime/Configuration.php index 31f80038..bd5c7ec7 100644 --- a/src/Prime/Configuration.php +++ b/src/Prime/Configuration.php @@ -5,6 +5,29 @@ declare(strict_types=1); namespace Flasher\Prime; /** + * Configuration for PHPFlasher. + * + * This class provides type-safe access to PHPFlasher's configuration structure. + * It uses PHPStan annotations to define the complex nested configuration schema, + * ensuring configuration consistency and enabling IDE autocompletion. + * + * Design pattern: Type-safe Configuration - Uses PHP's type system to validate + * and document complex configuration structures. + * + * The configuration array structure contains: + * - default: The default notification adapter to use + * - main_script: Main JavaScript file path + * - scripts: Additional JavaScript files to include + * - styles: CSS stylesheets to include + * - inject_assets: Whether to auto-inject assets in responses + * - translate: Whether to translate notifications + * - excluded_paths: Paths where notifications shouldn't be displayed + * - options: Global notification options + * - filter: Default filtering criteria + * - flash_bag: Flash bag configuration for framework integration + * - presets: Notification templates/presets with type, title, message and options + * - plugins: Plugin-specific configuration with scripts, styles and options + * * @phpstan-type ConfigType array{ * default: string, * main_script?: string, @@ -20,45 +43,26 @@ namespace Flasher\Prime; * type: string, * title: string, * message: string, - * options: array, + * options: array * }>, * plugins?: array, - * }>, + * options?: array + * }> * } */ final class Configuration { /** - * @param array{ - * default: string, - * main_script?: string, - * scripts?: string[], - * styles?: string[], - * inject_assets?: bool, - * translate?: bool, - * excluded_paths?: list, - * options?: array, - * filter?: array, - * flash_bag?: false|array, - * presets?: array, - * }>, - * plugins?: array, - * }>, - * } $config + * Validates and returns the configuration array. + * + * This method serves as a type-safe wrapper around configuration arrays, + * ensuring that they match the expected schema defined in the PHPStan annotations. * * @phpstan-param ConfigType $config * - * @return ConfigType + * @phpstan-return ConfigType */ public static function from(array $config): array { diff --git a/src/Prime/Container/FlasherContainer.php b/src/Prime/Container/FlasherContainer.php index 1ce090b9..fa291d32 100644 --- a/src/Prime/Container/FlasherContainer.php +++ b/src/Prime/Container/FlasherContainer.php @@ -9,24 +9,43 @@ use Flasher\Prime\FlasherInterface; use Psr\Container\ContainerInterface; /** - * Manages and provides access to Flasher service instances using a PSR-11 compatible container. - * Allows initializing the internal container using a direct instance, a Closure, or a callable - * that returns a ContainerInterface instance. + * FlasherContainer - Service locator for PHPFlasher services using PSR-11 containers. * - * @internal + * This class provides static access to Flasher services through a PSR-11 compatible + * container. It manages a singleton instance that wraps the container and provides + * type-safe access to Flasher services. + * + * Design patterns: + * - Service Locator: Provides a centralized registry for accessing services + * - Singleton: Maintains a single, global instance of the container wrapper + * - Adapter: Adapts a PSR-11 container to provide Flasher-specific service access + * + * @internal This class is not part of the public API and may change without notice */ final class FlasherContainer { + /** + * The singleton instance of this class. + */ private static ?self $instance = null; + /** + * Private constructor to prevent direct instantiation. + * + * @param ContainerInterface|\Closure $container A ContainerInterface instance or factory + */ private function __construct(private readonly ContainerInterface|\Closure $container) { } /** - * Initializes the container with a direct ContainerInterface or a Closure/callable that resolves to one. + * Initializes the container with a PSR-11 container or a factory. * - * @param ContainerInterface|\Closure $container a ContainerInterface instance or a resolver that returns one + * This method initializes the singleton instance if it doesn't exist yet. + * It accepts either a direct ContainerInterface implementation or a closure + * that will return one when invoked. + * + * @param ContainerInterface|\Closure $container A ContainerInterface instance or factory */ public static function from(ContainerInterface|\Closure $container): void { @@ -34,7 +53,11 @@ final class FlasherContainer } /** - * Resets the container instance, effectively clearing it. + * Resets the container instance. + * + * This method clears the singleton instance, effectively removing all + * service references. Useful for testing or situations that require + * container replacement. */ public static function reset(): void { @@ -42,10 +65,17 @@ final class FlasherContainer } /** - * Creates and returns an instance of a service identified by $id. - * Throws an exception if the service is not found or does not implement the required interfaces. + * Creates and returns an instance of a Flasher service by ID. * - * @param string $id the service identifier + * This method retrieves a service from the container and ensures it + * implements the appropriate interface. It provides type-safe access + * to various Flasher services with PHPStan return type specifications. + * + * @param string $id The service identifier (e.g., 'flasher', 'flasher.toastr') + * + * @return FlasherInterface|NotificationFactoryInterface The requested service + * + * @throws \InvalidArgumentException If the service doesn't exist or has an invalid type * * @phpstan-return ($id is 'flasher' ? \Flasher\Prime\FlasherInterface : * ($id is 'flasher.noty' ? \Flasher\Noty\Prime\NotyInterface : @@ -70,11 +100,11 @@ final class FlasherContainer } /** - * Checks if the container has a service identified by $id. + * Checks if a service exists in the container. * - * @param string $id the service identifier + * @param string $id The service identifier * - * @return bool true if the service exists, false otherwise + * @return bool True if the service exists, false otherwise */ public static function has(string $id): bool { @@ -84,7 +114,12 @@ final class FlasherContainer /** * Retrieves the container, resolving it if necessary. * - * @return ContainerInterface the container instance + * If the container was provided as a factory closure, this method + * invokes it to get the actual container instance. + * + * @return ContainerInterface The resolved container instance + * + * @throws \InvalidArgumentException If the resolved container is not a ContainerInterface */ public static function getContainer(): ContainerInterface { @@ -100,9 +135,11 @@ final class FlasherContainer } /** - * Retrieves the singleton instance of FlasherContainer, throws if not initialized. + * Retrieves the singleton instance, throws if not initialized. * - * @return self the singleton instance + * @return self The singleton instance + * + * @throws \LogicException If the instance hasn't been initialized yet */ private static function getInstance(): self { diff --git a/src/Prime/EventDispatcher/Event/FilterEvent.php b/src/Prime/EventDispatcher/Event/FilterEvent.php index 7b7b5fcf..1f696165 100644 --- a/src/Prime/EventDispatcher/Event/FilterEvent.php +++ b/src/Prime/EventDispatcher/Event/FilterEvent.php @@ -8,11 +8,20 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Storage\Filter\Filter; use Flasher\Prime\Storage\Filter\FilterInterface; +/** + * FilterEvent - Event dispatched when notifications are being filtered. + * + * This event is dispatched during the filtering process, allowing listeners + * to modify the filter, the envelopes being filtered, or the filter criteria. + */ final class FilterEvent { /** - * @param Envelope[] $envelopes - * @param array $criteria + * Creates a new FilterEvent instance. + * + * @param FilterInterface $filter The filter being applied + * @param Envelope[] $envelopes The notification envelopes to filter + * @param array $criteria The filtering criteria */ public function __construct( private FilterInterface $filter, @@ -21,18 +30,32 @@ final class FilterEvent ) { } + /** + * Gets the filter being applied. + * + * @return FilterInterface The filter + */ public function getFilter(): FilterInterface { return $this->filter; } + /** + * Sets the filter to be applied. + * + * This allows listeners to replace the filter with a custom implementation. + * + * @param Filter $filter The new filter + */ public function setFilter(Filter $filter): void { $this->filter = $filter; } /** - * @return Envelope[] + * Gets the notification envelopes being filtered. + * + * @return Envelope[] The notification envelopes */ public function getEnvelopes(): array { @@ -40,7 +63,11 @@ final class FilterEvent } /** - * @param Envelope[] $envelopes + * Sets the notification envelopes to be filtered. + * + * This allows listeners to modify the collection of envelopes before filtering. + * + * @param Envelope[] $envelopes The notification envelopes to filter */ public function setEnvelopes(array $envelopes): void { @@ -48,7 +75,11 @@ final class FilterEvent } /** - * @return array + * Gets the filtering criteria. + * + * These criteria determine how notifications will be filtered. + * + * @return array The filtering criteria */ public function getCriteria(): array { diff --git a/src/Prime/EventDispatcher/Event/NotificationEvents.php b/src/Prime/EventDispatcher/Event/NotificationEvents.php index 2bd0a21d..5da86951 100644 --- a/src/Prime/EventDispatcher/Event/NotificationEvents.php +++ b/src/Prime/EventDispatcher/Event/NotificationEvents.php @@ -7,13 +7,31 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; /** - * @internal + * NotificationEvents - Collector for notification envelopes. + * + * This class provides a simple container for collecting notification envelopes + * during various system processes. It's primarily used by logging and debugging + * listeners to track which notifications were dispatched or displayed. + * + * Design pattern: Collection - Provides a way to collect and access notification + * envelopes in a consistent manner. + * + * @internal This class is not part of the public API and may change without notice */ final class NotificationEvents { - /** @var Envelope[] */ + /** + * The collected notification envelopes. + * + * @var Envelope[] + */ private array $envelopes = []; + /** + * Adds multiple notification envelopes to the collection. + * + * @param Envelope ...$notifications The notification envelopes to add + */ public function add(Envelope ...$notifications): void { foreach ($notifications as $notification) { @@ -21,13 +39,20 @@ final class NotificationEvents } } + /** + * Adds a single notification envelope to the collection. + * + * @param Envelope $notification The notification envelope to add + */ public function addEnvelope(Envelope $notification): void { $this->envelopes[] = $notification; } /** - * @return Envelope[] + * Gets all the collected notification envelopes. + * + * @return Envelope[] The collected notification envelopes */ public function getEnvelopes(): array { diff --git a/src/Prime/EventDispatcher/Event/PersistEvent.php b/src/Prime/EventDispatcher/Event/PersistEvent.php index 80f5418a..7ad892a8 100644 --- a/src/Prime/EventDispatcher/Event/PersistEvent.php +++ b/src/Prime/EventDispatcher/Event/PersistEvent.php @@ -6,17 +6,27 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * PersistEvent - Event dispatched when notifications are being persisted. + * + * This event is dispatched when notifications are about to be added to storage. + * It allows listeners to modify the notifications before they are stored. + */ final class PersistEvent { /** - * @param Envelope[] $envelopes + * Creates a new PersistEvent instance. + * + * @param Envelope[] $envelopes The notification envelopes to be persisted */ public function __construct(private array $envelopes) { } /** - * @return Envelope[] + * Gets the notification envelopes to be persisted. + * + * @return Envelope[] The notification envelopes */ public function getEnvelopes(): array { @@ -24,7 +34,11 @@ final class PersistEvent } /** - * @param Envelope[] $envelopes + * Sets the notification envelopes to be persisted. + * + * This allows listeners to filter or modify the notifications before storage. + * + * @param Envelope[] $envelopes The notification envelopes to persist */ public function setEnvelopes(array $envelopes): void { diff --git a/src/Prime/EventDispatcher/Event/PostPersistEvent.php b/src/Prime/EventDispatcher/Event/PostPersistEvent.php index 13ebd2e3..d9c50716 100644 --- a/src/Prime/EventDispatcher/Event/PostPersistEvent.php +++ b/src/Prime/EventDispatcher/Event/PostPersistEvent.php @@ -6,17 +6,27 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * PostPersistEvent - Event dispatched after notifications are persisted. + * + * This event is dispatched after notification envelopes have been added to storage. + * It allows listeners to perform actions after notifications have been successfully stored. + */ final readonly class PostPersistEvent { /** - * @param Envelope[] $envelopes + * Creates a new PostPersistEvent instance. + * + * @param Envelope[] $envelopes The notification envelopes that were persisted */ public function __construct(private array $envelopes) { } /** - * @return Envelope[] + * Gets the notification envelopes that were persisted. + * + * @return Envelope[] The persisted notification envelopes */ public function getEnvelopes(): array { diff --git a/src/Prime/EventDispatcher/Event/PostRemoveEvent.php b/src/Prime/EventDispatcher/Event/PostRemoveEvent.php index 1c86038a..3709451e 100644 --- a/src/Prime/EventDispatcher/Event/PostRemoveEvent.php +++ b/src/Prime/EventDispatcher/Event/PostRemoveEvent.php @@ -6,11 +6,20 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * PostRemoveEvent - Event dispatched after notifications are removed. + * + * This event is dispatched after notification envelopes have been removed from or + * kept in storage. It provides information about which envelopes were removed + * and which were kept. + */ final readonly class PostRemoveEvent { /** - * @param Envelope[] $envelopesToRemove - * @param Envelope[] $envelopesToKeep + * Creates a new PostRemoveEvent instance. + * + * @param Envelope[] $envelopesToRemove The notification envelopes that were removed + * @param Envelope[] $envelopesToKeep The notification envelopes that were kept */ public function __construct( private array $envelopesToRemove = [], @@ -19,7 +28,9 @@ final readonly class PostRemoveEvent } /** - * @return Envelope[] + * Gets the notification envelopes that were removed. + * + * @return Envelope[] The removed notification envelopes */ public function getEnvelopesToRemove(): array { @@ -27,7 +38,9 @@ final readonly class PostRemoveEvent } /** - * @return Envelope[] + * Gets the notification envelopes that were kept. + * + * @return Envelope[] The kept notification envelopes */ public function getEnvelopesToKeep(): array { diff --git a/src/Prime/EventDispatcher/Event/PostUpdateEvent.php b/src/Prime/EventDispatcher/Event/PostUpdateEvent.php index 79c5931a..13a3cbec 100644 --- a/src/Prime/EventDispatcher/Event/PostUpdateEvent.php +++ b/src/Prime/EventDispatcher/Event/PostUpdateEvent.php @@ -6,17 +6,27 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * PostUpdateEvent - Event dispatched after notification envelopes are updated. + * + * This event is dispatched after notification envelopes have been updated in storage. + * It allows listeners to perform actions based on the updated notifications. + */ final readonly class PostUpdateEvent { /** - * @param Envelope[] $envelopes + * Creates a new PostUpdateEvent instance. + * + * @param Envelope[] $envelopes The updated notification envelopes */ public function __construct(private array $envelopes) { } /** - * @return Envelope[] + * Gets the updated notification envelopes. + * + * @return Envelope[] The updated notification envelopes */ public function getEnvelopes(): array { diff --git a/src/Prime/EventDispatcher/Event/PresentationEvent.php b/src/Prime/EventDispatcher/Event/PresentationEvent.php index 90aa0138..3ab28786 100644 --- a/src/Prime/EventDispatcher/Event/PresentationEvent.php +++ b/src/Prime/EventDispatcher/Event/PresentationEvent.php @@ -6,11 +6,20 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * PresentationEvent - Event dispatched when notifications are being prepared for presentation. + * + * This event is dispatched during the rendering process, before notifications are + * converted to their final format. It allows listeners to modify notifications + * right before they are presented to the user (e.g., for translation or formatting). + */ final readonly class PresentationEvent { /** - * @param Envelope[] $envelopes - * @param array $context + * Creates a new PresentationEvent instance. + * + * @param Envelope[] $envelopes The notification envelopes being presented + * @param array $context Additional context for presentation */ public function __construct( private array $envelopes, @@ -19,7 +28,9 @@ final readonly class PresentationEvent } /** - * @return Envelope[] + * Gets the notification envelopes being presented. + * + * @return Envelope[] The notification envelopes */ public function getEnvelopes(): array { @@ -27,7 +38,12 @@ final readonly class PresentationEvent } /** - * @return array + * Gets the presentation context. + * + * This context provides additional information that may be useful + * for listeners during the presentation process. + * + * @return array The presentation context */ public function getContext(): array { diff --git a/src/Prime/EventDispatcher/Event/RemoveEvent.php b/src/Prime/EventDispatcher/Event/RemoveEvent.php index d02d5973..88e16131 100644 --- a/src/Prime/EventDispatcher/Event/RemoveEvent.php +++ b/src/Prime/EventDispatcher/Event/RemoveEvent.php @@ -6,22 +6,36 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * RemoveEvent - Event dispatched when notifications are being removed. + * + * This event is dispatched when notifications are about to be removed from storage. + * It allows listeners to modify which notifications should be removed and which + * should be kept. This is used for implementing hopping behavior where notifications + * can persist across multiple requests. + */ final class RemoveEvent { /** + * Notification envelopes that should be kept in storage. + * * @var Envelope[] */ private array $envelopesToKeep = []; /** - * @param Envelope[] $envelopesToRemove + * Creates a new RemoveEvent instance. + * + * @param Envelope[] $envelopesToRemove The notification envelopes initially marked for removal */ public function __construct(private array $envelopesToRemove) { } /** - * @return Envelope[] + * Gets the notification envelopes marked for removal. + * + * @return Envelope[] The notification envelopes to remove */ public function getEnvelopesToRemove(): array { @@ -29,7 +43,9 @@ final class RemoveEvent } /** - * @param Envelope[] $envelopesToRemove + * Sets the notification envelopes to be removed. + * + * @param Envelope[] $envelopesToRemove The notification envelopes to remove */ public function setEnvelopesToRemove(array $envelopesToRemove): void { @@ -37,7 +53,9 @@ final class RemoveEvent } /** - * @return Envelope[] + * Gets the notification envelopes that should be kept in storage. + * + * @return Envelope[] The notification envelopes to keep */ public function getEnvelopesToKeep(): array { @@ -45,7 +63,9 @@ final class RemoveEvent } /** - * @param Envelope[] $envelopesToKeep + * Sets the notification envelopes that should be kept in storage. + * + * @param Envelope[] $envelopesToKeep The notification envelopes to keep */ public function setEnvelopesToKeep(array $envelopesToKeep): void { diff --git a/src/Prime/EventDispatcher/Event/ResponseEvent.php b/src/Prime/EventDispatcher/Event/ResponseEvent.php index 6e2887bb..e6f42405 100644 --- a/src/Prime/EventDispatcher/Event/ResponseEvent.php +++ b/src/Prime/EventDispatcher/Event/ResponseEvent.php @@ -4,24 +4,55 @@ declare(strict_types=1); namespace Flasher\Prime\EventDispatcher\Event; +/** + * ResponseEvent - Event dispatched when a response is being prepared. + * + * This event is dispatched during the response rendering process. It allows + * listeners to modify the rendered response before it's returned to the client. + * This is particularly useful for adapting the response format or adding + * additional data. + */ final class ResponseEvent { + /** + * Creates a new ResponseEvent instance. + * + * @param mixed $response The response data that has been rendered + * @param string $presenter The name of the presenter that rendered the response + */ public function __construct( private mixed $response, private readonly string $presenter, ) { } + /** + * Gets the current response data. + * + * @return mixed The response data + */ public function getResponse(): mixed { return $this->response; } + /** + * Sets the response data. + * + * This allows listeners to modify or replace the response content. + * + * @param mixed $response The new response data + */ public function setResponse(mixed $response): void { $this->response = $response; } + /** + * Gets the name of the presenter that rendered the response. + * + * @return string The presenter name (e.g., 'html', 'json') + */ public function getPresenter(): string { return $this->presenter; diff --git a/src/Prime/EventDispatcher/Event/StoppableEventInterface.php b/src/Prime/EventDispatcher/Event/StoppableEventInterface.php index fb93d7a2..5b337506 100644 --- a/src/Prime/EventDispatcher/Event/StoppableEventInterface.php +++ b/src/Prime/EventDispatcher/Event/StoppableEventInterface.php @@ -4,7 +4,25 @@ declare(strict_types=1); namespace Flasher\Prime\EventDispatcher\Event; +/** + * StoppableEventInterface - Contract for events that can stop propagation. + * + * This interface marks an event as being able to stop propagation through the + * event dispatcher. Once an event's propagation is stopped, no further listeners + * will be called for that event. + * + * Design pattern: Circuit Breaker - Provides a mechanism to halt the normal + * flow of execution when certain conditions are met. + */ interface StoppableEventInterface { + /** + * Checks if event propagation should be stopped. + * + * This method returns true if event propagation should be stopped, + * false otherwise. + * + * @return bool True if event propagation should stop, false otherwise + */ public function isPropagationStopped(): bool; } diff --git a/src/Prime/EventDispatcher/Event/UpdateEvent.php b/src/Prime/EventDispatcher/Event/UpdateEvent.php index 27b78765..d72f60b6 100644 --- a/src/Prime/EventDispatcher/Event/UpdateEvent.php +++ b/src/Prime/EventDispatcher/Event/UpdateEvent.php @@ -6,17 +6,27 @@ namespace Flasher\Prime\EventDispatcher\Event; use Flasher\Prime\Notification\Envelope; +/** + * UpdateEvent - Event dispatched when notifications are being updated. + * + * This event is dispatched when notifications are about to be updated in storage. + * It allows listeners to modify the notifications before they are committed to storage. + */ final class UpdateEvent { /** - * @param Envelope[] $envelopes + * Creates a new UpdateEvent instance. + * + * @param Envelope[] $envelopes The notification envelopes to be updated */ public function __construct(private array $envelopes) { } /** - * @return Envelope[] + * Gets the notification envelopes to be updated. + * + * @return Envelope[] The notification envelopes */ public function getEnvelopes(): array { @@ -24,7 +34,11 @@ final class UpdateEvent } /** - * @param Envelope[] $envelopes + * Sets the notification envelopes to be updated. + * + * This allows listeners to modify which notifications will be updated. + * + * @param Envelope[] $envelopes The notification envelopes to update */ public function setEnvelopes(array $envelopes): void { diff --git a/src/Prime/EventDispatcher/EventDispatcher.php b/src/Prime/EventDispatcher/EventDispatcher.php index 46d620ad..3f8d7613 100644 --- a/src/Prime/EventDispatcher/EventDispatcher.php +++ b/src/Prime/EventDispatcher/EventDispatcher.php @@ -10,13 +10,36 @@ use Flasher\Prime\EventDispatcher\EventListener\AttachDefaultStampsListener; use Flasher\Prime\EventDispatcher\EventListener\EnvelopeRemovalListener; use Flasher\Prime\EventDispatcher\EventListener\EventListenerInterface; +/** + * EventDispatcher - Default implementation of the event dispatcher interface. + * + * This class provides the core event dispatching functionality for PHPFlasher. + * It allows registering listeners for specific events and dispatches events + * to all relevant listeners. It also comes pre-configured with the essential + * system listeners that implement core notification behaviors. + * + * Design patterns: + * - Observer: Implements the observer pattern for event notification + * - Chain of Responsibility: Passes events through a chain of listeners + */ final class EventDispatcher implements EventDispatcherInterface { /** + * Mapping of event names to their registered listeners. + * * @var array */ private array $listeners = []; + /** + * Creates a new EventDispatcher with default system listeners. + * + * This constructor automatically registers the core system listeners + * that are essential for proper notification handling: + * - EnvelopeRemovalListener: Manages notification lifetime across requests + * - AttachDefaultStampsListener: Adds required stamps to notifications + * - AddToStorageListener: Filters notifications before storage + */ public function __construct() { $this->addListener(new EnvelopeRemovalListener()); @@ -24,6 +47,15 @@ final class EventDispatcher implements EventDispatcherInterface $this->addListener(new AddToStorageListener()); } + /** + * {@inheritdoc} + * + * This implementation supports event propagation stopping for events + * that implement StoppableEventInterface. It calls each listener in + * registration order until all listeners are called or propagation is stopped. + * + * @throws \InvalidArgumentException If a listener is not callable + */ public function dispatch(object $event): object { $listeners = $this->getListeners($event::class); @@ -43,6 +75,12 @@ final class EventDispatcher implements EventDispatcherInterface return $event; } + /** + * {@inheritdoc} + * + * This implementation registers a listener for all events it declares + * interest in through its getSubscribedEvents() method. + */ public function addListener(EventListenerInterface $listener): void { foreach ((array) $listener->getSubscribedEvents() as $eventName) { @@ -50,9 +88,6 @@ final class EventDispatcher implements EventDispatcherInterface } } - /** - * @return EventListenerInterface[] - */ public function getListeners(string $eventName): array { return $this->listeners[$eventName] ?? []; diff --git a/src/Prime/EventDispatcher/EventDispatcherInterface.php b/src/Prime/EventDispatcher/EventDispatcherInterface.php index bcba6cf3..790fd1f0 100644 --- a/src/Prime/EventDispatcher/EventDispatcherInterface.php +++ b/src/Prime/EventDispatcher/EventDispatcherInterface.php @@ -6,10 +6,30 @@ namespace Flasher\Prime\EventDispatcher; use Flasher\Prime\EventDispatcher\EventListener\EventListenerInterface; +/** + * EventDispatcherInterface - Contract for event dispatching services. + * + * This interface defines the essential operations for dispatching events to + * registered listeners. It provides a simple but powerful event system where + * listeners can react to various events in the notification lifecycle. + * + * Design pattern: Observer - Defines a one-to-many dependency between objects + * where multiple observers are notified of state changes in the subject. + */ interface EventDispatcherInterface { /** - * @phpstan-template T of object + * Dispatches an event to all registered listeners. + * + * This method notifies all listeners subscribed to the event's class. + * It passes the event object to each listener for processing and returns + * the potentially modified event object. + * + * @template T of object + * + * @param T $event The event to dispatch + * + * @return T The event after it has been processed by all listeners * * @phpstan-param T $event * @@ -17,10 +37,24 @@ interface EventDispatcherInterface */ public function dispatch(object $event): object; + /** + * Registers a listener with the dispatcher. + * + * This method registers a listener that will be notified when events + * it's interested in are dispatched. + * + * @param EventListenerInterface $listener The listener to register + */ public function addListener(EventListenerInterface $listener): void; /** - * @return EventListenerInterface[] + * Gets all listeners for a specific event. + * + * This method retrieves all listeners that are registered for the given event name. + * + * @param string $eventName The event class name + * + * @return EventListenerInterface[] Array of listeners for the event */ public function getListeners(string $eventName): array; } diff --git a/src/Prime/EventDispatcher/EventListener/AddToStorageListener.php b/src/Prime/EventDispatcher/EventListener/AddToStorageListener.php index 14f66f99..d4f9327c 100644 --- a/src/Prime/EventDispatcher/EventListener/AddToStorageListener.php +++ b/src/Prime/EventDispatcher/EventListener/AddToStorageListener.php @@ -9,8 +9,26 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\UnlessStamp; use Flasher\Prime\Stamp\WhenStamp; +/** + * AddToStorageListener - Filters notifications before storage based on conditions. + * + * This listener is responsible for checking whether notifications should be stored + * based on their WhenStamp and UnlessStamp conditions. It allows for conditional + * notifications that only appear when certain criteria are met. + * + * Design patterns: + * - Filter Chain: Acts as a filter in the notification processing pipeline + * - Strategy: Applies different filtering strategies based on stamp conditions + */ final readonly class AddToStorageListener implements EventListenerInterface { + /** + * Handles persist events by filtering notifications based on conditions. + * + * This method filters out notifications that don't meet their when/unless conditions. + * + * @param PersistEvent $event The persist event + */ public function __invoke(PersistEvent $event): void { $envelopes = array_filter($event->getEnvelopes(), $this->isEligibleForStorage(...)); @@ -23,11 +41,33 @@ final readonly class AddToStorageListener implements EventListenerInterface return PersistEvent::class; } + /** + * Checks if an envelope is eligible for storage based on its conditions. + * + * An envelope is eligible if: + * - It passes its when condition (if any) + * - It passes its unless condition (if any) + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope should be stored, false otherwise + */ private function isEligibleForStorage(Envelope $envelope): bool { return $this->whenCondition($envelope) && $this->unlessCondition($envelope); } + /** + * Checks if the envelope passes its when condition. + * + * The envelope passes if: + * - It has no WhenStamp, or + * - Its WhenStamp condition evaluates to true + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope passes its when condition + */ private function whenCondition(Envelope $envelope): bool { $whenStamp = $envelope->get(WhenStamp::class); @@ -35,6 +75,17 @@ final readonly class AddToStorageListener implements EventListenerInterface return !($whenStamp instanceof WhenStamp && !$whenStamp->getCondition()); } + /** + * Checks if the envelope passes its unless condition. + * + * The envelope passes if: + * - It has no UnlessStamp, or + * - Its UnlessStamp condition evaluates to false + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope passes its unless condition + */ private function unlessCondition(Envelope $envelope): bool { $unlessStamp = $envelope->get(UnlessStamp::class); diff --git a/src/Prime/EventDispatcher/EventListener/ApplyPresetListener.php b/src/Prime/EventDispatcher/EventListener/ApplyPresetListener.php index e8655efd..9642782b 100644 --- a/src/Prime/EventDispatcher/EventListener/ApplyPresetListener.php +++ b/src/Prime/EventDispatcher/EventListener/ApplyPresetListener.php @@ -10,6 +10,16 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\PresetStamp; /** + * ApplyPresetListener - Applies preset configurations to notifications. + * + * This listener is responsible for applying predefined notification templates (presets) + * to envelopes that contain a PresetStamp. Presets allow defining common notification + * patterns that can be reused across an application. + * + * Design patterns: + * - Template Method: Applies predefined templates to notifications + * - Strategy: Uses different preset configurations based on the preset name + * * @phpstan-type PresetType array{ * type: string, * title: string, @@ -20,14 +30,20 @@ use Flasher\Prime\Stamp\PresetStamp; final readonly class ApplyPresetListener implements EventListenerInterface { /** - * @phpstan-param array $presets + * Creates a new ApplyPresetListener with the specified presets. + * + * @param array $presets Map of preset names to their configurations */ public function __construct(private array $presets) { } /** - * @throws PresetNotFoundException + * Handles persist events by applying presets to envelopes with PresetStamps. + * + * @param PersistEvent $event The persist event + * + * @throws PresetNotFoundException If a requested preset doesn't exist */ public function __invoke(PersistEvent $event): void { @@ -44,7 +60,12 @@ final readonly class ApplyPresetListener implements EventListenerInterface /** * Applies preset settings to an envelope if applicable. * - * @throws PresetNotFoundException if the preset is not found + * This method checks if the envelope has a PresetStamp and if so, applies + * the corresponding preset configuration. + * + * @param Envelope $envelope The envelope to process + * + * @throws PresetNotFoundException If the requested preset doesn't exist */ private function applyPreset(Envelope $envelope): void { @@ -63,11 +84,11 @@ final readonly class ApplyPresetListener implements EventListenerInterface } /** - * Retrieves preset data or default values if not set. + * Retrieves preset data with default values for missing fields. * - * @param string $alias the preset key + * @param string $alias The preset key * - * @phpstan-return PresetType The preset data. + * @return PresetType The preset data with defaults for missing fields */ private function getPreset(string $alias): array { @@ -83,9 +104,12 @@ final readonly class ApplyPresetListener implements EventListenerInterface /** * Updates the envelope with the provided preset data. * - * @param Envelope $envelope the envelope to be updated + * This method applies the preset data to the envelope, but only for fields + * that aren't already set. Envelope-specific settings take precedence over + * preset defaults. * - * @phpstan-param PresetType $preset The preset data to apply. + * @param Envelope $envelope The envelope to update + * @param PresetType $preset The preset data to apply */ private function updateEnvelope(Envelope $envelope, array $preset): void { diff --git a/src/Prime/EventDispatcher/EventListener/AttachDefaultStampsListener.php b/src/Prime/EventDispatcher/EventListener/AttachDefaultStampsListener.php index 26714961..2494fb60 100644 --- a/src/Prime/EventDispatcher/EventListener/AttachDefaultStampsListener.php +++ b/src/Prime/EventDispatcher/EventListener/AttachDefaultStampsListener.php @@ -14,10 +14,23 @@ use Flasher\Prime\Stamp\IdStamp; use Flasher\Prime\Stamp\PriorityStamp; /** - * Listener responsible for attaching default stamps to envelopes during persist and update events. + * AttachDefaultStampsListener - Ensures notifications have required stamps. + * + * This listener is responsible for ensuring that all notification envelopes + * have the required system stamps. These stamps provide essential functionality + * like identification, timing, and lifecycle management. + * + * Design patterns: + * - Decorator: Adds default stamps to notification envelopes + * - Template Method: Defines a standard set of stamps for all notifications */ final readonly class AttachDefaultStampsListener implements EventListenerInterface { + /** + * Handles persist and update events by attaching default stamps. + * + * @param PersistEvent|UpdateEvent $event The event to handle + */ public function __invoke(PersistEvent|UpdateEvent $event): void { foreach ($event->getEnvelopes() as $envelope) { @@ -26,6 +39,11 @@ final readonly class AttachDefaultStampsListener implements EventListenerInterfa } /** + * {@inheritdoc} + * + * This listener subscribes to both persist and update events to ensure + * that stamps are attached to notifications in both scenarios. + * * @return string[] */ public function getSubscribedEvents(): array @@ -36,6 +54,18 @@ final readonly class AttachDefaultStampsListener implements EventListenerInterfa ]; } + /** + * Attaches default stamps to an envelope if they don't already exist. + * + * The default stamps are: + * - CreatedAtStamp: Records when the notification was created + * - IdStamp: Provides a unique identifier + * - DelayStamp: Controls display timing (default: immediate) + * - HopsStamp: Controls persistence across requests (default: 1 request) + * - PriorityStamp: Controls display order (default: normal priority) + * + * @param Envelope $envelope The envelope to attach stamps to + */ private function attachStamps(Envelope $envelope): void { $envelope->withStamp(new CreatedAtStamp(), false); diff --git a/src/Prime/EventDispatcher/EventListener/EnvelopeRemovalListener.php b/src/Prime/EventDispatcher/EventListener/EnvelopeRemovalListener.php index 030b8de7..49dfd2b6 100644 --- a/src/Prime/EventDispatcher/EventListener/EnvelopeRemovalListener.php +++ b/src/Prime/EventDispatcher/EventListener/EnvelopeRemovalListener.php @@ -8,8 +8,28 @@ use Flasher\Prime\EventDispatcher\Event\RemoveEvent; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\HopsStamp; +/** + * EnvelopeRemovalListener - Manages notification lifecycle across requests. + * + * This listener is responsible for implementing the "hops" feature, which allows + * notifications to persist across multiple page loads or redirects. When a notification + * is removed, this listener checks if it has remaining hops and if so, decrements + * the hop count and keeps the notification in storage. + * + * Design patterns: + * - Chain of Responsibility: Processes each envelope and decides its fate + * - State: Manages the state transition of notifications across requests + */ final readonly class EnvelopeRemovalListener implements EventListenerInterface { + /** + * Handles remove events by categorizing envelopes into keep and remove groups. + * + * This method processes the envelopes marked for removal, checking if any should + * be kept based on their hop count. + * + * @param RemoveEvent $event The remove event + */ public function __invoke(RemoveEvent $event): void { [$envelopesToKeep, $envelopesToRemove] = $this->categorizeEnvelopes($event->getEnvelopesToRemove()); @@ -24,9 +44,15 @@ final readonly class EnvelopeRemovalListener implements EventListenerInterface } /** - * @param Envelope[] $envelopes + * Categorizes envelopes into those to keep and those to remove. * - * @return array + * For each envelope: + * - If it has a HopsStamp with a count > 1, decrement the count and keep it + * - Otherwise, mark it for removal + * + * @param Envelope[] $envelopes The envelopes to categorize + * + * @return array Array with [0] => envelopes to keep, [1] => envelopes to remove */ private function categorizeEnvelopes(array $envelopes): array { diff --git a/src/Prime/EventDispatcher/EventListener/EventListenerInterface.php b/src/Prime/EventDispatcher/EventListener/EventListenerInterface.php index bc2aec75..c24aa3d3 100644 --- a/src/Prime/EventDispatcher/EventListener/EventListenerInterface.php +++ b/src/Prime/EventDispatcher/EventListener/EventListenerInterface.php @@ -5,19 +5,44 @@ declare(strict_types=1); namespace Flasher\Prime\EventDispatcher\EventListener; /** - * Event listener interface for handling events. - * Implementers should define an __invoke method with their specific event type. + * EventListenerInterface - Contract for event listeners. * - * Example: function __invoke(MyCustomEvent $event): void + * This interface defines the essential operations for event listeners in the + * PHPFlasher event system. Implementers should define an __invoke method that + * processes events they're interested in and declare which events they listen to. * - * @method void __invoke(object $event) + * Design patterns: + * - Observer: Defines a contract for objects that observe and respond to events + * - Visitor: Allows operations to be performed on event objects + * + * Example implementation: + * ```php + * class MyListener implements EventListenerInterface + * { + * public function __invoke(MyEvent $event): void + * { + * // Handle the event + * } + * + * public function getSubscribedEvents(): string + * { + * return MyEvent::class; + * } + * } + * ``` + * + * @method void __invoke(object $event) Method that handles the event */ interface EventListenerInterface { /** * Returns a list of event names this listener wants to listen to. * - * @return string|string[] + * The returned value can be: + * - A string: Single event class name + * - An array: Multiple event class names + * + * @return string|string[] The event class name(s) this listener subscribes to */ public function getSubscribedEvents(): string|array; } diff --git a/src/Prime/EventDispatcher/EventListener/NotificationLoggerListener.php b/src/Prime/EventDispatcher/EventListener/NotificationLoggerListener.php index 4d1cc21c..ca2e13e9 100644 --- a/src/Prime/EventDispatcher/EventListener/NotificationLoggerListener.php +++ b/src/Prime/EventDispatcher/EventListener/NotificationLoggerListener.php @@ -9,26 +9,56 @@ use Flasher\Prime\EventDispatcher\Event\PersistEvent; use Flasher\Prime\EventDispatcher\Event\PresentationEvent; /** - * @internal + * NotificationLoggerListener - Logs all notifications that are dispatched and displayed. + * + * This listener keeps track of all notification envelopes that are dispatched to storage + * and those that are actually displayed to users. It's useful for debugging and testing + * to see which notifications were created vs which were actually shown. + * + * @internal This class is not part of the public API and may change without notice */ final class NotificationLoggerListener implements EventListenerInterface { + /** + * Collection of envelopes that were dispatched to storage. + */ private NotificationEvents $dispatchedEnvelopes; + /** + * Collection of envelopes that were displayed to users. + */ private NotificationEvents $displayedEnvelopes; + /** + * Creates a new NotificationLoggerListener instance. + * + * Initializes empty collections for tracking dispatched and displayed notifications. + */ public function __construct() { $this->dispatchedEnvelopes = new NotificationEvents(); $this->displayedEnvelopes = new NotificationEvents(); } + /** + * Resets the listener, clearing all tracked notifications. + * + * This is useful for testing or when you want to start with a clean slate. + */ public function reset(): void { $this->dispatchedEnvelopes = new NotificationEvents(); $this->displayedEnvelopes = new NotificationEvents(); } + /** + * Handles incoming events by delegating to specialized methods. + * + * This method implements the __invoke method required by the EventListenerInterface. + * It routes events to the appropriate handler method based on their type. + * + * @param object $event The event to handle + */ public function __invoke(object $event): void { if ($event instanceof PersistEvent) { @@ -40,21 +70,41 @@ final class NotificationLoggerListener implements EventListenerInterface } } + /** + * Handles persist events by logging the notifications being stored. + * + * @param PersistEvent $event The persist event + */ public function onPersist(PersistEvent $event): void { $this->dispatchedEnvelopes->add(...$event->getEnvelopes()); } + /** + * Handles presentation events by logging the notifications being displayed. + * + * @param PresentationEvent $event The presentation event + */ public function onPresentation(PresentationEvent $event): void { $this->displayedEnvelopes->add(...$event->getEnvelopes()); } + /** + * Gets the collection of envelopes that were dispatched to storage. + * + * @return NotificationEvents Collection of dispatched notification envelopes + */ public function getDispatchedEnvelopes(): NotificationEvents { return $this->dispatchedEnvelopes; } + /** + * Gets the collection of envelopes that were displayed to users. + * + * @return NotificationEvents Collection of displayed notification envelopes + */ public function getDisplayedEnvelopes(): NotificationEvents { return $this->displayedEnvelopes; diff --git a/src/Prime/EventDispatcher/EventListener/TranslationListener.php b/src/Prime/EventDispatcher/EventListener/TranslationListener.php index 9d166f9d..42a0f9d8 100644 --- a/src/Prime/EventDispatcher/EventListener/TranslationListener.php +++ b/src/Prime/EventDispatcher/EventListener/TranslationListener.php @@ -13,17 +13,41 @@ use Flasher\Prime\Translation\Language; use Flasher\Prime\Translation\TranslatorInterface; /** - * Listener responsible for applying translations to envelopes during presentation events based on TranslationStamps and locale settings. + * TranslationListener - Applies translations to notifications during presentation. + * + * This listener is responsible for translating notification titles and messages + * before they are displayed to users. It also sets RTL mode for right-to-left + * languages and translates preset parameters. + * + * Design patterns: + * - Decorator: Adds translation functionality to notifications + * - Strategy: Uses pluggable translation strategies via TranslatorInterface */ final readonly class TranslationListener implements EventListenerInterface { + /** + * The translator to use for translating notification content. + */ private TranslatorInterface $translator; + /** + * Creates a new TranslationListener instance. + * + * If no translator is provided, falls back to the EchoTranslator which simply + * returns the input strings unchanged. + * + * @param TranslatorInterface|null $translator The translator to use + */ public function __construct(?TranslatorInterface $translator = null) { $this->translator = $translator ?: new EchoTranslator(); } + /** + * Handles presentation events by translating all notifications. + * + * @param PresentationEvent $event The presentation event + */ public function __invoke(PresentationEvent $event): void { foreach ($event->getEnvelopes() as $envelope) { @@ -36,6 +60,17 @@ final readonly class TranslationListener implements EventListenerInterface return PresentationEvent::class; } + /** + * Translates a notification envelope. + * + * This method: + * 1. Determines the appropriate locale + * 2. Gathers translation parameters + * 3. Applies translations to title and message + * 4. Sets RTL mode if needed + * + * @param Envelope $envelope The notification envelope to translate + */ private function translateEnvelope(Envelope $envelope): void { $translationStamp = $envelope->get(TranslationStamp::class); @@ -53,7 +88,14 @@ final readonly class TranslationListener implements EventListenerInterface } /** - * @return array + * Extracts and translates parameters from preset stamps. + * + * @param Envelope $envelope The notification envelope + * @param string $locale The locale to use + * + * @return array The translated parameters + * + * @throws \InvalidArgumentException If a parameter value is not a string */ private function getParameters(Envelope $envelope, string $locale): array { @@ -76,7 +118,13 @@ final readonly class TranslationListener implements EventListenerInterface } /** - * @param array $parameters + * Applies translations to the notification title and message. + * + * If the title is empty, the notification type is used as a fallback. + * + * @param Envelope $envelope The notification envelope + * @param string $locale The locale to use + * @param array $parameters The translation parameters */ private function applyTranslations(Envelope $envelope, string $locale, array $parameters): void { diff --git a/src/Prime/Exception/CriteriaNotRegisteredException.php b/src/Prime/Exception/CriteriaNotRegisteredException.php index ddbba3e7..3ce3c284 100644 --- a/src/Prime/Exception/CriteriaNotRegisteredException.php +++ b/src/Prime/Exception/CriteriaNotRegisteredException.php @@ -4,10 +4,24 @@ declare(strict_types=1); namespace Flasher\Prime\Exception; +/** + * CriteriaNotRegisteredException - Thrown when an unregistered filter criterion is requested. + * + * This exception is thrown when attempting to use a filter criterion that hasn't been + * registered with the FilterFactory. It provides a clear error message that includes + * the requested criterion name and available criteria for debugging purposes. + * + * Design pattern: Domain-specific exception - Provides contextual information about the error. + */ final class CriteriaNotRegisteredException extends \Exception { /** - * @param string[] $availableCriteria + * Creates a new CriteriaNotRegisteredException with a descriptive message. + * + * @param string $alias The name of the criterion that was requested + * @param string[] $availableCriteria The list of registered criteria names + * + * @return self The exception instance */ public static function create(string $alias, array $availableCriteria = []): self { diff --git a/src/Prime/Exception/FactoryNotFoundException.php b/src/Prime/Exception/FactoryNotFoundException.php index fcf9793f..14efdeaa 100644 --- a/src/Prime/Exception/FactoryNotFoundException.php +++ b/src/Prime/Exception/FactoryNotFoundException.php @@ -4,10 +4,24 @@ declare(strict_types=1); namespace Flasher\Prime\Exception; +/** + * FactoryNotFoundException - Thrown when an unregistered notification factory is requested. + * + * This exception is thrown when attempting to use a notification factory that hasn't been + * registered with the system. It provides a clear error message that includes the requested + * factory name and available factories for debugging purposes. + * + * Design pattern: Domain-specific exception - Provides contextual information about the error. + */ final class FactoryNotFoundException extends \Exception { /** - * @param string[] $availableFactories + * Creates a new FactoryNotFoundException with a descriptive message. + * + * @param string $alias The name of the factory that was requested + * @param string[] $availableFactories The list of registered factory names + * + * @return self The exception instance */ public static function create(string $alias, array $availableFactories = []): self { diff --git a/src/Prime/Exception/PresenterNotFoundException.php b/src/Prime/Exception/PresenterNotFoundException.php index 0b1fbfa4..0b30db6c 100644 --- a/src/Prime/Exception/PresenterNotFoundException.php +++ b/src/Prime/Exception/PresenterNotFoundException.php @@ -4,10 +4,24 @@ declare(strict_types=1); namespace Flasher\Prime\Exception; +/** + * PresenterNotFoundException - Thrown when an unregistered presenter is requested. + * + * This exception is thrown when attempting to use a notification presenter that hasn't been + * registered with the system. It provides a clear error message that includes the requested + * presenter name and available presenters for debugging purposes. + * + * Design pattern: Domain-specific exception - Provides contextual information about the error. + */ final class PresenterNotFoundException extends \Exception { /** - * @param string[] $availablePresenters + * Creates a new PresenterNotFoundException with a descriptive message. + * + * @param string $alias The name of the presenter that was requested + * @param string[] $availablePresenters The list of registered presenter names + * + * @return self The exception instance */ public static function create(string $alias, array $availablePresenters = []): self { diff --git a/src/Prime/Exception/PresetNotFoundException.php b/src/Prime/Exception/PresetNotFoundException.php index 55a6f8af..eb5d468b 100644 --- a/src/Prime/Exception/PresetNotFoundException.php +++ b/src/Prime/Exception/PresetNotFoundException.php @@ -4,11 +4,25 @@ declare(strict_types=1); namespace Flasher\Prime\Exception; +/** + * PresetNotFoundException - Thrown when an unregistered notification preset is requested. + * + * This exception is thrown when attempting to use a notification preset that hasn't been + * registered with the system. Presets define reusable notification templates that can be + * referenced by name. The exception provides a clear error message that includes the requested + * preset name and available presets for debugging purposes. + * + * Design pattern: Domain-specific exception - Provides contextual information about the error. + */ final class PresetNotFoundException extends \Exception { /** - * @param string $preset the name of the preset that was not found - * @param string[] $availablePresets the list of available presets for reference + * Creates a new PresetNotFoundException with a descriptive message. + * + * @param string $preset The name of the preset that was not found + * @param string[] $availablePresets The list of available presets for reference + * + * @return self The exception instance */ public static function create(string $preset, array $availablePresets = []): self { diff --git a/src/Prime/Factory/FlasherFactory.php b/src/Prime/Factory/FlasherFactory.php index 618b033e..676c224e 100644 --- a/src/Prime/Factory/FlasherFactory.php +++ b/src/Prime/Factory/FlasherFactory.php @@ -8,10 +8,27 @@ use Flasher\Prime\Notification\FlasherBuilder; use Flasher\Prime\Notification\NotificationBuilderInterface; /** + * Default implementation of FlasherFactoryInterface. + * + * This factory creates FlasherBuilder instances for constructing notifications + * with the default PHPFlasher implementation. It extends the base NotificationFactory + * to inherit common factory functionality. + * + * Design pattern: Factory Method - Defines an interface for creating objects, + * but lets subclasses decide which class to instantiate. + * * @mixin \Flasher\Prime\Notification\FlasherBuilder */ final class FlasherFactory extends NotificationFactory implements FlasherFactoryInterface { + /** + * Creates a new FlasherBuilder instance. + * + * This implementation returns a FlasherBuilder configured with the current plugin name. + * The builder provides a fluent API for constructing notifications with type-safe methods. + * + * @return NotificationBuilderInterface The created notification builder + */ public function createNotificationBuilder(): NotificationBuilderInterface { return new FlasherBuilder($this->plugin ?? 'flasher', $this->storageManager); diff --git a/src/Prime/Factory/FlasherFactoryInterface.php b/src/Prime/Factory/FlasherFactoryInterface.php index 3b07d9d2..dcec1b60 100644 --- a/src/Prime/Factory/FlasherFactoryInterface.php +++ b/src/Prime/Factory/FlasherFactoryInterface.php @@ -5,6 +5,13 @@ declare(strict_types=1); namespace Flasher\Prime\Factory; /** + * Factory interface for creating Flasher-specific notification builders. + * + * This interface extends the base NotificationFactoryInterface to provide + * type safety when working specifically with the default Flasher implementation. + * It ensures that methods like createNotificationBuilder() return Flasher-specific + * builder instances. + * * @mixin \Flasher\Prime\Notification\FlasherBuilder */ interface FlasherFactoryInterface extends NotificationFactoryInterface diff --git a/src/Prime/Factory/NotificationFactory.php b/src/Prime/Factory/NotificationFactory.php index 438cd09a..eb085655 100644 --- a/src/Prime/Factory/NotificationFactory.php +++ b/src/Prime/Factory/NotificationFactory.php @@ -8,18 +8,53 @@ use Flasher\Prime\Storage\StorageManagerInterface; use Flasher\Prime\Support\Traits\ForwardsCalls; /** + * Base abstract class for notification factories. + * + * This abstract class provides common functionality for all notification factories, + * such as storage management and method forwarding to notification builders. + * Specific factory implementations should extend this class and implement + * the createNotificationBuilder() method. + * + * Design pattern: Template Method - Defines the skeleton of an algorithm, + * deferring some steps to subclasses. + * * @mixin \Flasher\Prime\Notification\NotificationBuilderInterface */ abstract class NotificationFactory implements NotificationFactoryInterface { use ForwardsCalls; - public function __construct(protected StorageManagerInterface $storageManager, protected ?string $plugin = null) - { + /** + * Creates a new NotificationFactory instance. + * + * @param StorageManagerInterface $storageManager The storage manager for persisting notifications + * @param string|null $plugin The plugin/adapter name (e.g., 'toastr', 'sweetalert') + */ + public function __construct( + protected StorageManagerInterface $storageManager, + protected ?string $plugin = null, + ) { } /** - * @param mixed[] $parameters + * Dynamically forwards method calls to a notification builder instance. + * + * This magic method allows calling notification builder methods directly on the factory, + * creating a more fluent API for client code. + * + * Example: + * ```php + * // Instead of: + * $factory->createNotificationBuilder()->success('Message'); + * + * // You can do: + * $factory->success('Message'); + * ``` + * + * @param string $method The method name to call + * @param mixed[] $parameters The parameters to pass to the method + * + * @return mixed The result of the method call */ public function __call(string $method, array $parameters): mixed { diff --git a/src/Prime/Factory/NotificationFactoryInterface.php b/src/Prime/Factory/NotificationFactoryInterface.php index cfd31c66..6c1ff807 100644 --- a/src/Prime/Factory/NotificationFactoryInterface.php +++ b/src/Prime/Factory/NotificationFactoryInterface.php @@ -7,9 +7,26 @@ namespace Flasher\Prime\Factory; use Flasher\Prime\Notification\NotificationBuilderInterface; /** + * NotificationFactoryInterface - Core factory abstraction. + * + * Defines the contract for all notification factories that can create + * notification builders. This interface enables the system to work + * with different notification implementations through a common API. + * + * Design pattern: Abstract Factory - Provides an interface for creating + * families of related objects without specifying concrete classes. + * * @mixin \Flasher\Prime\Notification\NotificationBuilderInterface */ interface NotificationFactoryInterface { + /** + * Creates a notification builder instance. + * + * Each implementation of this method should return a builder tailored + * to the specific notification library or system that the factory supports. + * + * @return NotificationBuilderInterface A builder for constructing notifications + */ public function createNotificationBuilder(): NotificationBuilderInterface; } diff --git a/src/Prime/Factory/NotificationFactoryLocator.php b/src/Prime/Factory/NotificationFactoryLocator.php index 7f82b8c1..11280f01 100644 --- a/src/Prime/Factory/NotificationFactoryLocator.php +++ b/src/Prime/Factory/NotificationFactoryLocator.php @@ -6,13 +6,37 @@ namespace Flasher\Prime\Factory; use Flasher\Prime\Exception\FactoryNotFoundException; +/** + * NotificationFactoryLocator - Registry of available factories. + * + * Maintains a registry of all available notification factories, allowing + * the system to look up the appropriate factory by name at runtime. + * Factories can be registered as instances or lazy-loaded through callbacks. + * + * Design pattern: Service Locator - Centralizes factory discovery and + * instantiation, enabling loose coupling between components. + */ final class NotificationFactoryLocator implements NotificationFactoryLocatorInterface { /** + * Map of factory aliases to instances or factory callbacks. + * * @var array */ private array $factories = []; + /** + * Gets a notification factory by its identifier. + * + * If the factory was registered as a callback, it will be invoked to create + * the actual factory instance on first access. + * + * @param string $id The identifier for the factory to retrieve + * + * @return NotificationFactoryInterface The requested notification factory + * + * @throws FactoryNotFoundException If no factory is registered with the given identifier + */ public function get(string $id): NotificationFactoryInterface { if (!$this->has($id)) { @@ -24,6 +48,13 @@ final class NotificationFactoryLocator implements NotificationFactoryLocatorInte return \is_callable($factory) ? $factory() : $factory; } + /** + * Checks if a notification factory exists for the given identifier. + * + * @param string $id The identifier to check + * + * @return bool True if a factory exists for the given identifier, false otherwise + */ public function has(string $id): bool { return \array_key_exists($id, $this->factories); @@ -31,6 +62,23 @@ final class NotificationFactoryLocator implements NotificationFactoryLocatorInte /** * Register a custom notification factory. + * + * This method allows registering either a factory instance directly or a callback + * that creates the factory when needed (lazy-loading). + * + * Example: + * ```php + * // Register a factory instance + * $locator->addFactory('custom', new CustomFactory($storageManager)); + * + * // Register a factory with lazy-loading + * $locator->addFactory('custom', function() use ($storageManager) { + * return new ExpensiveFactory($storageManager); + * }); + * ``` + * + * @param string $alias The identifier for the factory + * @param callable|NotificationFactoryInterface $factory The factory instance or a callback that returns one */ public function addFactory(string $alias, callable|NotificationFactoryInterface $factory): void { diff --git a/src/Prime/Factory/NotificationFactoryLocatorInterface.php b/src/Prime/Factory/NotificationFactoryLocatorInterface.php index 1e0d06b4..8f18a94d 100644 --- a/src/Prime/Factory/NotificationFactoryLocatorInterface.php +++ b/src/Prime/Factory/NotificationFactoryLocatorInterface.php @@ -4,11 +4,37 @@ declare(strict_types=1); namespace Flasher\Prime\Factory; +/** + * Interface for the notification factory locator service. + * + * This interface defines the contract for a service that locates and + * provides notification factories by their identifiers. It allows the + * system to resolve factories at runtime without directly coupling to + * specific implementations. + * + * Design pattern: Service Locator - Provides a centralized registry + * for finding and accessing services. + */ interface NotificationFactoryLocatorInterface { + /** + * Checks if a notification factory exists for the given identifier. + * + * @param string $id The identifier to check + * + * @return bool True if a factory exists for the given identifier, false otherwise + */ public function has(string $id): bool; /** + * Gets a notification factory by its identifier. + * + * @param string $id The identifier for the factory to retrieve + * + * @return NotificationFactoryInterface The requested notification factory + * + * @throws \Flasher\Prime\Exception\FactoryNotFoundException If no factory is registered with the given identifier + * * @phpstan-return ($id is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface : * ($id is 'noty' ? \Flasher\Noty\Prime\NotyInterface : * ($id is 'notyf' ? \Flasher\Notyf\Prime\NotyfInterface : diff --git a/src/Prime/Flasher.php b/src/Prime/Flasher.php index 2757ce93..87162f86 100644 --- a/src/Prime/Flasher.php +++ b/src/Prime/Flasher.php @@ -11,12 +11,34 @@ use Flasher\Prime\Response\ResponseManagerInterface; use Flasher\Prime\Storage\StorageManagerInterface; use Flasher\Prime\Support\Traits\ForwardsCalls; +/** + * Core implementation of the FlasherInterface. + * + * This class serves as the central hub for the notification system, managing + * factories, storage, and response handling. It implements the Façade pattern + * to provide a clean, simple API for client code while coordinating complex + * subsystems behind the scenes. + * + * Design pattern: Façade - Provides a simplified interface to a complex system, + * delegating to specialized components for specific functionality. + */ final readonly class Flasher implements FlasherInterface { use ForwardsCalls; + /** + * Current version of PHPFlasher. + */ public const VERSION = '2.1.6'; + /** + * Creates a new Flasher instance. + * + * @param string $default The default factory to use when none is specified + * @param NotificationFactoryLocatorInterface $factoryLocator Service locator for notification factories + * @param ResponseManagerInterface $responseManager Manager for rendering notifications + * @param StorageManagerInterface $storageManager Manager for storing notifications + */ public function __construct( private string $default, private NotificationFactoryLocatorInterface $factoryLocator, @@ -25,6 +47,13 @@ final readonly class Flasher implements FlasherInterface ) { } + /** + * {@inheritdoc} + * + * This implementation tries to resolve the requested factory from the + * factory locator. If not found, it falls back to creating a generic + * FlasherFactory with the given alias. + */ public function use(?string $alias): NotificationFactoryInterface { $alias = trim($alias ?: $this->default); @@ -40,20 +69,46 @@ final readonly class Flasher implements FlasherInterface return new FlasherFactory($this->storageManager, $alias); } + /** + * {@inheritdoc} + * + * This is an alias for the use() method. + */ public function create(?string $alias): NotificationFactoryInterface { return $this->use($alias); } + /** + * {@inheritdoc} + * + * Delegates rendering to the response manager. + */ public function render(string $presenter = 'html', array $criteria = [], array $context = []): mixed { return $this->responseManager->render($presenter, $criteria, $context); } /** - * Dynamically call the default factory instance. + * Dynamically call methods on the default factory instance. * - * @param mixed[] $parameters + * This magic method enables using the Flasher instance directly with + * notification methods like success(), error(), etc., without explicitly + * calling use() first. + * + * Example: + * ```php + * // Instead of: + * $flasher->use('flasher')->success('Message'); + * + * // You can do: + * $flasher->success('Message'); + * ``` + * + * @param string $method The method name to call + * @param mixed[] $parameters The parameters to pass to the method + * + * @return mixed The result of the method call */ public function __call(string $method, array $parameters): mixed { diff --git a/src/Prime/FlasherInterface.php b/src/Prime/FlasherInterface.php index 86c3ae16..b21b920a 100644 --- a/src/Prime/FlasherInterface.php +++ b/src/Prime/FlasherInterface.php @@ -8,6 +8,15 @@ use Flasher\Prime\Factory\NotificationFactoryInterface; use Flasher\Prime\Response\Presenter\ArrayPresenter; /** + * FlasherInterface - The primary entry point to the notification system. + * + * This interface defines the contract for the notification service, + * providing methods to access notification factories and render + * notifications. It's the main touchpoint for client code. + * + * Design pattern: Façade Pattern - Provides a simplified interface + * to the complex notification subsystems. + * * @mixin \Flasher\Prime\Notification\NotificationBuilder * * @phpstan-import-type ArrayPresenterType from ArrayPresenter @@ -15,9 +24,20 @@ use Flasher\Prime\Response\Presenter\ArrayPresenter; interface FlasherInterface { /** - * Get a notification factory instance. + * Get a notification factory instance by its alias. * - * @throws \InvalidArgumentException + * This method provides access to specific notification factories (like Toastr, SweetAlert, etc.) + * through a unified interface. It allows you to use specialized notification features + * while maintaining a consistent API. + * + * Example: + * ```php + * $flasher->use('toastr')->success('Message using Toastr library'); + * ``` + * + * @param string $alias The alias of the factory to retrieve (e.g., 'toastr', 'sweetalert') + * + * @throws \InvalidArgumentException When the requested factory cannot be resolved * * @phpstan-return ($alias is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface : * ($alias is 'noty' ? \Flasher\Noty\Prime\NotyInterface : @@ -29,9 +49,19 @@ interface FlasherInterface public function use(string $alias): NotificationFactoryInterface; /** - * Get a notification factory instance. + * Get a notification factory instance by its alias (alias for use()). * - * @throws \InvalidArgumentException + * This method is identical to use() but provides a more intuitive name + * for creating new notification factories. + * + * Example: + * ```php + * $flasher->create('sweetalert')->success('Message using SweetAlert library'); + * ``` + * + * @param string $alias The alias of the factory to retrieve (e.g., 'toastr', 'sweetalert') + * + * @throws \InvalidArgumentException When the requested factory cannot be resolved * * @phpstan-return ($alias is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface : * ($alias is 'noty' ? \Flasher\Noty\Prime\NotyInterface : @@ -45,9 +75,27 @@ interface FlasherInterface /** * Renders the flash notifications based on the specified criteria, presenter, and context. * - * @param array $criteria the criteria to filter the notifications - * @param string|"html"|"json" $presenter The presenter format for rendering the notifications (e.g., 'html', 'json'). - * @param array $context additional context or options for rendering + * This method retrieves notifications from storage and formats them for display. + * Different presenter formats can be specified (html, json, array) to support + * various output requirements. + * + * Example: + * ```php + * // Render as HTML + * $html = $flasher->render('html'); + * + * // Render as JSON (for API responses) + * $json = $flasher->render('json'); + * + * // Render with filtering criteria + * $errors = $flasher->render('html', ['type' => 'error']); + * ``` + * + * @param string $presenter The format to render notifications in ('html', 'json', 'array') + * @param array $criteria Optional filtering criteria for notifications + * @param array $context Additional context or options for rendering + * + * @return mixed The rendered notifications in the requested format * * @phpstan-return ($presenter is 'html' ? string : * ($presenter is 'array' ? ArrayPresenterType : diff --git a/src/Prime/Http/Csp/ContentSecurityPolicyHandler.php b/src/Prime/Http/Csp/ContentSecurityPolicyHandler.php index 041255c6..6907fa8a 100644 --- a/src/Prime/Http/Csp/ContentSecurityPolicyHandler.php +++ b/src/Prime/Http/Csp/ContentSecurityPolicyHandler.php @@ -7,13 +7,40 @@ namespace Flasher\Prime\Http\Csp; use Flasher\Prime\Http\RequestInterface; use Flasher\Prime\Http\ResponseInterface; +/** + * ContentSecurityPolicyHandler - Manages Content Security Policy for PHPFlasher. + * + * This class handles the complex task of managing Content Security Policy (CSP) headers + * to allow PHPFlasher's JavaScript and CSS to run securely. It generates nonces for + * scripts and styles, modifies CSP headers to include these nonces, and ensures that + * inline code can execute without being blocked by CSP. + * + * Design patterns: + * - Strategy: Implements a specific approach to CSP handling + * - Adapter: Works with different request and response implementations + */ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandlerInterface { + /** + * Header for passing script nonce between components. + */ private const SCRIPT_NONCE_HEADER = 'X-PHPFlasher-Script-Nonce'; + + /** + * Header for passing style nonce between components. + */ private const STYLE_NONCE_HEADER = 'X-PHPFlasher-Style-Nonce'; + /** + * Whether CSP handling is disabled. + */ private bool $cspDisabled = false; + /** + * Creates a new ContentSecurityPolicyHandler instance. + * + * @param NonceGeneratorInterface $nonceGenerator The generator for creating secure nonces + */ public function __construct(private readonly NonceGeneratorInterface $nonceGenerator) { } @@ -62,7 +89,12 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler /** * Returns nonces from headers if existing, otherwise null. * - * @return array{csp_script_nonce: ?string, csp_style_nonce: ?string}|null + * This method checks if the necessary nonce headers are present in the provided + * request or response object and returns their values if found. + * + * @param RequestInterface|ResponseInterface $object The object to check for nonce headers + * + * @return array{csp_script_nonce: ?string, csp_style_nonce: ?string}|null Nonce values or null if not found */ private function getHeaderNonces(RequestInterface|ResponseInterface $object): ?array { @@ -76,12 +108,27 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler return null; } + /** + * Removes temporary nonce headers from the response. + * + * These headers are used internally to pass nonces between components but + * should not be sent to the client. + * + * @param ResponseInterface $response The response to clean + */ private function cleanHeaders(ResponseInterface $response): void { $response->removeHeader(self::SCRIPT_NONCE_HEADER); $response->removeHeader(self::STYLE_NONCE_HEADER); } + /** + * Removes all CSP headers from the response. + * + * This is used when CSP handling is disabled to ensure no CSP restrictions are applied. + * + * @param ResponseInterface $response The response to modify + */ private function removeCspHeaders(ResponseInterface $response): void { $response->removeHeader('X-Content-Security-Policy'); @@ -92,9 +139,13 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler /** * Updates Content-Security-Policy headers in a response. * - * @param array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} $nonces + * This method modifies existing CSP headers to include nonces for PHPFlasher's + * JavaScript and CSS, allowing them to execute without being blocked by CSP. * - * @return array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} + * @param ResponseInterface $response The response to modify + * @param array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} $nonces The nonces to add to CSP + * + * @return array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} The nonces used */ private function updateCspHeaders(ResponseInterface $response, array $nonces = []): array { @@ -153,6 +204,8 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler /** * Generates a valid Content-Security-Policy nonce. + * + * @return string The generated nonce */ private function generateNonce(): string { @@ -160,9 +213,11 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler } /** - * Converts a directive set array into Content-Security-Policy header. + * Converts a directive set array into Content-Security-Policy header value. * - * @param array $directives + * @param array $directives The CSP directives to convert + * + * @return string The formatted CSP header value */ private function generateCspHeader(array $directives): string { @@ -172,7 +227,9 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler /** * Converts a Content-Security-Policy header value into a directive set array. * - * @return array + * @param string|null $header The CSP header value to parse + * + * @return array The parsed directive set */ private function parseDirectives(?string $header): array { @@ -191,9 +248,15 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler } /** - * Detects if the 'unsafe-inline' is prevented for a directive within the directive set. + * Detects if the 'unsafe-inline' is permitted for a directive within the directive set. * - * @param array $directivesSet + * This method checks if a specific CSP directive allows inline scripts or styles, + * taking into account CSP level 2+ hash and nonce exceptions. + * + * @param array $directivesSet The directives to check + * @param string $type The directive type to check + * + * @return bool True if inline content is permitted */ private function authorizesInline(array $directivesSet, string $type): bool { @@ -207,7 +270,14 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler } /** - * @param string[] $directives + * Checks if a directive list contains hash or nonce restrictions. + * + * This is important for CSP processing because in CSP Level 2+, the presence of a + * hash or nonce invalidates 'unsafe-inline' even if it's present. + * + * @param string[] $directives The CSP directive values to check + * + * @return bool True if the directive list contains a hash or nonce restriction */ private function hasHashOrNonce(array $directives): bool { @@ -227,9 +297,15 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler } /** - * @param array $directiveSet + * Finds the fallback directive for a specific directive type. * - * @return string[]|null + * In CSP, if a specific directive isn't defined, browsers fall back to default-src. + * This method implements that behavior for the PHPFlasher CSP handler. + * + * @param array $directiveSet The complete directive set + * @param string $type The directive type to find a fallback for + * + * @return string[]|null The fallback directive values, or null if no fallback exists */ private function getDirectiveFallback(array $directiveSet, string $type): ?array { @@ -242,14 +318,18 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler } /** - * Retrieves the Content-Security-Policy headers (either X-Content-Security-Policy or Content-Security-Policy) from - * a response. + * Retrieves the Content-Security-Policy headers from a response. + * + * This method extracts all CSP-related headers from the response + * and parses their values into directive sets. + * + * @param ResponseInterface $response The response to extract CSP headers from * * @return array{ * Content-Security-Policy?: array, * Content-Security-Policy-Report-Only?: array, * X-Content-Security-Policy?: array, - * } + * } Mapped CSP headers and their parsed directive sets */ private function getCspHeaders(ResponseInterface $response): array { diff --git a/src/Prime/Http/Csp/ContentSecurityPolicyHandlerInterface.php b/src/Prime/Http/Csp/ContentSecurityPolicyHandlerInterface.php index 12c26b78..3ba48974 100644 --- a/src/Prime/Http/Csp/ContentSecurityPolicyHandlerInterface.php +++ b/src/Prime/Http/Csp/ContentSecurityPolicyHandlerInterface.php @@ -7,30 +7,49 @@ namespace Flasher\Prime\Http\Csp; use Flasher\Prime\Http\RequestInterface; use Flasher\Prime\Http\ResponseInterface; +/** + * ContentSecurityPolicyHandlerInterface - Contract for handling Content Security Policy. + * + * This interface defines operations for managing Content Security Policy (CSP) headers + * and nonces, which are necessary for securely injecting JavaScript and CSS into HTML pages. + * It ensures that PHPFlasher's injected content doesn't violate the site's CSP. + * + * Design pattern: Strategy - Defines a common interface for CSP handling strategies. + */ interface ContentSecurityPolicyHandlerInterface { /** - * Returns an array of nonces to be used in html templates and Content-Security-Policy headers. + * Returns an array of nonces to be used in HTML templates and Content-Security-Policy headers. * - * Nonce can be provided by; + * Nonce can be provided by: * - The request - In case HTML content is fetched via AJAX and inserted in DOM, it must use the same nonce as origin * - The response - A call to getNonces() has already been done previously. Same nonce are returned * - They are otherwise randomly generated * - * @return array{csp_script_nonce: ?string, csp_style_nonce: ?string} + * @param RequestInterface $request The current request + * @param ResponseInterface|null $response The current response (optional) + * + * @return array{csp_script_nonce: ?string, csp_style_nonce: ?string} Array with script and style nonces */ public function getNonces(RequestInterface $request, ?ResponseInterface $response = null): array; /** * Disables Content-Security-Policy. * - * All related headers will be removed. + * All related headers will be removed. This is useful in environments where CSP + * is not needed or when debugging. */ public function disableCsp(): void; /** * Cleanup temporary headers and updates Content-Security-Policy headers. * + * This method modifies CSP headers in the response to allow PHPFlasher's + * JavaScript and CSS to execute without being blocked by CSP. + * + * @param RequestInterface $request The current request + * @param ResponseInterface $response The current response + * * @return array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} Nonces used in Content-Security-Policy header */ public function updateResponseHeaders(RequestInterface $request, ResponseInterface $response): array; diff --git a/src/Prime/Http/Csp/NonceGenerator.php b/src/Prime/Http/Csp/NonceGenerator.php index 2633d5d1..e80ca617 100644 --- a/src/Prime/Http/Csp/NonceGenerator.php +++ b/src/Prime/Http/Csp/NonceGenerator.php @@ -4,8 +4,24 @@ declare(strict_types=1); namespace Flasher\Prime\Http\Csp; +/** + * NonceGenerator - Default implementation for generating CSP nonces. + * + * This class provides a straightforward implementation for generating + * cryptographically secure nonces using PHP's random_bytes function. + * The nonces are encoded as hexadecimal strings for compatibility with + * HTML attributes and CSP headers. + * + * Design pattern: Strategy - Implements a specific nonce generation strategy. + */ final readonly class NonceGenerator implements NonceGeneratorInterface { + /** + * {@inheritdoc} + * + * This implementation generates a 16-byte (128-bit) random value and + * encodes it as a hexadecimal string. + */ public function generate(): string { return bin2hex(random_bytes(16)); diff --git a/src/Prime/Http/Csp/NonceGeneratorInterface.php b/src/Prime/Http/Csp/NonceGeneratorInterface.php index e45336a7..d004882e 100644 --- a/src/Prime/Http/Csp/NonceGeneratorInterface.php +++ b/src/Prime/Http/Csp/NonceGeneratorInterface.php @@ -4,7 +4,24 @@ declare(strict_types=1); namespace Flasher\Prime\Http\Csp; +/** + * NonceGeneratorInterface - Contract for generating CSP nonces. + * + * This interface defines the essential operation for generating secure nonces + * (number used once) for Content Security Policy purposes. Nonces are used to + * whitelist specific inline scripts and styles in a CSP-protected environment. + * + * Design pattern: Strategy - Allows for different implementations of nonce generation. + */ interface NonceGeneratorInterface { + /** + * Generates a cryptographically secure nonce. + * + * The generated nonce should be suitable for use in Content-Security-Policy headers + * and should provide sufficient entropy to be secure. + * + * @return string The generated nonce + */ public function generate(): string; } diff --git a/src/Prime/Http/RequestExtension.php b/src/Prime/Http/RequestExtension.php index 26e3b063..7d7c80e6 100644 --- a/src/Prime/Http/RequestExtension.php +++ b/src/Prime/Http/RequestExtension.php @@ -6,21 +6,44 @@ namespace Flasher\Prime\Http; use Flasher\Prime\FlasherInterface; +/** + * RequestExtension - Converts framework flash messages to PHPFlasher notifications. + * + * This class is responsible for extracting flash messages from request objects + * (typically stored in the session) and converting them to PHPFlasher notifications. + * It provides a bridge between framework-specific flash messaging systems and + * PHPFlasher's notification system. + * + * Design patterns: + * - Adapter: Adapts framework flash message systems to PHPFlasher + * - Mediator: Coordinates the interaction between framework and PHPFlasher + */ final readonly class RequestExtension implements RequestExtensionInterface { /** + * Flattened mapping from flash message keys to notification types. + * * @var array */ private array $mapping; /** - * @param array $mapping + * Creates a new RequestExtension instance. + * + * @param FlasherInterface $flasher The flasher service for creating notifications + * @param array $mapping Mapping from framework flash types to PHPFlasher types */ public function __construct(private FlasherInterface $flasher, array $mapping = []) { $this->mapping = $this->flatMapping($mapping); } + /** + * {@inheritdoc} + * + * This method processes the request to extract flash messages and convert them + * to PHPFlasher notifications. It only processes requests with active sessions. + */ public function flash(RequestInterface $request, ResponseInterface $response): ResponseInterface { if (!$request->hasSession()) { @@ -33,9 +56,14 @@ final readonly class RequestExtension implements RequestExtensionInterface } /** - * @param array $mapping + * Flattens a nested mapping array to a simple key-value mapping. * - * @return array + * Converts a mapping like ['success' => ['success', 'ok']] to + * ['success' => 'success', 'ok' => 'success']. + * + * @param array $mapping The nested mapping array + * + * @return array The flattened mapping */ private function flatMapping(array $mapping): array { @@ -51,7 +79,12 @@ final readonly class RequestExtension implements RequestExtensionInterface } /** - * Process the request and flash messages. + * Processes the request and converts flash messages to notifications. + * + * This method checks for each flash message type in the request and creates + * corresponding PHPFlasher notifications for any that are found. + * + * @param RequestInterface $request The request to process */ private function processRequest(RequestInterface $request): void { diff --git a/src/Prime/Http/RequestExtensionInterface.php b/src/Prime/Http/RequestExtensionInterface.php index 73c2a697..fbabf9e3 100644 --- a/src/Prime/Http/RequestExtensionInterface.php +++ b/src/Prime/Http/RequestExtensionInterface.php @@ -4,7 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Http; +/** + * RequestExtensionInterface - Contract for request-related notification integrations. + * + * This interface defines the essential operations for integrating PHPFlasher with + * HTTP request objects from various frameworks. Implementations can extract flash messages + * from request objects (typically from session flash bags) and convert them to + * PHPFlasher notifications. + * + * Design pattern: Adapter - Provides a common interface for working with different + * framework request objects. + */ interface RequestExtensionInterface { + /** + * Processes flash messages from the request and converts them to notifications. + * + * This method should extract flash messages from the request (typically from session + * flash bags), convert them to PHPFlasher notifications, and return the potentially + * modified response. + * + * @param RequestInterface $request The framework-specific request wrapper + * @param ResponseInterface $response The framework-specific response wrapper + * + * @return ResponseInterface The potentially modified response + */ public function flash(RequestInterface $request, ResponseInterface $response): ResponseInterface; } diff --git a/src/Prime/Http/RequestInterface.php b/src/Prime/Http/RequestInterface.php index 307681a8..b8e91714 100644 --- a/src/Prime/Http/RequestInterface.php +++ b/src/Prime/Http/RequestInterface.php @@ -5,30 +5,89 @@ declare(strict_types=1); namespace Flasher\Prime\Http; /** - * @method string getUri() + * RequestInterface - Adapter interface for HTTP requests from different frameworks. + * + * This interface defines a common contract for request wrappers that adapt framework-specific + * request objects to work with PHPFlasher. It provides methods for querying request properties + * and accessing flash messages in a consistent way across different frameworks. + * + * Design pattern: Adapter - Provides a common interface for working with different + * framework request objects. + * + * @method string getUri() Gets the URI of the request (optional method, used for path exclusion) */ interface RequestInterface { - // public function getUri(): string; - + /** + * Checks if the request was made via XMLHttpRequest (AJAX). + * + * @return bool True if the request is an AJAX request + */ public function isXmlHttpRequest(): bool; + /** + * Checks if the request expects an HTML response. + * + * This is determined by examining the Accept header or request format. + * + * @return bool True if the request expects HTML + */ public function isHtmlRequestFormat(): bool; + /** + * Checks if the request has an associated session. + * + * @return bool True if a session is available + */ public function hasSession(): bool; + /** + * Checks if the session has been started. + * + * @return bool True if the session is active + */ public function isSessionStarted(): bool; + /** + * Checks if the flash bag contains messages of the specified type. + * + * @param string $type The flash message type + * + * @return bool True if messages exist for the type + */ public function hasType(string $type): bool; /** - * @return string|string[] + * Gets flash messages of the specified type. + * + * @param string $type The flash message type + * + * @return string|string[] The flash message(s) of the specified type */ public function getType(string $type): string|array; + /** + * Removes flash messages of the specified type. + * + * @param string $type The flash message type to remove + */ public function forgetType(string $type): void; + /** + * Checks if the request has the specified header. + * + * @param string $key The header name + * + * @return bool True if the header exists + */ public function hasHeader(string $key): bool; + /** + * Gets the value of the specified header. + * + * @param string $key The header name + * + * @return string|null The header value, or null if it doesn't exist + */ public function getHeader(string $key): ?string; } diff --git a/src/Prime/Http/ResponseExtension.php b/src/Prime/Http/ResponseExtension.php index 8c61572a..e7536e65 100644 --- a/src/Prime/Http/ResponseExtension.php +++ b/src/Prime/Http/ResponseExtension.php @@ -8,10 +8,26 @@ use Flasher\Prime\FlasherInterface; use Flasher\Prime\Http\Csp\ContentSecurityPolicyHandlerInterface; use Flasher\Prime\Response\Presenter\HtmlPresenter; +/** + * ResponseExtension - Injects notification HTML into HTTP responses. + * + * This class is responsible for injecting the HTML for notifications into HTTP responses. + * It handles the placement of notification code at appropriate injection points in HTML + * responses, manages Content Security Policy headers, and ensures that notifications are + * only injected in suitable responses. + * + * Design patterns: + * - Decorator: Enhances HTTP responses by adding notification HTML + * - Chain of Responsibility: Determines whether each response is suitable for modification + */ final readonly class ResponseExtension implements ResponseExtensionInterface { /** - * @param list $excludedPaths + * Creates a new ResponseExtension instance. + * + * @param FlasherInterface $flasher The flasher service for rendering notifications + * @param ContentSecurityPolicyHandlerInterface $cspHandler The CSP handler for managing security headers + * @param list $excludedPaths Regex patterns for paths where notifications shouldn't be rendered */ public function __construct( private FlasherInterface $flasher, @@ -20,6 +36,16 @@ final readonly class ResponseExtension implements ResponseExtensionInterface ) { } + /** + * {@inheritdoc} + * + * This method: + * 1. Checks if the response is eligible for notification injection + * 2. Finds an appropriate insertion point in the HTML + * 3. Updates CSP headers if needed + * 4. Renders the notification HTML + * 5. Injects the rendered HTML into the response + */ public function render(RequestInterface $request, ResponseInterface $response): ResponseInterface { if (!$this->isRenderable($request, $response)) { @@ -73,6 +99,24 @@ final readonly class ResponseExtension implements ResponseExtensionInterface return $response; } + /** + * Determines if a response is eligible for notification rendering. + * + * A response is renderable if: + * - The request path is not excluded + * - It's not an AJAX request + * - It's an HTML request format + * - The response is HTML + * - The response is successful (2XX) + * - It's not a redirection + * - It's not an attachment + * - It's not JSON + * + * @param RequestInterface $request The request + * @param ResponseInterface $response The response + * + * @return bool True if notifications should be rendered in this response + */ private function isRenderable(RequestInterface $request, ResponseInterface $response): bool { return !$this->isPathExcluded($request) @@ -85,6 +129,13 @@ final readonly class ResponseExtension implements ResponseExtensionInterface && !$response->isJson(); } + /** + * Checks if the current request path is excluded from notification rendering. + * + * @param RequestInterface $request The request + * + * @return bool True if the path is excluded + */ private function isPathExcluded(RequestInterface $request): bool { if (!method_exists($request, 'getUri')) { // @phpstan-ignore-line diff --git a/src/Prime/Http/ResponseExtensionInterface.php b/src/Prime/Http/ResponseExtensionInterface.php index 1841e0f9..cff32e1a 100644 --- a/src/Prime/Http/ResponseExtensionInterface.php +++ b/src/Prime/Http/ResponseExtensionInterface.php @@ -4,15 +4,28 @@ declare(strict_types=1); namespace Flasher\Prime\Http; +/** + * ResponseExtensionInterface - Contract for response modification to include notifications. + * + * This interface defines the essential operation for integrating PHPFlasher notifications + * into HTTP responses. Implementations are responsible for injecting the rendered notification + * HTML into appropriate locations in the response content. + * + * Design pattern: Adapter - Provides a common interface for working with different + * framework response objects. + */ interface ResponseExtensionInterface { /** * Renders the response by inserting the HTML response generated by the flasher library. * - * @param RequestInterface $request the current request - * @param ResponseInterface $response the current response + * This method should analyze the response, determine if and where notifications should + * be inserted, render them, and modify the response content accordingly. * - * @return ResponseInterface the modified response with the HTML response inserted + * @param RequestInterface $request The current request + * @param ResponseInterface $response The current response + * + * @return ResponseInterface The modified response with the HTML response inserted */ public function render(RequestInterface $request, ResponseInterface $response): ResponseInterface; } diff --git a/src/Prime/Http/ResponseInterface.php b/src/Prime/Http/ResponseInterface.php index 360c4bd1..2c77ce65 100644 --- a/src/Prime/Http/ResponseInterface.php +++ b/src/Prime/Http/ResponseInterface.php @@ -4,30 +4,97 @@ declare(strict_types=1); namespace Flasher\Prime\Http; +/** + * ResponseInterface - Adapter interface for HTTP responses from different frameworks. + * + * This interface defines a common contract for response wrappers that adapt framework-specific + * response objects to work with PHPFlasher. It provides methods for inspecting response + * properties and modifying content in a consistent way across different frameworks. + * + * Design pattern: Adapter - Provides a common interface for working with different + * framework response objects. + */ interface ResponseInterface { + /** + * Checks if the response is a redirection. + * + * @return bool True if the response is a redirect (3XX status code) + */ public function isRedirection(): bool; + /** + * Checks if the response contains JSON content. + * + * @return bool True if the response is JSON + */ public function isJson(): bool; + /** + * Checks if the response contains HTML content. + * + * @return bool True if the response is HTML + */ public function isHtml(): bool; + /** + * Checks if the response is a file download/attachment. + * + * @return bool True if the response is an attachment + */ public function isAttachment(): bool; + /** + * Checks if the response represents a successful operation. + * + * @return bool True if the response has a 2XX status code + */ public function isSuccessful(): bool; + /** + * Gets the response content. + * + * @return string The response content + */ public function getContent(): string; + /** + * Sets the response content. + * + * @param string $content The new response content + */ public function setContent(string $content): void; + /** + * Checks if the response has the specified header. + * + * @param string $key The header name + * + * @return bool True if the header exists + */ public function hasHeader(string $key): bool; + /** + * Gets the value of the specified header. + * + * @param string $key The header name + * + * @return string|null The header value, or null if it doesn't exist + */ public function getHeader(string $key): ?string; /** - * @param string|string[]|null $values + * Sets the value of the specified header. + * + * @param string $key The header name + * @param string|string[]|null $values The header value(s) */ public function setHeader(string $key, string|array|null $values): void; + /** + * Removes the specified header. + * + * @param string $key The header name + */ public function removeHeader(string $key): void; } diff --git a/src/Prime/Notification/Envelope.php b/src/Prime/Notification/Envelope.php index a2d1c651..9c3fb02a 100644 --- a/src/Prime/Notification/Envelope.php +++ b/src/Prime/Notification/Envelope.php @@ -9,19 +9,31 @@ use Flasher\Prime\Stamp\StampInterface; use Flasher\Prime\Support\Traits\ForwardsCalls; /** - * Envelope class wraps a notification and manages associated stamps. + * Envelope - A wrapper for notifications with metadata. + * + * Wraps a notification object with "stamps" that provide additional + * metadata and behavior. This allows attaching cross-cutting concerns + * to notifications without modifying the notification itself. + * + * Design pattern: Decorator + Envelope - Extends functionality of + * notifications by wrapping them in a container with additional capabilities. */ final class Envelope implements NotificationInterface { use ForwardsCalls; /** + * Collection of stamps attached to this envelope, indexed by class name. + * * @var array, StampInterface> */ private array $stamps = []; /** - * @param StampInterface[]|StampInterface $stamps stamps to be added to the envelope + * Creates a new Envelope instance. + * + * @param NotificationInterface $notification The notification to wrap + * @param StampInterface[]|StampInterface $stamps One or more stamps to attach to the envelope */ public function __construct(private readonly NotificationInterface $notification, array|StampInterface $stamps = []) { @@ -33,7 +45,21 @@ final class Envelope implements NotificationInterface /** * Wraps a notification in an Envelope and adds the given stamps. * - * @param StampInterface|StampInterface[] $stamps stamps to be added to the envelope + * This static factory method provides a convenient way to create and configure + * an envelope in a single operation. + * + * Example: + * ```php + * $envelope = Envelope::wrap(new Notification(), [ + * new PriorityStamp(10), + * new HopsStamp(2) + * ]); + * ``` + * + * @param NotificationInterface $notification The notification to wrap + * @param StampInterface[]|StampInterface $stamps One or more stamps to attach to the envelope + * + * @return self The created envelope */ public static function wrap(NotificationInterface $notification, array|StampInterface $stamps = []): self { @@ -48,7 +74,7 @@ final class Envelope implements NotificationInterface /** * Adds multiple stamps to the envelope. * - * @param StampInterface ...$stamps The stamps to add. + * @param StampInterface ...$stamps The stamps to add */ public function with(StampInterface ...$stamps): void { @@ -60,8 +86,14 @@ final class Envelope implements NotificationInterface /** * Adds or replaces a stamp in the envelope. * - * @param StampInterface $stamp the stamp to add or replace - * @param bool $replace whether to replace an existing stamp of the same type + * Each stamp class can only have one instance in the envelope at a time. + * When adding a stamp of a class that already exists, the behavior depends + * on the $replace parameter: + * - If true (default), the new stamp replaces the existing one + * - If false, the existing stamp is preserved + * + * @param StampInterface $stamp The stamp to add + * @param bool $replace Whether to replace an existing stamp of the same type */ public function withStamp(StampInterface $stamp, bool $replace = true): void { @@ -72,6 +104,8 @@ final class Envelope implements NotificationInterface /** * Removes specified stamps from the envelope. + * + * @param StampInterface ...$stamps The stamps to remove */ public function without(StampInterface ...$stamps): void { @@ -83,7 +117,7 @@ final class Envelope implements NotificationInterface /** * Removes a specific type of stamp from the envelope. * - * @param class-string|StampInterface $type the type of stamp to remove + * @param class-string|StampInterface $type The type of stamp to remove */ public function withoutStamp(string|StampInterface $type): void { @@ -97,7 +131,9 @@ final class Envelope implements NotificationInterface * * @template T of StampInterface * - * @phpstan-param class-string $type + * @phpstan-param class-string $type The class name of the stamp to retrieve + * + * @return StampInterface|null The stamp if found, null otherwise * * @phpstan-return T|null */ @@ -110,9 +146,9 @@ final class Envelope implements NotificationInterface } /** - * All stamps by their class name. + * Returns all stamps by their class name. * - * @return array, StampInterface> + * @return array, StampInterface> Map of stamp class names to stamp instances */ public function all(): array { @@ -122,63 +158,118 @@ final class Envelope implements NotificationInterface /** * Gets the original notification contained in the envelope. * - * @return NotificationInterface the wrapped notification + * @return NotificationInterface The wrapped notification */ public function getNotification(): NotificationInterface { return $this->notification; } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function getTitle(): string { return $this->notification->getTitle(); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function setTitle(string $title): void { $this->notification->setTitle($title); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function getMessage(): string { return $this->notification->getMessage(); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function setMessage(string $message): void { $this->notification->setMessage($message); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function getType(): string { return $this->notification->getType(); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function setType(string $type): void { $this->notification->setType($type); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function getOptions(): array { return $this->notification->getOptions(); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function setOptions(array $options): void { $this->notification->setOptions($options); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function getOption(string $name, mixed $default = null): mixed { return $this->notification->getOption($name, $default); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function setOption(string $name, mixed $value): void { $this->notification->setOption($name, $value); } + /** + * {@inheritdoc} + * + * Delegates to the wrapped notification. + */ public function unsetOption(string $name): void { $this->notification->unsetOption($name); @@ -187,13 +278,16 @@ final class Envelope implements NotificationInterface /** * Converts the envelope and its contents to an array format. * + * This method combines the notification data with metadata from all + * presentable stamps attached to the envelope. + * * @return array{ * title: string, * message: string, * type: string, * options: array, * metadata: array, - * } + * } The notification data with metadata */ public function toArray(): array { @@ -214,10 +308,13 @@ final class Envelope implements NotificationInterface /** * Dynamically call methods on the wrapped notification. * - * @param string $method the method name to call - * @param mixed[] $parameters the parameters to pass to the method + * This magic method allows calling methods directly on the envelope + * which will be forwarded to the wrapped notification. * - * @return mixed the result of the method call + * @param string $method The method name to call + * @param mixed[] $parameters The parameters to pass to the method + * + * @return mixed The result of the method call */ public function __call(string $method, array $parameters): mixed { diff --git a/src/Prime/Notification/FlasherBuilder.php b/src/Prime/Notification/FlasherBuilder.php index 09f96131..2ccd1ac6 100644 --- a/src/Prime/Notification/FlasherBuilder.php +++ b/src/Prime/Notification/FlasherBuilder.php @@ -5,6 +5,14 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; /** + * FlasherBuilder - Default implementation of the notification builder. + * + * Provides methods for building and configuring flasher-specific notifications. + * Extends the core builder with additional type-safety and convenience methods. + * + * Design pattern: Concrete Builder - Implements the builder interface + * with specific behaviors for flasher notifications. + * * @phpstan-type NotificationType "success"|"info"|"warning"|"error" * @phpstan-type OptionsType array{ * timeout?: int, @@ -20,7 +28,11 @@ namespace Flasher\Prime\Notification; final class FlasherBuilder extends NotificationBuilder { /** - * @phpstan-param NotificationType $type + * Sets the notification type with type-safety. + * + * @phpstan-param NotificationType $type The notification type (success, info, warning, error) + * + * @return static The builder instance for method chaining */ public function type(string $type): static { @@ -28,7 +40,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates and stores a success notification. + * + * @param string $message The notification message + * @param OptionsType $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function success(string $message, array $options = [], ?string $title = null): Envelope { @@ -36,7 +54,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates and stores an error notification. + * + * @param string $message The notification message + * @param OptionsType $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function error(string $message, array $options = [], ?string $title = null): Envelope { @@ -44,7 +68,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates and stores an info notification. + * + * @param string $message The notification message + * @param OptionsType $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function info(string $message, array $options = [], ?string $title = null): Envelope { @@ -52,7 +82,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Creates and stores a warning notification. + * + * @param string $message The notification message + * @param OptionsType $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function warning(string $message, array $options = [], ?string $title = null): Envelope { @@ -60,8 +96,18 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @phpstan-param NotificationType $type - * @phpstan-param OptionsType $options + * Creates and stores a notification with specified type. + * + * This is a general-purpose method that can be used to create notifications + * of any type, with complete control over all parameters. + * + * @phpstan-param NotificationType|null $type The notification type + * @phpstan-param OptionsType $options Optional configuration for the notification + * + * @param string|null $message The notification message + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope { @@ -69,7 +115,12 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param OptionsType $options + * Sets multiple options for the notification. + * + * @param OptionsType $options Configuration options for the notification + * @param bool $append Whether to merge with existing options (true) or replace them (false) + * + * @return static The builder instance for method chaining */ public function options(array $options, bool $append = true): static { @@ -77,17 +128,28 @@ final class FlasherBuilder extends NotificationBuilder } /** + * Sets a single option for the notification. + * * @template T of OptionsType * @template K of key-of * - * @phpstan-param K $name - * @phpstan-param T[K] $value + * @phpstan-param K $name The option name + * @phpstan-param T[K] $value The option value + * + * @return static The builder instance for method chaining */ public function option(string $name, mixed $value): static { return parent::option($name, $value); } + /** + * Sets the display timeout for the notification. + * + * @param int $milliseconds The timeout duration in milliseconds (0 for no timeout) + * + * @return self The builder instance for method chaining + */ public function timeout(int $milliseconds): self { $this->option('timeout', $milliseconds); @@ -96,7 +158,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @param "top"|"bottom" $direction + * Sets the stacking direction for notifications. + * + * @param "top"|"bottom" $direction The direction in which notifications should stack + * - 'top': newer notifications appear above older ones + * - 'bottom': newer notifications appear below older ones + * + * @return self The builder instance for method chaining */ public function direction(string $direction): self { @@ -106,7 +174,13 @@ final class FlasherBuilder extends NotificationBuilder } /** - * @phpstan-param OptionsType['position'] $position + * Sets the display position for notifications. + * + * @phpstan-param OptionsType['position'] $position The position on screen + * (top-right, top-left, top-center, + * bottom-right, bottom-left, bottom-center) + * + * @return self The builder instance for method chaining */ public function position(string $position): self { diff --git a/src/Prime/Notification/Notification.php b/src/Prime/Notification/Notification.php index 7a5fda1c..e8a0be5d 100644 --- a/src/Prime/Notification/Notification.php +++ b/src/Prime/Notification/Notification.php @@ -4,23 +4,44 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; +/** + * Default implementation of NotificationInterface. + * + * This class represents a notification with its basic properties: title, message, + * type, and options. It provides the core functionality for storing and + * manipulating notification data. + * + * Design pattern: Value Object - Represents a simple entity with equality based + * on attribute values rather than identity. + */ final class Notification implements NotificationInterface { + /** + * The notification title. + */ private string $title = ''; + /** + * The notification message content. + */ private string $message = ''; + /** + * The notification type (e.g., "success", "error", "warning", "info"). + */ private string $type = ''; /** - * @var array options for the notification + * Configuration options for the notification. + * + * @var array */ private array $options = []; /** * Gets the title of the notification. * - * @return string the notification title + * @return string The notification title */ public function getTitle(): string { @@ -30,7 +51,7 @@ final class Notification implements NotificationInterface /** * Sets the title of the notification. * - * @param string $title the title to set + * @param string $title The title to set */ public function setTitle(string $title): void { @@ -40,7 +61,7 @@ final class Notification implements NotificationInterface /** * Gets the message of the notification. * - * @return string the notification message + * @return string The notification message */ public function getMessage(): string { @@ -50,7 +71,7 @@ final class Notification implements NotificationInterface /** * Sets the message of the notification. * - * @param string $message the message to set + * @param string $message The message to set */ public function setMessage(string $message): void { @@ -60,7 +81,7 @@ final class Notification implements NotificationInterface /** * Gets the type of the notification. * - * @return string the notification type + * @return string The notification type */ public function getType(): string { @@ -70,7 +91,7 @@ final class Notification implements NotificationInterface /** * Sets the type of the notification. * - * @param string $type the type to set + * @param string $type The type to set */ public function setType(string $type): void { @@ -80,7 +101,7 @@ final class Notification implements NotificationInterface /** * Gets all options of the notification. * - * @return array the notification options + * @return array The notification options */ public function getOptions(): array { @@ -90,7 +111,10 @@ final class Notification implements NotificationInterface /** * Sets or updates the options of the notification. * - * @param array $options the options to set or update + * This method merges the provided options with existing ones, + * with new values taking precedence over existing values. + * + * @param array $options The options to set or update */ public function setOptions(array $options): void { @@ -100,10 +124,10 @@ final class Notification implements NotificationInterface /** * Gets a specific option of the notification with a default fallback. * - * @param string $name the name of the option - * @param mixed $default the default value to return if the option is not set + * @param string $name The name of the option + * @param mixed $default The default value to return if the option is not set * - * @return mixed the option value or the default value + * @return mixed The option value or the default value */ public function getOption(string $name, mixed $default = null): mixed { @@ -115,8 +139,8 @@ final class Notification implements NotificationInterface /** * Sets a specific option for the notification. * - * @param string $name the name of the option - * @param mixed $value the value of the option + * @param string $name The name of the option + * @param mixed $value The value of the option */ public function setOption(string $name, mixed $value): void { @@ -126,7 +150,7 @@ final class Notification implements NotificationInterface /** * Unsets a specific option of the notification. * - * @param string $name the name of the option to unset + * @param string $name The name of the option to unset */ public function unsetOption(string $name): void { @@ -141,7 +165,7 @@ final class Notification implements NotificationInterface * message: string, * type: string, * options: array, - * } + * } The notification as an array */ public function toArray(): array { diff --git a/src/Prime/Notification/NotificationBuilder.php b/src/Prime/Notification/NotificationBuilder.php index 14350005..6972011a 100644 --- a/src/Prime/Notification/NotificationBuilder.php +++ b/src/Prime/Notification/NotificationBuilder.php @@ -8,6 +8,17 @@ use Flasher\Prime\Stamp\PluginStamp; use Flasher\Prime\Storage\StorageManagerInterface; use Flasher\Prime\Support\Traits\Macroable; +/** + * Base abstract class for notification builders. + * + * This abstract class provides the foundation for all notification builders, + * implementing common functionality through traits. It manages the envelope + * creation and configuration process through a fluent API. + * + * Design pattern: Builder - Separates the construction of complex objects + * from their representation, allowing the same construction process to + * create different representations. + */ abstract class NotificationBuilder implements NotificationBuilderInterface { use Macroable; @@ -15,6 +26,16 @@ abstract class NotificationBuilder implements NotificationBuilderInterface use NotificationMethodAliases; use NotificationStorageMethods; + /** + * Creates a new NotificationBuilder instance. + * + * This constructor accepts either a notification object or a string representing + * the plugin name. In the latter case, it creates a new notification and sets + * the plugin stamp accordingly. + * + * @param string|NotificationInterface $notification Either a notification object or a plugin name + * @param StorageManagerInterface $storageManager The storage manager for persisting notifications + */ public function __construct(string|NotificationInterface $notification, StorageManagerInterface $storageManager) { if (\is_string($notification)) { diff --git a/src/Prime/Notification/NotificationBuilderInterface.php b/src/Prime/Notification/NotificationBuilderInterface.php index c42e9f23..f861c21a 100644 --- a/src/Prime/Notification/NotificationBuilderInterface.php +++ b/src/Prime/Notification/NotificationBuilderInterface.php @@ -6,131 +6,394 @@ namespace Flasher\Prime\Notification; use Flasher\Prime\Stamp\StampInterface; +/** + * NotificationBuilderInterface - Fluent interface for notification creation. + * + * Provides a rich, fluent API for constructing notifications with various + * options and behaviors. Simplifies the process of creating and configuring + * notifications. + * + * Design pattern: Builder - Separates the construction of complex objects + * from their representation, allowing the same construction process to + * create different representations. + */ interface NotificationBuilderInterface { + /** + * Sets the notification title. + * + * @param string $title The title to set + * + * @return static The builder instance for method chaining + */ public function title(string $title): static; + /** + * Sets the notification message. + * + * @param string $message The message to set + * + * @return static The builder instance for method chaining + */ public function message(string $message): static; + /** + * Sets the notification type. + * + * @param string $type The type to set (e.g., "success", "error", "info", "warning") + * + * @return static The builder instance for method chaining + */ public function type(string $type): static; /** - * @param array $options + * Sets multiple options for the notification. + * + * @param array $options The options to set + * @param bool $merge Whether to merge with existing options (true) or replace them (false) + * + * @return static The builder instance for method chaining */ public function options(array $options, bool $merge = true): static; + /** + * Sets a single option for the notification. + * + * @param string $name The option name + * @param mixed $value The option value + * + * @return static The builder instance for method chaining + */ public function option(string $name, mixed $value): static; + /** + * Sets the notification priority. + * + * Higher priority notifications are typically displayed before lower priority ones. + * + * @param int $priority The priority value (higher values indicate higher priority) + * + * @return static The builder instance for method chaining + */ public function priority(int $priority): static; + /** + * Increases the number of request hops the notification will persist. + * + * This method is useful for keeping a notification across multiple redirects. + * + * @return static The builder instance for method chaining + */ public function keep(): static; + /** + * Sets the exact number of request hops the notification will persist. + * + * @param int $amount The number of hops to keep the notification + * + * @return static The builder instance for method chaining + */ public function hops(int $amount): static; + /** + * Sets a delay before showing the notification. + * + * @param int $delay The delay in milliseconds + * + * @return static The builder instance for method chaining + */ public function delay(int $delay): static; /** - * @param array $parameters + * Configures translation parameters for the notification. + * + * @param array $parameters Translation parameters + * @param string|null $locale Specific locale to use, or null for default + * + * @return static The builder instance for method chaining */ public function translate(array $parameters = [], ?string $locale = null): static; + /** + * Sets the handler (plugin) that should process the notification. + * + * @param string $handler The handler/plugin name (e.g., "toastr", "sweetalert") + * + * @return static The builder instance for method chaining + */ public function handler(string $handler): static; /** - * @param array $context + * Sets additional context data for the notification. + * + * @param array $context The context data + * + * @return static The builder instance for method chaining */ public function context(array $context): static; + /** + * Adds a condition that must be true for the notification to be displayed. + * + * @param bool|\Closure $condition A boolean or closure returning a boolean + * + * @return static The builder instance for method chaining + */ public function when(bool|\Closure $condition): static; + /** + * Adds a condition that must be false for the notification to be displayed. + * + * @param bool|\Closure $condition A boolean or closure returning a boolean + * + * @return static The builder instance for method chaining + */ public function unless(bool|\Closure $condition): static; /** - * @param StampInterface[]|StampInterface $stamps + * Adds one or more stamps to the notification envelope. + * + * @param StampInterface[]|StampInterface $stamps The stamps to add + * + * @return static The builder instance for method chaining */ public function with(array|StampInterface $stamps): static; + /** + * Gets the current notification envelope. + * + * @return Envelope The notification envelope + */ public function getEnvelope(): Envelope; /** - * @param array $options + * Creates and stores a success notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function success(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Creates and stores an error notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function error(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Creates and stores an info notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function info(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Creates and stores a warning notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function warning(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Creates and stores a notification with specified type. + * + * @param string|null $type The notification type + * @param string|null $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope */ public function flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope; /** - * @param array $parameters + * Creates a notification from a named preset template. + * + * @param string $preset The preset name + * @param array $parameters Template parameters + * + * @return Envelope The created notification envelope */ public function preset(string $preset, array $parameters = []): Envelope; + /** + * Creates a notification for a generic operation on a resource. + * + * @param string $operation The operation name (e.g., "created", "updated") + * @param string|object|null $resource The resource that was operated on + * + * @return Envelope The created notification envelope + */ public function operation(string $operation, string|object|null $resource = null): Envelope; + /** + * Creates a notification for a resource creation operation. + * + * @param string|object|null $resource The resource that was created + * + * @return Envelope The created notification envelope + */ public function created(string|object|null $resource = null): Envelope; + /** + * Creates a notification for a resource update operation. + * + * @param string|object|null $resource The resource that was updated + * + * @return Envelope The created notification envelope + */ public function updated(string|object|null $resource = null): Envelope; + /** + * Creates a notification for a resource save operation. + * + * @param string|object|null $resource The resource that was saved + * + * @return Envelope The created notification envelope + */ public function saved(string|object|null $resource = null): Envelope; + /** + * Creates a notification for a resource deletion operation. + * + * @param string|object|null $resource The resource that was deleted + * + * @return Envelope The created notification envelope + */ public function deleted(string|object|null $resource = null): Envelope; + /** + * Finalizes and stores the current notification. + * + * @return Envelope The stored notification envelope + */ public function push(): Envelope; /** - * @param array $options + * Alias for success() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope */ public function addSuccess(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Alias for error() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope */ public function addError(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Alias for info() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope */ public function addInfo(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Alias for warning() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope */ public function addWarning(string $message, array $options = [], ?string $title = null): Envelope; /** - * @param array $options + * Alias for flash() method. + * + * @param string|null $type The notification type + * @param string|null $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope */ public function addFlash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope; /** - * @param array $parameters + * Alias for preset() method. + * + * @param string $preset The preset name + * @param array $parameters Template parameters + * + * @return Envelope The created notification envelope */ public function addPreset(string $preset, array $parameters = []): Envelope; + /** + * Alias for created() method. + * + * @param string|object|null $resource The resource that was created + * + * @return Envelope The created notification envelope + */ public function addCreated(string|object|null $resource = null): Envelope; + /** + * Alias for updated() method. + * + * @param string|object|null $resource The resource that was updated + * + * @return Envelope The created notification envelope + */ public function addUpdated(string|object|null $resource = null): Envelope; + /** + * Alias for saved() method. + * + * @param string|object|null $resource The resource that was saved + * + * @return Envelope The created notification envelope + */ public function addSaved(string|object|null $resource = null): Envelope; + /** + * Alias for deleted() method. + * + * @param string|object|null $resource The resource that was deleted + * + * @return Envelope The created notification envelope + */ public function addDeleted(string|object|null $resource = null): Envelope; + /** + * Alias for operation() method. + * + * @param string $operation The operation name + * @param string|object|null $resource The resource that was operated on + * + * @return Envelope The created notification envelope + */ public function addOperation(string $operation, string|object|null $resource = null): Envelope; } diff --git a/src/Prime/Notification/NotificationBuilderMethods.php b/src/Prime/Notification/NotificationBuilderMethods.php index a0c37733..97955c8a 100644 --- a/src/Prime/Notification/NotificationBuilderMethods.php +++ b/src/Prime/Notification/NotificationBuilderMethods.php @@ -14,10 +14,26 @@ use Flasher\Prime\Stamp\TranslationStamp; use Flasher\Prime\Stamp\UnlessStamp; use Flasher\Prime\Stamp\WhenStamp; +/** + * Core notification builder methods. + * + * This trait implements the core builder methods for configuring notifications. + * It provides the fluent API for setting notification properties and attaching stamps. + */ trait NotificationBuilderMethods { + /** + * The notification envelope being built. + */ protected readonly Envelope $envelope; + /** + * Sets the notification title. + * + * @param string $title The title to set + * + * @return static The builder instance for method chaining + */ public function title(string $title): static { $this->envelope->setTitle($title); @@ -25,6 +41,13 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets the notification message. + * + * @param string $message The message to set + * + * @return static The builder instance for method chaining + */ public function message(string $message): static { $this->envelope->setMessage($message); @@ -32,6 +55,13 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets the notification type. + * + * @param string $type The type to set (e.g., "success", "error", "info", "warning") + * + * @return static The builder instance for method chaining + */ public function type(string $type): static { $this->envelope->setType($type); @@ -39,6 +69,14 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets multiple options for the notification. + * + * @param array $options The options to set + * @param bool $append Whether to merge with existing options (true) or replace them (false) + * + * @return static The builder instance for method chaining + */ public function options(array $options, bool $append = true): static { if ($append) { @@ -50,6 +88,14 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets a single option for the notification. + * + * @param string $name The option name + * @param mixed $value The option value + * + * @return static The builder instance for method chaining + */ public function option(string $name, mixed $value): static { $this->envelope->setOption($name, $value); @@ -57,6 +103,15 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets the notification priority. + * + * Higher priority notifications are typically displayed before lower priority ones. + * + * @param int $priority The priority value (higher values indicate higher priority) + * + * @return static The builder instance for method chaining + */ public function priority(int $priority): static { $this->envelope->withStamp(new PriorityStamp($priority)); @@ -64,6 +119,15 @@ trait NotificationBuilderMethods return $this; } + /** + * Increases the number of request hops the notification will persist. + * + * This method is useful for keeping a notification across multiple redirects. + * It increments the current hop count by 1, ensuring the notification persists + * for one additional request. + * + * @return static The builder instance for method chaining + */ public function keep(): static { $stamp = $this->envelope->get(HopsStamp::class); @@ -72,6 +136,17 @@ trait NotificationBuilderMethods return $this->hops(1 + $amount); } + /** + * Sets the exact number of request hops the notification will persist. + * + * This determines how many redirects/page loads a notification will survive. + * For example, a value of 2 means the notification will be shown on the current + * request and the next request, then be removed. + * + * @param int $amount The number of hops to keep the notification + * + * @return static The builder instance for method chaining + */ public function hops(int $amount): static { $this->envelope->withStamp(new HopsStamp($amount)); @@ -79,6 +154,13 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets a delay before showing the notification. + * + * @param int $delay The delay in milliseconds + * + * @return static The builder instance for method chaining + */ public function delay(int $delay): static { $this->envelope->withStamp(new DelayStamp($delay)); @@ -86,6 +168,17 @@ trait NotificationBuilderMethods return $this; } + /** + * Configures translation parameters for the notification. + * + * This allows the message and title to be translated according to the current locale. + * Parameters can be used for variable substitution in translation strings. + * + * @param array $parameters Translation parameters + * @param string|null $locale Specific locale to use, or null for default + * + * @return static The builder instance for method chaining + */ public function translate(array $parameters = [], ?string $locale = null): static { $this->envelope->withStamp(new TranslationStamp($parameters, $locale)); @@ -93,6 +186,16 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets the handler (plugin) that should process the notification. + * + * This specifies which adapter (e.g., 'toastr', 'sweetalert') should be used + * to render the notification. + * + * @param string $handler The handler/plugin name + * + * @return static The builder instance for method chaining + */ public function handler(string $handler): static { $this->envelope->withStamp(new PluginStamp($handler)); @@ -100,6 +203,16 @@ trait NotificationBuilderMethods return $this; } + /** + * Sets additional context data for the notification. + * + * Context data can be used for customizing the rendering of notifications + * or providing additional information to handlers. + * + * @param array $context The context data + * + * @return static The builder instance for method chaining + */ public function context(array $context): static { $this->envelope->withStamp(new ContextStamp($context)); @@ -107,6 +220,15 @@ trait NotificationBuilderMethods return $this; } + /** + * Adds a condition that must be true for the notification to be displayed. + * + * When multiple when() conditions are used, all must be true (AND logic). + * + * @param bool|\Closure $condition A boolean or closure returning a boolean + * + * @return static The builder instance for method chaining + */ public function when(bool|\Closure $condition): static { $condition = $this->validateCallableCondition($condition); @@ -121,6 +243,15 @@ trait NotificationBuilderMethods return $this; } + /** + * Adds a condition that must be false for the notification to be displayed. + * + * When multiple unless() conditions are used, all must be false (OR logic for the negation). + * + * @param bool|\Closure $condition A boolean or closure returning a boolean + * + * @return static The builder instance for method chaining + */ public function unless(bool|\Closure $condition): static { $condition = $this->validateCallableCondition($condition); @@ -135,6 +266,13 @@ trait NotificationBuilderMethods return $this; } + /** + * Adds one or more stamps to the notification envelope. + * + * @param StampInterface[]|StampInterface $stamps The stamps to add + * + * @return static The builder instance for method chaining + */ public function with(array|StampInterface $stamps): static { if ($stamps instanceof StampInterface) { @@ -146,11 +284,27 @@ trait NotificationBuilderMethods return $this; } + /** + * Gets the current notification envelope. + * + * @return Envelope The notification envelope + */ public function getEnvelope(): Envelope { return $this->envelope; } + /** + * Validates that a condition is either a boolean or a closure returning a boolean. + * + * If the condition is a closure, it is evaluated with the current envelope as parameter. + * + * @param bool|\Closure $condition The condition to validate + * + * @return bool The evaluated boolean condition + * + * @throws \InvalidArgumentException If the condition is not a boolean or doesn't return a boolean + */ protected function validateCallableCondition(bool|\Closure $condition): bool { if ($condition instanceof \Closure) { diff --git a/src/Prime/Notification/NotificationInterface.php b/src/Prime/Notification/NotificationInterface.php index 9fa1fa58..2b9596cf 100644 --- a/src/Prime/Notification/NotificationInterface.php +++ b/src/Prime/Notification/NotificationInterface.php @@ -4,43 +4,109 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; +/** + * NotificationInterface - The core notification contract. + * + * Defines the essential properties and behaviors of a notification. + * All notification implementations must adhere to this interface. + * + * Design pattern: Component Interface - Establishes a common contract + * for notification objects throughout the system. + */ interface NotificationInterface { + /** + * Gets the title of the notification. + * + * @return string The notification title + */ public function getTitle(): string; + /** + * Sets the title of the notification. + * + * @param string $title The title to set + */ public function setTitle(string $title): void; + /** + * Gets the message content of the notification. + * + * @return string The notification message + */ public function getMessage(): string; + /** + * Sets the message content of the notification. + * + * @param string $message The message to set + */ public function setMessage(string $message): void; + /** + * Gets the type of the notification. + * + * Common types include "success", "error", "info", and "warning". + * + * @return string The notification type + */ public function getType(): string; + /** + * Sets the type of the notification. + * + * @param string $type The type to set + */ public function setType(string $type): void; /** - * @return array + * Gets all options of the notification. + * + * @return array The notification options */ public function getOptions(): array; /** - * @param array $options + * Sets or updates the options of the notification. + * + * @param array $options The options to set or update */ public function setOptions(array $options): void; + /** + * Gets a specific option of the notification with a default fallback. + * + * @param string $name The name of the option + * @param mixed $default The default value to return if the option is not set + * + * @return mixed The option value or the default value + */ public function getOption(string $name, mixed $default = null): mixed; + /** + * Sets a specific option for the notification. + * + * @param string $name The name of the option + * @param mixed $value The value of the option + */ public function setOption(string $name, mixed $value): void; + /** + * Unsets a specific option of the notification. + * + * @param string $name The name of the option to unset + */ public function unsetOption(string $name): void; /** + * Converts the notification into an associative array. + * * @return array{ * title: string, * message: string, * type: string, * options: array, - * } + * } The notification as an array */ public function toArray(): array; } diff --git a/src/Prime/Notification/NotificationMethodAliases.php b/src/Prime/Notification/NotificationMethodAliases.php index 7dedac44..d8df0405 100644 --- a/src/Prime/Notification/NotificationMethodAliases.php +++ b/src/Prime/Notification/NotificationMethodAliases.php @@ -4,58 +4,156 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; +/** + * Provides alternative method names for notification creation. + * + * This trait implements aliases for the standard notification methods, offering + * alternative naming conventions with 'add' prefix. These aliases improve API + * usability by providing familiar method names for developers coming from different + * frameworks. + */ trait NotificationMethodAliases { + /** + * Alias for success() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope + */ public function addSuccess(string $message, array $options = [], ?string $title = null): Envelope { return $this->success($message, $options, $title); } + /** + * Alias for error() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope + */ public function addError(string $message, array $options = [], ?string $title = null): Envelope { return $this->error($message, $options, $title); } + /** + * Alias for info() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope + */ public function addInfo(string $message, array $options = [], ?string $title = null): Envelope { return $this->info($message, $options, $title); } + /** + * Alias for warning() method. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope + */ public function addWarning(string $message, array $options = [], ?string $title = null): Envelope { return $this->warning($message, $options, $title); } + /** + * Alias for flash() method. + * + * @param string|null $type The notification type + * @param string|null $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created notification envelope + */ public function addFlash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope { return $this->flash($type, $message, $options, $title); } + /** + * Alias for preset() method. + * + * @param string $preset The preset name + * @param array $parameters Template parameters + * + * @return Envelope The created notification envelope + */ public function addPreset(string $preset, array $parameters = []): Envelope { return $this->preset($preset, $parameters); } + /** + * Alias for created() method. + * + * @param string|object|null $resource The resource that was created + * + * @return Envelope The created notification envelope + */ public function addCreated(string|object|null $resource = null): Envelope { return $this->created($resource); } + /** + * Alias for updated() method. + * + * @param string|object|null $resource The resource that was updated + * + * @return Envelope The created notification envelope + */ public function addUpdated(string|object|null $resource = null): Envelope { return $this->updated($resource); } + /** + * Alias for saved() method. + * + * @param string|object|null $resource The resource that was saved + * + * @return Envelope The created notification envelope + */ public function addSaved(string|object|null $resource = null): Envelope { return $this->saved($resource); } + /** + * Alias for deleted() method. + * + * @param string|object|null $resource The resource that was deleted + * + * @return Envelope The created notification envelope + */ public function addDeleted(string|object|null $resource = null): Envelope { return $this->deleted($resource); } + /** + * Alias for operation() method. + * + * @param string $operation The operation name + * @param string|object|null $resource The resource that was operated on + * + * @return Envelope The created notification envelope + */ public function addOperation(string $operation, string|object|null $resource = null): Envelope { return $this->operation($operation, $resource); diff --git a/src/Prime/Notification/NotificationStorageMethods.php b/src/Prime/Notification/NotificationStorageMethods.php index 318ea255..ff618040 100644 --- a/src/Prime/Notification/NotificationStorageMethods.php +++ b/src/Prime/Notification/NotificationStorageMethods.php @@ -7,30 +7,90 @@ namespace Flasher\Prime\Notification; use Flasher\Prime\Stamp\PresetStamp; use Flasher\Prime\Storage\StorageManagerInterface; +/** + * Implements notification creation and storage methods. + * + * This trait provides the concrete implementations for creating different types + * of notifications and storing them via the storage manager. It implements the + * final step in the builder pattern by creating and persisting notifications. + */ trait NotificationStorageMethods { + /** + * The storage manager for persisting notifications. + */ protected readonly StorageManagerInterface $storageManager; + /** + * Creates and stores a success notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope + */ public function success(string $message, array $options = [], ?string $title = null): Envelope { return $this->flash(Type::SUCCESS, $message, $options, $title); } + /** + * Creates and stores an error notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope + */ public function error(string $message, array $options = [], ?string $title = null): Envelope { return $this->flash(Type::ERROR, $message, $options, $title); } + /** + * Creates and stores an info notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope + */ public function info(string $message, array $options = [], ?string $title = null): Envelope { return $this->flash(Type::INFO, $message, $options, $title); } + /** + * Creates and stores a warning notification. + * + * @param string $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope + */ public function warning(string $message, array $options = [], ?string $title = null): Envelope { return $this->flash(Type::WARNING, $message, $options, $title); } + /** + * Creates and stores a notification with specified type. + * + * This is the core method used by specific notification type methods. + * It configures the notification based on the provided parameters and + * then stores it via push(). + * + * @param string|null $type The notification type + * @param string|null $message The notification message + * @param array $options Optional configuration for the notification + * @param string|null $title Optional title for the notification + * + * @return Envelope The created and stored notification envelope + */ public function flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope { if (null !== $type) { @@ -52,6 +112,17 @@ trait NotificationStorageMethods return $this->push(); } + /** + * Creates a notification from a named preset template. + * + * Presets allow defining common notification templates in configuration, + * which can be reused with different parameters. + * + * @param string $preset The preset name + * @param array $parameters Template parameters + * + * @return Envelope The created notification envelope + */ public function preset(string $preset, array $parameters = []): Envelope { $this->envelope->withStamp(new PresetStamp($preset, $parameters)); @@ -59,26 +130,14 @@ trait NotificationStorageMethods return $this->push(); } - public function created(string|object|null $resource = null): Envelope - { - return $this->operation('created', $resource); - } - - public function updated(string|object|null $resource = null): Envelope - { - return $this->operation('updated', $resource); - } - - public function saved(string|object|null $resource = null): Envelope - { - return $this->operation('saved', $resource); - } - - public function deleted(string|object|null $resource = null): Envelope - { - return $this->operation('deleted', $resource); - } - + /** + * Creates a notification for a generic operation on a resource. + * + * @param string $operation The operation name (e.g., "created", "updated") + * @param string|object|null $resource The resource that was operated on + * + * @return Envelope The created notification envelope + */ public function operation(string $operation, string|object|null $resource = null): Envelope { $resource = match (true) { @@ -90,6 +149,62 @@ trait NotificationStorageMethods return $this->preset($operation, [':resource' => $resource ?: 'resource']); } + /** + * Creates a notification for a resource creation operation. + * + * @param string|object|null $resource The resource that was created + * + * @return Envelope The created notification envelope + */ + public function created(string|object|null $resource = null): Envelope + { + return $this->operation('created', $resource); + } + + /** + * Creates a notification for a resource update operation. + * + * @param string|object|null $resource The resource that was updated + * + * @return Envelope The created notification envelope + */ + public function updated(string|object|null $resource = null): Envelope + { + return $this->operation('updated', $resource); + } + + /** + * Creates a notification for a resource save operation. + * + * @param string|object|null $resource The resource that was saved + * + * @return Envelope The created notification envelope + */ + public function saved(string|object|null $resource = null): Envelope + { + return $this->operation('saved', $resource); + } + + /** + * Creates a notification for a resource deletion operation. + * + * @param string|object|null $resource The resource that was deleted + * + * @return Envelope The created notification envelope + */ + public function deleted(string|object|null $resource = null): Envelope + { + return $this->operation('deleted', $resource); + } + + /** + * Finalizes and stores the current notification. + * + * This method completes the builder process, stores the notification + * in the storage manager, and returns the final envelope. + * + * @return Envelope The stored notification envelope + */ public function push(): Envelope { $envelope = $this->getEnvelope(); @@ -99,6 +214,17 @@ trait NotificationStorageMethods return $envelope; } + /** + * Resolves a display name for a resource object. + * + * This method tries to determine a user-friendly name for an object by: + * 1. Checking if the object has a getFlashIdentifier() method + * 2. Falling back to the simple class name if not + * + * @param object $object The object to resolve a name for + * + * @return string|null The resolved name + */ private function resolveResourceName(object $object): ?string { $displayName = \is_callable([$object, 'getFlashIdentifier']) ? $object->getFlashIdentifier() : null; diff --git a/src/Prime/Notification/Type.php b/src/Prime/Notification/Type.php index cd251abb..e868279e 100644 --- a/src/Prime/Notification/Type.php +++ b/src/Prime/Notification/Type.php @@ -4,13 +4,41 @@ declare(strict_types=1); namespace Flasher\Prime\Notification; +/** + * Type - Constants for standard notification types. + * + * This class defines the standard notification types supported by PHPFlasher. + * Using these constants instead of string literals ensures consistency and + * prevents typos when specifying notification types. + */ final class Type { + /** + * Success notification type. + * + * Used for positive feedback, successful operations, or completed actions. + */ public const SUCCESS = 'success'; + /** + * Error notification type. + * + * Used for failures, exceptions, or problems that prevented an operation from completing. + */ public const ERROR = 'error'; + /** + * Info notification type. + * + * Used for neutral informational messages, status updates, or helpful tips. + */ public const INFO = 'info'; + /** + * Warning notification type. + * + * Used for potential issues, alerts, or important notices that don't prevent + * an operation but deserve attention. + */ public const WARNING = 'warning'; } diff --git a/src/Prime/Plugin/FlasherPlugin.php b/src/Prime/Plugin/FlasherPlugin.php index 26b30931..6b05d27c 100644 --- a/src/Prime/Plugin/FlasherPlugin.php +++ b/src/Prime/Plugin/FlasherPlugin.php @@ -9,6 +9,16 @@ use Flasher\Prime\FlasherInterface; use Flasher\Prime\Notification\Type; /** + * FlasherPlugin - Core plugin for PHPFlasher. + * + * This class represents the core PHPFlasher plugin, which provides the basic + * notification functionality. It serves as the default plugin and the foundation + * for other plugins to build upon. + * + * Design patterns: + * - Core Plugin: Provides essential functionality that other plugins extend + * - Configuration Management: Handles normalization and defaults for system config + * * @phpstan-type ConfigType array{ * default: string, * main_script: string, @@ -60,11 +70,21 @@ final class FlasherPlugin extends Plugin return FlasherInterface::class; } + /** + * Gets the default plugin name. + * + * @return string The default plugin name + */ public function getDefault(): string { return 'flasher'; } + /** + * Gets the path to the main PHPFlasher script. + * + * @return string The script path + */ public function getRootScript(): string { return '/vendor/flasher/flasher.min.js'; @@ -83,6 +103,17 @@ final class FlasherPlugin extends Plugin return '/vendor/flasher/flasher.min.css'; } + /** + * {@inheritdoc} + * + * This overridden method extends the parent's normalization process with + * additional steps specific to the core plugin: + * - Normalizing plugin configurations + * - Normalizing preset configurations + * - Adding default configuration values + * - Normalizing flash bag mappings + * - Setting preset defaults + */ public function normalizeConfig(array $config = []): array { $config = parent::normalizeConfig($config); @@ -97,6 +128,11 @@ final class FlasherPlugin extends Plugin } /** + * Normalizes the plugins configuration. + * + * This method ensures the core plugin is properly configured, merges global options + * with plugin-specific options, and normalizes array formats for scripts and styles. + * * @param array{ * scripts: string[], * styles: string[], @@ -106,14 +142,14 @@ final class FlasherPlugin extends Plugin * styles?: string[], * options?: array, * }>, - * } $config + * } $config The raw configuration * * @return array{ * scripts: string[], * styles: string[], * options: array, * plugins: array, - * } + * } The normalized configuration */ private function normalizePlugins(array $config): array { @@ -154,13 +190,18 @@ final class FlasherPlugin extends Plugin } /** + * Normalizes the presets configuration. + * + * This method ensures that string-only preset definitions are expanded to full arrays + * with the string value as the message. + * * @param array{ * scripts: string[], * styles: string[], * options: array, * presets?: array>, * plugins: array, - * } $config + * } $config The raw configuration * * @return array{ * scripts: string[], @@ -168,7 +209,7 @@ final class FlasherPlugin extends Plugin * options: array, * presets?: array>, * plugins: array, - * } + * } The normalized configuration */ private function normalizePresets(array $config): array { @@ -184,6 +225,11 @@ final class FlasherPlugin extends Plugin } /** + * Adds default configuration values. + * + * This method ensures that all required configuration keys have values, + * providing defaults for any that are missing. + * * @param array{ * default?: string|null, * main_script?: string|null, @@ -196,7 +242,7 @@ final class FlasherPlugin extends Plugin * options: array, * presets?: array>, * plugins: array, - * } $config + * } $config The raw configuration * * @return array{ * default: string|null, @@ -210,7 +256,7 @@ final class FlasherPlugin extends Plugin * options: array, * presets: array>, * plugins: array, - * } + * } The configuration with defaults added */ private function addDefaultConfig(array $config): array { @@ -232,6 +278,11 @@ final class FlasherPlugin extends Plugin } /** + * Normalizes the flash bag configuration. + * + * This method ensures that the flash bag mapping has the correct structure + * and includes default mappings unless explicitly disabled. + * * @param array{ * default: string|null, * main_script: string|null, @@ -245,7 +296,7 @@ final class FlasherPlugin extends Plugin * presets: array>, * plugins: array, * flash_bag?: bool|array, - * } $config + * } $config The raw configuration * * @return array{ * default: string|null, @@ -260,7 +311,7 @@ final class FlasherPlugin extends Plugin * presets: array>, * plugins: array, * flash_bag: false|array, - * } + * } The normalized configuration */ private function normalizeFlashBag(array $config): array { @@ -285,6 +336,10 @@ final class FlasherPlugin extends Plugin } /** + * Sets default values for presets. + * + * This method ensures that all presets have required fields with default values. + * * @param array{ * default: string|null, * main_script: string|null, @@ -298,7 +353,7 @@ final class FlasherPlugin extends Plugin * presets: array>, * plugins: array, * flash_bag: false|array, - * } $config + * } $config The raw configuration * * @return array{ * default: string|null, @@ -313,7 +368,7 @@ final class FlasherPlugin extends Plugin * presets: array>, * plugins: array, * flash_bag: false|array, - * } + * } The normalized configuration */ private function setPresetsDefaults(array $config): array { diff --git a/src/Prime/Plugin/Plugin.php b/src/Prime/Plugin/Plugin.php index 16c12942..7ee7ac66 100644 --- a/src/Prime/Plugin/Plugin.php +++ b/src/Prime/Plugin/Plugin.php @@ -4,38 +4,85 @@ declare(strict_types=1); namespace Flasher\Prime\Plugin; +/** + * Plugin - Base class for PHPFlasher plugins. + * + * This abstract class provides a common foundation for PHPFlasher plugins, + * implementing standard behaviors and conventions. Plugin implementations + * should extend this class and customize the specific aspects they need to change. + * + * Design patterns: + * - Template Method: Defines standard algorithms with customizable steps + * - Convention over Configuration: Provides sensible defaults based on naming conventions + */ abstract class Plugin implements PluginInterface { + /** + * {@inheritdoc} + * + * By default, derives the plugin name from its alias with a "flasher_" prefix. + */ public function getName(): string { return 'flasher_'.$this->getAlias(); } + /** + * {@inheritdoc} + * + * By default, derives the service ID from the plugin alias with a "flasher." prefix. + */ public function getServiceId(): string { return 'flasher.'.$this->getAlias(); } + /** + * {@inheritdoc} + * + * By default, returns an empty array, meaning no service aliases are registered. + */ public function getServiceAliases(): string|array { return []; } + /** + * {@inheritdoc} + * + * By default, returns an empty array, meaning no scripts are included. + */ public function getScripts(): string|array { return []; } + /** + * {@inheritdoc} + * + * By default, returns an empty array, meaning no styles are included. + */ public function getStyles(): string|array { return []; } + /** + * {@inheritdoc} + * + * By default, returns an empty array, meaning no options are configured. + */ public function getOptions(): array { return []; } + /** + * {@inheritdoc} + * + * By default, locates the assets directory by convention based on the plugin's + * class location. + */ public function getAssetsDir(): string { $resourcesDir = $this->getResourcesDir(); @@ -44,6 +91,13 @@ abstract class Plugin implements PluginInterface return realpath($assetsDir) ?: ''; } + /** + * {@inheritdoc} + * + * By default, locates the resources directory by convention based on the plugin's + * class location. Looks first for a Resources subdirectory in the same directory, + * then falls back to a Resources directory one level up. + */ public function getResourcesDir(): string { $reflection = new \ReflectionClass($this); @@ -55,6 +109,13 @@ abstract class Plugin implements PluginInterface return realpath($resourcesDir) ?: ''; } + /** + * {@inheritdoc} + * + * This implementation merges the provided config with defaults from the plugin's + * getScripts(), getStyles(), and getOptions() methods and ensures arrays are properly + * normalized. + */ public function normalizeConfig(array $config): array { $config = [ diff --git a/src/Prime/Plugin/PluginInterface.php b/src/Prime/Plugin/PluginInterface.php index 0cc9f383..fe75fef0 100644 --- a/src/Prime/Plugin/PluginInterface.php +++ b/src/Prime/Plugin/PluginInterface.php @@ -6,55 +6,133 @@ namespace Flasher\Prime\Plugin; use Flasher\Prime\Factory\NotificationFactoryInterface; +/** + * PluginInterface - Contract for PHPFlasher plugins. + * + * This interface defines the essential operations for PHPFlasher plugins. + * A plugin typically provides a notification renderer implementation along with + * its associated assets (scripts, styles) and configuration. The interface + * ensures that plugins can be discovered and loaded in a consistent way. + * + * Design patterns: + * - Plugin: Defines an extension mechanism for the core system + * - Strategy: Allows plugging in different notification display strategies + */ interface PluginInterface { + /** + * Gets the plugin's short name/alias. + * + * This is used in configuration and as part of service IDs. + * + * @return string The plugin alias (e.g., 'toastr', 'sweetalert') + */ public function getAlias(): string; + /** + * Gets the plugin's full name. + * + * This can be used for display purposes in UIs. + * + * @return string The plugin name + */ public function getName(): string; + /** + * Gets the plugin's primary service ID. + * + * This is the ID that will be used to register the plugin's factory + * in the service container. + * + * @return string The service ID (e.g., 'flasher.toastr') + */ public function getServiceId(): string; /** - * @return class-string + * Gets the plugin's factory class. + * + * This is the class that will be instantiated to create notifications + * for this plugin. + * + * @return class-string The factory class name */ public function getFactory(): string; /** - * @return string|string[] + * Gets the service aliases for the plugin. + * + * These are alternative service IDs or interfaces that the plugin's + * factory will be registered under. + * + * @return string|string[] The service alias(es) */ public function getServiceAliases(): string|array; /** - * @return string|string[] + * Gets the JavaScript files needed by the plugin. + * + * These scripts will be included in the page when notifications + * from this plugin are displayed. + * + * @return string|string[] The script file path(s) */ public function getScripts(): string|array; /** - * @return string|string[] + * Gets the CSS stylesheets needed by the plugin. + * + * These styles will be included in the page when notifications + * from this plugin are displayed. + * + * @return string|string[] The stylesheet file path(s) */ public function getStyles(): string|array; /** - * @return array + * Gets the default configuration options for the plugin. + * + * These options control the appearance and behavior of notifications + * created by this plugin. + * + * @return array The default options */ public function getOptions(): array; + /** + * Gets the directory containing the plugin's assets. + * + * This directory contains the public files (JS, CSS, images) used by the plugin. + * + * @return string The absolute path to the assets directory + */ public function getAssetsDir(): string; + /** + * Gets the directory containing the plugin's resources. + * + * This directory contains templates, configurations, and other non-public files. + * + * @return string The absolute path to the resources directory + */ public function getResourcesDir(): string; /** + * Normalizes the plugin configuration. + * + * This method takes a raw configuration array and transforms it into a + * standardized format with all required keys and proper value types. + * * @param array{ * scripts?: string|string[], * styles?: string|string[], * options?: array, - * } $config + * } $config The raw configuration * * @return array{ * scripts: string[], * styles: string[], * options: array, - * } + * } The normalized configuration */ public function normalizeConfig(array $config): array; } diff --git a/src/Prime/Response/Presenter/ArrayPresenter.php b/src/Prime/Response/Presenter/ArrayPresenter.php index ac8011f0..99abd92c 100644 --- a/src/Prime/Response/Presenter/ArrayPresenter.php +++ b/src/Prime/Response/Presenter/ArrayPresenter.php @@ -7,6 +7,17 @@ namespace Flasher\Prime\Response\Presenter; use Flasher\Prime\Response\Response; /** + * ArrayPresenter - Presents notifications as a PHP array structure. + * + * This presenter converts a Response object into a PHP array structure that can + * be used for further processing, serialization, or API responses. It's particularly + * useful as a base for other presenters that need structured data rather than + * rendered output. + * + * Design patterns: + * - Presenter: Transforms domain objects (Notifications) into a presentation format + * - Adapter: Converts internal representation to a format suitable for external use + * * @phpstan-type ArrayPresenterType array{ * envelopes: arraymainScript) { @@ -61,6 +85,15 @@ final readonly class ResourceManager implements ResourceManagerInterface return $response; } + /** + * Adds an HTML stamp to a notification envelope. + * + * This method renders a template for the notification and attaches the + * rendered HTML as a stamp on the envelope. + * + * @param string $view The template view name + * @param Envelope $envelope The notification envelope + */ private function addHtmlStamp(string $view, Envelope $envelope): void { $compiled = $this->templateEngine->render($view, ['envelope' => $envelope]); @@ -68,6 +101,15 @@ final readonly class ResourceManager implements ResourceManagerInterface $envelope->withStamp(new HtmlStamp($compiled)); } + /** + * Adds resources for a plugin to a response. + * + * This method retrieves the resource configuration for a plugin and adds + * its scripts, styles, and options to the response. + * + * @param Response $response The response to populate + * @param string $plugin The plugin alias + */ private function addResources(Response $response, string $plugin): void { $resource = $this->resources[$plugin] ?? []; diff --git a/src/Prime/Response/Resource/ResourceManagerInterface.php b/src/Prime/Response/Resource/ResourceManagerInterface.php index 0793f9a7..805e9b4a 100644 --- a/src/Prime/Response/Resource/ResourceManagerInterface.php +++ b/src/Prime/Response/Resource/ResourceManagerInterface.php @@ -6,7 +6,27 @@ namespace Flasher\Prime\Response\Resource; use Flasher\Prime\Response\Response; +/** + * ResourceManagerInterface - Contract for managing notification resources. + * + * This interface defines the essential operation for populating Response objects + * with the resources (scripts, styles, options) required by the notifications + * they contain. + * + * Design pattern: Builder - Defines an interface for incrementally building + * complete responses with all required resources. + */ interface ResourceManagerInterface { + /** + * Populates a response with required resources. + * + * This method should analyze the notifications in the response and add + * the scripts, styles, and options needed to render them properly. + * + * @param Response $response The response to populate + * + * @return Response The populated response + */ public function populateResponse(Response $response): Response; } diff --git a/src/Prime/Response/Response.php b/src/Prime/Response/Response.php index 924367b5..5273c077 100644 --- a/src/Prime/Response/Response.php +++ b/src/Prime/Response/Response.php @@ -6,37 +6,62 @@ namespace Flasher\Prime\Response; use Flasher\Prime\Notification\Envelope; +/** + * Response - Container for notification data ready for presentation. + * + * This class encapsulates all the data needed to render notifications in a response, + * including the notification envelopes themselves, scripts, styles, options, and context. + * It serves as a data transfer object between the notification storage and presenters. + * + * Design patterns: + * - Data Transfer Object (DTO): Aggregates all data needed for notification rendering + * - Immutable Collection: Core envelopes and context are immutable while allowing + * controlled mutation of presentation resources + */ final class Response { + /** + * The main script to be included in the response. + */ private string $mainScript = ''; /** + * Additional JavaScript files to be included in the response. + * * @var string[] */ private array $scripts = []; /** + * CSS stylesheets to be included in the response. + * * @var string[] */ private array $styles = []; /** + * Plugin-specific options organized by plugin alias. + * * @var array> */ private array $options = []; /** - * @param Envelope[] $envelopes the array of notification envelopes - * @param array $context additional context for the response + * Creates a new Response instance. + * + * @param Envelope[] $envelopes The notification envelopes to present + * @param array $context Additional context for the presentation (e.g., CSP nonces) */ public function __construct(private readonly array $envelopes, private readonly array $context) { } /** - * Add scripts to the response. + * Adds JavaScript scripts to the response. * - * @param string[] $scripts the scripts to add + * This method appends new scripts to the existing list, ensuring uniqueness. + * + * @param string[] $scripts The scripts to add */ public function addScripts(array $scripts): void { @@ -44,9 +69,11 @@ final class Response } /** - * Add styles to the response. + * Adds CSS stylesheets to the response. * - * @param string[] $styles the styles to add + * This method appends new styles to the existing list, ensuring uniqueness. + * + * @param string[] $styles The styles to add */ public function addStyles(array $styles): void { @@ -54,10 +81,13 @@ final class Response } /** - * Add or merge options for a specific alias. + * Adds or merges options for a specific plugin. * - * @param string $alias the alias for the options - * @param array $options the options to add or merge + * This method merges new options with existing ones for the specified alias, + * creating the entry if it doesn't exist. + * + * @param string $alias The plugin alias + * @param array $options The options to add or merge */ public function addOptions(string $alias, array $options): void { @@ -66,25 +96,39 @@ final class Response } /** - * @return Envelope[] + * Gets the notification envelopes to be presented. + * + * @return Envelope[] The notification envelopes */ public function getEnvelopes(): array { return $this->envelopes; } + /** + * Gets the main script path. + * + * @return string The path to the main JavaScript file + */ public function getMainScript(): string { return $this->mainScript; } + /** + * Sets the main script path. + * + * @param string $mainScript The path to the main JavaScript file + */ public function setMainScript(string $mainScript): void { $this->mainScript = $mainScript; } /** - * @return string[] + * Gets the CSS stylesheets. + * + * @return string[] The stylesheet paths */ public function getStyles(): array { @@ -92,7 +136,9 @@ final class Response } /** - * @return string[] + * Gets the JavaScript scripts. + * + * @return string[] The script paths */ public function getScripts(): array { @@ -100,7 +146,9 @@ final class Response } /** - * @return array> + * Gets the plugin-specific options. + * + * @return array> The options organized by plugin alias */ public function getOptions(): array { @@ -108,7 +156,9 @@ final class Response } /** - * @return array + * Gets the presentation context. + * + * @return array The context data */ public function getContext(): array { @@ -116,6 +166,11 @@ final class Response } /** + * Converts the response to an array representation. + * + * This method transforms all data in the response, including converting notification + * envelopes to arrays, into a format suitable for serialization or rendering. + * * @return array{ * envelopes: arrayaddPresenter('array', fn () => new ArrayPresenter()); } + /** + * {@inheritdoc} + * + * This implementation follows a multi-step process: + * 1. Filters notifications from storage based on criteria + * 2. Removes rendered notifications from storage + * 3. Dispatches a PresentationEvent to allow listeners to modify notifications + * 4. Creates a Response object with notifications and context + * 5. Uses the appropriate presenter to render the response + * 6. Dispatches a ResponseEvent to allow final modifications + */ public function render(string $presenter = 'html', array $criteria = [], array $context = []): mixed { $envelopes = $this->storageManager->filter($criteria); @@ -54,6 +86,18 @@ final class ResponseManager implements ResponseManagerInterface $this->presenters[$alias] = $presenter; } + /** + * Creates a presenter instance for the specified format. + * + * This method retrieves a presenter by alias from the registry and instantiates + * it if it's a factory closure. + * + * @param string $alias The presenter alias + * + * @return PresenterInterface The presenter instance + * + * @throws PresenterNotFoundException If no presenter is registered with the given alias + */ private function createPresenter(string $alias): PresenterInterface { if (!isset($this->presenters[$alias])) { @@ -66,8 +110,15 @@ final class ResponseManager implements ResponseManagerInterface } /** - * @param Envelope[] $envelopes - * @param array $context + * Creates a Response object with notifications and context. + * + * This method creates a basic Response object and then populates it with + * resources (scripts, styles, options) using the ResourceManager. + * + * @param Envelope[] $envelopes The notification envelopes + * @param array $context Additional context for the presentation + * + * @return Response The populated response object */ private function createResponse(array $envelopes, array $context): Response { @@ -76,6 +127,17 @@ final class ResponseManager implements ResponseManagerInterface return $this->resourceManager->populateResponse($response); } + /** + * Uses a presenter to render a response. + * + * This method selects the appropriate presenter based on the requested format + * and uses it to render the response. + * + * @param Response $response The response to render + * @param string $presenter The presenter format to use + * + * @return mixed The rendered result + */ private function presentResponse(Response $response, string $presenter): mixed { $presenter = $this->createPresenter($presenter); diff --git a/src/Prime/Response/ResponseManagerInterface.php b/src/Prime/Response/ResponseManagerInterface.php index 4ba12a19..a0db9a16 100644 --- a/src/Prime/Response/ResponseManagerInterface.php +++ b/src/Prime/Response/ResponseManagerInterface.php @@ -8,13 +8,33 @@ use Flasher\Prime\Response\Presenter\ArrayPresenter; use Flasher\Prime\Response\Presenter\PresenterInterface; /** + * ResponseManagerInterface - Contract for response generation and presenter management. + * + * This interface defines the essential operations for creating responses with + * notifications and managing the presenters that render them in different formats. + * It serves as the main entry point for generating notification responses in + * specific formats. + * + * Design patterns: + * - Strategy: Manages different presentation strategies through presenters + * - Factory: Creates responses with specific presenters based on requested format + * * @phpstan-import-type ArrayPresenterType from ArrayPresenter */ interface ResponseManagerInterface { /** - * @param array $criteria - * @param array $context + * Renders notifications in a specific format. + * + * This method filters notifications based on criteria, removes them from storage, + * and renders them using the specified presenter. It provides sophisticated return + * type information for different presenter formats. + * + * @param string $presenter The presenter format to use (e.g., 'html', 'array', 'json') + * @param array $criteria Filtering criteria for selecting notifications + * @param array $context Additional context for the presentation + * + * @return mixed The rendered result in the requested format * * @phpstan-return ($presenter is 'html' ? string : * ($presenter is 'array' ? ArrayPresenterType : @@ -23,5 +43,15 @@ interface ResponseManagerInterface */ public function render(string $presenter = 'html', array $criteria = [], array $context = []): mixed; + /** + * Registers a presenter with the response manager. + * + * This method allows adding custom presenters that can render notifications + * in different formats. The presenter can be provided either as an instance + * or as a factory callback. + * + * @param string $alias The alias/name for the presenter + * @param callable|PresenterInterface $presenter The presenter instance or factory + */ public function addPresenter(string $alias, callable|PresenterInterface $presenter): void; } diff --git a/src/Prime/Stamp/ContextStamp.php b/src/Prime/Stamp/ContextStamp.php index d06a5c4d..6c2e0872 100644 --- a/src/Prime/Stamp/ContextStamp.php +++ b/src/Prime/Stamp/ContextStamp.php @@ -4,17 +4,32 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * ContextStamp - Stores additional context data for a notification. + * + * This stamp provides a way to attach arbitrary context data to a notification. + * This context data can be used by presenters, templates, or frontend code to + * customize how the notification is rendered or processed. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Memento: Captures and externalizes an object's internal state + */ final readonly class ContextStamp implements PresentableStampInterface, StampInterface { /** - * @param array $context + * Creates a new ContextStamp instance. + * + * @param array $context The context data to attach */ public function __construct(private array $context) { } /** - * @return array + * Gets the context data. + * + * @return array The context data */ public function getContext(): array { @@ -22,7 +37,11 @@ final readonly class ContextStamp implements PresentableStampInterface, StampInt } /** - * @return array{context: array} + * Converts the stamp to an array representation. + * + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{context: array} The array representation */ public function toArray(): array { diff --git a/src/Prime/Stamp/CreatedAtStamp.php b/src/Prime/Stamp/CreatedAtStamp.php index b2e78549..4cd18c8d 100644 --- a/src/Prime/Stamp/CreatedAtStamp.php +++ b/src/Prime/Stamp/CreatedAtStamp.php @@ -4,15 +4,35 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * CreatedAtStamp - Records when a notification was created. + * + * This stamp stores the creation time of a notification, which can be used + * for ordering, filtering, or display purposes. It implements OrderableStampInterface + * to enable sorting notifications by creation time. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Temporal Marker: Records a point in time for domain objects + * - Comparable: Implements comparison logic for time-based sorting + */ final readonly class CreatedAtStamp implements OrderableStampInterface, PresentableStampInterface, StampInterface { + /** + * The creation timestamp. + */ private \DateTimeImmutable $createdAt; + /** + * The format string for date presentation. + */ private string $format; /** - * @param \DateTimeImmutable|null $createdAt the datetime object representing the creation time - * @param string|null $format the format in which the datetime should be presented + * Creates a new CreatedAtStamp instance. + * + * @param \DateTimeImmutable|null $createdAt The datetime object representing the creation time + * @param string|null $format The format in which the datetime should be presented */ public function __construct(?\DateTimeImmutable $createdAt = null, ?string $format = null) { @@ -20,19 +40,25 @@ final readonly class CreatedAtStamp implements OrderableStampInterface, Presenta $this->format = $format ?: 'Y-m-d H:i:s'; } + /** + * Gets the creation timestamp. + * + * @return \DateTimeImmutable The creation timestamp + */ public function getCreatedAt(): \DateTimeImmutable { return $this->createdAt; } /** - * Compares the current stamp with another orderable stamp. + * Compares this stamp with another for ordering purposes. * - * @param StampInterface $orderable the stamp to compare with + * This method implements the comparison logic required by OrderableStampInterface. + * It orders notifications chronologically by creation time. * - * @return int returns less than 0 if current is less than the given stamp, - * greater than 0 if current is greater - * and 0 if they are equal + * @param StampInterface $orderable The stamp to compare with + * + * @return int Negative if this is older, positive if newer, zero if same time */ public function compare(StampInterface $orderable): int { @@ -44,7 +70,11 @@ final readonly class CreatedAtStamp implements OrderableStampInterface, Presenta } /** - * @return array{created_at: string} returns an associative array representation of the stamp + * Converts the stamp to an array representation. + * + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{created_at: string} The array representation with formatted timestamp */ public function toArray(): array { diff --git a/src/Prime/Stamp/DelayStamp.php b/src/Prime/Stamp/DelayStamp.php index 2976f609..a97cb943 100644 --- a/src/Prime/Stamp/DelayStamp.php +++ b/src/Prime/Stamp/DelayStamp.php @@ -4,12 +4,31 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * DelayStamp - Controls notification display delay. + * + * This stamp specifies how long to wait (in milliseconds) before displaying + * a notification. It allows for scheduling notifications to appear after a + * certain amount of time has passed. + * + * Design pattern: Value Object - Immutable object representing a specific concept + */ final readonly class DelayStamp implements StampInterface { + /** + * Creates a new DelayStamp instance. + * + * @param int $delay The delay in milliseconds before displaying the notification + */ public function __construct(private int $delay) { } + /** + * Gets the delay value. + * + * @return int The delay in milliseconds + */ public function getDelay(): int { return $this->delay; diff --git a/src/Prime/Stamp/HopsStamp.php b/src/Prime/Stamp/HopsStamp.php index 2d653330..2420e956 100644 --- a/src/Prime/Stamp/HopsStamp.php +++ b/src/Prime/Stamp/HopsStamp.php @@ -4,12 +4,33 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * HopsStamp - Controls notification persistence across requests. + * + * This stamp defines how many request cycles ("hops") a notification should persist for. + * A value of 1 means the notification will only appear in the current request, + * while higher values allow notifications to survive across redirects or page loads. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Lifecycle Control: Controls the lifecycle of a notification across requests + */ final readonly class HopsStamp implements StampInterface { + /** + * Creates a new HopsStamp instance. + * + * @param int $amount The number of request cycles the notification should persist for + */ public function __construct(private int $amount) { } + /** + * Gets the amount of hops. + * + * @return int The number of request cycles + */ public function getAmount(): int { return $this->amount; diff --git a/src/Prime/Stamp/HtmlStamp.php b/src/Prime/Stamp/HtmlStamp.php index 43c10fc8..865d1f7b 100644 --- a/src/Prime/Stamp/HtmlStamp.php +++ b/src/Prime/Stamp/HtmlStamp.php @@ -4,17 +4,45 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * HtmlStamp - Contains prerendered HTML content for a notification. + * + * This stamp stores HTML content that should be rendered directly in the page + * instead of being processed by the JavaScript notification library. It's useful + * for complex notifications that require custom markup or server-rendered content. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Memento: Captures and externalizes an object's internal state + */ final readonly class HtmlStamp implements StampInterface, PresentableStampInterface { + /** + * Creates a new HtmlStamp instance. + * + * @param string $html The prerendered HTML content + */ public function __construct(private string $html) { } + /** + * Gets the HTML content. + * + * @return string The prerendered HTML content + */ public function getHtml(): string { return $this->html; } + /** + * Converts the stamp to an array representation. + * + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{html: string} The array representation + */ public function toArray(): array { return ['html' => $this->html]; diff --git a/src/Prime/Stamp/IdStamp.php b/src/Prime/Stamp/IdStamp.php index 9eb91188..98d798f8 100644 --- a/src/Prime/Stamp/IdStamp.php +++ b/src/Prime/Stamp/IdStamp.php @@ -6,12 +6,27 @@ namespace Flasher\Prime\Stamp; use Flasher\Prime\Notification\Envelope; +/** + * IdStamp - Provides a unique identifier for notifications. + * + * This stamp assigns a unique identifier to each notification, which can be + * used for referencing, tracking, or manipulating specific notifications. It + * also provides utility methods for working with collections of notifications. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Identity: Provides a unique identifier for domain objects + * - Collection Utility: Provides methods for working with notification collections + */ final readonly class IdStamp implements PresentableStampInterface, StampInterface { + /** + * The unique identifier. + */ private string $id; /** - * Constructs an IdStamp with a unique identifier. + * Creates a new IdStamp instance. * * @param string|null $id The identifier. If not provided, a unique identifier is generated. */ @@ -23,7 +38,10 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac /** * Generates a unique identifier. * - * @return string the generated unique identifier + * This method attempts to use cryptographically secure random bytes for the ID, + * with a fallback to uniqid() if random_bytes() fails. + * + * @return string The generated unique identifier */ private function generateUniqueId(): string { @@ -39,9 +57,12 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac /** * Indexes an array of envelopes by their ID. * - * @param Envelope[] $envelopes an array of envelopes to index + * This utility method creates a map of envelopes keyed by their unique IDs, + * adding IdStamps to envelopes that don't already have them. * - * @return array an associative array of envelopes indexed by their ID + * @param Envelope[] $envelopes An array of envelopes to index + * + * @return array An associative array of envelopes indexed by their ID */ public static function indexById(array $envelopes): array { @@ -65,7 +86,7 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac /** * Gets the identifier. * - * @return string the identifier + * @return string The identifier */ public function getId(): string { @@ -73,9 +94,11 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac } /** - * Converts the stamp to an array. + * Converts the stamp to an array representation. * - * @return array{id: string} an associative array with the identifier + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{id: string} The array representation */ public function toArray(): array { diff --git a/src/Prime/Stamp/OrderableStampInterface.php b/src/Prime/Stamp/OrderableStampInterface.php index e0def512..b03ab7a2 100644 --- a/src/Prime/Stamp/OrderableStampInterface.php +++ b/src/Prime/Stamp/OrderableStampInterface.php @@ -4,7 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * OrderableStampInterface - Contract for stamps that affect notification ordering. + * + * This interface identifies stamps that provide ordering behavior for notifications. + * It defines a comparison method that can be used to sort notifications based on + * stamp-specific criteria. + * + * Design patterns: + * - Comparable: Defines an interface for objects that can be compared for ordering + * - Strategy: Allows different ordering strategies to be implemented and used interchangeably + */ interface OrderableStampInterface { + /** + * Compares this stamp with another for determining ordering. + * + * This method should return: + * - A negative value if this stamp should be ordered before the other + * - A positive value if this stamp should be ordered after the other + * - Zero if the stamps are equivalent for ordering purposes + * + * @param StampInterface $orderable The stamp to compare with + * + * @return int Negative if before, positive if after, zero if equivalent + */ public function compare(StampInterface $orderable): int; } diff --git a/src/Prime/Stamp/PluginStamp.php b/src/Prime/Stamp/PluginStamp.php index f5262cdd..b16e0c05 100644 --- a/src/Prime/Stamp/PluginStamp.php +++ b/src/Prime/Stamp/PluginStamp.php @@ -4,19 +4,44 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * PluginStamp - Associates a notification with a specific plugin. + * + * This stamp identifies which plugin should handle a notification. It ensures + * that notifications are rendered with the correct plugin's resources and options. + * This information is also serialized into the notification's metadata. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Strategy Identifier: Identifies which rendering strategy to use + */ final readonly class PluginStamp implements PresentableStampInterface, StampInterface { + /** + * Creates a new PluginStamp instance. + * + * @param string $plugin The plugin alias (e.g., 'toastr', 'sweetalert') + */ public function __construct(private string $plugin) { } + /** + * Gets the plugin alias. + * + * @return string The plugin alias + */ public function getPlugin(): string { return $this->plugin; } /** - * @return array{plugin: string} + * Converts the stamp to an array representation. + * + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{plugin: string} The array representation */ public function toArray(): array { diff --git a/src/Prime/Stamp/PresentableStampInterface.php b/src/Prime/Stamp/PresentableStampInterface.php index 50628085..8519c874 100644 --- a/src/Prime/Stamp/PresentableStampInterface.php +++ b/src/Prime/Stamp/PresentableStampInterface.php @@ -4,10 +4,26 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * PresentableStampInterface - Contract for stamps that contribute to presentation. + * + * This interface identifies stamps that should include their data in the notification's + * serialized form when being presented. Stamps implementing this interface will have + * their data included in the notification's metadata when rendered. + * + * Design patterns: + * - Marker Interface: Identifies stamps that provide presentation metadata + * - Serializable: Defines a standard way to convert objects to serializable data + */ interface PresentableStampInterface { /** - * @return array + * Converts the stamp to an array representation. + * + * This method should return an associative array containing the stamp's + * data in a form that can be serialized and used in presentation. + * + * @return array The array representation */ public function toArray(): array; } diff --git a/src/Prime/Stamp/PresenterStamp.php b/src/Prime/Stamp/PresenterStamp.php index 3ed9da94..148c8139 100644 --- a/src/Prime/Stamp/PresenterStamp.php +++ b/src/Prime/Stamp/PresenterStamp.php @@ -4,8 +4,27 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * PresenterStamp - Controls which presenters can render a notification. + * + * This stamp restricts which presenters can render a notification by specifying + * a regex pattern. Only presenters whose names match the pattern will be allowed + * to render the notification. This is useful for creating notifications that + * are only meant for specific presentation formats. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Filter: Restricts which presenters can render a notification + */ final readonly class PresenterStamp implements StampInterface { + /** + * Creates a new PresenterStamp instance. + * + * @param string $pattern A regex pattern that presenter names must match + * + * @throws \InvalidArgumentException If the provided pattern is not a valid regex + */ public function __construct(private string $pattern) { if (false === @preg_match($pattern, '')) { @@ -13,6 +32,11 @@ final readonly class PresenterStamp implements StampInterface } } + /** + * Gets the pattern value. + * + * @return string The regex pattern + */ public function getPattern(): string { return $this->pattern; diff --git a/src/Prime/Stamp/PresetStamp.php b/src/Prime/Stamp/PresetStamp.php index e3379b27..e9496660 100644 --- a/src/Prime/Stamp/PresetStamp.php +++ b/src/Prime/Stamp/PresetStamp.php @@ -4,22 +4,44 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * PresetStamp - Associates a notification with a predefined template. + * + * This stamp identifies which preset template should be used for a notification. + * Presets allow defining reusable notification templates with predefined types, + * titles, messages, and options. The stamp can also include template parameters + * for variable substitution. + * + * Design patterns: + * - Template Reference: References a predefined template to use + * - Parameter Carrier: Carries parameters for template variable substitution + */ final readonly class PresetStamp implements StampInterface { /** - * @param array $parameters + * Creates a new PresetStamp instance. + * + * @param string $preset The preset name to use + * @param array $parameters Template parameters for variable substitution */ public function __construct(private string $preset, private array $parameters = []) { } + /** + * Gets the preset name. + * + * @return string The preset name + */ public function getPreset(): string { return $this->preset; } /** - * @return array + * Gets the template parameters. + * + * @return array The template parameters */ public function getParameters(): array { diff --git a/src/Prime/Stamp/PriorityStamp.php b/src/Prime/Stamp/PriorityStamp.php index 4cb6fe6d..28ed25b4 100644 --- a/src/Prime/Stamp/PriorityStamp.php +++ b/src/Prime/Stamp/PriorityStamp.php @@ -4,17 +4,51 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * PriorityStamp - Controls notification priority for ordering. + * + * This stamp assigns a priority value to notifications, which affects their + * display order. Higher priority notifications are typically displayed before + * lower priority ones. This stamp also implements the OrderableStampInterface to + * participate in sorting operations and PresentableStampInterface to be included + * in the notification's metadata. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Comparable: Implements comparison logic for sorting + */ final readonly class PriorityStamp implements OrderableStampInterface, PresentableStampInterface, StampInterface { + /** + * Creates a new PriorityStamp instance. + * + * @param int $priority The priority value (higher values typically displayed first) + */ public function __construct(private int $priority) { } + /** + * Gets the priority value. + * + * @return int The priority value + */ public function getPriority(): int { return $this->priority; } + /** + * Compares this stamp with another for ordering purposes. + * + * This method implements the comparison logic required by OrderableStampInterface. + * It returns a positive value if this stamp has higher priority, negative if lower, + * or zero if equal. + * + * @param StampInterface $orderable The stamp to compare with + * + * @return int Negative if this has lower priority, positive if higher, zero if equal + */ public function compare(StampInterface $orderable): int { if (!$orderable instanceof self) { @@ -25,7 +59,11 @@ final readonly class PriorityStamp implements OrderableStampInterface, Presentab } /** - * @return array{priority: int} + * Converts the stamp to an array representation. + * + * This method implements the serialization logic required by PresentableStampInterface. + * + * @return array{priority: int} The array representation */ public function toArray(): array { diff --git a/src/Prime/Stamp/StampInterface.php b/src/Prime/Stamp/StampInterface.php index 883b413e..0f8306c9 100644 --- a/src/Prime/Stamp/StampInterface.php +++ b/src/Prime/Stamp/StampInterface.php @@ -4,6 +4,19 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * StampInterface - Marker interface for notification metadata. + * + * This interface is a base marker for all stamp classes that can be attached + * to notification envelopes. Stamps provide metadata and behavior modifiers + * for notifications, following the Envelope Pattern where the core notification + * is wrapped with various stamps that affect its behavior. + * + * Design patterns: + * - Marker Interface: Identifies classes that can serve as notification stamps + * - Envelope Pattern: Part of a system where a core object is wrapped with metadata + */ interface StampInterface { + // This is a marker interface with no methods } diff --git a/src/Prime/Stamp/TranslationStamp.php b/src/Prime/Stamp/TranslationStamp.php index 03dadeb1..0c75e283 100644 --- a/src/Prime/Stamp/TranslationStamp.php +++ b/src/Prime/Stamp/TranslationStamp.php @@ -4,23 +4,44 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * TranslationStamp - Provides translation parameters and locale for a notification. + * + * This stamp contains translation parameters and an optional locale override + * for translating notification content. It allows for customization of how + * notification titles and messages are translated. + * + * Design patterns: + * - Value Object: Immutable object representing a specific concept + * - Parameter Carrier: Carries parameters for translation processing + */ final readonly class TranslationStamp implements StampInterface { /** - * @param array $parameters + * Creates a new TranslationStamp instance. + * + * @param array $parameters Translation parameters for variable substitution + * @param string|null $locale Locale override (null uses default locale) */ public function __construct(private array $parameters = [], private ?string $locale = null) { } /** - * @return array + * Gets the translation parameters. + * + * @return array The translation parameters */ public function getParameters(): array { return $this->parameters; } + /** + * Gets the locale override. + * + * @return string|null The locale override, or null to use default locale + */ public function getLocale(): ?string { return $this->locale; diff --git a/src/Prime/Stamp/UnlessStamp.php b/src/Prime/Stamp/UnlessStamp.php index f14aeefa..7d8a2a64 100644 --- a/src/Prime/Stamp/UnlessStamp.php +++ b/src/Prime/Stamp/UnlessStamp.php @@ -4,12 +4,31 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * UnlessStamp - Controls conditional suppression of notifications. + * + * This stamp provides a way to conditionally suppress notifications based on + * a boolean condition. If the condition is true, the notification will be + * suppressed, otherwise it will be displayed. + * + * Design pattern: Specification - Encapsulates a business rule as a value object + */ final readonly class UnlessStamp implements StampInterface { + /** + * Creates a new UnlessStamp instance. + * + * @param bool $condition When true, the notification will be suppressed + */ public function __construct(private bool $condition) { } + /** + * Gets the condition value. + * + * @return bool True if the notification should be suppressed, false otherwise + */ public function getCondition(): bool { return $this->condition; diff --git a/src/Prime/Stamp/WhenStamp.php b/src/Prime/Stamp/WhenStamp.php index a5c4716b..fd387932 100644 --- a/src/Prime/Stamp/WhenStamp.php +++ b/src/Prime/Stamp/WhenStamp.php @@ -4,12 +4,31 @@ declare(strict_types=1); namespace Flasher\Prime\Stamp; +/** + * WhenStamp - Controls conditional display of notifications. + * + * This stamp provides a way to conditionally display notifications based on + * a boolean condition. If the condition is true, the notification will be + * displayed, otherwise it will be suppressed. + * + * Design pattern: Specification - Encapsulates a business rule as a value object + */ final readonly class WhenStamp implements StampInterface { + /** + * Creates a new WhenStamp instance. + * + * @param bool $condition When true, the notification will be displayed + */ public function __construct(private bool $condition) { } + /** + * Gets the condition value. + * + * @return bool True if the notification should be displayed, false otherwise + */ public function getCondition(): bool { return $this->condition; diff --git a/src/Prime/Storage/Bag/ArrayBag.php b/src/Prime/Storage/Bag/ArrayBag.php index cfe6e5fe..6119456b 100644 --- a/src/Prime/Storage/Bag/ArrayBag.php +++ b/src/Prime/Storage/Bag/ArrayBag.php @@ -6,18 +6,41 @@ namespace Flasher\Prime\Storage\Bag; use Flasher\Prime\Notification\Envelope; +/** + * ArrayBag - In-memory storage bag for notifications. + * + * This class provides a simple implementation of the storage bag interface + * that stores notification envelopes in a instance-level array. It's suitable + * for testing or applications with short-lived processes. + * + * Design pattern: Value Object - Provides a simple container for notification storage. + */ final class ArrayBag implements BagInterface { /** + * The stored notification envelopes. + * * @var Envelope[] */ private array $envelopes = []; + /** + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes + */ public function get(): array { return $this->envelopes; } + /** + * Sets the stored notification envelopes. + * + * This method replaces all existing envelopes with the provided ones. + * + * @param Envelope[] $envelopes The notification envelopes to store + */ public function set(array $envelopes): void { $this->envelopes = $envelopes; diff --git a/src/Prime/Storage/Bag/BagInterface.php b/src/Prime/Storage/Bag/BagInterface.php index ae946e5c..33711606 100644 --- a/src/Prime/Storage/Bag/BagInterface.php +++ b/src/Prime/Storage/Bag/BagInterface.php @@ -6,15 +6,31 @@ namespace Flasher\Prime\Storage\Bag; use Flasher\Prime\Notification\Envelope; +/** + * BagInterface - Contract for notification envelope storage containers. + * + * This interface defines the essential operations for a storage container + * that can hold notification envelopes. Different implementations can use + * different storage backends (array, session, etc.). + * + * Design pattern: Repository - Defines a simple contract for storing and + * retrieving notification envelopes. + */ interface BagInterface { /** - * @return Envelope[] + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes */ public function get(): array; /** - * @param Envelope[] $envelopes + * Sets the stored notification envelopes. + * + * This method should replace all existing envelopes with the provided ones. + * + * @param Envelope[] $envelopes The notification envelopes to store */ public function set(array $envelopes): void; } diff --git a/src/Prime/Storage/Bag/StaticBag.php b/src/Prime/Storage/Bag/StaticBag.php index 5c3f9ee2..1c75a508 100644 --- a/src/Prime/Storage/Bag/StaticBag.php +++ b/src/Prime/Storage/Bag/StaticBag.php @@ -6,18 +6,43 @@ namespace Flasher\Prime\Storage\Bag; use Flasher\Prime\Notification\Envelope; +/** + * StaticBag - Static/global storage bag for notifications. + * + * This class provides an implementation of the storage bag interface that + * stores notification envelopes in a static class-level array. This allows + * sharing notifications across multiple instances of the bag class. + * + * Design pattern: Value Object with Singleton state - Provides a container + * for notification storage with globally shared state. + */ final class StaticBag implements BagInterface { /** + * The stored notification envelopes (shared across all instances). + * * @var Envelope[] */ private static array $envelopes = []; + /** + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes + */ public function get(): array { return self::$envelopes; } + /** + * Sets the stored notification envelopes. + * + * This method replaces all existing envelopes with the provided ones + * in the shared static storage. + * + * @param Envelope[] $envelopes The notification envelopes to store + */ public function set(array $envelopes): void { self::$envelopes = $envelopes; diff --git a/src/Prime/Storage/Filter/Criteria/CriteriaInterface.php b/src/Prime/Storage/Filter/Criteria/CriteriaInterface.php index cb9581e5..f480cb0d 100644 --- a/src/Prime/Storage/Filter/Criteria/CriteriaInterface.php +++ b/src/Prime/Storage/Filter/Criteria/CriteriaInterface.php @@ -6,12 +6,27 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; +/** + * CriteriaInterface - Contract for notification filtering criteria. + * + * This interface defines the essential operation for filtering notification + * envelopes based on specific criteria. Each implementation defines its own + * logic for determining which envelopes match the criteria. + * + * Design pattern: Specification - Defines a clear, boolean-logic based way + * to check if an object satisfies some criteria. + */ interface CriteriaInterface { /** - * @param Envelope[] $envelopes + * Applies the criterion to filter notification envelopes. * - * @return Envelope[] + * This method should analyze the provided envelopes and return + * only those that match the criterion's conditions. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] The filtered notification envelopes */ public function apply(array $envelopes): array; } diff --git a/src/Prime/Storage/Filter/Criteria/DelayCriteria.php b/src/Prime/Storage/Filter/Criteria/DelayCriteria.php index cbf96112..2d78f626 100644 --- a/src/Prime/Storage/Filter/Criteria/DelayCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/DelayCriteria.php @@ -7,14 +7,38 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\DelayStamp; +/** + * DelayCriteria - Filters notifications by delay time. + * + * This criterion filters notification envelopes based on their delay time, + * which determines how long to wait before displaying a notification. + * It can filter for delays within a specific range (min to max) or above a minimum. + * + * Design pattern: Specification - Defines filter logic based on delay ranges. + */ final readonly class DelayCriteria implements CriteriaInterface { use RangeExtractor; + /** + * The minimum delay time in milliseconds (inclusive). + */ private ?int $minDelay; + /** + * The maximum delay time in milliseconds (inclusive). + */ private ?int $maxDelay; + /** + * Creates a new DelayCriteria instance. + * + * @param mixed $criteria The delay criteria, either: + * - An integer (minimum delay threshold) + * - An array with 'min' and/or 'max' keys + * + * @throws \InvalidArgumentException If the criteria format is invalid + */ public function __construct(mixed $criteria) { $criteria = $this->extractRange('priority', $criteria); @@ -23,11 +47,25 @@ final readonly class DelayCriteria implements CriteriaInterface $this->maxDelay = $criteria['max']; } + /** + * Filters envelopes by delay time. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] Envelopes that match the delay criteria + */ public function apply(array $envelopes): array { return array_filter($envelopes, fn (Envelope $envelope): bool => $this->match($envelope)); } + /** + * Checks if an envelope matches the delay criteria. + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope matches the criteria, false otherwise + */ public function match(Envelope $envelope): bool { $stamp = $envelope->get(DelayStamp::class); diff --git a/src/Prime/Storage/Filter/Criteria/FilterCriteria.php b/src/Prime/Storage/Filter/Criteria/FilterCriteria.php index d22caa22..ca7e9cd0 100644 --- a/src/Prime/Storage/Filter/Criteria/FilterCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/FilterCriteria.php @@ -4,15 +4,35 @@ declare(strict_types=1); namespace Flasher\Prime\Storage\Filter\Criteria; +use Flasher\Prime\Notification\Envelope; + +/** + * FilterCriteria - Applies custom closure-based filters to notifications. + * + * This criterion allows applying custom filtering logic provided as closures. + * It's a flexible way to implement complex or specific filtering needs that + * aren't covered by the standard criteria. + * + * Design pattern: Strategy - Encapsulates custom filtering algorithms provided + * as closures and applies them to notification collections. + */ final class FilterCriteria implements CriteriaInterface { /** + * The collection of filter callbacks. + * * @var \Closure[] */ private array $callbacks; /** - * @throws \InvalidArgumentException if the criteria type is invalid + * Creates a new FilterCriteria instance. + * + * @param mixed $criteria Either a single closure or an array of closures + * Each closure should accept an array of Envelope objects + * and return a filtered array of Envelope objects + * + * @throws \InvalidArgumentException If the criteria is not a closure or array of closures */ public function __construct(mixed $criteria) { @@ -32,6 +52,13 @@ final class FilterCriteria implements CriteriaInterface /** * Applies the filter callbacks to the envelopes. + * + * Each callback is applied in sequence, with the output of one becoming + * the input to the next. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] The filtered notification envelopes */ public function apply(array $envelopes): array { diff --git a/src/Prime/Storage/Filter/Criteria/HopsCriteria.php b/src/Prime/Storage/Filter/Criteria/HopsCriteria.php index ffb865b2..319436de 100644 --- a/src/Prime/Storage/Filter/Criteria/HopsCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/HopsCriteria.php @@ -7,14 +7,38 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\HopsStamp; +/** + * HopsCriteria - Filters notifications by hops count. + * + * This criterion filters notification envelopes based on their hops count, + * which determines how many requests/redirects a notification should persist for. + * It can filter for hops within a specific range (min to max) or above a minimum. + * + * Design pattern: Specification - Defines filter logic based on hops ranges. + */ final readonly class HopsCriteria implements CriteriaInterface { use RangeExtractor; + /** + * The minimum hops amount (inclusive). + */ private readonly ?int $minAmount; + /** + * The maximum hops amount (inclusive). + */ private readonly ?int $maxAmount; + /** + * Creates a new HopsCriteria instance. + * + * @param mixed $criteria The hops criteria, either: + * - An integer (minimum hops threshold) + * - An array with 'min' and/or 'max' keys + * + * @throws \InvalidArgumentException If the criteria format is invalid + */ public function __construct(mixed $criteria) { $criteria = $this->extractRange('priority', $criteria); @@ -23,11 +47,25 @@ final readonly class HopsCriteria implements CriteriaInterface $this->maxAmount = $criteria['max']; } + /** + * Filters envelopes by hops count. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] Envelopes that match the hops criteria + */ public function apply(array $envelopes): array { return array_filter($envelopes, fn (Envelope $e): bool => $this->match($e)); } + /** + * Checks if an envelope matches the hops criteria. + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope matches the criteria, false otherwise + */ public function match(Envelope $envelope): bool { $stamp = $envelope->get(HopsStamp::class); diff --git a/src/Prime/Storage/Filter/Criteria/LimitCriteria.php b/src/Prime/Storage/Filter/Criteria/LimitCriteria.php index d20e3595..9be697ae 100644 --- a/src/Prime/Storage/Filter/Criteria/LimitCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/LimitCriteria.php @@ -4,10 +4,31 @@ declare(strict_types=1); namespace Flasher\Prime\Storage\Filter\Criteria; +use Flasher\Prime\Notification\Envelope; + +/** + * LimitCriteria - Limits the number of notifications returned. + * + * This criterion doesn't filter based on notification content but instead + * limits the total number of notifications in the result set. It's useful + * for pagination or enforcing display limits. + * + * Design pattern: Specification - Defines filter logic based on result count. + */ final readonly class LimitCriteria implements CriteriaInterface { + /** + * The maximum number of notifications to return. + */ private int $limit; + /** + * Creates a new LimitCriteria instance. + * + * @param mixed $criteria The maximum number of notifications to return + * + * @throws \InvalidArgumentException If the criteria is not an integer + */ public function __construct(mixed $criteria) { if (!\is_int($criteria)) { @@ -17,6 +38,13 @@ final readonly class LimitCriteria implements CriteriaInterface $this->limit = $criteria; } + /** + * Limits the number of envelopes in the result set. + * + * @param Envelope[] $envelopes The notification envelopes to limit + * + * @return Envelope[] The limited set of notification envelopes + */ public function apply(array $envelopes): array { return \array_slice($envelopes, 0, $this->limit, true); diff --git a/src/Prime/Storage/Filter/Criteria/OrderByCriteria.php b/src/Prime/Storage/Filter/Criteria/OrderByCriteria.php index 2ac3af4b..b0f67ab8 100644 --- a/src/Prime/Storage/Filter/Criteria/OrderByCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/OrderByCriteria.php @@ -19,12 +19,30 @@ use Flasher\Prime\Stamp\TranslationStamp; use Flasher\Prime\Stamp\UnlessStamp; use Flasher\Prime\Stamp\WhenStamp; +/** + * OrderByCriteria - Sorts notifications based on stamp attributes. + * + * This criterion doesn't filter notifications but instead sorts them based + * on attributes from their stamps. It supports sorting by multiple fields + * in either ascending or descending order. + * + * Design pattern: Sorter - Defines sorting logic for collections. + */ final class OrderByCriteria implements CriteriaInterface { + /** + * Constant for ascending sort order. + */ public const ASC = 'ASC'; + + /** + * Constant for descending sort order. + */ public const DESC = 'DESC'; /** + * Map of friendly names to stamp class names. + * * @var array> */ private array $aliases = [ @@ -42,10 +60,21 @@ final class OrderByCriteria implements CriteriaInterface ]; /** + * The sort ordering configuration. + * * @var array, "ASC"|"DESC"> */ private array $orderings = []; + /** + * Creates a new OrderByCriteria instance. + * + * @param mixed $criteria The sort criteria, either: + * - A string with field name (defaults to ASC) + * - An array mapping field names to directions + * + * @throws \InvalidArgumentException If the criteria format is invalid + */ public function __construct(mixed $criteria) { if (!\is_string($criteria) && !\is_array($criteria)) { @@ -81,6 +110,13 @@ final class OrderByCriteria implements CriteriaInterface } } + /** + * Sorts the notification envelopes. + * + * @param Envelope[] $envelopes The notification envelopes to sort + * + * @return Envelope[] The sorted notification envelopes + */ public function apply(array $envelopes): array { usort($envelopes, function (Envelope $first, Envelope $second): int { diff --git a/src/Prime/Storage/Filter/Criteria/PresenterCriteria.php b/src/Prime/Storage/Filter/Criteria/PresenterCriteria.php index ae2fbd31..081f44db 100644 --- a/src/Prime/Storage/Filter/Criteria/PresenterCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/PresenterCriteria.php @@ -7,10 +7,29 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\PresenterStamp; +/** + * PresenterCriteria - Filters notifications by presenter compatibility. + * + * This criterion filters envelopes based on whether they are compatible + * with a specific presenter format. It uses regular expression patterns + * from PresenterStamp to determine compatibility. + * + * Design pattern: Specification - Defines filter logic based on presenter compatibility. + */ final class PresenterCriteria implements CriteriaInterface { + /** + * The presenter format to check for compatibility. + */ private string $presenter; + /** + * Creates a new PresenterCriteria instance. + * + * @param mixed $criteria The presenter format name (e.g., 'html', 'json') + * + * @throws \InvalidArgumentException If the criteria is not a string + */ public function __construct(mixed $criteria) { if (!\is_string($criteria)) { @@ -20,6 +39,16 @@ final class PresenterCriteria implements CriteriaInterface $this->presenter = $criteria; } + /** + * Filters envelopes by presenter compatibility. + * + * An envelope is compatible if it either has no presenter restrictions + * or has a presenter pattern that matches the specified presenter format. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] Envelopes that are compatible with the presenter + */ public function apply(array $envelopes): array { return array_filter($envelopes, function (Envelope $envelope) { diff --git a/src/Prime/Storage/Filter/Criteria/PriorityCriteria.php b/src/Prime/Storage/Filter/Criteria/PriorityCriteria.php index 3a3b08c6..b84199c8 100644 --- a/src/Prime/Storage/Filter/Criteria/PriorityCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/PriorityCriteria.php @@ -7,14 +7,38 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\PriorityStamp; +/** + * PriorityCriteria - Filters notifications by priority. + * + * This criterion filters notification envelopes based on their priority level. + * It can filter for priorities within a specific range (min to max) or above + * a minimum threshold. + * + * Design pattern: Specification - Defines filter logic based on priority ranges. + */ final readonly class PriorityCriteria implements CriteriaInterface { use RangeExtractor; + /** + * The minimum priority threshold (inclusive). + */ private ?int $minPriority; + /** + * The maximum priority threshold (inclusive). + */ private ?int $maxPriority; + /** + * Creates a new PriorityCriteria instance. + * + * @param mixed $criteria The priority criteria, either: + * - An integer (minimum priority threshold) + * - An array with 'min' and/or 'max' keys + * + * @throws \InvalidArgumentException If the criteria format is invalid + */ public function __construct(mixed $criteria) { $criteria = $this->extractRange('priority', $criteria); @@ -23,11 +47,25 @@ final readonly class PriorityCriteria implements CriteriaInterface $this->maxPriority = $criteria['max']; } + /** + * Filters envelopes by priority. + * + * @param Envelope[] $envelopes The envelopes to filter + * + * @return Envelope[] Envelopes that match the priority criteria + */ public function apply(array $envelopes): array { return array_filter($envelopes, fn (Envelope $envelope): bool => $this->match($envelope)); } + /** + * Checks if an envelope matches the priority criteria. + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope matches the criteria, false otherwise + */ public function match(Envelope $envelope): bool { $stamp = $envelope->get(PriorityStamp::class); diff --git a/src/Prime/Storage/Filter/Criteria/RangeExtractor.php b/src/Prime/Storage/Filter/Criteria/RangeExtractor.php index 1b390001..91c98b09 100644 --- a/src/Prime/Storage/Filter/Criteria/RangeExtractor.php +++ b/src/Prime/Storage/Filter/Criteria/RangeExtractor.php @@ -4,17 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Storage\Filter\Criteria; +/** + * RangeExtractor - Helper trait for extracting range values from criteria. + * + * This trait provides common functionality for criteria that operate on + * numeric ranges. It standardizes how min/max range values are extracted + * from various input formats. + * + * Design pattern: Utility - Provides reusable functionality across multiple classes. + */ trait RangeExtractor { /** * Extracts a range from the given criteria. * - * @param string $name the name of the criteria - * @param mixed $criteria the criteria value + * This method standardizes range extraction from various input formats: + * - An integer value is treated as a minimum threshold + * - An array with 'min' and/or 'max' keys specifies a range * - * @return array{min: ?int, max: ?int} an associative array with 'min' and 'max' keys + * @param string $name The name of the criteria (for error messages) + * @param mixed $criteria The criteria value to extract range from * - * @throws \InvalidArgumentException if the criteria is not of an expected type + * @return array{min: ?int, max: ?int} An associative array with 'min' and 'max' keys + * + * @throws \InvalidArgumentException If the criteria is not of an expected type */ private function extractRange(string $name, mixed $criteria): array { diff --git a/src/Prime/Storage/Filter/Criteria/StampsCriteria.php b/src/Prime/Storage/Filter/Criteria/StampsCriteria.php index 55a01f4a..58186130 100644 --- a/src/Prime/Storage/Filter/Criteria/StampsCriteria.php +++ b/src/Prime/Storage/Filter/Criteria/StampsCriteria.php @@ -6,16 +6,43 @@ namespace Flasher\Prime\Storage\Filter\Criteria; use Flasher\Prime\Notification\Envelope; +/** + * StampsCriteria - Filters notifications by the presence of specific stamps. + * + * This criterion filters envelopes based on whether they have specific stamps. + * It supports two filtering strategies: + * - AND: Envelope must have all specified stamps + * - OR: Envelope must have at least one of the specified stamps + * + * Design pattern: Specification - Defines filter logic based on stamp presence. + */ final class StampsCriteria implements CriteriaInterface { + /** + * Strategy constant for requiring all stamps (logical AND). + */ public const STRATEGY_AND = 'and'; + + /** + * Strategy constant for requiring at least one stamp (logical OR). + */ public const STRATEGY_OR = 'or'; /** + * The stamps to check for. + * * @var array */ private array $stamps = []; + /** + * Creates a new StampsCriteria instance. + * + * @param mixed $criteria An array of stamp class names to check for + * @param string $strategy The matching strategy to use (AND or OR) + * + * @throws \InvalidArgumentException If the criteria is not an array + */ public function __construct(mixed $criteria, private readonly string $strategy = self::STRATEGY_AND) { if (!\is_array($criteria)) { @@ -27,11 +54,25 @@ final class StampsCriteria implements CriteriaInterface } } + /** + * Filters envelopes based on stamp presence. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] Envelopes that match the stamp criteria + */ public function apply(array $envelopes): array { return array_filter($envelopes, fn (Envelope $e): bool => $this->match($e)); } + /** + * Checks if an envelope matches the stamps criteria. + * + * @param Envelope $envelope The envelope to check + * + * @return bool True if the envelope matches the criteria, false otherwise + */ public function match(Envelope $envelope): bool { $diff = array_diff($this->stamps, array_keys($envelope->all())); diff --git a/src/Prime/Storage/Filter/Filter.php b/src/Prime/Storage/Filter/Filter.php index 8b491154..67088b07 100644 --- a/src/Prime/Storage/Filter/Filter.php +++ b/src/Prime/Storage/Filter/Filter.php @@ -4,15 +4,37 @@ declare(strict_types=1); namespace Flasher\Prime\Storage\Filter; +use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Storage\Filter\Criteria\CriteriaInterface; +/** + * Filter - Default implementation of the filter interface. + * + * This class implements a chain of filtering criteria that are applied in sequence + * to notification envelopes. Each criterion refines the set of envelopes further. + * + * Design pattern: Chain of Responsibility - Each criterion in the chain has a chance + * to process the envelopes, potentially filtering some out, before passing to the next. + */ final class Filter implements FilterInterface { /** + * The chain of filtering criteria. + * * @var CriteriaInterface[] */ private array $criteriaChain = []; + /** + * Applies the filter to an array of notification envelopes. + * + * This method applies each criterion in the chain sequentially, + * passing the results from one to the next. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] The filtered notification envelopes + */ public function apply(array $envelopes): array { foreach ($this->criteriaChain as $criteria) { @@ -22,6 +44,13 @@ final class Filter implements FilterInterface return $envelopes; } + /** + * Adds a criterion to the filter chain. + * + * Each added criterion will be applied in the order they were added. + * + * @param CriteriaInterface $criteria The criterion to add + */ public function addCriteria(CriteriaInterface $criteria): void { $this->criteriaChain[] = $criteria; diff --git a/src/Prime/Storage/Filter/FilterFactory.php b/src/Prime/Storage/Filter/FilterFactory.php index 83f9ddc0..c810826a 100644 --- a/src/Prime/Storage/Filter/FilterFactory.php +++ b/src/Prime/Storage/Filter/FilterFactory.php @@ -15,13 +15,32 @@ use Flasher\Prime\Storage\Filter\Criteria\PresenterCriteria; use Flasher\Prime\Storage\Filter\Criteria\PriorityCriteria; use Flasher\Prime\Storage\Filter\Criteria\StampsCriteria; +/** + * FilterFactory - Creates and configures filter instances. + * + * This class is responsible for creating filter instances with the appropriate + * criteria based on configuration. It maintains a registry of available criteria + * implementations that can be used to build filters. + * + * Design patterns: + * - Factory: Creates and configures filters based on configuration + * - Registry: Maintains a collection of available criteria implementations + */ final class FilterFactory implements FilterFactoryInterface { /** + * Registry of available criteria implementations. + * * @var array */ private array $criteria = []; + /** + * Creates a new FilterFactory instance with default criteria. + * + * This constructor registers all the standard criteria types that can + * be used for filtering notifications. + */ public function __construct() { $criteriaClasses = [ @@ -40,6 +59,18 @@ final class FilterFactory implements FilterFactoryInterface } } + /** + * Creates a filter based on the provided configuration. + * + * This method creates a new filter and adds criteria to it based on + * the provided configuration keys and values. + * + * @param array $config Configuration for the filter criteria + * + * @return Filter The created filter with configured criteria + * + * @throws CriteriaNotRegisteredException If a requested criterion doesn't exist + */ public function createFilter(array $config): Filter { $filter = new Filter(); @@ -53,13 +84,33 @@ final class FilterFactory implements FilterFactoryInterface return $filter; } + /** + * Registers a new criterion implementation. + * + * This method allows adding custom criteria to the factory. The criterion + * can be provided either as an instance or as a factory callback. + * + * @param string $name The name of the criterion + * @param callable|CriteriaInterface $criteria The criterion implementation or factory + */ public function addCriteria(string $name, callable|CriteriaInterface $criteria): void { $this->criteria[$name] = $criteria; } /** - * @throws CriteriaNotRegisteredException + * Internal helper to create a criterion instance. + * + * This method looks up the requested criterion by name and creates an + * instance with the provided value. + * + * @param string $name The name of the criterion + * @param mixed $value The configuration value for the criterion + * + * @return CriteriaInterface The created criterion instance + * + * @throws CriteriaNotRegisteredException If the requested criterion doesn't exist + * @throws \UnexpectedValueException If the factory doesn't return a CriteriaInterface */ private function createCriteria(string $name, mixed $value): CriteriaInterface { diff --git a/src/Prime/Storage/Filter/FilterFactoryInterface.php b/src/Prime/Storage/Filter/FilterFactoryInterface.php index cbc4aaea..24404845 100644 --- a/src/Prime/Storage/Filter/FilterFactoryInterface.php +++ b/src/Prime/Storage/Filter/FilterFactoryInterface.php @@ -7,14 +7,34 @@ namespace Flasher\Prime\Storage\Filter; use Flasher\Prime\Exception\CriteriaNotRegisteredException; use Flasher\Prime\Storage\Filter\Criteria\CriteriaInterface; +/** + * FilterFactoryInterface - Contract for creating filter instances. + * + * This interface defines the operations required for a filter factory, + * which is responsible for creating and configuring filter instances + * based on provided configuration. + * + * Design pattern: Abstract Factory - Defines an interface for creating + * filter objects without specifying their concrete classes. + */ interface FilterFactoryInterface { /** - * @param array $config + * Creates a filter based on the provided configuration. * - * @throws CriteriaNotRegisteredException + * @param array $config Configuration for the filter criteria + * + * @return Filter The created filter with configured criteria + * + * @throws CriteriaNotRegisteredException If a requested criterion doesn't exist */ public function createFilter(array $config): Filter; + /** + * Registers a new criterion implementation. + * + * @param string $name The name of the criterion + * @param callable|CriteriaInterface $criteria The criterion implementation or factory + */ public function addCriteria(string $name, callable|CriteriaInterface $criteria): void; } diff --git a/src/Prime/Storage/Filter/FilterInterface.php b/src/Prime/Storage/Filter/FilterInterface.php index baea67ea..112c1826 100644 --- a/src/Prime/Storage/Filter/FilterInterface.php +++ b/src/Prime/Storage/Filter/FilterInterface.php @@ -7,14 +7,36 @@ namespace Flasher\Prime\Storage\Filter; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Storage\Filter\Criteria\CriteriaInterface; +/** + * FilterInterface - Contract for notification envelope filters. + * + * This interface defines the essential operations for filtering notification + * envelopes. It allows composing multiple criteria into a single filter chain. + * + * Design pattern: Chain of Responsibility - Allows chaining multiple filtering + * criteria that are applied in sequence. + */ interface FilterInterface { /** - * @param Envelope[] $envelopes + * Applies the filter to an array of notification envelopes. * - * @return Envelope[] + * This method should filter the provided envelopes according to + * the filter's criteria and return the matching subset. + * + * @param Envelope[] $envelopes The notification envelopes to filter + * + * @return Envelope[] The filtered notification envelopes */ public function apply(array $envelopes): array; + /** + * Adds a criterion to the filter chain. + * + * This method should add the provided criterion to the filter, + * extending its filtering capabilities. + * + * @param CriteriaInterface $criteria The criterion to add + */ public function addCriteria(CriteriaInterface $criteria): void; } diff --git a/src/Prime/Storage/Storage.php b/src/Prime/Storage/Storage.php index 872f3091..a05c91a5 100644 --- a/src/Prime/Storage/Storage.php +++ b/src/Prime/Storage/Storage.php @@ -8,27 +8,65 @@ use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Stamp\IdStamp; use Flasher\Prime\Storage\Bag\BagInterface; +/** + * Storage - Default implementation of the storage interface. + * + * This class provides a standard implementation of StorageInterface that + * delegates to a storage bag. It handles identity tracking using IdStamp + * to ensure unique envelopes in storage. + * + * Design patterns: + * - Adapter: Adapts the bag interface to the storage interface + * - Decorator: Adds identity tracking to the underlying storage bag + */ final readonly class Storage implements StorageInterface { + /** + * Creates a new Storage instance. + * + * @param BagInterface $bag The underlying storage bag implementation + */ public function __construct(private BagInterface $bag) { } + /** + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes as values + */ public function all(): array { return array_values($this->bag->get()); } + /** + * Adds one or more notification envelopes to the storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to store + */ public function add(Envelope ...$envelopes): void { $this->save(...$envelopes); } + /** + * Updates one or more notification envelopes in the storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to update + */ public function update(Envelope ...$envelopes): void { $this->save(...$envelopes); } + /** + * Removes one or more notification envelopes from the storage. + * + * This method filters out the specified envelopes from storage based on their IDs. + * + * @param Envelope ...$envelopes One or more notification envelopes to remove + */ public function remove(Envelope ...$envelopes): void { $envelopes = IdStamp::indexById($envelopes); @@ -38,11 +76,22 @@ final readonly class Storage implements StorageInterface $this->bag->set(array_values($envelopes)); } + /** + * Clears all notification envelopes from the storage. + */ public function clear(): void { $this->bag->set([]); } + /** + * Internal helper method to save envelopes. + * + * This method indexes envelopes by their IDs, merges them with existing envelopes, + * and updates the storage bag. New envelopes replace existing ones with the same ID. + * + * @param Envelope ...$envelopes One or more notification envelopes to save + */ private function save(Envelope ...$envelopes): void { $envelopes = IdStamp::indexById($envelopes); diff --git a/src/Prime/Storage/StorageInterface.php b/src/Prime/Storage/StorageInterface.php index 77f26a39..7c9a39f9 100644 --- a/src/Prime/Storage/StorageInterface.php +++ b/src/Prime/Storage/StorageInterface.php @@ -6,18 +6,51 @@ namespace Flasher\Prime\Storage; use Flasher\Prime\Notification\Envelope; +/** + * StorageInterface - Core contract for notification storage implementations. + * + * This interface defines the essential operations for managing notifications + * in a storage system. Implementations can range from simple in-memory arrays + * to persistent storage like sessions, databases, or files. + * + * Design pattern: Repository - Defines a common interface for storing and + * retrieving notification envelopes from various storage backends. + */ interface StorageInterface { /** - * @return Envelope[] + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes */ public function all(): array; + /** + * Adds one or more notification envelopes to the storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to store + */ public function add(Envelope ...$envelopes): void; + /** + * Updates one or more notification envelopes in the storage. + * + * This method should replace existing envelopes with the same identifiers, + * or add them if they don't exist yet. + * + * @param Envelope ...$envelopes One or more notification envelopes to update + */ public function update(Envelope ...$envelopes): void; + /** + * Removes one or more notification envelopes from the storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to remove + */ public function remove(Envelope ...$envelopes): void; + /** + * Clears all notification envelopes from the storage. + */ public function clear(): void; } diff --git a/src/Prime/Storage/StorageManager.php b/src/Prime/Storage/StorageManager.php index f69a59ce..f1482394 100644 --- a/src/Prime/Storage/StorageManager.php +++ b/src/Prime/Storage/StorageManager.php @@ -16,10 +16,26 @@ use Flasher\Prime\Exception\CriteriaNotRegisteredException; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Storage\Filter\FilterFactoryInterface; +/** + * StorageManager - Manages notification storage with event dispatch and filtering. + * + * This class orchestrates the storage of notifications, dispatching events before and + * after storage operations, and filtering notifications based on criteria. It acts as + * a mediator between the storage implementation and the rest of the system. + * + * Design patterns: + * - Mediator: Coordinates operations between storage, filters, and event dispatcher + * - Decorator: Adds event dispatching and filtering capabilities to the core storage + */ final readonly class StorageManager implements StorageManagerInterface { /** - * @param array $criteria + * Creates a new StorageManager instance. + * + * @param StorageInterface $storage The underlying storage implementation + * @param EventDispatcherInterface $eventDispatcher Event dispatcher for notification lifecycle events + * @param FilterFactoryInterface $filterFactory Factory for creating notification filters + * @param array $criteria Default criteria for filtering notifications */ public function __construct( private StorageInterface $storage, @@ -29,13 +45,28 @@ final readonly class StorageManager implements StorageManagerInterface ) { } + /** + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of notification envelopes + */ public function all(): array { return $this->storage->all(); } /** - * @throws CriteriaNotRegisteredException + * Filters notifications based on provided criteria. + * + * This method combines default criteria with the provided criteria, + * creates a filter using the filter factory, and applies it to the envelopes. + * Before applying the filter, it dispatches a FilterEvent to allow modification. + * + * @param array $criteria Filtering criteria + * + * @return Envelope[] Array of filtered notification envelopes + * + * @throws CriteriaNotRegisteredException If a requested filter criterion doesn't exist */ public function filter(array $criteria = []): array { @@ -48,6 +79,14 @@ final readonly class StorageManager implements StorageManagerInterface return $event->getFilter()->apply($event->getEnvelopes()); } + /** + * Adds one or more notification envelopes to storage. + * + * Before adding envelopes, it dispatches a PersistEvent to allow modification + * of the envelopes. After storage, it dispatches a PostPersistEvent. + * + * @param Envelope ...$envelopes One or more notification envelopes to store + */ public function add(Envelope ...$envelopes): void { $event = new PersistEvent($envelopes); @@ -59,6 +98,14 @@ final readonly class StorageManager implements StorageManagerInterface $this->eventDispatcher->dispatch($event); } + /** + * Updates one or more notification envelopes in storage. + * + * Before updating envelopes, it dispatches an UpdateEvent to allow modification + * of the envelopes. After update, it dispatches a PostUpdateEvent. + * + * @param Envelope ...$envelopes One or more notification envelopes to update + */ public function update(Envelope ...$envelopes): void { $event = new UpdateEvent($envelopes); @@ -70,6 +117,15 @@ final readonly class StorageManager implements StorageManagerInterface $this->eventDispatcher->dispatch($event); } + /** + * Removes one or more notification envelopes from storage. + * + * Before removal, it dispatches a RemoveEvent to allow listeners to modify + * which envelopes should be removed or kept. After removal, it dispatches + * a PostRemoveEvent. + * + * @param Envelope ...$envelopes One or more notification envelopes to remove + */ public function remove(Envelope ...$envelopes): void { $event = new RemoveEvent($envelopes); @@ -82,6 +138,9 @@ final readonly class StorageManager implements StorageManagerInterface $this->eventDispatcher->dispatch($event); } + /** + * Clears all notification envelopes from storage. + */ public function clear(): void { $this->storage->clear(); diff --git a/src/Prime/Storage/StorageManagerInterface.php b/src/Prime/Storage/StorageManagerInterface.php index 263e6c53..c4a3075b 100644 --- a/src/Prime/Storage/StorageManagerInterface.php +++ b/src/Prime/Storage/StorageManagerInterface.php @@ -6,25 +6,60 @@ namespace Flasher\Prime\Storage; use Flasher\Prime\Notification\Envelope; +/** + * StorageManagerInterface - Contract for notification storage management. + * + * This interface extends the basic storage operations with filtering capabilities. + * It defines the contract for the storage manager, which orchestrates the storage, + * retrieval, and filtering of notification envelopes. + * + * Design pattern: Mediator - Defines an interface for coordinating storage operations + * with additional capabilities like filtering and event dispatch. + */ interface StorageManagerInterface { /** - * @return Envelope[] + * Retrieves all stored notification envelopes. + * + * @return Envelope[] Array of all stored notification envelopes */ public function all(): array; /** - * @param array $criteria + * Filters notifications based on provided criteria. * - * @return Envelope[] + * This method allows retrieving a subset of notifications that match + * specific criteria, such as notification type, priority, or other attributes. + * + * @param array $criteria Filtering criteria + * + * @return Envelope[] Array of filtered notification envelopes */ public function filter(array $criteria = []): array; + /** + * Adds one or more notification envelopes to storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to store + */ public function add(Envelope ...$envelopes): void; + /** + * Updates one or more notification envelopes in storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to update + */ public function update(Envelope ...$envelopes): void; + /** + * Removes one or more notification envelopes from storage. + * + * @param Envelope ...$envelopes One or more notification envelopes to remove + */ public function remove(Envelope ...$envelopes): void; + /** + * Clears all notification envelopes from storage. + */ public function clear(): void; } diff --git a/src/Prime/Template/PHPTemplateEngine.php b/src/Prime/Template/PHPTemplateEngine.php index 46e9b85b..566c5da9 100644 --- a/src/Prime/Template/PHPTemplateEngine.php +++ b/src/Prime/Template/PHPTemplateEngine.php @@ -4,8 +4,35 @@ declare(strict_types=1); namespace Flasher\Prime\Template; +/** + * PHPTemplateEngine - Simple PHP-based template rendering engine. + * + * This implementation provides a straightforward template rendering mechanism using + * native PHP as the template language. It works by including PHP files and extracting + * context variables into the template's scope. + * + * Design patterns: + * - Strategy: Implements a specific template rendering strategy + * - Output Capture: Uses output buffering to capture rendered content + * + * Security considerations: + * - Context variables are extracted with EXTR_SKIP flag to prevent variable overwriting + * - Template files must exist and be readable + */ final class PHPTemplateEngine implements TemplateEngineInterface { + /** + * {@inheritdoc} + * + * This implementation: + * 1. Verifies the template file exists and is readable + * 2. Starts output buffering + * 3. Extracts context variables into the current scope + * 4. Includes the template file + * 5. Captures and returns the output + * + * @throws \InvalidArgumentException If the template file doesn't exist or isn't readable + */ public function render(string $name, array $context = []): string { if (!file_exists($name) || !is_readable($name)) { diff --git a/src/Prime/Template/TemplateEngineInterface.php b/src/Prime/Template/TemplateEngineInterface.php index ed751766..8be64346 100644 --- a/src/Prime/Template/TemplateEngineInterface.php +++ b/src/Prime/Template/TemplateEngineInterface.php @@ -4,10 +4,31 @@ declare(strict_types=1); namespace Flasher\Prime\Template; +/** + * TemplateEngineInterface - Contract for template rendering engines. + * + * This interface defines the essential operation for template rendering engines + * in PHPFlasher. Template engines are responsible for transforming template files + * into HTML content, particularly for notifications that require custom rendering. + * + * Design patterns: + * - Strategy: Defines a family of template rendering algorithms that can be used interchangeably + * - Adapter: Provides a common interface for different template engine implementations + */ interface TemplateEngineInterface { /** - * @param array $context + * Renders a template with the given context variables. + * + * This method transforms a template file or string into rendered HTML by + * substituting variables from the context array. + * + * @param string $name The template name/path to render + * @param array $context Variables to pass to the template + * + * @return string The rendered template as a string + * + * @throws \InvalidArgumentException If the template cannot be found or parsed */ public function render(string $name, array $context = []): string; } diff --git a/src/Prime/Test/Constraint/Notification.php b/src/Prime/Test/Constraint/Notification.php index e5246722..4a2a623c 100644 --- a/src/Prime/Test/Constraint/Notification.php +++ b/src/Prime/Test/Constraint/Notification.php @@ -9,17 +9,26 @@ use Flasher\Prime\Notification\NotificationInterface; use PHPUnit\Framework\Constraint\Constraint; /** - * Checks for the existence of a notification with specified details. + * Notification - PHPUnit constraint for asserting complete notification properties. + * + * This constraint verifies that a NotificationEvents collection contains at least + * one notification matching a combination of type, message, title, and options. + * It allows for comprehensive notification assertions by checking multiple properties. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + * - Specification: Represents a specification that notifications can satisfy */ final class Notification extends Constraint { /** - * Constructor to initialize notification expectations. + * Creates a new Notification constraint. * - * @param string $expectedType expected type of the notification - * @param string|null $expectedMessage expected message content - * @param array $expectedOptions expected options array - * @param string|null $expectedTitle expected title of the notification + * @param string $expectedType Expected notification type (e.g., 'success', 'error') + * @param string|null $expectedMessage Expected message content (null to ignore) + * @param array $expectedOptions Expected options as an associative array + * @param string|null $expectedTitle Expected title content (null to ignore) */ public function __construct( private readonly string $expectedType, @@ -29,6 +38,11 @@ final class Notification extends Constraint ) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { $details = [ @@ -51,7 +65,12 @@ final class Notification extends Constraint } /** - * @param NotificationEvents|mixed $other + * Evaluates if the given NotificationEvents object contains at least one notification + * matching all the expected properties. + * + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate + * + * @return bool True if a matching notification is found */ protected function matches(mixed $other): bool { @@ -68,6 +87,19 @@ final class Notification extends Constraint return false; } + /** + * Checks if a specific notification matches all expected properties. + * + * A notification matches if: + * - Its type equals the expected type AND + * - Its message equals the expected message (if provided) AND + * - Its title equals the expected title (if provided) AND + * - Its options contain all expected option key-value pairs (if provided) + * + * @param NotificationInterface $notification The notification to check + * + * @return bool True if the notification matches all criteria + */ private function isNotificationMatching(NotificationInterface $notification): bool { return $notification->getType() === $this->expectedType @@ -77,7 +109,14 @@ final class Notification extends Constraint } /** - * @param NotificationEvents $other + * Provides a detailed failure description when the constraint fails. + * + * This method lists all actual notifications with their properties, + * making test failures easier to diagnose. + * + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description */ protected function failureDescription(mixed $other): string { diff --git a/src/Prime/Test/Constraint/NotificationCount.php b/src/Prime/Test/Constraint/NotificationCount.php index ccba4f41..e0e132b8 100644 --- a/src/Prime/Test/Constraint/NotificationCount.php +++ b/src/Prime/Test/Constraint/NotificationCount.php @@ -7,12 +7,33 @@ namespace Flasher\Prime\Test\Constraint; use Flasher\Prime\EventDispatcher\Event\NotificationEvents; use PHPUnit\Framework\Constraint\Constraint; +/** + * NotificationCount - PHPUnit constraint for asserting notification count. + * + * This constraint verifies that a NotificationEvents collection contains + * exactly the expected number of notifications. It's used by the FlasherAssert + * class for count-related assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + */ final class NotificationCount extends Constraint { + /** + * Creates a new NotificationCount constraint. + * + * @param int $expectedValue The expected number of notifications + */ public function __construct(private readonly int $expectedValue) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { return \sprintf('matches the expected notification count of %d.', $this->expectedValue); @@ -21,9 +42,9 @@ final class NotificationCount extends Constraint /** * Evaluates if the given NotificationEvents object matches the expected notification count. * - * @param NotificationEvents|mixed $other an instance of NotificationEvents to evaluate + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate * - * @return bool returns true if the actual notification count matches the expected count + * @return bool True if the actual count matches the expected count */ protected function matches(mixed $other): bool { @@ -35,11 +56,11 @@ final class NotificationCount extends Constraint } /** - * Provides a more detailed and understandable failure description. + * Provides a detailed failure description when the constraint fails. * - * @param NotificationEvents $other the evaluated NotificationEvents instance + * @param NotificationEvents $other The evaluated NotificationEvents instance * - * @return string returns a detailed failure description + * @return string A detailed failure description */ protected function failureDescription(mixed $other): string { @@ -51,9 +72,9 @@ final class NotificationCount extends Constraint /** * Counts the notifications in the given NotificationEvents object. * - * @param NotificationEvents $events the NotificationEvents instance to count notifications from + * @param NotificationEvents $events The NotificationEvents instance * - * @return int returns the count of notifications + * @return int The number of notifications */ private function countNotifications(NotificationEvents $events): int { diff --git a/src/Prime/Test/Constraint/NotificationMessage.php b/src/Prime/Test/Constraint/NotificationMessage.php index 32a6f59a..2e2810d5 100644 --- a/src/Prime/Test/Constraint/NotificationMessage.php +++ b/src/Prime/Test/Constraint/NotificationMessage.php @@ -9,19 +9,46 @@ use Flasher\Prime\Notification\NotificationInterface; use PHPUnit\Framework\Constraint\Constraint; /** - * Validates that at least one notification contains a specific message. + * NotificationMessage - PHPUnit constraint for asserting notification message presence. + * + * This constraint verifies that a NotificationEvents collection contains at least + * one notification with a message containing the expected text. It's used by the + * FlasherAssert class for message-related assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + * - String Matcher: Performs string matching on notification messages */ final class NotificationMessage extends Constraint { + /** + * Creates a new NotificationMessage constraint. + * + * @param string $expectedMessage The expected message text (or substring) + */ public function __construct(private readonly string $expectedMessage) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { return \sprintf('contains a notification with message "%s"', $this->expectedMessage); } + /** + * Evaluates if the given NotificationEvents object contains at least one notification + * with a message containing the expected text. + * + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate + * + * @return bool True if a notification with the expected message text is found + */ protected function matches(mixed $other): bool { if (!$other instanceof NotificationEvents) { @@ -37,8 +64,19 @@ final class NotificationMessage extends Constraint return false; } + /** + * Provides a detailed failure description when the constraint fails. + * + * This method lists all actual notification messages found, making + * test failures easier to diagnose. + * + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description + */ protected function failureDescription(mixed $other): string { + // @phpstan-ignore-next-line if (!$other instanceof NotificationEvents) { return 'Expected an instance of NotificationEvents but received a different type.'; } diff --git a/src/Prime/Test/Constraint/NotificationOption.php b/src/Prime/Test/Constraint/NotificationOption.php index 11ff1e5f..568343cf 100644 --- a/src/Prime/Test/Constraint/NotificationOption.php +++ b/src/Prime/Test/Constraint/NotificationOption.php @@ -9,18 +9,34 @@ use Flasher\Prime\Notification\NotificationInterface; use PHPUnit\Framework\Constraint\Constraint; /** - * Asserts that a notification contains an option with a specific key, and optionally, a specific value. + * NotificationOption - PHPUnit constraint for asserting a specific notification option. + * + * This constraint verifies that a NotificationEvents collection contains at least + * one notification with a specific option key and optionally a specific value. It's + * used by the FlasherAssert class for option-specific assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + * - Key-Value Matcher: Performs key-value matching on notification options */ final class NotificationOption extends Constraint { /** - * @param string $expectedKey the expected option key - * @param mixed $expectedValue The expected value for the option + * Creates a new NotificationOption constraint. + * + * @param string $expectedKey The expected option key + * @param mixed $expectedValue The expected option value (null to check only key existence) */ public function __construct(private readonly string $expectedKey, private readonly mixed $expectedValue = null) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { $description = \sprintf('contains a notification with an option "%s"', $this->expectedKey); @@ -32,6 +48,14 @@ final class NotificationOption extends Constraint return $description; } + /** + * Evaluates if the given NotificationEvents object contains at least one notification + * with the expected option key and value. + * + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate + * + * @return bool True if a notification with the expected option is found + */ protected function matches(mixed $other): bool { if (!$other instanceof NotificationEvents) { @@ -47,6 +71,13 @@ final class NotificationOption extends Constraint return false; } + /** + * Checks if a specific notification has the expected option key and value. + * + * @param NotificationInterface $notification The notification to check + * + * @return bool True if the notification has the expected option + */ private function isOptionMatching(NotificationInterface $notification): bool { $options = $notification->getOptions(); @@ -54,9 +85,21 @@ final class NotificationOption extends Constraint return isset($options[$this->expectedKey]) && $options[$this->expectedKey] === $this->expectedValue; } + /** + * Provides a detailed failure description when the constraint fails. + * + * This method lists all actual notification options found, making + * test failures easier to diagnose. + * + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description + */ protected function failureDescription(mixed $other): string { $actualOptions = []; + + // @phpstan-ignore-next-line if ($other instanceof NotificationEvents) { foreach ($other->getEnvelopes() as $notification) { $actualOptions[] = json_encode($notification->getOptions()); diff --git a/src/Prime/Test/Constraint/NotificationOptions.php b/src/Prime/Test/Constraint/NotificationOptions.php index ee98fa28..a7c2cdf8 100644 --- a/src/Prime/Test/Constraint/NotificationOptions.php +++ b/src/Prime/Test/Constraint/NotificationOptions.php @@ -8,22 +8,46 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents; use PHPUnit\Framework\Constraint\Constraint; /** - * Asserts that at least one notification contains a set of options. + * NotificationOptions - PHPUnit constraint for asserting notification options. + * + * This constraint verifies that a NotificationEvents collection contains at least + * one notification with options matching all the expected key-value pairs. It's used + * by the FlasherAssert class for options-related assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + * - Key-Value Matcher: Performs associative array matching on notification options */ final class NotificationOptions extends Constraint { /** - * @param array $expectedOptions the expected options + * Creates a new NotificationOptions constraint. + * + * @param array $expectedOptions The expected option key-value pairs */ public function __construct(private readonly array $expectedOptions) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { return 'contains a notification with options matching '.json_encode($this->expectedOptions, \JSON_PRETTY_PRINT); } + /** + * Evaluates if the given NotificationEvents object contains at least one notification + * with options containing all the expected key-value pairs. + * + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate + * + * @return bool True if a notification with matching options is found + */ protected function matches(mixed $other): bool { if (!$other instanceof NotificationEvents) { @@ -39,9 +63,21 @@ final class NotificationOptions extends Constraint return false; } + /** + * Provides a detailed failure description when the constraint fails. + * + * This method lists all actual notification options found, making + * test failures easier to diagnose. + * + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description + */ protected function failureDescription(mixed $other): string { $actualOptions = []; + + // @phpstan-ignore-next-line if ($other instanceof NotificationEvents) { foreach ($other->getEnvelopes() as $notification) { $actualOptions[] = json_encode($notification->getOptions()); diff --git a/src/Prime/Test/Constraint/NotificationTitle.php b/src/Prime/Test/Constraint/NotificationTitle.php index b1e34f46..470c389a 100644 --- a/src/Prime/Test/Constraint/NotificationTitle.php +++ b/src/Prime/Test/Constraint/NotificationTitle.php @@ -9,22 +9,46 @@ use Flasher\Prime\Notification\NotificationInterface; use PHPUnit\Framework\Constraint\Constraint; /** - * Asserts that at least one notification contains a specific title. + * NotificationTitle - PHPUnit constraint for asserting notification title presence. + * + * This constraint verifies that a NotificationEvents collection contains at least + * one notification with a title containing the expected text. It's used by the + * FlasherAssert class for title-related assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy + * - String Matcher: Performs string matching on notification titles */ final class NotificationTitle extends Constraint { /** - * @param string $expectedTitle the title content to search for within notifications + * Creates a new NotificationTitle constraint. + * + * @param string $expectedTitle The expected title text (or substring) */ public function __construct(private readonly string $expectedTitle) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { return \sprintf('contains a notification with a title containing "%s"', $this->expectedTitle); } + /** + * Evaluates if the given NotificationEvents object contains at least one notification + * with a title containing the expected text. + * + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate + * + * @return bool True if a notification with the expected title text is found + */ protected function matches(mixed $other): bool { if (!$other instanceof NotificationEvents) { @@ -40,8 +64,19 @@ final class NotificationTitle extends Constraint return false; } + /** + * Provides a detailed failure description when the constraint fails. + * + * This method lists all actual notification titles found, making + * test failures easier to diagnose. + * + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description + */ protected function failureDescription(mixed $other): string { + // @phpstan-ignore-next-line if (!$other instanceof NotificationEvents) { return 'Expected an instance of NotificationEvents but received a different type.'; } diff --git a/src/Prime/Test/Constraint/NotificationType.php b/src/Prime/Test/Constraint/NotificationType.php index 2b37235e..dbcc4554 100644 --- a/src/Prime/Test/Constraint/NotificationType.php +++ b/src/Prime/Test/Constraint/NotificationType.php @@ -9,30 +9,44 @@ use Flasher\Prime\Notification\NotificationInterface; use PHPUnit\Framework\Constraint\Constraint; /** - * A constraint that asserts a specific type of notification is present. + * NotificationType - PHPUnit constraint for asserting notification type presence. * - * This constraint checks if among the notifications present, there is at least - * one of a specified type. This is useful for tests that need to verify the presence - * of certain types of notifications among those that have been dispatched. + * This constraint verifies that a NotificationEvents collection contains at least + * one notification of the specified type. It's used by the FlasherAssert class + * for type-related assertions. + * + * Design patterns: + * - Composite: Part of PHPUnit's constraint composition system + * - Strategy: Implements a specific assertion strategy */ final class NotificationType extends Constraint { + /** + * Creates a new NotificationType constraint. + * + * @param string $expectedType The expected notification type (e.g., 'success', 'error') + */ public function __construct(private readonly string $expectedType) { } + /** + * Returns a string representation of the constraint. + * + * @return string The constraint description + */ public function toString(): string { return \sprintf('contains a notification of type "%s".', $this->expectedType); } /** - * Evaluates the constraint for the parameter $other. - * If $other is not an instance of NotificationEvents, the method will return false. + * Evaluates if the given NotificationEvents object contains at least one notification + * of the expected type. * - * @param NotificationEvents|mixed $other value or object to evaluate + * @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate * - * @return bool true if the constraint is met, false otherwise + * @return bool True if a notification of the expected type is found */ protected function matches(mixed $other): bool { @@ -50,11 +64,14 @@ final class NotificationType extends Constraint } /** - * Returns a custom failure description for when the constraint is not met. + * Provides a detailed failure description when the constraint fails. * - * @param NotificationEvents $other evaluated object or value + * This method provides context about what types were found instead of + * the expected type, making test failures easier to diagnose. * - * @return string the failure description + * @param NotificationEvents $other The evaluated NotificationEvents instance + * + * @return string A detailed failure description */ protected function failureDescription(mixed $other): string { diff --git a/src/Prime/Test/FlasherAssert.php b/src/Prime/Test/FlasherAssert.php index e3e881f8..927ab8e3 100644 --- a/src/Prime/Test/FlasherAssert.php +++ b/src/Prime/Test/FlasherAssert.php @@ -18,8 +18,16 @@ use Flasher\Prime\Test\Constraint\NotificationType; use PHPUnit\Framework\Assert; /** - * FlasherAssert provides a collection of static assertion methods for testing notification states within the Flasher notification system. - * These methods facilitate easy and readable assertions in tests, focusing on notification presence, type, content, and other attributes. + * FlasherAssert - Fluent assertion library for testing notifications. + * + * This class provides a rich set of assertion methods for testing PHPFlasher + * notifications in test suites. It uses a fluent interface for more readable tests + * and integrates with PHPUnit's assertion system. + * + * Design patterns: + * - Fluent Interface: Provides method chaining for readable assertions + * - Facade: Provides a simplified interface to a complex subsystem + * - Delegation: Delegates assertion logic to specialized constraint classes */ final class FlasherAssert { @@ -28,15 +36,8 @@ final class FlasherAssert * * This method serves as a starting point for chaining assertion methods in tests. * It provides a fluent interface that allows for more readable and expressive tests. - * By starting assertions with `that()`, tests can emulate natural language, improving clarity. * - * Usage Example: - * - * ```php - * FlasherAssert::that()->hasNotifications('Custom error message if no notifications found.'); - * ``` - * - * @return self an instance of the FlasherAssert class to allow for method chaining + * @return self A new instance for method chaining */ public static function that(): self { @@ -45,11 +46,13 @@ final class FlasherAssert /** * Asserts the presence of at least one notification in the system. - * This assertion passes if the notification system has logged any notifications, regardless of their specific attributes. * - * @param string $message a custom message to display if the assertion fails + * This assertion passes if the notification system has logged any notifications, + * regardless of their specific attributes. * - * @return self returns itself to allow method chaining + * @param string $message A custom message to display if the assertion fails + * + * @return self Returns itself to allow method chaining */ public static function hasNotifications(string $message = 'Expected at least one notification to exist.'): self { @@ -58,11 +61,12 @@ final class FlasherAssert /** * Asserts that no notifications have been registered in the system. + * * Useful for tests where the absence of notifications indicates a pass condition. * - * @param string $message a custom message to display if the assertion fails + * @param string $message A custom message to display if the assertion fails * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function noNotifications(string $message = 'Expected no notifications to exist.'): self { @@ -70,16 +74,17 @@ final class FlasherAssert } /** - * Asserts the existence of a notification matching specific criteria including type, message, options, and title. + * Asserts the existence of a notification matching specific criteria. + * * A notification must match all provided criteria to satisfy the assertion. * - * @param string $expectedType Expected notification type (e.g., 'success', 'error'). - * @param string|null $expectedMessage Expected message content. Null means the message is not considered. - * @param array $expectedOptions Expected options as an associative array. Empty array means options are not considered. - * @param string|null $expectedTitle Expected notification title. Null means the title is not considered. - * @param string $message custom failure message + * @param string $expectedType Expected notification type (e.g., 'success', 'error') + * @param string|null $expectedMessage Expected message content (null to ignore) + * @param array $expectedOptions Expected options as an associative array + * @param string|null $expectedTitle Expected notification title (null to ignore) + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withNotification(string $expectedType, ?string $expectedMessage = null, array $expectedOptions = [], ?string $expectedTitle = null, string $message = ''): self { @@ -87,18 +92,15 @@ final class FlasherAssert } /** - * @alias of withNotification + * Alias of withNotification() - Asserts the existence of a notification matching specific criteria. * - * Asserts the existence of a notification matching specific criteria including type, message, options, and title. - * A notification must match all provided criteria to satisfy the assertion. + * @param string $expectedType Expected notification type + * @param string|null $expectedMessage Expected message content + * @param array $expectedOptions Expected options + * @param string|null $expectedTitle Expected notification title + * @param string $message Custom failure message * - * @param string $expectedType Expected notification type (e.g., 'success', 'error'). - * @param string|null $expectedMessage Expected message content. Null means the message is not considered. - * @param array $expectedOptions Expected options as an associative array. Empty array means options are not considered. - * @param string|null $expectedTitle Expected notification title. Null means the title is not considered. - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function notification(string $expectedType, ?string $expectedMessage = null, array $expectedOptions = [], ?string $expectedTitle = null, string $message = ''): self { @@ -108,10 +110,10 @@ final class FlasherAssert /** * Asserts the total count of notifications matches an expected number. * - * @param int $expectedCount expected number of notifications - * @param string $message custom failure message + * @param int $expectedCount Expected number of notifications + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withCount(int $expectedCount, string $message = ''): self { @@ -119,14 +121,12 @@ final class FlasherAssert } /** - * @alias of withCount + * Alias of withCount() - Asserts the total count of notifications. * - * Asserts the total count of notifications matches an expected number. + * @param int $expectedCount Expected number of notifications + * @param string $message Custom failure message * - * @param int $expectedCount expected number of notifications - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function count(int $expectedCount, string $message = ''): self { @@ -136,10 +136,10 @@ final class FlasherAssert /** * Asserts the existence of at least one notification of a specific type. * - * @param string $expectedType expected notification type - * @param string $message custom failure message + * @param string $expectedType Expected notification type + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withType(string $expectedType, string $message = ''): self { @@ -147,14 +147,12 @@ final class FlasherAssert } /** - * @alias of withType + * Alias of withType() - Asserts the existence of a notification of a specific type. * - * Asserts the existence of at least one notification of a specific type. + * @param string $expectedType Expected notification type + * @param string $message Custom failure message * - * @param string $expectedType expected notification type - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function type(string $expectedType, string $message = ''): self { @@ -164,9 +162,9 @@ final class FlasherAssert /** * Asserts the presence of at least one 'success' type notification. * - * @param string $message custom failure message + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withSuccess(string $message = ''): self { @@ -174,13 +172,11 @@ final class FlasherAssert } /** - * @alias of withSuccess + * Alias of withSuccess() - Asserts the presence of a 'success' notification. * - * Asserts the presence of at least one 'success' type notification. + * @param string $message Custom failure message * - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function success(string $message = ''): self { @@ -190,9 +186,9 @@ final class FlasherAssert /** * Asserts the presence of at least one 'warning' type notification. * - * @param string $message custom failure message + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withWarning(string $message = ''): self { @@ -200,13 +196,11 @@ final class FlasherAssert } /** - * @alias of withWarning + * Alias of withWarning() - Asserts the presence of a 'warning' notification. * - * Asserts the presence of at least one 'warning' type notification. + * @param string $message Custom failure message * - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function warning(string $message = ''): self { @@ -216,9 +210,9 @@ final class FlasherAssert /** * Asserts the presence of at least one 'error' type notification. * - * @param string $message custom failure message + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withError(string $message = ''): self { @@ -226,13 +220,11 @@ final class FlasherAssert } /** - * @alias of withError + * Alias of withError() - Asserts the presence of an 'error' notification. * - * Asserts the presence of at least one 'error' type notification. + * @param string $message Custom failure message * - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function error(string $message = ''): self { @@ -242,9 +234,9 @@ final class FlasherAssert /** * Asserts the presence of at least one 'info' type notification. * - * @param string $message custom failure message + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withInfo(string $message = ''): self { @@ -252,13 +244,11 @@ final class FlasherAssert } /** - * @alias of withInfo + * Alias of withInfo() - Asserts the presence of an 'info' notification. * - * Asserts the presence of at least one 'info' type notification. + * @param string $message Custom failure message * - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function info(string $message = ''): self { @@ -268,10 +258,10 @@ final class FlasherAssert /** * Asserts the presence of a notification with a specific title. * - * @param string $expectedTitle expected notification title - * @param string $message custom failure message + * @param string $expectedTitle Expected notification title + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withTitle(string $expectedTitle, string $message = ''): self { @@ -279,14 +269,12 @@ final class FlasherAssert } /** - * @alias of withTitle + * Alias of withTitle() - Asserts the presence of a notification with specific title. * - * Asserts the presence of a notification with a specific title. + * @param string $expectedTitle Expected notification title + * @param string $message Custom failure message * - * @param string $expectedTitle expected notification title - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function title(string $expectedTitle, string $message = ''): self { @@ -296,10 +284,10 @@ final class FlasherAssert /** * Asserts the presence of a notification with a specific message. * - * @param string $expectedMessage expected notification message - * @param string $message custom failure message + * @param string $expectedMessage Expected notification message + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withMessage(string $expectedMessage, string $message = ''): self { @@ -307,14 +295,12 @@ final class FlasherAssert } /** - * @alias of withMessage + * Alias of withMessage() - Asserts the presence of a notification with specific message. * - * Asserts the presence of a notification with a specific message. + * @param string $expectedMessage Expected notification message + * @param string $message Custom failure message * - * @param string $expectedMessage expected notification message - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function message(string $expectedMessage, string $message = ''): self { @@ -324,10 +310,10 @@ final class FlasherAssert /** * Asserts the presence of a notification with specific options. * - * @param array $expectedOptions expected options as an associative array - * @param string $message custom failure message + * @param array $expectedOptions Expected options as an associative array + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withOptions(array $expectedOptions, string $message = ''): self { @@ -335,14 +321,12 @@ final class FlasherAssert } /** - * @alias of withOptions + * Alias of withOptions() - Asserts the presence of a notification with specific options. * - * Asserts the presence of a notification with specific options. + * @param array $expectedOptions Expected options + * @param string $message Custom failure message * - * @param array $expectedOptions expected options as an associative array - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function options(array $expectedOptions, string $message = ''): self { @@ -350,13 +334,13 @@ final class FlasherAssert } /** - * Asserts the presence of a notification with a specific option key and, optionally, a value. + * Asserts the presence of a notification with a specific option key and value. * - * @param string $expectedKey expected option key - * @param mixed $expectedValue Expected value of the option. Null or omitted to skip value check. - * @param string $message custom failure message + * @param string $expectedKey Expected option key + * @param mixed $expectedValue Expected option value (null to check only key existence) + * @param string $message Custom failure message * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function withOption(string $expectedKey, mixed $expectedValue = null, string $message = ''): self { @@ -364,15 +348,13 @@ final class FlasherAssert } /** - * @alias of withOption + * Alias of withOption() - Asserts notification has a specific option. * - * Asserts the presence of a notification with a specific option key and, optionally, a value. + * @param string $expectedKey Expected option key + * @param mixed $expectedValue Expected option value + * @param string $message Custom failure message * - * @param string $expectedKey expected option key - * @param mixed $expectedValue Expected value of the option. Null or omitted to skip value check. - * @param string $message custom failure message - * - * @return self returns itself to allow method chaining + * @return self Returns itself to allow method chaining */ public static function option(string $expectedKey, mixed $expectedValue = null, string $message = ''): self { @@ -380,12 +362,14 @@ final class FlasherAssert } /** - * A utility method used internally to wrap assertions and allow fluent interface chaining. - * Not intended for public use. + * Internal utility method for fluent interface implementation. * - * @param callable $callback The assertion logic encapsulated in a callable function + * This method executes a callback and returns a new instance of FlasherAssert + * to allow method chaining in a fluent interface style. * - * @return self returns itself to enable fluent chaining of methods + * @param callable $callback The assertion logic to execute + * + * @return self A new instance for method chaining */ private static function fluent(callable $callback): self { @@ -395,10 +379,12 @@ final class FlasherAssert } /** - * Fetches the NotificationEvents instance from the NotificationLoggerListener. - * This method simplifies the process of obtaining NotificationEvents, facilitating easier assertion writing in tests. + * Fetches the NotificationEvents instance for assertion. * - * @return NotificationEvents the NotificationEvents instance, allowing for further inspection or assertion of notification states + * This method retrieves notification events from the NotificationLoggerListener + * service, which tracks notifications that were displayed. + * + * @return NotificationEvents Collection of notification events for assertion */ public static function getNotificationEvents(): NotificationEvents { diff --git a/src/Prime/Translation/EchoTranslator.php b/src/Prime/Translation/EchoTranslator.php index 57247809..eba05440 100644 --- a/src/Prime/Translation/EchoTranslator.php +++ b/src/Prime/Translation/EchoTranslator.php @@ -5,15 +5,33 @@ declare(strict_types=1); namespace Flasher\Prime\Translation; /** - * In this implementation, it simply returns the identifier as is, without performing any actual translation. + * EchoTranslator - Minimal translator implementation that returns message IDs unchanged. + * + * This implementation simply returns the message identifiers as-is, without + * performing any actual translation. It serves as a fallback translator when + * no real translation service is available. + * + * Design patterns: + * - Null Object: Provides a do-nothing implementation that maintains API compatibility + * - Fallback: Serves as a default implementation when no specific translator is provided */ final readonly class EchoTranslator implements TranslatorInterface { + /** + * {@inheritdoc} + * + * This implementation simply returns the message identifier unchanged. + */ public function translate(string $id, array $parameters = [], ?string $locale = null): string { return $id; } + /** + * {@inheritdoc} + * + * This implementation always returns 'en' as the default locale. + */ public function getLocale(): string { return 'en'; diff --git a/src/Prime/Translation/Language.php b/src/Prime/Translation/Language.php index cca8d840..b9ea9d46 100644 --- a/src/Prime/Translation/Language.php +++ b/src/Prime/Translation/Language.php @@ -5,26 +5,37 @@ declare(strict_types=1); namespace Flasher\Prime\Translation; /** - * Provides utilities for determining the text direction (Left-to-Right or Right-to-Left) - * based on a given locale. This can be particularly useful for handling languages - * with different writing directions in internationalized applications. + * Language - Utility class for determining text direction based on locale. + * + * This class provides static methods for working with language directionality, + * particularly for determining if a language uses left-to-right (LTR) or + * right-to-left (RTL) text direction. + * + * Design patterns: + * - Utility: Provides a collection of static methods for a specific purpose + * - Service: Provides functionality related to a specific domain concept */ final readonly class Language { + /** + * Constant representing left-to-right text direction. + */ public const LTR = 'ltr'; + /** + * Constant representing right-to-left text direction. + */ public const RTL = 'rtl'; /** * Determines the text direction for a given locale. * - * It uses the 'intl' PHP extension to get text direction from the ICU data. - * Defaults to Left-to-Right (LTR) if the 'intl' extension is not available, - * the locale is not found, or the text direction data is not available. + * This method uses the PHP intl extension to access ICU data about text direction. + * If the extension is not available or the locale data is missing, it defaults to LTR. * - * @param string $locale the locale to check the text direction for + * @param string $locale The locale code to check (e.g., 'en', 'ar') * - * @return string returns 'ltr' for Left-to-Right or 'rtl' for Right-to-Left text direction + * @return string Either 'ltr' for left-to-right or 'rtl' for right-to-left */ public static function direction(string $locale): string { @@ -43,11 +54,11 @@ final readonly class Language } /** - * Checks if the given locale is Right-to-Left (RTL). + * Checks if the given locale uses right-to-left text direction. * - * @param string $locale the locale to check + * @param string $locale The locale code to check * - * @return bool returns true if the locale is RTL, false otherwise + * @return bool True if the locale uses RTL, false otherwise */ public static function isRTL(string $locale): bool { @@ -55,11 +66,11 @@ final readonly class Language } /** - * Checks if the given locale is Left-to-Right (LTR). + * Checks if the given locale uses left-to-right text direction. * - * @param string $locale the locale to check + * @param string $locale The locale code to check * - * @return bool returns true if the locale is LTR, false otherwise + * @return bool True if the locale uses LTR, false otherwise */ public static function isLTR(string $locale): bool { diff --git a/src/Prime/Translation/Language/Arabic.php b/src/Prime/Translation/Language/Arabic.php index b183ada0..a9d6773b 100644 --- a/src/Prime/Translation/Language/Arabic.php +++ b/src/Prime/Translation/Language/Arabic.php @@ -4,12 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * Arabic - Provides Arabic translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in Arabic. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Arabic is a right-to-left (RTL) language and requires special handling for display. + * The Language utility class handles RTL detection automatically. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class Arabic { /** - * Arabic translations. + * Provides Arabic translations for common notification messages. * - * @return array array of message keys and their Arabic translations + * The returned array maps message identifiers to their Arabic translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/Chinese.php b/src/Prime/Translation/Language/Chinese.php index 0ca5ea75..09a24d17 100644 --- a/src/Prime/Translation/Language/Chinese.php +++ b/src/Prime/Translation/Language/Chinese.php @@ -4,12 +4,27 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * Chinese - Provides Mandarin Chinese translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in Mandarin Chinese. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class Chinese { /** - * Chinese (Mandarin) translations. + * Provides Chinese translations for common notification messages. * - * @return array array of message keys and their Chinese translations + * The returned array maps message identifiers to their Chinese translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/English.php b/src/Prime/Translation/Language/English.php index 3487567d..ea98e448 100644 --- a/src/Prime/Translation/Language/English.php +++ b/src/Prime/Translation/Language/English.php @@ -4,12 +4,26 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * English - Provides English translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in English. It's part of PHPFlasher's built-in translation system that provides + * translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class English { /** - * English translations. + * Provides English translations for common notification messages. * - * @return array array of message keys and their English translations + * The returned array maps message identifiers to their English translations. + * It includes basic notification types and common resource action messages. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/French.php b/src/Prime/Translation/Language/French.php index e459e4b8..053edf05 100644 --- a/src/Prime/Translation/Language/French.php +++ b/src/Prime/Translation/Language/French.php @@ -4,12 +4,27 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * French - Provides French translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in French. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class French { /** - * French translations. + * Provides French translations for common notification messages. * - * @return array array of message keys and their French translations + * The returned array maps message identifiers to their French translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/German.php b/src/Prime/Translation/Language/German.php index a03359bf..94537104 100644 --- a/src/Prime/Translation/Language/German.php +++ b/src/Prime/Translation/Language/German.php @@ -4,12 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * German - Provides German translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in German. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class German { /** - * German translations. + * Provides German translations for common notification messages. * - * @return array array of message keys and their German translations + * The returned array maps message identifiers to their German translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * Note: German has different grammatical structures, so the translations + * include the full phrase "Die Ressource" rather than just inserting the placeholder. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/Portuguese.php b/src/Prime/Translation/Language/Portuguese.php index 11ae496d..32c39a13 100644 --- a/src/Prime/Translation/Language/Portuguese.php +++ b/src/Prime/Translation/Language/Portuguese.php @@ -4,12 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * Portuguese - Provides Portuguese translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in Portuguese. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class Portuguese { /** - * Portuguese translations. + * Provides Portuguese translations for common notification messages. * - * @return array array of message keys and their Portuguese translations + * The returned array maps message identifiers to their Portuguese translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * Note: Portuguese has grammatical gender, so these translations assume masculine + * form with "O" article, which works for "recurso" (resource). + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/Russian.php b/src/Prime/Translation/Language/Russian.php index b2ab0c9e..d64a5653 100644 --- a/src/Prime/Translation/Language/Russian.php +++ b/src/Prime/Translation/Language/Russian.php @@ -4,12 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * Russian - Provides Russian translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in Russian. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class Russian { /** - * Russian translations. + * Provides Russian translations for common notification messages. * - * @return array array of message keys and their Russian translations + * The returned array maps message identifiers to their Russian translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * Note: Russian translations have gender-specific grammar, assuming masculine + * form for the placeholder. + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Language/Spanish.php b/src/Prime/Translation/Language/Spanish.php index 97e49e25..0c6b4807 100644 --- a/src/Prime/Translation/Language/Spanish.php +++ b/src/Prime/Translation/Language/Spanish.php @@ -4,12 +4,30 @@ declare(strict_types=1); namespace Flasher\Prime\Translation\Language; +/** + * Spanish - Provides Spanish translations for common notification messages. + * + * This class contains translations of common notification messages and terms + * in Spanish. It's part of PHPFlasher's built-in translation system that + * provides translations without requiring an external translation service. + * + * Design patterns: + * - Data Transfer Object: Provides a structured set of translations + * - Static Provider: Offers static access to translation data + */ final readonly class Spanish { /** - * Spanish translations. + * Provides Spanish translations for common notification messages. * - * @return array array of message keys and their Spanish translations + * The returned array maps message identifiers to their Spanish translations. + * It includes basic notification types and common resource action messages. + * Uses the ':resource' placeholder for variable substitution. + * + * Note: Spanish has grammatical gender, so these translations assume masculine + * form with "El" article, which works for "recurso" (resource). + * + * @return array Mapping of message identifiers to translations */ public static function translations(): array { diff --git a/src/Prime/Translation/Messages.php b/src/Prime/Translation/Messages.php index 6f46d754..07a92d01 100644 --- a/src/Prime/Translation/Messages.php +++ b/src/Prime/Translation/Messages.php @@ -14,14 +14,29 @@ use Flasher\Prime\Translation\Language\Russian; use Flasher\Prime\Translation\Language\Spanish; /** - * This class provides a set of predefined message translations in various languages. - * It holds arrays of key-value pairs where keys are message identifiers and values - * are their respective translations. + * Messages - Registry of predefined translations for common notification messages. + * + * This class provides access to predefined translations for common notification + * messages in multiple languages. It serves as a centralized registry of translations + * that can be used without requiring an external translation service. + * + * Design patterns: + * - Registry: Provides a centralized registry of translations + * - Factory: Creates and returns language-specific translation sets + * - Static Access: Provides static access to translation data */ final readonly class Messages { /** - * @return array + * Gets translations for a specific language. + * + * This method returns a set of predefined translations for common notification + * messages in the requested language. If the language is not supported, it + * returns an empty array. + * + * @param string $language The language code (e.g., 'en', 'fr') + * + * @return array Key-value pairs of message identifiers and translations */ public static function get(string $language): array { diff --git a/src/Prime/Translation/TranslatorInterface.php b/src/Prime/Translation/TranslatorInterface.php index b605bafb..dc7c9050 100644 --- a/src/Prime/Translation/TranslatorInterface.php +++ b/src/Prime/Translation/TranslatorInterface.php @@ -4,12 +4,36 @@ declare(strict_types=1); namespace Flasher\Prime\Translation; +/** + * TranslatorInterface - Contract for notification translation services. + * + * This interface defines the essential operations for translating notification + * content. It supports parameter substitution and locale-specific translations. + * + * Design patterns: + * - Strategy: Defines a family of translation algorithms that can be used interchangeably + * - Adapter: Provides a common interface for different translation implementations + */ interface TranslatorInterface { /** - * @param array $parameters + * Translates a message identifier to the target locale. + * + * This method transforms a message identifier like 'success' or 'The resource was created' + * into a localized string, substituting any parameters in the process. + * + * @param string $id The message identifier to translate + * @param array $parameters Parameters to substitute in the translated string + * @param string|null $locale The target locale, or null to use the default + * + * @return string The translated string */ public function translate(string $id, array $parameters = [], ?string $locale = null): string; + /** + * Gets the default locale used by the translator. + * + * @return string The default locale code (e.g., 'en', 'fr') + */ public function getLocale(): string; } diff --git a/src/Prime/functions.php b/src/Prime/functions.php index 45b5f770..bc484dfc 100644 --- a/src/Prime/functions.php +++ b/src/Prime/functions.php @@ -12,26 +12,33 @@ if (!\function_exists('Flasher\Prime\flash')) { /** * Creates a flash message or returns the Flasher factory. * - * This function serves a dual purpose: - * 1. When called with no arguments, it returns an instance of FlasherInterface or NotificationFactoryInterface. - * This allows for accessing various methods provided by the Flasher factory. - * 2. When called with arguments, it creates a flash message and returns an Envelope. - * This envelope contains the flash message details and can be used for further operations. + * This function provides a convenient shorthand for working with PHPFlasher. + * It serves as the primary entry point in namespaced contexts. * - * @param string|null $message the message content of the flash message - * @param string $type The type of the message (e.g., success, error, warning, info). - * @param array $options additional options for the flash message - * @param string|null $title the title of the flash message + * Design pattern: Gateway - Provides a simple entry point to the complex API. * - * @return Envelope|FlasherInterface Returns an Envelope containing the message details when arguments are provided. - * Returns an instance of FlasherInterface or NotificationFactoryInterface when no arguments are provided. + * @param string|null $message The message content of the flash notification + * @param string $type The notification type (success, error, warning, info) + * @param array $options Additional configuration options + * @param string|null $title The notification title + * + * @return Envelope|FlasherInterface Returns an Envelope when creating a notification, + * or a FlasherInterface when called with no arguments * * @phpstan-return ($message is empty ? FlasherInterface : Envelope) * - * Usage: - * 1. Without arguments - Get the Flasher factory: $flasher = flash(); - * 2. With arguments - Create and return a flash message: - * flash('Message', Type::SUCCESS, ['option' => 'value'], 'Title'); + * Example usage: + * ```php + * // Get the flasher factory + * $flasher = \Flasher\Prime\flash(); + * $flasher->info('Information message'); + * + * // Create a notification directly + * \Flasher\Prime\flash('Operation successful', Type::SUCCESS); + * + * // With additional options + * \Flasher\Prime\flash('Profile updated', Type::SUCCESS, ['timeout' => 5000], 'Success'); + * ``` */ function flash(?string $message = null, string $type = Type::SUCCESS, array $options = [], ?string $title = null): Envelope|FlasherInterface { diff --git a/src/Prime/helpers.php b/src/Prime/helpers.php index caec5102..9785e66c 100644 --- a/src/Prime/helpers.php +++ b/src/Prime/helpers.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Flasher\Prime\Container\FlasherContainer; -use Flasher\Prime\Factory\NotificationFactoryInterface; use Flasher\Prime\FlasherInterface; use Flasher\Prime\Notification\Envelope; use Flasher\Prime\Notification\Type; @@ -12,26 +11,36 @@ if (!function_exists('flash')) { /** * Creates a flash message or returns the Flasher factory. * - * This function serves a dual purpose: - * 1. When called with no arguments, it returns an instance of FlasherInterface or NotificationFactoryInterface. - * This allows for accessing various methods provided by the Flasher factory. - * 2. When called with arguments, it creates a flash message and returns an Envelope. - * This envelope contains the flash message details and can be used for further operations. + * This global function provides a convenient way to access PHPFlasher from anywhere + * in your application without namespace qualification. It serves dual purposes: * - * @param string|null $message the message content of the flash message - * @param string $type The type of the message (e.g., success, error, warning, info). - * @param array $options additional options for the flash message - * @param string|null $title the title of the flash message + * 1. When called with no arguments: Returns the flasher factory for method chaining + * 2. When called with arguments: Creates and stores a flash notification * - * @return Envelope|FlasherInterface Returns an Envelope containing the message details when arguments are provided. - * Returns an instance of FlasherInterface or NotificationFactoryInterface when no arguments are provided. + * Design pattern: Gateway - Provides a simple, global entry point to the complex API. + * + * @param string|null $message The message content of the flash notification + * @param string $type The notification type (success, error, warning, info) + * @param array $options Additional configuration options + * @param string|null $title The notification title + * + * @return Envelope|FlasherInterface Returns an Envelope when creating a notification, + * or a FlasherInterface when called with no arguments * * @phpstan-return ($message is empty ? FlasherInterface : Envelope) * - * Usage: - * 1. Without arguments - Get the Flasher factory: $flasher = flash(); - * 2. With arguments - Create and return a flash message: - * flash('Message', Type::SUCCESS, ['option' => 'value'], 'Title'); + * Example usage: + * ```php + * // Get the flasher factory + * $flasher = flash(); + * $flasher->info('Information message'); + * + * // Create a notification directly + * flash('Operation successful', Type::SUCCESS); + * + * // With additional options + * flash('Profile updated', Type::SUCCESS, ['timeout' => 5000], 'Success'); + * ``` */ function flash(?string $message = null, string $type = Type::SUCCESS, array $options = [], ?string $title = null): Envelope|FlasherInterface { diff --git a/src/SweetAlert/Laravel/.github/FUNDING.yml b/src/SweetAlert/Laravel/.github/FUNDING.yml index 895dabf5..c621ab04 100644 --- a/src/SweetAlert/Laravel/.github/FUNDING.yml +++ b/src/SweetAlert/Laravel/.github/FUNDING.yml @@ -1,2 +1 @@ github: yoeunes -custom: https://www.paypal.com/paypalme/yoeunes diff --git a/src/SweetAlert/Laravel/Facade/SweetAlert.php b/src/SweetAlert/Laravel/Facade/SweetAlert.php index 34709892..5fb1252f 100644 --- a/src/SweetAlert/Laravel/Facade/SweetAlert.php +++ b/src/SweetAlert/Laravel/Facade/SweetAlert.php @@ -10,6 +10,35 @@ use Flasher\SweetAlert\Prime\SweetAlertBuilder; use Illuminate\Support\Facades\Facade; /** + * SweetAlert - Laravel facade for SweetAlert notifications. + * + * This facade provides a static interface to SweetAlert's extensive functionality within Laravel, + * following Laravel's facade pattern. It offers comprehensive IDE autocompletion for all + * SweetAlert builder methods, making it easy to create rich interactive dialogs. + * + * Design patterns: + * - Facade: Provides a simplified, static interface to a complex subsystem + * - Proxy: Acts as a proxy to the underlying SweetAlert service + * + * Usage examples: + * ```php + * // Simple notification + * SweetAlert::success('Operation completed successfully'); + * + * // Confirmation dialog + * SweetAlert::question('Are you sure?') + * ->showCancelButton() + * ->confirmButtonText('Yes, delete it!') + * ->cancelButtonText('Cancel') + * ->push(); + * + * // Input form dialog + * SweetAlert::title('Enter your email') + * ->input('email') + * ->inputPlaceholder('name@example.com') + * ->push(); + * ``` + * * @method static SweetAlertBuilder success(string $message, array $options = array()) * @method static SweetAlertBuilder error(string $message, array $options = array()) * @method static SweetAlertBuilder warning(string $message, array $options = array()) @@ -99,6 +128,11 @@ use Illuminate\Support\Facades\Facade; */ final class SweetAlert extends Facade { + /** + * Get the registered name of the component. + * + * @return string The service container binding key for SweetAlert + */ protected static function getFacadeAccessor(): string { return 'flasher.sweetalert'; diff --git a/src/SweetAlert/Laravel/FlasherSweetAlertServiceProvider.php b/src/SweetAlert/Laravel/FlasherSweetAlertServiceProvider.php index f9f7c3ab..7db39d27 100644 --- a/src/SweetAlert/Laravel/FlasherSweetAlertServiceProvider.php +++ b/src/SweetAlert/Laravel/FlasherSweetAlertServiceProvider.php @@ -8,18 +8,48 @@ use Flasher\Laravel\Support\PluginServiceProvider; use Flasher\Prime\EventDispatcher\EventDispatcherInterface; use Flasher\SweetAlert\Prime\SweetAlertPlugin; +/** + * FlasherSweetAlertServiceProvider - Laravel service provider for SweetAlert2 integration. + * + * This service provider registers the SweetAlert2 plugin with Laravel's service container + * and sets up the Livewire integration for interactive dialogs. It extends the base plugin + * service provider to inherit common registration logic while providing SweetAlert-specific + * plugin implementation and event listeners. + * + * Design patterns: + * - Service Provider: Implements Laravel's service provider pattern + * - Factory Method: Creates the plugin instance + * - Observer: Registers event listeners for framework integration + */ final class FlasherSweetAlertServiceProvider extends PluginServiceProvider { + /** + * Creates the SweetAlert plugin instance. + * + * @return SweetAlertPlugin The SweetAlert plugin instance + */ public function createPlugin(): SweetAlertPlugin { return new SweetAlertPlugin(); } + /** + * Performs additional setup after the service provider is booted. + * + * This method is called after all service providers have been registered. + * It's used here to set up the Livewire integration for interactive dialogs. + */ protected function afterBoot(): void { $this->registerLivewireListener(); } + /** + * Registers the Livewire event listener for SweetAlert dialogs. + * + * This listener enables SweetAlert's interactive dialogs to work with + * Livewire's AJAX-based component updates. + */ private function registerLivewireListener(): void { if (!$this->app->bound('livewire')) { diff --git a/src/SweetAlert/Laravel/LivewireListener.php b/src/SweetAlert/Laravel/LivewireListener.php index b4e1964d..c7ccaea3 100644 --- a/src/SweetAlert/Laravel/LivewireListener.php +++ b/src/SweetAlert/Laravel/LivewireListener.php @@ -7,10 +7,33 @@ namespace Flasher\SweetAlert\Laravel; use Flasher\Prime\EventDispatcher\Event\ResponseEvent; use Flasher\Prime\EventDispatcher\EventListener\EventListenerInterface; +/** + * LivewireListener - Enables SweetAlert interactivity with Livewire components. + * + * This event listener injects JavaScript code that bridges SweetAlert's promise-based + * API with Livewire's event system. It enables Livewire components to respond to + * user interactions with SweetAlert dialogs (confirm, deny, dismiss actions). + * + * Design patterns: + * - Observer: Observes PHPFlasher response events + * - Bridge: Connects SweetAlert's JavaScript API to Livewire's event system + * - Event-driven: Uses event-driven architecture for loose coupling + */ final readonly class LivewireListener implements EventListenerInterface { + /** + * Handles the response event by injecting SweetAlert-Livewire bridge code. + * + * When an HTML response is being prepared, this method adds JavaScript that will: + * 1. Listen for SweetAlert promise events + * 2. Forward these events to the appropriate Livewire component + * 3. Include payload data for the component to process + * + * @param ResponseEvent $event The response event being processed + */ public function __invoke(ResponseEvent $event): void { + // Only process HTML responses if ('html' !== $event->getPresenter()) { return; } @@ -20,6 +43,7 @@ final readonly class LivewireListener implements EventListenerInterface return; } + // Avoid duplicate script injection if (false === strripos($response, '