Simplify PHPDoc comments in Plugin, HTTP, Stamp, and Test constraint classes

Removes verbose documentation while preserving type annotations.
This commit is contained in:
Younes ENNAJI
2026-01-16 00:07:42 +01:00
parent 65e9125539
commit 3f920f2c01
22 changed files with 56 additions and 973 deletions
@@ -7,40 +7,13 @@ namespace Flasher\Prime\Http\Csp;
use Flasher\Prime\Http\RequestInterface;
use Flasher\Prime\Http\ResponseInterface;
/**
* ContentSecurityPolicyHandler - Manages Content Security Policy for PHPFlasher.
*
* This class handles the complex task of managing Content Security Policy (CSP) headers
* to allow PHPFlasher's JavaScript and CSS to run securely. It generates nonces for
* scripts and styles, modifies CSP headers to include these nonces, and ensures that
* inline code can execute without being blocked by CSP.
*
* Design patterns:
* - Strategy: Implements a specific approach to CSP handling
* - Adapter: Works with different request and response implementations
*/
final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandlerInterface
{
/**
* Header for passing script nonce between components.
*/
private const SCRIPT_NONCE_HEADER = 'X-PHPFlasher-Script-Nonce';
/**
* Header for passing style nonce between components.
*/
private const STYLE_NONCE_HEADER = 'X-PHPFlasher-Style-Nonce';
/**
* Whether CSP handling is disabled.
*/
private bool $cspDisabled = false;
/**
* Creates a new ContentSecurityPolicyHandler instance.
*
* @param NonceGeneratorInterface $nonceGenerator The generator for creating secure nonces
*/
public function __construct(private readonly NonceGeneratorInterface $nonceGenerator)
{
}
@@ -87,14 +60,7 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Returns nonces from headers if existing, otherwise null.
*
* This method checks if the necessary nonce headers are present in the provided
* request or response object and returns their values if found.
*
* @param RequestInterface|ResponseInterface $object The object to check for nonce headers
*
* @return array{csp_script_nonce: ?string, csp_style_nonce: ?string}|null Nonce values or null if not found
* @return array{csp_script_nonce: ?string, csp_style_nonce: ?string}|null
*/
private function getHeaderNonces(RequestInterface|ResponseInterface $object): ?array
{
@@ -108,27 +74,12 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
return null;
}
/**
* Removes temporary nonce headers from the response.
*
* These headers are used internally to pass nonces between components but
* should not be sent to the client.
*
* @param ResponseInterface $response The response to clean
*/
private function cleanHeaders(ResponseInterface $response): void
{
$response->removeHeader(self::SCRIPT_NONCE_HEADER);
$response->removeHeader(self::STYLE_NONCE_HEADER);
}
/**
* Removes all CSP headers from the response.
*
* This is used when CSP handling is disabled to ensure no CSP restrictions are applied.
*
* @param ResponseInterface $response The response to modify
*/
private function removeCspHeaders(ResponseInterface $response): void
{
$response->removeHeader('X-Content-Security-Policy');
@@ -137,15 +88,9 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Updates Content-Security-Policy headers in a response.
* @param array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} $nonces
*
* This method modifies existing CSP headers to include nonces for PHPFlasher's
* JavaScript and CSS, allowing them to execute without being blocked by CSP.
*
* @param ResponseInterface $response The response to modify
* @param array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} $nonces The nonces to add to CSP
*
* @return array{csp_script_nonce?: ?string, csp_style_nonce?: ?string} The nonces used
* @return array{csp_script_nonce?: ?string, csp_style_nonce?: ?string}
*/
private function updateCspHeaders(ResponseInterface $response, array $nonces = []): array
{
@@ -202,22 +147,13 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
return $nonces;
}
/**
* Generates a valid Content-Security-Policy nonce.
*
* @return string The generated nonce
*/
private function generateNonce(): string
{
return $this->nonceGenerator->generate();
}
/**
* Converts a directive set array into Content-Security-Policy header value.
*
* @param array<string, string[]> $directives The CSP directives to convert
*
* @return string The formatted CSP header value
* @param array<string, string[]> $directives
*/
private function generateCspHeader(array $directives): string
{
@@ -225,11 +161,7 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Converts a Content-Security-Policy header value into a directive set array.
*
* @param string|null $header The CSP header value to parse
*
* @return array<string, string[]> The parsed directive set
* @return array<string, string[]>
*/
private function parseDirectives(?string $header): array
{
@@ -248,15 +180,7 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Detects if the 'unsafe-inline' is permitted for a directive within the directive set.
*
* This method checks if a specific CSP directive allows inline scripts or styles,
* taking into account CSP level 2+ hash and nonce exceptions.
*
* @param array<string, string[]> $directivesSet The directives to check
* @param string $type The directive type to check
*
* @return bool True if inline content is permitted
* @param array<string, string[]> $directivesSet
*/
private function authorizesInline(array $directivesSet, string $type): bool
{
@@ -270,14 +194,7 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Checks if a directive list contains hash or nonce restrictions.
*
* This is important for CSP processing because in CSP Level 2+, the presence of a
* hash or nonce invalidates 'unsafe-inline' even if it's present.
*
* @param string[] $directives The CSP directive values to check
*
* @return bool True if the directive list contains a hash or nonce restriction
* @param string[] $directives
*/
private function hasHashOrNonce(array $directives): bool
{
@@ -297,15 +214,9 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Finds the fallback directive for a specific directive type.
* @param array<string, string[]> $directiveSet
*
* In CSP, if a specific directive isn't defined, browsers fall back to default-src.
* This method implements that behavior for the PHPFlasher CSP handler.
*
* @param array<string, string[]> $directiveSet The complete directive set
* @param string $type The directive type to find a fallback for
*
* @return string[]|null The fallback directive values, or null if no fallback exists
* @return string[]|null
*/
private function getDirectiveFallback(array $directiveSet, string $type): ?array
{
@@ -318,18 +229,11 @@ final class ContentSecurityPolicyHandler implements ContentSecurityPolicyHandler
}
/**
* Retrieves the Content-Security-Policy headers from a response.
*
* This method extracts all CSP-related headers from the response
* and parses their values into directive sets.
*
* @param ResponseInterface $response The response to extract CSP headers from
*
* @return array{
* Content-Security-Policy?: array<string, string[]>,
* Content-Security-Policy-Report-Only?: array<string, string[]>,
* X-Content-Security-Policy?: array<string, string[]>,
* } Mapped CSP headers and their parsed directive sets
* }
*/
private function getCspHeaders(ResponseInterface $response): array
{
+3 -39
View File
@@ -6,44 +6,21 @@ namespace Flasher\Prime\Http;
use Flasher\Prime\FlasherInterface;
/**
* RequestExtension - Converts framework flash messages to PHPFlasher notifications.
*
* This class is responsible for extracting flash messages from request objects
* (typically stored in the session) and converting them to PHPFlasher notifications.
* It provides a bridge between framework-specific flash messaging systems and
* PHPFlasher's notification system.
*
* Design patterns:
* - Adapter: Adapts framework flash message systems to PHPFlasher
* - Mediator: Coordinates the interaction between framework and PHPFlasher
*/
final readonly class RequestExtension implements RequestExtensionInterface
{
/**
* Flattened mapping from flash message keys to notification types.
*
* @var array<string, string>
*/
private array $mapping;
/**
* Creates a new RequestExtension instance.
*
* @param FlasherInterface $flasher The flasher service for creating notifications
* @param array<string, string[]> $mapping Mapping from framework flash types to PHPFlasher types
* @param array<string, string[]> $mapping
*/
public function __construct(private FlasherInterface $flasher, array $mapping = [])
{
$this->mapping = $this->flatMapping($mapping);
}
/**
* {@inheritdoc}
*
* This method processes the request to extract flash messages and convert them
* to PHPFlasher notifications. It only processes requests with active sessions.
*/
public function flash(RequestInterface $request, ResponseInterface $response): ResponseInterface
{
if (!$request->hasSession()) {
@@ -56,14 +33,9 @@ final readonly class RequestExtension implements RequestExtensionInterface
}
/**
* Flattens a nested mapping array to a simple key-value mapping.
* @param array<string, string[]> $mapping
*
* Converts a mapping like ['success' => ['success', 'ok']] to
* ['success' => 'success', 'ok' => 'success'].
*
* @param array<string, string[]> $mapping The nested mapping array
*
* @return array<string, string> The flattened mapping
* @return array<string, string>
*/
private function flatMapping(array $mapping): array
{
@@ -78,14 +50,6 @@ final readonly class RequestExtension implements RequestExtensionInterface
return $flatMapping;
}
/**
* Processes the request and converts flash messages to notifications.
*
* This method checks for each flash message type in the request and creates
* corresponding PHPFlasher notifications for any that are found.
*
* @param RequestInterface $request The request to process
*/
private function processRequest(RequestInterface $request): void
{
foreach ($this->mapping as $alias => $type) {
+1 -52
View File
@@ -8,26 +8,10 @@ use Flasher\Prime\FlasherInterface;
use Flasher\Prime\Http\Csp\ContentSecurityPolicyHandlerInterface;
use Flasher\Prime\Response\Presenter\HtmlPresenter;
/**
* ResponseExtension - Injects notification HTML into HTTP responses.
*
* This class is responsible for injecting the HTML for notifications into HTTP responses.
* It handles the placement of notification code at appropriate injection points in HTML
* responses, manages Content Security Policy headers, and ensures that notifications are
* only injected in suitable responses.
*
* Design patterns:
* - Decorator: Enhances HTTP responses by adding notification HTML
* - Chain of Responsibility: Determines whether each response is suitable for modification
*/
final readonly class ResponseExtension implements ResponseExtensionInterface
{
/**
* Creates a new ResponseExtension instance.
*
* @param FlasherInterface $flasher The flasher service for rendering notifications
* @param ContentSecurityPolicyHandlerInterface $cspHandler The CSP handler for managing security headers
* @param list<non-empty-string> $excludedPaths Regex patterns for paths where notifications shouldn't be rendered
* @param list<non-empty-string> $excludedPaths
*/
public function __construct(
private FlasherInterface $flasher,
@@ -36,16 +20,6 @@ final readonly class ResponseExtension implements ResponseExtensionInterface
) {
}
/**
* {@inheritdoc}
*
* This method:
* 1. Checks if the response is eligible for notification injection
* 2. Finds an appropriate insertion point in the HTML
* 3. Updates CSP headers if needed
* 4. Renders the notification HTML
* 5. Injects the rendered HTML into the response
*/
public function render(RequestInterface $request, ResponseInterface $response): ResponseInterface
{
if (!$this->isRenderable($request, $response)) {
@@ -99,24 +73,6 @@ final readonly class ResponseExtension implements ResponseExtensionInterface
return $response;
}
/**
* Determines if a response is eligible for notification rendering.
*
* A response is renderable if:
* - The request path is not excluded
* - It's not an AJAX request
* - It's an HTML request format
* - The response is HTML
* - The response is successful (2XX)
* - It's not a redirection
* - It's not an attachment
* - It's not JSON
*
* @param RequestInterface $request The request
* @param ResponseInterface $response The response
*
* @return bool True if notifications should be rendered in this response
*/
private function isRenderable(RequestInterface $request, ResponseInterface $response): bool
{
return !$this->isPathExcluded($request)
@@ -129,13 +85,6 @@ final readonly class ResponseExtension implements ResponseExtensionInterface
&& !$response->isJson();
}
/**
* Checks if the current request path is excluded from notification rendering.
*
* @param RequestInterface $request The request
*
* @return bool True if the path is excluded
*/
private function isPathExcluded(RequestInterface $request): bool
{
if (!method_exists($request, 'getUri')) { // @phpstan-ignore-line
+13 -75
View File
@@ -9,16 +9,6 @@ use Flasher\Prime\FlasherInterface;
use Flasher\Prime\Notification\Type;
/**
* FlasherPlugin - Core plugin for PHPFlasher.
*
* This class represents the core PHPFlasher plugin, which provides the basic
* notification functionality. It serves as the default plugin and the foundation
* for other plugins to build upon.
*
* Design patterns:
* - Core Plugin: Provides essential functionality that other plugins extend
* - Configuration Management: Handles normalization and defaults for system config
*
* @phpstan-type ConfigType array{
* default: string,
* main_script: string,
@@ -70,21 +60,11 @@ final class FlasherPlugin extends Plugin
return FlasherInterface::class;
}
/**
* Gets the default plugin name.
*
* @return string The default plugin name
*/
public function getDefault(): string
{
return 'flasher';
}
/**
* Gets the path to the main PHPFlasher script.
*
* @return string The script path
*/
public function getRootScript(): string
{
return '/vendor/flasher/flasher.min.js';
@@ -103,17 +83,6 @@ final class FlasherPlugin extends Plugin
return '/vendor/flasher/flasher.min.css';
}
/**
* {@inheritdoc}
*
* This overridden method extends the parent's normalization process with
* additional steps specific to the core plugin:
* - Normalizing plugin configurations
* - Normalizing preset configurations
* - Adding default configuration values
* - Normalizing flash bag mappings
* - Setting preset defaults
*/
public function normalizeConfig(array $config = []): array
{
$config = parent::normalizeConfig($config);
@@ -129,11 +98,6 @@ final class FlasherPlugin extends Plugin
}
/**
* Normalizes the plugins configuration.
*
* This method ensures the core plugin is properly configured, merges global options
* with plugin-specific options, and normalizes array formats for scripts and styles.
*
* @param array{
* scripts: string[],
* styles: string[],
@@ -143,14 +107,14 @@ final class FlasherPlugin extends Plugin
* styles?: string[],
* options?: array<string, mixed>,
* }>,
* } $config The raw configuration
* } $config
*
* @return array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>,
* plugins: array<string, mixed>,
* } The normalized configuration
* }
*/
private function normalizePlugins(array $config): array
{
@@ -191,11 +155,6 @@ final class FlasherPlugin extends Plugin
}
/**
* Normalizes the themes configuration.
*
* This method ensures that the themes configuration has the correct structure
* and includes default themes unless explicitly disabled.
*
* @param array{
* scripts: string[],
* styles: string[],
@@ -206,7 +165,7 @@ final class FlasherPlugin extends Plugin
* styles?: string[],
* options?: array<string, mixed>,
* }>,
* } $config The raw configuration
* } $config
*
* @return array{
* scripts: string[],
@@ -214,7 +173,7 @@ final class FlasherPlugin extends Plugin
* options: array<string, mixed>,
* plugins: array<string, mixed>,
* themes: array<string, mixed>,
* } The normalized configuration
* }
*/
private function normalizeThemes(array $config): array
{
@@ -261,18 +220,13 @@ final class FlasherPlugin extends Plugin
}
/**
* Normalizes the presets configuration.
*
* This method ensures that string-only preset definitions are expanded to full arrays
* with the string value as the message.
*
* @param array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>,
* presets?: array<string, string|array<string, mixed>>,
* plugins: array<string, mixed>,
* } $config The raw configuration
* } $config
*
* @return array{
* scripts: string[],
@@ -280,7 +234,7 @@ final class FlasherPlugin extends Plugin
* options: array<string, mixed>,
* presets?: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* } The normalized configuration
* }
*/
private function normalizePresets(array $config): array
{
@@ -296,11 +250,6 @@ final class FlasherPlugin extends Plugin
}
/**
* Adds default configuration values.
*
* This method ensures that all required configuration keys have values,
* providing defaults for any that are missing.
*
* @param array{
* default?: string|null,
* main_script?: string|null,
@@ -313,7 +262,7 @@ final class FlasherPlugin extends Plugin
* options: array<string, mixed>,
* presets?: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* } $config The raw configuration
* } $config
*
* @return array{
* default: string|null,
@@ -327,7 +276,7 @@ final class FlasherPlugin extends Plugin
* options: array<string, mixed>,
* presets: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* } The configuration with defaults added
* }
*/
private function addDefaultConfig(array $config): array
{
@@ -349,11 +298,6 @@ final class FlasherPlugin extends Plugin
}
/**
* Normalizes the flash bag configuration.
*
* This method ensures that the flash bag mapping has the correct structure
* and includes default mappings unless explicitly disabled.
*
* @param array{
* default: string|null,
* main_script: string|null,
@@ -367,7 +311,7 @@ final class FlasherPlugin extends Plugin
* presets: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* flash_bag?: bool|array<string, string[]>,
* } $config The raw configuration
* } $config
*
* @return array{
* default: string|null,
@@ -382,7 +326,7 @@ final class FlasherPlugin extends Plugin
* presets: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* flash_bag: false|array<string, string[]>,
* } The normalized configuration
* }
*/
private function normalizeFlashBag(array $config): array
{
@@ -407,10 +351,6 @@ final class FlasherPlugin extends Plugin
}
/**
* Sets default values for presets.
*
* This method ensures that all presets have required fields with default values.
*
* @param array{
* default: string|null,
* main_script: string|null,
@@ -424,7 +364,7 @@ final class FlasherPlugin extends Plugin
* presets: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* flash_bag: false|array<string, string[]>,
* } $config The raw configuration
* } $config
*
* @return array{
* default: string|null,
@@ -439,7 +379,7 @@ final class FlasherPlugin extends Plugin
* presets: array<string, array<string, mixed>>,
* plugins: array<string, mixed>,
* flash_bag: false|array<string, string[]>,
* } The normalized configuration
* }
*/
private function setPresetsDefaults(array $config): array
{
@@ -452,13 +392,11 @@ final class FlasherPlugin extends Plugin
}
/**
* Returns the default themes configuration.
*
* @return array<string, array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>
* }> The default themes configuration
* }>
*/
private function getDefaultThemes(): array
{
-61
View File
@@ -4,85 +4,38 @@ declare(strict_types=1);
namespace Flasher\Prime\Plugin;
/**
* Plugin - Base class for PHPFlasher plugins.
*
* This abstract class provides a common foundation for PHPFlasher plugins,
* implementing standard behaviors and conventions. Plugin implementations
* should extend this class and customize the specific aspects they need to change.
*
* Design patterns:
* - Template Method: Defines standard algorithms with customizable steps
* - Convention over Configuration: Provides sensible defaults based on naming conventions
*/
abstract class Plugin implements PluginInterface
{
/**
* {@inheritdoc}
*
* By default, derives the plugin name from its alias with a "flasher_" prefix.
*/
public function getName(): string
{
return 'flasher_'.$this->getAlias();
}
/**
* {@inheritdoc}
*
* By default, derives the service ID from the plugin alias with a "flasher." prefix.
*/
public function getServiceId(): string
{
return 'flasher.'.$this->getAlias();
}
/**
* {@inheritdoc}
*
* By default, returns an empty array, meaning no service aliases are registered.
*/
public function getServiceAliases(): string|array
{
return [];
}
/**
* {@inheritdoc}
*
* By default, returns an empty array, meaning no scripts are included.
*/
public function getScripts(): string|array
{
return [];
}
/**
* {@inheritdoc}
*
* By default, returns an empty array, meaning no styles are included.
*/
public function getStyles(): string|array
{
return [];
}
/**
* {@inheritdoc}
*
* By default, returns an empty array, meaning no options are configured.
*/
public function getOptions(): array
{
return [];
}
/**
* {@inheritdoc}
*
* By default, locates the assets directory by convention based on the plugin's
* class location.
*/
public function getAssetsDir(): string
{
$resourcesDir = $this->getResourcesDir();
@@ -91,13 +44,6 @@ abstract class Plugin implements PluginInterface
return realpath($assetsDir) ?: '';
}
/**
* {@inheritdoc}
*
* By default, locates the resources directory by convention based on the plugin's
* class location. Looks first for a Resources subdirectory in the same directory,
* then falls back to a Resources directory one level up.
*/
public function getResourcesDir(): string
{
$reflection = new \ReflectionClass($this);
@@ -109,13 +55,6 @@ abstract class Plugin implements PluginInterface
return realpath($resourcesDir) ?: '';
}
/**
* {@inheritdoc}
*
* This implementation merges the provided config with defaults from the plugin's
* getScripts(), getStyles(), and getOptions() methods and ensures arrays are properly
* normalized.
*/
public function normalizeConfig(array $config): array
{
$config = [
@@ -7,17 +7,6 @@ namespace Flasher\Prime\Response\Presenter;
use Flasher\Prime\Response\Response;
/**
* ArrayPresenter - Presents notifications as a PHP array structure.
*
* This presenter converts a Response object into a PHP array structure that can
* be used for further processing, serialization, or API responses. It's particularly
* useful as a base for other presenters that need structured data rather than
* rendered output.
*
* Design patterns:
* - Presenter: Transforms domain objects (Notifications) into a presentation format
* - Adapter: Converts internal representation to a format suitable for external use
*
* @phpstan-type ArrayPresenterType array{
* envelopes: array<array{
* title: string,
@@ -35,15 +24,6 @@ use Flasher\Prime\Response\Response;
final class ArrayPresenter implements PresenterInterface
{
/**
* Renders a response as a PHP array.
*
* This method simply calls toArray() on the response object to convert it
* to a structured array representation.
*
* @param Response $response The response to render
*
* @return array The array representation of the response
*
* @phpstan-return ArrayPresenterType
*/
public function render(Response $response): array
+1 -44
View File
@@ -7,49 +7,14 @@ namespace Flasher\Prime\Response\Presenter;
use Flasher\Prime\Response\Response;
use Livewire\LivewireManager;
/**
* HtmlPresenter - Presents notifications as JavaScript for HTML pages.
*
* This presenter generates JavaScript code that can be embedded in HTML pages
* to display notifications. It handles resource loading, Content Security Policy
* considerations, and special HTML content from notifications.
*
* Design patterns:
* - Presenter: Transforms domain objects into a presentation format
* - Template Method: Defines the skeleton of the JavaScript generation algorithm
* - Adapter: Integrates with frontend frameworks like Livewire
*/
final class HtmlPresenter implements PresenterInterface
{
/**
* Placeholder for inserting notification data in already rendered HTML.
*/
public const FLASHER_REPLACE_ME = '/** {--FLASHER_REPLACE_ME--} **/';
/**
* Placeholder for inserting at the end of head tag.
*/
public const HEAD_END_PLACE_HOLDER = '</head>';
/**
* Placeholder for inserting at the end of body tag.
*/
public const BODY_END_PLACE_HOLDER = '</body>';
/**
* Renders a response as JavaScript code for HTML pages.
*
* This method generates JavaScript code that will:
* 1. Extract any direct HTML content from notifications
* 2. Create a script that dynamically loads resources if needed
* 3. Initialize and render the notifications using the frontend library
* 4. Set up event listeners for dynamic notification rendering
*
* @param Response $response The response to render
*
* @return string The generated JavaScript code
*
* @throws \JsonException If JSON encoding fails
* @throws \JsonException
*/
public function render(Response $response): string
{
@@ -166,14 +131,6 @@ final class HtmlPresenter implements PresenterInterface
JAVASCRIPT;
}
/**
* Generates the JavaScript for Livewire integration.
*
* This method checks if Livewire is available and, if so, generates
* the necessary JavaScript to handle Livewire page navigation events.
*
* @return string The Livewire integration JavaScript, or an empty string if Livewire is not available
*/
private function getLivewireListenerScript(): string
{
if (!class_exists(LivewireManager::class)) {
@@ -12,16 +12,6 @@ use Flasher\Prime\Stamp\PluginStamp;
use Flasher\Prime\Template\TemplateEngineInterface;
/**
* ResourceManager - Manages resources for notification responses.
*
* This class is responsible for populating Response objects with the resources
* (scripts, styles, options) required by the notifications they contain. It also
* handles rendering HTML templates for notifications that require it.
*
* Design patterns:
* - Builder: Builds complete responses by adding resources incrementally
* - Template Method: Defines the algorithm for populating responses with customizable steps
*
* @phpstan-type ResourceType array{
* scripts?: string[],
* styles?: string[],
@@ -32,12 +22,7 @@ use Flasher\Prime\Template\TemplateEngineInterface;
final readonly class ResourceManager implements ResourceManagerInterface
{
/**
* Creates a new ResourceManager instance.
*
* @param TemplateEngineInterface $templateEngine Engine for rendering notification templates
* @param AssetManagerInterface $assetManager Manager for resolving asset paths
* @param string|null $mainScript Path to the main PHPFlasher script
* @param ResourceType[] $resources Configuration for plugin-specific resources
* @param ResourceType[] $resources
*/
public function __construct(
private TemplateEngineInterface $templateEngine,
@@ -47,15 +32,6 @@ final readonly class ResourceManager implements ResourceManagerInterface
) {
}
/**
* {@inheritdoc}
*
* This implementation:
* 1. Sets the main script path if available
* 2. Identifies unique plugins used by the notifications
* 3. Renders HTML templates for notifications that need them
* 4. Adds resources (scripts, styles, options) for each plugin
*/
public function populateResponse(Response $response): Response
{
if (null !== $this->mainScript) {
@@ -85,15 +61,6 @@ final readonly class ResourceManager implements ResourceManagerInterface
return $response;
}
/**
* Adds an HTML stamp to a notification envelope.
*
* This method renders a template for the notification and attaches the
* rendered HTML as a stamp on the envelope.
*
* @param string $view The template view name
* @param Envelope $envelope The notification envelope
*/
private function addHtmlStamp(string $view, Envelope $envelope): void
{
$compiled = $this->templateEngine->render($view, ['envelope' => $envelope]);
@@ -101,15 +68,6 @@ final readonly class ResourceManager implements ResourceManagerInterface
$envelope->withStamp(new HtmlStamp($compiled));
}
/**
* Adds resources for a plugin to a response.
*
* This method retrieves the resource configuration for a plugin and adds
* its scripts, styles, and options to the response.
*
* @param Response $response The response to populate
* @param string $plugin The plugin alias
*/
private function addResources(Response $response, string $plugin): void
{
$resource = $this->resources[$plugin] ?? [];
+8 -66
View File
@@ -6,62 +6,35 @@ namespace Flasher\Prime\Response;
use Flasher\Prime\Notification\Envelope;
/**
* Response - Container for notification data ready for presentation.
*
* This class encapsulates all the data needed to render notifications in a response,
* including the notification envelopes themselves, scripts, styles, options, and context.
* It serves as a data transfer object between the notification storage and presenters.
*
* Design patterns:
* - Data Transfer Object (DTO): Aggregates all data needed for notification rendering
* - Immutable Collection: Core envelopes and context are immutable while allowing
* controlled mutation of presentation resources
*/
final class Response
{
/**
* The main script to be included in the response.
*/
private string $mainScript = '';
/**
* Additional JavaScript files to be included in the response.
*
* @var string[]
*/
private array $scripts = [];
/**
* CSS stylesheets to be included in the response.
*
* @var string[]
*/
private array $styles = [];
/**
* Plugin-specific options organized by plugin alias.
*
* @var array<string, array<string, mixed>>
*/
private array $options = [];
/**
* Creates a new Response instance.
*
* @param Envelope[] $envelopes The notification envelopes to present
* @param array<string, mixed> $context Additional context for the presentation (e.g., CSP nonces)
* @param Envelope[] $envelopes
* @param array<string, mixed> $context
*/
public function __construct(private readonly array $envelopes, private readonly array $context)
{
}
/**
* Adds JavaScript scripts to the response.
*
* This method appends new scripts to the existing list, ensuring uniqueness.
*
* @param string[] $scripts The scripts to add
* @param string[] $scripts
*/
public function addScripts(array $scripts): void
{
@@ -69,11 +42,7 @@ final class Response
}
/**
* Adds CSS stylesheets to the response.
*
* This method appends new styles to the existing list, ensuring uniqueness.
*
* @param string[] $styles The styles to add
* @param string[] $styles
*/
public function addStyles(array $styles): void
{
@@ -81,13 +50,7 @@ final class Response
}
/**
* Adds or merges options for a specific plugin.
*
* This method merges new options with existing ones for the specified alias,
* creating the entry if it doesn't exist.
*
* @param string $alias The plugin alias
* @param array<string, mixed> $options The options to add or merge
* @param array<string, mixed> $options
*/
public function addOptions(string $alias, array $options): void
{
@@ -96,34 +59,24 @@ final class Response
}
/**
* Gets the notification envelopes to be presented.
*
* @return Envelope[] The notification envelopes
* @return Envelope[]
*/
public function getEnvelopes(): array
{
return $this->envelopes;
}
/**
* Gets the main script path.
*/
public function getMainScript(): string
{
return $this->mainScript;
}
/**
* Sets the main script path.
*/
public function setMainScript(string $mainScript): void
{
$this->mainScript = $mainScript;
}
/**
* Gets the CSS stylesheets.
*
* @return string[]
*/
public function getStyles(): array
@@ -132,8 +85,6 @@ final class Response
}
/**
* Gets the JavaScript scripts.
*
* @return string[]
*/
public function getScripts(): array
@@ -142,9 +93,7 @@ final class Response
}
/**
* Gets the plugin-specific options.
*
* @return array<string, array<string, mixed>> The options organized by plugin alias
* @return array<string, array<string, mixed>>
*/
public function getOptions(): array
{
@@ -152,9 +101,7 @@ final class Response
}
/**
* Gets the presentation context.
*
* @return array<string, mixed> The context data
* @return array<string, mixed>
*/
public function getContext(): array
{
@@ -162,11 +109,6 @@ final class Response
}
/**
* Converts the response to an array representation.
*
* This method transforms all data in the response, including converting notification
* envelopes to arrays, into a format suitable for serialization or rendering.
*
* @return array{
* envelopes: array<array{
* title: string,
+8 -53
View File
@@ -16,33 +16,15 @@ use Flasher\Prime\Response\Resource\ResourceManagerInterface;
use Flasher\Prime\Storage\StorageManagerInterface;
/**
* ResponseManager - Manages notification response generation and presentation.
*
* This class orchestrates the process of retrieving notifications from storage,
* populating them with resources (scripts, styles), and rendering them with the
* appropriate presenter. It serves as the central coordinator for notification rendering.
*
* Design patterns:
* - Mediator: Coordinates the interaction between storage, resources, events, and presenters
* - Strategy: Uses different presentation strategies based on the requested format
* - Factory: Creates presenters on demand
* @phpstan-import-type ArrayPresenterType from ArrayPresenter
*/
final class ResponseManager implements ResponseManagerInterface
{
/**
* Registry of presenter instances or factories.
*
* @var callable[]|PresenterInterface[]
*/
private array $presenters = [];
/**
* Creates a new ResponseManager instance.
*
* @param ResourceManagerInterface $resourceManager Manager for adding resources to responses
* @param StorageManagerInterface $storageManager Manager for accessing stored notifications
* @param EventDispatcherInterface $eventDispatcher Dispatcher for notification events
*/
public function __construct(
private readonly ResourceManagerInterface $resourceManager,
private readonly StorageManagerInterface $storageManager,
@@ -54,15 +36,13 @@ final class ResponseManager implements ResponseManagerInterface
}
/**
* {@inheritdoc}
* @param array<string, mixed> $criteria
* @param array<string, mixed> $context
*
* This implementation follows a multi-step process:
* 1. Filters notifications from storage based on criteria
* 2. Removes rendered notifications from storage
* 3. Dispatches a PresentationEvent to allow listeners to modify notifications
* 4. Creates a Response object with notifications and context
* 5. Uses the appropriate presenter to render the response
* 6. Dispatches a ResponseEvent to allow final modifications
* @phpstan-return ($presenter is 'html' ? string :
* ($presenter is 'array' ? ArrayPresenterType :
* ($presenter is 'json' ? ArrayPresenterType :
* mixed)))
*/
public function render(string $presenter = 'html', array $criteria = [], array $context = []): mixed
{
@@ -87,16 +67,7 @@ final class ResponseManager implements ResponseManagerInterface
}
/**
* Creates a presenter instance for the specified format.
*
* This method retrieves a presenter by alias from the registry and instantiates
* it if it's a factory closure.
*
* @param string $alias The presenter alias
*
* @return PresenterInterface The presenter instance
*
* @throws PresenterNotFoundException If no presenter is registered with the given alias
* @throws PresenterNotFoundException
*/
private function createPresenter(string $alias): PresenterInterface
{
@@ -110,11 +81,6 @@ final class ResponseManager implements ResponseManagerInterface
}
/**
* Creates a Response object with notifications and context.
*
* This method creates a Response object and populates it with resources
* (scripts, styles, options) using the ResourceManager.
*
* @param Envelope[] $envelopes
* @param array<string, mixed> $context
*/
@@ -125,17 +91,6 @@ final class ResponseManager implements ResponseManagerInterface
return $this->resourceManager->populateResponse($response);
}
/**
* Uses a presenter to render a response.
*
* This method selects the appropriate presenter based on the requested format
* and uses it to render the response.
*
* @param Response $response The response to render
* @param string $presenter The presenter format to use
*
* @return mixed The rendered result
*/
private function presentResponse(Response $response, string $presenter): mixed
{
$presenter = $this->createPresenter($presenter);
+1 -26
View File
@@ -4,44 +4,19 @@ declare(strict_types=1);
namespace Flasher\Prime\Stamp;
/**
* HtmlStamp - Contains prerendered HTML content for a notification.
*
* This stamp stores HTML content that should be rendered directly in the page
* instead of being processed by the JavaScript notification library. It's useful
* for complex notifications that require custom markup or server-rendered content.
*
* Design patterns:
* - Value Object: Immutable object representing a specific concept
* - Memento: Captures and externalizes an object's internal state
*/
final readonly class HtmlStamp implements StampInterface, PresentableStampInterface
{
/**
* Creates a new HtmlStamp instance.
*
* @param string $html The prerendered HTML content
*/
public function __construct(private string $html)
{
}
/**
* Gets the HTML content.
*
* @return string The prerendered HTML content
*/
public function getHtml(): string
{
return $this->html;
}
/**
* Converts the stamp to an array representation.
*
* This method implements the serialization logic required by PresentableStampInterface.
*
* @return array{html: string} The array representation
* @return array{html: string}
*/
public function toArray(): array
{
+1 -26
View File
@@ -4,44 +4,19 @@ declare(strict_types=1);
namespace Flasher\Prime\Stamp;
/**
* PluginStamp - Associates a notification with a specific plugin.
*
* This stamp identifies which plugin should handle a notification. It ensures
* that notifications are rendered with the correct plugin's resources and options.
* This information is also serialized into the notification's metadata.
*
* Design patterns:
* - Value Object: Immutable object representing a specific concept
* - Strategy Identifier: Identifies which rendering strategy to use
*/
final readonly class PluginStamp implements PresentableStampInterface, StampInterface
{
/**
* Creates a new PluginStamp instance.
*
* @param string $plugin The plugin alias (e.g., 'toastr', 'sweetalert')
*/
public function __construct(private string $plugin)
{
}
/**
* Gets the plugin alias.
*
* @return string The plugin alias
*/
public function getPlugin(): string
{
return $this->plugin;
}
/**
* Converts the stamp to an array representation.
*
* This method implements the serialization logic required by PresentableStampInterface.
*
* @return array{plugin: string} The array representation
* @return array{plugin: string}
*/
public function toArray(): array
{
+1 -22
View File
@@ -4,26 +4,10 @@ declare(strict_types=1);
namespace Flasher\Prime\Stamp;
/**
* PresenterStamp - Controls which presenters can render a notification.
*
* This stamp restricts which presenters can render a notification by specifying
* a regex pattern. Only presenters whose names match the pattern will be allowed
* to render the notification. This is useful for creating notifications that
* are only meant for specific presentation formats.
*
* Design patterns:
* - Value Object: Immutable object representing a specific concept
* - Filter: Restricts which presenters can render a notification
*/
final readonly class PresenterStamp implements StampInterface
{
/**
* Creates a new PresenterStamp instance.
*
* @param string $pattern A regex pattern that presenter names must match
*
* @throws \InvalidArgumentException If the provided pattern is not a valid regex
* @throws \InvalidArgumentException
*/
public function __construct(private string $pattern)
{
@@ -32,11 +16,6 @@ final readonly class PresenterStamp implements StampInterface
}
}
/**
* Gets the pattern value.
*
* @return string The regex pattern
*/
public function getPattern(): string
{
return $this->pattern;
+1 -25
View File
@@ -4,34 +4,10 @@ declare(strict_types=1);
namespace Flasher\Prime\Template;
/**
* PHPTemplateEngine - Simple PHP-based template rendering engine.
*
* This implementation provides a straightforward template rendering mechanism using
* native PHP as the template language. It works by including PHP files and extracting
* context variables into the template's scope.
*
* Design patterns:
* - Strategy: Implements a specific template rendering strategy
* - Output Capture: Uses output buffering to capture rendered content
*
* Security considerations:
* - Context variables are extracted with EXTR_SKIP flag to prevent variable overwriting
* - Template files must exist and be readable
*/
final class PHPTemplateEngine implements TemplateEngineInterface
{
/**
* {@inheritdoc}
*
* This implementation:
* 1. Verifies the template file exists and is readable
* 2. Starts output buffering
* 3. Extracts context variables into the current scope
* 4. Includes the template file
* 5. Captures and returns the output
*
* @throws \InvalidArgumentException If the template file doesn't exist or isn't readable
* @throws \InvalidArgumentException
*/
public function render(string $name, array $context = []): string
{
+2 -52
View File
@@ -8,27 +8,10 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use Flasher\Prime\Notification\NotificationInterface;
use PHPUnit\Framework\Constraint\Constraint;
/**
* Notification - PHPUnit constraint for asserting complete notification properties.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification matching a combination of type, message, title, and options.
* It allows for comprehensive notification assertions by checking multiple properties.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
* - Specification: Represents a specification that notifications can satisfy
*/
final class Notification extends Constraint
{
/**
* Creates a new Notification constraint.
*
* @param string $expectedType Expected notification type (e.g., 'success', 'error')
* @param string|null $expectedMessage Expected message content (null to ignore)
* @param array<string, mixed> $expectedOptions Expected options as an associative array
* @param string|null $expectedTitle Expected title content (null to ignore)
* @param array<string, mixed> $expectedOptions
*/
public function __construct(
private readonly string $expectedType,
@@ -38,11 +21,6 @@ final class Notification extends Constraint
) {
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
$details = [
@@ -64,14 +42,6 @@ final class Notification extends Constraint
return 'contains a notification with '.implode(', ', $details);
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* matching all the expected properties.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a matching notification is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -87,19 +57,6 @@ final class Notification extends Constraint
return false;
}
/**
* Checks if a specific notification matches all expected properties.
*
* A notification matches if:
* - Its type equals the expected type AND
* - Its message equals the expected message (if provided) AND
* - Its title equals the expected title (if provided) AND
* - Its options contain all expected option key-value pairs (if provided)
*
* @param NotificationInterface $notification The notification to check
*
* @return bool True if the notification matches all criteria
*/
private function isNotificationMatching(NotificationInterface $notification): bool
{
return $notification->getType() === $this->expectedType
@@ -109,14 +66,7 @@ final class Notification extends Constraint
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method lists all actual notifications with their properties,
* making test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
* @param NotificationEvents $other
*/
protected function failureDescription(mixed $other): string
{
@@ -7,45 +7,17 @@ namespace Flasher\Prime\Test\Constraint;
use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationCount - PHPUnit constraint for asserting notification count.
*
* This constraint verifies that a NotificationEvents collection contains
* exactly the expected number of notifications. It's used by the FlasherAssert
* class for count-related assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
*/
final class NotificationCount extends Constraint
{
/**
* Creates a new NotificationCount constraint.
*
* @param int $expectedValue The expected number of notifications
*/
public function __construct(private readonly int $expectedValue)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
return \sprintf('matches the expected notification count of %d.', $this->expectedValue);
}
/**
* Evaluates if the given NotificationEvents object matches the expected notification count.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if the actual count matches the expected count
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -56,11 +28,7 @@ final class NotificationCount extends Constraint
}
/**
* Provides a detailed failure description when the constraint fails.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
* @param NotificationEvents $other
*/
protected function failureDescription(mixed $other): string
{
@@ -69,13 +37,6 @@ final class NotificationCount extends Constraint
return \sprintf('Expected the notification count to be %d, but got %d instead.', $this->expectedValue, $actualCount);
}
/**
* Counts the notifications in the given NotificationEvents object.
*
* @param NotificationEvents $events The NotificationEvents instance
*
* @return int The number of notifications
*/
private function countNotifications(NotificationEvents $events): int
{
return \count($events->getEnvelopes());
@@ -8,47 +8,17 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use Flasher\Prime\Notification\NotificationInterface;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationMessage - PHPUnit constraint for asserting notification message presence.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification with a message containing the expected text. It's used by the
* FlasherAssert class for message-related assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
* - String Matcher: Performs string matching on notification messages
*/
final class NotificationMessage extends Constraint
{
/**
* Creates a new NotificationMessage constraint.
*
* @param string $expectedMessage The expected message text (or substring)
*/
public function __construct(private readonly string $expectedMessage)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
return \sprintf('contains a notification with message "%s"', $this->expectedMessage);
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* with a message containing the expected text.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a notification with the expected message text is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -65,14 +35,7 @@ final class NotificationMessage extends Constraint
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method lists all actual notification messages found, making
* test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
* @param NotificationEvents $other
*/
protected function failureDescription(mixed $other): string
{
@@ -8,35 +8,12 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use Flasher\Prime\Notification\NotificationInterface;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationOption - PHPUnit constraint for asserting a specific notification option.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification with a specific option key and optionally a specific value. It's
* used by the FlasherAssert class for option-specific assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
* - Key-Value Matcher: Performs key-value matching on notification options
*/
final class NotificationOption extends Constraint
{
/**
* Creates a new NotificationOption constraint.
*
* @param string $expectedKey The expected option key
* @param mixed $expectedValue The expected option value (null to check only key existence)
*/
public function __construct(private readonly string $expectedKey, private readonly mixed $expectedValue = null)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
$description = \sprintf('contains a notification with an option "%s"', $this->expectedKey);
@@ -48,14 +25,6 @@ final class NotificationOption extends Constraint
return $description;
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* with the expected option key and value.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a notification with the expected option is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -71,13 +40,6 @@ final class NotificationOption extends Constraint
return false;
}
/**
* Checks if a specific notification has the expected option key and value.
*
* @param NotificationInterface $notification The notification to check
*
* @return bool True if the notification has the expected option
*/
private function isOptionMatching(NotificationInterface $notification): bool
{
$options = $notification->getOptions();
@@ -85,21 +47,10 @@ final class NotificationOption extends Constraint
return isset($options[$this->expectedKey]) && $options[$this->expectedKey] === $this->expectedValue;
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method lists all actual notification options found, making
* test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
*/
protected function failureDescription(mixed $other): string
{
$actualOptions = [];
// @phpstan-ignore-next-line
if ($other instanceof NotificationEvents) {
foreach ($other->getEnvelopes() as $notification) {
$actualOptions[] = json_encode($notification->getOptions());
@@ -7,47 +7,20 @@ namespace Flasher\Prime\Test\Constraint;
use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationOptions - PHPUnit constraint for asserting notification options.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification with options matching all the expected key-value pairs. It's used
* by the FlasherAssert class for options-related assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
* - Key-Value Matcher: Performs associative array matching on notification options
*/
final class NotificationOptions extends Constraint
{
/**
* Creates a new NotificationOptions constraint.
*
* @param array<string, mixed> $expectedOptions The expected option key-value pairs
* @param array<string, mixed> $expectedOptions
*/
public function __construct(private readonly array $expectedOptions)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
return 'contains a notification with options matching '.json_encode($this->expectedOptions, \JSON_PRETTY_PRINT);
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* with options containing all the expected key-value pairs.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a notification with matching options is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -63,21 +36,10 @@ final class NotificationOptions extends Constraint
return false;
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method lists all actual notification options found, making
* test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
*/
protected function failureDescription(mixed $other): string
{
$actualOptions = [];
// @phpstan-ignore-next-line
if ($other instanceof NotificationEvents) {
foreach ($other->getEnvelopes() as $notification) {
$actualOptions[] = json_encode($notification->getOptions());
@@ -8,47 +8,17 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use Flasher\Prime\Notification\NotificationInterface;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationTitle - PHPUnit constraint for asserting notification title presence.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification with a title containing the expected text. It's used by the
* FlasherAssert class for title-related assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
* - String Matcher: Performs string matching on notification titles
*/
final class NotificationTitle extends Constraint
{
/**
* Creates a new NotificationTitle constraint.
*
* @param string $expectedTitle The expected title text (or substring)
*/
public function __construct(private readonly string $expectedTitle)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
return \sprintf('contains a notification with a title containing "%s"', $this->expectedTitle);
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* with a title containing the expected text.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a notification with the expected title text is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -64,19 +34,8 @@ final class NotificationTitle extends Constraint
return false;
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method lists all actual notification titles found, making
* test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
*/
protected function failureDescription(mixed $other): string
{
// @phpstan-ignore-next-line
if (!$other instanceof NotificationEvents) {
return 'Expected an instance of NotificationEvents but received a different type.';
}
+1 -37
View File
@@ -8,46 +8,17 @@ use Flasher\Prime\EventDispatcher\Event\NotificationEvents;
use Flasher\Prime\Notification\NotificationInterface;
use PHPUnit\Framework\Constraint\Constraint;
/**
* NotificationType - PHPUnit constraint for asserting notification type presence.
*
* This constraint verifies that a NotificationEvents collection contains at least
* one notification of the specified type. It's used by the FlasherAssert class
* for type-related assertions.
*
* Design patterns:
* - Composite: Part of PHPUnit's constraint composition system
* - Strategy: Implements a specific assertion strategy
*/
final class NotificationType extends Constraint
{
/**
* Creates a new NotificationType constraint.
*
* @param string $expectedType The expected notification type (e.g., 'success', 'error')
*/
public function __construct(private readonly string $expectedType)
{
}
/**
* Returns a string representation of the constraint.
*
* @return string The constraint description
*/
public function toString(): string
{
return \sprintf('contains a notification of type "%s".', $this->expectedType);
}
/**
* Evaluates if the given NotificationEvents object contains at least one notification
* of the expected type.
*
* @param NotificationEvents|mixed $other An instance of NotificationEvents to evaluate
*
* @return bool True if a notification of the expected type is found
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof NotificationEvents) {
@@ -64,14 +35,7 @@ final class NotificationType extends Constraint
}
/**
* Provides a detailed failure description when the constraint fails.
*
* This method provides context about what types were found instead of
* the expected type, making test failures easier to diagnose.
*
* @param NotificationEvents $other The evaluated NotificationEvents instance
*
* @return string A detailed failure description
* @param NotificationEvents $other
*/
protected function failureDescription(mixed $other): string
{
+1 -19
View File
@@ -4,34 +4,16 @@ declare(strict_types=1);
namespace Flasher\Prime\Translation;
/**
* EchoTranslator - Minimal translator implementation that returns message IDs unchanged.
*
* This implementation simply returns the message identifiers as-is, without
* performing any actual translation. It serves as a fallback translator when
* no real translation service is available.
*
* Design patterns:
* - Null Object: Provides a do-nothing implementation that maintains API compatibility
* - Fallback: Serves as a default implementation when no specific translator is provided
*/
final readonly class EchoTranslator implements TranslatorInterface
{
/**
* {@inheritdoc}
*
* This implementation simply returns the message identifier unchanged.
* @param array<string, mixed> $parameters
*/
public function translate(string $id, array $parameters = [], ?string $locale = null): string
{
return $id;
}
/**
* {@inheritdoc}
*
* This implementation always returns 'en' as the default locale.
*/
public function getLocale(): string
{
return 'en';