Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Emogrifier Laravel Package

pelago/emogrifier

Emogrifier converts CSS styles into inline style attributes in your HTML, ensuring reliable display in email clients and mobile readers that lack stylesheet support. Useful for Outlook 2007 and Gmail quirks. Install via Composer: pelago/emogrifier.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Assessment reviewed against upstream release v8.3.0 (previous baseline: 8.2.x). Changes below are marked [v8.3.0] where they alter prior conclusions.

Architecture Fit

  • Email & Transactional Messaging Systems: Ideal for Laravel applications handling transactional emails (password resets, notifications) or marketing emails where CSS support is inconsistent (Outlook, Gmail, mobile clients).
  • Hybrid HTML/CSS Workflows: Fits into Laravel's Blade templating for dynamic email generation (Mailable classes / Mailables).
  • Modularity: Lightweight and effectively dependency-free at runtime. [v8.3.0] The thecodingmachine/safe dependency has been removed (still dev-only for the upstream test suite), and sabberworm/php-css-parser is now pinned to >= 9.5.0 — so the runtime dependency graph is smaller but not zero.
  • Platform Currency: [v8.3.0] PHP 8.6 is now supported; PHP 7.3 is formally deprecated. This extends the usable life of the package on modern Laravel runtimes and signals an actively maintained project.
  • Extensibility: Post-processing hooks (HtmlPruner, CssToAttributeConverter) still align with Laravel service-layer patterns. Unchanged.

Integration Feasibility

  • Laravel Service Provider: Bootstrap as a service provider centralizing email processing (e.g., EmailServiceProvider exposing a CssInliner binding/facade). Unchanged.
  • Queueable Jobs: Integrates with Laravel's queue system for async rendering. [v8.3.0] A cache-clearing fix (see Operational Impact) materially improves behaviour in long-lived queue workers — this was previously a slow memory-growth risk in Horizon/worker fleets.
  • Blade Directives: Custom directives (e.g., @emogrify) to inline CSS during template rendering. Unchanged.
  • Error Semantics Now Require Explicit Handling: [v8.3.0] Two behaviour changes must be designed for, not bolted on later:
    1. Regex blowout → trigger_error() in production instead of throwing, returning the original string unmodified; exceptions only when the debug option is set.
    2. render() internal DOMDocument error → now throws instead of returning an empty string. Net effect: the integration wrapper needs a try/catch and an error-handler/'debug' config strategy. This is a modest but real added integration task.

Technical Risk

  • Silent Degradation in Production (new, highest-priority risk): [v8.3.0] With regex blowouts no longer throwing in production, a malformed or pathological template yields un-inlined HTML rather than a loud failure. Emails will still send — they'll just render badly in Outlook/Gmail. Risk mitigated by: (a) enabling the debug option in non-production environments, (b) installing a Laravel error handler / set_error_handler that routes trigger_error() into your log channel and alerts, (c) canary template rendering in CI.
  • New Exception Path on Render: [v8.3.0] render() throwing on internal DOMDocument failure is a safer default (previously an empty string could result in a blank email being delivered), but it converts a "silent blank" into a hard failure. Any caller assuming string return needs a fallback (e.g., send raw HTML, or fail the job with retry).
  • CSS Selector Limitations: Partial support for modern CSS (no :hover, @media beyond all|screen|print). Unchanged; mitigate via allowlisting.
  • Performance Overhead: Inlining on large HTML may add latency. Benchmark with real-world templates (~50KB HTML).
  • Deeply Nested HTML: [v8.3.0] Fixed — very deeply-nested HTML no longer fails. Reduces a previously undocumented edge case for table-heavy email layouts.
  • Memory Growth Across Invocations: [v8.3.0] Fixed — caches are cleared on each invocation, addressing memory build-up in long-running processes.
  • Dependency Bump Risk: [v8.3.0] sabberworm/php-css-parser >= 9.5.0 may conflict with other packages in a large Laravel app (e.g., PDF/HTML rendering libs). Needs a Composer resolution check before upgrade.
  • DOM Manipulation: Still DOMDocument-based and memory-intensive for malformed HTML; validate input.

