diff --git a/composer.json b/composer.json index 03ffdc3..4f4d0ec 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ } ], "require": { - "php": ">=7.3.0" + "php": ">=7.3.0", + "ext-calendar": "*" }, "require-dev": { "phpunit/phpunit": "^9.3.8", diff --git a/src/Exceptions/InvalidBirthDateException.php b/src/Exceptions/InvalidBirthDateException.php new file mode 100644 index 0000000..20d9cc5 --- /dev/null +++ b/src/Exceptions/InvalidBirthDateException.php @@ -0,0 +1,7 @@ + 'Nieprawidłowa długość numeru PESEL.', 'invalidCharacters' => 'Numer PESEL może zawierać tylko cyfry.', 'invalidChecksum' => 'Numer PESEL posiada niepoprawną sumę kontrolną.', + 'invalidBirthDate' => 'Numer PESEL zawiera nieprawidłową datę urodzenia.', ]; /** @@ -75,6 +77,8 @@ public function __construct($number, $errorMessages = []) $this->validateDigitsOnly(); $this->validateChecksum(); + + $this->validateBirthDateDigits(); } /** @@ -124,28 +128,7 @@ public function getNumber() */ public function getBirthDate() { - $year = substr($this->number, 0, 2); - $month = substr($this->number, 2, 2); - $day = substr($this->number, 4, 2); - - // 0 - 9 - $century = substr($this->number, 2, 1); - - // 2,3,4,5,6,7,8,9,10,11 - $century += 2; - - // 2,3,4,5,6,7,8,9,0,1 - $century %= 10; - - // 1,1,2,2,3,3,4,4,0,0 - $century = round($century / 2, 0, PHP_ROUND_HALF_DOWN); - - // 19,19,20,20,21,21,22,22,18,18 - $century += 18; - - $year = $century.$year; - - $month = str_pad($month % 20, 2, '0', STR_PAD_LEFT); + [$year, $month, $day] = $this->getYearMonthDate(); return DateTime::createFromFormat('Y-m-d H:i:s', "$year-$month-$day 00:00:00"); } @@ -234,6 +217,26 @@ protected function validateChecksum() } } + /** + * @throws InvalidArgumentException on invalid birth date digits + */ + protected function validateBirthDateDigits() + { + [$year, $month, $day] = $this->getYearMonthDate(); + + if ($year < 1800 || $year > 2299) { + throw new InvalidBirthDateException($this->errorMessages['invalidBirthDate']); + } + + if ($month < 1 || $month > 12) { + throw new InvalidBirthDateException($this->errorMessages['invalidBirthDate']); + } + + if ($day < 1 || $day > cal_days_in_month(CAL_GREGORIAN, $month, $year)) { + throw new InvalidBirthDateException($this->errorMessages['invalidBirthDate']); + } + } + /** * Check if provided gender matches accepted format. * @@ -251,4 +254,32 @@ protected static function validateGenderInput($gender) throw new InvalidGenderInputException('Podano płeć w niepoprawnym formacie'); } } + + protected function getYearMonthDate() + { + $year = substr($this->number, 0, 2); + $month = substr($this->number, 2, 2); + $day = substr($this->number, 4, 2); + + // 0 - 9 + $century = substr($this->number, 2, 1); + + // 2,3,4,5,6,7,8,9,10,11 + $century += 2; + + // 2,3,4,5,6,7,8,9,0,1 + $century %= 10; + + // 1,1,2,2,3,3,4,4,0,0 + $century = round($century / 2, 0, PHP_ROUND_HALF_DOWN); + + // 19,19,20,20,21,21,22,22,18,18 + $century += 18; + + $year = $century.$year; + + $month = str_pad($month % 20, 2, '0', STR_PAD_LEFT); + + return [$year, $month, $day]; + } } diff --git a/tests/PeselTest.php b/tests/PeselTest.php index 9ba926f..74bbb18 100644 --- a/tests/PeselTest.php +++ b/tests/PeselTest.php @@ -179,6 +179,18 @@ public function testCustomInvalidChecksumMessage() new Pesel('11111111111', $errorMessages); } + public function testCustomInvalidBirthDateMessage() + { + $errorMessages = [ + 'invalidBirthDate' => 'invalidBirthDate', + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage($errorMessages['invalidBirthDate']); + + new Pesel('44444444444', $errorMessages); + } + public function invalidNumberDataProvider() { return [ @@ -190,6 +202,8 @@ public function invalidNumberDataProvider() ['96100612532'], ['61122500187'], ['78091501150'], + ['00000000000'], + ['44444444444'], ]; }