EmailValidator/EmailLexer.php000064400000012672151743410730012216 0ustar00 self::S_OPENPARENTHESIS, ')' => self::S_CLOSEPARENTHESIS, '<' => self::S_LOWERTHAN, '>' => self::S_GREATERTHAN, '[' => self::S_OPENBRACKET, ']' => self::S_CLOSEBRACKET, ':' => self::S_COLON, ';' => self::S_SEMICOLON, '@' => self::S_AT, '\\' => self::S_BACKSLASH, '/' => self::S_SLASH, ',' => self::S_COMMA, '.' => self::S_DOT, '"' => self::S_DQUOTE, '-' => self::S_HYPHEN, '::' => self::S_DOUBLECOLON, ' ' => self::S_SP, "\t" => self::S_HTAB, "\r" => self::S_CR, "\n" => self::S_LF, "\r\n" => self::CRLF, 'IPv6' => self::S_IPV6TAG, '{' => self::S_OPENQBRACKET, '}' => self::S_CLOSEQBRACKET, '' => self::S_EMPTY, '\0' => self::C_NUL, ); protected $hasInvalidTokens = false; protected $previous; private static $nullToken = [ 'value' => '', 'type' => null, 'position' => 0, ]; public function __construct() { $this->previous = $this->token = self::$nullToken; } /** * @return void */ public function reset() { $this->hasInvalidTokens = false; parent::reset(); $this->previous = $this->token = self::$nullToken; } public function hasInvalidTokens() { return $this->hasInvalidTokens; } /** * @param string $type * @throws \UnexpectedValueException * @return boolean */ public function find($type) { $search = clone $this; $search->skipUntil($type); if (!$search->lookahead) { throw new \UnexpectedValueException($type . ' not found'); } return true; } /** * getPrevious * * @return array token */ public function getPrevious() { return $this->previous; } /** * moveNext * * @return boolean */ public function moveNext() { $this->previous = $this->token; $hasNext = parent::moveNext(); $this->token = $this->token ?: self::$nullToken; return $hasNext; } /** * Lexical catchable patterns. * * @return string[] */ protected function getCatchablePatterns() { return array( '[a-zA-Z_]+[46]?', //ASCII and domain literal '[^\x00-\x7F]', //UTF-8 '[0-9]+', '\r\n', '::', '\s+?', '.', ); } /** * Lexical non-catchable patterns. * * @return string[] */ protected function getNonCatchablePatterns() { return array('[\xA0-\xff]+'); } /** * Retrieve token type. Also processes the token value if necessary. * * @param string $value * @throws \InvalidArgumentException * @return integer */ protected function getType(&$value) { if ($this->isNullType($value)) { return self::C_NUL; } if ($this->isValid($value)) { return $this->charValue[$value]; } if ($this->isUTF8Invalid($value)) { $this->hasInvalidTokens = true; return self::INVALID; } return self::GENERIC; } protected function isValid($value) { if (isset($this->charValue[$value])) { return true; } return false; } /** * @param string $value * @return bool */ protected function isNullType($value) { if ($value === "\0") { return true; } return false; } /** * @param string $value * @return bool */ protected function isUTF8Invalid($value) { if (preg_match('/\p{Cc}+/u', $value)) { return true; } return false; } /** * @return string */ protected function getModifiers() { return 'iu'; } } EmailValidator/EmailParser.php000064400000005121151743410730012362 0ustar00 */ class EmailParser { const EMAIL_MAX_LENGTH = 254; protected $warnings; protected $domainPart = ''; protected $localPart = ''; protected $lexer; protected $localPartParser; protected $domainPartParser; public function __construct(EmailLexer $lexer) { $this->lexer = $lexer; $this->localPartParser = new LocalPart($this->lexer); $this->domainPartParser = new DomainPart($this->lexer); $this->warnings = new \SplObjectStorage(); } /** * @param string $str * @return array */ public function parse($str) { $this->lexer->setInput($str); if (!$this->hasAtToken()) { throw new NoLocalPart(); } $this->localPartParser->parse($str); $this->domainPartParser->parse($str); $this->setParts($str); if ($this->lexer->hasInvalidTokens()) { throw new ExpectingATEXT(); } return array('local' => $this->localPart, 'domain' => $this->domainPart); } public function getWarnings() { $localPartWarnings = $this->localPartParser->getWarnings(); $domainPartWarnings = $this->domainPartParser->getWarnings(); $this->warnings = array_merge($localPartWarnings, $domainPartWarnings); $this->addLongEmailWarning($this->localPart, $this->domainPart); return $this->warnings; } public function getParsedDomainPart() { return $this->domainPart; } protected function setParts($email) { $parts = explode('@', $email); $this->domainPart = $this->domainPartParser->getDomainPart(); $this->localPart = $parts[0]; } protected function hasAtToken() { $this->lexer->moveNext(); $this->lexer->moveNext(); if ($this->lexer->token['type'] === EmailLexer::S_AT) { return false; } return true; } /** * @param string $localPart * @param string $parsedDomainPart */ protected function addLongEmailWarning($localPart, $parsedDomainPart) { if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) { $this->warnings[EmailTooLong::CODE] = new EmailTooLong(); } } } EmailValidator/EmailValidator.php000064400000002275151743410730013062 0ustar00lexer = new EmailLexer(); } /** * @param string $email * @param EmailValidation $emailValidation * @return bool */ public function isValid($email, EmailValidation $emailValidation) { $isValid = $emailValidation->isValid($email, $this->lexer); $this->warnings = $emailValidation->getWarnings(); $this->error = $emailValidation->getError(); return $isValid; } /** * @return boolean */ public function hasWarnings() { return !empty($this->warnings); } /** * @return array */ public function getWarnings() { return $this->warnings; } /** * @return InvalidEmail */ public function getError() { return $this->error; } } EmailValidator/Exception/AtextAfterCFWS.php000064400000000245151743410730014650 0ustar00lexer->moveNext(); $this->performDomainStartChecks(); $domain = $this->doParseDomainPart(); $prev = $this->lexer->getPrevious(); $length = strlen($domain); if ($prev['type'] === EmailLexer::S_DOT) { throw new DotAtEnd(); } if ($prev['type'] === EmailLexer::S_HYPHEN) { throw new DomainHyphened(); } if ($length > self::DOMAIN_MAX_LENGTH) { $this->warnings[DomainTooLong::CODE] = new DomainTooLong(); } if ($prev['type'] === EmailLexer::S_CR) { throw new CRLFAtTheEnd(); } $this->domainPart = $domain; } private function performDomainStartChecks() { $this->checkInvalidTokensAfterAT(); $this->checkEmptyDomain(); if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { $this->warnings[DeprecatedComment::CODE] = new DeprecatedComment(); $this->parseDomainComments(); } } private function checkEmptyDomain() { $thereIsNoDomain = $this->lexer->token['type'] === EmailLexer::S_EMPTY || ($this->lexer->token['type'] === EmailLexer::S_SP && !$this->lexer->isNextToken(EmailLexer::GENERIC)); if ($thereIsNoDomain) { throw new NoDomainPart(); } } private function checkInvalidTokensAfterAT() { if ($this->lexer->token['type'] === EmailLexer::S_DOT) { throw new DotAtStart(); } if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) { throw new DomainHyphened(); } } public function getDomainPart() { return $this->domainPart; } public function checkIPV6Tag($addressLiteral, $maxGroups = 8) { $prev = $this->lexer->getPrevious(); if ($prev['type'] === EmailLexer::S_COLON) { $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd(); } $IPv6 = substr($addressLiteral, 5); //Daniel Marschall's new IPv6 testing strategy $matchesIP = explode(':', $IPv6); $groupCount = count($matchesIP); $colons = strpos($IPv6, '::'); if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) { $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar(); } if ($colons === false) { // We need exactly the right number of groups if ($groupCount !== $maxGroups) { $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount(); } return; } if ($colons !== strrpos($IPv6, '::')) { $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon(); return; } if ($colons === 0 || $colons === (strlen($IPv6) - 2)) { // RFC 4291 allows :: at the start or end of an address //with 7 other groups in addition ++$maxGroups; } if ($groupCount > $maxGroups) { $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups(); } elseif ($groupCount === $maxGroups) { $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated(); } } protected function doParseDomainPart() { $domain = ''; $openedParenthesis = 0; do { $prev = $this->lexer->getPrevious(); $this->checkNotAllowedChars($this->lexer->token); if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { $this->parseComments(); $openedParenthesis += $this->getOpenedParenthesis(); $this->lexer->moveNext(); $tmpPrev = $this->lexer->getPrevious(); if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) { $openedParenthesis--; } } if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { if ($openedParenthesis === 0) { throw new UnopenedComment(); } else { $openedParenthesis--; } } $this->checkConsecutiveDots(); $this->checkDomainPartExceptions($prev); if ($this->hasBrackets()) { $this->parseDomainLiteral(); } $this->checkLabelLength($prev); if ($this->isFWS()) { $this->parseFWS(); } $domain .= $this->lexer->token['value']; $this->lexer->moveNext(); } while (null !== $this->lexer->token['type']); return $domain; } private function checkNotAllowedChars($token) { $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true]; if (isset($notAllowed[$token['type']])) { throw new CharNotAllowed(); } } protected function parseDomainLiteral() { if ($this->lexer->isNextToken(EmailLexer::S_COLON)) { $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); } if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) { $lexer = clone $this->lexer; $lexer->moveNext(); if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) { $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); } } return $this->doParseDomainLiteral(); } protected function doParseDomainLiteral() { $IPv6TAG = false; $addressLiteral = ''; do { if ($this->lexer->token['type'] === EmailLexer::C_NUL) { throw new ExpectingDTEXT(); } if ($this->lexer->token['type'] === EmailLexer::INVALID || $this->lexer->token['type'] === EmailLexer::C_DEL || $this->lexer->token['type'] === EmailLexer::S_LF ) { $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); } if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) { throw new ExpectingDTEXT(); } if ($this->lexer->isNextTokenAny( array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF) )) { $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); $this->parseFWS(); } if ($this->lexer->isNextToken(EmailLexer::S_CR)) { throw new CRNoLF(); } if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) { $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); $addressLiteral .= $this->lexer->token['value']; $this->lexer->moveNext(); $this->validateQuotedPair(); } if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) { $IPv6TAG = true; } if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) { break; } $addressLiteral .= $this->lexer->token['value']; } while ($this->lexer->moveNext()); $addressLiteral = str_replace('[', '', $addressLiteral); $addressLiteral = $this->checkIPV4Tag($addressLiteral); if (false === $addressLiteral) { return $addressLiteral; } if (!$IPv6TAG) { $this->warnings[DomainLiteral::CODE] = new DomainLiteral(); return $addressLiteral; } $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); $this->checkIPV6Tag($addressLiteral); return $addressLiteral; } protected function checkIPV4Tag($addressLiteral) { $matchesIP = array(); // Extract IPv4 part from the end of the address-literal (if there is one) if (preg_match( '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $addressLiteral, $matchesIP ) > 0 ) { $index = strrpos($addressLiteral, $matchesIP[0]); if ($index === 0) { $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); return false; } // Convert IPv4 part to IPv6 format for further testing $addressLiteral = substr($addressLiteral, 0, $index) . '0:0'; } return $addressLiteral; } protected function checkDomainPartExceptions($prev) { $invalidDomainTokens = array( EmailLexer::S_DQUOTE => true, EmailLexer::S_SEMICOLON => true, EmailLexer::S_GREATERTHAN => true, EmailLexer::S_LOWERTHAN => true, ); if (isset($invalidDomainTokens[$this->lexer->token['type']])) { throw new ExpectingATEXT(); } if ($this->lexer->token['type'] === EmailLexer::S_COMMA) { throw new CommaInDomain(); } if ($this->lexer->token['type'] === EmailLexer::S_AT) { throw new ConsecutiveAt(); } if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) { throw new ExpectingATEXT(); } if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) { throw new DomainHyphened(); } if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::GENERIC)) { throw new ExpectingATEXT(); } } protected function hasBrackets() { if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { return false; } try { $this->lexer->find(EmailLexer::S_CLOSEBRACKET); } catch (\RuntimeException $e) { throw new ExpectingDomainLiteralClose(); } return true; } protected function checkLabelLength($prev) { if ($this->lexer->token['type'] === EmailLexer::S_DOT && $prev['type'] === EmailLexer::GENERIC && strlen($prev['value']) > 63 ) { $this->warnings[LabelTooLong::CODE] = new LabelTooLong(); } } protected function parseDomainComments() { $this->isUnclosedComment(); while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { $this->warnEscaping(); $this->lexer->moveNext(); } $this->lexer->moveNext(); if ($this->lexer->isNextToken(EmailLexer::S_DOT)) { throw new ExpectingATEXT(); } } protected function addTLDWarnings() { if ($this->warnings[DomainLiteral::CODE]) { $this->warnings[TLD::CODE] = new TLD(); } } } EmailValidator/Parser/LocalPart.php000064400000010637151743410730013303 0ustar00lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) { if ($this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']) { throw new DotAtStart(); } $closingQuote = $this->checkDQUOTE($closingQuote); if ($closingQuote && $parseDQuote) { $parseDQuote = $this->parseDoubleQuote(); } if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { $this->parseComments(); $openedParenthesis += $this->getOpenedParenthesis(); } if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { if ($openedParenthesis === 0) { throw new UnopenedComment(); } else { $openedParenthesis--; } } $this->checkConsecutiveDots(); if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_AT) ) { throw new DotAtEnd(); } $this->warnEscaping(); $this->isInvalidToken($this->lexer->token, $closingQuote); if ($this->isFWS()) { $this->parseFWS(); } $this->lexer->moveNext(); } $prev = $this->lexer->getPrevious(); if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) { $this->warnings[LocalTooLong::CODE] = new LocalTooLong(); } } protected function parseDoubleQuote() { $parseAgain = true; $special = array( EmailLexer::S_CR => true, EmailLexer::S_HTAB => true, EmailLexer::S_LF => true ); $invalid = array( EmailLexer::C_NUL => true, EmailLexer::S_HTAB => true, EmailLexer::S_CR => true, EmailLexer::S_LF => true ); $setSpecialsWarning = true; $this->lexer->moveNext(); while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) { $parseAgain = false; if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) { $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); $setSpecialsWarning = false; } if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) { $this->lexer->moveNext(); } $this->lexer->moveNext(); if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) { throw new ExpectingATEXT(); } } $prev = $this->lexer->getPrevious(); if ($prev['type'] === EmailLexer::S_BACKSLASH) { if (!$this->checkDQUOTE(false)) { throw new UnclosedQuotedString(); } } if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) { throw new ExpectingAT(); } return $parseAgain; } protected function isInvalidToken($token, $closingQuote) { $forbidden = array( EmailLexer::S_COMMA, EmailLexer::S_CLOSEBRACKET, EmailLexer::S_OPENBRACKET, EmailLexer::S_GREATERTHAN, EmailLexer::S_LOWERTHAN, EmailLexer::S_COLON, EmailLexer::S_SEMICOLON, EmailLexer::INVALID ); if (in_array($token['type'], $forbidden) && !$closingQuote) { throw new ExpectingATEXT(); } } } EmailValidator/Parser/Parser.php000064400000014423151743410730012653 0ustar00lexer = $lexer; } public function getWarnings() { return $this->warnings; } abstract public function parse($str); /** @return int */ public function getOpenedParenthesis() { return $this->openedParenthesis; } /** * validateQuotedPair */ protected function validateQuotedPair() { if (!($this->lexer->token['type'] === EmailLexer::INVALID || $this->lexer->token['type'] === EmailLexer::C_DEL)) { throw new ExpectingQPair(); } $this->warnings[QuotedPart::CODE] = new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); } protected function parseComments() { $this->openedParenthesis = 1; $this->isUnclosedComment(); $this->warnings[Comment::CODE] = new Comment(); while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { $this->openedParenthesis++; } $this->warnEscaping(); $this->lexer->moveNext(); } $this->lexer->moveNext(); if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) { throw new ExpectingATEXT(); } if ($this->lexer->isNextToken(EmailLexer::S_AT)) { $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); } } protected function isUnclosedComment() { try { $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS); return true; } catch (\RuntimeException $e) { throw new UnclosedComment(); } } protected function parseFWS() { $previous = $this->lexer->getPrevious(); $this->checkCRLFInFWS(); if ($this->lexer->token['type'] === EmailLexer::S_CR) { throw new CRNoLF(); } if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { throw new AtextAfterCFWS(); } if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { throw new ExpectingCTEXT(); } if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) { $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); } else { $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); } } protected function checkConsecutiveDots() { if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) { throw new ConsecutiveDot(); } } protected function isFWS() { if ($this->escaped()) { return false; } if ($this->lexer->token['type'] === EmailLexer::S_SP || $this->lexer->token['type'] === EmailLexer::S_HTAB || $this->lexer->token['type'] === EmailLexer::S_CR || $this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::CRLF ) { return true; } return false; } protected function escaped() { $previous = $this->lexer->getPrevious(); if ($previous['type'] === EmailLexer::S_BACKSLASH && $this->lexer->token['type'] !== EmailLexer::GENERIC ) { return true; } return false; } protected function warnEscaping() { if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) { return false; } if ($this->lexer->isNextToken(EmailLexer::GENERIC)) { throw new ExpectingATEXT(); } if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) { return false; } $this->warnings[QuotedPart::CODE] = new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); return true; } protected function checkDQUOTE($hasClosingQuote) { if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) { return $hasClosingQuote; } if ($hasClosingQuote) { return $hasClosingQuote; } $previous = $this->lexer->getPrevious(); if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { throw new ExpectingATEXT(); } try { $this->lexer->find(EmailLexer::S_DQUOTE); $hasClosingQuote = true; } catch (\Exception $e) { throw new UnclosedQuotedString(); } $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']); return $hasClosingQuote; } protected function checkCRLFInFWS() { if ($this->lexer->token['type'] !== EmailLexer::CRLF) { return; } if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { throw new CRLFX2(); } if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { throw new CRLFAtTheEnd(); } } } EmailValidator/Validation/DNSCheckValidation.php000064400000003554151743410730015655 0ustar00checkDNS($host); } public function getError() { return $this->error; } public function getWarnings() { return $this->warnings; } protected function checkDNS($host) { $variant = INTL_IDNA_VARIANT_2003; if ( defined('INTL_IDNA_VARIANT_UTS46') ) { $variant = INTL_IDNA_VARIANT_UTS46; } $host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.'; $Aresult = true; $MXresult = checkdnsrr($host, 'MX'); if (!$MXresult) { $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord(); $Aresult = checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA'); if (!$Aresult) { $this->error = new NoDNSRecord(); } } return $MXresult || $Aresult; } } EmailValidator/Validation/EmailValidation.php000064400000001357151743410730015321 0ustar00errors = $errors; parent::__construct(); } public function getErrors() { return $this->errors; } } EmailValidator/Validation/MultipleValidationWithAnd.php000064400000005120151743410730017334 0ustar00validations = $validations; $this->mode = $mode; } /** * {@inheritdoc} */ public function isValid($email, EmailLexer $emailLexer) { $result = true; $errors = []; foreach ($this->validations as $validation) { $emailLexer->reset(); $validationResult = $validation->isValid($email, $emailLexer); $result = $result && $validationResult; $this->warnings = array_merge($this->warnings, $validation->getWarnings()); $errors = $this->addNewError($validation->getError(), $errors); if ($this->shouldStop($result)) { break; } } if (!empty($errors)) { $this->error = new MultipleErrors($errors); } return $result; } private function addNewError($possibleError, array $errors) { if (null !== $possibleError) { $errors[] = $possibleError; } return $errors; } private function shouldStop($result) { return !$result && $this->mode === self::STOP_ON_ERROR; } /** * Returns the validation errors. * * @return MultipleErrors|null */ public function getError() { return $this->error; } /** * {@inheritdoc} */ public function getWarnings() { return $this->warnings; } } EmailValidator/Validation/NoRFCWarningsValidation.php000064400000001432151743410730016704 0ustar00getWarnings())) { return true; } $this->error = new RFCWarnings(); return false; } /** * {@inheritdoc} */ public function getError() { return $this->error ?: parent::getError(); } } EmailValidator/Validation/RFCValidation.php000064400000001675151743410730014707 0ustar00parser = new EmailParser($emailLexer); try { $this->parser->parse((string)$email); } catch (InvalidEmail $invalid) { $this->error = $invalid; return false; } $this->warnings = $this->parser->getWarnings(); return true; } public function getError() { return $this->error; } public function getWarnings() { return $this->warnings; } } EmailValidator/Validation/SpoofCheckValidation.php000064400000001736151743410730016317 0ustar00setChecks(Spoofchecker::SINGLE_SCRIPT); if ($checker->isSuspicious($email)) { $this->error = new SpoofEmail(); } return $this->error === null; } public function getError() { return $this->error; } public function getWarnings() { return []; } } EmailValidator/Warning/AddressLiteral.php000064400000000373151743410730014471 0ustar00message = 'Address literal in domain part'; $this->rfcNumber = 5321; } } EmailValidator/Warning/CFWSNearAt.php000064400000000335151743410730013422 0ustar00message = "Deprecated folding white space near @"; } } EmailValidator/Warning/CFWSWithFWS.php000064400000000355151743410730013545 0ustar00message = 'Folding whites space followed by folding white space'; } } EmailValidator/Warning/Comment.php000064400000000321151743410730013162 0ustar00message = "Comments found in this email"; } } EmailValidator/Warning/DeprecatedComment.php000064400000000322151743410730015144 0ustar00message = 'Deprecated comments'; } } EmailValidator/Warning/DomainLiteral.php000064400000000352151743410730014310 0ustar00message = 'Domain Literal'; $this->rfcNumber = 5322; } } EmailValidator/Warning/DomainTooLong.php000064400000000402151743410730014271 0ustar00message = 'Domain is too long, exceeds 255 chars'; $this->rfcNumber = 5322; } } EmailValidator/Warning/EmailTooLong.php000064400000000436151743410730014120 0ustar00message = 'Email is too long, exceeds ' . EmailParser::EMAIL_MAX_LENGTH; } } EmailValidator/Warning/IPV6BadChar.php000064400000000371151743410730013516 0ustar00message = 'Bad char in IPV6 domain literal'; $this->rfcNumber = 5322; } } EmailValidator/Warning/IPV6ColonEnd.php000064400000000404151743410730013730 0ustar00message = ':: found at the end of the domain literal'; $this->rfcNumber = 5322; } } EmailValidator/Warning/IPV6ColonStart.php000064400000000410151743410730014314 0ustar00message = ':: found at the start of the domain literal'; $this->rfcNumber = 5322; } } EmailValidator/Warning/IPV6Deprecated.php000064400000000364151743410730014274 0ustar00message = 'Deprecated form of IPV6'; $this->rfcNumber = 5321; } } EmailValidator/Warning/IPV6DoubleColon.php000064400000000377151743410730014445 0ustar00message = 'Double colon found after IPV6 tag'; $this->rfcNumber = 5322; } } EmailValidator/Warning/IPV6GroupCount.php000064400000000372151743410730014340 0ustar00message = 'Group count is not IPV6 valid'; $this->rfcNumber = 5322; } } EmailValidator/Warning/IPV6MaxGroups.php000064400000000415151743410730014156 0ustar00message = 'Reached the maximum number of IPV6 groups allowed'; $this->rfcNumber = 5321; } } EmailValidator/Warning/LabelTooLong.php000064400000000351151743410730014104 0ustar00message = 'Label too long'; $this->rfcNumber = 5322; } } EmailValidator/Warning/LocalTooLong.php000064400000000456151743410730014125 0ustar00message = 'Local part is too long, exceeds 64 chars (octets)'; $this->rfcNumber = 5322; } } EmailValidator/Warning/NoDNSMXRecord.php000064400000000404151743410730014107 0ustar00message = 'No MX DSN record was found for this email'; $this->rfcNumber = 5321; } } EmailValidator/Warning/ObsoleteDTEXT.php000064400000000374151743410730014155 0ustar00rfcNumber = 5322; $this->message = 'Obsolete DTEXT in domain literal'; } } EmailValidator/Warning/QuotedPart.php000064400000000416151743410730013655 0ustar00message = "Deprecated Quoted String found between $prevToken and $postToken"; } } EmailValidator/Warning/QuotedString.php000064400000000405151743410730014213 0ustar00message = "Quoted String found between $prevToken and $postToken"; } } EmailValidator/Warning/TLD.php000064400000000274151743410730012212 0ustar00message = "RFC5321, TLD"; } } EmailValidator/Warning/Warning.php000064400000000776151743410730013203 0ustar00message; } public function code() { return self::CODE; } public function RFCNumber() { return $this->rfcNumber; } public function __toString() { return $this->message() . " rfc: " . $this->rfcNumber . "interal code: " . static::CODE; } } LICENSE000064400000002055151743410730005560 0ustar00Copyright (c) 2013-2016 Eduardo Gulias Davis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md000064400000007711151743410730006036 0ustar00# EmailValidator [![Build Status](https://travis-ci.org/egulias/EmailValidator.svg?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.svg?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6/small.png)](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6) ============================= ## Suported RFCs ## This library aims to support: RFC 5321, 5322, 6530, 6531, 6532. ## Requirements ## * [Composer](https://getcomposer.org) is required for installation * [Spoofchecking](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php) and [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/DNSCheckValidation.php) validation requires that your PHP system has the [PHP Internationalization Libraries](https://php.net/manual/en/book.intl.php) (also known as PHP Intl) ## Installation ## Run the command below to install via Composer ```shell composer require egulias/email-validator ``` ## Getting Started ## `EmailValidator`requires you to decide which (or combination of them) validation/s strategy/ies you'd like to follow for each [validation](#available-validations). A basic example with the RFC validation ```php isValid("example@example.com", new RFCValidation()); //true ``` ### Available validations ### 1. [RFCValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/RFCValidation.php) 2. [NoRFCWarningsValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/NoRFCWarningsValidation.php) 3. [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/DNSCheckValidation.php) 4. [SpoofCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php) 5. [MultipleValidationWithAnd](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/MultipleValidationWithAnd.php) 6. [Your own validation](#how-to-extend) `MultipleValidationWithAnd` It is a validation that operates over other validations performing a logical and (&&) over the result of each validation. ```php isValid("example@example.com", $multipleValidations); //true ``` ### How to extend ### It's easy! You just need to implement [EmailValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/EmailValidation.php) and you can use your own validation. ## Other Contributors ## (You can find current contributors [here](https://github.com/egulias/EmailValidator/graphs/contributors)) As this is a port from another library and work, here are other people related to the previous one: * Ricard Clau [@ricardclau](https://github.com/ricardclau): Performance against PHP built-in filter_var * Josepf Bielawski [@stloyd](https://github.com/stloyd): For its first re-work of Dominic's lib * Dominic Sayers [@dominicsayers](https://github.com/dominicsayers): The original isemail function ## License ## Released under the MIT License attached with this code. composer.json000064400000001665151743410730007303 0ustar00{ "name": "egulias/email-validator", "description": "A library for validating emails against several RFCs", "homepage": "https://github.com/egulias/EmailValidator", "keywords": ["email", "validation", "validator", "emailvalidation", "emailvalidator"], "license": "MIT", "authors": [ {"name": "Eduardo Gulias Davis"} ], "extra": { "branch-alias": { "dev-master": "2.1.x-dev" } }, "require": { "php": ">=5.5", "doctrine/lexer": "^1.0.1" }, "require-dev": { "satooshi/php-coveralls": "^1.0.1", "phpunit/phpunit": "^4.8.36|^7.5.15", "dominicsayers/isemail": "^3.0.7" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" }, "autoload": { "psr-4": { "Egulias\\EmailValidator\\": "EmailValidator" } }, "autoload-dev": { "psr-4": { "Egulias\\Tests\\": "Tests" } } } phpunit.xml.dist000064400000001210151743410730007716 0ustar00 ./Tests/EmailValidator ./vendor/ ./EmailValidator/