Simplify PHPDoc comments in Prime classes

Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
This commit is contained in:
Younes ENNAJI
2026-01-15 21:21:11 +01:00
parent d65300a0e9
commit 8f594698c7
66 changed files with 176 additions and 2072 deletions
+1 -54
View File
@@ -4,45 +4,19 @@ 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<string, string>
*/
private array $entries = [];
/**
* 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();
@@ -55,16 +29,6 @@ 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) {
@@ -87,14 +51,7 @@ final class AssetManager implements AssetManagerInterface
}
/**
* Loads and returns the entries from the manifest file.
*
* 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<string, string> The manifest entries mapping original to versioned paths
*
* @throws \InvalidArgumentException If the manifest file contains invalid JSON
* @return array<string, string>
*/
private function getEntriesData(): array
{
@@ -116,16 +73,6 @@ 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);
+3 -33
View File
@@ -4,49 +4,19 @@ 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.
*
* 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.
*
* @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;
/**
* Resolves multiple paths to their hashed versions if available.
* @param string[] $paths
*
* 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
* @return string[]
*/
public function getPaths(array $paths): array;
/**
* Generates a JSON manifest from given files.
*
* 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
* @param string[] $files
*/
public function createManifest(array $files): void;
}
+1 -27
View File
@@ -5,28 +5,7 @@ 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
* Type-safe configuration for PHPFlasher.
*
* @phpstan-type ConfigType array{
* default: string,
@@ -55,11 +34,6 @@ namespace Flasher\Prime;
final class Configuration
{
/**
* 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
*
* @phpstan-return ConfigType
+1 -72
View File
@@ -9,80 +9,33 @@ use Flasher\Prime\FlasherInterface;
use Psr\Container\ContainerInterface;
/**
* FlasherContainer - Service locator for PHPFlasher services using PSR-11 containers.
*
* 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
* @internal
*/
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 PSR-11 container or a factory.
*
* 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
{
self::$instance ??= new self($container);
}
/**
* 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
{
self::$instance = null;
}
/**
* Creates and returns an instance of a Flasher service by ID.
*
* 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
*
* @phpstan-return ($id is 'flasher' ? \Flasher\Prime\FlasherInterface :
* ($id is 'flasher.noty' ? \Flasher\Noty\Prime\NotyInterface :
* ($id is 'flasher.notyf' ? \Flasher\Notyf\Prime\NotyfInterface :
* ($id is 'flasher.sweetalert' ? \Flasher\SweetAlert\Prime\SweetAlertInterface :
* ($id is 'flasher.toastr' ? \Flasher\Toastr\Prime\ToastrInterface :
* \Flasher\Prime\Factory\NotificationFactoryInterface)))))
*
* @throws \InvalidArgumentException If the service doesn't exist or has an invalid type
*/
public static function create(string $id): FlasherInterface|NotificationFactoryInterface
{
@@ -99,28 +52,11 @@ final class FlasherContainer
return $factory;
}
/**
* Checks if a service exists in the container.
*
* @param string $id The service identifier
*
* @return bool True if the service exists, false otherwise
*/
public static function has(string $id): bool
{
return self::getContainer()->has($id);
}
/**
* Retrieves the container, resolving it if necessary.
*
* 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
{
$container = self::getInstance()->container;
@@ -134,13 +70,6 @@ final class FlasherContainer
return $resolved;
}
/**
* Retrieves the singleton instance, throws if not initialized.
*
* @return self The singleton instance
*
* @throws \LogicException If the instance hasn't been initialized yet
*/
private static function getInstance(): self
{
if (!self::$instance instanceof self) {
@@ -9,19 +9,13 @@ 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.
* Event dispatched when notifications are being filtered.
*/
final class FilterEvent
{
/**
* Creates a new FilterEvent instance.
*
* @param FilterInterface $filter The filter being applied
* @param Envelope[] $envelopes The notification envelopes to filter
* @param array<string, mixed> $criteria The filtering criteria
* @param Envelope[] $envelopes
* @param array<string, mixed> $criteria
*/
public function __construct(
private FilterInterface $filter,
@@ -30,32 +24,18 @@ 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;
}
/**
* Gets the notification envelopes being filtered.
*
* @return Envelope[] The notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -63,11 +43,7 @@ final class FilterEvent
}
/**
* 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
* @param Envelope[] $envelopes
*/
public function setEnvelopes(array $envelopes): void
{
@@ -75,11 +51,7 @@ final class FilterEvent
}
/**
* Gets the filtering criteria.
*
* These criteria determine how notifications will be filtered.
*
* @return array<string, mixed> The filtering criteria
* @return array<string, mixed>
*/
public function getCriteria(): array
{
@@ -7,31 +7,17 @@ namespace Flasher\Prime\EventDispatcher\Event;
use Flasher\Prime\Notification\Envelope;
/**
* NotificationEvents - Collector for notification envelopes.
* 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
* @internal
*/
final class NotificationEvents
{
/**
* 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) {
@@ -39,20 +25,13 @@ 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;
}
/**
* Gets all the collected notification envelopes.
*
* @return Envelope[] The collected notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -7,26 +7,19 @@ 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.
* Event dispatched when notifications are being persisted.
*/
final class PersistEvent
{
/**
* Creates a new PersistEvent instance.
*
* @param Envelope[] $envelopes The notification envelopes to be persisted
* @param Envelope[] $envelopes
*/
public function __construct(private array $envelopes)
{
}
/**
* Gets the notification envelopes to be persisted.
*
* @return Envelope[] The notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -34,11 +27,7 @@ final class PersistEvent
}
/**
* 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
* @param Envelope[] $envelopes
*/
public function setEnvelopes(array $envelopes): void
{
@@ -7,26 +7,19 @@ 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.
* Event dispatched after notifications are persisted.
*/
final readonly class PostPersistEvent
{
/**
* Creates a new PostPersistEvent instance.
*
* @param Envelope[] $envelopes The notification envelopes that were persisted
* @param Envelope[] $envelopes
*/
public function __construct(private array $envelopes)
{
}
/**
* Gets the notification envelopes that were persisted.
*
* @return Envelope[] The persisted notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -7,19 +7,13 @@ 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.
* Event dispatched after notifications are removed.
*/
final readonly class PostRemoveEvent
{
/**
* Creates a new PostRemoveEvent instance.
*
* @param Envelope[] $envelopesToRemove The notification envelopes that were removed
* @param Envelope[] $envelopesToKeep The notification envelopes that were kept
* @param Envelope[] $envelopesToRemove
* @param Envelope[] $envelopesToKeep
*/
public function __construct(
private array $envelopesToRemove = [],
@@ -28,9 +22,7 @@ final readonly class PostRemoveEvent
}
/**
* Gets the notification envelopes that were removed.
*
* @return Envelope[] The removed notification envelopes
* @return Envelope[]
*/
public function getEnvelopesToRemove(): array
{
@@ -38,9 +30,7 @@ final readonly class PostRemoveEvent
}
/**
* Gets the notification envelopes that were kept.
*
* @return Envelope[] The kept notification envelopes
* @return Envelope[]
*/
public function getEnvelopesToKeep(): array
{
@@ -7,26 +7,19 @@ 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.
* Event dispatched after notification envelopes are updated.
*/
final readonly class PostUpdateEvent
{
/**
* Creates a new PostUpdateEvent instance.
*
* @param Envelope[] $envelopes The updated notification envelopes
* @param Envelope[] $envelopes
*/
public function __construct(private array $envelopes)
{
}
/**
* Gets the updated notification envelopes.
*
* @return Envelope[] The updated notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -7,19 +7,13 @@ 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).
* Event dispatched when notifications are being prepared for presentation.
*/
final readonly class PresentationEvent
{
/**
* Creates a new PresentationEvent instance.
*
* @param Envelope[] $envelopes The notification envelopes being presented
* @param array<string, mixed> $context Additional context for presentation
* @param Envelope[] $envelopes
* @param array<string, mixed> $context
*/
public function __construct(
private array $envelopes,
@@ -28,9 +22,7 @@ final readonly class PresentationEvent
}
/**
* Gets the notification envelopes being presented.
*
* @return Envelope[] The notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -38,12 +30,7 @@ final readonly class PresentationEvent
}
/**
* Gets the presentation context.
*
* This context provides additional information that may be useful
* for listeners during the presentation process.
*
* @return array<string, mixed> The presentation context
* @return array<string, mixed>
*/
public function getContext(): array
{
@@ -7,35 +7,24 @@ 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.
* Event dispatched when notifications are being removed.
*/
final class RemoveEvent
{
/**
* Notification envelopes that should be kept in storage.
*
* @var Envelope[]
*/
private array $envelopesToKeep = [];
/**
* Creates a new RemoveEvent instance.
*
* @param Envelope[] $envelopesToRemove The notification envelopes initially marked for removal
* @param Envelope[] $envelopesToRemove
*/
public function __construct(private array $envelopesToRemove)
{
}
/**
* Gets the notification envelopes marked for removal.
*
* @return Envelope[] The notification envelopes to remove
* @return Envelope[]
*/
public function getEnvelopesToRemove(): array
{
@@ -43,9 +32,7 @@ final class RemoveEvent
}
/**
* Sets the notification envelopes to be removed.
*
* @param Envelope[] $envelopesToRemove The notification envelopes to remove
* @param Envelope[] $envelopesToRemove
*/
public function setEnvelopesToRemove(array $envelopesToRemove): void
{
@@ -53,9 +40,7 @@ final class RemoveEvent
}
/**
* Gets the notification envelopes that should be kept in storage.
*
* @return Envelope[] The notification envelopes to keep
* @return Envelope[]
*/
public function getEnvelopesToKeep(): array
{
@@ -63,9 +48,7 @@ final class RemoveEvent
}
/**
* Sets the notification envelopes that should be kept in storage.
*
* @param Envelope[] $envelopesToKeep The notification envelopes to keep
* @param Envelope[] $envelopesToKeep
*/
public function setEnvelopesToKeep(array $envelopesToKeep): void
{
@@ -5,54 +5,26 @@ 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.
* Event dispatched when a response is being prepared.
*/
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;
@@ -5,24 +5,9 @@ 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.
* Contract for events that can stop propagation.
*/
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;
}
@@ -7,26 +7,19 @@ 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.
* Event dispatched when notifications are being updated.
*/
final class UpdateEvent
{
/**
* Creates a new UpdateEvent instance.
*
* @param Envelope[] $envelopes The notification envelopes to be updated
* @param Envelope[] $envelopes
*/
public function __construct(private array $envelopes)
{
}
/**
* Gets the notification envelopes to be updated.
*
* @return Envelope[] The notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
@@ -34,11 +27,7 @@ final class UpdateEvent
}
/**
* 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
* @param Envelope[] $envelopes
*/
public function setEnvelopes(array $envelopes): void
{
+1 -27
View File
@@ -11,35 +11,15 @@ 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
* Default implementation of the event dispatcher interface.
*/
final class EventDispatcher implements EventDispatcherInterface
{
/**
* Mapping of event names to listeners.
*
* @var array<string, EventListenerInterface[]>
*/
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());
@@ -48,12 +28,6 @@ final class EventDispatcher implements EventDispatcherInterface
}
/**
* {@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
*/
public function dispatch(object $event): object
@@ -6,55 +6,21 @@ 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
{
/**
* 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
*
* @phpstan-param T $event
*
* @return T The event after it has been processed by all listeners
*
* @phpstan-return T
*/
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;
/**
* 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
* @return EventListenerInterface[]
*/
public function getListeners(string $eventName): array;
}
@@ -10,25 +10,10 @@ 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
* Filters notifications before storage based on 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(...));
@@ -41,33 +26,11 @@ 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);
@@ -75,17 +38,6 @@ 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);
@@ -10,15 +10,7 @@ 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
* Applies preset configurations to notifications.
*
* @phpstan-type PresetType array{
* type: string,
@@ -30,20 +22,14 @@ use Flasher\Prime\Stamp\PresetStamp;
final readonly class ApplyPresetListener implements EventListenerInterface
{
/**
* Creates a new ApplyPresetListener with the specified presets.
*
* @param array<string, PresetType> $presets Map of preset names to their configurations
* @param array<string, PresetType> $presets
*/
public function __construct(private array $presets)
{
}
/**
* 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
* @throws PresetNotFoundException
*/
public function __invoke(PersistEvent $event): void
{
@@ -58,14 +44,7 @@ final readonly class ApplyPresetListener implements EventListenerInterface
}
/**
* Applies preset settings to an envelope if applicable.
*
* 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
* @throws PresetNotFoundException
*/
private function applyPreset(Envelope $envelope): void
{
@@ -84,11 +63,7 @@ final readonly class ApplyPresetListener implements EventListenerInterface
}
/**
* Retrieves preset data with default values for missing fields.
*
* @param string $alias The preset key
*
* @return PresetType The preset data with defaults for missing fields
* @return PresetType
*/
private function getPreset(string $alias): array
{
@@ -102,14 +77,7 @@ final readonly class ApplyPresetListener implements EventListenerInterface
}
/**
* Updates the envelope with the provided preset data.
*
* 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.
*
* @param Envelope $envelope The envelope to update
* @param PresetType $preset The preset data to apply
* @param PresetType $preset
*/
private function updateEnvelope(Envelope $envelope, array $preset): void
{
@@ -14,23 +14,10 @@ use Flasher\Prime\Stamp\IdStamp;
use Flasher\Prime\Stamp\PriorityStamp;
/**
* 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
* Ensures notifications have required stamps.
*/
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) {
@@ -39,11 +26,6 @@ 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
@@ -54,18 +36,6 @@ 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);
@@ -9,27 +9,10 @@ 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
* Manages notification lifecycle 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());
@@ -44,15 +27,9 @@ final readonly class EnvelopeRemovalListener implements EventListenerInterface
}
/**
* Categorizes envelopes into those to keep and those to remove.
* @param Envelope[] $envelopes
*
* 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<Envelope[]> Array with [0] => envelopes to keep, [1] => envelopes to remove
* @return array<Envelope[]>
*/
private function categorizeEnvelopes(array $envelopes): array
{
@@ -5,44 +5,12 @@ declare(strict_types=1);
namespace Flasher\Prime\EventDispatcher\EventListener;
/**
* EventListenerInterface - Contract for event listeners.
*
* 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.
*
* 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
* Contract for event listeners.
*/
interface EventListenerInterface
{
/**
* Returns a list of event names this listener wants to listen to.
*
* 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
* @return string|string[]
*/
public function getSubscribedEvents(): string|array;
}
@@ -9,56 +9,28 @@ use Flasher\Prime\EventDispatcher\Event\PersistEvent;
use Flasher\Prime\EventDispatcher\Event\PresentationEvent;
/**
* NotificationLoggerListener - Logs all notifications that are dispatched and displayed.
* 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
* @internal
*/
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) {
@@ -70,41 +42,21 @@ 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;
@@ -13,41 +13,17 @@ use Flasher\Prime\Translation\Language;
use Flasher\Prime\Translation\TranslatorInterface;
/**
* 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
* Applies translations to notifications during presentation.
*/
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) {
@@ -60,17 +36,6 @@ 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);
@@ -88,14 +53,9 @@ final readonly class TranslationListener implements EventListenerInterface
}
/**
* Extracts and translates parameters from preset stamps.
* @return array<string, mixed>
*
* @param Envelope $envelope The notification envelope
* @param string $locale The locale to use
*
* @return array<string, mixed> The translated parameters
*
* @throws \InvalidArgumentException If a parameter value is not a string
* @throws \InvalidArgumentException
*/
private function getParameters(Envelope $envelope, string $locale): array
{
@@ -118,13 +78,7 @@ final readonly class TranslationListener implements EventListenerInterface
}
/**
* 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<string, mixed> $parameters The translation parameters
* @param array<string, mixed> $parameters
*/
private function applyTranslations(Envelope $envelope, string $locale, array $parameters): void
{
-15
View File
@@ -10,25 +10,10 @@ 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);
@@ -7,11 +7,6 @@ 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
+1 -32
View File
@@ -10,26 +10,12 @@ 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;
/**
* 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,
@@ -37,24 +23,7 @@ abstract class NotificationFactory implements NotificationFactoryInterface
}
/**
* 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
* @param mixed[] $parameters
*/
public function __call(string $method, array $parameters): mixed
{
@@ -7,26 +7,11 @@ 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.
* Core factory abstraction.
*
* @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;
}
@@ -7,35 +7,17 @@ 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.
* Registry of available factories.
*/
final class NotificationFactoryLocator implements NotificationFactoryLocatorInterface
{
/**
* Map of factory aliases to instances or factory callbacks.
*
* @var array<string, callable|NotificationFactoryInterface>
*/
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
* @throws FactoryNotFoundException
*/
public function get(string $id): NotificationFactoryInterface
{
@@ -48,38 +30,11 @@ 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);
}
/**
* 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
{
$this->factories[$alias] = $factory;
@@ -6,33 +6,10 @@ 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
*
* @phpstan-return ($id is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface :
* ($id is 'noty' ? \Flasher\Noty\Prime\NotyInterface :
* ($id is 'notyf' ? \Flasher\Notyf\Prime\NotyfInterface :
@@ -40,7 +17,9 @@ interface NotificationFactoryLocatorInterface
* ($id is 'toastr' ? \Flasher\Toastr\Prime\ToastrInterface :
* \Flasher\Prime\Factory\NotificationFactoryInterface)))))
*
* @throws \Flasher\Prime\Exception\FactoryNotFoundException If no factory is registered with the given identifier
* @throws \Flasher\Prime\Exception\FactoryNotFoundException
*/
public function get(string $id): NotificationFactoryInterface;
public function has(string $id): bool;
}
+4 -54
View File
@@ -12,33 +12,14 @@ 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.
* @mixin \Flasher\Prime\Notification\NotificationBuilder
*/
final readonly class Flasher implements FlasherInterface
{
use ForwardsCalls;
/**
* Current version of PHPFlasher.
*/
public const VERSION = '2.4.0';
/**
* 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,
@@ -47,13 +28,6 @@ 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);
@@ -69,20 +43,14 @@ 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.
* @param array<string, mixed> $criteria
* @param array<string, mixed> $context
*/
public function render(string $presenter = 'html', array $criteria = [], array $context = []): mixed
{
@@ -90,25 +58,7 @@ final readonly class Flasher implements FlasherInterface
}
/**
* Dynamically call methods on the default factory instance.
*
* 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
* @param mixed[] $parameters
*/
public function __call(string $method, array $parameters): mixed
{
+2 -61
View File
@@ -8,15 +8,6 @@ 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
@@ -24,78 +15,28 @@ use Flasher\Prime\Response\Presenter\ArrayPresenter;
interface FlasherInterface
{
/**
* Get a notification factory instance by its alias.
*
* 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')
*
* @phpstan-return ($alias is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface :
* ($alias is 'noty' ? \Flasher\Noty\Prime\NotyInterface :
* ($alias is 'notyf' ? \Flasher\Notyf\Prime\NotyfInterface :
* ($alias is 'sweetalert' ? \Flasher\SweetAlert\Prime\SweetAlertInterface :
* ($alias is 'toastr' ? \Flasher\Toastr\Prime\ToastrInterface :
* \Flasher\Prime\Factory\NotificationFactoryInterface)))))
*
* @throws \InvalidArgumentException When the requested factory cannot be resolved
*/
public function use(string $alias): NotificationFactoryInterface;
/**
* Get a notification factory instance by its alias (alias for use()).
*
* 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')
*
* @phpstan-return ($alias is 'flasher' ? \Flasher\Prime\Factory\FlasherFactoryInterface :
* ($alias is 'noty' ? \Flasher\Noty\Prime\NotyInterface :
* ($alias is 'notyf' ? \Flasher\Notyf\Prime\NotyfInterface :
* ($alias is 'sweetalert' ? \Flasher\SweetAlert\Prime\SweetAlertInterface :
* ($alias is 'toastr' ? \Flasher\Toastr\Prime\ToastrInterface :
* \Flasher\Prime\Factory\NotificationFactoryInterface)))))
*
* @throws \InvalidArgumentException When the requested factory cannot be resolved
*/
public function create(string $alias): NotificationFactoryInterface;
/**
* Renders the flash notifications based on the specified criteria, presenter, and context.
*
* 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<string, mixed> $criteria Optional filtering criteria for notifications
* @param array<string, mixed> $context Additional context or options for rendering
*
* @return mixed The rendered notifications in the requested format
* @param array<string, mixed> $criteria
* @param array<string, mixed> $context
*
* @phpstan-return ($presenter is 'html' ? string :
* ($presenter is 'array' ? ArrayPresenterType :
+8 -139
View File
@@ -9,31 +9,19 @@ use Flasher\Prime\Stamp\StampInterface;
use Flasher\Prime\Support\Traits\ForwardsCalls;
/**
* 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.
* Wraps a notification with metadata stamps.
*/
final class Envelope implements NotificationInterface
{
use ForwardsCalls;
/**
* Collection of stamps attached to this envelope, indexed by class name.
*
* @var array<class-string<StampInterface>, StampInterface>
*/
private array $stamps = [];
/**
* 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
* @param StampInterface[]|StampInterface $stamps
*/
public function __construct(private readonly NotificationInterface $notification, array|StampInterface $stamps = [])
{
@@ -43,23 +31,7 @@ final class Envelope implements NotificationInterface
}
/**
* Wraps a notification in an Envelope and adds the given stamps.
*
* 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
* @param StampInterface[]|StampInterface $stamps
*/
public static function wrap(NotificationInterface $notification, array|StampInterface $stamps = []): self
{
@@ -71,11 +43,6 @@ final class Envelope implements NotificationInterface
return $envelope;
}
/**
* Adds multiple stamps to the envelope.
*
* @param StampInterface ...$stamps The stamps to add
*/
public function with(StampInterface ...$stamps): void
{
foreach ($stamps as $stamp) {
@@ -83,18 +50,6 @@ final class Envelope implements NotificationInterface
}
}
/**
* Adds or replaces a stamp in the envelope.
*
* 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
{
if (!isset($this->stamps[$stamp::class]) || $replace) {
@@ -102,11 +57,6 @@ final class Envelope implements NotificationInterface
}
}
/**
* Removes specified stamps from the envelope.
*
* @param StampInterface ...$stamps The stamps to remove
*/
public function without(StampInterface ...$stamps): void
{
foreach ($stamps as $stamp) {
@@ -115,9 +65,7 @@ final class Envelope implements NotificationInterface
}
/**
* Removes a specific type of stamp from the envelope.
*
* @param class-string<StampInterface>|StampInterface $type The type of stamp to remove
* @param class-string<StampInterface>|StampInterface $type
*/
public function withoutStamp(string|StampInterface $type): void
{
@@ -127,13 +75,9 @@ final class Envelope implements NotificationInterface
}
/**
* Retrieves a stamp by its type.
*
* @template T of StampInterface
*
* @phpstan-param class-string<T> $type The class name of the stamp to retrieve
*
* @return StampInterface|null The stamp if found, null otherwise
* @phpstan-param class-string<T> $type
*
* @phpstan-return T|null
*/
@@ -146,148 +90,81 @@ final class Envelope implements NotificationInterface
}
/**
* Returns all stamps by their class name.
*
* @return array<class-string<StampInterface>, StampInterface> Map of stamp class names to stamp instances
* @return array<class-string<StampInterface>, StampInterface>
*/
public function all(): array
{
return $this->stamps;
}
/**
* Gets the original notification contained in the envelope.
*
* @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);
}
/**
* 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<string, mixed>,
* metadata: array<string, mixed>,
* } The notification data with metadata
* }
*/
public function toArray(): array
{
@@ -306,15 +183,7 @@ final class Envelope implements NotificationInterface
}
/**
* Dynamically call methods on the wrapped notification.
*
* This magic method allows calling methods directly on the envelope
* which will be forwarded to the wrapped notification.
*
* @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
* @param mixed[] $parameters
*/
public function __call(string $method, array $parameters): mixed
{
+2 -33
View File
@@ -5,14 +5,6 @@ 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,
@@ -28,8 +20,6 @@ namespace Flasher\Prime\Notification;
final class FlasherBuilder extends NotificationBuilder
{
/**
* Sets the notification type.
*
* @phpstan-param NotificationType $type
*/
public function type(string $type): static
@@ -38,8 +28,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Creates and stores a success notification.
*
* @param OptionsType $options
*/
public function success(string $message, array $options = [], ?string $title = null): Envelope
@@ -48,8 +36,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Creates and stores an error notification.
*
* @param OptionsType $options
*/
public function error(string $message, array $options = [], ?string $title = null): Envelope
@@ -58,8 +44,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Creates and stores an info notification.
*
* @param OptionsType $options
*/
public function info(string $message, array $options = [], ?string $title = null): Envelope
@@ -68,8 +52,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Creates and stores a warning notification.
*
* @param OptionsType $options
*/
public function warning(string $message, array $options = [], ?string $title = null): Envelope
@@ -78,10 +60,8 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Creates and stores a notification with specified type.
*
* @phpstan-param NotificationType|null $type
* @phpstan-param OptionsType $options
* @phpstan-param OptionsType $options
*/
public function flash(?string $type = null, ?string $message = null, array $options = [], ?string $title = null): Envelope
{
@@ -89,8 +69,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Sets multiple options.
*
* @param OptionsType $options
*/
public function options(array $options, bool $append = true): static
@@ -99,12 +77,10 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Sets a single option.
*
* @template T of OptionsType
* @template K of key-of<T>
*
* @phpstan-param K $name
* @phpstan-param K $name
* @phpstan-param T[K] $value
*/
public function option(string $name, mixed $value): static
@@ -112,9 +88,6 @@ final class FlasherBuilder extends NotificationBuilder
return parent::option($name, $value);
}
/**
* Sets the display timeout.
*/
public function timeout(int $milliseconds): self
{
$this->option('timeout', $milliseconds);
@@ -123,8 +96,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Sets the stacking direction.
*
* @param "top"|"bottom" $direction
*/
public function direction(string $direction): self
@@ -135,8 +106,6 @@ final class FlasherBuilder extends NotificationBuilder
}
/**
* Sets the display position.
*
* @phpstan-param OptionsType['position'] $position
*/
public function position(string $position): self
+1 -53
View File
@@ -6,89 +6,51 @@ 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 = '';
/**
* Configuration options for the notification.
*
* @var array<string, mixed>
*/
private array $options = [];
/**
* Gets the title.
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Sets the title.
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* Gets the message.
*/
public function getMessage(): string
{
return $this->message;
}
/**
* Sets the message.
*/
public function setMessage(string $message): void
{
$this->message = $message;
}
/**
* Gets the type.
*/
public function getType(): string
{
return $this->type;
}
/**
* Sets the type.
*/
public function setType(string $type): void
{
$this->type = $type;
}
/**
* Gets all options.
*
* @return array<string, mixed>
*/
public function getOptions(): array
@@ -97,21 +59,13 @@ final class Notification implements NotificationInterface
}
/**
* Sets or updates the options of the notification.
*
* This method merges the provided options with existing ones,
* with new values taking precedence over existing values.
*
* @param array<string, mixed> $options The options to set or update
* @param array<string, mixed> $options
*/
public function setOptions(array $options): void
{
$this->options = array_replace($this->options, $options);
}
/**
* Gets a specific option with a default fallback.
*/
public function getOption(string $name, mixed $default = null): mixed
{
return \array_key_exists($name, $this->options)
@@ -119,17 +73,11 @@ final class Notification implements NotificationInterface
: $default;
}
/**
* Sets a specific option.
*/
public function setOption(string $name, mixed $value): void
{
$this->options[$name] = $value;
}
/**
* Unsets a specific option.
*/
public function unsetOption(string $name): void
{
unset($this->options[$name]);
@@ -4,109 +4,43 @@ 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;
/**
* Gets all options of the notification.
*
* @return array<string, mixed> The notification options
* @return array<string, mixed>
*/
public function getOptions(): array;
/**
* Sets or updates the options of the notification.
*
* @param array<string, mixed> $options The options to set or update
* @param array<string, mixed> $options
*/
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<string, mixed>,
* } The notification as an array
* }
*/
public function toArray(): array;
}
+4 -20
View File
@@ -5,31 +5,19 @@ 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
* Stores additional context data for a notification.
*/
final readonly class ContextStamp implements PresentableStampInterface, StampInterface
{
/**
* Creates a new ContextStamp instance.
*
* @param array<string, mixed> $context The context data to attach
* @param array<string, mixed> $context
*/
public function __construct(private array $context)
{
}
/**
* Gets the context data.
*
* @return array<string, mixed> The context data
* @return array<string, mixed>
*/
public function getContext(): array
{
@@ -37,11 +25,7 @@ final readonly class ContextStamp implements PresentableStampInterface, StampInt
}
/**
* Converts the stamp to an array representation.
*
* This method implements the serialization logic required by PresentableStampInterface.
*
* @return array{context: array<string, mixed>} The array representation
* @return array{context: array<string, mixed>}
*/
public function toArray(): array
{
+2 -42
View File
@@ -5,61 +5,25 @@ 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
* Records when a notification was created.
*/
final readonly class CreatedAtStamp implements OrderableStampInterface, PresentableStampInterface, StampInterface
{
/**
* The creation timestamp.
*/
private \DateTimeImmutable $createdAt;
/**
* The format string for date presentation.
*/
private string $format;
/**
* 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)
{
$this->createdAt = $createdAt ?: new \DateTimeImmutable();
$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 this stamp with another for ordering purposes.
*
* This method implements the comparison logic required by OrderableStampInterface.
* It orders notifications chronologically by creation time.
*
* @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
{
if (!$orderable instanceof self) {
@@ -70,11 +34,7 @@ final readonly class CreatedAtStamp implements OrderableStampInterface, Presenta
}
/**
* 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
* @return array{created_at: string}
*/
public function toArray(): array
{
+1 -17
View File
@@ -5,30 +5,14 @@ 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
* Controls notification display delay.
*/
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;
+1 -19
View File
@@ -5,32 +5,14 @@ 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
* Controls notification persistence 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;
+4 -43
View File
@@ -7,42 +7,17 @@ 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
* Provides a unique identifier for notifications.
*/
final readonly class IdStamp implements PresentableStampInterface, StampInterface
{
/**
* The unique identifier.
*/
private string $id;
/**
* Creates a new IdStamp instance.
*
* @param string|null $id The identifier. If not provided, a unique identifier is generated.
*/
public function __construct(?string $id = null)
{
$this->id = $id ?? $this->generateUniqueId();
}
/**
* Generates a 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
{
try {
@@ -55,14 +30,9 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac
}
/**
* Indexes an array of envelopes by their ID.
* @param Envelope[] $envelopes
*
* This utility method creates a map of envelopes keyed by their unique IDs,
* adding IdStamps to envelopes that don't already have them.
*
* @param Envelope[] $envelopes An array of envelopes to index
*
* @return array<string, Envelope> An associative array of envelopes indexed by their ID
* @return array<string, Envelope>
*/
public static function indexById(array $envelopes): array
{
@@ -83,22 +53,13 @@ final readonly class IdStamp implements PresentableStampInterface, StampInterfac
return $map;
}
/**
* Gets the identifier.
*
* @return string The identifier
*/
public function getId(): string
{
return $this->id;
}
/**
* Converts the stamp to an array representation.
*
* This method implements the serialization logic required by PresentableStampInterface.
*
* @return array{id: string} The array representation
* @return array{id: string}
*/
public function toArray(): array
{
+1 -21
View File
@@ -5,29 +5,9 @@ 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
* Contract for stamps that affect notification ordering.
*/
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;
}
+2 -15
View File
@@ -5,25 +5,12 @@ 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
* Contract for stamps that contribute to presentation.
*/
interface PresentableStampInterface
{
/**
* 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<string, mixed> The array representation
* @return array<string, mixed>
*/
public function toArray(): array;
}
+3 -22
View File
@@ -5,43 +5,24 @@ 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
* Associates a notification with a predefined template.
*/
final readonly class PresetStamp implements StampInterface
{
/**
* Creates a new PresetStamp instance.
*
* @param string $preset The preset name to use
* @param array<string, mixed> $parameters Template parameters for variable substitution
* @param array<string, mixed> $parameters
*/
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;
}
/**
* Gets the template parameters.
*
* @return array<string, mixed> The template parameters
* @return array<string, mixed>
*/
public function getParameters(): array
{
+2 -37
View File
@@ -5,50 +5,19 @@ 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
* Controls notification priority for ordering.
*/
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) {
@@ -59,11 +28,7 @@ final readonly class PriorityStamp implements OrderableStampInterface, Presentab
}
/**
* Converts the stamp to an array representation.
*
* This method implements the serialization logic required by PresentableStampInterface.
*
* @return array{priority: int} The array representation
* @return array{priority: int}
*/
public function toArray(): array
{
+1 -11
View File
@@ -5,18 +5,8 @@ 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
* Marker interface for notification metadata.
*/
interface StampInterface
{
// This is a marker interface with no methods
}
+3 -21
View File
@@ -5,43 +5,25 @@ 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
* Provides translation parameters and locale for a notification.
*/
final readonly class TranslationStamp implements StampInterface
{
/**
* Creates a new TranslationStamp instance.
*
* @param array<string, mixed> $parameters Translation parameters for variable substitution
* @param string|null $locale Locale override (null uses default locale)
* @param array<string, mixed> $parameters
*/
public function __construct(private array $parameters = [], private ?string $locale = null)
{
}
/**
* Gets the translation parameters.
*
* @return array<string, mixed> The translation parameters
* @return array<string, mixed>
*/
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;
+1 -17
View File
@@ -5,30 +5,14 @@ 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
* Controls conditional suppression of notifications.
*/
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;
+1 -17
View File
@@ -5,30 +5,14 @@ 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
* Controls conditional display of notifications.
*/
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;
+3 -17
View File
@@ -7,27 +7,17 @@ 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.
* In-memory storage bag for notifications.
*/
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
* @return Envelope[]
*/
public function get(): array
{
@@ -35,11 +25,7 @@ final class ArrayBag implements BagInterface
}
/**
* Sets the stored notification envelopes.
*
* This method replaces all existing envelopes with the provided ones.
*
* @param Envelope[] $envelopes The notification envelopes to store
* @param Envelope[] $envelopes
*/
public function set(array $envelopes): void
{
+3 -16
View File
@@ -7,30 +7,17 @@ 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.
* Contract for notification envelope storage containers.
*/
interface BagInterface
{
/**
* Retrieves all stored notification envelopes.
*
* @return Envelope[] Array of all stored notification envelopes
* @return Envelope[]
*/
public function get(): array;
/**
* Sets the stored notification envelopes.
*
* This method should replace all existing envelopes with the provided ones.
*
* @param Envelope[] $envelopes The notification envelopes to store
* @param Envelope[] $envelopes
*/
public function set(array $envelopes): void;
}
+3 -19
View File
@@ -7,28 +7,17 @@ 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.
* Static/global storage bag for notifications.
*/
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
* @return Envelope[]
*/
public function get(): array
{
@@ -36,12 +25,7 @@ final class StaticBag implements BagInterface
}
/**
* 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
* @param Envelope[] $envelopes
*/
public function set(array $envelopes): void
{
@@ -7,26 +7,14 @@ 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.
* Contract for notification filtering criteria.
*/
interface CriteriaInterface
{
/**
* Applies the criterion to filter notification envelopes.
* @param Envelope[] $envelopes
*
* 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
* @return Envelope[]
*/
public function apply(array $envelopes): array;
}
@@ -8,36 +8,18 @@ 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.
* Filters notifications by delay time.
*/
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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -48,24 +30,15 @@ final readonly class DelayCriteria implements CriteriaInterface
}
/**
* Filters envelopes by delay time.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The notification envelopes to filter
*
* @return Envelope[] Envelopes that match the delay criteria
* @return Envelope[]
*/
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);
@@ -7,32 +7,17 @@ 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.
* Applies custom closure-based filters to notifications.
*/
final class FilterCriteria implements CriteriaInterface
{
/**
* The collection of filter callbacks.
*
* @var \Closure[]
*/
private array $callbacks;
/**
* 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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -51,14 +36,9 @@ final class FilterCriteria implements CriteriaInterface
}
/**
* Applies the filter callbacks to the envelopes.
* @param Envelope[] $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
* @return Envelope[]
*/
public function apply(array $envelopes): array
{
@@ -8,36 +8,18 @@ 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.
* Filters notifications by hops count.
*/
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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -48,24 +30,15 @@ final readonly class HopsCriteria implements CriteriaInterface
}
/**
* Filters envelopes by hops count.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The notification envelopes to filter
*
* @return Envelope[] Envelopes that match the hops criteria
* @return Envelope[]
*/
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);
@@ -7,27 +7,14 @@ 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.
* Limits the number of notifications returned.
*/
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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -39,11 +26,9 @@ final readonly class LimitCriteria implements CriteriaInterface
}
/**
* Limits the number of envelopes in the result set.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The notification envelopes to limit
*
* @return Envelope[] The limited set of notification envelopes
* @return Envelope[]
*/
public function apply(array $envelopes): array
{
@@ -20,29 +20,15 @@ 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.
* Sorts notifications based on stamp attributes.
*/
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<string, class-string<StampInterface>>
*/
private array $aliases = [
@@ -60,20 +46,12 @@ final class OrderByCriteria implements CriteriaInterface
];
/**
* The sort ordering configuration.
*
* @var array<class-string<StampInterface>, "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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -111,11 +89,9 @@ final class OrderByCriteria implements CriteriaInterface
}
/**
* Sorts the notification envelopes.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The notification envelopes to sort
*
* @return Envelope[] The sorted notification envelopes
* @return Envelope[]
*/
public function apply(array $envelopes): array
{
@@ -8,27 +8,14 @@ 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.
* Filters notifications by 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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -40,14 +27,9 @@ final class PresenterCriteria implements CriteriaInterface
}
/**
* Filters envelopes by presenter compatibility.
* @param Envelope[] $envelopes
*
* 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
* @return Envelope[]
*/
public function apply(array $envelopes): array
{
@@ -8,36 +8,18 @@ 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.
* Filters notifications by priority.
*/
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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria)
{
@@ -48,24 +30,15 @@ final readonly class PriorityCriteria implements CriteriaInterface
}
/**
* Filters envelopes by priority.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The envelopes to filter
*
* @return Envelope[] Envelopes that match the priority criteria
* @return Envelope[]
*/
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);
@@ -7,41 +7,21 @@ 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.
* Filters notifications by the presence of specific stamps.
*/
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<string, mixed>
*/
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
* @throws \InvalidArgumentException
*/
public function __construct(mixed $criteria, private readonly string $strategy = self::STRATEGY_AND)
{
@@ -55,24 +35,15 @@ final class StampsCriteria implements CriteriaInterface
}
/**
* Filters envelopes based on stamp presence.
* @param Envelope[] $envelopes
*
* @param Envelope[] $envelopes The notification envelopes to filter
*
* @return Envelope[] Envelopes that match the stamp criteria
* @return Envelope[]
*/
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()));
+3 -23
View File
@@ -8,32 +8,19 @@ 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.
* Default implementation of the filter interface.
*/
final class Filter implements FilterInterface
{
/**
* The chain of filtering criteria.
*
* @var CriteriaInterface[]
*/
private array $criteriaChain = [];
/**
* Applies the filter to an array of notification envelopes.
* @param Envelope[] $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
* @return Envelope[]
*/
public function apply(array $envelopes): array
{
@@ -44,13 +31,6 @@ 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;
+5 -47
View File
@@ -16,31 +16,15 @@ 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
* Creates and configures filter instances.
*/
final class FilterFactory implements FilterFactoryInterface
{
/**
* Registry of available criteria implementations.
*
* @var array<string, callable|CriteriaInterface>
*/
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 = [
@@ -60,16 +44,9 @@ final class FilterFactory implements FilterFactoryInterface
}
/**
* Creates a filter based on the provided configuration.
* @param array<string, mixed> $config
*
* This method creates a new filter and adds criteria to it based on
* the provided configuration keys and values.
*
* @param array<string, mixed> $config Configuration for the filter criteria
*
* @return Filter The created filter with configured criteria
*
* @throws CriteriaNotRegisteredException If a requested criterion doesn't exist
* @throws CriteriaNotRegisteredException
*/
public function createFilter(array $config): Filter
{
@@ -84,33 +61,14 @@ 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;
}
/**
* 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
* @throws CriteriaNotRegisteredException
* @throws \UnexpectedValueException
*/
private function createCriteria(string $name, mixed $value): CriteriaInterface
{
+1 -47
View File
@@ -8,65 +8,30 @@ 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
* @return Envelope[]
*/
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);
@@ -76,22 +41,11 @@ 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);
+1 -34
View File
@@ -6,51 +6,18 @@ 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
{
/**
* Retrieves all stored notification envelopes.
*
* @return Envelope[] Array of all stored notification envelopes
* @return Envelope[]
*/
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;
}
+1 -50
View File
@@ -12,30 +12,13 @@ use Flasher\Prime\EventDispatcher\Event\PostUpdateEvent;
use Flasher\Prime\EventDispatcher\Event\RemoveEvent;
use Flasher\Prime\EventDispatcher\Event\UpdateEvent;
use Flasher\Prime\EventDispatcher\EventDispatcherInterface;
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
{
/**
* 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<string, mixed> $criteria Default criteria for filtering notifications
* @param array<string, mixed> $criteria
*/
public function __construct(
private StorageInterface $storage,
@@ -46,8 +29,6 @@ final readonly class StorageManager implements StorageManagerInterface
}
/**
* Retrieves all stored envelopes.
*
* @return Envelope[]
*/
public function all(): array
@@ -56,17 +37,9 @@ final readonly class StorageManager implements StorageManagerInterface
}
/**
* Filters notifications by criteria.
*
* This method combines default criteria with provided criteria,
* creates a filter using the filter factory, and applies it to envelopes.
* Before applying the filter, it dispatches a FilterEvent to allow modification.
*
* @param array<string, mixed> $criteria
*
* @return Envelope[]
*
* @throws CriteriaNotRegisteredException
*/
public function filter(array $criteria = []): array
{
@@ -79,12 +52,6 @@ final readonly class StorageManager implements StorageManagerInterface
return $event->getFilter()->apply($event->getEnvelopes());
}
/**
* Adds notification envelopes to storage.
*
* Before adding envelopes, it dispatches a PersistEvent to allow modification
* of the envelopes. After storage, it dispatches a PostPersistEvent.
*/
public function add(Envelope ...$envelopes): void
{
$event = new PersistEvent($envelopes);
@@ -96,12 +63,6 @@ final readonly class StorageManager implements StorageManagerInterface
$this->eventDispatcher->dispatch($event);
}
/**
* Updates notification envelopes in storage.
*
* Before updating envelopes, it dispatches an UpdateEvent to allow modification
* of the envelopes. After update, it dispatches a PostUpdateEvent.
*/
public function update(Envelope ...$envelopes): void
{
$event = new UpdateEvent($envelopes);
@@ -113,13 +74,6 @@ final readonly class StorageManager implements StorageManagerInterface
$this->eventDispatcher->dispatch($event);
}
/**
* Removes 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.
*/
public function remove(Envelope ...$envelopes): void
{
$event = new RemoveEvent($envelopes);
@@ -132,9 +86,6 @@ final readonly class StorageManager implements StorageManagerInterface
$this->eventDispatcher->dispatch($event);
}
/**
* Clears all notification envelopes from storage.
*/
public function clear(): void
{
$this->storage->clear();