diff --git a/src/Exception/InvalidBarcodeException.php b/src/Exception/InvalidBarcodeException.php index 25bd4cd..88d3a78 100644 --- a/src/Exception/InvalidBarcodeException.php +++ b/src/Exception/InvalidBarcodeException.php @@ -37,4 +37,14 @@ public static function becauseGroupSeparatorWasNotExpected(string $value): self { return new static(sprintf('Group separator was not expected in AI "%s"', $value)); } -} \ No newline at end of file + + public static function becauseValueContainsInvalidCharacters(string ...$invalidCharacters): self + { + return new static(sprintf( + 'Value contains invalid characters: %s', + implode(', ', array_map(static function (string $character) { + return "\"$character\""; + }, $invalidCharacters)) + )); + } +} diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index 5378999..481f4f8 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -13,6 +13,90 @@ */ final class Parser implements ParserInterface { + private const ENCODABLE_VALUE_CHARACTERS_SET = [ + '!', + '"', + '%', + '&', + '\'', + '(', + ')', + '*', + '+', + ',', + '-', + '_', + '.', + '/', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + ':', + ';', + '<', + '=', + '>', + '?', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + ]; private const FIXED_LENGTH_AIS = [ '00' => 20, '01' => 16, @@ -119,6 +203,8 @@ public function parse(string $data): Barcode $position += $length + strlen($this->config->getGroupSeparator()); } + $this->assertValueIsValid($value); + if ($ai) { $foundAIs[$ai] = $value; } else { @@ -168,4 +254,13 @@ private function fetchKnownAI(string $data, int $position): array return [null, null]; } -} \ No newline at end of file + + private function assertValueIsValid(string $value): void + { + $unencodableCharacters = array_diff(str_split($value), self::ENCODABLE_VALUE_CHARACTERS_SET); + + if (count($unencodableCharacters) > 0) { + throw InvalidBarcodeException::becauseValueContainsInvalidCharacters(...$unencodableCharacters); + } + } +} diff --git a/tests/Parser/ParserTest.php b/tests/Parser/ParserTest.php index bfa714f..50e012e 100644 --- a/tests/Parser/ParserTest.php +++ b/tests/Parser/ParserTest.php @@ -174,11 +174,15 @@ public function dataParsingValidCode(): array /** * @dataProvider dataParsingInvalidBarcode */ - public function testParsingInvalidBarcode(ParserConfig $config, string $data): void - { + public function testParsingInvalidBarcode( + ParserConfig $config, + string $data, + string $expectedExceptionMessage + ): void { $parser = new Parser($config); $this->expectException(InvalidBarcodeException::class); + $this->expectExceptionMessage($expectedExceptionMessage); $parser->parse($data); } @@ -192,24 +196,34 @@ public function dataParsingInvalidBarcode(): array return [ 'empty' => [ $default, - '' + '', + 'Barcode is empty', ], 'no fnc1' => [ $default, - '01034531200000111719112510ABCD1234' + '01034531200000111719112510ABCD1234', + 'FNC1 sequence is not found at the start of barcode', ], 'no data after fnc1' => [ $default, - ']d2' + ']d2', + 'Barcode does not contain data', ], 'invalid data for fixed length ai' => [ $default, - ']d2010345' + ']d2010345', + 'Not enough data for AI "01": 16 expected but 6 exists', ], 'group separator inside fixed length data' => [ $default, - "]d20103453\u{001d}200000111719112510ABCD1234" - ] + "]d20103453\u{001d}200000111719112510ABCD1234", + 'Group separator was not expected in AI "010345320000011"' + ], + 'value contains invalid characters' => [ + $default, + "]d2010 3`5'1200000111719112510ABCD123", + 'Value contains invalid characters: " ", "`"' + ], ]; } }