Merge branch '5.2' into 5.x

* 5.2:
  bug #40427 [Console] Stop accepting ints as InputOption defaults
  Fix fingerprint when context is not serializable
  Fix `ConstraintViolation#getMessageTemplate()` to always return `string`
This commit is contained in:
Alexander M. Turek 2021-03-12 01:42:40 +01:00
commit 7f65a27996
6 changed files with 65 additions and 12 deletions

View File

@ -49,7 +49,7 @@ final class BodyRenderer implements BodyRendererInterface
$previousRenderingKey = $messageContext[__CLASS__] ?? null; $previousRenderingKey = $messageContext[__CLASS__] ?? null;
unset($messageContext[__CLASS__]); unset($messageContext[__CLASS__]);
$currentRenderingKey = md5(serialize([$messageContext, $message->getTextTemplate(), $message->getHtmlTemplate()])); $currentRenderingKey = $this->getFingerPrint($message);
if ($previousRenderingKey === $currentRenderingKey) { if ($previousRenderingKey === $currentRenderingKey) {
return; return;
} }
@ -77,6 +77,23 @@ final class BodyRenderer implements BodyRendererInterface
$message->context($message->getContext() + [__CLASS__ => $currentRenderingKey]); $message->context($message->getContext() + [__CLASS__ => $currentRenderingKey]);
} }
private function getFingerPrint(TemplatedEmail $message): string
{
$messageContext = $message->getContext();
unset($messageContext[__CLASS__]);
$payload = [$messageContext, $message->getTextTemplate(), $message->getHtmlTemplate()];
try {
$serialized = serialize($payload);
} catch (\Exception $e) {
// Serialization of 'Closure' is not allowed
// Happens when context contain a closure, in that case, we assume that context always change.
$serialized = random_bytes(8);
}
return md5($serialized);
}
private function convertHtmlToText(string $html): string private function convertHtmlToText(string $html): string
{ {
if (null !== $this->converter) { if (null !== $this->converter) {

View File

@ -100,6 +100,27 @@ HTML;
$this->assertEquals('reset', $email->getTextBody()); $this->assertEquals('reset', $email->getTextBody());
} }
public function testRenderedOnceUnserializableContext()
{
$twig = new Environment(new ArrayLoader([
'text' => 'Text',
]));
$renderer = new BodyRenderer($twig);
$email = (new TemplatedEmail())
->to('fabien@symfony.com')
->from('helene@symfony.com')
;
$email->textTemplate('text');
$email->context([
'foo' => static function () {
return 'bar';
},
]);
$renderer->render($email);
$this->assertEquals('Text', $email->getTextBody());
}
private function prepareEmail(?string $text, ?string $html, array $context = []): TemplatedEmail private function prepareEmail(?string $text, ?string $html, array $context = []): TemplatedEmail
{ {
$twig = new Environment(new ArrayLoader([ $twig = new Environment(new ArrayLoader([

View File

@ -429,9 +429,9 @@ class Command
/** /**
* Adds an option. * Adds an option.
* *
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param int|null $mode The option mode: One of the InputOption::VALUE_* constants * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
* @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE) * @param string|string[]|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
* *
* @throws InvalidArgumentException If option mode is invalid or incompatible * @throws InvalidArgumentException If option mode is invalid or incompatible
* *

View File

@ -34,11 +34,11 @@ class InputOption
private $description; private $description;
/** /**
* @param string $name The option name * @param string $name The option name
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param int|null $mode The option mode: One of the VALUE_* constants * @param int|null $mode The option mode: One of the VALUE_* constants
* @param string $description A description text * @param string $description A description text
* @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE) * @param string|string[]|bool|null $default The default value (must be null for self::VALUE_NONE)
* *
* @throws InvalidArgumentException If option mode is invalid or incompatible * @throws InvalidArgumentException If option mode is invalid or incompatible
*/ */
@ -158,7 +158,7 @@ class InputOption
/** /**
* Sets the default value. * Sets the default value.
* *
* @param string|string[]|int|bool|null $default The default value * @param string|string[]|bool|null $default The default value
* *
* @throws LogicException When incorrect default value is given * @throws LogicException When incorrect default value is given
*/ */
@ -185,7 +185,7 @@ class InputOption
/** /**
* Returns the default value. * Returns the default value.
* *
* @return string|string[]|int|bool|null The default value * @return string|string[]|bool|null The default value
*/ */
public function getDefault() public function getDefault()
{ {

View File

@ -100,7 +100,7 @@ class ConstraintViolation implements ConstraintViolationInterface
*/ */
public function getMessageTemplate() public function getMessageTemplate()
{ {
return $this->messageTemplate; return (string) $this->messageTemplate;
} }
/** /**

View File

@ -171,4 +171,19 @@ EOF;
))->getPropertyPath() ))->getPropertyPath()
); );
} }
public function testRetrievedMessageTemplateIsAStringEvenIfNotSet()
{
self::assertSame(
'',
(new ConstraintViolation(
'irrelevant',
null,
[],
'irrelevant',
'irrelevant',
null
))->getMessageTemplate()
);
}
} }