diff --git a/.travis.yml b/.travis.yml index ece02c3..1643539 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,16 +21,32 @@ matrix: - php: 5.5 env: deps=low - php: 5.6 - env: deps=high + env: + - deps=high + - psalm=yes + - php: 7.0 + env: + - psalm=yes + - php: 7.1 + env: + - psalm=yes + - php: 7.2 + env: + - psalm=yes + - php: 7.3 + env: + - psalm=yes install: - if [ "$deps" = "no" ]; then composer install; fi - if [ "$deps" = "low" ]; then composer update --prefer-lowest; fi - if [ "$deps" = "high" ]; then composer update; fi + - if [ "$psalm" = "yes" ]; then composer require --dev vimeo/psalm; fi script: - mkdir -p build/logs - vendor/bin/phpunit --coverage-clover build/logs/clover.xml + - if [ "$psalm" = "yes" ]; then vendor/bin/psalm; fi after_script: - php vendor/bin/coveralls diff --git a/EmailValidator/EmailLexer.php b/EmailValidator/EmailLexer.php index 882c968..bec75b1 100644 --- a/EmailValidator/EmailLexer.php +++ b/EmailValidator/EmailLexer.php @@ -73,9 +73,15 @@ class EmailLexer extends AbstractLexer '\0' => self::C_NUL, ); + /** + * @var bool + */ protected $hasInvalidTokens = false; - protected $previous; + /** + * @var array + */ + protected $previous = []; public function reset() { @@ -83,15 +89,20 @@ public function reset() parent::reset(); } + /** + * @return bool + */ public function hasInvalidTokens() { return $this->hasInvalidTokens; } /** - * @param $type + * @param int $type * @throws \UnexpectedValueException * @return boolean + * + * @psalm-suppress InvalidScalarArgument */ public function find($type) { @@ -107,7 +118,7 @@ public function find($type) /** * getPrevious * - * @return array token + * @return array */ public function getPrevious() { @@ -179,6 +190,11 @@ protected function getType(&$value) return self::GENERIC; } + /** + * @param string $value + * + * @return bool + */ protected function isValid($value) { if (isset($this->charValue[$value])) { @@ -189,7 +205,7 @@ protected function isValid($value) } /** - * @param $value + * @param string $value * @return bool */ protected function isNullType($value) @@ -202,7 +218,7 @@ protected function isNullType($value) } /** - * @param $value + * @param string $value * @return bool */ protected function isUTF8Invalid($value) diff --git a/EmailValidator/EmailParser.php b/EmailValidator/EmailParser.php index d0627d8..09a63cc 100644 --- a/EmailValidator/EmailParser.php +++ b/EmailValidator/EmailParser.php @@ -17,11 +17,33 @@ class EmailParser { const EMAIL_MAX_LENGTH = 254; + /** + * @var \SplObjectStorage|array + */ protected $warnings; + + /** + * @var string + */ protected $domainPart = ''; + + /** + * @var string + */ protected $localPart = ''; + /** + * @var EmailLexer + */ protected $lexer; + + /** + * @var LocalPart + */ protected $localPartParser; + + /** + * @var DomainPart + */ protected $domainPartParser; public function __construct(EmailLexer $lexer) @@ -33,7 +55,7 @@ public function __construct(EmailLexer $lexer) } /** - * @param $str + * @param string $str * @return array */ public function parse($str) @@ -57,6 +79,9 @@ public function parse($str) return array('local' => $this->localPart, 'domain' => $this->domainPart); } + /** + * @return Warning\Warning[] + */ public function getWarnings() { $localPartWarnings = $this->localPartParser->getWarnings(); @@ -68,11 +93,17 @@ public function getWarnings() return $this->warnings; } + /** + * @return string + */ public function getParsedDomainPart() { return $this->domainPart; } + /** + * @param string $email + */ protected function setParts($email) { $parts = explode('@', $email); @@ -80,6 +111,9 @@ protected function setParts($email) $this->localPart = $parts[0]; } + /** + * @return bool + */ protected function hasAtToken() { $this->lexer->moveNext(); diff --git a/EmailValidator/EmailValidator.php b/EmailValidator/EmailValidator.php index 44b4b93..a30f21d 100644 --- a/EmailValidator/EmailValidator.php +++ b/EmailValidator/EmailValidator.php @@ -13,12 +13,12 @@ class EmailValidator private $lexer; /** - * @var array + * @var Warning\Warning[] */ - protected $warnings; + protected $warnings = []; /** - * @var InvalidEmail + * @var InvalidEmail|null */ protected $error; @@ -28,7 +28,7 @@ public function __construct() } /** - * @param $email + * @param string $email * @param EmailValidation $emailValidation * @return bool */ @@ -58,7 +58,7 @@ public function getWarnings() } /** - * @return InvalidEmail + * @return InvalidEmail|null */ public function getError() { diff --git a/EmailValidator/Parser/DomainPart.php b/EmailValidator/Parser/DomainPart.php index 51769de..b39d53a 100644 --- a/EmailValidator/Parser/DomainPart.php +++ b/EmailValidator/Parser/DomainPart.php @@ -35,6 +35,10 @@ class DomainPart extends Parser { const DOMAIN_MAX_LENGTH = 254; + + /** + * @var string + */ protected $domainPart = ''; public function parse($domainPart) @@ -77,11 +81,18 @@ public function parse($domainPart) $this->domainPart = $domain; } + /** + * @return string + */ public function getDomainPart() { return $this->domainPart; } + /** + * @param string $addressLiteral + * @param int $maxGroups + */ public function checkIPV6Tag($addressLiteral, $maxGroups = 8) { $prev = $this->lexer->getPrevious(); @@ -125,6 +136,9 @@ public function checkIPV6Tag($addressLiteral, $maxGroups = 8) } } + /** + * @return string + */ protected function doParseDomainPart() { $domain = ''; @@ -171,7 +185,7 @@ protected function doParseDomainPart() return $domain; } - private function checkNotAllowedChars($token) + private function checkNotAllowedChars(array $token) { $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true]; if (isset($notAllowed[$token['type']])) { @@ -179,6 +193,9 @@ private function checkNotAllowedChars($token) } } + /** + * @return string|false + */ protected function parseDomainLiteral() { if ($this->lexer->isNextToken(EmailLexer::S_COLON)) { @@ -195,6 +212,9 @@ protected function parseDomainLiteral() return $this->doParseDomainLiteral(); } + /** + * @return string|false + */ protected function doParseDomainLiteral() { $IPv6TAG = false; @@ -262,6 +282,11 @@ protected function doParseDomainLiteral() return $addressLiteral; } + /** + * @param string $addressLiteral + * + * @return string|false + */ protected function checkIPV4Tag($addressLiteral) { $matchesIP = array(); @@ -279,13 +304,13 @@ protected function checkIPV4Tag($addressLiteral) return false; } // Convert IPv4 part to IPv6 format for further testing - $addressLiteral = substr($addressLiteral, 0, $index) . '0:0'; + $addressLiteral = substr($addressLiteral, 0, (int) $index) . '0:0'; } return $addressLiteral; } - protected function checkDomainPartExceptions($prev) + protected function checkDomainPartExceptions(array $prev) { $invalidDomainTokens = array( EmailLexer::S_DQUOTE => true, @@ -320,6 +345,9 @@ protected function checkDomainPartExceptions($prev) } } + /** + * @return bool + */ protected function hasBrackets() { if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { @@ -335,7 +363,7 @@ protected function hasBrackets() return true; } - protected function checkLabelLength($prev) + protected function checkLabelLength(array $prev) { if ($this->lexer->token['type'] === EmailLexer::S_DOT && $prev['type'] === EmailLexer::GENERIC && diff --git a/EmailValidator/Parser/LocalPart.php b/EmailValidator/Parser/LocalPart.php index 8ab16ab..a7be490 100644 --- a/EmailValidator/Parser/LocalPart.php +++ b/EmailValidator/Parser/LocalPart.php @@ -67,6 +67,9 @@ public function parse($localPart) } } + /** + * @return bool + */ protected function parseDoubleQuote() { $parseAgain = true; @@ -118,7 +121,10 @@ protected function parseDoubleQuote() return $parseAgain; } - protected function isInvalidToken($token, $closingQuote) + /** + * @param bool $closingQuote + */ + protected function isInvalidToken(array $token, $closingQuote) { $forbidden = array( EmailLexer::S_COMMA, diff --git a/EmailValidator/Parser/Parser.php b/EmailValidator/Parser/Parser.php index e5042e1..7e70a88 100644 --- a/EmailValidator/Parser/Parser.php +++ b/EmailValidator/Parser/Parser.php @@ -21,8 +21,19 @@ abstract class Parser { + /** + * @var \Egulias\EmailValidator\Warning\Warning[] + */ protected $warnings = []; + + /** + * @var EmailLexer + */ protected $lexer; + + /** + * @var int + */ protected $openedParenthesis = 0; public function __construct(EmailLexer $lexer) @@ -30,11 +41,17 @@ public function __construct(EmailLexer $lexer) $this->lexer = $lexer; } + /** + * @return \Egulias\EmailValidator\Warning\Warning[] + */ public function getWarnings() { return $this->warnings; } + /** + * @param string $str + */ abstract public function parse($str); /** @return int */ @@ -80,6 +97,9 @@ protected function parseComments() } } + /** + * @return bool + */ protected function isUnclosedComment() { try { @@ -122,6 +142,9 @@ protected function checkConsecutiveDots() } } + /** + * @return bool + */ protected function isFWS() { if ($this->escaped()) { @@ -140,6 +163,9 @@ protected function isFWS() return false; } + /** + * @return bool + */ protected function escaped() { $previous = $this->lexer->getPrevious(); @@ -154,6 +180,9 @@ protected function escaped() return false; } + /** + * @return bool + */ protected function warnEscaping() { if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) { @@ -174,6 +203,11 @@ protected function warnEscaping() } + /** + * @param bool $hasClosingQuote + * + * @return bool + */ protected function checkDQUOTE($hasClosingQuote) { if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) { diff --git a/EmailValidator/Validation/DNSCheckValidation.php b/EmailValidator/Validation/DNSCheckValidation.php index e5c3e5d..207fc73 100644 --- a/EmailValidator/Validation/DNSCheckValidation.php +++ b/EmailValidator/Validation/DNSCheckValidation.php @@ -15,10 +15,10 @@ class DNSCheckValidation implements EmailValidation private $warnings = []; /** - * @var InvalidEmail + * @var InvalidEmail|null */ private $error; - + public function __construct() { if (!extension_loaded('intl')) { @@ -49,6 +49,11 @@ public function getWarnings() return $this->warnings; } + /** + * @param string $host + * + * @return bool + */ protected function checkDNS($host) { $variant = INTL_IDNA_VARIANT_2003; diff --git a/EmailValidator/Validation/Exception/EmptyValidationList.php b/EmailValidator/Validation/Exception/EmptyValidationList.php index 775ad16..ee7c41a 100644 --- a/EmailValidator/Validation/Exception/EmptyValidationList.php +++ b/EmailValidator/Validation/Exception/EmptyValidationList.php @@ -6,6 +6,9 @@ class EmptyValidationList extends \InvalidArgumentException { + /** + * @param int $code + */ public function __construct($code = 0, Exception $previous = null) { parent::__construct("Empty validation list is not allowed", $code, $previous); diff --git a/EmailValidator/Validation/MultipleErrors.php b/EmailValidator/Validation/MultipleErrors.php index d5e87d8..3be5973 100644 --- a/EmailValidator/Validation/MultipleErrors.php +++ b/EmailValidator/Validation/MultipleErrors.php @@ -9,16 +9,22 @@ class MultipleErrors extends InvalidEmail const CODE = 999; const REASON = "Accumulated errors for multiple validations"; /** - * @var array + * @var InvalidEmail[] */ private $errors = []; + /** + * @param InvalidEmail[] $errors + */ public function __construct(array $errors) { $this->errors = $errors; parent::__construct(); } + /** + * @return InvalidEmail[] + */ public function getErrors() { return $this->errors; diff --git a/EmailValidator/Validation/MultipleValidationWithAnd.php b/EmailValidator/Validation/MultipleValidationWithAnd.php index ce161ac..d2fde7b 100644 --- a/EmailValidator/Validation/MultipleValidationWithAnd.php +++ b/EmailValidator/Validation/MultipleValidationWithAnd.php @@ -30,12 +30,12 @@ class MultipleValidationWithAnd implements EmailValidation private $warnings = []; /** - * @var MultipleErrors + * @var MultipleErrors|null */ private $error; /** - * @var bool + * @var int */ private $mode; @@ -79,6 +79,12 @@ public function isValid($email, EmailLexer $emailLexer) return $result; } + /** + * @param \Egulias\EmailValidator\Exception\InvalidEmail|null $possibleError + * @param \Egulias\EmailValidator\Exception\InvalidEmail[] $errors + * + * @return \Egulias\EmailValidator\Exception\InvalidEmail[] + */ private function addNewError($possibleError, array $errors) { if (null !== $possibleError) { @@ -88,6 +94,11 @@ private function addNewError($possibleError, array $errors) return $errors; } + /** + * @param bool $result + * + * @return bool + */ private function shouldStop($result) { return !$result && $this->mode === self::STOP_ON_ERROR; diff --git a/EmailValidator/Validation/NoRFCWarningsValidation.php b/EmailValidator/Validation/NoRFCWarningsValidation.php index e4bf0cc..6b31e54 100644 --- a/EmailValidator/Validation/NoRFCWarningsValidation.php +++ b/EmailValidator/Validation/NoRFCWarningsValidation.php @@ -9,7 +9,7 @@ class NoRFCWarningsValidation extends RFCValidation { /** - * @var InvalidEmail + * @var InvalidEmail|null */ private $error; diff --git a/EmailValidator/Validation/RFCValidation.php b/EmailValidator/Validation/RFCValidation.php index c4ffe35..8781e0b 100644 --- a/EmailValidator/Validation/RFCValidation.php +++ b/EmailValidator/Validation/RFCValidation.php @@ -9,7 +9,7 @@ class RFCValidation implements EmailValidation { /** - * @var EmailParser + * @var EmailParser|null */ private $parser; @@ -19,7 +19,7 @@ class RFCValidation implements EmailValidation private $warnings = []; /** - * @var InvalidEmail + * @var InvalidEmail|null */ private $error; diff --git a/EmailValidator/Validation/SpoofCheckValidation.php b/EmailValidator/Validation/SpoofCheckValidation.php index 4721f0d..e10bfab 100644 --- a/EmailValidator/Validation/SpoofCheckValidation.php +++ b/EmailValidator/Validation/SpoofCheckValidation.php @@ -10,7 +10,7 @@ class SpoofCheckValidation implements EmailValidation { /** - * @var InvalidEmail + * @var InvalidEmail|null */ private $error; @@ -21,6 +21,9 @@ public function __construct() } } + /** + * @psalm-suppress InvalidArgument + */ public function isValid($email, EmailLexer $emailLexer) { $checker = new Spoofchecker(); @@ -33,6 +36,9 @@ public function isValid($email, EmailLexer $emailLexer) return $this->error === null; } + /** + * @return InvalidEmail|null + */ public function getError() { return $this->error; diff --git a/EmailValidator/Warning/QuotedPart.php b/EmailValidator/Warning/QuotedPart.php index 7be9e6a..36a4265 100644 --- a/EmailValidator/Warning/QuotedPart.php +++ b/EmailValidator/Warning/QuotedPart.php @@ -6,6 +6,10 @@ class QuotedPart extends Warning { const CODE = 36; + /** + * @param scalar $prevToken + * @param scalar $postToken + */ public function __construct($prevToken, $postToken) { $this->message = "Deprecated Quoted String found between $prevToken and $postToken"; diff --git a/EmailValidator/Warning/QuotedString.php b/EmailValidator/Warning/QuotedString.php index e9d56e1..817e4e8 100644 --- a/EmailValidator/Warning/QuotedString.php +++ b/EmailValidator/Warning/QuotedString.php @@ -6,6 +6,10 @@ class QuotedString extends Warning { const CODE = 11; + /** + * @param scalar $prevToken + * @param scalar $postToken + */ public function __construct($prevToken, $postToken) { $this->message = "Quoted String found between $prevToken and $postToken"; diff --git a/EmailValidator/Warning/Warning.php b/EmailValidator/Warning/Warning.php index ec6a365..bce7e7a 100644 --- a/EmailValidator/Warning/Warning.php +++ b/EmailValidator/Warning/Warning.php @@ -5,19 +5,36 @@ abstract class Warning { const CODE = 0; - protected $message; - protected $rfcNumber; + /** + * @var string + */ + protected $message = ''; + + /** + * @var int + */ + protected $rfcNumber = 0; + + /** + * @return string + */ public function message() { return $this->message; } + /** + * @return int + */ public function code() { return self::CODE; } + /** + * @return int + */ public function RFCNumber() { return $this->rfcNumber; diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..8a83de7 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + +