Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
<?php
namespace Egulias\EmailValidator\Result;
use Egulias\EmailValidator\Result\Reason\Reason;
class InvalidEmail implements Result
{
/**
* @var string
*/
private string $token;
/**
* @var Reason
*/
protected Reason $reason;
public function __construct(Reason $reason, string $token)
{
$this->token = $token;
$this->reason = $reason;
}
public function isValid(): bool
{
return false;
}
public function isInvalid(): bool
{
return true;
}
public function description(): string
{
return $this->reason->description() . " in char " . $this->token;
}
public function code(): int
{
return $this->reason->code();
}
public function reason(): Reason
{
return $this->reason;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Egulias\EmailValidator\Result;
use Egulias\EmailValidator\Result\Reason\EmptyReason;
use Egulias\EmailValidator\Result\Reason\Reason;
/**
* @psalm-suppress PropertyNotSetInConstructor
*/
class MultipleErrors extends InvalidEmail
{
/**
* @var Reason[]
*/
private $reasons = [];
public function __construct()
{
}
public function addReason(Reason $reason) : void
{
$this->reasons[$reason->code()] = $reason;
}
/**
* @return Reason[]
*/
public function getReasons() : array
{
return $this->reasons;
}
public function reason() : Reason
{
return 0 !== count($this->reasons)
? current($this->reasons)
: new EmptyReason();
}
public function description() : string
{
$description = '';
foreach($this->reasons as $reason) {
$description .= $reason->description() . PHP_EOL;
}
return $description;
}
public function code() : int
{
return 0;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class AtextAfterCFWS implements Reason
{
public function code() : int
{
return 133;
}
public function description() : string
{
return 'ATEXT found after CFWS';
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRLFAtTheEnd implements Reason
{
public const CODE = 149;
public const REASON = "CRLF at the end";
public function code() : int
{
return 149;
}
public function description() : string
{
return 'CRLF at the end';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRLFX2 implements Reason
{
public function code() : int
{
return 148;
}
public function description() : string
{
return 'CR LF tokens found twice';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CRNoLF implements Reason
{
public function code() : int
{
return 150;
}
public function description() : string
{
return 'Missing LF after CR';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CharNotAllowed implements Reason
{
public function code() : int
{
return 1;
}
public function description() : string
{
return "Character not allowed";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CommaInDomain implements Reason
{
public function code() : int
{
return 200;
}
public function description() : string
{
return "Comma ',' is not allowed in domain part";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class CommentsInIDRight implements Reason
{
public function code() : int
{
return 400;
}
public function description() : string
{
return 'Comments are not allowed in IDRight for message-id';
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ConsecutiveAt implements Reason
{
public function code() : int
{
return 128;
}
public function description() : string
{
return '@ found after another @';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ConsecutiveDot implements Reason
{
public function code() : int
{
return 132;
}
public function description() : string
{
return 'Concecutive DOT found';
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
abstract class DetailedReason implements Reason
{
protected $detailedDescription;
public function __construct(string $details)
{
$this->detailedDescription = $details;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class DomainAcceptsNoMail implements Reason
{
public function code() : int
{
return 154;
}
public function description() : string
{
return 'Domain accepts no mail (Null MX, RFC7505)';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class DomainHyphened extends DetailedReason
{
public function code() : int
{
return 144;
}
public function description() : string
{
return 'S_HYPHEN found in domain';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class DomainTooLong implements Reason
{
public function code() : int
{
return 244;
}
public function description() : string
{
return 'Domain is longer than 253 characters';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class DotAtEnd implements Reason
{
public function code() : int
{
return 142;
}
public function description() : string
{
return 'Dot at the end';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class DotAtStart implements Reason
{
public function code() : int
{
return 141;
}
public function description() : string
{
return "Starts with a DOT";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class EmptyReason implements Reason
{
public function code() : int
{
return 0;
}
public function description() : string
{
return 'Empty reason';
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExceptionFound implements Reason
{
/**
* @var \Exception
*/
private $exception;
public function __construct(\Exception $exception)
{
$this->exception = $exception;
}
public function code() : int
{
return 999;
}
public function description() : string
{
return $this->exception->getMessage();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExpectingATEXT extends DetailedReason
{
public function code() : int
{
return 137;
}
public function description() : string
{
return "Expecting ATEXT (Printable US-ASCII). Extended: " . $this->detailedDescription;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExpectingCTEXT implements Reason
{
public function code() : int
{
return 139;
}
public function description() : string
{
return 'Expecting CTEXT';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExpectingDTEXT implements Reason
{
public function code() : int
{
return 129;
}
public function description() : string
{
return 'Expecting DTEXT';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class ExpectingDomainLiteralClose implements Reason
{
public function code() : int
{
return 137;
}
public function description() : string
{
return "Closing bracket ']' for domain literal not found";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class LabelTooLong implements Reason
{
public function code() : int
{
return 245;
}
public function description() : string
{
return 'Domain "label" is longer than 63 characters';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class LocalOrReservedDomain implements Reason
{
public function code() : int
{
return 153;
}
public function description() : string
{
return 'Local, mDNS or reserved domain (RFC2606, RFC6762)';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class NoDNSRecord implements Reason
{
public function code() : int
{
return 5;
}
public function description() : string
{
return 'No MX or A DSN record was found for this email';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class NoDomainPart implements Reason
{
public function code() : int
{
return 131;
}
public function description() : string
{
return 'No domain part found';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class NoLocalPart implements Reason
{
public function code() : int
{
return 130;
}
public function description() : string
{
return "No local part";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class RFCWarnings implements Reason
{
public function code() : int
{
return 997;
}
public function description() : string
{
return 'Warnings found after validating';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
interface Reason
{
/**
* Code for user land to act upon;
*/
public function code() : int;
/**
* Short description of the result, human readable.
*/
public function description() : string;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class SpoofEmail implements Reason
{
public function code() : int
{
return 298;
}
public function description() : string
{
return 'The email contains mixed UTF8 chars that makes it suspicious';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class UnOpenedComment implements Reason
{
public function code() : int
{
return 152;
}
public function description(): string
{
return 'Missing opening comment parentheses - https://tools.ietf.org/html/rfc5322#section-3.2.2';
}
}

View File

@@ -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';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class UnclosedComment implements Reason
{
public function code() : int
{
return 146;
}
public function description(): string
{
return 'No closing comment token found';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class UnclosedQuotedString implements Reason
{
public function code() : int
{
return 145;
}
public function description() : string
{
return "Unclosed quoted string";
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Egulias\EmailValidator\Result\Reason;
class UnusualElements implements Reason
{
/**
* @var string $element
*/
private $element;
public function __construct(string $element)
{
$this->element = $element;
}
public function code() : int
{
return 201;
}
public function description() : string
{
return 'Unusual element found, wourld render invalid in majority of cases. Element found: ' . $this->element;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Egulias\EmailValidator\Result;
interface Result
{
/**
* Is validation result valid?
*/
public function isValid() : bool;
/**
* Is validation result invalid?
* Usually the inverse of isValid()
*/
public function isInvalid() : bool;
/**
* Short description of the result, human readable.
*/
public function description() : string;
/**
* Code for user land to act upon.
*/
public function code() : int;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Egulias\EmailValidator\Result;
use Egulias\EmailValidator\Result\Reason\SpoofEmail as ReasonSpoofEmail;
class SpoofEmail extends InvalidEmail
{
public function __construct()
{
$this->reason = new ReasonSpoofEmail();
parent::__construct($this->reason, '');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Egulias\EmailValidator\Result;
class ValidEmail implements Result
{
public function isValid(): bool
{
return true;
}
public function isInvalid(): bool
{
return false;
}
public function description(): string
{
return "Valid email";
}
public function code(): int
{
return 0;
}
}