diff --git a/ruleset.xml b/ruleset.xml index bf502b2..c5eeec1 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -6,7 +6,7 @@ - + diff --git a/src/Personnummer.php b/src/Personnummer.php index 604f385..7d3d7e8 100644 --- a/src/Personnummer.php +++ b/src/Personnummer.php @@ -21,18 +21,12 @@ */ final class Personnummer implements PersonnummerInterface { - private $parts; + private array $parts; - private $options; + private array $options; /** - * - * @param string $ssn - * @param array $options - * - * @return PersonnummerInterface - * - * @throws PersonnummerException + * @inheritDoc */ public static function parse(string $ssn, array $options = []): PersonnummerInterface { @@ -40,22 +34,19 @@ public static function parse(string $ssn, array $options = []): PersonnummerInte } /** - * Check if a Swedish social security number is for a male. - * - * @return bool + * @inheritDoc */ public function isMale(): bool { $parts = $this->parts; $genderDigit = substr($parts['num'], -1); - return boolval($genderDigit % 2); + return (bool)($genderDigit % 2); } + /** - * Check if a Swedish social security number is for a female. - * - * @return bool + * @inheritDoc */ public function isFemale(): bool { @@ -63,14 +54,7 @@ public function isFemale(): bool } /** - * Format a Swedish social security/coordination number as one of the official formats, - * A long format or a short format. - * - * If the input number could not be parsed an empty string will be returned. - * - * @param bool $longFormat short format YYMMDD-XXXX or long YYYYMMDDXXXX since the tax office says both are official - * - * @return string + * @inheritDoc */ public function format(bool $longFormat = false): string { @@ -94,18 +78,24 @@ public function format(bool $longFormat = false): string ); } + /** + * @inheritDoc + */ public function isCoordinationNumber(): bool { $parts = $this->parts; - return checkdate(intval($parts['month']), $parts['day'] - 60, $parts['fullYear']); + return checkdate((int)$parts['month'], $parts['day'] - 60, $parts['fullYear']); } + /** + * @inheritDoc + */ public static function valid(string $ssn, array $options = []): bool { try { return self::parse($ssn, $options)->isValid(); - } catch (PersonnummerException $exception) { + } catch (PersonnummerException) { return false; } } @@ -132,7 +122,7 @@ private static function getParts(string $ssn): array $parts = array_filter($match, 'is_string', ARRAY_FILTER_USE_KEY); if (!empty($parts['century'])) { - if (date('Y') - intval(strval($parts['century']) . strval($parts['year'])) < 100) { + if (date('Y') - (int)((string)$parts['century'] . (string)$parts['year']) < 100) { $parts['sep'] = '-'; } else { $parts['sep'] = '+'; @@ -163,8 +153,9 @@ private static function luhn(string $str): int { $sum = 0; - for ($i = 0; $i < strlen($str); $i++) { - $v = intval($str[$i]); + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $v = (int)$str[$i]; $v *= 2 - ($i % 2); if ($v > 9) { @@ -174,7 +165,7 @@ private static function luhn(string $str): int $sum += $v; } - return intval(ceil($sum / 10) * 10 - $sum); + return (int)(ceil($sum / 10) * 10 - $sum); } /** @@ -206,7 +197,7 @@ public function getAge(): int { $parts = $this->parts; - $day = intval($parts['day']); + $day = (int)$parts['day']; if ($this->isCoordinationNumber()) { $day -= 60; } @@ -257,7 +248,7 @@ private function isValid(): bool } $checkStr = $parts['year'] . $parts['month'] . $parts['day'] . $parts['num']; - $validCheck = self::luhn($checkStr) === intval($parts['check']); + $validCheck = self::luhn($checkStr) === (int)$parts['check']; return $validDate && $validCheck; } diff --git a/src/PersonnummerException.php b/src/PersonnummerException.php index 347d859..dcfa786 100644 --- a/src/PersonnummerException.php +++ b/src/PersonnummerException.php @@ -14,9 +14,9 @@ class PersonnummerException extends Exception * @param null|Exception $previous */ public function __construct( - $message = 'Invalid swedish social security number', - $code = 400, - $previous = null + string $message = 'Invalid swedish social security number', + int $code = 400, + ?Exception $previous = null ) { parent::__construct($message, $code, $previous); } diff --git a/src/PersonnummerInterface.php b/src/PersonnummerInterface.php index 7e1822e..c41a3d6 100644 --- a/src/PersonnummerInterface.php +++ b/src/PersonnummerInterface.php @@ -2,6 +2,8 @@ namespace Personnummer; +use Exception; + /** * Interface PersonnummerInterface * @@ -18,17 +20,66 @@ */ interface PersonnummerInterface { + /** + * Parse a string representation of a Swedish social security number. + * + * @param string $ssn Social Security number to parse. + * @param array $options Parse options. + * @return PersonnummerInterface + * @throws PersonnummerException + */ public static function parse(string $ssn, array $options = []): self; + /** + * Test a string representation of a Swedish social security number to see + * if it's valid. + * + * @param string $ssn Social Security number to parse. + * @param array $options Parse options. + * @return bool + */ public static function valid(string $ssn, array $options = []): bool; + /** + * Format a Swedish social security/coordination number as one of the official formats, + * A long format or a short format. + * + * If the input number could not be parsed an empty string will be returned. + * + * @param bool $longFormat short format YYMMDD-XXXX or long YYYYMMDDXXXX since the tax office says both are official + * @return string + */ public function format(bool $longFormat = false): string; + /** + * Check if a Swedish social security number is for a female. + * + * @return bool + */ public function isFemale(): bool; + /** + * Check if a Swedish social security number is for a male. + * + * @return bool + */ public function isMale(): bool; + /** + * Check if the Swedish social security number is a coordination number. + * + * @return bool + */ public function isCoordinationNumber(): bool; public function __construct(string $ssn, array $options = []); + + /** + * Get age from a Swedish social security/coordination number. + * + * @return int + * + * @throws Exception When date is invalid or problems with DateTime library + */ + public function getAge(): int; } diff --git a/tests/AssertError.php b/tests/AssertError.php index 7fdcff7..61e158f 100644 --- a/tests/AssertError.php +++ b/tests/AssertError.php @@ -6,7 +6,7 @@ trait AssertError { - private $errors; + private array $errors; /** * AssertError. @@ -37,9 +37,10 @@ public function assertError( $callable(); restore_error_handler(); - $comparisons = array_filter(compact('error_type', 'error_msg', 'error_file', 'error_line'), function ($value) { - return !is_null($value); - }); + $comparisons = array_filter( + compact('error_type', 'error_msg', 'error_file', 'error_line'), + static fn ($value) => !is_null($value), + ); $matchingErrors = []; foreach ($this->errors as $error) { @@ -51,7 +52,7 @@ public function assertError( if (empty($matchingErrors)) { $failMessage = 'Expected error was not found'; $failMessage .= $comparisons ? ': ' : ''; - $failMessage .= implode(', ', array_map(function ($value, $key) { + $failMessage .= implode(', ', array_map(static function ($value, $key) { return $key . ': ' . $value; }, $comparisons, array_keys($comparisons))); diff --git a/tests/PersonnummerTest.php b/tests/PersonnummerTest.php index d804751..29fcba3 100644 --- a/tests/PersonnummerTest.php +++ b/tests/PersonnummerTest.php @@ -15,11 +15,11 @@ class PersonnummerTest extends TestCase use AssertThrows; use AssertError; - private static $testdataList; + private static array $testdataList; - private static $testdataStructured; + private static array $testdataStructured; - private static $availableListFormats = [ + private static array $availableListFormats = [ 'integer', 'long_format', 'short_format', @@ -29,17 +29,17 @@ class PersonnummerTest extends TestCase public static function setUpBeforeClass(): void { - self::$testdataList = json_decode(file_get_contents('https://raw.githubusercontent.com/personnummer/meta/master/testdata/list.json'), true); // phpcs:ignore - self::$testdataStructured = json_decode(file_get_contents('https://raw.githubusercontent.com/personnummer/meta/master/testdata/structured.json'), true); // phpcs:ignore + self::$testdataList = json_decode(file_get_contents('https://raw.githubusercontent.com/personnummer/meta/master/testdata/list.json'), true, 512, JSON_THROW_ON_ERROR); // phpcs:ignore + self::$testdataStructured = json_decode(file_get_contents('https://raw.githubusercontent.com/personnummer/meta/master/testdata/structured.json'), true, 512, JSON_THROW_ON_ERROR); // phpcs:ignore } - public function testParse() + public function testParse(): void { $this->assertSame(Personnummer::class, get_class(Personnummer::parse('1212121212'))); $this->assertEquals(new Personnummer('1212121212'), Personnummer::parse('1212121212')); } - public function testOptions() + public function testOptions(): void { new Personnummer('1212621211'); @@ -51,7 +51,7 @@ public function testOptions() }, E_USER_WARNING); } - public function testPersonnummerData() + public function testPersonnummerData(): void { foreach (self::$testdataList as $testdata) { foreach (self::$availableListFormats as $format) { @@ -69,7 +69,7 @@ public function testPersonnummerData() } foreach (self::$testdataStructured as $ssnType => $testdataInputs) { - foreach ($testdataInputs as $testdataType => $testdata) { + foreach ($testdataInputs as $testdata) { foreach ($testdata as $valid => $ssns) { foreach ($ssns as $ssn) { $this->assertSame( @@ -87,12 +87,12 @@ public function testPersonnummerData() } } - public function testFormat() + public function testFormat(): void { foreach (self::$testdataList as $testdata) { if ($testdata['valid']) { foreach (self::$availableListFormats as $format) { - if ($format === 'short_format' && strpos($testdata['separated_format'], '+') !== false) { + if ($format === 'short_format' && str_contains($testdata['separated_format'], '+')) { continue; } @@ -104,7 +104,7 @@ public function testFormat() } } - public function testThrowsErrorOnInvalid() + public function testThrowsErrorOnInvalid(): void { foreach (self::$testdataList as $testdata) { if (!$testdata['valid']) { @@ -144,20 +144,20 @@ public function testThrowsErrorOnInvalid() } } - public function testAge() + public function testAge(): void { foreach (self::$testdataList as $testdata) { if ($testdata['valid']) { $birthdate = substr($testdata['separated_long'], 0, 8); if ($testdata['type'] === 'con') { $birthdate = substr($birthdate, 0, 6) . - str_pad(intval(substr($birthdate, -2)) - 60, 2, "0", STR_PAD_LEFT); + str_pad((int)substr($birthdate, -2) - 60, 2, "0", STR_PAD_LEFT); } - $expected = intval((new DateTime($birthdate))->diff(new DateTime())->format('%y')); + $expected = (int)(new DateTime($birthdate))->diff(new DateTime())->format('%y'); foreach (self::$availableListFormats as $format) { - if ($format === 'short_format' && strpos($testdata['separated_format'], '+') !== false) { + if ($format === 'short_format' && str_contains($testdata['separated_format'], '+')) { continue; } @@ -167,10 +167,10 @@ public function testAge() } } - public function testAgeOnBirthday() + public function testAgeOnBirthday(): void { $date = (new DateTime())->modify('-30 years midnight'); - $expected = intval($date->diff(new DateTime())->format('%y')); + $expected = (int)$date->diff(new DateTime())->format('%y'); $ssn = $date->format('Ymd') . '999'; @@ -183,7 +183,7 @@ public function testAgeOnBirthday() $this->assertSame($expected, Personnummer::parse($ssn)->getAge()); } - public function testSex() + public function testSex(): void { foreach (self::$testdataList as $testdata) { if ($testdata['valid']) { @@ -195,7 +195,7 @@ public function testSex() } } - public function testProperties() + public function testProperties(): void { // Parts, as position and length $separatedLongParts = [ @@ -220,7 +220,7 @@ public function testProperties() } } - public function testMissingProperties() + public function testMissingProperties(): void { $this->assertError(function () { Personnummer::parse('1212121212')->missingProperty;