-
-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
129 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Intervention\Validation\Rules; | ||
|
||
use Intervention\Validation\AbstractRegexRule; | ||
|
||
class Grid extends AbstractRegexRule | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
* | ||
* @see AbstractRegexRule::pattern() | ||
*/ | ||
protected function pattern(): string | ||
{ | ||
return "/^(GRID:)?(?P<grid>A1-?[A-Z0-9]{5}-?[A-Z0-9]{10}-?[A-Z0-9])$/"; | ||
} | ||
|
||
/** | ||
* Determine if the validation rule passes. | ||
* | ||
* @param mixed $value | ||
* @return bool | ||
*/ | ||
public function isValid(mixed $value): bool | ||
{ | ||
return is_string($value) | ||
&& parent::isValid(strtoupper($value)) | ||
&& $this->hasValidChecksum($value); | ||
} | ||
|
||
/** | ||
* Calculate checksum from given grid number | ||
* | ||
* @param string $value | ||
* @return bool | ||
*/ | ||
private function hasValidChecksum(string $value): bool | ||
{ | ||
// get GRid from value | ||
preg_match($this->pattern(), strtoupper($value), $matches); | ||
|
||
// split GRid into single characters (without dashes) | ||
$characters = str_split( | ||
str_replace('-', '', $matches['grid']) | ||
); | ||
|
||
// extract last (check) character | ||
$checkCharacter = array_pop($characters); | ||
|
||
$m = 36; | ||
$n = 37; | ||
$product = $m; | ||
$sum = 0; | ||
|
||
// calculate checksum | ||
foreach ($characters as $char) { | ||
$sum = ($product + $this->charValue($char)) % $m; | ||
$sum = $sum === 0 ? $m : $sum; | ||
$product = ($sum * 2) % $n; | ||
} | ||
|
||
// compare checksum to check character value | ||
return $n - $product === $this->charValue($checkCharacter); | ||
} | ||
|
||
/** | ||
* Get character value according to GRid standard v2.1 | ||
* | ||
* @param string $char | ||
* @return int | ||
*/ | ||
private function charValue(string $char): int | ||
{ | ||
return is_numeric($char) ? (int) $char : (int) str_replace(range('A', 'Z'), range(10, 35), strtoupper($char)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Intervention\Validation\Tests\Rules; | ||
|
||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Intervention\Validation\Rules\Grid; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class GridTest extends TestCase | ||
{ | ||
#[DataProvider('dataProvider')] | ||
public function testValidation($result, $value): void | ||
{ | ||
$valid = (new Grid())->isValid($value); | ||
$this->assertEquals($result, $valid); | ||
} | ||
|
||
public static function dataProvider(): array | ||
{ | ||
return [ | ||
[true, 'A12425GABC1234002M'], | ||
[true, 'a12425gabc1234002m'], // lowercase valid | ||
[true, 'GRid:A12425GABC1234002M'], | ||
[true, 'GRID:A12425GABC1234002M'], | ||
[true, 'A1-2425G-ABC1234002-M'], | ||
[true, 'A1-2425GABC1234002M'], // only one dash valid | ||
[true, 'GRid:A1-2425G-ABC1234002-M'], | ||
[true, 'GRID:A1-2425G-ABC1234002-M'], | ||
[true, 'A11244BC12345678DP'], | ||
[true, 'A1-1244B-C12345678D-P'], | ||
[true, 'GRid:A11244BC12345678DP'], | ||
[true, 'GRID:A11244BC12345678DP'], | ||
[true, 'GRid:A1-1244B-C12345678D-P'], | ||
[true, 'GRID:A1-1244B-C12345678D-P'], | ||
[false, 'FOO:A1-1244B-C12345678D-P'], // false prefix | ||
[false, 'B1-1244B-C12345678D-P'], // false Identifier Scheme element | ||
[false, 'A1-1244B-C12345678D-A'], // false Check Character | ||
[false, 'ZA9382189201'], // no grid | ||
[false, ''], // empty | ||
]; | ||
} | ||
} |