Added Finnish personal identity code rule and tests.

This commit is contained in:
Ville Hukkamäki 2024-03-12 22:36:45 +02:00
parent 52b75bc1ed
commit 35d8d2e0c7
No known key found for this signature in database
2 changed files with 135 additions and 0 deletions

69
library/Rules/Hetu.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/*
* Copyright (c) Ville Hukkamäki <vhukkamaki@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Rules\Core\Simple;
use function is_string;
use function preg_match;
use function strtoupper;
/**
* @see https://en.wikipedia.org/wiki/National_identification_number#Finland
*/
#[Template(
'{{name}} must be a valid Finnish personal identity code',
'{{name}} must not be a valid Finnish personal identity code',
)]
final class Hetu extends Simple
{
public function validate(mixed $input): bool
{
if (!is_string($input)) {
return false;
}
$input = strtoupper($input);
if (!preg_match('/^(\d{2})(\d{2})(\d{2})([+\-A-FU-Y])(\d{3})([0-9A-FHJ-NPR-Y])$/', $input, $m)) {
return false;
}
$century = match ($m[4]) {
'+' => '18',
'-','U','V','W','X','Y' => '19',
'A','B','C','D','E','F' => '20',
default => null,
};
if ($century === null) {
return false;
}
$dateRule = new Date();
if (!$dateRule->evaluate($century . $m[3] . '-' . $m[2] . '-' . $m[1])->isValid) {
return false;
}
return $this->getChecksum($m[1] . $m[2] . $m[3] . $m[5]) === $m[6];
}
private function getChecksum(string $s): string
{
$mod = (int)$s % 31;
$tab = [
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','H','J','K','L','M','N','P',
'R','S','T','U','C','W','X','Y',
];
return $tab[$mod];
}
}

View file

@ -0,0 +1,66 @@
<?php
/*
* Copyright (c) Ville Hukkamäki <vhukkamaki@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use Respect\Validation\Test\RuleTestCase;
#[Group('rule')]
#[CoversClass(Hetu::class)]
final class HetuTest extends RuleTestCase
{
/** @return iterable<array{Hetu, mixed}> */
public static function providerForValidInput(): iterable
{
$rule = new Hetu();
return [
[$rule, '010106A9012'],
[$rule, '290199-907A'],
[$rule, '010199+9012'],
[$rule, '280291+913L'],
[$rule, '280291+923X'],
];
}
/** @return iterable<array{Hetu, mixed}> */
public static function providerForInvalidInput(): iterable
{
$rule = new Hetu();
return [
[$rule, '010106_9012'],
[$rule, '010106G9012'],
[$rule, '010106Z9012'],
[$rule, '010106A901G'],
[$rule, '010106A901I'],
[$rule, '010106A901O'],
[$rule, '010106A901Q'],
[$rule, '010106A901Z'],
[$rule, '010106!9012'],
[$rule, '010106'],
[$rule, '01X199+9012'],
[$rule, '01X199Z9012'],
[$rule, '01X199T9012'],
[$rule, '999999A9999'],
[$rule, '999999A999F'],
[$rule, '300201A1236'],
[$rule, '290201A123J'],
[$rule, null],
[$rule, []],
[$rule, false],
[$rule, 42],
[$rule, '127.0.0.1'],
[$rule, new DateTime()],
];
}
}