Browse Source

:octocat: fix PHPCS config, add Slevomat rules

smiley 1 year ago
parent
commit
f32696c8a7
97 changed files with 462 additions and 654 deletions
  1. 3 2
      .github/workflows/ci.yml
  2. 5 1
      composer.json
  3. 0 12
      examples/custom_output.php
  4. 1 1
      examples/eps.php
  5. 2 2
      examples/fpdf.php
  6. 6 4
      examples/imageWithLogo.php
  7. 10 12
      examples/imageWithRoundedShapes.php
  8. 0 3
      examples/imageWithText.php
  9. 11 5
      examples/imagickConvertSVGtoPNG.php
  10. 0 6
      examples/imagickImageAsBackground.php
  11. 3 1
      examples/imagickWithLogo.php
  12. 0 3
      examples/intervention-image.php
  13. 2 2
      examples/qrcode-interactive.php
  14. 1 1
      examples/svg.php
  15. 4 4
      examples/svgConvertViaCanvas.php
  16. 1 10
      examples/svgMeltedModules.php
  17. 1 4
      examples/svgRandomColoredDots.php
  18. 3 12
      examples/svgRoundQuietzone.php
  19. 2 8
      examples/svgWithLogo.php
  20. 3 9
      examples/svgWithLogoAndCustomShapes.php
  21. 3 2
      examples/xml.php
  22. 226 112
      phpcs.xml.dist
  23. 0 5
      src/Common/GDLuminanceSource.php
  24. 0 13
      src/Common/GenericGFPoly.php
  25. 0 5
      src/Common/IMagickLuminanceSource.php
  26. 0 10
      src/Common/LuminanceSourceAbstract.php
  27. 1 0
      src/Common/LuminanceSourceInterface.php
  28. 2 12
      src/Data/AlphaNum.php
  29. 0 12
      src/Data/Byte.php
  30. 2 7
      src/Data/ECI.php
  31. 0 12
      src/Data/Hanzi.php
  32. 0 12
      src/Data/Kanji.php
  33. 10 14
      src/Data/Number.php
  34. 2 2
      src/Data/QRData.php
  35. 0 3
      src/Data/QRDataModeAbstract.php
  36. 2 0
      src/Data/QRMatrix.php
  37. 0 6
      src/Decoder/Binarizer.php
  38. 1 10
      src/Decoder/BitMatrix.php
  39. 0 6
      src/Decoder/Decoder.php
  40. 1 10
      src/Decoder/DecoderResult.php
  41. 2 2
      src/Decoder/ReedSolomonDecoder.php
  42. 1 1
      src/Detector/AlignmentPattern.php
  43. 7 10
      src/Detector/AlignmentPatternFinder.php
  44. 3 6
      src/Detector/Detector.php
  45. 6 18
      src/Detector/FinderPattern.php
  46. 11 7
      src/Detector/FinderPatternFinder.php
  47. 9 9
      src/Detector/GridSampler.php
  48. 7 22
      src/Detector/PerspectiveTransform.php
  49. 0 12
      src/Detector/ResultPoint.php
  50. 1 13
      src/Output/QREps.php
  51. 3 6
      src/Output/QRFpdf.php
  52. 15 9
      src/Output/QRGdImage.php
  53. 0 3
      src/Output/QRGdImageAVIF.php
  54. 0 3
      src/Output/QRGdImageBMP.php
  55. 0 3
      src/Output/QRGdImageGIF.php
  56. 0 6
      src/Output/QRGdImageJPEG.php
  57. 0 3
      src/Output/QRGdImagePNG.php
  58. 0 3
      src/Output/QRGdImageWEBP.php
  59. 19 12
      src/Output/QRImagick.php
  60. 2 6
      src/Output/QRInterventionImage.php
  61. 0 3
      src/Output/QRMarkup.php
  62. 1 4
      src/Output/QRMarkupHTML.php
  63. 2 11
      src/Output/QRMarkupSVG.php
  64. 6 3
      src/Output/QRMarkupXML.php
  65. 11 4
      src/Output/QRStringJSON.php
  66. 1 7
      src/Output/QRStringText.php
  67. 2 2
      src/QRCode.php
  68. 2 5
      src/QROptionsTrait.php
  69. 9 1
      tests/BuildDirTrait.php
  70. 0 6
      tests/Data/ByteTest.php
  71. 1 1
      tests/Data/DataInterfaceTestAbstract.php
  72. 2 2
      tests/Data/HanziTest.php
  73. 2 2
      tests/Data/KanjiTest.php
  74. 0 3
      tests/Data/QRDataTest.php
  75. 1 1
      tests/Data/QRMatrixTest.php
  76. 1 4
      tests/Output/QREpsTest.php
  77. 1 7
      tests/Output/QRFpdfTest.php
  78. 1 4
      tests/Output/QRGdImageAVIFTest.php
  79. 1 4
      tests/Output/QRGdImageBMPTest.php
  80. 1 4
      tests/Output/QRGdImageGIFTest.php
  81. 1 4
      tests/Output/QRGdImageJPGTest.php
  82. 1 4
      tests/Output/QRGdImagePNGTest.php
  83. 0 6
      tests/Output/QRGdImageTestAbstract.php
  84. 1 4
      tests/Output/QRGdImageWEBPTest.php
  85. 1 7
      tests/Output/QRImagickTest.php
  86. 1 7
      tests/Output/QRInterventionImageTest.php
  87. 1 4
      tests/Output/QRMarkupHTMLTest.php
  88. 1 4
      tests/Output/QRMarkupSVGTest.php
  89. 0 3
      tests/Output/QRMarkupTestAbstract.php
  90. 0 3
      tests/Output/QRMarkupXMLTest.php
  91. 1 7
      tests/Output/QRStringJSONTest.php
  92. 1 7
      tests/Output/QRStringTextTest.php
  93. 1 1
      tests/QRCodeReaderGDTest.php
  94. 1 1
      tests/QRCodeReaderImagickTest.php
  95. 10 3
      tests/QRCodeReaderTestAbstract.php
  96. 1 1
      tests/QRMatrixDebugTrait.php
  97. 1 1
      tests/QRMaxLengthTrait.php

+ 3 - 2
.github/workflows/ci.yml

@@ -47,11 +47,12 @@ jobs:
       - name: "Install dependencies with composer"
         uses: ramsey/composer-install@v3
 
-      - name: "Run phan"
-        run: php vendor/bin/phan --target-php-version=${{ matrix.php-version }}
       - name: "Run PHPStan"
         run: php vendor/bin/phpstan
 
+      - name: "Run PHP_CodeSniffer"
+        run: php vendor/bin/phpstan
+
 
   tests:
     name: "Unit Tests"

+ 5 - 1
composer.json

@@ -60,6 +60,7 @@
 		"phpstan/phpstan": "^1.11",
 		"phpstan/phpstan-deprecation-rules": "^1.2",
 		"setasign/fpdf": "^1.8.2",
+		"slevomat/coding-standard": "^8.15",
 		"squizlabs/php_codesniffer": "^3.10"
 	},
 	"suggest": {
@@ -92,6 +93,9 @@
 	"config": {
 		"lock": false,
 		"sort-packages": true,
-		"platform-check": true
+		"platform-check": true,
+		"allow-plugins": {
+			"dealerdirect/phpcodesniffer-composer-installer": true
+		}
 	}
 }

+ 0 - 12
examples/custom_output.php

