mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-30 12:07:32 +00:00
🛀 QRDataModeInterface: validate data on init, test cleanup
This commit is contained in:
+3
-17
@@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
|
||||
use function array_flip, ceil, ord, sprintf, str_split;
|
||||
use function array_flip, ceil, str_split;
|
||||
|
||||
/**
|
||||
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
|
||||
@@ -75,30 +75,16 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
|
||||
// encode 2 characters in 11 bits
|
||||
for($i = 0; $i + 1 < $len; $i += 2){
|
||||
$bitBuffer->put($this->getCharCode($this->data[$i]) * 45 + $this->getCharCode($this->data[$i + 1]), 11);
|
||||
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[$i + 1]], 11);
|
||||
}
|
||||
|
||||
// encode a remaining character in 6 bits
|
||||
if($i < $len){
|
||||
$bitBuffer->put($this->getCharCode($this->data[$i]), 6);
|
||||
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the code for the given character
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
|
||||
*/
|
||||
private function getCharCode(string $chr):int{
|
||||
|
||||
if(isset(self::CHAR_TO_ORD[$chr])){
|
||||
return self::CHAR_TO_ORD[$chr];
|
||||
}
|
||||
|
||||
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
|
||||
use function ord;
|
||||
use function chr, ord;
|
||||
|
||||
/**
|
||||
* Byte mode, ISO-8859-1 or UTF-8
|
||||
@@ -76,7 +76,7 @@ final class Byte extends QRDataModeAbstract{
|
||||
$readBytes = '';
|
||||
|
||||
for($i = 0; $i < $length; $i++){
|
||||
$readBytes .= \chr($bitBuffer->read(8));
|
||||
$readBytes .= chr($bitBuffer->read(8));
|
||||
}
|
||||
|
||||
return $readBytes;
|
||||
|
||||
@@ -57,6 +57,10 @@ final class Kanji extends QRDataModeAbstract{
|
||||
$i = 0;
|
||||
$len = strlen($string);
|
||||
|
||||
if($len < 2){
|
||||
return false;
|
||||
}
|
||||
|
||||
while($i + 1 < $len){
|
||||
$c = ((0xff & ord($string[$i])) << 8) | (0xff & ord($string[$i + 1]));
|
||||
|
||||
|
||||
+2
-4
@@ -98,14 +98,12 @@ final class Number extends QRDataModeAbstract{
|
||||
$num = 0;
|
||||
|
||||
foreach(str_split($string) as $chr){
|
||||
$c = ord($chr);
|
||||
|
||||
if(!isset(self::NUMBER_TO_ORD[$chr])){
|
||||
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
|
||||
throw new QRCodeDataException(sprintf('illegal char: "%s"', $chr));
|
||||
}
|
||||
|
||||
$c = $c - 48; // ord('0')
|
||||
$num = $num * 10 + $c;
|
||||
$num = $num * 10 + ord($chr) - 48;
|
||||
}
|
||||
|
||||
return $num;
|
||||
|
||||
+4
-7
@@ -193,19 +193,16 @@ final class QRData{
|
||||
// The message bit stream shall then be extended to fill the data capacity of the symbol
|
||||
// corresponding to the Version and Error Correction Level, by the addition of the Pad
|
||||
// Codewords 11101100 and 00010001 alternately.
|
||||
$alternate = false;
|
||||
|
||||
while(true){
|
||||
|
||||
if($this->bitBuffer->getLength() >= $MAX_BITS){
|
||||
break;
|
||||
}
|
||||
|
||||
$this->bitBuffer->put(0b11101100, 8);
|
||||
|
||||
if($this->bitBuffer->getLength() >= $MAX_BITS){
|
||||
break;
|
||||
}
|
||||
|
||||
$this->bitBuffer->put(0b00010001, 8);
|
||||
$this->bitBuffer->put($alternate ? 0b00010001 : 0b11101100, 8);
|
||||
$alternate = !$alternate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,8 +26,15 @@ abstract class QRDataModeAbstract implements QRDataModeInterface{
|
||||
|
||||
/**
|
||||
* QRDataModeAbstract constructor.
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public function __construct(string $data){
|
||||
|
||||
if(!$this::validateString($data)){
|
||||
throw new QRCodeDataException('invalid data');
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,43 +10,14 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Data\{AlphaNum, QRCodeDataException};
|
||||
use chillerlan\QRCode\Data\AlphaNum;
|
||||
|
||||
/**
|
||||
* Tests the AlphaNum class
|
||||
*/
|
||||
final class AlphaNumTest extends DatainterfaceTestAbstract{
|
||||
|
||||
/** @internal */
|
||||
protected array $testdata = [AlphaNum::class, '0 $%*+-./:'];
|
||||
|
||||
/** @internal */
|
||||
protected array $expected = [
|
||||
32, 80, 36, 212, 252, 15, 175, 251,
|
||||
176, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
112, 43, 9, 248, 200, 194, 75, 25,
|
||||
205, 173, 154, 68, 191, 16, 128,
|
||||
92, 112, 20, 198, 27
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown when an invalid character is encountered
|
||||
*/
|
||||
public function testGetCharCodeException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('illegal char: "#" [35]');
|
||||
|
||||
$this->testdata = [AlphaNum::class, '#'];
|
||||
|
||||
$this->setTestData();
|
||||
}
|
||||
protected string $FQN = AlphaNum::class;
|
||||
protected string $testdata = '0 $%*+-./:';
|
||||
|
||||
}
|
||||
|
||||
+8
-18
@@ -17,24 +17,14 @@ use chillerlan\QRCode\Data\Byte;
|
||||
*/
|
||||
final class ByteTest extends DatainterfaceTestAbstract{
|
||||
|
||||
/** @internal */
|
||||
protected array $testdata = [Byte::class, '[¯\_(ツ)_/¯]'];
|
||||
protected string $FQN = Byte::class;
|
||||
protected string $testdata = '[¯\_(ツ)_/¯]';
|
||||
|
||||
/** @internal */
|
||||
protected array $expected = [
|
||||
64, 245, 188, 42, 245, 197, 242, 142,
|
||||
56, 56, 66, 149, 242, 252, 42, 245,
|
||||
208, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
79, 89, 226, 48, 209, 89, 151, 1,
|
||||
12, 73, 42, 163, 11, 34, 255, 205,
|
||||
21, 47, 250, 101
|
||||
];
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function testInvalidDataException():void{
|
||||
$this->markTestSkipped('N/A');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,28 +24,16 @@ use function str_repeat;
|
||||
*/
|
||||
abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
|
||||
/** @internal */
|
||||
protected ReflectionClass $reflection;
|
||||
/** @internal */
|
||||
protected QRData $dataInterface;
|
||||
/** @internal */
|
||||
protected array $testdata;
|
||||
/** @internal */
|
||||
protected array $expected;
|
||||
protected QRData $dataInterface;
|
||||
protected string $FQN;
|
||||
protected string $testdata;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function setUp():void{
|
||||
$this->dataInterface = new QRData(new QROptions(['version' => 4]), []);
|
||||
$this->dataInterface = new QRData(new QROptions(['version' => 4]));
|
||||
$this->reflection = new ReflectionClass($this->dataInterface);
|
||||
}
|
||||
|
||||
protected function setTestData():void{
|
||||
[$class, $data] = $this->testdata;
|
||||
$this->dataInterface->setData([new $class($data)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the data interface instance
|
||||
*/
|
||||
@@ -53,38 +41,21 @@ abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
$this::assertInstanceOf(QRData::class, $this->dataInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ecc masking and verifies against a sample
|
||||
*/
|
||||
/* public function testMaskEcc():void{
|
||||
$this->dataInterface->setData([$this->testdata]);
|
||||
|
||||
$maskECC = $this->reflection->getMethod('maskECC');
|
||||
$maskECC->setAccessible(true);
|
||||
|
||||
$bitBuffer = $this->reflection->getProperty('bitBuffer');
|
||||
$bitBuffer->setAccessible(true);
|
||||
$bb = $bitBuffer->getValue($this->dataInterface);
|
||||
|
||||
$this::assertSame($this->expected, $maskECC->invokeArgs($this->dataInterface, [$bb->getBuffer()]));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @see testInitMatrix()
|
||||
* @internal
|
||||
* @return int[][]
|
||||
*/
|
||||
public function MaskPatternProvider():array{
|
||||
public function maskPatternProvider():array{
|
||||
return [[0], [1], [2], [3], [4], [5], [6], [7]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests initializing the data matrix
|
||||
*
|
||||
* @dataProvider MaskPatternProvider
|
||||
* @dataProvider maskPatternProvider
|
||||
*/
|
||||
public function testInitMatrix(int $maskPattern):void{
|
||||
$this->setTestData();
|
||||
$this->dataInterface->setData([new $this->FQN($this->testdata)]);
|
||||
|
||||
$matrix = $this->dataInterface->writeMatrix(new MaskPattern($maskPattern));
|
||||
|
||||
@@ -96,7 +67,7 @@ abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
* Tests getting the minimum QR version for the given data
|
||||
*/
|
||||
public function testGetMinimumVersion():void{
|
||||
$this->setTestData();
|
||||
$this->dataInterface->setData([new $this->FQN($this->testdata)]);
|
||||
|
||||
$getMinimumVersion = $this->reflection->getMethod('getMinimumVersion');
|
||||
$getMinimumVersion->setAccessible(true);
|
||||
@@ -110,11 +81,10 @@ abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
public function testGetMinimumVersionException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('data exceeds');
|
||||
[$class, $data] = $this->testdata;
|
||||
|
||||
$this->dataInterface = new QRData(
|
||||
new QROptions(['version' => QRCode::VERSION_AUTO]),
|
||||
[new $class(str_repeat($data, 1337))]
|
||||
[new $this->FQN(str_repeat($this->testdata, 1337))]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,10 +94,28 @@ abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
public function testCodeLengthOverflowException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('code length overflow');
|
||||
[$class, $data] = $this->testdata;
|
||||
$this->testdata = [$class, str_repeat($data, 1337)];
|
||||
|
||||
$this->setTestData();
|
||||
$this->dataInterface->setData([new $this->FQN(str_repeat($this->testdata, 1337))]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown when an invalid character is encountered
|
||||
*/
|
||||
public function testInvalidDataException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('invalid data');
|
||||
|
||||
$this->dataInterface->setData([new $this->FQN('##')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown if the given string is empty
|
||||
*/
|
||||
public function testInvalidDataOnEmptyException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('invalid data');
|
||||
|
||||
$this->dataInterface->setData([new $this->FQN('')]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,55 +10,14 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Data\{Kanji, QRCodeDataException};
|
||||
use chillerlan\QRCode\Data\Kanji;
|
||||
|
||||
/**
|
||||
* Tests the Kanji class
|
||||
*/
|
||||
final class KanjiTest extends DatainterfaceTestAbstract{
|
||||
|
||||
/** @internal */
|
||||
protected array $testdata = [Kanji::class, '茗荷茗荷茗荷茗荷茗荷'];
|
||||
|
||||
/** @internal */
|
||||
protected array $expected = [
|
||||
128, 173, 85, 26, 95, 85, 70, 151,
|
||||
213, 81, 165, 245, 84, 105, 125, 85,
|
||||
26, 92, 0, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
195, 11, 221, 91, 141, 220, 163, 46,
|
||||
165, 37, 163, 176, 79, 0, 64, 68,
|
||||
96, 113, 54, 191
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown when an invalid character is encountered
|
||||
*/
|
||||
public function testIllegalCharException1():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('illegal char at 1 [16191]');
|
||||
|
||||
$this->testdata = [Kanji::class, 'ÃÃ'];
|
||||
|
||||
$this->setTestData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown when an invalid character is encountered
|
||||
*/
|
||||
public function testIllegalCharException2():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('illegal char at 1');
|
||||
|
||||
$this->testdata = [Kanji::class, 'Ã'];
|
||||
|
||||
$this->setTestData();
|
||||
}
|
||||
protected string $FQN = Kanji::class;
|
||||
protected string $testdata = '茗荷茗荷茗荷茗荷茗荷';
|
||||
|
||||
}
|
||||
|
||||
@@ -10,43 +10,14 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Data\{Number, QRCodeDataException};
|
||||
use chillerlan\QRCode\Data\Number;
|
||||
|
||||
/**
|
||||
* Tests the Number class
|
||||
*/
|
||||
final class NumberTest extends DatainterfaceTestAbstract{
|
||||
|
||||
/** @internal */
|
||||
protected array $testdata = [Number::class, '0123456789'];
|
||||
|
||||
/** @internal */
|
||||
protected array $expected = [
|
||||
16, 40, 12, 86, 106, 105, 0, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
17, 236, 17, 236, 17, 236, 17, 236,
|
||||
201, 141, 102, 116, 238, 162, 239, 230,
|
||||
222, 37, 79, 192, 42, 109, 188, 72,
|
||||
89, 63, 168, 151
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests if an exception is thrown when an invalid character is encountered
|
||||
*/
|
||||
public function testGetCharCodeException():void{
|
||||
$this->expectException(QRCodeDataException::class);
|
||||
$this->expectExceptionMessage('illegal char: "#" [35]');
|
||||
|
||||
$this->testdata = [Number::class, '#'];
|
||||
|
||||
$this->setTestData();
|
||||
}
|
||||
protected string $FQN = Number::class;
|
||||
protected string $testdata = '0123456789';
|
||||
|
||||
}
|
||||
|
||||
@@ -106,12 +106,9 @@ class QRCodeReaderTest extends TestCase{
|
||||
|
||||
foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
|
||||
$eccLevel = new EccLevel($ecc);
|
||||
$expected = substr($str, 0, $version->getMaxLengthForMode(Mode::BYTE, $eccLevel) ?? '');
|
||||
|
||||
yield 'version: '.$version.$eccLevel => [
|
||||
$version,
|
||||
$eccLevel,
|
||||
substr($str, 0, $version->getMaxLengthForMode(Mode::BYTE, $eccLevel) ?? '')
|
||||
];
|
||||
yield 'version: '.$version.$eccLevel => [$version, $eccLevel, $expected];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user