Mailer

Kaly does not provide a MailerInterface and has no Kaly\Mail namespace. This is deliberate.

Kaly defines an abstraction when the framework itself needs a capability, or when it provides a small, reusable primitive with a concrete implementation and a stable contract — see Tasks. It does not introduce interfaces merely to wrap application services or third-party libraries. Email delivery is such a case: Symfony Mailer already provides a complete standalone abstraction and transport ecosystem, so Kaly only documents how to wire it.

Unlike RendererInterface (Kaly needs the concept “render this View”), there is no Kaly-specific concept for email. Re-creating an Email model would mean re-building symfony/mime (multiple addresses, CC/BCC, envelope sender, HTML/text multipart, attachments, inline CID, custom headers) in a worse version. So: use Symfony Mailer directly. It works standalone with Transport::fromDsn(), Mailer and Mime\Email; no FrameworkBundle needed.

Install

composer require symfony/mailer

Wiring

Bind Symfony’s MailerInterface in the container. App loads .env.local before .env, so $_ENV['MAILER_DSN'] is available when defined there:

use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport;

$definitions->set(
    MailerInterface::class,
    static fn() => new Mailer(
        Transport::fromDsn($_ENV['MAILER_DSN'])
    ),
);

A factory only becomes justified once it carries a Kaly policy (PSR-3 logger, extra transport factories, worker reset, shared defaults). One line of Transport::fromDsn() does not need a name.

DSN

# Local development with Buggregator (SMTP catcher)
MAILER_DSN=smtp://buggregator:1025

# Production SMTP
MAILER_DSN=smtp://user:password@mail.example.com:587

# Local sendmail binary, when the machine is configured for it
MAILER_DSN=sendmail://default

# Available, but not recommended: you don't control the underlying
# configuration (sendmail_path, Windows behavior differs)
# MAILER_DSN=native://default

Prefer smtp://, including in development with Buggregator. Use sendmail://default when the host is configured that way. native://default exists but Symfony itself recommends avoiding it when possible.

Sending

Symfony\Component\Mime\Email accepts text() and html() directly:

use Symfony\Component\Mime\Email;

$email = (new Email())
    ->from('no-reply@example.com')
    ->to('user@example.com')
    ->subject('Welcome')
    ->text('Welcome aboard!')
    ->html('<strong>Welcome aboard!</strong>');

$mailer->send($email);

An application may naturally produce that HTML with its RendererInterface if that suits it, but there is no conceptual dependency between the mailer and the renderer: the mailer transports MIME, how the application produces its HTML is the application’s business.

Providers

Third-party providers are covered by Symfony Mailer bridges: install the corresponding bridge and use its DSN. See the Symfony Mailer documentation for the full list of officially supported bridges. Example with one of them:

composer require symfony/resend-mailer
MAILER_DSN=resend+api://KEY@default

Testing

To disable delivery, use the null transport:

MAILER_DSN=null://default

Test the business logic before the Mailer layer: delivery intents, provider fakes or an outbox in automated tests, with only a few integration tests hitting the real adapter.


Kaly PHP framework — MIT

This site uses Just the Docs, a documentation theme for Jekyll.