@@ -21,33 +21,21 @@ require_once __DIR__.'/../vendor/autoload.php';
 
 class MyCustomOutput extends QROutputAbstract{
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function moduleValueIsValid(mixed $value):bool{
 		// TODO: Implement moduleValueIsValid() method. (interface)
 		return false;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function prepareModuleValue(mixed $value):mixed{
 		// TODO: Implement prepareModuleValue() method. (abstract)
 		return null;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):mixed{
 		// TODO: Implement getDefaultModuleValue() method. (abstract)
 		return null;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string{
 		$output = '';
 

+ 1 - 1
examples/eps.php

@@ -55,7 +55,7 @@ $options->moduleValues     = [
 
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ', __DIR__.'/qrcode.eps');
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	// if viewed in the browser, we should push it as file download as EPS isn't usually supported
 	header('Content-type: application/postscript');
 	header('Content-Disposition: filename="qrcode.eps"');

+ 2 - 2
examples/fpdf.php

@@ -11,7 +11,7 @@ use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\QRFpdf;
 
-require_once __DIR__ . '/../vendor/autoload.php';
+require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
@@ -53,7 +53,7 @@ $options->moduleValues     = [
 
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: application/pdf');
 }
 

+ 6 - 4
examples/imageWithLogo.php

@@ -24,13 +24,11 @@ require_once __DIR__.'/../vendor/autoload.php';
 class QRImageWithLogo extends QRGdImagePNG{
 
 	/**
-	 * @param string|null $file
-	 * @param string|null $logo
-	 *
-	 * @return string
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
 	public function dump(string|null $file = null, string|null $logo = null):string{
+		$logo ??= '';
+
 		// set returnResource to true to skip further processing for now
 		$this->options->returnResource = true;
 
@@ -45,6 +43,10 @@ class QRImageWithLogo extends QRGdImagePNG{
 
 		$im = imagecreatefrompng($logo);
 
+		if($im === false){
+			throw new QRCodeOutputException('imagecreatefrompng() error');
+		}
+
 		// get logo image size
 		$w = imagesx($im);
 		$h = imagesy($im);

+ 10 - 12
examples/imageWithRoundedShapes.php

@@ -19,7 +19,7 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\QRGdImagePNG;
 use chillerlan\Settings\SettingsContainerInterface;
 
-require_once __DIR__ . '/../vendor/autoload.php';
+require_once __DIR__.'/../vendor/autoload.php';
 
 // --------------------
 // Class definition
@@ -27,7 +27,6 @@ require_once __DIR__ . '/../vendor/autoload.php';
 
 class QRGdRounded extends QRGdImagePNG{
 
-	/** @inheritDoc */
 	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 		// enable the internal scaling for better rounding results at scale < 20
 		$options->drawCircularModules = true;
@@ -35,7 +34,6 @@ class QRGdRounded extends QRGdImagePNG{
 		parent::__construct($options, $matrix);
 	}
 
-	/** @inheritDoc */
 	protected function module(int $x, int $y, int $M_TYPE):void{
 
 		/**
@@ -121,7 +119,7 @@ class QRGdRounded extends QRGdImagePNG{
 			(int)($y * $this->scale + $this->scale / 2),
 			($this->scale - 1),
 			($this->scale - 1),
-			$light
+			$light,
 		);
 	}
 
@@ -133,14 +131,14 @@ class QRGdRounded extends QRGdImagePNG{
 // --------------------
 
 $options = new QROptions([
-    'version'         => 7,
-    'eccLevel'        => EccLevel::H,
-    'outputInterface' => QRGdRounded::class,
-    'outputBase64'    => false,
-    'scale'           => 30,
-    'addLogoSpace'    => true,
-    'logoSpaceWidth'  => 13,
-    'logoSpaceHeight' => 13,
+	'version'         => 7,
+	'eccLevel'        => EccLevel::H,
+	'outputInterface' => QRGdRounded::class,
+	'outputBase64'    => false,
+	'scale'           => 30,
+	'addLogoSpace'    => true,
+	'logoSpaceWidth'  => 13,
+	'logoSpaceHeight' => 13,
 ]);
 
 

+ 0 - 3
examples/imageWithText.php

@@ -23,9 +23,6 @@ require_once __DIR__.'/../vendor/autoload.php';
 
 class QRImageWithText extends QRGdImagePNG{
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null, string|null $text = null):string{
 		// set returnResource to true to skip further processing for now
 		$this->options->returnResource = true;

+ 11 - 5
examples/imagickConvertSVGtoPNG.php

@@ -14,17 +14,18 @@
  * @author       smiley <smiley@chillerlan.net>
  * @copyright    2023 smiley
  * @license      MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
  */
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QRMarkupSVG;
+use chillerlan\QRCode\Output\{QRCodeOutputException, QRMarkupSVG};
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 class SVGConvert extends QRMarkupSVG{
 
-	/** @inheritDoc */
 	protected function header():string{
 		[$width, $height] = $this->getOutputDimensions();
 
@@ -36,7 +37,7 @@ class SVGConvert extends QRMarkupSVG{
 			$this->options->svgPreserveAspectRatio,
 			$this->options->eol,
 			($width * $this->scale), // use the scale option to modify the size
-			($height * $this->scale)
+			($height * $this->scale),
 		);
 
 		if($this->options->svgAddXmlHeader){
@@ -46,7 +47,6 @@ class SVGConvert extends QRMarkupSVG{
 		return $header;
 	}
 
-	/** @inheritDoc */
 	public function dump(string|null $file = null):string{
 		$base64 = $this->options->outputBase64;
 		// we don't want the SVG in base64
@@ -70,7 +70,13 @@ class SVGConvert extends QRMarkupSVG{
 
 		if($base64){
 			// use finfo to guess the mime type
-			$imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
+			$mime = (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData);
+
+			if($mime === false){
+				throw new QRCodeOutputException('unable to detect mime type');
+			}
+
+			$imageData = $this->toBase64DataURI($imageData);
 		}
 
 		return $imageData;

+ 0 - 6
examples/imagickImageAsBackground.php

@@ -16,17 +16,11 @@ require_once __DIR__.'/../vendor/autoload.php';
 
 class QRImagickImageAsBackground extends QRImagick{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):ImagickPixel{
 		// RGBA, adjust opacity to increase contrast
 		return $this->prepareModuleValue(($isDark) ? '#00000040' : '#ffffffa0');
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function createImage():Imagick{
 		$imagick = new Imagick($this->options->background);
 		$width   = $imagick->getImageWidth();

+ 3 - 1
examples/imagickWithLogo.php

@@ -55,7 +55,7 @@ class QRImagickWithLogo extends QRImagick{
 		$this->saveToFile($imageData, $file);
 
 		if($this->options->outputBase64){
-			$imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
+			$imageData = $this->toBase64DataURI($imageData, $this->guessMimeType($imageData));
 		}
 
 		return $imageData;
@@ -77,6 +77,8 @@ class ImagickWithLogoOptions extends QROptions{
 	 *
 	 * of course, we could accept other formats too.
 	 * we're not checking for the file type either for simplicity reasons (assuming PNG)
+	 *
+	 * @throws \chillerlan\QRCode\QRCodeException
 	 */
 	protected function set_pngLogo(string $pngLogo):void{
 

+ 0 - 3
examples/intervention-image.php

@@ -75,6 +75,3 @@ header('Content-type: image/png');
 echo $out;
 
 exit;
-
-
-

+ 2 - 2
examples/qrcode-interactive.php

@@ -56,7 +56,7 @@ try{
 
 	$moduleValues = array_map(function($v){
 		if(preg_match('/[a-f\d]{6}/i', $v) === 1){
-			return in_array($_POST['output_type'], ['png', 'jpg', 'gif'])
+			return in_array($_POST['output_type'], ['png', 'jpg', 'gif'], true)
 				? array_map('hexdec', str_split($v, 2))
 				: '#'.$v ;
 		}
@@ -81,7 +81,7 @@ try{
 
 	$qrcode = (new QRCode($options))->render($_POST['inputstring']);
 
-	if(in_array($_POST['output_type'], ['png', 'jpg', 'gif', 'svg'])){
+	if(in_array($_POST['output_type'], ['png', 'jpg', 'gif', 'svg'], true)){
 		$qrcode = '<img alt="qrcode" src="'.$qrcode.'" />';
 	}
 	elseif($_POST['output_type'] === 'text'){

+ 1 - 1
examples/svg.php

@@ -60,7 +60,7 @@ catch(Throwable $e){
 }
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: image/svg+xml');
 
 	if(extension_loaded('zlib')){

+ 4 - 4
examples/svgConvertViaCanvas.php

@@ -80,9 +80,9 @@ header('Content-type: text/html');
 <script type="module">
 	import SVGConvert from './SVGConvert.js';
 
-    // SVG DOM element
-    SVGConvert.toDataURI(document.querySelector('svg.qr-svg'), document.getElementById('qr-svg-dest'), 300, 300, 'image/jpeg');
-    // base64 data URI in image element
-    SVGConvert.toDataURI(document.getElementById('qr-svg-base64'), document.getElementById('qr-svg-base64-dest'), 300, 300);
+	// SVG DOM element
+	SVGConvert.toDataURI(document.querySelector('svg.qr-svg'), document.getElementById('qr-svg-dest'), 300, 300, 'image/jpeg');
+	// base64 data URI in image element
+	SVGConvert.toDataURI(document.getElementById('qr-svg-base64'), document.getElementById('qr-svg-base64-dest'), 300, 300);
 </script>
 </html>

+ 1 - 10
examples/svgMeltedModules.php

@@ -26,17 +26,11 @@ require_once __DIR__.'/../vendor/autoload.php';
  */
 class MeltedSVGQRCodeOutput extends QRMarkupSVG{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function path(string $path, int $M_TYPE):string{
 		// omit the "fill" and "opacity" attributes on the path element
 		return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function collectModules(Closure $transform):array{
 		$paths = [];
 		$melt  = $this->options->melt; // avoid magic getter in long loops
@@ -76,9 +70,6 @@ class MeltedSVGQRCodeOutput extends QRMarkupSVG{
 		return $paths;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function module(int $x, int $y, int $M_TYPE):string{
 		$bits     = $this->matrix->checkNeighbours($x, $y, null);
 		$check    = fn(int $all, int $any = 0):bool => ($bits & ($all | (~$any & 0xff))) === $all;
@@ -289,7 +280,7 @@ $options->svgDefs            = '
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: image/svg+xml');
 
 	if(extension_loaded('zlib')){

+ 1 - 4
examples/svgRandomColoredDots.php

@@ -26,9 +26,6 @@ require_once __DIR__.'/../vendor/autoload.php';
 // the extended SVG output module
 class RandomDotsSVGOutput extends QRMarkupSVG{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function path(string $path, int $M_TYPE):string{
 		// omit the "fill" and "opacity" attributes on the path element
 		return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
@@ -160,7 +157,7 @@ $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgX
 
 
 // dump the output
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('content-type: image/svg+xml');
 }
 

+ 3 - 12
examples/svgRoundQuietzone.php

@@ -31,9 +31,6 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 
 	protected float $center;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function createMarkup(bool $saveToFile):string{
 		// some Pythagorean magick
 		$diameter      = sqrt(2 * pow(($this->moduleCount + $this->options->additionalModules), 2));
@@ -69,9 +66,6 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 		return $svg;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function path(string $path, int $M_TYPE):string{
 		// omit the "fill" and "opacity" attributes on the path element
 		return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
@@ -141,7 +135,7 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 			$this->center,
 			round($radius, 5),
 			($this->options->circleRadius * 2),
-			$this->options->eol
+			$this->options->eol,
 		);
 	}
 
@@ -159,13 +153,10 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 			$this->options->svgLogoScale,
 			$this->options->svgLogoCssClass,
 			file_get_contents($this->options->svgLogo),
-			$this->options->eol
+			$this->options->eol,
 		);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function collectModules(Closure $transform):array{
 		$paths     = [];
 		$dotColors = $this->options->dotColors; // avoid magic getter in long loops
@@ -345,7 +336,7 @@ $options->keepAsSquare        = [
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: image/svg+xml');
 
 	if(extension_loaded('zlib')){

+ 2 - 8
examples/svgWithLogo.php

@@ -26,9 +26,6 @@ require_once __DIR__.'/../vendor/autoload.php';
  */
 class QRSvgWithLogo extends QRMarkupSVG{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function paths():string{
 		$size = (int)ceil($this->moduleCount * $this->options->svgLogoScale);
 
@@ -41,9 +38,6 @@ class QRSvgWithLogo extends QRMarkupSVG{
 		return $svg;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function path(string $path, int $M_TYPE):string{
 		// omit the "fill" and "opacity" attributes on the path element
 		return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
@@ -63,7 +57,7 @@ class QRSvgWithLogo extends QRMarkupSVG{
 			$this->options->svgLogoScale,
 			$this->options->svgLogoCssClass,
 			file_get_contents($this->options->svgLogo),
-			$this->options->eol
+			$this->options->eol,
 		);
 	}
 
@@ -146,7 +140,7 @@ $options->svgDefs = '
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: image/svg+xml');
 
 	if(extension_loaded('zlib')){

+ 3 - 9
examples/svgWithLogoAndCustomShapes.php

@@ -28,9 +28,6 @@ require_once __DIR__.'/../vendor/autoload.php';
  */
 class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function paths():string{
 		// make sure connect paths is enabled
 		$this->options->connectPaths = true;
@@ -48,9 +45,6 @@ class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{
 		return $svg;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function path(string $path, int $M_TYPE):string{
 		// omit the "fill" and "opacity" attributes on the path element
 		return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
@@ -103,7 +97,7 @@ class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{
 			'%s<path class="%s" d="%s"/>',
 			$this->options->eol,
 			$this->getCssClass(QRMatrix::M_FINDER_DARK),
-			implode(' ', $finder)
+			implode(' ', $finder),
 		);
 	}
 
@@ -121,7 +115,7 @@ class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{
 			$this->options->svgLogoScale,
 			$this->options->svgLogoCssClass,
 			file_get_contents($this->options->svgLogo),
-			$this->options->eol
+			$this->options->eol,
 		);
 	}
 
@@ -199,7 +193,7 @@ $options->svgDefs         = '
 $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: image/svg+xml');
 
 	if(extension_loaded('zlib')){

+ 3 - 2
examples/xml.php

@@ -8,7 +8,8 @@
  * @license      MIT
  */
 
-use chillerlan\QRCode\{Data\QRMatrix, QRCode, QROptions};
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Output\QRMarkupXML;
 
 require_once __DIR__.'/../vendor/autoload.php';
@@ -61,7 +62,7 @@ catch(Throwable $e){
 }
 
 
-if(php_sapi_name() !== 'cli'){
+if(PHP_SAPI !== 'cli'){
 	header('Content-type: '.QRMarkupXML::MIME_TYPE);
 }
 

+ 226 - 112
phpcs.xml.dist

@@ -9,35 +9,207 @@
 	<file>tests</file>
 
 	<arg name="basepath" value="."/>
+	<arg name="extensions" value="php"/>
 	<arg name="tab-width" value="4"/>
 
 	<rule ref="Internal.Tokenizer.Exception">
 		<type>error</type>
 	</rule>
 
+	<!--
+		Slevomat https://github.com/slevomat/coding-standard
+	-->
 
-	<rule ref="Generic">
-		<exclude name="Generic.Arrays.DisallowShortArraySyntax" />
-		<exclude name="Generic.CodeAnalysis.EmptyStatement" />
-		<exclude name="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop" />
-		<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter" />
-		<exclude name="Generic.Commenting.Todo" />
-		<exclude name="Generic.ControlStructures.InlineControlStructure" />
-		<exclude name="Generic.Debug" />
-		<exclude name="Generic.Files.EndFileNoNewline" />
-		<exclude name="Generic.Files.LowercasedFilename" />
-		<exclude name="Generic.Formatting.SpaceBeforeCast" />
-		<exclude name="Generic.Functions.OpeningFunctionBraceBsdAllman" />
-		<exclude name="Generic.NamingConventions.AbstractClassNamePrefix" />
-		<exclude name="Generic.NamingConventions.CamelCapsFunctionName" />
-		<exclude name="Generic.PHP.ClosingPHPTag" />
-		<exclude name="Generic.PHP.RequireStrictTypes" />
-		<exclude name="Generic.PHP.UpperCaseConstant" />
-		<exclude name="Generic.VersionControl" />
-		<exclude name="Generic.WhiteSpace.DisallowTabIndent" />
-		<exclude name="Generic." />
+	<config name="installed_paths" value="../../slevomat/coding-standard"/>
+	<rule ref="SlevomatCodingStandard.Arrays.TrailingArrayComma"/>
+	<rule ref="SlevomatCodingStandard.Arrays.DisallowImplicitArrayCreation"/>
+	<rule ref="SlevomatCodingStandard.Arrays.DisallowPartiallyKeyed"/>
+
+	<rule ref="SlevomatCodingStandard.Attributes.DisallowAttributesJoining"/>
+	<rule ref="SlevomatCodingStandard.Attributes.DisallowMultipleAttributesPerLine"/>
+	<rule ref="SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment"/>
+
+	<rule ref="SlevomatCodingStandard.Classes.ClassConstantVisibility"/>
+	<rule ref="SlevomatCodingStandard.Classes.DisallowConstructorPropertyPromotion"/>
+	<rule ref="SlevomatCodingStandard.Classes.ForbiddenPublicProperty"/>
+	<rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference"/>
+
+	<rule ref="SlevomatCodingStandard.Commenting.DeprecatedAnnotationDeclaration"/>
+	<rule ref="SlevomatCodingStandard.Commenting.EmptyComment"/>
+	<rule ref="SlevomatCodingStandard.Commenting.UselessFunctionDocComment"/>
+	<rule ref="SlevomatCodingStandard.Commenting.UselessInheritDocComment"/>
+
+	<rule ref="SlevomatCodingStandard.ControlStructures.AssignmentInCondition"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.DisallowTrailingMultiLineTernaryOperator"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.NewWithoutParentheses"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceEqualOperator"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullSafeObjectOperator"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.DisallowYodaComparison"/>
+	<rule ref="SlevomatCodingStandard.ControlStructures.UselessTernaryOperator"/>
+
+	<rule ref="SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch"/>
+
+	<rule ref="SlevomatCodingStandard.Functions.DisallowEmptyFunction"/>
+	<rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInCall"/>
+	<rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration"/>
+	<rule ref="SlevomatCodingStandard.Functions.StrictCall"/>
+
+	<rule ref="SlevomatCodingStandard.Namespaces.RequireOneNamespaceInFile"/>
+	<rule ref="SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash"/>
+	<rule ref="SlevomatCodingStandard.Namespaces.UselessAlias"/>
+	<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses"/>
+
+	<rule ref="SlevomatCodingStandard.Numbers.DisallowNumericLiteralSeparator"/>
+
+	<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators"/>
+	<rule ref="SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator"/>
+
+	<rule ref="SlevomatCodingStandard.PHP.OptimizedFunctionsWithoutUnpacking"/>
+	<rule ref="SlevomatCodingStandard.PHP.ShortList"/>
+	<rule ref="SlevomatCodingStandard.PHP.TypeCast"/>
+	<rule ref="SlevomatCodingStandard.PHP.UselessSemicolon"/>
+
+	<rule ref="SlevomatCodingStandard.Strings.DisallowVariableParsing"/>
+
+	<!--<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes"/>-->
+	<!--<rule ref="SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint"/>-->
+	<rule ref="SlevomatCodingStandard.TypeHints.LongTypeHints"/>
+	<rule ref="SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition"/>
+	<rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/>
+	<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint"/>
+	<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint"/>
+	<!--<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint"/>-->
+
+	<rule ref="SlevomatCodingStandard.Variables.DisallowVariableVariable"/>
+	<rule ref="SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable"/>
+	<rule ref="SlevomatCodingStandard.Variables.UselessVariable"/>
+
+
+	<rule ref="SlevomatCodingStandard.Functions.RequireMultiLineCall">
+		<properties>
+			<property name="minLineLength" value="131"/>
+		</properties>
 	</rule>
 
+	<rule ref="SlevomatCodingStandard.Variables.DisallowSuperGlobalVariable">
+		<exclude-pattern>examples</exclude-pattern>
+	</rule>
+
+	<rule ref="SlevomatCodingStandard.TypeHints.UnionTypeHintFormat">
+		<properties>
+			<property name="withSpaces" value="no"/>
+			<property name="shortNullable" value="no"/>
+			<property name="nullPosition" value="last"/>
+		</properties>
+	</rule>
+
+	<rule ref="SlevomatCodingStandard.Variables.UnusedVariable">
+		<properties>
+			<property name="ignoreUnusedValuesWhenOnlyKeysAreUsedInForeach" value="true"/>
+		</properties>
+	</rule>
+
+
+	<!--
+		PHPCS built-in https://tentyp.dev/library/php/phpcs/
+	-->
+
+	<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
+	<rule ref="Generic.Classes.DuplicateClassName"/>
+	<rule ref="Generic.CodeAnalysis.AssignmentInCondition"/>
+	<rule ref="Generic.CodeAnalysis.EmptyPHPStatement"/>
+	<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
+	<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
+	<!--<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>-->
+	<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
+	<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
+	<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
+	<rule ref="Generic.Commenting.Fixme"/>
+	<!--<rule ref="Generic.Commenting.Todo"/>-->
+	<rule ref="Generic.ControlStructures.InlineControlStructure"/>
+	<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
+	<!--<rule ref="Generic.Formatting.MultipleStatementAlignment"/>-->
+	<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
+	<rule ref="Generic.Functions.CallTimePassByReference"/>
+	<rule ref="Generic.NamingConventions.InterfaceNameSuffix"/>
+	<rule ref="Generic.NamingConventions.TraitNameSuffix"/>
+	<rule ref="Generic.PHP.BacktickOperator"/>
+	<rule ref="Generic.PHP.CharacterBeforePHPOpeningTag"/>
+	<rule ref="Generic.PHP.DeprecatedFunctions"/>
+	<rule ref="Generic.PHP.DisallowAlternativePHPTags"/>
+	<rule ref="Generic.PHP.DisallowRequestSuperglobal"/>
+	<rule ref="Generic.PHP.DisallowShortOpenTag"/>
+	<rule ref="Generic.PHP.DiscourageGoto"/>
+	<rule ref="Generic.PHP.LowerCaseConstant"/>
+	<rule ref="Generic.PHP.LowerCaseKeyword"/>
+	<rule ref="Generic.PHP.LowerCaseType"/>
+	<rule ref="Generic.PHP.NoSilencedErrors"/>
+	<!--<rule ref="Generic.PHP.RequireStrictTypes"/>-->
+	<rule ref="Generic.PHP.SAPIUsage"/>
+	<rule ref="Generic.PHP.Syntax"/>
+	<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
+	<!--<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>-->
+	<rule ref="Generic.WhiteSpace.IncrementDecrementSpacing"/>
+	<rule ref="Generic.WhiteSpace.ScopeIndent"/>
+	<rule ref="Generic.WhiteSpace.SpreadOperatorSpacingAfter"/>
+
+	<rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
+	<rule ref="PSR2.Files.ClosingTag"/>
+	<rule ref="PSR2.Files.EndFileNewline"/>
+	<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
+
+	<rule ref="PSR12.Classes.AnonClassDeclaration"/>
+	<rule ref="PSR12.Classes.ClosingBrace"/>
+	<rule ref="PSR12.Files.OpenTag"/>
+	<rule ref="PSR12.Functions.NullableTypeDeclaration"/>
+
+
+	<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
+	<rule ref="Squiz.Classes.DuplicateProperty"/>
+	<rule ref="Squiz.Classes.LowercaseClassKeywords"/>
+	<rule ref="Squiz.Classes.SelfMemberReference"/>
+	<rule ref="Squiz.Commenting.DocCommentAlignment"/>
+	<rule ref="Squiz.Commenting.EmptyCatchComment"/>
+	<rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>
+	<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
+	<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
+	<rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
+	<rule ref="Squiz.ControlStructures.LowercaseDeclaration"/>
+	<rule ref="Squiz.Formatting.OperatorBracket"/>
+	<rule ref="Squiz.Functions.FunctionDeclaration"/>
+	<rule ref="Squiz.Functions.FunctionDuplicateArgument"/>
+	<rule ref="Squiz.Functions.LowercaseFunctionKeywords"/>
+	<rule ref="Squiz.Operators.IncrementDecrementUsage"/>
+	<rule ref="Squiz.Operators.ValidLogicalOperators"/>
+	<rule ref="Squiz.PHP.DisallowMultipleAssignments"/>
+	<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
+	<rule ref="Squiz.PHP.Eval"/>
+	<rule ref="Squiz.PHP.InnerFunctions"/>
+	<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
+	<rule ref="Squiz.PHP.NonExecutableCode"/>
+	<rule ref="Squiz.Scope.MemberVarScope"/>
+	<rule ref="Squiz.Scope.MethodScope"/>
+	<rule ref="Squiz.Scope.StaticThisUsage"/>
+	<rule ref="Squiz.Strings.DoubleQuoteUsage"/>
+	<rule ref="Squiz.Strings.EchoedStrings"/>
+	<rule ref="Squiz.WhiteSpace.CastSpacing"/>
+
+	<rule ref="Squiz.PHP.GlobalKeyword">
+		<exclude-pattern>examples</exclude-pattern>
+	</rule>
+
+	<rule ref="PSR1.Files.SideEffects">
+		<exclude-pattern>examples</exclude-pattern>
+	</rule>
+
+
+	<!--
+		Configurable built-in https://github.com/squizlabs/PHP_CodeSniffer/wiki/Customisable-Sniff-Properties
+	-->
+
 	<rule ref="Generic.ControlStructures.InlineControlStructure">
 		<properties>
 			<property name="error" value="true"/>
@@ -46,10 +218,17 @@
 
 	<rule ref="Generic.Files.LineLength">
 		<properties>
-			<property name="lineLimit" value="130" />
+			<property name="lineLimit" value="140" />
 			<property name="absoluteLineLimit" value="160" />
 			<property name="ignoreComments" value="true" />
 		</properties>
+		<exclude-pattern>examples</exclude-pattern>
+	</rule>
+
+	<rule ref="Generic.Formatting.SpaceAfterNot">
+		<properties>
+			<property name="spacing" value="0" />
+		</properties>
 	</rule>
 
 	<rule ref="Generic.PHP.ForbiddenFunctions">
@@ -65,127 +244,62 @@
 		</properties>
 	</rule>
 
-	<rule ref="Generic.Formatting.SpaceAfterCast">
+	<rule ref="Generic.Strings.UnnecessaryStringConcat">
 		<properties>
-			<property name="spacing" value="0" />
+			<property name="allowMultiline" value="true" />
 		</properties>
 	</rule>
 
-	<rule ref="Generic.Formatting.SpaceAfterNot">
+	<rule ref="Generic.WhiteSpace.ArbitraryParenthesesSpacing">
 		<properties>
-			<property name="spacing" value="0" />
+			<property name="ignoreNewlines" value="true" />
 		</properties>
 	</rule>
 
 	<rule ref="Generic.WhiteSpace.ScopeIndent">
 		<properties>
 			<property name="tabIndent" value="true" />
+			<property name="ignoreIndentationTokens" type="array">
+				<element value="T_COMMENT"/>
+				<element value="T_DOC_COMMENT_OPEN_TAG"/>
+			</property>
 		</properties>
 	</rule>
 
-
-	<rule ref="PEAR">
-		<exclude name="PEAR.Classes" />
-		<exclude name="PEAR.Commenting" />
-		<exclude name="PEAR.ControlStructures" />
-		<exclude name="PEAR.Functions.FunctionCallSignature" />
-		<exclude name="PEAR.Functions.FunctionDeclaration" />
-		<exclude name="PEAR.NamingConventions" />
-		<exclude name="PEAR.WhiteSpace.ScopeIndent" />
-	</rule>
-
-	<rule ref="PEAR.Commenting.FunctionComment">
+	<rule ref="PSR12.ControlStructures.BooleanOperatorPlacement">
 		<properties>
-			<property name="minimumVisibility" value="public" />
+			<property name="allowOnly" value="first" />
 		</properties>
 	</rule>
 
-
-	<rule ref="PSR2">
-		<exclude name="PSR1.Methods.CamelCapsMethodName.NotCamelCaps" />
-
-		<exclude name="PSR2.Classes.ClassDeclaration" />
-		<exclude name="PSR2.ControlStructures.ControlStructureSpacing" />
-		<exclude name="PSR2.ControlStructures.SwitchDeclaration" />
-		<exclude name="PSR2.Methods.FunctionClosingBrace" />
-		<exclude name="PSR2.Namespaces.UseDeclaration.MultipleDeclarations" />
-		<exclude name="PSR2.Namespaces.UseDeclaration.SpaceAfterLastUse" />
-	</rule>
-
-	<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
-		<type>error</type>
-	</rule>
-
-	<rule ref="PSR2.Classes.PropertyDeclaration.Underscore">
-		<type>error</type>
-	</rule>
-
-
-	<rule ref="PSR12">
-		<exclude name="PSR12.Classes.AnonClassDeclaration.SpaceAfterKeyword" />
-		<exclude name="PSR12.Classes.ClassInstantiation" />
-		<exclude name="PSR12.Classes.OpeningBraceSpace" />
-		<exclude name="PSR12.ControlStructures" />
-		<exclude name="PSR12.Files.FileHeader.SpacingInsideBlock" />
-		<exclude name="PSR12.Files.FileHeader.SpacingAfterBlock" />
-		<exclude name="PSR12.Functions.ReturnTypeDeclaration" />
-		<exclude name="PSR12.Operators.OperatorSpacing" />
-		<exclude name="PSR12.Traits.UseDeclaration.MultipleImport" />
-	</rule>
-
 	<rule ref="PSR12.Namespaces.CompoundNamespaceDepth">
 		<properties>
 			<property name="maxDepth" value="1" />
 		</properties>
 	</rule>
 
-	<rule ref="Squiz">
-		<exclude name="Squiz.Arrays.ArrayDeclaration.IndexNoNewline" />
-		<exclude name="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed" />
-		<exclude name="Squiz.Arrays.ArrayDeclaration.ValueNoNewline" />
-		<exclude name="Squiz.Classes.ClassDeclaration" />
-		<exclude name="Squiz.Commenting" />
-		<exclude name="Squiz.ControlStructures.ControlSignature" />
-		<exclude name="Squiz.ControlStructures.ElseIfDeclaration" />
-		<exclude name="Squiz.ControlStructures.InlineIfDeclaration.NotSingleLine" />
-		<exclude name="Squiz.ControlStructures.SwitchDeclaration" />
-		<exclude name="Squiz.Files" />
-		<exclude name="Squiz.Functions" />
-		<exclude name="Squiz.NamingConventions.ValidVariableName.NotCamelCaps" />
-		<exclude name="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps" />
-		<exclude name="Squiz.NamingConventions.ValidFunctionName.ScopeNotCamelCaps" />
-		<exclude name="Squiz.Objects.ObjectInstantiation.NotAssigned" />
-		<exclude name="Squiz.Operators.ComparisonOperatorUsage" />
-		<exclude name="Squiz.PHP.CommentedOutCode" />
-		<exclude name="Squiz.PHP.DisallowBooleanStatement" />
-		<exclude name="Squiz.PHP.DisallowComparisonAssignment" />
-		<exclude name="Squiz.PHP.DisallowInlineIf" />
-		<exclude name="Squiz.PHP.EmbeddedPhp" />
-		<exclude name="Squiz.Strings.ConcatenationSpacing" />
-		<exclude name="Squiz.WhiteSpace" />
-	</rule>
-
-	<rule ref="Squiz.Commenting.EmptyCatchComment" />
-
-	<!-- exclude some checks for the examples-->
-	<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
-		<exclude-pattern>examples</exclude-pattern>
-	</rule>
-
-	<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
-		<exclude-pattern>examples</exclude-pattern>
+	<rule ref="Squiz.Strings.ConcatenationSpacing">
+		<properties>
+			<property name="ignoreNewlines" value="true" />
+		</properties>
 	</rule>
 
-	<rule ref="PSR1.Files.SideEffects.FoundWithSymbols">
-		<exclude-pattern>examples</exclude-pattern>
+	<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
+		<properties>
+			<property name="ignoreNewlines" value="true" />
+		</properties>
 	</rule>
 
-	<rule ref="Squiz.Classes.ClassFileName.NoMatch">
-		<exclude-pattern>examples</exclude-pattern>
+	<rule ref="Squiz.WhiteSpace.OperatorSpacing">
+		<properties>
+			<property name="ignoreNewlines" value="true" />
+		</properties>
 	</rule>
 
-	<rule ref="Squiz.PHP.DiscouragedFunctions.Discouraged">
-		<exclude-pattern>examples</exclude-pattern>
+	<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
+		<properties>
+			<property name="ignoreBlankLines" value="true" />
+		</properties>
 	</rule>
 
 </ruleset>

+ 0 - 5
src/Common/GDLuminanceSource.php

@@ -54,9 +54,6 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
 		$this->setLuminancePixels();
 	}
 
-	/**
-	 *
-	 */
 	private function setLuminancePixels():void{
 
 		for($j = 0; $j < $this->height; $j++){
@@ -70,12 +67,10 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
 
 	}
 
-	/** @inheritDoc */
 	public static function fromFile(string $path, SettingsContainerInterface|QROptions $options = new QROptions):static{
 		return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
 	}
 
-	/** @inheritDoc */
 	public static function fromBlob(string $blob, SettingsContainerInterface|QROptions $options = new QROptions):static{
 		return new self(imagecreatefromstring($blob), $options);
 	}

+ 0 - 13
src/Common/GenericGFPoly.php

@@ -31,7 +31,6 @@ final class GenericGFPoly{
 	/**
 	 * @param int[]      $coefficients array coefficients as ints representing elements of GF(size), arranged
 	 *                                 from most significant (highest-power term) coefficient to the least significant
-	 * @param int|null   $degree
 	 *
 	 * @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
 	 *                                            is not a constant polynomial (that is, it is not the monomial "0")
@@ -116,9 +115,6 @@ final class GenericGFPoly{
 		return $result;
 	}
 
-	/**
-	 *
-	 */
 	public function multiply(GenericGFPoly $other):self{
 
 		if($this->isZero() || $other->isZero()){
@@ -163,9 +159,6 @@ final class GenericGFPoly{
 
 	}
 
-	/**
-	 *
-	 */
 	public function multiplyInt(int $scalar):self{
 
 		if($scalar === 0){
@@ -207,9 +200,6 @@ final class GenericGFPoly{
 		return new self($product);
 	}
 
-	/**
-	 *
-	 */
 	public function mod(GenericGFPoly $other):self{
 
 		if((count($this->coefficients) - count($other->coefficients)) < 0){
@@ -225,9 +215,6 @@ final class GenericGFPoly{
 		return (new self($this->coefficients))->mod($other);
 	}
 
-	/**
-	 *
-	 */
 	public function addOrSubtract(GenericGFPoly $other):self{
 
 		if($this->isZero()){

+ 0 - 5
src/Common/IMagickLuminanceSource.php

@@ -51,9 +51,6 @@ final class IMagickLuminanceSource extends LuminanceSourceAbstract{
 		$this->setLuminancePixels();
 	}
 
-	/**
-	 *
-	 */
 	private function setLuminancePixels():void{
 		$pixels = $this->imagick->exportImagePixels(1, 1, $this->width, $this->height, 'RGB', Imagick::PIXEL_CHAR);
 		$count  = count($pixels);
@@ -63,12 +60,10 @@ final class IMagickLuminanceSource extends LuminanceSourceAbstract{
 		}
 	}
 
-	/** @inheritDoc */
 	public static function fromFile(string $path, SettingsContainerInterface|QROptions $options = new QROptions):static{
 		return new self(new Imagick(self::checkFile($path)), $options);
 	}
 
-	/** @inheritDoc */
 	public static function fromBlob(string $blob, SettingsContainerInterface|QROptions $options = new QROptions):static{
 		$im = new Imagick;
 		$im->readImageBlob($blob);

+ 0 - 10
src/Common/LuminanceSourceAbstract.php

@@ -31,9 +31,6 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
 	protected int   $width;
 	protected int   $height;
 
-	/**
-	 *
-	 */
 	public function __construct(int $width, int $height, SettingsContainerInterface|QROptions $options = new QROptions){
 		$this->width   = $width;
 		$this->height  = $height;
@@ -42,22 +39,18 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
 		$this->luminances = [];
 	}
 
-	/** @inheritDoc */
 	public function getLuminances():array{
 		return $this->luminances;
 	}
 
-	/** @inheritDoc */
 	public function getWidth():int{
 		return $this->width;
 	}
 
-	/** @inheritDoc */
 	public function getHeight():int{
 		return $this->height;
 	}
 
-	/** @inheritDoc */
 	public function getRow(int $y):array{
 
 		if($y < 0 || $y >= $this->getHeight()){
@@ -71,9 +64,6 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
 		return $arr;
 	}
 
-	/**
-	 *
-	 */
 	protected function setLuminancePixel(int $r, int $g, int $b):void{
 		$this->luminances[] = ($r === $g && $g === $b)
 			// Image is already greyscale, so pick any channel.

+ 1 - 0
src/Common/LuminanceSourceInterface.php

@@ -14,6 +14,7 @@ use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 
 /**
+ * Interface for the luminance sources
  */
 interface LuminanceSourceInterface{
 

+ 2 - 12
src/Data/AlphaNum.php

@@ -27,7 +27,9 @@ final class AlphaNum extends QRDataModeAbstract{
 	 * @var int[]
 	 */
 	private const CHAR_TO_ORD = [
+		// phpcs:ignore
 		'0' =>  0, '1' =>  1, '2' =>  2, '3' =>  3, '4' =>  4, '5' =>  5, '6' =>  6, '7' =>  7,
+		// phpcs:ignore
 		'8' =>  8, '9' =>  9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
 		'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
 		'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
@@ -47,21 +49,12 @@ final class AlphaNum extends QRDataModeAbstract{
 		'+', '-', '.', '/', ':',
 	];
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::ALPHANUM;
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 		return (int)ceil($this->getCharCount() * (11 / 2));
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function validateString(string $string):bool{
 
 		if($string === ''){
@@ -77,9 +70,6 @@ final class AlphaNum extends QRDataModeAbstract{
 		return true;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 

+ 0 - 12
src/Data/Byte.php

@@ -21,28 +21,16 @@ use function chr, ord;
  */
 final class Byte extends QRDataModeAbstract{
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::BYTE;
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 		return ($this->getCharCount() * 8);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function validateString(string $string):bool{
 		return $string !== '';
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 

+ 2 - 7
src/Data/ECI.php

@@ -22,9 +22,6 @@ use function mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, spri
  */
 final class ECI extends QRDataModeAbstract{
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::ECI;
 
 	/**
@@ -34,6 +31,7 @@ final class ECI extends QRDataModeAbstract{
 
 	/**
 	 * @inheritDoc
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 * @noinspection PhpMissingParentConstructorInspection
 	 */
 	public function __construct(int $encoding){
@@ -45,9 +43,6 @@ final class ECI extends QRDataModeAbstract{
 		$this->encoding = $encoding;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 
 		if($this->encoding < 128){
@@ -107,7 +102,7 @@ final class ECI extends QRDataModeAbstract{
 			$id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
 		}
 		else{
-			throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte)); // @codeCoverageIgnore
+			throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte));// @codeCoverageIgnore
 		}
 
 		return new ECICharset($id);

+ 0 - 12
src/Data/Hanzi.php

@@ -43,28 +43,16 @@ final class Hanzi extends QRDataModeAbstract{
 	 */
 	public const GB2312_SUBSET = 0b0001;
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::HANZI;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getCharCount():int{
 		return mb_strlen($this->data, self::ENCODING);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 		return ($this->getCharCount() * 13);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function convertEncoding(string $string):string{
 		mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
 

+ 0 - 12
src/Data/Kanji.php

@@ -36,28 +36,16 @@ final class Kanji extends QRDataModeAbstract{
 	 */
 	public const ENCODING = 'SJIS';
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::KANJI;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getCharCount():int{
 		return mb_strlen($this->data, self::ENCODING);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 		return ($this->getCharCount() * 13);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function convertEncoding(string $string):string{
 		mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
 

+ 10 - 14
src/Data/Number.php

@@ -35,21 +35,12 @@ final class Number extends QRDataModeAbstract{
 		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 	];
 
-	/**
-	 * @inheritDoc
-	 */
 	public const DATAMODE = Mode::NUMBER;
 
-	/**
-	 * @inheritDoc
-	 */
 	public function getLengthInBits():int{
 		return (int)ceil($this->getCharCount() * (10 / 3));
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function validateString(string $string):bool{
 
 		if($string === ''){
@@ -65,9 +56,6 @@ final class Number extends QRDataModeAbstract{
 		return true;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 
@@ -102,12 +90,20 @@ final class Number extends QRDataModeAbstract{
 
 	/**
 	 * get the code for the given numeric string
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
 	private function parseInt(string $string):int{
 		$num = 0;
 
-		foreach(unpack('C*', $string) as $chr){
-			$num = ($num * 10 + $chr - 48);
+		$ords = unpack('C*', $string);
+
+		if($ords === false){
+			throw new QRCodeDataException('unpack() error');
+		}
+
+		foreach($ords as $ord){
+			$num = ($num * 10 + $ord - 48);
 		}
 
 		return $num;

+ 2 - 2
src/Data/QRData.php

@@ -208,7 +208,7 @@ final class QRData{
 	/**
 	 * creates a BitBuffer and writes the string data to it
 	 *
-	 * @throws \chillerlan\QRCode\QRCodeException on data overflow
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
 	 */
 	private function writeBitBuffer():void{
 		$MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
@@ -220,7 +220,7 @@ final class QRData{
 		// overflow, likely caused due to invalid version setting
 		if($this->bitBuffer->getLength() > $MAX_BITS){
 			throw new QRCodeDataException(
-				sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS)
+				sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS),
 			);
 		}
 

+ 0 - 3
src/Data/QRDataModeAbstract.php

@@ -44,9 +44,6 @@ abstract class QRDataModeAbstract implements QRDataModeInterface{
 		return strlen($this->data);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function convertEncoding(string $string):string{
 		return $string;
 	}

+ 2 - 0
src/Data/QRMatrix.php

@@ -392,6 +392,7 @@ class QRMatrix{
 
 		foreach($pos as $c){
 			$this
+				// phpcs:ignore
 				->setArea( $c[0]     ,  $c[1]     , 7, 7, true, $this::M_FINDER)
 				->setArea(($c[0] + 1), ($c[1] + 1), 5, 5, false, $this::M_FINDER)
 				->setArea(($c[0] + 2), ($c[1] + 2), 3, 3, true, $this::M_FINDER_DOT)
@@ -422,6 +423,7 @@ class QRMatrix{
 
 		for($c = 0; $c < 3; $c++){
 			for($i = 0; $i < 8; $i++){
+				// phpcs:ignore
 				$this->set( $h[$c][0]      , ($h[$c][1] + $i), false, $this::M_SEPARATOR);
 				$this->set(($v[$c][0] - $i),  $v[$c][1]      , false, $this::M_SEPARATOR);
 			}

+ 0 - 6
src/Decoder/Binarizer.php

@@ -50,9 +50,6 @@ final class Binarizer{
 	/** @var int[] */
 	private array                    $luminances;
 
-	/**
-	 *
-	 */
 	public function __construct(LuminanceSourceInterface $source){
 		$this->source     = $source;
 		$this->luminances = $this->source->getLuminances();
@@ -162,9 +159,6 @@ final class Binarizer{
 		return $this->getHistogramBlackMatrix($width, $height);
 	}
 
-	/**
-	 *
-	 */
 	private function getHistogramBlackMatrix(int $width, int $height):BitMatrix{
 
 		// Quickly calculates the histogram by sampling four rows from the image. This proved to be

+ 1 - 10
src/Decoder/BitMatrix.php

@@ -221,7 +221,7 @@ final class BitMatrix extends QRMatrix{
 			// Try again by actually masking the pattern first.
 			$formatInfo = $this->doDecodeFormatInformation(
 				($formatInfoBits1 ^ $this::FORMAT_INFO_MASK_QR),
-				($formatInfoBits2 ^ $this::FORMAT_INFO_MASK_QR)
+				($formatInfoBits2 ^ $this::FORMAT_INFO_MASK_QR),
 			);
 
 			// still nothing???
@@ -237,9 +237,6 @@ final class BitMatrix extends QRMatrix{
 		return $this;
 	}
 
-	/**
-	 *
-	 */
 	private function copyVersionBit(int $i, int $j, int $versionBits):int{
 
 		$bit = $this->mirror
@@ -380,9 +377,6 @@ final class BitMatrix extends QRMatrix{
 		return null;
 	}
 
-	/**
-	 *
-	 */
 	private function uRShift(int $a, int $b):int{
 
 		if($b === 0){
@@ -392,9 +386,6 @@ final class BitMatrix extends QRMatrix{
 		return (($a >> $b) & ~((1 << (8 * PHP_INT_SIZE - 1)) >> ($b - 1)));
 	}
 
-	/**
-	 *
-	 */
 	private function numBitsDiffering(int $a, int $b):int{
 		// a now has a 1 bit exactly where its bit differs with b's
 		$a ^= $b;

+ 0 - 6
src/Decoder/Decoder.php

@@ -27,9 +27,6 @@ use function chr, str_replace;
  */
 final class Decoder{
 
-	/**
-	 * @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
-	 */
 	private SettingsContainerInterface|QROptions   $options;
 	private Version|null                           $version     = null;
 	private EccLevel|null                          $eccLevel    = null;
@@ -164,9 +161,6 @@ final class Decoder{
 		]);
 	}
 
-	/**
-	 *
-	 */
 	private function decodeAlphanumSegment(int $versionNumber, bool $fc1InEffect):string{
 		$str = AlphaNum::decodeSegment($this->bitBuffer, $versionNumber);
 

+ 1 - 10
src/Decoder/DecoderResult.php

@@ -60,10 +60,7 @@ final class DecoderResult{
 
 	}
 
-	/**
-	 * @return mixed|null
-	 */
-	public function __get(string $property){
+	public function __get(string $property):mixed{
 
 		if(property_exists($this, $property)){
 			return $this->{$property};
@@ -72,16 +69,10 @@ final class DecoderResult{
 		return null;
 	}
 
-	/**
-	 *
-	 */
 	public function __toString():string{
 		return $this->data;
 	}
 
-	/**
-	 *
-	 */
 	public function hasStructuredAppend():bool{
 		return $this->structuredAppendParity >= 0 && $this->structuredAppendSequence >= 0;
 	}

+ 2 - 2
src/Decoder/ReedSolomonDecoder.php

@@ -97,7 +97,7 @@ final class ReedSolomonDecoder{
 		while($longerBlocksStartAt >= 0){
 			$numCodewords = count($result[$longerBlocksStartAt][1]);
 
-			if($numCodewords == $shorterBlocksTotalCodewords){
+			if($numCodewords === $shorterBlocksTotalCodewords){
 				break;
 			}
 
@@ -193,7 +193,7 @@ final class ReedSolomonDecoder{
 		[$sigma, $omega] = $this->runEuclideanAlgorithm(
 			GF256::buildMonomial($numEccCodewords, 1),
 			new GenericGFPoly(array_reverse($syndromeCoefficients)),
-			$numEccCodewords
+			$numEccCodewords,
 		);
 
 		$errorLocations      = $this->findErrorLocations($sigma);

+ 1 - 1
src/Detector/AlignmentPattern.php

@@ -27,7 +27,7 @@ final class AlignmentPattern extends ResultPoint{
 		return new self(
 			(($this->x + $j) / 2.0),
 			(($this->y + $i) / 2.0),
-			(($this->estimatedModuleSize + $newModuleSize) / 2.0)
+			(($this->estimatedModuleSize + $newModuleSize) / 2.0),
 		);
 	}
 

+ 7 - 10
src/Detector/AlignmentPatternFinder.php

@@ -202,9 +202,6 @@ final class AlignmentPatternFinder{
 	 * figures the location of the center of this black/white/black run.
 	 *
 	 * @param int[] $stateCount
-	 * @param int   $end
-	 *
-	 * @return float
 	 */
 	private function centerFromEnd(array $stateCount, int $end):float{
 		return (float)(($end - $stateCount[2]) - $stateCount[1] / 2);
@@ -215,13 +212,12 @@ final class AlignmentPatternFinder{
 	 * "cross-checks" by scanning down vertically through the center of the possible
 	 * alignment pattern to see if the same proportion is detected.
 	 *
-	 * @param int $startI   row where an alignment pattern was detected
-	 * @param int $centerJ  center of the section that appears to cross an alignment pattern
-	 * @param int $maxCount maximum reasonable number of modules that should be
-	 *                      observed in any reading state, based on the results of the horizontal scan
-	 * @param int $originalStateCountTotal
+	 * $startI   row where an alignment pattern was detected
+	 * $centerJ  center of the section that appears to cross an alignment pattern
+	 * $maxCount maximum reasonable number of modules that should be
+	 *           observed in any reading state, based on the results of the horizontal scan
 	 *
-	 * @return float|null vertical center of alignment pattern, or null if not found
+	 * returns vertical center of alignment pattern, or null if not found
 	 */
 	private function crossCheckVertical(int $startI, int $centerJ, int $maxCount, int $originalStateCountTotal):float|null{
 		$maxI          = $this->matrix->getSize();
@@ -257,7 +253,7 @@ final class AlignmentPatternFinder{
 			$i++;
 		}
 
-		if($i == $maxI || $stateCount[1] > $maxCount){
+		if($i === $maxI || $stateCount[1] > $maxCount){
 			return null;
 		}
 
@@ -270,6 +266,7 @@ final class AlignmentPatternFinder{
 			return null;
 		}
 
+		// phpcs:ignore
 		if((5 * abs(($stateCount[0] + $stateCount[1] + $stateCount[2]) - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
 			return null;
 		}

+ 3 - 6
src/Detector/Detector.php

@@ -274,7 +274,7 @@ final class Detector{
 		float $overallEstModuleSize,
 		int $estAlignmentX,
 		int $estAlignmentY,
-		float $allowanceFactor
+		float $allowanceFactor,
 	):AlignmentPattern|null{
 		// Look for an alignment pattern (3 modules in size) around where it should be
 		$dimension           = $this->matrix->getSize();
@@ -301,15 +301,12 @@ final class Detector{
 		);
 	}
 
-	/**
-	 *
-	 */
 	private function createTransform(
 		FinderPattern         $nw,
 		FinderPattern         $ne,
 		FinderPattern         $sw,
 		int                   $size,
-		AlignmentPattern|null $ap = null
+		AlignmentPattern|null $ap = null,
 	):PerspectiveTransform{
 		$dimMinusThree = ($size - 3.5);
 
@@ -343,7 +340,7 @@ final class Detector{
 			$bottomRightX,
 			$bottomRightY,
 			$sw->getX(),
-			$sw->getY()
+			$sw->getY(),
 		);
 	}
 

+ 6 - 18
src/Detector/FinderPattern.php

@@ -24,18 +24,12 @@ final class FinderPattern extends ResultPoint{
 
 	private int $count;
 
-	/**
-	 *
-	 */
 	public function __construct(float $posX, float $posY, float $estimatedModuleSize, int|null $count = null){
 		parent::__construct($posX, $posY, $estimatedModuleSize);
 
 		$this->count = ($count ?? 1);
 	}
 
-	/**
-	 *
-	 */
 	public function getCount():int{
 		return $this->count;
 	}
@@ -46,14 +40,14 @@ final class FinderPattern extends ResultPoint{
 	 * @return float distance between two points
 	 */
 	public function getDistance(FinderPattern $b):float{
-		return self::distance($this->x, $this->y, $b->x, $b->y);
+		return $this->distance($this->x, $this->y, $b->x, $b->y);
 	}
 
 	/**
 	 * Get square of distance between a and b.
 	 */
 	public function getSquaredDistance(FinderPattern $b):float{
-		return self::squaredDistance($this->x, $this->y, $b->x, $b->y);
+		return $this->squaredDistance($this->x, $this->y, $b->x, $b->y);
 	}
 
 	/**
@@ -68,25 +62,19 @@ final class FinderPattern extends ResultPoint{
 			($this->count * $this->x + $j) / $combinedCount,
 			($this->count * $this->y + $i) / $combinedCount,
 			($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount,
-			$combinedCount
+			$combinedCount,
 		);
 	}
 
-	/**
-	 *
-	 */
-	private static function squaredDistance(float $aX, float $aY, float $bX, float $bY):float{
+	private function squaredDistance(float $aX, float $aY, float $bX, float $bY):float{
 		$xDiff = ($aX - $bX);
 		$yDiff = ($aY - $bY);
 
 		return ($xDiff * $xDiff + $yDiff * $yDiff);
 	}
 
-	/**
-	 *
-	 */
-	public static function distance(float $aX, float $aY, float $bX, float $bY):float{
-		return sqrt(self::squaredDistance($aX, $aY, $bX, $bY));
+	public function distance(float $aX, float $aY, float $bX, float $bY):float{
+		return sqrt($this->squaredDistance($aX, $aY, $bX, $bY));
 	}
 
 }

+ 11 - 7
src/Detector/FinderPatternFinder.php

@@ -288,11 +288,13 @@ final class FinderPatternFinder{
 
 		// Now also count down, right from center
 		$i = 1;
+		// phpcs:ignore
 		while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
 			$stateCount[2]++;
 			$i++;
 		}
 
+		// phpcs:ignore
 		while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && !$this->matrix->check(($centerJ + $i), ($centerI + $i))){
 			$stateCount[3]++;
 			$i++;
@@ -302,6 +304,7 @@ final class FinderPatternFinder{
 			return false;
 		}
 
+		// phpcs:ignore
 		while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
 			$stateCount[4]++;
 			$i++;
@@ -319,13 +322,14 @@ final class FinderPatternFinder{
 	 * "cross-checks" by scanning down vertically through the center of the possible
 	 * finder pattern to see if the same proportion is detected.
 	 *
-	 * @param int $startI   row where a finder pattern was detected
-	 * @param int $centerJ  center of the section that appears to cross a finder pattern
-	 * @param int $maxCount maximum reasonable number of modules that should be
-	 *                      observed in any reading state, based on the results of the horizontal scan
-	 * @param int $originalStateCountTotal
+	 * $startI   row where a finder pattern was detected
+	 * $centerJ  center of the section that appears to cross a finder pattern
+	 * $maxCount maximum reasonable number of modules that should be
+	 *           observed in any reading state, based on the results of the horizontal scan
+	 * $originalStateCountTotal
+	 *
+	 * returns vertical center of finder pattern, or null if not found
 	 *
-	 * @return float|null vertical center of finder pattern, or null if not found
 	 * @noinspection DuplicatedCode
 	 */
 	private function crossCheckVertical(int $startI, int $centerJ, int $maxCount, int $originalStateCountTotal):float|null{
@@ -630,7 +634,7 @@ final class FinderPatternFinder{
 
 		usort(
 			$this->possibleCenters,
-			fn(FinderPattern $a, FinderPattern $b) => ($a->getEstimatedModuleSize() <=> $b->getEstimatedModuleSize())
+			fn(FinderPattern $a, FinderPattern $b) => ($a->getEstimatedModuleSize() <=> $b->getEstimatedModuleSize()),
 		);
 
 		$distortion   = PHP_FLOAT_MAX;

+ 9 - 9
src/Detector/GridSampler.php

@@ -153,15 +153,15 @@ final class GridSampler{
 
 			// no need to try/catch as QRMatrix::set() will silently discard out of bounds values
 #			try{
-				for($x = 0; $x < $max; $x += 2){
-					// Black(-ish) pixel
-					$bits->set(
-						intdiv($x, 2),
-						$y,
-						$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
-						QRMatrix::M_DATA
-					);
-				}
+			for($x = 0; $x < $max; $x += 2){
+				// Black(-ish) pixel
+				$bits->set(
+					intdiv($x, 2),
+					$y,
+					$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
+					QRMatrix::M_DATA,
+				);
+			}
 #			}
 #			catch(\Throwable $aioobe){//ArrayIndexOutOfBoundsException
 				// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting

+ 7 - 22
src/Detector/PerspectiveTransform.php

@@ -32,13 +32,10 @@ final class PerspectiveTransform{
 	private float $a32;
 	private float $a33;
 
-	/**
-	 *
-	 */
 	private function set(
 		float $a11, float $a21, float $a31,
 		float $a12, float $a22, float $a32,
-		float $a13, float $a23, float $a33
+		float $a13, float $a23, float $a33,
 	):self{
 		$this->a11 = $a11;
 		$this->a12 = $a12;
@@ -58,19 +55,16 @@ final class PerspectiveTransform{
 	 */
 	public function quadrilateralToQuadrilateral(
 		float $x0, float $y0, float $x1, float $y1, float $x2, float $y2, float $x3, float $y3,
-		float $x0p, float $y0p, float $x1p, float $y1p, float $x2p, float $y2p, float $x3p, float $y3p
+		float $x0p, float $y0p, float $x1p, float $y1p, float $x2p, float $y2p, float $x3p, float $y3p,
 	):self{
 		return (new self)
 			->squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p)
 			->times($this->quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3));
 	}
 
-	/**
-	 *
-	 */
 	private function quadrilateralToSquare(
 		float $x0, float $y0, float $x1, float $y1,
-		float $x2, float $y2, float $x3, float $y3
+		float $x2, float $y2, float $x3, float $y3,
 	):self{
 		// Here, the adjoint serves as the inverse:
 		return $this
@@ -78,9 +72,6 @@ final class PerspectiveTransform{
 			->buildAdjoint();
 	}
 
-	/**
-	 *
-	 */
 	private function buildAdjoint():self{
 		// Adjoint is the transpose of the cofactor matrix:
 		return $this->set(
@@ -92,16 +83,13 @@ final class PerspectiveTransform{
 			($this->a12 * $this->a31 - $this->a11 * $this->a32),
 			($this->a12 * $this->a23 - $this->a13 * $this->a22),
 			($this->a13 * $this->a21 - $this->a11 * $this->a23),
-			($this->a11 * $this->a22 - $this->a12 * $this->a21)
+			($this->a11 * $this->a22 - $this->a12 * $this->a21),
 		);
 	}
 
-	/**
-	 *
-	 */
 	private function squareToQuadrilateral(
 		float $x0, float $y0, float $x1, float $y1,
-		float $x2, float $y2, float $x3, float $y3
+		float $x2, float $y2, float $x3, float $y3,
 	):self{
 		$dx3 = ($x0 - $x1 + $x2 - $x3);
 		$dy3 = ($y0 - $y1 + $y2 - $y3);
@@ -128,13 +116,10 @@ final class PerspectiveTransform{
 			$y0,
 			$a13,
 			$a23,
-			1.0
+			1.0,
 		);
 	}
 
-	/**
-	 *
-	 */
 	private function times(PerspectiveTransform $other):self{
 		return $this->set(
 			($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13),
@@ -145,7 +130,7 @@ final class PerspectiveTransform{
 			($this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33),
 			($this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13),
 			($this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23),
-			($this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33)
+			($this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33),
 		);
 	}
 

+ 0 - 12
src/Detector/ResultPoint.php

@@ -25,32 +25,20 @@ abstract class ResultPoint{
 	protected float $y;
 	protected float $estimatedModuleSize;
 
-	/**
-	 *
-	 */
 	public function __construct(float $x, float $y, float $estimatedModuleSize){
 		$this->x                   = $x;
 		$this->y                   = $y;
 		$this->estimatedModuleSize = $estimatedModuleSize;
 	}
 
-	/**
-	 *
-	 */
 	public function getX():float{
 		return $this->x;
 	}
 
-	/**
-	 *
-	 */
 	public function getY():float{
 		return $this->y;
 	}
 
-	/**
-	 *
-	 */
 	public function getEstimatedModuleSize():float{
 		return $this->estimatedModuleSize;
 	}

+ 1 - 13
src/Output/QREps.php

@@ -24,9 +24,6 @@ class QREps extends QROutputAbstract{
 
 	final public const MIME_TYPE = 'application/postscript';
 
-	/**
-	 * @inheritDoc
-	 */
 	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_array($value) || count($value) < 3){
@@ -49,9 +46,6 @@ class QREps extends QROutputAbstract{
 		return true;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function prepareModuleValue(mixed $value):string{
 		$values = [];
 
@@ -68,9 +62,6 @@ class QREps extends QROutputAbstract{
 		return $this->formatColor($values);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):string{
 		return $this->formatColor(($isDark) ? [0.0, 0.0, 0.0] : [1.0, 1.0, 1.0]);
 	}
@@ -95,14 +86,11 @@ class QREps extends QROutputAbstract{
 			// CMYK
 			? '%f %f %f %f C'
 			// RGB
-			:'%f %f %f R';
+			: '%f %f %f R';
 
 		return sprintf($format, ...$values);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string{
 		[$width, $height] = $this->getOutputDimensions();
 

+ 3 - 6
src/Output/QRFpdf.php

@@ -29,10 +29,9 @@ class QRFpdf extends QROutputAbstract{
 
 	final public const MIME_TYPE = 'application/pdf';
 
-	protected FPDF       $fpdf;
-
 	/** @var int[]  */
 	protected array|null $prevColor = null;
+	protected FPDF       $fpdf;
 
 	/**
 	 * QRFpdf constructor.
@@ -45,6 +44,7 @@ class QRFpdf extends QROutputAbstract{
 			// @codeCoverageIgnoreStart
 			throw new QRCodeOutputException(
 				'The QRFpdf output requires FPDF (https://github.com/Setasign/FPDF)'.
+				// phpcs:ignore
 				' as dependency but the class "\\FPDF" could not be found.'
 			);
 			// @codeCoverageIgnoreEnd
@@ -57,15 +57,12 @@ class QRFpdf extends QROutputAbstract{
 	 * Initializes an FPDF instance
 	 */
 	protected function initFPDF():FPDF{
-		$fpdf =  new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
+		$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
 		$fpdf->AddPage();
 
 		return $fpdf;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null, FPDF|null $fpdf = null):string|FPDF{
 		$this->fpdf = ($fpdf ?? $this->initFPDF());
 

+ 15 - 9
src/Output/QRGdImage.php

@@ -53,8 +53,6 @@ abstract class QRGdImage extends QROutputAbstract{
 	protected bool $upscaled = false;
 
 	/**
-	 * @inheritDoc
-	 *
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 * @noinspection PhpMissingParentConstructorInspection
 	 */
@@ -131,9 +129,6 @@ abstract class QRGdImage extends QROutputAbstract{
 		return $color;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):int{
 		return $this->prepareModuleValue(($isDark) ? [0, 0, 0] : [255, 255, 255]);
 	}
@@ -141,7 +136,7 @@ abstract class QRGdImage extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 *
-	 * @throws \ErrorException
+	 * @throws \ErrorException|\chillerlan\QRCode\Output\QRCodeOutputException
 	 */
 	public function dump(string|null $file = null):string|GdImage{
 
@@ -160,7 +155,13 @@ abstract class QRGdImage extends QROutputAbstract{
 
 		if($this->upscaled){
 			// scale down to the expected size
-			$this->image    = imagescale($this->image, ($this->length / 10), ($this->length / 10));
+			$scaled = imagescale($this->image, ($this->length / 10), ($this->length / 10));
+
+			if($scaled === false){
+				throw new QRCodeOutputException('imagescale() error');
+			}
+
+			$this->image    = $scaled;
 			$this->upscaled = false;
 			// Reset scaled and length values after rescaling image to prevent issues with subclasses that use the output from dump()
 			$this->setMatrixDimensions();
@@ -273,7 +274,7 @@ abstract class QRGdImage extends QROutputAbstract{
 				(($y * $this->scale) + intdiv($this->scale, 2)),
 				(int)($this->circleDiameter * $this->scale),
 				(int)($this->circleDiameter * $this->scale),
-				$color
+				$color,
 			);
 
 			return;
@@ -285,7 +286,7 @@ abstract class QRGdImage extends QROutputAbstract{
 			($y * $this->scale),
 			(($x + 1) * $this->scale),
 			(($y + 1) * $this->scale),
-			$color
+			$color,
 		);
 	}
 
@@ -311,6 +312,11 @@ abstract class QRGdImage extends QROutputAbstract{
 			$this->renderImage();
 
 			$imageData = ob_get_contents();
+
+			if($imageData === false){
+				throw new QRCodeOutputException('ob_get_contents() error');
+			}
+
 			imagedestroy($this->image);
 		}
 		// not going to cover edge cases

+ 0 - 3
src/Output/QRGdImageAVIF.php

@@ -23,9 +23,6 @@ class QRGdImageAVIF extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/avif';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imageavif($this->image, null, max(-1, min(100, $this->options->quality)));
 	}

+ 0 - 3
src/Output/QRGdImageBMP.php

@@ -23,9 +23,6 @@ class QRGdImageBMP extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/bmp';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imagebmp($this->image, null, ($this->options->quality > 0));
 	}

+ 0 - 3
src/Output/QRGdImageGIF.php

@@ -23,9 +23,6 @@ class QRGdImageGIF extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/gif';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imagegif($this->image);
 	}

+ 0 - 6
src/Output/QRGdImageJPEG.php

@@ -23,16 +23,10 @@ class QRGdImageJPEG extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/jpg';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function setTransparencyColor():void{
 		// noop - transparency is not supported
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
 	}

+ 0 - 3
src/Output/QRGdImagePNG.php

@@ -23,9 +23,6 @@ class QRGdImagePNG extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/png';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
 	}

+ 0 - 3
src/Output/QRGdImageWEBP.php

@@ -23,9 +23,6 @@ class QRGdImageWEBP extends QRGdImage{
 
 	final public const MIME_TYPE = 'image/webp';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function renderImage():void{
 		imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
 	}

+ 19 - 12
src/Output/QRImagick.php

@@ -94,23 +94,14 @@ class QRImagick extends QROutputAbstract{
 		return false;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function prepareModuleValue(mixed $value):ImagickPixel{
 		return new ImagickPixel($value);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):ImagickPixel{
 		return $this->prepareModuleValue(($isDark) ? '#000' : '#fff');
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string|Imagick{
 		$this->setBgColor();
 
@@ -131,12 +122,28 @@ class QRImagick extends QROutputAbstract{
 		$this->saveToFile($imageData, $file);
 
 		if($this->options->outputBase64){
-			$imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
+
+			$imageData = $this->toBase64DataURI($imageData, $this->guessMimeType($imageData));
 		}
 
 		return $imageData;
 	}
 
+	/**
+	 * @todo: move to QROutputAbstract
+	 *
+	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+	 */
+	protected function guessMimeType(string $imageData):string{
+		$mime = (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData);
+
+		if($mime === false){
+			throw new QRCodeOutputException('unable to detect mime type');
+		}
+
+		return $mime;
+	}
+
 	/**
 	 * Sets the background color
 	 */
@@ -217,7 +224,7 @@ class QRImagick extends QROutputAbstract{
 				(($x + 0.5) * $this->scale),
 				(($y + 0.5) * $this->scale),
 				(($x + 0.5 + $this->circleRadius) * $this->scale),
-				(($y + 0.5) * $this->scale)
+				(($y + 0.5) * $this->scale),
 			);
 
 			return;
@@ -227,7 +234,7 @@ class QRImagick extends QROutputAbstract{
 			($x * $this->scale),
 			($y * $this->scale),
 			((($x + 1) * $this->scale) - 1),
-			((($y + 1) * $this->scale) - 1)
+			((($y + 1) * $this->scale) - 1),
 		);
 	}
 

+ 2 - 6
src/Output/QRInterventionImage.php

@@ -82,9 +82,6 @@ class QRInterventionImage extends QROutputAbstract{
 		return $this;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string|ImageInterface{
 		[$width, $height] = $this->getOutputDimensions();
 
@@ -126,7 +123,6 @@ class QRInterventionImage extends QROutputAbstract{
 		return $imageData;
 	}
 
-
 	/**
 	 * draws a single pixel at the given position
 	 */
@@ -146,7 +142,7 @@ class QRInterventionImage extends QROutputAbstract{
 				function(CircleFactory $circle) use ($color):void{
 					$circle->radius((int)($this->circleRadius * $this->scale));
 					$circle->background($color);
-				}
+				},
 			);
 
 			return;
@@ -158,7 +154,7 @@ class QRInterventionImage extends QROutputAbstract{
 			function(RectangleFactory $rectangle) use ($color):void{
 				$rectangle->size($this->scale, $this->scale);
 				$rectangle->background($color);
-			}
+			},
 		);
 	}
 

+ 0 - 3
src/Output/QRMarkup.php

@@ -16,9 +16,6 @@ namespace chillerlan\QRCode\Output;
 abstract class QRMarkup extends QROutputAbstract{
 	use CssColorModuleValueTrait;
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string{
 		$saveToFile = $file !== null;
 		$data       = $this->createMarkup($saveToFile);

+ 1 - 4
src/Output/QRMarkupHTML.php

@@ -19,9 +19,6 @@ class QRMarkupHTML extends QRMarkup{
 
 	final public const MIME_TYPE = 'text/html';
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function createMarkup(bool $saveToFile):string{
 		$rows     = [];
 		$cssClass = $this->getCssClass();
@@ -41,7 +38,7 @@ class QRMarkupHTML extends QRMarkup{
 				'<!DOCTYPE html><html lang="none">%2$s<head>%2$s<meta charset="UTF-8">%2$s'.
 					'<title>QR Code</title></head>%2$s<body>%1$s</body>%2$s</html>',
 				$html,
-				$this->eol
+				$this->eol,
 			);
 		}
 

+ 2 - 11
src/Output/QRMarkupSVG.php

@@ -50,16 +50,10 @@ class QRMarkupSVG extends QRMarkup{
 		return parent::moduleValueIsValid($value);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getOutputDimensions():array{
 		return [$this->moduleCount, $this->moduleCount];
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getCssClass(int $M_TYPE = 0):string{
 		return implode(' ', [
 			'qr-'.($this::LAYERNAMES[$M_TYPE] ?? $M_TYPE),
@@ -68,9 +62,6 @@ class QRMarkupSVG extends QRMarkup{
 		]);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function createMarkup(bool $saveToFile):string{
 		$svg = $this->header();
 
@@ -110,7 +101,7 @@ class QRMarkupSVG extends QRMarkup{
 			$this->options->cssClass,
 			$this->getViewBox(),
 			$this->options->svgPreserveAspectRatio,
-			$this->eol
+			$this->eol,
 		);
 
 		if($this->options->svgAddXmlHeader){
@@ -161,7 +152,7 @@ class QRMarkupSVG extends QRMarkup{
 				'<path class="%s" fill="%s" d="%s"/>',
 				$this->getCssClass($M_TYPE),
 				$this->getModuleValue($M_TYPE),
-				$path
+				$path,
 			);
 		}
 

+ 6 - 3
src/Output/QRMarkupXML.php

@@ -28,7 +28,6 @@ class QRMarkupXML extends QRMarkup{
 
 	/**
 	 * @inheritDoc
-	 *
 	 * @return int[]
 	 */
 	protected function getOutputDimensions():array{
@@ -37,6 +36,7 @@ class QRMarkupXML extends QRMarkup{
 
 	/**
 	 * @inheritDoc
+	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
 	protected function createMarkup(bool $saveToFile):string{
 		/** @noinspection PhpComposerExtensionStubsInspection */
@@ -62,6 +62,10 @@ class QRMarkupXML extends QRMarkup{
 
 		$xml = $this->dom->saveXML();
 
+		if($xml === false){
+			throw new QRCodeOutputException('XML error');
+		}
+
 		return $xml;
 	}
 
@@ -129,9 +133,8 @@ class QRMarkupXML extends QRMarkup{
 
 		$module = $this->dom->createElement('module');
 
-		$module->setAttribute('x', $x);
-		$module->setAttribute('dark', ($isDark ? 'true' : 'false'));
 		$module->setAttribute('x', (string)$x);
+		$module->setAttribute('dark', (($isDark) ? 'true' : 'false'));
 		$module->setAttribute('layer', ($this::LAYERNAMES[$M_TYPE] ?? ''));
 		$module->setAttribute('value', (string)$this->getModuleValue($M_TYPE));
 

+ 11 - 4
src/Output/QRStringJSON.php

@@ -12,9 +12,12 @@
 
 namespace chillerlan\QRCode\Output;
 
+use JsonException;
 use function json_encode;
 
 /**
+ * JSON Output
+ *
  * @method string getModuleValue(int $M_TYPE)
  *
  * @phpstan-type Module array{x: int, dark: bool, layer: string, value: string}
@@ -40,12 +43,12 @@ class QRStringJSON extends QROutputAbstract{
 	 */
 	public function dump(string|null $file = null):string{
 		[$width, $height] = $this->getOutputDimensions();
-		$version   = $this->matrix->getVersion();
-		$dimension = $version->getDimension();
+		$version          = $this->matrix->getVersion();
+		$dimension        = $version->getDimension();
 
 		$json = [
 			'$schema' => $this::SCHEMA,
-			'qrcode' => [
+			'qrcode'  => [
 				'version'  => $version->getVersionNumber(),
 				'eccLevel' => (string)$this->matrix->getEccLevel(),
 				'matrix'   => [
@@ -67,7 +70,11 @@ class QRStringJSON extends QROutputAbstract{
 			}
 		}
 
-		$data = json_encode($json, $this->options->jsonFlags);;
+		$data = json_encode($json, $this->options->jsonFlags);
+
+		if($data === false){
+			throw new JsonException('error while encoding JSON');
+		}
 
 		$this->saveToFile($data, $file);
 

+ 1 - 7
src/Output/QRStringText.php

@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Output;
 use function array_map, implode, is_string, max, min, sprintf;
 
 /**
- *
+ * String/plaintext output (for CLI etc.)
  */
 class QRStringText extends QROutputAbstract{
 
@@ -37,16 +37,10 @@ class QRStringText extends QROutputAbstract{
 		return $value;
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function getDefaultModuleValue(bool $isDark):string{
 		return ($isDark) ? '██' : '░░';
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function dump(string|null $file = null):string{
 		$lines     = [];
 		$linestart = $this->options->textLineStart;

+ 2 - 2
src/QRCode.php

@@ -133,7 +133,7 @@ class QRCode{
 				$logoSpaceWidth,
 				$logoSpaceHeight,
 				$this->options->logoSpaceStartX,
-				$this->options->logoSpaceStartY
+				$this->options->logoSpaceStartY,
 			);
 		}
 
@@ -156,7 +156,7 @@ class QRCode{
 			throw new QRCodeOutputException('invalid output class');
 		}
 
-		if(!in_array(QROutputInterface::class, class_implements($outputInterface))){
+		if(!in_array(QROutputInterface::class, class_implements($outputInterface), true)){
 			throw new QRCodeOutputException('output class does not implement QROutputInterface');
 		}
 

+ 2 - 5
src/QROptionsTrait.php

@@ -19,7 +19,6 @@ use chillerlan\QRCode\Output\QRMarkupSVG;
 use function constant, in_array, is_string, max, min, sprintf, strtolower, strtoupper, trim;
 use const JSON_THROW_ON_ERROR, JSON_UNESCAPED_SLASHES, PHP_EOL;
 
-
 /**
  * The QRCode plug-in settings & setter functionality
  *
@@ -346,8 +345,6 @@ trait QROptionsTrait{
 	 *
 	 * @see \imagecolortransparent()
 	 * @see \Imagick::transparentPaintImage()
-	 *
-	 * @var mixed|null
 	 */
 	protected mixed $transparencyColor = null;
 
@@ -463,7 +460,7 @@ trait QROptionsTrait{
 	 *
 	 * @see https://www.php.net/manual/json.constants.php
 	 */
-	protected int $jsonFlags = JSON_THROW_ON_ERROR|JSON_UNESCAPED_SLASHES;
+	protected int $jsonFlags = (JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
 
 
 	/*
@@ -532,7 +529,7 @@ trait QROptionsTrait{
 		if(is_string($eccLevel)){
 			$ecc = strtoupper(trim($eccLevel));
 
-			if(!in_array($ecc, ['L', 'M', 'Q', 'H'])){
+			if(!in_array($ecc, ['L', 'M', 'Q', 'H'], true)){
 				throw new QRCodeException(sprintf('Invalid ECC level: "%s"', $ecc));
 			}
 

+ 9 - 1
tests/BuildDirTrait.php

@@ -81,9 +81,17 @@ trait BuildDirTrait{
 
 	/**
 	 * returns the contents of the given build file
+	 *
+	 * @throws \RuntimeException
 	 */
 	protected function getBuildFileContent(string $fileSubPath):string{
-		return file_get_contents($this->getBuildFilePath($fileSubPath));
+		$content = file_get_contents($this->getBuildFilePath($fileSubPath));
+
+		if($content === false){
+			throw new RuntimeException('file_get_contents() error while reading build file');
+		}
+
+		return $content;
 	}
 
 }

+ 0 - 6
tests/Data/ByteTest.php

@@ -38,16 +38,10 @@ final class ByteTest extends DataInterfaceTestAbstract{
 		];
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testInvalidDataException():void{
 		$this::markTestSkipped('N/A (binary mode)');
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testBinaryStringInvalid():void{
 		$this::markTestSkipped('N/A (binary mode)');
 	}

+ 1 - 1
tests/Data/DataInterfaceTestAbstract.php

@@ -187,7 +187,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		try{
 			$this::assertSame($version->getVersionNumber(), $minimumVersionNumber);
 		}
-		catch(ExpectationFailedException $e){
+		catch(ExpectationFailedException){
 			$this::assertSame(($version->getVersionNumber() + 1), $minimumVersionNumber, 'safety margin');
 		}
 

+ 2 - 2
tests/Data/HanziTest.php

@@ -76,7 +76,7 @@ final class HanziTest extends DataInterfaceTestAbstract{
 		try{
 			$this::assertTrue(Hanzi::validateString($chr));
 		}
-		catch(Throwable $e){
+		catch(Throwable){
 			/** @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist */
 			if(defined('TEST_IS_CI') && TEST_IS_CI === true){
 				$this::markTestSkipped();
@@ -85,7 +85,7 @@ final class HanziTest extends DataInterfaceTestAbstract{
 			$this::markTestSkipped(sprintf(
 				'invalid glyph: %s => %s',
 				bin2hex(mb_convert_encoding($chr, Hanzi::ENCODING, 'UTF-8')),
-				$chr
+				$chr,
 			));
 		}
 	}

+ 2 - 2
tests/Data/KanjiTest.php

@@ -103,7 +103,7 @@ final class KanjiTest extends DataInterfaceTestAbstract{
 		try{
 			$this::assertTrue(Kanji::validateString($chr));
 		}
-		catch(Throwable $e){
+		catch(Throwable){
 			/** @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist */
 			if(defined('TEST_IS_CI') && TEST_IS_CI === true){
 				$this::markTestSkipped();
@@ -112,7 +112,7 @@ final class KanjiTest extends DataInterfaceTestAbstract{
 			$this::markTestSkipped(sprintf(
 				'invalid glyph: %s => %s',
 				bin2hex(mb_convert_encoding($chr, Kanji::ENCODING, 'UTF-8')),
-				$chr
+				$chr,
 			));
 		}
 	}

+ 0 - 3
tests/Data/QRDataTest.php

@@ -21,9 +21,6 @@ use chillerlan\QRCode\QROptions;
 use chillerlan\QRCodeTest\QRMatrixDebugTrait;
 use PHPUnit\Framework\TestCase;
 
-/**
- *
- */
 final class QRDataTest extends TestCase{
 	use QRMatrixDebugTrait;
 

+ 1 - 1
tests/Data/QRMatrixTest.php

@@ -33,7 +33,7 @@ final class QRMatrixTest extends TestCase{
 	protected function setUp():void{
 		$this->matrix = new QRMatrix(
 			new Version($this::version),
-			new EccLevel(EccLevel::L)
+			new EccLevel(EccLevel::L),
 		);
 	}
 

+ 1 - 4
tests/Output/QREpsTest.php

@@ -31,14 +31,11 @@ class QREpsTest extends QROutputTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QREps($options, $matrix);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 7
tests/Output/QRFpdfTest.php

@@ -23,9 +23,6 @@ use function class_exists;
 final class QRFpdfTest extends QROutputTestAbstract{
 	use RGBArrayModuleValueProviderTrait;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function setUp():void{
 
 		if(!class_exists(FPDF::class)){
@@ -37,14 +34,11 @@ final class QRFpdfTest extends QROutputTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRFpdf($options, $matrix);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 4
tests/Output/QRGdImageAVIFTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImageAVIF, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImageAVIFTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImageAVIF($options, $matrix);
 	}

+ 1 - 4
tests/Output/QRGdImageBMPTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImageBMP, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImageBMPTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImageBMP($options, $matrix);
 	}

+ 1 - 4
tests/Output/QRGdImageGIFTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImageGIF, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImageGIFTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImageGIF($options, $matrix);
 	}

+ 1 - 4
tests/Output/QRGdImageJPGTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImageJPEG, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImageJPGTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImageJPEG($options, $matrix);
 	}

+ 1 - 4
tests/Output/QRGdImagePNGTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImagePNG, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImagePNGTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImagePNG($options, $matrix);
 	}

+ 0 - 6
tests/Output/QRGdImageTestAbstract.php

@@ -22,9 +22,6 @@ use function extension_loaded;
 abstract class QRGdImageTestAbstract extends QROutputTestAbstract{
 	use RGBArrayModuleValueProviderTrait;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function setUp():void{
 
 		if(!extension_loaded('gd')){
@@ -34,9 +31,6 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 4
tests/Output/QRGdImageWEBPTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRGdImageWEBP, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRGdImageWEBPTest extends QRGdImageTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRGdImageWEBP($options, $matrix);
 	}

+ 1 - 7
tests/Output/QRImagickTest.php

@@ -24,9 +24,6 @@ use function extension_loaded;
  */
 final class QRImagickTest extends QROutputTestAbstract{
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function setUp():void{
 
 		if(!extension_loaded('imagick')){
@@ -38,7 +35,7 @@ final class QRImagickTest extends QROutputTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRImagick($options, $matrix);
 	}
@@ -69,9 +66,6 @@ final class QRImagickTest extends QROutputTestAbstract{
 		];
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 7
tests/Output/QRInterventionImageTest.php

@@ -24,9 +24,6 @@ use function extension_loaded;
 class QRInterventionImageTest extends QROutputTestAbstract{
 	use CssColorModuleValueProviderTrait;
 
-	/**
-	 * @inheritDoc
-	 */
 	protected function setUp():void{
 
 		if(!extension_loaded('gd')){
@@ -38,14 +35,11 @@ class QRInterventionImageTest extends QROutputTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRInterventionImage($options, $matrix);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 4
tests/Output/QRMarkupHTMLTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRMarkupHTML, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRMarkupHTML($options, $matrix);
 	}

+ 1 - 4
tests/Output/QRMarkupSVGTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRMarkupSVG, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRMarkupSVG($options, $matrix);
 	}

+ 0 - 3
tests/Output/QRMarkupTestAbstract.php

@@ -18,9 +18,6 @@ use chillerlan\QRCode\Data\QRMatrix;
 abstract class QRMarkupTestAbstract extends QROutputTestAbstract{
 	use CssColorModuleValueProviderTrait;
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 		$this->options->outputBase64     = false;
 		$this->options->drawLightModules = true;

+ 0 - 3
tests/Output/QRMarkupXMLTest.php

@@ -15,9 +15,6 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRMarkupXML, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 class QRMarkupXMLTest extends QRMarkupTestAbstract{
 
 	protected function getOutputInterface(

+ 1 - 7
tests/Output/QRStringJSONTest.php

@@ -15,22 +15,16 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QROutputInterface, QRStringJSON};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRStringJSONTest extends QROutputTestAbstract{
 	use CssColorModuleValueProviderTrait;
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRStringJSON($options, $matrix);
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 7
tests/Output/QRStringTextTest.php

@@ -15,14 +15,11 @@ use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QROutputInterface, QRStringText};
 use chillerlan\Settings\SettingsContainerInterface;
 
-/**
- *
- */
 final class QRStringTextTest extends QROutputTestAbstract{
 
 	protected function getOutputInterface(
 		SettingsContainerInterface|QROptions $options,
-		QRMatrix                             $matrix
+		QRMatrix                             $matrix,
 	):QROutputInterface{
 		return new QRStringText($options, $matrix);
 	}
@@ -39,9 +36,6 @@ final class QRStringTextTest extends QROutputTestAbstract{
 		];
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	public function testSetModuleValues():void{
 
 		$this->options->moduleValues = [

+ 1 - 1
tests/QRCodeReaderGDTest.php

@@ -21,7 +21,7 @@ final class QRCodeReaderGDTest extends QRCodeReaderTestAbstract{
 
 	protected function getLuminanceSourceFromFile(
 		string                               $file,
-		SettingsContainerInterface|QROptions $options
+		SettingsContainerInterface|QROptions $options,
 	):LuminanceSourceInterface{
 		return GDLuminanceSource::fromFile($file, $options);
 	}

+ 1 - 1
tests/QRCodeReaderImagickTest.php

@@ -36,7 +36,7 @@ final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 
 	protected function getLuminanceSourceFromFile(
 		string                               $file,
-		SettingsContainerInterface|QROptions $options
+		SettingsContainerInterface|QROptions $options,
 	):LuminanceSourceInterface{
 		return IMagickLuminanceSource::fromFile($file, $options);
 	}

+ 10 - 3
tests/QRCodeReaderTestAbstract.php

@@ -20,6 +20,7 @@ use chillerlan\Settings\SettingsContainerInterface;
 use PHPUnit\Framework\Attributes\{DataProvider, Group};
 use PHPUnit\Framework\TestCase;
 use Exception, Generator;
+use RuntimeException;
 use function array_map, defined, realpath, sprintf, str_repeat, substr;
 
 /**
@@ -83,7 +84,7 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 
 	abstract protected function getLuminanceSourceFromFile(
 		string                               $file,
-		SettingsContainerInterface|QROptions $options
+		SettingsContainerInterface|QROptions $options,
 	):LuminanceSourceInterface;
 
 	#[Group('slow')]
@@ -95,7 +96,13 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 			$this->options->readerIncreaseContrast = true;
 		}
 
-		$luminanceSource = $this->getLuminanceSourceFromFile(realpath($this::samplesDir.$img), $this->options);
+		$file = realpath($this::samplesDir.$img);
+
+		if($file === false){
+			throw new RuntimeException(sprintf('invalid file given: "%s" in samples directory "%s"', $img, $this::samplesDir));
+		}
+
+		$luminanceSource = $this->getLuminanceSourceFromFile($file, $this->options);
 		$result          = (new Decoder)->decode($luminanceSource);
 
 		$this->debugMatrix($result->getQRMatrix());
@@ -141,7 +148,7 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 				yield 'version: '.$version.$eccLevel => [
 					$version,
 					$eccLevel,
-					substr($str, 0, (self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel) ?? '')),
+					substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
 				];
 			}
 		}

+ 1 - 1
tests/QRMatrixDebugTrait.php

@@ -72,7 +72,7 @@ trait QRMatrixDebugTrait{
 
 		$out = (new QRStringText($options, $matrix))->dump();
 
-		printf("\n\n%s\n\n", $out) ;
+		printf("\n\n%s\n\n", $out);
 	}
 
 	/**

+ 1 - 1
tests/QRMaxLengthTrait.php

@@ -25,7 +25,7 @@ trait QRMaxLengthTrait{
 	 *
 	 * @var int[][][]
 	 */
-	protected const MAX_LENGTH =[
+	protected const MAX_LENGTH = [
 	//	v  => [NUMERIC => [L, M, Q, H ], ALPHANUM => [L, M, Q, H], BINARY => [L, M, Q, H  ], KANJI => [L, M, Q, H   ]]
 		1  => [[  41,   34,   27,   17], [  25,   20,   16,   10], [  17,   14,   11,    7], [  10,    8,    7,    4]],
 		2  => [[  77,   63,   48,   34], [  47,   38,   29,   20], [  32,   26,   20,   14], [  20,   16,   12,    8]],