Browse Source

Merge branch 'master' into php-7.4

# Conflicts:
#	src/QROptionsTrait.php
codemasher 5 years ago
parent
commit
d041202a2a
6 changed files with 50 additions and 3 deletions
  1. 1 0
      .gitignore
  2. 1 0
      README.md
  3. 12 2
      src/QRCode.php
  4. 1 0
      src/QROptions.php
  5. 8 0
      src/QROptionsTrait.php
  6. 27 1
      tests/QRCodeTest.php

+ 1 - 0
.gitignore

@@ -2,3 +2,4 @@
 .idea/*
 vendor/*
 composer.lock
+*.phpunit.result.cache

+ 1 - 0
README.md

@@ -324,6 +324,7 @@ property | type | default | allowed | description
 `$maskPattern` | int | `QRCode::MASK_PATTERN_AUTO` | 0...7 | Mask Pattern to use
 `$addQuietzone` | bool | `true` | - | Add a "quiet zone" (margin) according to the QR code spec
 `$quietzoneSize` | int | 4 | clamped to 0 ... `$matrixSize / 2` | Size of the quiet zone
+`$dataMode` | string | `null` | `Number`, `AlphaNum`, `Kanji`, `Byte` | allows overriding the data type detection
 `$outputType` | string | `QRCode::OUTPUT_IMAGE_PNG` | `QRCode::OUTPUT_*` | built-in output type
 `$outputInterface` | string | `null` | * | FQCN of the custom `QROutputInterface` if `QROptions::$outputType` is set to `QRCode::OUTPUT_CUSTOM`
 `$cachefile` | string | `null` | * | optional cache file path

+ 12 - 2
src/QRCode.php

@@ -177,9 +177,19 @@ class QRCode{
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
 	public function initDataInterface(string $data):QRDataInterface{
+		$dataModes     = ['Number', 'AlphaNum', 'Kanji', 'Byte'];
+		$dataNamespace = __NAMESPACE__.'\\Data\\';
 
-		foreach(['Number', 'AlphaNum', 'Kanji', 'Byte'] as $mode){
-			$dataInterface = __NAMESPACE__.'\\Data\\'.$mode;
+		// allow forcing the data mode
+		// see https://github.com/chillerlan/php-qrcode/issues/39
+		if(in_array($this->options->dataMode, $dataModes, true)){
+			$dataInterface = $dataNamespace.$this->options->dataMode;
+
+			return new $dataInterface($this->options, $data);
+		}
+
+		foreach($dataModes as $mode){
+			$dataInterface = $dataNamespace.$mode;
 
 			if(call_user_func_array([$this, 'is'.$mode], [$data]) && class_exists($dataInterface)){
 				return new $dataInterface($this->options, $data);

+ 1 - 0
src/QROptions.php

@@ -23,6 +23,7 @@ use chillerlan\Settings\SettingsContainerAbstract;
  * @property bool   $addQuietzone
  * @property bool   $quietzoneSize
  *
+ * @property string $dataMode
  * @property string $outputType
  * @property string $outputInterface
  * @property string $cachefile

+ 8 - 0
src/QROptionsTrait.php

@@ -63,6 +63,14 @@ trait QROptionsTrait{
 	 */
 	protected int $quietzoneSize = 4;
 
+	/**
+	 * Use this to circumvent the data mode detection and force the usage of the given mode.
+	 * valid modes are: Number, AlphaNum, Kanji, Byte
+	 *
+	 * @see https://github.com/chillerlan/php-qrcode/issues/39
+	 */
+	protected ?string $dataMode = null;
+
 	/**
 	 * QRCode::OUTPUT_MARKUP_XXXX where XXXX = HTML, SVG
 	 * QRCode::OUTPUT_IMAGE_XXX where XXX = PNG, GIF, JPG

+ 27 - 1
tests/QRCodeTest.php

@@ -13,10 +13,12 @@
 namespace chillerlan\QRCodeTest;
 
 use chillerlan\QRCode\{QROptions, QRCode};
-use chillerlan\QRCode\Data\QRCodeDataException;
+use chillerlan\QRCode\Data\{AlphaNum, Byte, Number, QRCodeDataException};
 use chillerlan\QRCode\Output\QRCodeOutputException;
 use chillerlan\QRCodeExamples\MyCustomOutput;
 
+use function random_bytes;
+
 class QRCodeTest extends QRTestAbstract{
 
 	protected string $FQCN = QRCode::class;
@@ -111,4 +113,28 @@ class QRCodeTest extends QRTestAbstract{
 
 		$this->assertSame($expected, $this->reflection->newInstanceArgs([$options])->render('test'));
 	}
+
+	public function testDataModeOverride(){
+		$this->qrcode = $this->reflection->newInstance();
+
+		$this->assertInstanceOf(Number::class, $this->qrcode->initDataInterface('123'));
+		$this->assertInstanceOf(AlphaNum::class, $this->qrcode->initDataInterface('ABC123'));
+		$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
+
+		$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['dataMode' => 'Byte'])]);
+
+		$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('123'));
+		$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('ABC123'));
+		$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
+	}
+
+	public function testDataModeOverrideError(){
+		$this->expectException(QRCodeDataException::class);
+		$this->expectExceptionMessage('illegal char:');
+
+		$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['dataMode' => 'AlphaNum'])]);
+
+		$this->qrcode->initDataInterface(random_bytes(32));
+	}
+
 }