Key Questions

  1. Use Case Priority: Transactional (low volume, strict formatting) vs. marketing (high volume, complex templates)? Dynamic content requiring runtime selector generation?
  2. Failure Visibility (revised): [v8.3.0] Given production regex blowouts now degrade silently, what is our detection path for un-inlined CSS? Do we have a trigger_error handler wired to alerting, and is 'debug' => true set in staging?
  3. Exception Policy (new): When render() throws, what is the desired user-facing behaviour — retry the job, fall back to raw HTML, or fail loudly? This must be a product decision, not a framework default.
  4. Performance SLAs: Max acceptable latency for email rendering? Test CssInliner::fromHtml() against production-scale templates.
  5. Fallback Strategy: How is unsupported CSS (@font-face) handled — graceful degradation via addExcludedCssSelector()?
  6. Composer Resolution (new): Does sabberworm/php-css-parser >= 9.5.0 resolve cleanly alongside our existing dependency tree?
  7. Testing: Pre-built templates to validate against (cross-client tools: Litmus / Email on Acid)?
  8. Maintenance: Long-term support status — MIT license plus active maintenance; PHP 7.3 deprecation suggests a v9.0 breaking release is likely, so pin and plan.

Integration Approach

Stack Fit

  • Laravel Ecosystem:
    • Mailables: Wrap CssInliner in a dedicated renderer rather than inlining logic inside every Mailable::build() — keeps the blast radius of the v8.3.0 exception change to one class.
    • Queues / Horizon: Offload processing to queues; the [v8.3.0] per-invocation cache clearing makes this safe for long-lived workers.
    • Logging / Error Handling: [v8.3.0] Add a set_error_handler (or Laravel's Log:: + report() bridge) to capture trigger_error() emissions from regex blowouts.
  • Frontend Stack:
    • Blade: Custom directives (e.g., @emogrify) to inline CSS during compilation.
    • Tailwind/PostCSS: Pre-process to avoid unsupported selectors (purge unused classes).
  • Testing:
    • Pest/PHPUnit: Mock CssInliner; add explicit tests for (a) the thrown-on-DOMDocument-error path and (b) the "returns original string unmodified" degradation path [v8.3.0].
    • BrowserStack / Litmus / Email on Acid: Cross-client rendering checks.

Migration Path

  1. Phase 0: Composer Validation (new)
    • Confirm sabberworm/php-css-parser >= 9.5.0 resolves with the existing lockfile. Use a throwaway branch and composer why-not before committing to the upgrade.
  2. Phase 1: Proof of Concept
    • Replace a single email template (password reset) with CssInliner; diff output against manual inlining (Premailer).
    • Explicitly exercise the bad-input path to confirm observed behaviour matches the new production/default semantics.
  3. Phase 2: Service Layer
    • Create an EmailRenderer service that isolates the new exception and degradation semantics:
      class EmailRenderer
      {
          public function render(Mailable $mailable): string
          {
              $html = $mailable->render();
              try {
                  return CssInliner::fromHtml($html)
                      ->inlineCss($mailable->css())
                      ->render();
              } catch (\Throwable $e) {
                  report($e);               // surfaces the new DOMDocument exception
                  return $html;             // fall back to un-inlined HTML
              }
          }
      }
      
    • Register as a Laravel service provider; set the 'debug' option from app()->environment().
  4. Phase 3: Queue Integration
    • Wrap rendering in RenderEmailJob with retries + exponential backoff; decide whether a thrown render error retries or falls back.
  5. Phase 4: Optimization
    • Add HtmlPruner; cache static templates via Cache::remember().

Compatibility

  • PHP: [v8.3.0] Supports PHP 8.6; PHP 7.3 is deprecated (plan removal in the next major). Verify our runtime matrix — if anything still runs 7.3, this is a blocker for future upgrades.
  • Laravel Versions: Compatible with LTS lines (8.x–11.x); validate on our current LTS with PHP 8.1+.
  • Dependencies: [v8.3.0] sabberworm/php-css-parser >= 9.5.0 required; thecodingmachine/safe removed (smaller footprint).
  • Behavioural Compatibility (new): The regex-blowout change restores the 8.1.0 behaviour — if any code was written against 8.2.x's throwing behaviour, it will silently stop throwing. Audit for catch blocks that now never fire.
  • CSS Frameworks: Tailwind (@apply sparingly); Bootstrap utility classes.
  • Dynamic Content: Avoid user-generated class names; use data-* attributes.

Sequencing

  1. Composer & Compatibility Audit (new) — resolve php-css-parser and confirm PHP versions.
  2. Template Design — inline-first CSS, semantic/tabular HTML.
  3. Development — static templates first, then dynamic; implement EmailRenderer with fallback.
  4. Deployment — roll out to non-critical emails (newsletters) first; keep 'debug' => true in staging to surface blowouts as exceptions.
  5. Monitoring — track rendering time, exception counts on render(), and trigger_error emissions from the inliner.

Operational Impact

Maintenance

  • Dependencies: [v8.3.0] Runtime graph shrinks (safe removed) but the parser floor rises to >= 9.5.0. Re-run composer update --dry-run in CI to catch conflicts early.
  • Upgrade Cadence: PHP 7.3 deprecation is a leading indicator of a v9.0 breaking release — pin to ^8.3 and schedule review.
  • Behavioural Regression Testing (new): Add fixtures for (a) pathological regex input, (b) malformed HTML triggering DOMDocument errors, (c) deeply nested tables, (d) high-invocation loops (memory). These map 1:1 to v8.3.0 changes and are cheap to write.
  • Debugging: Log failures to Laravel's log channel via the error handler; use ->getDomDocument()->saveHTML() to inspect intermediate HTML.
  • Documentation: Update internal docs for allowed/excluded selectors, 'debug' option semantics per environment, and production degradation behaviour (returns original string).

Support

  • Common Issues:
    • Un-inlined email in production (new) — regex blowout degrading silently. Solution: error-handler alerting; reproduce in staging with 'debug' => true.
    • Render exception (new) — DOMDocument internal error now throws. Solution: try/catch with raw-HTML fallback.
    • Broken layouts — unsupported selectors (:nth-child). Solution: manual overrides / addExcludedCssSelector().
    • Slow rendering — large HTML or complex CSS. Solution: flatten nesting, prune.
  • Support Channels: GitHub Issues (package bugs); internal Slack/Teams (integration); Litmus / Email on Acid (rendering).

Scaling

  • Horizontal Scaling: Distribute rendering across queue workers; cache static template output.
  • Long-Running Workers: [v8.3.0] Cache-clearing per invocation removes a memory-growth pathology — worker fleets need less frequent recycling, a direct operational cost win.
  • Vertical Scaling: Memory limits for large templates; enable OpCache.
  • Database Impact: None — purely in-memory; optional Redis/S3 for rendered-email caching.
  • Deep Templates: [v8.3.0] Very deeply-nested HTML now supported — reduces a layout-complexity ceiling for table-based designs.

Failure Modes

Failure Scenario Impact Mitigation
Malformed HTML input DOMDocument internal error [v8.3.0] render() now throws (previously returned ""). Catch and fall back to raw HTML; validate input.
Regex blowout (pathological CSS/HTML) Silent — un-inlined HTML delivered (changed) [v8.3.0] Production: trigger_error() + returns original string. Set 'debug' => true below production and route errors to alerting.
Unsupported CSS selectors Partial rendering Log warnings; addExcludedCssSelector().
High memory usage Worker timeouts [v8.3.0] Per-invocation cache clearing reduces build-up; still bound template size and set memory_limit.
Very deeply nested HTML Render failure [v8.3.0] Fixed — no longer a failure mode.
Queue job failures Delayed emails Retries with exponential backoff; distinguish transient vs. render exceptions.
CSS variable evaluation failures Un-resolved values in output Pre-resolve variables at build time; exclude unsupported declarations.
Dependency conflict on php-css-parser Deploy/upgrade blocked [v8.3.0] Validate resolution pre-upgrade in CI.
PHP 7.3 runtime Unsupported / deprecated [v8.3.0] Confirm all runtimes on PHP 8.1+; plan for v9.0.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log