-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Mime] [Email] Add encoders to text and html parts #54196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 7.4
Are you sure you want to change the base?
Conversation
Hi @kumulo, and thank you for your PR! 😄 I created a controller as in the example at https://symfony.com/doc/current/mailer.html#creating-sending-messages: class MailerController extends AbstractController
{
#[Route('/text_html', name: 'text_html')]
public function textHtml(MailerInterface $mailer): Response
{
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
->subject('Time for Symfony Mailer!')
->text('Sending emails is fun again!', 'utf-8', 'base64') // <-- encoding option !
->html('<p>See Twig integration for better HTML integration!</p>', 'utf-8', 'base64'); // <-- encoding option !
$mailer->send($email);
return new Response('text_html');
}
} It's good! In the Symfony Profiler, I can see the following raw message (with base64 encoding):
But, if I want to use class MailerController extends AbstractController
{
#[Route('/templated_email', name: 'templated_email')]
public function templatedEmail(MailerInterface $mailer): Response
{
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
->htmlTemplate('emails/signup.html.twig') // <-- no encoding option
->locale('de')
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
]);
$mailer->send($email);
return new Response('templated_email');
}
} In this case, rendering is performed in this following method //src/Symfony/Bridge/Twig/Mime/BodyRenderer.php
...
if ($template = $message->getTextTemplate()) {
$message->text($this->twig->render($template, $vars)); // <-- no encoding option
}
if ($template = $message->getHtmlTemplate()) {
$message->html($this->twig->render($template, $vars)); // <-- no encoding option
}
... Another little detail in // src/Symfony/Component/Mime/Email.php
...
public function getHtmlEncoding(): ?string
{
return $this->htmlEncoding;
}
... I haven't yet found all the places where the base64 encoding option would be needed. What are your thoughts on this? |
Hi @jprivet-dev , thanks for you feedback !
Body rendering is very far from TemplatedEmail creation, so I've made some properties setters, you will be able to do this : class MailerController extends AbstractController
{
#[Route('/templated_email', name: 'templated_email')]
public function templatedEmail(MailerInterface $mailer): Response
{
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
->htmlTemplate('emails/signup.html.twig')
->htmlEncoding('base64') // <= Encoding option :)
->locale('de')
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
]);
$mailer->send($email);
return new Response('templated_email');
}
}
Properties accessors are there now :) |
I'm not sure to understand the Where is this done / by "who" ? |
Hi @smnandre, Without encoding (by default), raw message is :
With encoding it could be :
Those two parts are TextPart objects, TextPart allows you to set the encoding of the part : class TextPart extends AbstractPart
{
public function __construct($body, ?string $charset = 'utf-8', string $subtype = 'plain', ?string $encoding = null)
{
//...
}
} For Email object, those parts are constructed by |
Stop all! It's all so unnecessary! 😄 After some investigation and a lot of help from @smnandre, it turns out that the base64 encoding option is already available in private function chooseEncoding(): string
{
if (null === $this->charset) {
return 'base64';
}
return 'quoted-printable';
} And that's where the problem lies: you can't get a null charset using class Email extends Message
{
...
private ?string $textCharset = null; // <-- textCharset can be null
private ?string $htmlCharset = null; // <-- htmlCharset can be null
...
public function text($body, string $charset = 'utf-8'): static
{
...
$this->textCharset = $charset; // <-- Impossible to set textCharset to null: default value is 'utf-8'
...
}
public function html($body, string $charset = 'utf-8'): static
{
...
$this->htmlCharset = $charset; // <-- Impossible to set htmlCharset to null: default value is 'utf-8'
...
}
} In this context, the following code: class MailerController extends AbstractController
{
#[Route('/']
public function textHtml(MailerInterface $mailer): Response
{
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
->subject('Time for Symfony Mailer!')
->text('Sending emails is fun again!', null) // <-- Set charset to null to access base64 encoding
->html('<p>See Twig integration for better HTML integration!</p>', null); // <-- Set charset to null to access base64 encoding
$mailer->send($email);
return new Response('text_html');
}
} Returns errors :
If the following changes are made: class Email extends Message
{
public function text($body, ?string $charset = 'utf-8'): static // <-- add "?"
{
...
}
public function html($body, ?string $charset = 'utf-8'): static // <-- add "?"
{
...
}
} Then the code in the controller will be able to run in base64 encoding 🥳 :
However, the problem remains the same with class MailerController extends AbstractController
{
#[Route('/')]
public function templatedEmail(MailerInterface $mailer): Response
{
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
->htmlTemplate('emails/signup.html.twig') // <-- no base64 encoding option
->locale('de')
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
]);
$mailer->send($email);
return new Response('templated_email');
}
} Solution under investigation... |
That was my first investigation but, IMO, charset and encoding are 2 different things and encoded content can be iso or utf. |
Does the charset represent the "current" charset ? Here, So how would you do if someone wanted to pass some already base64-encoded text (genuine question, not saying this is a every-day-scenario either) ? |
Rapid test with accentuated characters: #[Route('/')]
public function textHtml(MailerInterface $mailer): Response
{
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
->subject('Time for Symfony Mailer!')
->text('éàèéàèéàèéàèéàèéàè', null)
->html('<p>éàèéàèéàèéàèéàèéàè</p>', null);
$mailer->send($email);
return new Response('text_html');
} Results:
Screenshot: Everything looks good 🤔
|
For me, charset can depends of the part : Or maybe I misunderstood something in the RFC. @jprivet-dev in your last exemple, is argument $charset can really be null ? public function text($body, string $charset = 'utf-8'): static Maybe signature has to be update as : public function text($body, ?string $charset = 'utf-8'): static |
Perhaps this is a subject that should be explored in greater depth to get closer to the RFC?
Yes, that's exactly what I was suggesting and testing in my previous message 😄 : class Email extends Message
{
public function text($body, ?string $charset = 'utf-8'): static // <-- add "?"
{
...
}
public function html($body, ?string $charset = 'utf-8'): static // <-- add "?"
{
...
}
} However, the problem remains the same with TemplatedEmail(): how to indicate the use of base64 encoding? |
I have add |
Add encoders to text and html parts of Email object.
Each part of the sent mail will be encoded in Base 64 with defined charset in
Content-Type
header andbase64
inContent-Transfer-Encoding
header.