Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/ProviderClient/GenericProviderClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

class GenericProviderClient
{
/**
* Seconds to wait for an MX server to accept the TCP connection
*/
public const int DEFAULT_CONNECT_TIMEOUT = 30;

/**
* Host domain to use to connect to the MX servers
* Warning: some MX servers require the domain to point to an IP with a valid reverse DNS record
Expand All @@ -30,7 +35,8 @@ public function __construct(
private readonly BounceIsCausedByUnknownUserSpecification $bounceIsCausedByUnknownUserSpecification,
private readonly BounceIsCausedByInactiveUserSpecification $bounceIsCausedByInactiveUserSpecification,
private readonly BounceTypeResolver $bounceTypeResolver,
private readonly string $host
private readonly string $host,
private readonly int $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT
) {
}

Expand All @@ -39,11 +45,11 @@ public function __construct(
*/
public function check(string $email, string $mxServer): ValidationStatusDtoInterface
{
$connection = new SMTP();
$connection = $this->createConnection();
$connection->Debugoutput = $this->logger;
try {
// Based on https://github.com/PHPMailer/PHPMailer/blob/master/examples/smtp_check.phps
if (!$connection->connect($mxServer)) {
if (!$connection->connect($mxServer, null, $this->connectTimeout)) {
throw new SmtpConnectionRuntimeException(
sprintf('Failed to connect to server: %s', $mxServer),
0,
Expand Down Expand Up @@ -144,4 +150,9 @@ public function check(string $email, string $mxServer): ValidationStatusDtoInter
throw $exception;
}
}

protected function createConnection(): SMTP
{
return new SMTP();
}
}
100 changes: 100 additions & 0 deletions tests/ProviderClient/GenericProviderClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace AssoConnect\SmtpToolbox\Tests\ProviderClient;

use AssoConnect\SmtpToolbox\ProviderClient\GenericProviderClient;
use AssoConnect\SmtpToolbox\Resolver\BounceTypeResolver;
use AssoConnect\SmtpToolbox\Specification\BounceIsCausedByInactiveUserSpecification;
use AssoConnect\SmtpToolbox\Specification\BounceIsCausedByUnknownUserSpecification;
use AssoConnect\SmtpToolbox\Specification\ExceptionComesFromTemporaryFailureSpecification;
use PHPMailer\PHPMailer\SMTP;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;

class GenericProviderClientTest extends TestCase
{
public function testConnectionUsesTheDefaultTimeout(): void
{
self::assertSame(
GenericProviderClient::DEFAULT_CONNECT_TIMEOUT,
$this->connectTimeoutUsedBy(null),
);
}

public function testConnectionUsesTheConfiguredTimeout(): void
{
self::assertSame(5, $this->connectTimeoutUsedBy(5));
}

private function connectTimeoutUsedBy(?int $connectTimeout): ?int
{
$connection = new class () extends SMTP {
private ?int $timeout = null;

/**
* @param string $host
* @param int|null $port
* @param int $timeout
* @param array<mixed> $options
*/
public function connect($host, $port = null, $timeout = 30, $options = []): bool
{
$this->timeout = $timeout;

return false;
}

public function getConnectTimeout(): ?int
{
return $this->timeout;
}
};

$client = new class ($connection, $connectTimeout) extends GenericProviderClient {
public function __construct(private readonly SMTP $connection, ?int $connectTimeout)
{
$logger = new NullLogger();
$temporaryFailure = new ExceptionComesFromTemporaryFailureSpecification();
$unknownUser = new BounceIsCausedByUnknownUserSpecification();
$inactiveUser = new BounceIsCausedByInactiveUserSpecification();
$bounceTypeResolver = new BounceTypeResolver();

if (null === $connectTimeout) {
parent::__construct(
$logger,
$temporaryFailure,
$unknownUser,
$inactiveUser,
$bounceTypeResolver,
'hello.org',
);
} else {
parent::__construct(
$logger,
$temporaryFailure,
$unknownUser,
$inactiveUser,
$bounceTypeResolver,
'hello.org',
$connectTimeout,
);
}
}

protected function createConnection(): SMTP
{
return $this->connection;
}
};

try {
$client->check('john@example.org', 'mx.example.org');
} catch (\Exception) {
// A refused connection always ends in an exception; only the timeout passed to connect() matters here
}

return $connection->getConnectTimeout();
}
}
Loading