This commit is contained in:
2025-05-12 14:25:25 +02:00
parent ab2db755ef
commit 9e378ca2b7
2719 changed files with 46505 additions and 60181 deletions

View File

@@ -28,7 +28,7 @@ abstract class AbstractHttpTransport extends AbstractTransport
protected $port;
protected $client;
public function __construct(HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
public function __construct(?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
{
$this->client = $client;
if (null === $client) {

View File

@@ -35,7 +35,7 @@ abstract class AbstractTransport implements TransportInterface
private float $rate = 0;
private float $lastSent = 0;
public function __construct(EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
public function __construct(?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
{
$this->dispatcher = $dispatcher;
$this->logger = $logger ?? new NullLogger();
@@ -58,7 +58,7 @@ abstract class AbstractTransport implements TransportInterface
return $this;
}
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
$message = clone $message;
$envelope = null !== $envelope ? clone $envelope : Envelope::create($message);

View File

@@ -25,7 +25,7 @@ abstract class AbstractTransportFactory implements TransportFactoryInterface
protected $client;
protected $logger;
public function __construct(EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null)
public function __construct(?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null)
{
$this->dispatcher = $dispatcher;
$this->client = $client;

View File

@@ -25,7 +25,7 @@ final class Dsn
private ?int $port;
private array $options;
public function __construct(string $scheme, string $host, string $user = null, #[\SensitiveParameter] string $password = null, int $port = null, array $options = [])
public function __construct(string $scheme, string $host, ?string $user = null, #[\SensitiveParameter] ?string $password = null, ?int $port = null, array $options = [])
{
$this->scheme = $scheme;
$this->host = $host;
@@ -37,24 +37,24 @@ final class Dsn
public static function fromString(#[\SensitiveParameter] string $dsn): self
{
if (false === $parsedDsn = parse_url($dsn)) {
if (false === $params = parse_url($dsn)) {
throw new InvalidArgumentException('The mailer DSN is invalid.');
}
if (!isset($parsedDsn['scheme'])) {
if (!isset($params['scheme'])) {
throw new InvalidArgumentException('The mailer DSN must contain a scheme.');
}
if (!isset($parsedDsn['host'])) {
if (!isset($params['host'])) {
throw new InvalidArgumentException('The mailer DSN must contain a host (use "default" by default).');
}
$user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null;
$password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null;
$port = $parsedDsn['port'] ?? null;
parse_str($parsedDsn['query'] ?? '', $query);
$user = '' !== ($params['user'] ?? '') ? rawurldecode($params['user']) : null;
$password = '' !== ($params['pass'] ?? '') ? rawurldecode($params['pass']) : null;
$port = $params['port'] ?? null;
parse_str($params['query'] ?? '', $query);
return new self($parsedDsn['scheme'], $parsedDsn['host'], $user, $password, $port, $query);
return new self($params['scheme'], $params['host'], $user, $password, $port, $query);
}
public function getScheme(): string
@@ -77,7 +77,7 @@ final class Dsn
return $this->password;
}
public function getPort(int $default = null): ?int
public function getPort(?int $default = null): ?int
{
return $this->port ?? $default;
}

View File

@@ -46,7 +46,7 @@ class RoundRobinTransport implements TransportInterface
$this->retryPeriod = $retryPeriod;
}
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
$exception = null;

View File

@@ -49,7 +49,7 @@ class SendmailTransport extends AbstractTransport
*
* -f<sender> flag will be appended automatically if one is not present.
*/
public function __construct(string $command = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
public function __construct(?string $command = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
{
parent::__construct($dispatcher, $logger);
@@ -64,11 +64,12 @@ class SendmailTransport extends AbstractTransport
$this->stream = new ProcessStream();
if (str_contains($this->command, ' -bs')) {
$this->stream->setCommand($this->command);
$this->stream->setInteractive(true);
$this->transport = new SmtpTransport($this->stream, $dispatcher, $logger);
}
}
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
if ($this->transport) {
return $this->transport->send($message, $envelope);
@@ -113,7 +114,7 @@ class SendmailTransport extends AbstractTransport
$this->stream->setCommand($command);
$this->stream->initialize();
foreach ($chunks as $chunk) {
$this->stream->write($chunk);
$this->stream->write($chunk, false);
}
$this->stream->flush();
$this->stream->terminate();

View File

@@ -15,6 +15,7 @@ use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
use Symfony\Component\Mailer\Transport\Smtp\Auth\AuthenticatorInterface;
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
@@ -32,7 +33,7 @@ class EsmtpTransport extends SmtpTransport
private string $password = '';
private array $capabilities;
public function __construct(string $host = 'localhost', int $port = 0, bool $tls = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null, AbstractStream $stream = null, array $authenticators = null)
public function __construct(string $host = 'localhost', int $port = 0, ?bool $tls = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null, ?AbstractStream $stream = null, ?array $authenticators = null)
{
parent::__construct($stream, $dispatcher, $logger);
@@ -199,7 +200,7 @@ class EsmtpTransport extends SmtpTransport
$authenticator->authenticate($this);
return;
} catch (TransportExceptionInterface $e) {
} catch (UnexpectedResponseException $e) {
$code = $e->getCode();
try {

View File

@@ -17,6 +17,7 @@ use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Exception\LogicException;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
@@ -38,10 +39,9 @@ class SmtpTransport extends AbstractTransport
private int $pingThreshold = 100;
private float $lastMessageTime = 0;
private AbstractStream $stream;
private string $mtaResult = '';
private string $domain = '[127.0.0.1]';
public function __construct(AbstractStream $stream = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
public function __construct(?AbstractStream $stream = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
{
parent::__construct($dispatcher, $logger);
@@ -131,7 +131,7 @@ class SmtpTransport extends AbstractTransport
return $this->domain;
}
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
try {
$message = parent::send($message, $envelope);
@@ -147,10 +147,6 @@ class SmtpTransport extends AbstractTransport
throw $e;
}
if ($this->mtaResult && $messageId = $this->parseMessageId($this->mtaResult)) {
$message->setMessageId($messageId);
}
$this->checkRestartThreshold();
return $message;
@@ -209,11 +205,11 @@ class SmtpTransport extends AbstractTransport
$this->ping();
}
if (!$this->started) {
$this->start();
}
try {
if (!$this->started) {
$this->start();
}
$envelope = $message->getEnvelope();
$this->doMailFromCommand($envelope->getSender()->getEncodedAddress());
foreach ($envelope->getRecipients() as $recipient) {
@@ -234,9 +230,13 @@ class SmtpTransport extends AbstractTransport
$this->getLogger()->debug(sprintf('Email transport "%s" stopped', __CLASS__));
throw $e;
}
$this->mtaResult = $this->executeCommand("\r\n.\r\n", [250]);
$mtaResult = $this->executeCommand("\r\n.\r\n", [250]);
$message->appendDebug($this->stream->getDebug());
$this->lastMessageTime = microtime(true);
if ($mtaResult && $messageId = $this->parseMessageId($mtaResult)) {
$message->setMessageId($messageId);
}
} catch (TransportExceptionInterface $e) {
$e->appendDebug($this->stream->getDebug());
$this->lastMessageTime = 0;
@@ -335,7 +335,7 @@ class SmtpTransport extends AbstractTransport
$codeStr = $code ? sprintf('code "%s"', $code) : 'empty code';
$responseStr = $response ? sprintf(', with message "%s"', trim($response)) : '';
throw new TransportException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0);
throw new UnexpectedResponseException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0);
}
}

View File

@@ -30,6 +30,7 @@ abstract class AbstractStream
protected $in;
/** @var resource|null */
protected $out;
protected $err;
private string $debug = '';
@@ -68,7 +69,7 @@ abstract class AbstractStream
public function terminate(): void
{
$this->stream = $this->out = $this->in = null;
$this->stream = $this->err = $this->out = $this->in = null;
}
public function readLine(): string
@@ -77,15 +78,17 @@ abstract class AbstractStream
return '';
}
$line = fgets($this->out);
$line = @fgets($this->out);
if ('' === $line || false === $line) {
$metas = stream_get_meta_data($this->out);
if ($metas['timed_out']) {
if (stream_get_meta_data($this->out)['timed_out']) {
throw new TransportException(sprintf('Connection to "%s" timed out.', $this->getReadConnectionDescription()));
}
if ($metas['eof']) {
if (feof($this->out)) { // don't use "eof" metadata, it's not accurate on Windows
throw new TransportException(sprintf('Connection to "%s" has been closed unexpectedly.', $this->getReadConnectionDescription()));
}
if (false === $line) {
throw new TransportException(sprintf('Unable to read from connection to "%s": ', $this->getReadConnectionDescription().error_get_last()['message'] ?? ''));
}
}
$this->debug .= sprintf('< %s', $line);

View File

@@ -24,12 +24,18 @@ use Symfony\Component\Mailer\Exception\TransportException;
final class ProcessStream extends AbstractStream
{
private string $command;
private bool $interactive = false;
public function setCommand(string $command): void
{
$this->command = $command;
}
public function setInteractive(bool $interactive): void
{
$this->interactive = $interactive;
}
public function initialize(): void
{
$descriptorSpec = [
@@ -45,17 +51,27 @@ final class ProcessStream extends AbstractStream
}
$this->in = &$pipes[0];
$this->out = &$pipes[1];
$this->err = &$pipes[2];
}
public function terminate(): void
{
if (null !== $this->stream) {
fclose($this->in);
$out = stream_get_contents($this->out);
fclose($this->out);
proc_close($this->stream);
$err = stream_get_contents($this->err);
fclose($this->err);
if (0 !== $exitCode = proc_close($this->stream)) {
$errorMessage = 'Process failed with exit code '.$exitCode.': '.$out.$err;
}
}
parent::terminate();
if (!$this->interactive && isset($errorMessage)) {
throw new TransportException($errorMessage);
}
}
protected function getReadConnectionDescription(): string

View File

@@ -160,7 +160,7 @@ final class SocketStream extends AbstractStream
}
stream_set_blocking($this->stream, true);
stream_set_timeout($this->stream, $timeout);
stream_set_timeout($this->stream, (int) $timeout, (int) (($timeout - (int) $timeout) * 1000000));
$this->in = &$this->stream;
$this->out = &$this->stream;
}

View File

@@ -29,5 +29,5 @@ interface TransportInterface extends \Stringable
/**
* @throws TransportExceptionInterface
*/
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage;
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage;
}

View File

@@ -44,7 +44,7 @@ final class Transports implements TransportInterface
}
}
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
/** @var Message $message */
if (RawMessage::class === $message::class || !$message->getHeaders()->has('X-Transport')) {