diff options
| author | Li Zhineng <[email protected]> | 2025-05-12 21:25:25 +0800 |
|---|---|---|
| committer | Li Zhineng <[email protected]> | 2025-05-12 21:25:25 +0800 |
| commit | 54bf2e23550c784bd650b99c4c8f7f824a5be1a5 (patch) | |
| tree | 7874f7f32750c68e3398b4e968704f45c9e179b9 | |
| parent | d07cb71f53a8344fe420e32f4664d3d92dc05a07 (diff) | |
| download | vehicle-license-china-54bf2e23550c784bd650b99c4c8f7f824a5be1a5.tar.gz vehicle-license-china-54bf2e23550c784bd650b99c4c8f7f824a5be1a5.zip | |
support large vehicle with clean energy
| -rw-r--r-- | src/RegistrationNumber.php | 68 | ||||
| -rw-r--r-- | tests/RegistrationNumberTest.php | 96 |
2 files changed, 159 insertions, 5 deletions
diff --git a/src/RegistrationNumber.php b/src/RegistrationNumber.php index 3f6edcc..3e284ed 100644 --- a/src/RegistrationNumber.php +++ b/src/RegistrationNumber.php @@ -224,6 +224,42 @@ final readonly class RegistrationNumber private static function checkSequenceForCleanEnergy(string $sequence): bool { $sequence = mb_strtoupper($sequence); + + $last = mb_substr($sequence, -1); + $codepoint = mb_ord($last); + + if (is_int($codepoint) && $codepoint >= ord('A') && $codepoint <= ord('Z')) { + return static::checkSequenceForLargeCleanEnergy($sequence); + } + + return static::checkSequenceForSmallCleanEnergy($sequence); + } + + private static function checkSequenceForLargeCleanEnergy(string $sequence): bool + { + $last = mb_substr($sequence, -1); + + if (! in_array($last, self::CLEAN_ENERGY_FIRST_LETTERS)) { + return false; + } + + for ($i = 0; $i < mb_strlen($sequence) - 1; $i++) { + $codepoint = mb_ord($sequence[$i]); + + if ($codepoint === false) { + return false; + } + + if ($codepoint < ord('0') || $codepoint > ord('9')) { + return false; + } + } + + return true; + } + + private static function checkSequenceForSmallCleanEnergy(string $sequence): bool + { $first = mb_substr($sequence, 0, 1); if (! in_array($first, self::CLEAN_ENERGY_FIRST_LETTERS)) { @@ -276,14 +312,40 @@ final readonly class RegistrationNumber return strlen($this->sequence) === 6; } - public function isBatteryElectric(): bool + public function isSmallCleanEnergy(): bool + { + if (! $this->isCleanEnergy()) { + return false; + } + + $first = mb_substr($this->sequence, 0, 1); + + return in_array($first, self::CLEAN_ENERGY_FIRST_LETTERS, strict: true); + } + + public function isLargeCleanEnergy(): bool { if (! $this->isCleanEnergy()) { return false; } - $code = mb_substr($this->sequence, 0, 1); + $last = mb_substr($this->sequence, -1); + + return in_array($last, self::CLEAN_ENERGY_FIRST_LETTERS, strict: true); + } + + public function isBatteryElectric(): bool + { + $letter = match (true) { + $this->isSmallCleanEnergy() => mb_substr($this->sequence, 0, 1), + $this->isLargeCleanEnergy() => mb_substr($this->sequence, -1), + default => false, + }; + + if ($letter === false) { + return false; + } - return in_array($code, self::BATTERY_ELECTRIC_LETTERS); + return in_array($letter, self::BATTERY_ELECTRIC_LETTERS, strict: true); } } diff --git a/tests/RegistrationNumberTest.php b/tests/RegistrationNumberTest.php index fcf5f33..5eb9f28 100644 --- a/tests/RegistrationNumberTest.php +++ b/tests/RegistrationNumberTest.php @@ -15,6 +15,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')] public function test_valid_registration_number(string $registrationNumber): void { $instance = RegistrationNumber::make($registrationNumber); @@ -23,6 +24,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')] public function test_invalid_registration_number(string $registrationNumber): void { $this->expectException(RegistrationNumberException::class); @@ -36,13 +38,14 @@ final class RegistrationNumberTest extends TestCase $this->assertFalse(RegistrationNumber::make('粤ED1234')->isCleanEnergy()); } - public function test_is_battery_electric_determines_if_vehicle_is_a_fully_electric_vehicle(): void + public function test_is_battery_electric_determines_if_small_vehicle_is_a_fully_electric_vehicle(): void { $this->assertTrue(RegistrationNumber::make('粤ED12345')->isBatteryElectric()); $this->assertTrue(RegistrationNumber::make('粤EA12345')->isBatteryElectric()); $this->assertTrue(RegistrationNumber::make('粤EB12345')->isBatteryElectric()); $this->assertTrue(RegistrationNumber::make('粤EC12345')->isBatteryElectric()); $this->assertTrue(RegistrationNumber::make('粤EE12345')->isBatteryElectric()); + $this->assertFalse(RegistrationNumber::make('粤EF12345')->isBatteryElectric()); $this->assertFalse(RegistrationNumber::make('粤EG12345')->isBatteryElectric()); $this->assertFalse(RegistrationNumber::make('粤EH12345')->isBatteryElectric()); @@ -50,6 +53,59 @@ final class RegistrationNumberTest extends TestCase $this->assertFalse(RegistrationNumber::make('粤EK12345')->isBatteryElectric()); } + public function test_is_battery_electric_determines_if_large_vehicle_is_a_fully_electric_vehicle(): void + { + $this->assertTrue(RegistrationNumber::make('粤E12345D')->isBatteryElectric()); + $this->assertTrue(RegistrationNumber::make('粤E12345A')->isBatteryElectric()); + $this->assertTrue(RegistrationNumber::make('粤E12345B')->isBatteryElectric()); + $this->assertTrue(RegistrationNumber::make('粤E12345C')->isBatteryElectric()); + $this->assertTrue(RegistrationNumber::make('粤E12345E')->isBatteryElectric()); + + $this->assertFalse(RegistrationNumber::make('粤E12345F')->isBatteryElectric()); + $this->assertFalse(RegistrationNumber::make('粤E12345G')->isBatteryElectric()); + $this->assertFalse(RegistrationNumber::make('粤E12345H')->isBatteryElectric()); + $this->assertFalse(RegistrationNumber::make('粤E12345J')->isBatteryElectric()); + $this->assertFalse(RegistrationNumber::make('粤E12345K')->isBatteryElectric()); + } + + public function test_is_small_clean_energy_determines_if_vehicle_is_small_vehicle_with_clean_energy(): void + { + $this->assertTrue(RegistrationNumber::make('粤ED12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EA12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EB12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EC12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EE12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EF12345')->isSmallCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤EG12345')->isSmallCleanEnergy()); + + $this->assertFalse(RegistrationNumber::make('粤E12345D')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345A')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345B')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345C')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345E')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345F')->isSmallCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤E12345G')->isSmallCleanEnergy()); + } + + public function test_is_large_clean_energy_determines_if_vehicle_is_large_vehicle_with_clean_energy(): void + { + $this->assertTrue(RegistrationNumber::make('粤E12345D')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345A')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345B')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345C')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345E')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345F')->isLargeCleanEnergy()); + $this->assertTrue(RegistrationNumber::make('粤E12345G')->isLargeCleanEnergy()); + + $this->assertFalse(RegistrationNumber::make('粤ED12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EA12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EB12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EC12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EE12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EF12345')->isLargeCleanEnergy()); + $this->assertFalse(RegistrationNumber::make('粤EG12345')->isLargeCleanEnergy()); + } + /** * @return string[] */ @@ -86,7 +142,7 @@ final class RegistrationNumberTest extends TestCase } /** - * @return string[] + * @return string[][] */ public static function provide_valid_clean_energy_registration_numbers(): array { @@ -106,6 +162,28 @@ final class RegistrationNumberTest extends TestCase } /** + * @return string[][] + */ + public static function provide_valid_large_clean_energy_vehicle_registration_numbers(): array + { + return [ + // Battery electric vehicle + ['粤E12345D'], + ['粤E12345A'], + ['粤E12345B'], + ['粤E12345C'], + ['粤E12345E'], + + // Non-battery electric vehicle + ['粤E12345F'], + ['粤E12345G'], + ['粤E12345H'], + ['粤E12345J'], + ['粤E12345K'], + ]; + } + + /** * @return string[] */ public static function provide_invalid_registration_numbers(): array @@ -158,4 +236,18 @@ final class RegistrationNumberTest extends TestCase ['粤EDI1234'], ]; } + + /** + * @return string[][] + */ + public static function provide_invalid_large_clean_energy_vehicle_registration_numbers(): array + { + return [ + ['粤E1234DD'], + ['粤E123D4D'], + ['粤E12D34D'], + ['粤E1D234D'], + ['粤ED1234D'], + ]; + } } |
