mirror of
https://github.com/egulias/EmailValidator.git
synced 2026-08-17 18:01:33 +00:00
Use a safe way to execute dns_get_record (v3) (#286)
* #256 Bypass a PHP warning generated by @dns_get_record The idea is to create a custom error handler for dns_get_record(), call dns_get_record and then restore the original error handler. This way we can catch PHP warnings (e.g. "Warning: dns_get_record(): DNS Query failed" or "dns_get_record(): A temporary server error occurred." [ErrorException]) * #256 Make `UnableToGetDNSRecord` reason more testable * #256 Suppress psalm’s false-positive error see https://github.com/vimeo/psalm/issues/5134#issuecomment-782937791 * #256 Speed up tests: start from previously failed * #256 Suppress psalm’s false-positive error We don’t have a control over AbstractLexer and can’t specify array shape in this parent class
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ build:
|
||||
tests:
|
||||
override:
|
||||
-
|
||||
command: 'vendor/bin/phpunit --coverage-clover=clover.xml'
|
||||
command: 'vendor/bin/phpunit --coverage-clover=clover.xml --exclude-group slow'
|
||||
coverage:
|
||||
file: 'clover.xml'
|
||||
format: 'clover'
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ before_script:
|
||||
- mkdir -p build/logs
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml --exclude-group slow
|
||||
- if [ "$psalm" = "yes" ]; then vendor/bin/psalm; fi
|
||||
|
||||
after_script:
|
||||
|
||||
+5
-2
@@ -1,14 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
executionOrder="defects"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="EmailValidator Test Suite">
|
||||
|
||||
@@ -125,6 +125,7 @@ class EmailLexer extends AbstractLexer
|
||||
* @var array
|
||||
*
|
||||
* @psalm-var array{value:string, type:null|int, position:int}
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
*/
|
||||
public $token;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Egulias\EmailValidator\Result\Reason;
|
||||
|
||||
/**
|
||||
* Used on SERVFAIL, TIMEOUT or other runtime and network errors
|
||||
*/
|
||||
class UnableToGetDNSRecord extends NoDNSRecord
|
||||
{
|
||||
public function code() : int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
public function description() : string
|
||||
{
|
||||
return 'Unable to get DNS records for the host';
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,18 @@ namespace Egulias\EmailValidator\Validation;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;
|
||||
use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
|
||||
class DNSCheckValidation implements EmailValidation
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -114,12 +120,27 @@ class DNSCheckValidation implements EmailValidation
|
||||
*/
|
||||
private function validateDnsRecords($host) : bool
|
||||
{
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
$dnsRecords = @dns_get_record($host, DNS_MX + DNS_A + DNS_AAAA);
|
||||
// A workaround to fix https://bugs.php.net/bug.php?id=73149
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
set_error_handler(
|
||||
static function (int $errorLevel, string $errorMessage): ?bool {
|
||||
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
// Get all MX, A and AAAA DNS records for host
|
||||
$dnsRecords = dns_get_record($host, static::DNS_RECORD_TYPES_TO_CHECK);
|
||||
} catch (\RuntimeException $exception) {
|
||||
$this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
// No MX, A or AAAA DNS records
|
||||
if (empty($dnsRecords)) {
|
||||
if ($dnsRecords === [] || $dnsRecords === false) {
|
||||
$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
namespace Egulias\EmailValidator\Tests\EmailValidator\Validation;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Egulias\EmailValidator\EmailLexer;
|
||||
use Egulias\EmailValidator\Result\InvalidEmail;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
|
||||
use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
|
||||
use Egulias\EmailValidator\Result\Reason\NoDNSRecord;
|
||||
use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
|
||||
use Egulias\EmailValidator\Validation\DNSCheckValidation;
|
||||
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DNSCheckValidationTest extends TestCase
|
||||
{
|
||||
@@ -92,7 +93,7 @@ class DNSCheckValidationTest extends TestCase
|
||||
|
||||
public function testDNSWarnings()
|
||||
{
|
||||
$this->markTestSkipped('Need to found a domain with AAAA redords and no MX that fails later in the validations');
|
||||
$this->markTestSkipped('Need to found a domain with AAAA records and no MX that fails later in the validations');
|
||||
$validation = new DNSCheckValidation();
|
||||
$expectedWarnings = [NoDNSMXRecord::CODE => new NoDNSMXRecord()];
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
@@ -106,4 +107,22 @@ class DNSCheckValidationTest extends TestCase
|
||||
$validation->isValid("example@invalid.example.com", new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group slow
|
||||
*/
|
||||
public function testUnableToGetDNSRecord()
|
||||
{
|
||||
error_reporting(\E_ALL);
|
||||
|
||||
// UnableToGetDNSRecord raises on network errors (e.g. timeout) that we can‘t emulate in tests (for sure),
|
||||
// but we can try to get timeout error by trying to fetch all DNS records
|
||||
$validation = new class extends DNSCheckValidation {
|
||||
protected const DNS_RECORD_TYPES_TO_CHECK = \DNS_ALL;
|
||||
};
|
||||
$expectedError = new InvalidEmail(new UnableToGetDNSRecord(), '');
|
||||
|
||||
$validation->isValid('example@invalid.example.com', new EmailLexer());
|
||||
$this->assertEquals($expectedError, $validation->getError());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user