diff options
| author | Li Zhineng <[email protected]> | 2025-05-13 10:32:08 +0800 |
|---|---|---|
| committer | Li Zhineng <[email protected]> | 2025-05-13 10:32:08 +0800 |
| commit | f451ac56a4db706fc3f45410be32d35201c8d431 (patch) | |
| tree | fd854f38b7acccb84efa9d29837ce5d72ed11462 | |
| parent | 54bf2e23550c784bd650b99c4c8f7f824a5be1a5 (diff) | |
| download | vehicle-license-china-f451ac56a4db706fc3f45410be32d35201c8d431.tar.gz vehicle-license-china-f451ac56a4db706fc3f45410be32d35201c8d431.zip | |
support suffix
| -rw-r--r-- | src/RegistrationNumber.php | 98 | ||||
| -rw-r--r-- | tests/RegistrationNumberTest.php | 37 |
2 files changed, 116 insertions, 19 deletions
diff --git a/src/RegistrationNumber.php b/src/RegistrationNumber.php index 3e284ed..a94c21b 100644 --- a/src/RegistrationNumber.php +++ b/src/RegistrationNumber.php @@ -141,6 +141,24 @@ final readonly class RegistrationNumber 'D', 'A', 'B', 'C', 'E', ]; + /** + * @var string[] + */ + private const array SPECIAL_SUFFIXS = [ + '领', '使', '警', '学', '挂', '港', '澳', '试', '超', + ]; + + /** + * @var string[] + */ + private const array GUANGDONG_Z_SPECIAL_SUFFIXS = [ + '港', '澳', + ]; + + private const string GUANGDONG_PROVINCE = '粤'; + + private const string GUANGDONG_SPECIAL_AUTHORITY = 'Z'; + public string $region; public string $authority; @@ -155,45 +173,87 @@ final readonly class RegistrationNumber public static function make(string $registrationNumber): static { - static::validateAuthority($registrationNumber); - static::validateSequence($registrationNumber); + [$region, $authority, $sequence, $suffix] = static::extractComponents($registrationNumber); + + if (! static::checkAuthority($region, $authority)) { + static::notValid($registrationNumber); + } + + if (! static::checkSequence($sequence)) { + static::notValid($registrationNumber); + } + + if (! static::checkSuffix($region, $authority, $sequence, $suffix)) { + static::notValid($registrationNumber); + } return new self($registrationNumber); } - private static function validateAuthority(string $registrationNumber): void + /** + * @return string[] + */ + private static function extractComponents(string $registrationNumber): array + { + $normalized = mb_strtoupper($registrationNumber); + + $region = mb_substr($normalized, 0, 1); + $authority = mb_substr($normalized, 1, 1); + + $last = mb_substr($normalized, -1); + + $suffix = in_array($last, self::SPECIAL_SUFFIXS, strict: true) + ? $last + : ''; + + $sequence = $suffix === '' + ? mb_substr($normalized, 2) + : mb_substr($normalized, 2, -1); + + return [ + $region, $authority, $sequence, $suffix, + ]; + } + + private static function checkAuthority(string $region, string $authority): bool { - $region = mb_substr($registrationNumber, 0, 1); $authorities = self::AUTHORITIES[$region] ?? []; if ($authorities === []) { - static::notValid($registrationNumber); + return false; } - $code = mb_substr($registrationNumber, 1, 1); - - if ($code === 'O') { - return; + if ($authority === 'O') { + return true; } - if (! in_array($code, $authorities)) { - static::notValid($registrationNumber); - } + return in_array($authority, $authorities, strict: true); } - private static function validateSequence(string $registrationNumber): void + private static function checkSequence(string $sequence): bool { - $sequence = mb_strtoupper(mb_substr($registrationNumber, 2)); - - $valid = match (mb_strlen($sequence)) { + return match (mb_strlen($sequence)) { 4, 5 => static::checkSequenceForGeneral($sequence), 6 => static::checkSequenceForCleanEnergy($sequence), - default => static::notValid($registrationNumber), + default => false, }; + } - if (! $valid) { - static::notValid($registrationNumber); + private static function checkSuffix( + string $region, string $authority, string $sequence, string $suffix + ): bool { + if (in_array($suffix, self::GUANGDONG_Z_SPECIAL_SUFFIXS, strict: true)) { + return $region === self::GUANGDONG_PROVINCE + && $authority === self::GUANGDONG_SPECIAL_AUTHORITY; } + + if ($suffix !== '' && ! in_array($suffix, self::SPECIAL_SUFFIXS, strict: true)) { + $len = mb_strlen($sequence); + + return $len >= 4 && $len <= 5; + } + + return true; } private static function checkSequenceForGeneral(string $sequence): bool diff --git a/tests/RegistrationNumberTest.php b/tests/RegistrationNumberTest.php index 5eb9f28..53a0ff9 100644 --- a/tests/RegistrationNumberTest.php +++ b/tests/RegistrationNumberTest.php @@ -16,6 +16,7 @@ final class RegistrationNumberTest extends TestCase #[DataProvider('provide_valid_registration_numbers')] #[DataProvider('provide_valid_clean_energy_registration_numbers')] #[DataProvider('provide_valid_large_clean_energy_vehicle_registration_numbers')] + #[DataProvider('provide_valid_special_registration_numbers')] public function test_valid_registration_number(string $registrationNumber): void { $instance = RegistrationNumber::make($registrationNumber); @@ -25,6 +26,7 @@ final class RegistrationNumberTest extends TestCase #[DataProvider('provide_invalid_registration_numbers')] #[DataProvider('provide_invalid_clean_energy_registration_numbers')] #[DataProvider('provide_invalid_large_clean_energy_vehicle_registration_numbers')] + #[DataProvider('provide_invalid_special_registration_numbers')] public function test_invalid_registration_number(string $registrationNumber): void { $this->expectException(RegistrationNumberException::class); @@ -250,4 +252,39 @@ final class RegistrationNumberTest extends TestCase ['粤ED1234D'], ]; } + + /** + * @return string[][] + */ + public static function provide_valid_special_registration_numbers(): array + { + return [ + ['京A0006警'], + ['京A00006警'], + ['粤E0000学'], + ['粤E00000学'], + ['粤E0000挂'], + ['粤E00000挂'], + ['粤Z0000港'], + ['粤Z00000港'], + ['粤Z0000澳'], + ['粤Z00000澳'], + ['粤Z0000试'], + ['粤Z00000试'], + ['粤Z0000超'], + ['粤Z00000超'], + ]; + } + + /** + * @return string[][] + */ + public static function provide_invalid_special_registration_numbers(): array + { + return [ + ['粤E0006假'], + ['粤E0006港'], + ['粤E0000澳'], + ]; + } } |
