Skip to content

v1.9.0

Choose a tag to compare

@MichaelSowah MichaelSowah released this 11 Jun 19:52
· 11 commits to main since this release
958fb28

[1.9.0] - 2026-06-11 — Recipient Domain Policy & Hardening (Framework 1.51)

Added

  • Recipient domain policy. EmailChannel now enforces an optional allow-list /
    block-list on the recipient's domain (security.allowed_domains / security.blocked_domains,
    each a comma-separated string or array) before sending. A disallowed recipient yields a
    non-retryable NotificationResult failure (blocked_domain) and no mail is sent. With
    neither configured, all domains are allowed (prior behavior).
  • A test suite (the extension previously had none). Covers the domain policy
    (allow/block/no-policy/no-recipient), sendNotification() success/failure result mapping via
    Symfony's null transport, TransportFactory DSN/transport building, and EnhancedEmailFormatter
    instantiation. Adds phpunit.xml.

Changed

  • EnhancedEmailFormatter is now the default formatter for EmailChannel (and registered
    in services()), so the enhanced-template path (priority, embedded images, attachments,
    custom headers) is actually reachable. Twig remains opt-in (off by default), so no
    optional twig/twig dependency is required.
  • Config honesty. Removed config blocks that were defined but never enforced anywhere
    (rate_limit.*, monitoring.*, performance.*, security.content_scanning/verify_ssl,
    and the dead events.listeners entry pointing at a removed class). The README's
    corresponding "Performance Monitoring / Rate limiting" and over-broad "Enhanced Security"
    claims were removed/replaced with the real recipient-domain-policy docs; fixed the stale
    "Version 1.0.0 / Glueful 1.22.0" header.

Fixed

  • EnhancedEmailFormatter was uninstantiable. Its constructor called
    parent::__construct($templates, $options), but the base EmailFormatter requires an
    ApplicationContext first -- so constructing it threw a TypeError, and the EmailChannel
    enhanced-template path (priority, embedded images, attachments, custom headers) could never
    run. The constructor now accepts and forwards ApplicationContext, and exposes
    isUsingTwig() (the Twig-enabled flag was previously write-only).
  • PHPStan raised to level 8 and green (was 16 errors, then level 5): fixed the constructor
    type error, removed an unreachable statement in EmailFormatter::renderTemplate() and five
    dead duplicate transport methods in TransportFactory, and added a phpstan.neon.dist that
    ignores the optional twig/twig (suggest) class references. Reaching level 8 also added
    array-shape (array<string, mixed> / array<int, string>) annotations across all public
    signatures and fixed real robustness gaps surfaced by levels 7–8: preg_replace/
    preg_replace_callback returning null on a backtrack-limit failure (now falls back to the
    unmodified input instead of producing a null string), glob() returning false in
    EmailFormatter::registerDefaultTemplates() (now ?: [] so the foreach is always iterable),
    and file_get_contents() returning false before json_decode() in composerVersion().
  • Debug-log gate never fired. process() checked !empty($this->config['debug']), but
    debug is configured as an array (['enabled' => false, ...]), so the check was always
    truthy and the debug log path ran regardless of the flag. Now keys off
    $this->config['debug']['enabled'].
  • getMetrics() bypassed the container. It built a new \Glueful\Database\Connection()
    directly instead of resolving from the application context; now uses
    app($this->context, Connection::class) so the pooled/configured connection is reused.
  • Code-style standard switched from the deprecated Squiz to PSR12 (composer phpcs /
    phpcbf), matching the framework and the other extensions; src/ is clean under PSR-12.