From 28178108d3ce3df0eedb4a5c38975da33be35186 Mon Sep 17 00:00:00 2001 From: Michel Hunziker Date: Fri, 7 Feb 2020 14:16:26 +0100 Subject: [PATCH 1/5] [Mailer] Do not ping the SMTP server before sending every message --- .../Transport/Smtp/SmtpTransportTest.php | 95 +++++++++++++++++++ .../Mailer/Transport/Smtp/SmtpTransport.php | 32 ++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php index 659062d947..7ec8313612 100644 --- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php +++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php @@ -12,8 +12,12 @@ namespace Symfony\Component\Mailer\Tests\Transport\Smtp; use PHPUnit\Framework\TestCase; +use Symfony\Component\Mailer\Envelope; use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport; +use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream; use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\RawMessage; class SmtpTransportTest extends TestCase { @@ -25,4 +29,95 @@ class SmtpTransportTest extends TestCase $t = new SmtpTransport((new SocketStream())->setHost('127.0.0.1')->setPort(2525)->disableTls()); $this->assertEquals('smtp://127.0.0.1:2525', (string) $t); } + + public function testSendDoesNotPingBelowThreshold(): void + { + $stream = new DummyStream(); + $envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]); + + $transport = new SmtpTransport($stream); + $transport->send(new RawMessage('Message 1'), $envelope); + $transport->send(new RawMessage('Message 2'), $envelope); + $transport->send(new RawMessage('Message 3'), $envelope); + + $this->assertNotContains("NOOP\r\n", $stream->getCommands()); + } + + public function testSendDoesPingAboveThreshold(): void + { + $stream = new DummyStream(); + $envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]); + + $transport = new SmtpTransport($stream); + $transport->setPingThreshold(1); + + $transport->send(new RawMessage('Message 1'), $envelope); + $transport->send(new RawMessage('Message 2'), $envelope); + + $this->assertNotContains("NOOP\r\n", $stream->getCommands()); + + $stream->clearCommands(); + sleep(1); + + $transport->send(new RawMessage('Message 3'), $envelope); + $this->assertContains("NOOP\r\n", $stream->getCommands()); + } +} + +class DummyStream extends AbstractStream +{ + /** + * @var string + */ + private $nextResponse; + + /** + * @var string[] + */ + private $commands; + + public function initialize(): void + { + $this->nextResponse = '220 localhost'; + } + + public function write(string $bytes, $debug = true): void + { + $this->commands[] = $bytes; + + if (0 === strpos($bytes, 'DATA')) { + $this->nextResponse = '354 Enter message, ending with "." on a line by itself'; + } elseif (0 === strpos($bytes, 'QUIT')) { + $this->nextResponse = '221 Goodbye'; + } else { + $this->nextResponse = '250 OK'; + } + } + + public function readLine(): string + { + return $this->nextResponse; + } + + public function flush(): void + { + } + + /** + * @return string[] + */ + public function getCommands(): array + { + return $this->commands; + } + + public function clearCommands(): void + { + $this->commands = []; + } + + protected function getReadConnectionDescription(): string + { + return 'null'; + } } diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php index cb3b4e0ae5..091b5e2bc5 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php @@ -35,6 +35,8 @@ class SmtpTransport extends AbstractTransport private $restartThreshold = 100; private $restartThresholdSleep = 0; private $restartCounter; + private $pingThreshold = 100; + private $lastMessageTime = 0; private $stream; private $domain = '[127.0.0.1]'; @@ -66,6 +68,28 @@ class SmtpTransport extends AbstractTransport return $this; } + /** + * Sets the minimum number of seconds required between two messages, before the server is pinged. + * If the transport wants to send a message and the time since the last message exceeds the specified threshold, + * the transport will ping the server first (NOOP command) to check if the connection is still alive. + * Otherwise the message will be sent without pinging the server first. + * + * Do not set the threshold too low, as the SMTP server may drop the connection if there are too many + * non-mail commands (like pinging the server with NOOP). + * + * By default, the threshold is set to 100 seconds. + * + * @param int $seconds The minimum number of seconds between two messages required to ping the server + * + * @return $this + */ + public function setPingThreshold(int $seconds): self + { + $this->pingThreshold = $seconds; + + return $this; + } + /** * Sets the name of the local domain that will be used in HELO. * @@ -160,7 +184,10 @@ class SmtpTransport extends AbstractTransport protected function doSend(SentMessage $message): void { - $this->ping(); + if (microtime(true) - $this->lastMessageTime > $this->pingThreshold) { + $this->ping(); + } + if (!$this->started) { $this->start(); } @@ -183,6 +210,8 @@ class SmtpTransport extends AbstractTransport $e->appendDebug($this->stream->getDebug()); throw $e; + } finally { + $this->lastMessageTime = microtime(true); } } @@ -213,6 +242,7 @@ class SmtpTransport extends AbstractTransport $this->assertResponseCode($this->getFullResponse(), [220]); $this->doHeloCommand(); $this->started = true; + $this->lastMessageTime = 0; $this->getLogger()->debug(sprintf('Email transport "%s" started', __CLASS__)); } From 7f6d71c2a39650fdf3abcae2636647157ebb1c19 Mon Sep 17 00:00:00 2001 From: Loulier Guillaume Date: Fri, 7 Feb 2020 19:31:10 +0100 Subject: [PATCH 2/5] refactor(Process): fromShellCommandLine --- src/Symfony/Component/Process/PhpProcess.php | 9 +++++++++ .../Component/Process/Tests/PhpProcessTest.php | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Symfony/Component/Process/PhpProcess.php b/src/Symfony/Component/Process/PhpProcess.php index 126d9b754f..22fc1b385a 100644 --- a/src/Symfony/Component/Process/PhpProcess.php +++ b/src/Symfony/Component/Process/PhpProcess.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Process; +use Symfony\Component\Process\Exception\LogicException; use Symfony\Component\Process\Exception\RuntimeException; /** @@ -49,6 +50,14 @@ class PhpProcess extends Process parent::__construct($php, $cwd, $env, $script, $timeout); } + /** + * {@inheritdoc} + */ + public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + { + throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); + } + /** * Sets the path to the PHP binary to use. * diff --git a/src/Symfony/Component/Process/Tests/PhpProcessTest.php b/src/Symfony/Component/Process/Tests/PhpProcessTest.php index b7b21ebcb1..5f1e5e6700 100644 --- a/src/Symfony/Component/Process/Tests/PhpProcessTest.php +++ b/src/Symfony/Component/Process/Tests/PhpProcessTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Process\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Exception\LogicException; use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\PhpProcess; @@ -60,4 +61,14 @@ PHP; $process->run(); $this->assertEquals($expected, $process->getOutput()); } + + public function testProcessCannotBeCreatedUsingFromShellCommandLine() + { + static::expectException(LogicException::class); + static::expectExceptionMessage('The "Symfony\Component\Process\PhpProcess::fromShellCommandline()" method cannot be called when using "Symfony\Component\Process\PhpProcess".'); + PhpProcess::fromShellCommandline(<< Date: Fri, 7 Feb 2020 19:04:34 +0100 Subject: [PATCH 3/5] [ErrorHandler] Never throw on warnings triggered by assert() and set assert.exception=1 in Debug::enable() --- src/Symfony/Component/ErrorHandler/Debug.php | 5 +++ .../Component/ErrorHandler/ErrorHandler.php | 5 +++ .../ErrorHandler/Tests/ErrorHandlerTest.php | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/src/Symfony/Component/ErrorHandler/Debug.php b/src/Symfony/Component/ErrorHandler/Debug.php index 50d1789f09..a3c0511d22 100644 --- a/src/Symfony/Component/ErrorHandler/Debug.php +++ b/src/Symfony/Component/ErrorHandler/Debug.php @@ -29,6 +29,11 @@ class Debug ini_set('display_errors', 1); } + ini_set('zend.assertions', 1); + ini_set('assert.active', 1); + ini_set('assert.warning', 0); + ini_set('assert.exception', 1); + DebugClassLoader::enable(); return ErrorHandler::register(new ErrorHandler(new BufferingLogger(), true)); diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php index b3afb53c9e..2ac16e380d 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php +++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php @@ -413,6 +413,11 @@ class ErrorHandler $throw = $this->thrownErrors & $type & $level; $type &= $level | $this->screamedErrors; + // Never throw on warnings triggered by assert() + if (E_WARNING === $type && 'a' === $message[0] && 0 === strncmp($message, 'assert(): ', 10)) { + $throw = 0; + } + if (!$type || (!$log && !$throw)) { return !$silenced && $type && $log; } diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php index 747dbb72db..70710302fd 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php @@ -615,4 +615,39 @@ class ErrorHandlerTest extends TestCase } } } + + public function testAssertQuietEval() + { + $ini = [ + ini_set('zend.assertions', 1), + ini_set('assert.active', 1), + ini_set('assert.bail', 0), + ini_set('assert.warning', 1), + ini_set('assert.callback', null), + ini_set('assert.exception', 0), + ]; + + $logger = new BufferingLogger(); + $handler = new ErrorHandler($logger); + $handler = ErrorHandler::register($handler); + + try { + \assert(false); + } finally { + restore_error_handler(); + restore_exception_handler(); + + ini_set('zend.assertions', $ini[0]); + ini_set('assert.active', $ini[1]); + ini_set('assert.bail', $ini[2]); + ini_set('assert.warning', $ini[3]); + ini_set('assert.callback', $ini[4]); + ini_set('assert.exception', $ini[5]); + } + + $logs = $logger->cleanLogs(); + + $this->assertSame('warning', $logs[0][0]); + $this->assertSame('Warning: assert(): assert(false) failed', $logs[0][1]); + } } From 365f4d76bd4b22091489d9607ecdcb766d959a9b Mon Sep 17 00:00:00 2001 From: Erkhembayar Gantulga Date: Sat, 8 Feb 2020 18:05:56 +0800 Subject: [PATCH 4/5] [Validator] Added the missing Mongolian translations https://github.com/symfony/symfony/issues/30175 Added the missing translations for the Mongolian ("mn") locale. --- .../Resources/translations/validators.mn.xlf | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf index 4be2198aa3..b1458eee1a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.mn.xlf @@ -146,6 +146,226 @@ This value is not a valid language. Энэ утга үнэн зөв хэл биш байна . + + This value is not a valid country. + Энэ утга үнэн бодит улс биш байна. + + + This value is already used. + Энэ утга аль хэдийнээ хэрэглэгдсэн байна. + + + The size of the image could not be detected. + Зургийн хэмжээ тогтоогдож чадсангүй. + + + The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. + Зургийн өргөн хэтэрхий том байна ({{ width }}px). Өргөн нь хамгийн ихдээ {{ max_width }}px байх боломжтой. + + + The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. + Зургийн өргөн хэтэрхий жижиг байна ({{ width }}px). Өргөн нь хамгийн багадаа {{ min_width }}px байх боломжтой. + + + The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. + Зургийн өндөр хэтэрхий том байна ({{ height }}px). Өндөр нь хамгийн ихдээ {{ max_height }}px байх боломжтой. + + + The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. + Зургийн өндөр хэтэрхий жижиг байна ({{ height }}px). Өндөр нь хамгийн багадаа {{ min_height }}px байх боломжтой. + + + This value should be the user's current password. + Энэ утга хэрэглэгчийн одоогийн нууц үг байх ёстой. + + + This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters. + Энэ утга яг {{ limit }} тэмдэгт байх ёстой.|Энэ утга яг {{ limit }} тэмдэгт байх ёстой. + + + The file was only partially uploaded. + Файлын зөвхөн хагас нь upload хийгдсэн. + + + No file was uploaded. + Ямар ч файл upload хийгдсэнгүй. + + + No temporary folder was configured in php.ini. + php.ini дээр түр зуурын хавтсыг тохируулаагүй байна, эсвэл тохируулсан хавтас байхгүй байна. + + + Cannot write temporary file to disk. + Түр зуурын файлыг диск руу бичиж болохгүй байна. + + + A PHP extension caused the upload to fail. + PHP extension нь upload -г амжилтгүй болгоод байна. + + + This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. + Энэ коллекц {{ limit }} ба түүнээс дээш тооны элемент агуулах ёстой.|Энэ коллекц {{ limit }} ба түүнээс дээш тооны элемент агуулах ёстой. + + + This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. + Энэ коллекц {{ limit }} ба түүнээс доош тооны элемент агуулах ёстой.|Энэ коллекц {{ limit }} ба түүнээс доош тооны элемент агуулах ёстой. + + + This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. + Энэ коллекц яг {{ limit }} элемент агуулах ёстой.|Энэ коллекц яг {{ limit }} элемент агуулах ёстой. + + + Invalid card number. + Картын дугаар буруу байна. + + + Unsupported card type or invalid card number. + Дэмжигдээгүй картын төрөл эсвэл картын дугаар буруу байна. + + + This is not a valid International Bank Account Number (IBAN). + Энэ утга үнэн зөв Олон Улсын Банкны Дансны Дугаар (IBAN) биш байна. + + + This value is not a valid ISBN-10. + Энэ утга үнэн зөв ISBN-10 биш байна. + + + This value is not a valid ISBN-13. + Энэ утга үнэн зөв ISBN-13 биш байна. + + + This value is neither a valid ISBN-10 nor a valid ISBN-13. + Энэ утга үнэн зөв ISBN-10 юмуу ISBN-13 биш байна. + + + This value is not a valid ISSN. + Энэ утга үнэн зөв ISSN биш байна. + + + This value is not a valid currency. + Энэ утга үнэн бодит валют биш байна. + + + This value should be equal to {{ compared_value }}. + Энэ утга {{ compared_value }} -тaй тэнцүү байх ёстой. + + + This value should be greater than {{ compared_value }}. + Энэ утга {{ compared_value }} -с их байх ёстой. + + + This value should be greater than or equal to {{ compared_value }}. + Энэ утга {{ compared_value }} -тай тэнцүү юмуу эсвэл их байх ёстой. + + + This value should be identical to {{ compared_value_type }} {{ compared_value }}. + Энэ утга {{ compared_value_type }} {{ compared_value }} -тай яг ижил байх ёстой. + + + This value should be less than {{ compared_value }}. + Энэ утга {{ compared_value }} -с бага байх ёстой. + + + This value should be less than or equal to {{ compared_value }}. + Энэ утга {{ compared_value }} -тай ижил юмуу эсвэл бага байх ёстой. + + + This value should not be equal to {{ compared_value }}. + Энэ утга {{ compared_value }} -тай тэнцүү байх ёсгүй. + + + This value should not be identical to {{ compared_value_type }} {{ compared_value }}. + Энэ утга {{ compared_value_type }} {{ compared_value }} -тай яг ижил байх ёсгүй. + + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Зургийн харьцаа хэтэрхий том байна ({{ ratio }}). Харьцаа нь хамгийн ихдээ {{ max_ratio }} байна. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Зургийн харьцаа хэтэрхий жижиг байна ({{ ratio }}). Харьцаа нь хамгийн багадаа {{ min_ratio }} байна. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Зураг дөрвөлжин хэлбэртэй байна ({{ width }}x{{ height }}px). Дөрвөлжин зургууд оруулах боломжгүй. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Зураг хэвтээ байрлалтай байна ({{ width }}x{{ height }}px). Хэвтээ байрлалтай зургууд оруулах боломжгүй. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Зургууд босоо байрлалтай байна ({{ width }}x{{ height }}px). Босоо байрлалтай зургууд оруулах боломжгүй. + + + An empty file is not allowed. + Хоосон файл оруулах боломжгүй. + + + The host could not be resolved. + Хост зөв тохирогдоогүй байна. + + + This value does not match the expected {{ charset }} charset. + Энэ утга тооцоолсон {{ charset }} тэмдэгттэй таарахгүй байна. + + + This is not a valid Business Identifier Code (BIC). + Энэ утга үнэн зөв Business Identifier Code (BIC) биш байна. + + + Error + Алдаа + + + This is not a valid UUID. + Энэ утга үнэн зөв UUID биш байна. + + + This value should be a multiple of {{ compared_value }}. + Энэ утга {{ compared_value }} -н үржвэр байх ёстой. + + + This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}. + Энэ Business Identifier Code (BIC) код нь IBAN {{ iban }} -тай холбоогүй байна. + + + This value should be valid JSON. + Энэ утга JSON байх ёстой. + + + This collection should contain only unique elements. + Энэ коллекц зөвхөн давтагдахгүй элементүүд агуулах ёстой. + + + This value should be positive. + Энэ утга эерэг байх ёстой. + + + This value should be either positive or zero. + Энэ утга тэг эсвэл эерэг байх ёстой. + + + This value should be negative. + Энэ утга сөрөг байх ёстой. + + + This value should be either negative or zero. + Энэ утга сөрөг эсвэл тэг байх ёстой. + + + This value is not a valid timezone. + Энэ утга үнэн зөв цагийн бүс биш байна. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Энэ нууц үгийн мэдээлэл алдагдсан байх магадлалтай учраас дахин ашиглагдах ёсгүй. Өөр нууц үг ашиглана уу. + + + This value should be between {{ min }} and {{ max }}. + Энэ утга {{ min }} -с {{ max }} хооронд байх ёстой. + From 48272f000a61bada9ceb613186a6616f1f181903 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 8 Feb 2020 17:59:15 +0100 Subject: [PATCH 5/5] Add missing symfony/mime to require-dev --- src/Symfony/Component/Serializer/composer.json | 1 + src/Symfony/Component/Validator/composer.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index cf48812e1e..a28d18fe4e 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -28,6 +28,7 @@ "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/error-handler": "^4.4|^5.0", "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/mime": "^4.4|^5.0", "symfony/property-access": "^3.4|^4.0|^5.0", "symfony/property-info": "^3.4.13|~4.0|^5.0", "symfony/validator": "^3.4|^4.0|^5.0", diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index fec833702d..230dfab8e2 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -31,6 +31,7 @@ "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/expression-language": "^3.4|^4.0|^5.0", "symfony/cache": "^3.4|^4.0|^5.0", + "symfony/mime": "^4.4|^5.0", "symfony/property-access": "^3.4|^4.0|^5.0", "symfony/property-info": "^3.4|^4.0|^5.0", "symfony/translation": "^4.2",