diff --git a/.travis.yml b/.travis.yml index e648e82..316b7f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,4 +17,5 @@ install: script: - vendor/bin/php-cs-fixer fix --dry-run + - vendor/bin/phpstan analyze - vendor/bin/phpunit diff --git a/composer.json b/composer.json index 49d006b..3a1c738 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ }, "require-dev" : { "phpunit/phpunit" : "^9.1", - "friendsofphp/php-cs-fixer": "^2.16.3" + "friendsofphp/php-cs-fixer": "^2.16.3", + "phpstan/phpstan": "^0.12.25" }, "suggest": { "ext-bcmath" : "This library makes use of bcmod function, so an installed bcmath extension is recommended." diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..4d667cb --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + paths: + - src + level: + max + checkMissingIterableValueType: false \ No newline at end of file diff --git a/src/CountryInfo.php b/src/CountryInfo.php index 44807dc..a4c6b02 100644 --- a/src/CountryInfo.php +++ b/src/CountryInfo.php @@ -33,11 +33,7 @@ class CountryInfo public function __construct(string $countryCode, Registry $swiftRegistry = null) { $this->countryCode = $countryCode; - $this->swiftRegistry = $swiftRegistry; - - if (null === $swiftRegistry) { - $this->swiftRegistry = new Registry(); - } + $this->swiftRegistry = $swiftRegistry ?? new Registry(); if (!$this->swiftRegistry->isCountryAvailable($this->countryCode)) { throw new UnsupportedCountryCodeException($this->countryCode); @@ -69,22 +65,22 @@ public function getBbanRegex(): string return $this->swiftRegistry->getBbanRegex($this->countryCode); } - public function getIbanLength(): string + public function getIbanLength(): int { return $this->swiftRegistry->getIbanLength($this->countryCode); } - public function getBbanLength(): string + public function getBbanLength(): int { return $this->swiftRegistry->getBbanLength($this->countryCode); } - public function getBbanBankIdentifierStartPos(): string + public function getBbanBankIdentifierStartPos(): int { return $this->swiftRegistry->getBbanBankIdentifierStartPos($this->countryCode); } - public function getBbanBankIdentifierEndPos(): string + public function getBbanBankIdentifierEndPos(): int { return $this->swiftRegistry->getBbanBankIdentifierEndPos($this->countryCode); } diff --git a/src/Exception/CanNotBeNormalizedException.php b/src/Exception/CanNotBeNormalizedException.php new file mode 100644 index 0000000..51f4614 --- /dev/null +++ b/src/Exception/CanNotBeNormalizedException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Iban\Validation\Exception; + +/** + * @author Jan Schädlich + */ +class CanNotBeNormalizedException extends \RuntimeException +{ + public function __construct(string $iban) + { + parent::__construct(sprintf('Given IBAN "%s" can not be normalized.', $iban)); + } +} diff --git a/src/Exception/InvalidChecksumException.php b/src/Exception/InvalidChecksumException.php index 5395bd9..3c6d71f 100644 --- a/src/Exception/InvalidChecksumException.php +++ b/src/Exception/InvalidChecksumException.php @@ -16,13 +16,16 @@ */ class InvalidChecksumException extends \RuntimeException { + /** + * @var string + */ protected $validChecksum; public function __construct(string $iban, string $validChecksum) { $this->validChecksum = $validChecksum; - parent::__construct(sprintf('Checksum of given IBAN "%s" is not valid! Valid checksum is %s', $iban, $validChecksum)); + parent::__construct(sprintf('Checksum of given IBAN "%s" is not valid. Valid checksum is "%s".', $iban, $validChecksum)); } public function getValidChecksum(): string diff --git a/src/Exception/InvalidFormatException.php b/src/Exception/InvalidFormatException.php index 95430d1..ce7d171 100644 --- a/src/Exception/InvalidFormatException.php +++ b/src/Exception/InvalidFormatException.php @@ -18,6 +18,6 @@ class InvalidFormatException extends \RuntimeException { public function __construct(string $iban) { - parent::__construct(sprintf('Format of given IBAN "%s" is not valid!', $iban)); + parent::__construct(sprintf('Format of given IBAN "%s" is not valid.', $iban)); } } diff --git a/src/Exception/InvalidLengthException.php b/src/Exception/InvalidLengthException.php index f022861..d6a4f35 100644 --- a/src/Exception/InvalidLengthException.php +++ b/src/Exception/InvalidLengthException.php @@ -18,6 +18,6 @@ class InvalidLengthException extends \RuntimeException { public function __construct(string $iban) { - parent::__construct(sprintf('Length of given IBAN "%s" is not valid!', $iban)); + parent::__construct(sprintf('Length of given IBAN "%s" is not valid.', $iban)); } } diff --git a/src/Iban.php b/src/Iban.php index 4e8d477..d0588a8 100644 --- a/src/Iban.php +++ b/src/Iban.php @@ -11,6 +11,8 @@ namespace Iban\Validation; +use Iban\Validation\Exception\CanNotBeNormalizedException; + /** * Represents an International Bank Account Number (IBAN). * @@ -45,13 +47,13 @@ public function __toString(): string public function getNormalizedIban(): string { - $iban = $this->iban; + $normalizedIban = trim(strtoupper($this->iban)); - $iban = trim(strtoupper($iban)); - $iban = preg_replace('/^I?IBAN/', '', $iban); - $iban = preg_replace('/[^a-zA-Z0-9]/', '', $iban); + if (null === $normalizedIban = preg_replace(['/^I?IBAN/', '/[^a-zA-Z0-9]/', '/\s+/'], '', $normalizedIban)) { + throw new CanNotBeNormalizedException($this->iban); + } - return preg_replace('/\s+/', '', $iban); + return $normalizedIban; } public function format(string $type = self::FORMAT_PRINT): string diff --git a/src/Swift/Exception/RegexConversionException.php b/src/Swift/Exception/RegexConversionException.php new file mode 100644 index 0000000..ea5e0b3 --- /dev/null +++ b/src/Swift/Exception/RegexConversionException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Iban\Validation\Swift\Exception; + +/** + * @author Jan Schädlich + */ +class RegexConversionException extends \RuntimeException +{ + public function __construct(string $input) + { + parent::__construct(sprintf('Can not convert given input "%s".', $input)); + } +} diff --git a/src/Swift/Exception/RegistryLoaderException.php b/src/Swift/Exception/RegistryLoaderException.php new file mode 100644 index 0000000..e342bc8 --- /dev/null +++ b/src/Swift/Exception/RegistryLoaderException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Iban\Validation\Swift\Exception; + +/** + * @author Jan Schädlich + */ +class RegistryLoaderException extends \RuntimeException +{ + public function __construct(string $filename) + { + parent::__construct(sprintf('Can not load contents of file "%s".', $filename)); + } +} diff --git a/src/Swift/Exception/UnsupportedCountryCodeException.php b/src/Swift/Exception/UnsupportedCountryCodeException.php index 83d14b3..d20be0b 100644 --- a/src/Swift/Exception/UnsupportedCountryCodeException.php +++ b/src/Swift/Exception/UnsupportedCountryCodeException.php @@ -18,6 +18,6 @@ class UnsupportedCountryCodeException extends \RuntimeException { public function __construct(string $countryCode) { - parent::__construct(sprintf('Country with countryCode "%s" is not supported', $countryCode)); + parent::__construct(sprintf('Country with countryCode "%s" is not supported.', $countryCode)); } } diff --git a/src/Swift/RegexConverter.php b/src/Swift/RegexConverter.php index a907b23..521f580 100644 --- a/src/Swift/RegexConverter.php +++ b/src/Swift/RegexConverter.php @@ -11,6 +11,8 @@ namespace Iban\Validation\Swift; +use Iban\Validation\Swift\Exception\RegexConversionException; + /** * Converts iban and bban structure notation used in iban_registry text file provided by SWIFT to common regex. * @@ -22,11 +24,24 @@ class RegexConverter { public function convert(string $input): string { - $input = preg_replace('/(^[A-Z]{2})/', '${1}', $input); - $input = preg_replace('/(\d+)\!(n)/', '\d{${1}}', $input); - $input = preg_replace('/(\d+)\!(c)/', '[A-Z0-9]{${1}}', $input); - $input = preg_replace('/(\d+)\!(a)/', '[A-Z]{${1}}', $input); + $searchPatterns = [ + '/(^[A-Z]{2})/', + '/(\d+)\!(n)/', + '/(\d+)\!(c)/', + '/(\d+)\!(a)/', + ]; + + $replacements = [ + '${1}', + '\d{${1}}', + '[A-Z0-9]{${1}}', + '[A-Z]{${1}}', + ]; + + if (null === $convertedInput = preg_replace($searchPatterns, $replacements, $input)) { + throw new RegexConversionException($input); + } - return $input; + return $convertedInput; } } diff --git a/src/Swift/RegistryLoader.php b/src/Swift/RegistryLoader.php index 7c745b6..7acfee1 100644 --- a/src/Swift/RegistryLoader.php +++ b/src/Swift/RegistryLoader.php @@ -11,6 +11,7 @@ namespace Iban\Validation\Swift; +use Iban\Validation\Swift\Exception\RegistryLoaderException; use Symfony\Component\Yaml\Yaml; /** @@ -32,6 +33,10 @@ public function __construct(string $filename) public function load(): array { - return Yaml::parse(file_get_contents($this->filename)); + if (false === $content = file_get_contents($this->filename)) { + throw new RegistryLoaderException($this->filename); + } + + return Yaml::parse($content); } } diff --git a/src/Validator.php b/src/Validator.php index 9909bf7..ec6102b 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -74,11 +74,7 @@ class Validator public function __construct(array $options = [], Registry $swiftRegistry = null) { - $this->swiftRegistry = $swiftRegistry; - - if (null === $swiftRegistry) { - $this->swiftRegistry = new Registry(); - } + $this->swiftRegistry = $swiftRegistry ?? new Registry(); $resolver = new OptionsResolver(); $this->configureOptions($resolver); @@ -130,7 +126,7 @@ public function getViolations(): array return $this->violations; } - protected function configureOptions(OptionsResolver $resolver) + protected function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'violation.unsupported_country' => 'The requested country is not supported!', @@ -153,7 +149,7 @@ protected function validateCountryCode(Iban $iban): void /** * @throws InvalidLengthException */ - protected function validateLength(Iban $iban) + protected function validateLength(Iban $iban): void { if ((strlen($iban->getNormalizedIban()) !== $this->swiftRegistry->getIbanLength($iban->getCountryCode()))) { throw new InvalidLengthException($iban); @@ -182,7 +178,7 @@ protected function validateChecksum(Iban $iban): void if (!preg_match('/^\d+$/', $checksum)) { $validChecksumIban = $numericBban.$numericCountryCode.'00'; $validChecksum = 98 - intval($this->local_bcmod($validChecksumIban, '97')); - throw new InvalidChecksumException($iban, $validChecksum); + throw new InvalidChecksumException($iban, (string) $validChecksum); } $invertedIban = $numericBban.$numericCountryCode.$checksum; @@ -190,7 +186,7 @@ protected function validateChecksum(Iban $iban): void if ('1' !== $this->local_bcmod($invertedIban, '97')) { $validChecksumIban = $numericBban.$numericCountryCode.'00'; $validChecksum = 98 - intval($this->local_bcmod($validChecksumIban, '97')); - throw new InvalidChecksumException($iban, $validChecksum); + throw new InvalidChecksumException($iban, (string) $validChecksum); } } @@ -199,7 +195,7 @@ private function getNumericRepresentation(string $letterRepresentation): string $numericRepresentation = ''; foreach (str_split($letterRepresentation) as $char) { if (array_search($char, $this->letterMap)) { - $numericRepresentation .= array_search($char, $this->letterMap) + 9; + $numericRepresentation .= (int) array_search($char, $this->letterMap) + 9; } else { $numericRepresentation .= $char; } @@ -208,7 +204,7 @@ private function getNumericRepresentation(string $letterRepresentation): string return $numericRepresentation; } - private function local_bcmod(string $operand, string $modulus): string + private function local_bcmod(string $operand, string $modulus): ?string { if (function_exists('bcmod')) { return PHP_VERSION_ID >= 70200 @@ -220,7 +216,7 @@ private function local_bcmod(string $operand, string $modulus): string $mod = ''; do { - $a = (int) $mod.substr($operand, 0, $take); + $a = intval($mod.substr($operand, 0, $take)); $operand = substr($operand, $take); $mod = $a % $modulus; } while (strlen($operand));