Просмотр исходного кода

:fire: v6.0 first pass: PHP 8.2, remove deprecated elements, add type hints

smiley 2 лет назад
Родитель
Сommit
39e3dfe83b
80 измененных файлов с 332 добавлено и 1029 удалено
  1. 7 10
      .phan/config.php
  2. 5 5
      composer.json
  3. 4 6
      examples/custom_output.php
  4. 2 2
      examples/eps.php
  5. 2 2
      examples/fpdf.php
  6. 5 5
      examples/html.php
  7. 2 2
      examples/image.php
  8. 1 3
      examples/imageWithRoundedShapes.php
  9. 2 2
      examples/imageWithText.php
  10. 2 2
      examples/imagick.php
  11. 0 2
      examples/imagickConvertSVGtoPNG.php
  12. 0 2
      examples/imagickImageAsBackground.php
  13. 1 2
      examples/imagickWithLogo.php
  14. 2 2
      examples/reflectance.php
  15. 2 2
      examples/svg.php
  16. 2 2
      examples/svgConvertViaCanvas.php
  17. 1 2
      examples/svgMeltedModules.php
  18. 1 2
      examples/svgRandomColoredDots.php
  19. 1 2
      examples/svgRoundQuietzone.php
  20. 1 2
      examples/svgWithLogo.php
  21. 1 2
      examples/svgWithLogoAndCustomShapes.php
  22. 7 7
      examples/text.php
  23. 0 3
      src/Common/EccLevel.php
  24. 11 25
      src/Common/GDLuminanceSource.php
  25. 7 6
      src/Common/IMagickLuminanceSource.php
  26. 2 3
      src/Common/LuminanceSourceAbstract.php
  27. 2 2
      src/Common/LuminanceSourceInterface.php
  28. 1 1
      src/Data/AlphaNum.php
  29. 1 1
      src/Data/Byte.php
  30. 1 1
      src/Data/ECI.php
  31. 1 1
      src/Data/Hanzi.php
  32. 1 1
      src/Data/Kanji.php
  33. 1 1
      src/Data/Number.php
  34. 3 4
      src/Data/QRData.php
  35. 1 1
      src/Data/QRDataModeInterface.php
  36. 18 63
      src/Data/QRMatrix.php
  37. 6 6
      src/Decoder/BitMatrix.php
  38. 1 1
      src/Detector/AlignmentPattern.php
  39. 1 1
      src/Detector/FinderPattern.php
  40. 4 6
      src/Output/QREps.php
  41. 6 9
      src/Output/QRFpdf.php
  42. 22 65
      src/Output/QRGdImage.php
  43. 1 1
      src/Output/QRGdImageBMP.php
  44. 1 1
      src/Output/QRGdImageGIF.php
  45. 1 1
      src/Output/QRGdImageJPEG.php
  46. 1 1
      src/Output/QRGdImagePNG.php
  47. 1 1
      src/Output/QRGdImageWEBP.php
  48. 0 19
      src/Output/QRImage.php
  49. 5 6
      src/Output/QRImagick.php
  50. 2 2
      src/Output/QRMarkup.php
  51. 1 1
      src/Output/QRMarkupHTML.php
  52. 3 3
      src/Output/QRMarkupSVG.php
  53. 11 19
      src/Output/QROutputAbstract.php
  54. 16 112
      src/Output/QROutputInterface.php
  55. 0 111
      src/Output/QRString.php
  56. 3 3
      src/Output/QRStringJSON.php
  57. 4 4
      src/Output/QRStringText.php
  58. 17 207
      src/QRCode.php
  59. 32 172
      src/QROptionsTrait.php
  60. 2 4
      tests/Data/QRDataTest.php
  61. 21 22
      tests/Data/QRMatrixTest.php
  62. 1 3
      tests/Output/QREpsTest.php
  63. 2 3
      tests/Output/QRFpdfTest.php
  64. 1 3
      tests/Output/QRGdImageBMPTest.php
  65. 1 3
      tests/Output/QRGdImageGIFTest.php
  66. 0 2
      tests/Output/QRGdImageJPGTest.php
  67. 0 2
      tests/Output/QRGdImagePNGTest.php
  68. 1 3
      tests/Output/QRGdImageWEBPTest.php
  69. 2 3
      tests/Output/QRImagickTest.php
  70. 2 3
      tests/Output/QRMarkupHTMLTest.php
  71. 2 3
      tests/Output/QRMarkupSVGTest.php
  72. 3 3
      tests/Output/QROutputTestAbstract.php
  73. 1 3
      tests/Output/QRStringJSONTest.php
  74. 1 3
      tests/Output/QRStringTEXTTest.php
  75. 3 4
      tests/Performance/output.php
  76. 4 4
      tests/Performance/qrcode.php
  77. 4 4
      tests/QRCodeReaderTestAbstract.php
  78. 2 15
      tests/QRCodeTest.php
  79. 4 6
      tests/QRMaxLengthTrait.php
  80. 35 0
      tests/QROptionsTest.php

+ 7 - 10
.phan/config.php

@@ -14,8 +14,8 @@ return [
 	// Note that the **only** effect of choosing `'5.6'` is to infer
 	// that functions removed in php 7.0 exist.
 	// (See `backward_compatibility_checks` for additional options)
-	'target_php_version' => null,
-	'minimum_target_php_version' => '7.4',
+	'target_php_version'              => null,
+	'minimum_target_php_version'      => '8.2',
 
 	// A list of directories that should be parsed for class and
 	// method information. After excluding the directories
@@ -24,19 +24,19 @@ return [
 	//
 	// Thus, both first-party and third-party code being used by
 	// your application should be included in this list.
-	'directory_list' => [
+	'directory_list'                  => [
 		'examples',
 		'src',
 		'tests',
 		'vendor',
-		'.phan/stubs'
+		'.phan/stubs',
 	],
 
 	// A regex used to match every file name that you want to
 	// exclude from parsing. Actual value will exclude every
 	// "test", "tests", "Test" and "Tests" folders found in
 	// "vendor/" directory.
-	'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
+	'exclude_file_regex'              => '@^vendor/.*/(tests?|Tests?)/@',
 
 	// A directory list that defines files that will be excluded
 	// from static analysis, but whose class and method
@@ -51,12 +51,9 @@ return [
 	//       and `exclude_analysis_directory_list` arrays.
 	'exclude_analysis_directory_list' => [
 		'vendor/',
-		'.phan/stubs'
+		'.phan/stubs',
 	],
-	'suppress_issue_types' => [
+	'suppress_issue_types'            => [
 		'PhanAccessMethodInternal',
-		'PhanAccessOverridesFinalConstant',
-		'PhanDeprecatedClass',
-		'PhanDeprecatedClassConstant',
 	],
 ];

+ 5 - 5
composer.json

@@ -1,6 +1,6 @@
 {
 	"name": "chillerlan/php-qrcode",
-	"description": "A QR code generator and reader with a user friendly API. PHP 7.4+",
+	"description": "A QR code generator and reader with a user friendly API. PHP 8.2+",
 	"homepage": "https://github.com/chillerlan/php-qrcode",
 	"license": [
 		"MIT", "Apache-2.0"
@@ -40,15 +40,15 @@
 	"minimum-stability": "stable",
 	"prefer-stable": true,
 	"require": {
-		"php": "^7.4 || ^8.0",
+		"php": "^8.2",
 		"ext-mbstring": "*",
-		"chillerlan/php-settings-container": "^2.1.4 || ^3.1"
+		"chillerlan/php-settings-container": "^3.1"
 	},
 	"require-dev": {
-		"chillerlan/php-authenticator": "^4.0 || ^5.0",
+		"chillerlan/php-authenticator": "^5.0",
 		"phan/phan": "^5.4",
 		"phpunit/phpunit": "^9.6",
-		"phpmd/phpmd": "^2.13",
+		"phpmd/phpmd": "^2.14",
 		"setasign/fpdf": "^1.8.2",
 		"squizlabs/php_codesniffer": "^3.7"
 	},

+ 4 - 6
examples/custom_output.php

@@ -11,8 +11,7 @@
  */
 
 use chillerlan\QRCode\{QRCode, QROptions};
-use chillerlan\QRCode\Common\EccLevel;
-use chillerlan\QRCode\Output\{QROutputAbstract, QROutputInterface};
+use chillerlan\QRCode\Output\QROutputAbstract;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -25,7 +24,7 @@ class MyCustomOutput extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 		// TODO: Implement moduleValueIsValid() method. (interface)
 		return false;
 	}
@@ -33,7 +32,7 @@ class MyCustomOutput extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value){
+	protected function prepareModuleValue(mixed $value):mixed{
 		// TODO: Implement prepareModuleValue() method. (abstract)
 		return null;
 	}
@@ -73,7 +72,7 @@ class MyCustomOutput extends QROutputAbstract{
 $options = new QROptions;
 
 $options->version  = 5;
-$options->eccLevel = EccLevel::L;
+$options->eccLevel = 'L';
 
 $data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
 
@@ -89,7 +88,6 @@ $qrOutputInterface = new MyCustomOutput($options, $qrcode->getQRMatrix());
 var_dump($qrOutputInterface->dump());
 
 // or just via the options
-$options->outputType      = QROutputInterface::CUSTOM;
 $options->outputInterface = MyCustomOutput::class;
 
 var_dump((new QRCode($options))->render($data));

+ 2 - 2
examples/eps.php

@@ -10,14 +10,14 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QREps;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
 $options->version          = 7;
-$options->outputType       = QROutputInterface::EPS;
+$options->outputInterface  = QREps::class;
 $options->scale            = 5;
 $options->drawLightModules = false;
 // colors can be specified either as [R, G, B] or [C, M, Y, K] (0-255)

+ 2 - 2
examples/fpdf.php

@@ -9,14 +9,14 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRFpdf;
 
 require_once __DIR__ . '/../vendor/autoload.php';
 
 $options = new QROptions;
 
 $options->version          = 7;
-$options->outputType       = QROutputInterface::FPDF;
+$options->outputInterface  = QRFpdf::class;
 $options->scale            = 5;
 $options->fpdfMeasureUnit  = 'mm';
 $options->bgColor          = [222, 222, 222];

+ 5 - 5
examples/html.php

@@ -10,16 +10,16 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRMarkupHTML;
 
 require_once '../vendor/autoload.php';
 
 $options = new QROptions;
 
-$options->version      = 5;
-$options->outputType   = QROutputInterface::MARKUP_HTML;
-$options->cssClass     = 'qrcode';
-$options->moduleValues = [
+$options->version         = 5;
+$options->outputInterface = QRMarkupHTML::class;
+$options->cssClass        = 'qrcode';
+$options->moduleValues    = [
 	// finder
 	QRMatrix::M_FINDER_DARK    => '#A71111', // dark (true)
 	QRMatrix::M_FINDER_DOT     => '#A71111', // finder dot, dark (true)

+ 2 - 2
examples/image.php

@@ -10,14 +10,14 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRGdImagePNG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
 $options->version             = 7;
-$options->outputType          = QROutputInterface::GDIMAGE_PNG;
+$options->outputInterface     = QRGdImagePNG::class;
 $options->scale               = 20;
 $options->outputBase64        = false;
 $options->bgColor             = [200, 150, 200];

+ 1 - 3
examples/imageWithRoundedShapes.php

@@ -16,7 +16,6 @@
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\QRGdImagePNG;
-use chillerlan\QRCode\Output\QROutputInterface;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
@@ -30,7 +29,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
 class QRGdRounded extends QRGdImagePNG{
 
 	/** @inheritDoc */
-	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 		// enable the internal scaling for better rounding results at scale < 20
 		$options->drawCircularModules = true;
 
@@ -137,7 +136,6 @@ class QRGdRounded extends QRGdImagePNG{
 $options = new QROptions([
     'version'         => 7,
     'eccLevel'        => EccLevel::H,
-    'outputType'      => QROutputInterface::CUSTOM,
     'outputInterface' => QRGdRounded::class,
     'outputBase64'    => false,
     'scale'           => 30,

+ 2 - 2
examples/imageWithText.php

@@ -13,7 +13,7 @@
  */
 
 use chillerlan\QRCode\{QRCode, QROptions};
-use chillerlan\QRCode\Output\{QROutputInterface, QRGdImagePNG};
+use chillerlan\QRCode\Output\QRGdImagePNG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -66,7 +66,7 @@ class QRImageWithText extends QRGdImagePNG{
 		$background  = imagecolorallocate($this->image, ...$textBG);
 
 		// allow transparency
-		if($this->options->imageTransparent && $this->options->outputType !== QROutputInterface::GDIMAGE_JPG){
+		if($this->options->imageTransparent){
 			imagecolortransparent($this->image, $background);
 		}
 

+ 2 - 2
examples/imagick.php

@@ -10,14 +10,14 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRImagick;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
 $options->version             = 7;
-$options->outputType          = QROutputInterface::IMAGICK;
+$options->outputInterface     = QRImagick::class;
 $options->imagickFormat       = 'webp';
 $options->quality             = 90;
 $options->scale               = 20;

+ 0 - 2
examples/imagickConvertSVGtoPNG.php

@@ -18,7 +18,6 @@
 
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\QRMarkupSVG;
-use chillerlan\QRCode\Output\QROutputInterface;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
 
@@ -85,7 +84,6 @@ class SVGConvert extends QRMarkupSVG{
 $options = new QROptions;
 
 $options->version              = 7;
-$options->outputType           = QROutputInterface::CUSTOM;
 $options->outputInterface      = SVGConvert::class;
 $options->imagickFormat        = 'png32';
 $options->scale                = 20;

+ 0 - 2
examples/imagickImageAsBackground.php

@@ -10,7 +10,6 @@
 
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Output\QRImagick;
-use chillerlan\QRCode\Output\QROutputInterface;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QRCodeException;
 use chillerlan\QRCode\QROptions;
@@ -91,7 +90,6 @@ $options = new ImageAsBackgroundOptions;
 
 $options->background           = __DIR__.'/background.jpg'; // setting from the augmented options
 $options->version              = 5;
-$options->outputType           = QROutputInterface::CUSTOM;
 $options->outputInterface      = QRImagickImageAsBackground::class; // use the custom output class
 $options->eccLevel             = EccLevel::H;
 $options->outputBase64         = false;

+ 1 - 2
examples/imagickWithLogo.php

@@ -13,7 +13,7 @@
 use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QRImagick, QROutputInterface};
+use chillerlan\QRCode\Output\QRImagick;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -98,7 +98,6 @@ $options = new ImagickWithLogoOptions;
 
 $options->pngLogo             = __DIR__.'/octocat.png'; // setting from the augmented options
 $options->version             = 5;
-$options->outputType          = QROutputInterface::CUSTOM;
 $options->outputInterface     = QRImagickWithLogo::class; // use the custom output class
 $options->eccLevel            = EccLevel::H;
 $options->outputBase64        = false;

+ 2 - 2
examples/reflectance.php

@@ -10,7 +10,7 @@
 
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRMarkupSVG;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
 
@@ -18,7 +18,7 @@ require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
-$options->outputType          = QROutputInterface::MARKUP_SVG;
+$options->outputInterface     = QRMarkupSVG::class;
 $options->outputBase64        = false;
 $options->svgAddXmlHeader     = false;
 $options->connectPaths        = true;

+ 2 - 2
examples/svg.php

@@ -12,14 +12,14 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
 $options->version              = 7;
-$options->outputType           = QROutputInterface::MARKUP_SVG;
+$options->outputInterface      = QRMarkupSVG::class;
 $options->outputBase64         = false;
 // if set to false, the light modules won't be rendered
 $options->drawLightModules     = true;

+ 2 - 2
examples/svgConvertViaCanvas.php

@@ -10,7 +10,7 @@
 
 
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRMarkupSVG;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
 
@@ -19,7 +19,7 @@ require_once __DIR__.'/../vendor/autoload.php';
 $options = new QROptions;
 
 $options->version              = 7;
-$options->outputType           = QROutputInterface::MARKUP_SVG;
+$options->outputInterface      = QRMarkupSVG::class;
 $options->outputBase64         = false;
 $options->svgAddXmlHeader      = false;
 $options->svgUseFillAttributes = false;

+ 1 - 2
examples/svgMeltedModules.php

@@ -13,7 +13,7 @@
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -256,7 +256,6 @@ $options->inverseMelt        = true;
 $options->meltRadius         = 0.4;
 
 $options->version            = 7;
-$options->outputType         = QROutputInterface::CUSTOM;
 $options->outputInterface    = MeltedSVGQRCodeOutput::class;
 $options->outputBase64       = false;
 $options->addQuietzone       = true;

+ 1 - 2
examples/svgRandomColoredDots.php

@@ -15,7 +15,7 @@
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -126,7 +126,6 @@ $options->svgDefs = '
 	]]></style>';
 
 // set the custom output interface
-$options->outputType          = QROutputInterface::CUSTOM;
 $options->outputInterface     = RandomDotsSVGOutput::class;
 
 // common qrcode options

+ 1 - 2
examples/svgRoundQuietzone.php

@@ -14,7 +14,7 @@
 
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
 
 require_once __DIR__.'/../vendor/autoload.php';
@@ -326,7 +326,6 @@ $options->version             = 7;
 $options->eccLevel            = EccLevel::H;
 $options->addQuietzone        = false; // we're not adding a quiet zone, this is done internally in our own module
 $options->outputBase64        = false; // avoid base64 URI output for the example
-$options->outputType          = QROutputInterface::CUSTOM;
 $options->outputInterface     = RoundQuietzoneSVGoutput::class; // load our own output class
 $options->drawLightModules    = false; // set to true to add the light modules
 // common SVG options

+ 1 - 2
examples/svgWithLogo.php

@@ -13,7 +13,7 @@
 use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -113,7 +113,6 @@ $options->svgLogoScale        = 0.25;
 $options->svgLogoCssClass     = 'dark';
 // QROptions
 $options->version             = 5;
-$options->outputType          = QROutputInterface::CUSTOM;
 $options->outputInterface     = QRSvgWithLogo::class;
 $options->outputBase64        = false;
 $options->eccLevel            = EccLevel::H; // ECC level H is necessary when using logos

+ 1 - 2
examples/svgWithLogoAndCustomShapes.php

@@ -15,7 +15,7 @@
 use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
 use chillerlan\QRCode\Common\EccLevel;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
@@ -174,7 +174,6 @@ $options->svgLogoCssClass = 'qr-logo dark';
 // QROptions
 $options->version         = 5;
 $options->quietzoneSize   = 4;
-$options->outputType      = QROutputInterface::CUSTOM;
 $options->outputInterface = QRSvgWithLogoAndCustomShapes::class;
 $options->outputBase64    = false;
 $options->eccLevel        = EccLevel::H; // ECC level H is required when using logos

+ 7 - 7
examples/text.php

@@ -10,18 +10,18 @@
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QROutputInterface, QRStringText};
+use chillerlan\QRCode\Output\QRStringText;
 
 require_once __DIR__.'/../vendor/autoload.php';
 
 $options = new QROptions;
 
-$options->version       = 3;
-$options->quietzoneSize = 2;
-$options->outputType    = QROutputInterface::STRING_TEXT;
-$options->eol           = "\n";
-$options->textLineStart = str_repeat(' ', 6);
-$options->moduleValues  = [
+$options->version         = 3;
+$options->quietzoneSize   = 2;
+$options->outputInterface = QRStringText::class;
+$options->eol             = "\n";
+$options->textLineStart   = str_repeat(' ', 6);
+$options->moduleValues    = [
 	QRMatrix::M_FINDER_DARK    => QRStringText::ansi8('██', 124),
 	QRMatrix::M_FINDER         => QRStringText::ansi8('░░', 124),
 	QRMatrix::M_FINDER_DOT     => QRStringText::ansi8('██', 124),

+ 0 - 3
src/Common/EccLevel.php

@@ -143,9 +143,6 @@ final class EccLevel{
 	/**
 	 * @param int $eccLevel containing the two bits encoding a QR Code's error correction level
 	 *
-	 * @todo: accept string values (PHP8+)
-	 * @see https://github.com/chillerlan/php-qrcode/discussions/160
-	 *
 	 * @throws \chillerlan\QRCode\QRCodeException
 	 */
 	public function __construct(int $eccLevel){

+ 11 - 25
src/Common/GDLuminanceSource.php

@@ -13,41 +13,27 @@
 
 namespace chillerlan\QRCode\Common;
 
-use chillerlan\QRCode\Decoder\QRCodeDecoderException;
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
-use function file_get_contents, get_resource_type, imagecolorat, imagecolorsforindex,
-	imagecreatefromstring, imagefilter, imagesx, imagesy, is_resource;
-use const IMG_FILTER_BRIGHTNESS, IMG_FILTER_CONTRAST, IMG_FILTER_GRAYSCALE, IMG_FILTER_NEGATE, PHP_MAJOR_VERSION;
+use GdImage;
+use function file_get_contents, imagecolorat, imagecolorsforindex,
+	imagecreatefromstring, imagefilter, imagesx, imagesy;
+use const IMG_FILTER_BRIGHTNESS, IMG_FILTER_CONTRAST, IMG_FILTER_GRAYSCALE, IMG_FILTER_NEGATE;
 
 /**
  * This class is used to help decode images from files which arrive as GD Resource
  * It does not support rotation.
  */
-class GDLuminanceSource extends LuminanceSourceAbstract{
+final class GDLuminanceSource extends LuminanceSourceAbstract{
 
-	/**
-	 * @var resource|\GdImage
-	 */
-	protected $gdImage;
+	private GdImage $gdImage;
 
 	/**
 	 * GDLuminanceSource constructor.
 	 *
-	 * @param resource|\GdImage                                    $gdImage
-	 * @param \chillerlan\Settings\SettingsContainerInterface|null $options
-	 *
 	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
 	 */
-	public function __construct($gdImage, SettingsContainerInterface $options = null){
-
-		/** @noinspection PhpFullyQualifiedNameUsageInspection */
-		if(
-			(PHP_MAJOR_VERSION >= 8 && !$gdImage instanceof \GdImage) // @todo: remove version check in v6
-			|| (PHP_MAJOR_VERSION < 8 && (!is_resource($gdImage) || get_resource_type($gdImage) !== 'gd'))
-		){
-			throw new QRCodeDecoderException('Invalid GD image source.'); // @codeCoverageIgnore
-		}
-
+	public function __construct(GdImage $gdImage, SettingsContainerInterface|QROptions $options = null){
 		parent::__construct(imagesx($gdImage), imagesy($gdImage), $options);
 
 		$this->gdImage = $gdImage;
@@ -71,7 +57,7 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
 	/**
 	 *
 	 */
-	protected function setLuminancePixels():void{
+	private function setLuminancePixels():void{
 
 		for($j = 0; $j < $this->height; $j++){
 			for($i = 0; $i < $this->width; $i++){
@@ -85,12 +71,12 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
 	}
 
 	/** @inheritDoc */
-	public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
+	public static function fromFile(string $path, SettingsContainerInterface $options = null):static{
 		return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
 	}
 
 	/** @inheritDoc */
-	public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
+	public static function fromBlob(string $blob, SettingsContainerInterface $options = null):static{
 		return new self(imagecreatefromstring($blob), $options);
 	}
 

+ 7 - 6
src/Common/IMagickLuminanceSource.php

@@ -13,6 +13,7 @@
 
 namespace chillerlan\QRCode\Common;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 use Imagick;
 use function count;
@@ -21,14 +22,14 @@ use function count;
  * This class is used to help decode images from files which arrive as Imagick Resource
  * It does not support rotation.
  */
-class IMagickLuminanceSource extends LuminanceSourceAbstract{
+final class IMagickLuminanceSource extends LuminanceSourceAbstract{
 
-	protected Imagick $imagick;
+	private Imagick $imagick;
 
 	/**
 	 * IMagickLuminanceSource constructor.
 	 */
-	public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
+	public function __construct(Imagick $imagick, SettingsContainerInterface|QROptions $options = null){
 		parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
 
 		$this->imagick = $imagick;
@@ -53,7 +54,7 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
 	/**
 	 *
 	 */
-	protected function setLuminancePixels():void{
+	private function setLuminancePixels():void{
 		$pixels = $this->imagick->exportImagePixels(1, 1, $this->width, $this->height, 'RGB', Imagick::PIXEL_CHAR);
 		$count  = count($pixels);
 
@@ -63,12 +64,12 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
 	}
 
 	/** @inheritDoc */
-	public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
+	public static function fromFile(string $path, SettingsContainerInterface $options = null):static{
 		return new self(new Imagick(self::checkFile($path)), $options);
 	}
 
 	/** @inheritDoc */
-	public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
+	public static function fromBlob(string $blob, SettingsContainerInterface $options = null):static{
 		$im = new Imagick;
 		$im->readImageBlob($blob);
 

+ 2 - 3
src/Common/LuminanceSourceAbstract.php

@@ -25,8 +25,7 @@ use function array_slice, array_splice, file_exists, is_file, is_readable, realp
  */
 abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
 
-	/** @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface */
-	protected SettingsContainerInterface $options;
+	protected SettingsContainerInterface|QROptions $options;
 	protected array $luminances;
 	protected int   $width;
 	protected int   $height;
@@ -34,7 +33,7 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
 	/**
 	 *
 	 */
-	public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
+	public function __construct(int $width, int $height, SettingsContainerInterface|QROptions $options = null){
 		$this->width   = $width;
 		$this->height  = $height;
 		$this->options = ($options ?? new QROptions);

+ 2 - 2
src/Common/LuminanceSourceInterface.php

@@ -51,11 +51,11 @@ interface LuminanceSourceInterface{
 	/**
 	 * Creates a LuminanceSource instance from the given file
 	 */
-	public static function fromFile(string $path):self;
+	public static function fromFile(string $path):static;
 
 	/**
 	 * Creates a LuminanceSource instance from the given data blob
 	 */
-	public static function fromBlob(string $blob):self;
+	public static function fromBlob(string $blob):static;
 
 }

+ 1 - 1
src/Data/AlphaNum.php

@@ -69,7 +69,7 @@ final class AlphaNum extends QRDataModeAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 
 		$bitBuffer

+ 1 - 1
src/Data/Byte.php

@@ -44,7 +44,7 @@ final class Byte extends QRDataModeAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 
 		$bitBuffer

+ 1 - 1
src/Data/ECI.php

@@ -67,7 +67,7 @@ final class ECI extends QRDataModeAbstract{
 	 * @inheritDoc
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$bitBuffer->put(self::DATAMODE, 4);
 
 		if($this->encoding < 128){

+ 1 - 1
src/Data/Hanzi.php

@@ -130,7 +130,7 @@ final class Hanzi extends QRDataModeAbstract{
 	 *
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 
 		$bitBuffer
 			->put(self::DATAMODE, 4)

+ 1 - 1
src/Data/Kanji.php

@@ -123,7 +123,7 @@ final class Kanji extends QRDataModeAbstract{
 	 *
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 
 		$bitBuffer
 			->put(self::DATAMODE, 4)

+ 1 - 1
src/Data/Number.php

@@ -62,7 +62,7 @@ final class Number extends QRDataModeAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static{
 		$len = $this->getCharCount();
 
 		$bitBuffer

+ 3 - 4
src/Data/QRData.php

@@ -11,6 +11,7 @@
 namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, EccLevel, Mode, Version};
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 
 use function count;
@@ -23,10 +24,8 @@ final class QRData{
 
 	/**
 	 * the options instance
-	 *
-	 * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
 	 */
-	private SettingsContainerInterface $options;
+	private SettingsContainerInterface|QROptions $options;
 
 	/**
 	 * a BitBuffer instance
@@ -58,7 +57,7 @@ final class QRData{
 	/**
 	 * QRData constructor.
 	 */
-	public function __construct(SettingsContainerInterface $options, array $dataSegments = []){
+	public function __construct(SettingsContainerInterface|QROptions $options, array $dataSegments = []){
 		$this->options       = $options;
 		$this->bitBuffer     = new BitBuffer;
 		$this->eccLevel      = new EccLevel($this->options->eccLevel);

+ 1 - 1
src/Data/QRDataModeInterface.php

@@ -53,7 +53,7 @@ interface QRDataModeInterface{
 	 *
 	 * @see \chillerlan\QRCode\Data\QRData::writeBitBuffer()
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface;
+	public function write(BitBuffer $bitBuffer, int $versionNumber):static;
 
 	/**
 	 * reads a segment from the BitBuffer and decodes in the current data mode

+ 18 - 63
src/Data/QRMatrix.php

@@ -158,7 +158,7 @@ class QRMatrix{
 	/**
 	 * shortcut to initialize the functional patterns
 	 */
-	public function initFunctionalPatterns():self{
+	public function initFunctionalPatterns():static{
 		return $this
 			->setFinderPattern()
 			->setSeparators()
@@ -184,21 +184,12 @@ class QRMatrix{
 		$matrix = $this->matrix;
 
 		foreach($matrix as &$row){
-			$row = array_map([$this, 'isDark'], $row);
+			$row = array_map($this->isDark(...), $row);
 		}
 
 		return $matrix;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRMatrix::getMatrix() instead
-	 * @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
-	 * @codeCoverageIgnore
-	 */
-	public function matrix(bool $boolean = null):array{
-		return $this->getMatrix($boolean);
-	}
-
 	/**
 	 * Returns the current version number
 	 */
@@ -206,15 +197,6 @@ class QRMatrix{
 		return $this->version;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRMatrix::getVersion() instead
-	 * @see \chillerlan\QRCode\Data\QRMatrix::getVersion()
-	 * @codeCoverageIgnore
-	 */
-	public function version():?Version{
-		return $this->getVersion();
-	}
-
 	/**
 	 * Returns the current ECC level
 	 */
@@ -222,15 +204,6 @@ class QRMatrix{
 		return $this->eccLevel;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRMatrix::getEccLevel() instead
-	 * @see \chillerlan\QRCode\Data\QRMatrix::getEccLevel()
-	 * @codeCoverageIgnore
-	 */
-	public function eccLevel():?EccLevel{
-		return $this->getEccLevel();
-	}
-
 	/**
 	 * Returns the current mask pattern
 	 */
@@ -238,15 +211,6 @@ class QRMatrix{
 		return $this->maskPattern;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRMatrix::getMaskPattern() instead
-	 * @see \chillerlan\QRCode\Data\QRMatrix::getMaskPattern()
-	 * @codeCoverageIgnore
-	 */
-	public function maskPattern():?MaskPattern{
-		return $this->getMaskPattern();
-	}
-
 	/**
 	 * Returns the absoulute size of the matrix, including quiet zone (after setting it).
 	 *
@@ -256,15 +220,6 @@ class QRMatrix{
 		return $this->moduleCount;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRMatrix::getSize() instead
-	 * @see \chillerlan\QRCode\Data\QRMatrix::getSize()
-	 * @codeCoverageIgnore
-	 */
-	public function size():int{
-		return $this->getSize();
-	}
-
 	/**
 	 * Returns the value of the module at position [$x, $y] or -1 if the coordinate is outside the matrix
 	 */
@@ -283,7 +238,7 @@ class QRMatrix{
 	 *   true  => $M_TYPE | 0x800
 	 *   false => $M_TYPE
 	 */
-	public function set(int $x, int $y, bool $value, int $M_TYPE):self{
+	public function set(int $x, int $y, bool $value, int $M_TYPE):static{
 
 		if(isset($this->matrix[$y][$x])){
 			$this->matrix[$y][$x] = (($M_TYPE & ~$this::IS_DARK) | (($value) ? $this::IS_DARK : 0));
@@ -295,7 +250,7 @@ class QRMatrix{
 	/**
 	 * Fills an area of $width * $height, from the given starting point [$startX, $startY] (top left) with $value for $M_TYPE.
 	 */
-	public function setArea(int $startX, int $startY, int $width, int $height, bool $value, int $M_TYPE):self{
+	public function setArea(int $startX, int $startY, int $width, int $height, bool $value, int $M_TYPE):static{
 
 		for($y = $startY; $y < ($startY + $height); $y++){
 			for($x = $startX; $x < ($startX + $width); $x++){
@@ -309,7 +264,7 @@ class QRMatrix{
 	/**
 	 * Flips the value of the module at ($x, $y)
 	 */
-	public function flip(int $x, int $y):self{
+	public function flip(int $x, int $y):static{
 
 		if(isset($this->matrix[$y][$x])){
 			$this->matrix[$y][$x] ^= $this::IS_DARK;
@@ -402,7 +357,7 @@ class QRMatrix{
 	 *
 	 * 4 * version + 9 or moduleCount - 8
 	 */
-	public function setDarkModule():self{
+	public function setDarkModule():static{
 		$this->set(8, ($this->moduleCount - 8), true, $this::M_DARKMODULE);
 
 		return $this;
@@ -413,7 +368,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 7.3.2
 	 */
-	public function setFinderPattern():self{
+	public function setFinderPattern():static{
 
 		$pos = [
 			[0, 0], // top left
@@ -437,7 +392,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 7.3.3
 	 */
-	public function setSeparators():self{
+	public function setSeparators():static{
 
 		$h = [
 			[7, 0],
@@ -467,7 +422,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 7.3.5
 	 */
-	public function setAlignmentPattern():self{
+	public function setAlignmentPattern():static{
 		$alignmentPattern = $this->version->getAlignmentPattern();
 
 		foreach($alignmentPattern as $y){
@@ -496,7 +451,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 7.3.4
 	 */
-	public function setTimingPattern():self{
+	public function setTimingPattern():static{
 
 		for($i = 8; $i < ($this->moduleCount - 8); $i++){
 
@@ -518,7 +473,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 8.10
 	 */
-	public function setVersionNumber():self{
+	public function setVersionNumber():static{
 		$bits = $this->version->getVersionPattern();
 
 		if($bits !== null){
@@ -542,7 +497,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 8.9
 	 */
-	public function setFormatInfo(MaskPattern $maskPattern = null):self{
+	public function setFormatInfo(MaskPattern $maskPattern = null):static{
 		$this->maskPattern = $maskPattern;
 		$bits              = 0; // sets all format fields to false (test mode)
 
@@ -585,7 +540,7 @@ class QRMatrix{
 	 *
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
-	public function setQuietZone(int $quietZoneSize):self{
+	public function setQuietZone(int $quietZoneSize):static{
 
 		// early exit if there's nothing to add
 		if($quietZoneSize < 1){
@@ -617,7 +572,7 @@ class QRMatrix{
 	/**
 	 * Rotates the matrix by 90 degrees clock wise
 	 */
-	public function rotate90():self{
+	public function rotate90():static{
 		/** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
 		$this->matrix = array_map((fn(int ...$a):array => array_reverse($a)), ...$this->matrix);
 
@@ -629,7 +584,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2015 Section 6.2 - Reflectance reversal
 	 */
-	public function invert():self{
+	public function invert():static{
 
 		foreach($this->matrix as $y => $row){
 			foreach($row as $x => $val){
@@ -668,7 +623,7 @@ class QRMatrix{
 	 *
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
-	public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
+	public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):static{
 		$height ??= $width;
 
 		// if width and height happen to be negative or 0 (default value), just return - nothing to do
@@ -730,7 +685,7 @@ class QRMatrix{
 	/**
 	 * Maps the interleaved binary $data on the matrix
 	 */
-	public function writeCodewords(BitBuffer $bitBuffer):self{
+	public function writeCodewords(BitBuffer $bitBuffer):static{
 		$data      = (new ReedSolomonEncoder($this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
 		$byteCount = count($data);
 		$iByte     = 0;
@@ -785,7 +740,7 @@ class QRMatrix{
 	 *
 	 * ISO/IEC 18004:2000 Section 8.8.1
 	 */
-	public function mask(MaskPattern $maskPattern):self{
+	public function mask(MaskPattern $maskPattern):static{
 		$this->maskPattern = $maskPattern;
 		$mask              = $this->maskPattern->getMask();
 

+ 6 - 6
src/Decoder/BitMatrix.php

@@ -80,7 +80,7 @@ final class BitMatrix extends QRMatrix{
 	/**
 	 * Resets the current version info in order to attempt another reading
 	 */
-	public function resetVersionInfo():self{
+	public function resetVersionInfo():static{
 		$this->version     = null;
 		$this->eccLevel    = null;
 		$this->maskPattern = null;
@@ -91,7 +91,7 @@ final class BitMatrix extends QRMatrix{
 	/**
 	 * Mirror the bit matrix diagonally in order to attempt a second reading.
 	 */
-	public function mirrorDiagonal():self{
+	public function mirrorDiagonal():static{
 		$this->mirror = !$this->mirror;
 
 		// mirror vertically
@@ -179,7 +179,7 @@ final class BitMatrix extends QRMatrix{
 	 *
 	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
 	 */
-	private function readFormatInformation():self{
+	private function readFormatInformation():static{
 
 		if($this->eccLevel !== null && $this->maskPattern !== null){
 			return $this;
@@ -296,7 +296,7 @@ final class BitMatrix extends QRMatrix{
 	 * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
 	 * @noinspection DuplicatedCode
 	 */
-	private function readVersion():self{
+	private function readVersion():static{
 
 		if($this->version !== null){
 			return $this;
@@ -415,7 +415,7 @@ final class BitMatrix extends QRMatrix{
 	 * @codeCoverageIgnore
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
-	public function setQuietZone(int $quietZoneSize = null):self{
+	public function setQuietZone(int $quietZoneSize = null):static{
 		throw new QRCodeDataException('not supported');
 	}
 
@@ -423,7 +423,7 @@ final class BitMatrix extends QRMatrix{
 	 * @codeCoverageIgnore
 	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
-	public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
+	public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):static{
 		throw new QRCodeDataException('not supported');
 	}
 

+ 1 - 1
src/Detector/AlignmentPattern.php

@@ -23,7 +23,7 @@ final class AlignmentPattern extends ResultPoint{
 	 * Combines this object's current estimate of a finder pattern position and module size
 	 * with a new estimate. It returns a new FinderPattern containing an average of the two.
 	 */
-	public function combineEstimate(float $i, float $j, float $newModuleSize):self{
+	public function combineEstimate(float $i, float $j, float $newModuleSize):static{
 		return new self(
 			(($this->x + $j) / 2.0),
 			(($this->y + $i) / 2.0),

+ 1 - 1
src/Detector/FinderPattern.php

@@ -61,7 +61,7 @@ final class FinderPattern extends ResultPoint{
 	 * with a new estimate. It returns a new FinderPattern containing a weighted average
 	 * based on count.
 	 */
-	public function combineEstimate(float $i, float $j, float $newModuleSize):self{
+	public function combineEstimate(float $i, float $j, float $newModuleSize):static{
 		$combinedCount = ($this->count + 1);
 
 		return new self(

+ 4 - 6
src/Output/QREps.php

@@ -22,12 +22,12 @@ use function array_values, count, date, implode, is_array, is_numeric, max, min,
  */
 class QREps extends QROutputAbstract{
 
-	public const MIME_TYPE = 'application/postscript';
+	final public const MIME_TYPE = 'application/postscript';
 
 	/**
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_array($value) || count($value) < 3){
 			return false;
@@ -50,11 +50,9 @@ class QREps extends QROutputAbstract{
 	}
 
 	/**
-	 * @param array $value
-	 *
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value):string{
+	protected function prepareModuleValue(mixed $value):string{
 		$values = [];
 
 		foreach(array_values($value) as $i => $val){
@@ -130,7 +128,7 @@ class QREps extends QROutputAbstract{
 		}
 
 		// create the path elements
-		$paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
+		$paths = $this->collectModules($this->module(...));
 
 		foreach($paths as $M_TYPE => $path){
 

+ 6 - 9
src/Output/QRFpdf.php

@@ -12,6 +12,7 @@
 namespace chillerlan\QRCode\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 use FPDF;
 
@@ -25,7 +26,7 @@ use function array_values, class_exists, count, intval, is_array, is_numeric, ma
  */
 class QRFpdf extends QROutputAbstract{
 
-	public const MIME_TYPE = 'application/pdf';
+	final public const MIME_TYPE = 'application/pdf';
 
 	protected FPDF   $fpdf;
 	protected ?array $prevColor = null;
@@ -35,7 +36,7 @@ class QRFpdf extends QROutputAbstract{
 	 *
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
-	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 
 		if(!class_exists(FPDF::class)){
 			// @codeCoverageIgnoreStart
@@ -52,7 +53,7 @@ class QRFpdf extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_array($value) || count($value) < 3){
 			return false;
@@ -75,12 +76,10 @@ class QRFpdf extends QROutputAbstract{
 	}
 
 	/**
-	 * @param array $value
-	 *
 	 * @inheritDoc
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
-	protected function prepareModuleValue($value):array{
+	protected function prepareModuleValue(mixed $value):array{
 		$values = [];
 
 		foreach(array_values($value) as $i => $val){
@@ -115,10 +114,8 @@ class QRFpdf extends QROutputAbstract{
 
 	/**
 	 * @inheritDoc
-	 *
-	 * @return string|\FPDF
 	 */
-	public function dump(string $file = null){
+	public function dump(string $file = null):string|FPDF{
 		$this->fpdf = $this->initFPDF();
 		$this->fpdf->AddPage();
 

+ 22 - 65
src/Output/QRGdImage.php

@@ -13,34 +13,28 @@
 namespace chillerlan\QRCode\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
-use ErrorException;
-use Throwable;
-use function array_values, count, extension_loaded, gd_info, imagebmp, imagecolorallocate, imagecolortransparent,
-	imagecreatetruecolor, imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng,
-	imagescale, imagewebp, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start,
+use ErrorException, GdImage, Throwable;
+use function array_values, count, extension_loaded, gd_info, imagecolorallocate, imagecolortransparent,
+	imagecreatetruecolor, imagedestroy, imagefilledellipse, imagefilledrectangle,
+	imagescale, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start,
 	restore_error_handler, set_error_handler, sprintf;
 
 /**
  * Converts the matrix into GD images, raw or base64 output (requires ext-gd)
  *
  * @see https://php.net/manual/book.image.php
- *
- * @deprecated 5.0.0 this class will be made abstract in future versions,
- *                   calling it directly is deprecated - use one of the child classes instead
  * @see https://github.com/chillerlan/php-qrcode/issues/223
  */
-class QRGdImage extends QROutputAbstract{
+abstract class QRGdImage extends QROutputAbstract{
 
 	/**
 	 * The GD image resource
 	 *
 	 * @see imagecreatetruecolor()
-	 * @var resource|\GdImage
-	 *
-	 * @todo: add \GdImage type in v6
 	 */
-	protected $image;
+	protected GdImage $image;
 
 	/**
 	 * The allocated background color
@@ -62,7 +56,7 @@ class QRGdImage extends QROutputAbstract{
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 * @noinspection PhpMissingParentConstructorInspection
 	 */
-	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 		$this->options = $options;
 		$this->matrix  = $matrix;
 
@@ -79,7 +73,6 @@ class QRGdImage extends QROutputAbstract{
 	/**
 	 * Checks whether GD is installed and if the given mode is supported
 	 *
-	 * @return void
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 * @codeCoverageIgnore
 	 */
@@ -90,23 +83,23 @@ class QRGdImage extends QROutputAbstract{
 		}
 
 		$modes = [
-			self::GDIMAGE_BMP  => 'BMP Support',
-			self::GDIMAGE_GIF  => 'GIF Create Support',
-			self::GDIMAGE_JPG  => 'JPEG Support',
-			self::GDIMAGE_PNG  => 'PNG Support',
-			self::GDIMAGE_WEBP => 'WebP Support',
+			QRGdImageBMP::class  => 'BMP Support',
+			QRGdImageGIF::class  => 'GIF Create Support',
+			QRGdImageJPEG::class => 'JPEG Support',
+			QRGdImagePNG::class  => 'PNG Support',
+			QRGdImageWEBP::class => 'WebP Support',
 		];
 
 		// likely using default or custom output
-		if(!isset($modes[$this->options->outputType])){
+		if(!isset($modes[$this->options->outputInterface])){
 			return;
 		}
 
 		$info = gd_info();
-		$mode = $modes[$this->options->outputType];
+		$mode = $modes[$this->options->outputInterface];
 
 		if(!isset($info[$mode]) || $info[$mode] !== true){
-			throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputType));
+			throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputInterface));
 		}
 
 	}
@@ -114,7 +107,7 @@ class QRGdImage extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_array($value) || count($value) < 3){
 			return false;
@@ -137,12 +130,10 @@ class QRGdImage extends QROutputAbstract{
 	}
 
 	/**
-	 * @param array $value
-	 *
 	 * @inheritDoc
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
-	protected function prepareModuleValue($value):int{
+	protected function prepareModuleValue(mixed $value):int{
 		$values = [];
 
 		foreach(array_values($value) as $i => $val){
@@ -174,12 +165,9 @@ class QRGdImage extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 *
-	 * @return string|resource|\GdImage
-	 *
-	 * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
 	 * @throws \ErrorException
 	 */
-	public function dump(string $file = null){
+	public function dump(string $file = null):string|GdImage{
 
 		set_error_handler(function(int $errno, string $errstr):bool{
 			throw new ErrorException($errstr, $errno);
@@ -215,8 +203,7 @@ class QRGdImage extends QROutputAbstract{
 		$this->saveToFile($imageData, $file);
 
 		if($this->options->outputBase64){
-			// @todo: remove mime parameter in v6
-			$imageData = $this->toBase64DataURI($imageData, 'image/'.$this->options->outputType);
+			$imageData = $this->toBase64DataURI($imageData);
 		}
 
 		restore_error_handler();
@@ -268,8 +255,7 @@ class QRGdImage extends QROutputAbstract{
 	 */
 	protected function setTransparencyColor():void{
 
-		// @todo: the jpg skip can be removed in v6
-		if($this->options->outputType === QROutputInterface::GDIMAGE_JPG || !$this->options->imageTransparent){
+		if(!$this->options->imageTransparent){
 			return;
 		}
 
@@ -330,38 +316,9 @@ class QRGdImage extends QROutputAbstract{
 	/**
 	 * Renders the image with the gdimage function for the desired output
 	 *
-	 * @see \imagebmp()
-	 * @see \imagegif()
-	 * @see \imagejpeg()
-	 * @see \imagepng()
-	 * @see \imagewebp()
-	 *
-	 * @todo: v6.0: make abstract and call from child classes
 	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 * @codeCoverageIgnore
 	 */
-	protected function renderImage():void{
-
-		switch($this->options->outputType){
-			case QROutputInterface::GDIMAGE_BMP:
-				imagebmp($this->image, null, ($this->options->quality > 0));
-				break;
-			case QROutputInterface::GDIMAGE_GIF:
-				imagegif($this->image);
-				break;
-			case QROutputInterface::GDIMAGE_JPG:
-				imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
-				break;
-			case QROutputInterface::GDIMAGE_WEBP:
-				imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
-				break;
-			// silently default to png output
-			case QROutputInterface::GDIMAGE_PNG:
-			default:
-				imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
-		}
-
-	}
+	abstract protected function renderImage():void;
 
 	/**
 	 * Creates the final image by calling the desired GD output function

+ 1 - 1
src/Output/QRGdImageBMP.php

@@ -21,7 +21,7 @@ use function imagebmp;
  */
 class QRGdImageBMP extends QRGdImage{
 
-	public const MIME_TYPE = 'image/bmp';
+	final public const MIME_TYPE = 'image/bmp';
 
 	/**
 	 * @inheritDoc

+ 1 - 1
src/Output/QRGdImageGIF.php

@@ -21,7 +21,7 @@ use function imagegif;
  */
 class QRGdImageGIF extends QRGdImage{
 
-	public const MIME_TYPE = 'image/gif';
+	final public const MIME_TYPE = 'image/gif';
 
 	/**
 	 * @inheritDoc

+ 1 - 1
src/Output/QRGdImageJPEG.php

@@ -23,7 +23,7 @@ use function min;
  */
 class QRGdImageJPEG extends QRGdImage{
 
-	public const MIME_TYPE = 'image/jpg';
+	final public const MIME_TYPE = 'image/jpg';
 
 	/**
 	 * @inheritDoc

+ 1 - 1
src/Output/QRGdImagePNG.php

@@ -23,7 +23,7 @@ use function min;
  */
 class QRGdImagePNG extends QRGdImage{
 
-	public const MIME_TYPE = 'image/png';
+	final public const MIME_TYPE = 'image/png';
 
 	/**
 	 * @inheritDoc

+ 1 - 1
src/Output/QRGdImageWEBP.php

@@ -23,7 +23,7 @@ use function min;
  */
 class QRGdImageWEBP extends QRGdImage{
 
-	public const MIME_TYPE = 'image/webp';
+	final public const MIME_TYPE = 'image/webp';
 
 	/**
 	 * @inheritDoc

+ 0 - 19
src/Output/QRImage.php

@@ -1,19 +0,0 @@
-<?php
-/**
- * Class QRImage
- *
- * @created      14.12.2021
- * @author       smiley <smiley@chillerlan.net>
- * @copyright    2021 smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCode\Output;
-
-/**
- * @deprecated 5.0.0 backward compatibility, use QRGdImage instead
- * @see \chillerlan\QRCode\Output\QRGdImage
- */
-class QRImage extends QRGdImage{
-
-}

+ 5 - 6
src/Output/QRImagick.php

@@ -13,6 +13,7 @@
 namespace chillerlan\QRCode\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 use finfo, Imagick, ImagickDraw, ImagickPixel;
 use function extension_loaded, in_array, is_string, max, min, preg_match, strlen;
@@ -46,7 +47,7 @@ class QRImagick extends QROutputAbstract{
 	 *
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
-	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 
 		if(!extension_loaded('imagick')){
 			throw new QRCodeOutputException('ext-imagick not loaded'); // @codeCoverageIgnore
@@ -65,7 +66,7 @@ class QRImagick extends QROutputAbstract{
 	 * @see https://www.php.net/manual/imagickpixel.construct.php
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_string($value)){
 			return false;
@@ -98,7 +99,7 @@ class QRImagick extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value):ImagickPixel{
+	protected function prepareModuleValue(mixed $value):ImagickPixel{
 		return new ImagickPixel($value);
 	}
 
@@ -111,10 +112,8 @@ class QRImagick extends QROutputAbstract{
 
 	/**
 	 * @inheritDoc
-	 *
-	 * @return string|\Imagick
 	 */
-	public function dump(string $file = null){
+	public function dump(string $file = null):string|Imagick{
 		$this->setBgColor();
 
 		$this->imagick = $this->createImage();

+ 2 - 2
src/Output/QRMarkup.php

@@ -26,7 +26,7 @@ abstract class QRMarkup extends QROutputAbstract{
 	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_string($value)){
 			return false;
@@ -57,7 +57,7 @@ abstract class QRMarkup extends QROutputAbstract{
 	/**
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value):string{
+	protected function prepareModuleValue(mixed $value):string{
 		return trim(strip_tags($value), " '\"\r\n\t");
 	}
 

+ 1 - 1
src/Output/QRMarkupHTML.php

@@ -17,7 +17,7 @@ use function implode, sprintf;
  */
 class QRMarkupHTML extends QRMarkup{
 
-	public const MIME_TYPE = 'text/html';
+	final public const MIME_TYPE = 'text/html';
 
 	/**
 	 * @inheritDoc

+ 3 - 3
src/Output/QRMarkupSVG.php

@@ -22,7 +22,7 @@ use function array_chunk, implode, is_string, preg_match, sprintf, trim;
  */
 class QRMarkupSVG extends QRMarkup{
 
-	public const MIME_TYPE = 'image/svg+xml';
+	final public const MIME_TYPE = 'image/svg+xml';
 
 	/**
 	 * @todo: XSS proof
@@ -30,7 +30,7 @@ class QRMarkupSVG extends QRMarkup{
 	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 
 		if(!is_string($value)){
 			return false;
@@ -115,7 +115,7 @@ class QRMarkupSVG extends QRMarkup{
 	 * returns one or more SVG <path> elements
 	 */
 	protected function paths():string{
-		$paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
+		$paths = $this->collectModules($this->module(...));
 		$svg   = [];
 
 		// create the path elements

+ 11 - 19
src/Output/QROutputAbstract.php

@@ -11,6 +11,7 @@
 namespace chillerlan\QRCode\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\QROptions;
 use chillerlan\Settings\SettingsContainerInterface;
 use Closure;
 use function base64_encode, dirname, file_put_contents, is_writable, ksort, sprintf;
@@ -43,9 +44,9 @@ abstract class QROutputAbstract implements QROutputInterface{
 	protected QRMatrix $matrix;
 
 	/**
-	 * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
+	 * the options instance
 	 */
-	protected SettingsContainerInterface $options;
+	protected SettingsContainerInterface|QROptions $options;
 
 	/** @see \chillerlan\QRCode\QROptions::$scale */
 	protected int $scale;
@@ -68,7 +69,7 @@ abstract class QROutputAbstract implements QROutputInterface{
 	/**
 	 * QROutputAbstract constructor.
 	 */
-	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+	public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
 		$this->options = $options;
 		$this->matrix  = $matrix;
 
@@ -148,10 +149,9 @@ abstract class QROutputAbstract implements QROutputInterface{
 	/**
 	 * Returns the prepared value for the given $M_TYPE
 	 *
-	 * @return mixed return value depends on the output class
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException if $moduleValues[$M_TYPE] doesn't exist
 	 */
-	protected function getModuleValue(int $M_TYPE){
+	protected function getModuleValue(int $M_TYPE):mixed{
 
 		if(!isset($this->moduleValues[$M_TYPE])){
 			throw new QRCodeOutputException(sprintf('$M_TYPE %012b not found in module values map', $M_TYPE));
@@ -162,34 +162,26 @@ abstract class QROutputAbstract implements QROutputInterface{
 
 	/**
 	 * Returns the prepared module value at the given coordinate [$x, $y] (convenience)
-	 *
-	 * @return mixed|null
 	 */
-	protected function getModuleValueAt(int $x, int $y){
+	protected function getModuleValueAt(int $x, int $y):mixed{
 		return $this->getModuleValue($this->matrix->get($x, $y));
 	}
 
 	/**
-	 * Prepares the value for the given input ()
-	 *
-	 * @param mixed $value
-	 *
-	 * @return mixed|null return value depends on the output class
+	 * Prepares the value for the given input (return value depends on the output class)
 	 */
-	abstract protected function prepareModuleValue($value);
+	abstract protected function prepareModuleValue(mixed $value):mixed;
 
 	/**
-	 * Returns a default value for either dark or light modules
-	 *
-	 * @return mixed|null return value depends on the output class
+	 * Returns a default value for either dark or light modules (return value depends on the output class)
 	 */
-	abstract protected function getDefaultModuleValue(bool $isDark);
+	abstract protected function getDefaultModuleValue(bool $isDark):mixed;
 
 	/**
 	 * Returns a base64 data URI for the given string and mime type
 	 */
 	protected function toBase64DataURI(string $data, string $mime = null):string{
-		return sprintf('data:%s;base64,%s', ($mime ?? $this::MIME_TYPE), base64_encode($data));
+		return sprintf('data:%s;base64,%s', ($mime ?? static::MIME_TYPE), base64_encode($data));
 	}
 
 	/**

+ 16 - 112
src/Output/QROutputInterface.php

@@ -18,116 +18,24 @@ use chillerlan\QRCode\Data\QRMatrix;
 interface QROutputInterface{
 
 	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const MARKUP_HTML  = 'html';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const MARKUP_SVG   = 'svg';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const GDIMAGE_BMP  = 'bmp';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const GDIMAGE_GIF  = 'gif';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const GDIMAGE_JPG  = 'jpg';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const GDIMAGE_PNG  = 'png';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const GDIMAGE_WEBP = 'webp';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const STRING_JSON  = 'json';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const STRING_TEXT  = 'text';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const IMAGICK      = 'imagick';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const FPDF         = 'fpdf';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const EPS          = 'eps';
-
-	/**
-	 * @var string
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 */
-	public const CUSTOM       = 'custom';
-
-	/**
-	 * Map of built-in output modes => class FQN
+	 * Map of built-in output class FQN
 	 *
 	 * @var string[]
-	 * @deprecated 5.0.0 <no replacement>
-	 * @see        https://github.com/chillerlan/php-qrcode/issues/223
+	 * @see https://github.com/chillerlan/php-qrcode/issues/223
 	 */
 	public const MODES = [
-		self::MARKUP_SVG   => QRMarkupSVG::class,
-		self::MARKUP_HTML  => QRMarkupHTML::class,
-		self::GDIMAGE_BMP  => QRGdImageBMP::class,
-		self::GDIMAGE_GIF  => QRGdImageGIF::class,
-		self::GDIMAGE_JPG  => QRGdImageJPEG::class,
-		self::GDIMAGE_PNG  => QRGdImagePNG::class,
-		self::GDIMAGE_WEBP => QRGdImageWEBP::class,
-		self::STRING_JSON  => QRStringJSON::class,
-		self::STRING_TEXT  => QRStringText::class,
-		self::IMAGICK      => QRImagick::class,
-		self::FPDF         => QRFpdf::class,
-		self::EPS          => QREps::class,
+		QRMarkupSVG::class,
+		QRMarkupHTML::class,
+		QRGdImageBMP::class,
+		QRGdImageGIF::class,
+		QRGdImageJPEG::class,
+		QRGdImagePNG::class,
+		QRGdImageWEBP::class,
+		QRStringJSON::class,
+		QRStringText::class,
+		QRImagick::class,
+		QRFpdf::class,
+		QREps::class,
 	];
 
 	/**
@@ -205,10 +113,8 @@ interface QROutputInterface{
 
 	/**
 	 * Determines whether the given value is valid
-	 *
-	 * @param mixed $value
 	 */
-	public static function moduleValueIsValid($value):bool;
+	public static function moduleValueIsValid(mixed $value):bool;
 
 	/**
 	 * Generates the output, optionally dumps it to a file, and returns it
@@ -218,9 +124,7 @@ interface QROutputInterface{
 	 * you need to supply the $file parameter here in that case (or handle the option value in your custom output module).
 	 *
 	 * @see \chillerlan\QRCode\QRCode::renderMatrix()
-	 *
-	 * @return mixed
 	 */
-	public function dump(string $file = null);
+	public function dump(string $file = null):mixed;
 
 }

+ 0 - 111
src/Output/QRString.php

@@ -1,111 +0,0 @@
-<?php
-/**
- * Class QRString
- *
- * @created      05.12.2015
- * @author       Smiley <smiley@chillerlan.net>
- * @copyright    2015 Smiley
- * @license      MIT
- *
- * @noinspection PhpComposerExtensionStubsInspection
- */
-
-namespace chillerlan\QRCode\Output;
-
-use function implode, is_string, json_encode, max, min, sprintf;
-use const JSON_THROW_ON_ERROR;
-
-/**
- * Converts the matrix data into string types
- *
- * @deprecated 5.0.0 this class will be removed in future versions, use one of QRStringText or QRStringJSON instead
- */
-class QRString extends QROutputAbstract{
-
-	/**
-	 * @inheritDoc
-	 */
-	public static function moduleValueIsValid($value):bool{
-		return is_string($value);
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	protected function prepareModuleValue($value):string{
-		return $value;
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	protected function getDefaultModuleValue(bool $isDark):string{
-		return ($isDark) ? '██' : '░░';
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	public function dump(string $file = null):string{
-
-		switch($this->options->outputType){
-			case QROutputInterface::STRING_TEXT:
-				$data = $this->text();
-				break;
-			case QROutputInterface::STRING_JSON:
-			default:
-				$data = $this->json();
-		}
-
-		$this->saveToFile($data, $file);
-
-		return $data;
-	}
-
-	/**
-	 * string output
-	 */
-	protected function text():string{
-		$lines     = [];
-		$linestart = $this->options->textLineStart;
-
-		for($y = 0; $y < $this->moduleCount; $y++){
-			$r = [];
-
-			for($x = 0; $x < $this->moduleCount; $x++){
-				$r[] = $this->getModuleValueAt($x, $y);
-			}
-
-			$lines[] = $linestart.implode('', $r);
-		}
-
-		return implode($this->eol, $lines);
-	}
-
-	/**
-	 * JSON output
-	 *
-	 * @throws \JsonException
-	 */
-	protected function json():string{
-		return json_encode($this->matrix->getMatrix($this->options->jsonAsBooleans), JSON_THROW_ON_ERROR);
-	}
-
-	//
-
-	/**
-	 * a little helper to create a proper ANSI 8-bit color escape sequence
-	 *
-	 * @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
-	 * @see https://en.wikipedia.org/wiki/Block_Elements
-	 *
-	 * @codeCoverageIgnore
-	 */
-	public static function ansi8(string $str, int $color, bool $background = null):string{
-		$color      = max(0, min($color, 255));
-		$background = ($background === true) ? 48 : 38;
-
-		return sprintf("\x1b[%s;5;%sm%s\x1b[0m", $background, $color, $str);
-	}
-
-}

+ 3 - 3
src/Output/QRStringJSON.php

@@ -19,7 +19,7 @@ use function json_encode;
  */
 class QRStringJSON extends QROutputAbstract{
 
-	public const MIME_TYPE = 'application/json';
+	final public const MIME_TYPE = 'application/json';
 
 	/**
 	 * @inheritDoc
@@ -39,7 +39,7 @@ class QRStringJSON extends QROutputAbstract{
 	 *
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value):string{
+	protected function prepareModuleValue(mixed $value):string{
 		return '';
 	}
 
@@ -57,7 +57,7 @@ class QRStringJSON extends QROutputAbstract{
 	 *
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 		return true;
 	}
 

+ 4 - 4
src/Output/QRStringText.php

@@ -22,19 +22,19 @@ use function sprintf;
  */
 class QRStringText extends QROutputAbstract{
 
-	public const MIME_TYPE = 'text/plain';
+	final public const MIME_TYPE = 'text/plain';
 
 	/**
 	 * @inheritDoc
 	 */
-	public static function moduleValueIsValid($value):bool{
+	public static function moduleValueIsValid(mixed $value):bool{
 		return is_string($value);
 	}
 
 	/**
 	 * @inheritDoc
 	 */
-	protected function prepareModuleValue($value):string{
+	protected function prepareModuleValue(mixed $value):string{
 		return $value;
 	}
 
@@ -53,7 +53,7 @@ class QRStringText extends QROutputAbstract{
 		$linestart = $this->options->textLineStart;
 
 		foreach($this->matrix->getMatrix() as $row){
-			$lines[] = $linestart.implode('', array_map([$this, 'getModuleValue'], $row));
+			$lines[] = $linestart.implode('', array_map($this->getModuleValue(...), $row));
 		}
 
 		$data = implode($this->eol, $lines);

+ 17 - 207
src/QRCode.php

@@ -13,7 +13,7 @@
 namespace chillerlan\QRCode;
 
 use chillerlan\QRCode\Common\{
-	EccLevel, ECICharset, GDLuminanceSource, IMagickLuminanceSource, LuminanceSourceInterface, MaskPattern, Mode, Version
+	ECICharset, GDLuminanceSource, IMagickLuminanceSource, LuminanceSourceInterface, MaskPattern, Mode
 };
 use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Hanzi, Kanji, Number, QRData, QRDataModeInterface, QRMatrix};
 use chillerlan\QRCode\Decoder\{Decoder, DecoderResult};
@@ -32,138 +32,10 @@ use function class_exists, class_implements, in_array, mb_convert_encoding, mb_i
  */
 class QRCode{
 
-	/**
-	 * @deprecated 5.0.0 use Version::AUTO instead
-	 * @see \chillerlan\QRCode\Common\Version::AUTO
-	 * @var int
-	 */
-	public const VERSION_AUTO      = Version::AUTO;
-
-	/**
-	 * @deprecated 5.0.0 use MaskPattern::AUTO instead
-	 * @see \chillerlan\QRCode\Common\MaskPattern::AUTO
-	 * @var int
-	 */
-	public const MASK_PATTERN_AUTO = MaskPattern::AUTO;
-
-	/**
-	 * @deprecated 5.0.0 use EccLevel::L instead
-	 * @see \chillerlan\QRCode\Common\EccLevel::L
-	 * @var int
-	 */
-	public const ECC_L = EccLevel::L;
-
-	/**
-	 * @deprecated 5.0.0 use EccLevel::M instead
-	 * @see \chillerlan\QRCode\Common\EccLevel::M
-	 * @var int
-	 */
-	public const ECC_M = EccLevel::M;
-
-	/**
-	 * @deprecated 5.0.0 use EccLevel::Q instead
-	 * @see \chillerlan\QRCode\Common\EccLevel::Q
-	 * @var int
-	 */
-	public const ECC_Q = EccLevel::Q;
-
-	/**
-	 * @deprecated 5.0.0 use EccLevel::H instead
-	 * @see \chillerlan\QRCode\Common\EccLevel::H
-	 * @var int
-	 */
-	public const ECC_H = EccLevel::H;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::MARKUP_HTML instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::MARKUP_HTML
-	 * @var string
-	 */
-	public const OUTPUT_MARKUP_HTML = QROutputInterface::MARKUP_HTML;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::MARKUP_SVG instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::MARKUP_SVG
-	 * @var string
-	 */
-	public const OUTPUT_MARKUP_SVG  = QROutputInterface::MARKUP_SVG;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_PNG instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_PNG
-	 * @var string
-	 */
-	public const OUTPUT_IMAGE_PNG   = QROutputInterface::GDIMAGE_PNG;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_JPG instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_JPG
-	 * @var string
-	 */
-	public const OUTPUT_IMAGE_JPG   = QROutputInterface::GDIMAGE_JPG;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::GDIMAGE_GIF instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::GDIMAGE_GIF
-	 * @var string
-	 */
-	public const OUTPUT_IMAGE_GIF   = QROutputInterface::GDIMAGE_GIF;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::STRING_JSON instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::STRING_JSON
-	 * @var string
-	 */
-	public const OUTPUT_STRING_JSON = QROutputInterface::STRING_JSON;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::STRING_TEXT instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::STRING_TEXT
-	 * @var string
-	 */
-	public const OUTPUT_STRING_TEXT = QROutputInterface::STRING_TEXT;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::IMAGICK instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::IMAGICK
-	 * @var string
-	 */
-	public const OUTPUT_IMAGICK     = QROutputInterface::IMAGICK;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::FPDF instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::FPDF
-	 * @var string
-	 */
-	public const OUTPUT_FPDF        = QROutputInterface::FPDF;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::EPS instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::EPS
-	 * @var string
-	 */
-	public const OUTPUT_EPS         = QROutputInterface::EPS;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::CUSTOM instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::CUSTOM
-	 * @var string
-	 */
-	public const OUTPUT_CUSTOM      = QROutputInterface::CUSTOM;
-
-	/**
-	 * @deprecated 5.0.0 use QROutputInterface::MODES instead
-	 * @see \chillerlan\QRCode\Output\QROutputInterface::MODES
-	 * @var string[]
-	 */
-	public const OUTPUT_MODES       = QROutputInterface::MODES;
-
 	/**
 	 * The settings container
-	 *
-	 * @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
 	 */
-	protected SettingsContainerInterface $options;
+	protected SettingsContainerInterface|QROptions $options;
 
 	/**
 	 * A collection of one or more data segments of QRDataModeInterface instances to write
@@ -182,14 +54,14 @@ class QRCode{
 	 *
 	 * PHP8: accept iterable
 	 */
-	public function __construct(SettingsContainerInterface $options = null){
+	public function __construct(SettingsContainerInterface|QROptions $options = null){
 		$this->setOptions(($options ?? new QROptions));
 	}
 
 	/**
 	 * Sets an options instance
 	 */
-	public function setOptions(SettingsContainerInterface $options):self{
+	public function setOptions(SettingsContainerInterface|QROptions $options):static{
 		$this->options = $options;
 
 		if($this->options->readerUseImagickIfAvailable){
@@ -201,10 +73,8 @@ class QRCode{
 
 	/**
 	 * Renders a QR Code for the given $data and QROptions, saves $file optionally
-	 *
-	 * @return mixed
 	 */
-	public function render(string $data = null, string $file = null){
+	public function render(string $data = null, string $file = null):mixed{
 
 		if($data !== null){
 			/** @var \chillerlan\QRCode\Data\QRDataModeInterface $dataInterface */
@@ -223,10 +93,8 @@ class QRCode{
 
 	/**
 	 * Renders a QR Code for the given QRMatrix and QROptions, saves $file optionally
-	 *
-	 * @return mixed
 	 */
-	public function renderMatrix(QRMatrix $matrix, string $file = null){
+	public function renderMatrix(QRMatrix $matrix, string $file = null):mixed{
 		return $this->initOutputInterface($matrix)->dump($file ?? $this->options->cachefile);
 	}
 
@@ -277,29 +145,15 @@ class QRCode{
 		return $matrix;
 	}
 
-	/**
-	 * @deprecated 5.0.0 use QRCode::getQRMatrix() instead
-	 * @see \chillerlan\QRCode\QRCode::getQRMatrix()
-	 * @codeCoverageIgnore
-	 */
-	public function getMatrix():QRMatrix{
-		return $this->getQRMatrix();
-	}
-
 	/**
 	 * initializes a fresh built-in or custom QROutputInterface
 	 *
 	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
 	 */
 	protected function initOutputInterface(QRMatrix $matrix):QROutputInterface{
-		// @todo: remove custom invocation in v6
-		$outputInterface = (QROutputInterface::MODES[$this->options->outputType] ?? null);
-
-		if($this->options->outputType === QROutputInterface::CUSTOM){
-			$outputInterface = $this->options->outputInterface;
-		}
+		$outputInterface = $this->options->outputInterface;
 
-		if(!$outputInterface || !class_exists($outputInterface)){
+		if(empty($outputInterface) || !class_exists($outputInterface)){
 			throw new QRCodeOutputException('invalid output module');
 		}
 
@@ -310,57 +164,13 @@ class QRCode{
 		return new $outputInterface($this->options, $matrix);
 	}
 
-	/**
-	 * checks if a string qualifies as numeric (convenience method)
-	 *
-	 * @deprecated 5.0.0 use Number::validateString() instead
-	 * @see \chillerlan\QRCode\Data\Number::validateString()
-	 * @codeCoverageIgnore
-	 */
-	public function isNumber(string $string):bool{
-		return Number::validateString($string);
-	}
-
-	/**
-	 * checks if a string qualifies as alphanumeric (convenience method)
-	 *
-	 * @deprecated 5.0.0 use AlphaNum::validateString() instead
-	 * @see \chillerlan\QRCode\Data\AlphaNum::validateString()
-	 * @codeCoverageIgnore
-	 */
-	public function isAlphaNum(string $string):bool{
-		return AlphaNum::validateString($string);
-	}
-
-	/**
-	 * checks if a string qualifies as Kanji (convenience method)
-	 *
-	 * @deprecated 5.0.0 use Kanji::validateString() instead
-	 * @see \chillerlan\QRCode\Data\Kanji::validateString()
-	 * @codeCoverageIgnore
-	 */
-	public function isKanji(string $string):bool{
-		return Kanji::validateString($string);
-	}
-
-	/**
-	 * a dummy (convenience method)
-	 *
-	 * @deprecated 5.0.0 use Byte::validateString() instead
-	 * @see \chillerlan\QRCode\Data\Byte::validateString()
-	 * @codeCoverageIgnore
-	 */
-	public function isByte(string $string):bool{
-		return Byte::validateString($string);
-	}
-
 	/**
 	 * Adds a data segment
 	 *
 	 * ISO/IEC 18004:2000 8.3.6 - Mixing modes
 	 * ISO/IEC 18004:2000 Annex H - Optimisation of bit stream length
 	 */
-	public function addSegment(QRDataModeInterface $segment):self{
+	public function addSegment(QRDataModeInterface $segment):static{
 		$this->dataSegments[] = $segment;
 
 		return $this;
@@ -371,7 +181,7 @@ class QRCode{
 	 *
 	 * @codeCoverageIgnore
 	 */
-	public function clearSegments():self{
+	public function clearSegments():static{
 		$this->dataSegments = [];
 
 		return $this;
@@ -382,7 +192,7 @@ class QRCode{
 	 *
 	 * ISO/IEC 18004:2000 8.3.2 - Numeric Mode
 	 */
-	public function addNumericSegment(string $data):self{
+	public function addNumericSegment(string $data):static{
 		return $this->addSegment(new Number($data));
 	}
 
@@ -391,7 +201,7 @@ class QRCode{
 	 *
 	 * ISO/IEC 18004:2000 8.3.3 - Alphanumeric Mode
 	 */
-	public function addAlphaNumSegment(string $data):self{
+	public function addAlphaNumSegment(string $data):static{
 		return $this->addSegment(new AlphaNum($data));
 	}
 
@@ -400,7 +210,7 @@ class QRCode{
 	 *
 	 * ISO/IEC 18004:2000 8.3.5 - Kanji Mode
 	 */
-	public function addKanjiSegment(string $data):self{
+	public function addKanjiSegment(string $data):static{
 		return $this->addSegment(new Kanji($data));
 	}
 
@@ -409,7 +219,7 @@ class QRCode{
 	 *
 	 * GBT18284-2000 Hanzi Mode
 	 */
-	public function addHanziSegment(string $data):self{
+	public function addHanziSegment(string $data):static{
 		return $this->addSegment(new Hanzi($data));
 	}
 
@@ -418,7 +228,7 @@ class QRCode{
 	 *
 	 * ISO/IEC 18004:2000 8.3.4 - 8-bit Byte Mode
 	 */
-	public function addByteSegment(string $data):self{
+	public function addByteSegment(string $data):static{
 		return $this->addSegment(new Byte($data));
 	}
 
@@ -429,7 +239,7 @@ class QRCode{
 	 *
 	 * ISO/IEC 18004:2000 8.3.1 - Extended Channel Interpretation (ECI) Mode
 	 */
-	public function addEciDesignator(int $encoding):self{
+	public function addEciDesignator(int $encoding):static{
 		return $this->addSegment(new ECI($encoding));
 	}
 
@@ -442,7 +252,7 @@ class QRCode{
 	 *
 	 * @throws \chillerlan\QRCode\QRCodeException
 	 */
-	public function addEciSegment(int $encoding, string $data):self{
+	public function addEciSegment(int $encoding, string $data):static{
 		// validate the encoding id
 		$eciCharset = new ECICharset($encoding);
 		// get charset name

+ 32 - 172
src/QROptionsTrait.php

@@ -14,9 +14,9 @@
 
 namespace chillerlan\QRCode;
 
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRMarkupSVG;
 use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
-use function extension_loaded, in_array, max, min, strtolower;
+use function constant, extension_loaded, in_array, is_string, max, min, sprintf, strtolower, strtoupper, trim;
 use const JSON_THROW_ON_ERROR, PHP_EOL;
 
 /**
@@ -61,7 +61,6 @@ trait QROptionsTrait{
 	 * - `Q` => 25%
 	 * - `H` => 30%
 	 *
-	 * @todo: accept string values (PHP8+)
 	 * @see \chillerlan\QRCode\Common\EccLevel
 	 * @see https://github.com/chillerlan/php-qrcode/discussions/160
 	 */
@@ -96,44 +95,9 @@ trait QROptionsTrait{
 	 */
 
 	/**
-	 * The built-in output type
-	 *
-	 * - `QROutputInterface::MARKUP_SVG` (default)
-	 * - `QROutputInterface::MARKUP_HTML`
-	 * - `QROutputInterface::GDIMAGE_BMP`
-	 * - `QROutputInterface::GDIMAGE_GIF`
-	 * - `QROutputInterface::GDIMAGE_JPG`
-	 * - `QROutputInterface::GDIMAGE_PNG`
-	 * - `QROutputInterface::GDIMAGE_WEBP`
-	 * - `QROutputInterface::STRING_TEXT`
-	 * - `QROutputInterface::STRING_JSON`
-	 * - `QROutputInterface::IMAGICK`
-	 * - `QROutputInterface::EPS`
-	 * - `QROutputInterface::FPDF`
-	 * - `QROutputInterface::CUSTOM`
-	 *
-	 * @see \chillerlan\QRCode\Output\QREps
-	 * @see \chillerlan\QRCode\Output\QRFpdf
-	 * @see \chillerlan\QRCode\Output\QRGdImage
-	 * @see \chillerlan\QRCode\Output\QRImagick
-	 * @see \chillerlan\QRCode\Output\QRMarkupHTML
-	 * @see \chillerlan\QRCode\Output\QRMarkupSVG
-	 * @see \chillerlan\QRCode\Output\QRString
-	 * @see https://github.com/chillerlan/php-qrcode/issues/223
-	 *
-	 * @deprecated 5.0.0 see issue #223
-	 */
-	protected string $outputType = QROutputInterface::MARKUP_SVG;
-
-	/**
-	 * The FQCN of the custom `QROutputInterface`
-	 *
-	 * if `QROptions::$outputType` is set to `QROutputInterface::CUSTOM` (default: `null`)
-	 *
-	 * @deprecated 5.0.0 the nullable type will be removed in future versions
-	 *                   and the default value will be set to `QRMarkupSVG::class`
+	 * The FQCN of the `QROutputInterface` to use
 	 */
-	protected ?string $outputInterface = null;
+	protected string $outputInterface = QRMarkupSVG::class;
 
 	/**
 	 * Return the image resource instead of a render if applicable.
@@ -186,10 +150,8 @@ trait QROptionsTrait{
 	 * - `QRImagick`: defaults to `"white"`
 	 * - `QRGdImage`: defaults to `[255, 255, 255]`
 	 * - `QRFpdf`: defaults to blank internally (white page)
-	 *
-	 * @var mixed|null
 	 */
-	protected $bgColor = null;
+	protected mixed $bgColor = null;
 
 	/**
 	 * Whether to invert the matrix (reflectance reversal)
@@ -324,7 +286,7 @@ trait QROptionsTrait{
 	 *
 	 * @var mixed|null
 	 */
-	protected $transparencyColor = null;
+	protected mixed $transparencyColor = null;
 
 	/**
 	 * Compression quality
@@ -511,6 +473,32 @@ trait QROptionsTrait{
 		$this->version = ($version !== Version::AUTO) ? max(1, min(40, $version)) : Version::AUTO;
 	}
 
+	/**
+	 * sets the ECC level
+	 *
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function set_eccLevel(int|string $eccLevel):void{
+
+		if(is_string($eccLevel)){
+			$ecc = strtoupper(trim($eccLevel));
+
+			if(!in_array($ecc, ['L', 'M', 'Q', 'H'])){
+				throw new QRCodeException(sprintf('Invalid ECC level: "%s"', $ecc));
+			}
+
+			// @todo: PHP 8.3+
+			// $eccLevel = EccLevel::{$ecc};
+			$eccLevel = constant(EccLevel::class.'::'.$ecc);
+		}
+
+		if((0b11 & $eccLevel) !== $eccLevel){
+			throw new QRCodeException(sprintf('Invalid ECC level: "%s"', $eccLevel));
+		}
+
+		$this->eccLevel = $eccLevel;
+	}
+
 	/**
 	 * sets/clamps the quiet zone size
 	 */
@@ -587,132 +575,4 @@ trait QROptionsTrait{
 		$this->circleRadius = max(0.1, min(0.75, $circleRadius));
 	}
 
-	/*
-	 * redirect calls of deprecated variables to new/renamed property
-	 */
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$outputBase64 instead
-	 * @see        \chillerlan\QRCode\QROptions::$outputBase64
-	 */
-	protected bool $imageBase64;
-
-	/**
-	 * redirect call to the new variable
-	 *
-	 * @deprecated 5.0.0 use QROptions::$outputBase64 instead
-	 * @see        \chillerlan\QRCode\QROptions::$outputBase64
-	 * @codeCoverageIgnore
-	 */
-	protected function set_imageBase64(bool $imageBase64):void{
-		$this->outputBase64 = $imageBase64;
-	}
-
-	/**
-	 * redirect call to the new variable
-	 *
-	 * @deprecated 5.0.0 use QROptions::$outputBase64 instead
-	 * @see        \chillerlan\QRCode\QROptions::$outputBase64
-	 * @codeCoverageIgnore
-	 */
-	protected function get_imageBase64():bool{
-		return $this->outputBase64;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 */
-	protected int $jpegQuality;
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 * @codeCoverageIgnore
-	 */
-	protected function set_jpegQuality(int $jpegQuality):void{
-		$this->quality = $jpegQuality;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 * @codeCoverageIgnore
-	 */
-	protected function get_jpegQuality():int{
-		return $this->quality;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 */
-	protected int $pngCompression;
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 * @codeCoverageIgnore
-	 */
-	protected function set_pngCompression(int $pngCompression):void{
-		$this->quality = $pngCompression;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$quality instead
-	 * @see        \chillerlan\QRCode\QROptions::$quality
-	 * @codeCoverageIgnore
-	 */
-	protected function get_pngCompression():int{
-		return $this->quality;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$transparencyColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$transparencyColor
-	 */
-	protected array $imageTransparencyBG;
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$transparencyColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$transparencyColor
-	 * @codeCoverageIgnore
-	 */
-	protected function set_imageTransparencyBG(?array $imageTransparencyBG):void{
-		$this->transparencyColor = $imageTransparencyBG;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$transparencyColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$transparencyColor
-	 * @codeCoverageIgnore
-	 */
-	protected function get_imageTransparencyBG():?array{
-		return $this->transparencyColor;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$bgColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$bgColor
-	 */
-	protected string $imagickBG;
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$bgColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$bgColor
-	 * @codeCoverageIgnore
-	 */
-	protected function set_imagickBG(?string $imagickBG):void{
-		$this->bgColor = $imagickBG;
-	}
-
-	/**
-	 * @deprecated 5.0.0 use QROptions::$bgColor instead
-	 * @see        \chillerlan\QRCode\QROptions::$bgColor
-	 * @codeCoverageIgnore
-	 */
-	protected function get_imagickBG():?string{
-		return $this->bgColor;
-	}
-
 }

+ 2 - 4
tests/Data/QRDataTest.php

@@ -13,8 +13,7 @@ namespace chillerlan\QRCodeTest\Data;
 use chillerlan\QRCode\Common\BitBuffer;
 use chillerlan\QRCode\Common\MaskPattern;
 use chillerlan\QRCode\Data\QRData;
-use chillerlan\QRCode\Output\QRGdImage;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRGdImagePNG;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
 use PHPUnit\Framework\TestCase;
@@ -51,11 +50,10 @@ final class QRDataTest extends TestCase{
 		$this::assertSame(3, $matrix->getVersion()->getVersionNumber());
 
 		// attempt to read
-		$options->outputType                  = QROutputInterface::GDIMAGE_PNG;
 		$options->outputBase64                = false;
 		$options->readerUseImagickIfAvailable = false;
 
-		$output       = new QRGdImage($options, $matrix);
+		$output       = new QRGdImagePNG($options, $matrix);
 		$decodeResult = (new QRCode($options))->readFromBlob($output->dump());
 
 		QRMatrixTest::debugMatrix($matrix);

+ 21 - 22
tests/Data/QRMatrixTest.php

@@ -13,7 +13,7 @@ namespace chillerlan\QRCodeTest\Data;
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
 use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
-use chillerlan\QRCode\Output\{QROutputInterface, QRString};
+use chillerlan\QRCode\Output\QRStringText;
 use PHPUnit\Framework\TestCase;
 use Generator;
 use function defined;
@@ -47,41 +47,40 @@ final class QRMatrixTest extends TestCase{
 		}
 
 		$opt = new QROptions;
-		$opt->outputType  = QROutputInterface::STRING_TEXT;
-		$opt->eol         = "\n";
+		$opt->eol          = "\n";
 		$opt->moduleValues = [
 			// finder
-			QRMatrix::M_FINDER_DARK    => QRString::ansi8('██', 124), // dark (true)
-			QRMatrix::M_FINDER         => QRString::ansi8('░░', 124), // light (false)
-			QRMatrix::M_FINDER_DOT     => QRString::ansi8('██', 124), // finder dot, dark (true)
+			QRMatrix::M_FINDER_DARK    => QRStringText::ansi8('██', 124), // dark (true)
+			QRMatrix::M_FINDER         => QRStringText::ansi8('░░', 124), // light (false)
+			QRMatrix::M_FINDER_DOT     => QRStringText::ansi8('██', 124), // finder dot, dark (true)
 			// alignment
-			QRMatrix::M_ALIGNMENT_DARK => QRString::ansi8('██', 2),
-			QRMatrix::M_ALIGNMENT      => QRString::ansi8('░░', 2),
+			QRMatrix::M_ALIGNMENT_DARK => QRStringText::ansi8('██', 2),
+			QRMatrix::M_ALIGNMENT      => QRStringText::ansi8('░░', 2),
 			// timing
-			QRMatrix::M_TIMING_DARK    => QRString::ansi8('██', 184),
-			QRMatrix::M_TIMING         => QRString::ansi8('░░', 184),
+			QRMatrix::M_TIMING_DARK    => QRStringText::ansi8('██', 184),
+			QRMatrix::M_TIMING         => QRStringText::ansi8('░░', 184),
 			// format
-			QRMatrix::M_FORMAT_DARK    => QRString::ansi8('██', 200),
-			QRMatrix::M_FORMAT         => QRString::ansi8('░░', 200),
+			QRMatrix::M_FORMAT_DARK    => QRStringText::ansi8('██', 200),
+			QRMatrix::M_FORMAT         => QRStringText::ansi8('░░', 200),
 			// version
-			QRMatrix::M_VERSION_DARK   => QRString::ansi8('██', 21),
-			QRMatrix::M_VERSION        => QRString::ansi8('░░', 21),
+			QRMatrix::M_VERSION_DARK   => QRStringText::ansi8('██', 21),
+			QRMatrix::M_VERSION        => QRStringText::ansi8('░░', 21),
 			// data
-			QRMatrix::M_DATA_DARK      => QRString::ansi8('██', 166),
-			QRMatrix::M_DATA           => QRString::ansi8('░░', 166),
+			QRMatrix::M_DATA_DARK      => QRStringText::ansi8('██', 166),
+			QRMatrix::M_DATA           => QRStringText::ansi8('░░', 166),
 			// dark module
-			QRMatrix::M_DARKMODULE     => QRString::ansi8('██', 53),
+			QRMatrix::M_DARKMODULE     => QRStringText::ansi8('██', 53),
 			// separator
-			QRMatrix::M_SEPARATOR      => QRString::ansi8('░░', 219),
+			QRMatrix::M_SEPARATOR      => QRStringText::ansi8('░░', 219),
 			// quiet zone
-			QRMatrix::M_QUIETZONE      => QRString::ansi8('░░', 195),
+			QRMatrix::M_QUIETZONE      => QRStringText::ansi8('░░', 195),
 			// logo space
-			QRMatrix::M_LOGO           => QRString::ansi8('░░', 105),
+			QRMatrix::M_LOGO           => QRStringText::ansi8('░░', 105),
 			// empty
-			QRMatrix::M_NULL           => QRString::ansi8('░░', 231),
+			QRMatrix::M_NULL           => QRStringText::ansi8('░░', 231),
 		];
 
-		$out = (new QRString($opt, $matrix))->dump();
+		$out = (new QRStringText($opt, $matrix))->dump();
 
 		echo "\n\n".$out."\n\n";
 	}

+ 1 - 3
tests/Output/QREpsTest.php

@@ -12,12 +12,10 @@ namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\QREps;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 class QREpsTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QREps::class;
-	protected string $type = QROutputInterface::EPS;
+	protected string $FQN = QREps::class;
 
 	public static function moduleValueProvider():array{
 		return [

+ 2 - 3
tests/Output/QRFpdfTest.php

@@ -12,7 +12,7 @@ namespace chillerlan\QRCodeTest\Output;
 
 use FPDF;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QRFpdf, QROutputInterface};
+use chillerlan\QRCode\Output\QRFpdf;
 
 use function class_exists;
 
@@ -21,8 +21,7 @@ use function class_exists;
  */
 final class QRFpdfTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QRFpdf::class;
-	protected string $type = QROutputInterface::FPDF;
+	protected string $FQN = QRFpdf::class;
 
 	/**
 	 * @inheritDoc

+ 1 - 3
tests/Output/QRGdImageBMPTest.php

@@ -11,14 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Output\QRGdImageBMP;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  *
  */
 final class QRGdImageBMPTest extends QRGdImageTestAbstract{
 
-	protected string $type = QROutputInterface::GDIMAGE_BMP;
-	protected string $FQN  = QRGdImageBMP::class;
+	protected string $FQN = QRGdImageBMP::class;
 
 }

+ 1 - 3
tests/Output/QRGdImageGIFTest.php

@@ -11,14 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Output\QRGdImageGIF;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  *
  */
 final class QRGdImageGIFTest extends QRGdImageTestAbstract{
 
-	protected string $type = QROutputInterface::GDIMAGE_GIF;
-	protected string $FQN  = QRGdImageGIF::class;
+	protected string $FQN = QRGdImageGIF::class;
 
 }

+ 0 - 2
tests/Output/QRGdImageJPGTest.php

@@ -11,14 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Output\QRGdImageJPEG;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  *
  */
 final class QRGdImageJPGTest extends QRGdImageTestAbstract{
 
-	protected string $type = QROutputInterface::GDIMAGE_JPG;
 	protected string $FQN  = QRGdImageJPEG::class;
 
 }

+ 0 - 2
tests/Output/QRGdImagePNGTest.php

@@ -11,14 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Output\QRGdImagePNG;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  *
  */
 final class QRGdImagePNGTest extends QRGdImageTestAbstract{
 
-	protected string $type = QROutputInterface::GDIMAGE_PNG;
 	protected string $FQN  = QRGdImagePNG::class;
 
 }

+ 1 - 3
tests/Output/QRGdImageWEBPTest.php

@@ -11,14 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Output\QRGdImageWEBP;
-use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  *
  */
 final class QRGdImageWEBPTest extends QRGdImageTestAbstract{
 
-	protected string $type = QROutputInterface::GDIMAGE_WEBP;
-	protected string $FQN  = QRGdImageWEBP::class;
+	protected string $FQN = QRGdImageWEBP::class;
 
 }

+ 2 - 3
tests/Output/QRImagickTest.php

@@ -14,7 +14,7 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\{QRImagick, QROutputInterface};
+use chillerlan\QRCode\Output\QRImagick;
 use Imagick;
 
 /**
@@ -22,8 +22,7 @@ use Imagick;
  */
 final class QRImagickTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QRImagick::class;
-	protected string $type = QROutputInterface::IMAGICK;
+	protected string $FQN = QRImagick::class;
 
 	/**
 	 * @inheritDoc

+ 2 - 3
tests/Output/QRMarkupHTMLTest.php

@@ -10,14 +10,13 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\{QRMarkupHTML, QROutputInterface};
+use chillerlan\QRCode\Output\QRMarkupHTML;
 
 /**
  *
  */
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 
-	protected string $FQN  = QRMarkupHTML::class;
-	protected string $type = QROutputInterface::MARKUP_HTML;
+	protected string $FQN = QRMarkupHTML::class;
 
 }

+ 2 - 3
tests/Output/QRMarkupSVGTest.php

@@ -10,15 +10,14 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\{QRMarkupSVG, QROutputInterface};
+use chillerlan\QRCode\Output\QRMarkupSVG;
 
 /**
  *
  */
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 
-	protected string $FQN  = QRMarkupSVG::class;
-	protected string $type = QROutputInterface::MARKUP_SVG;
+	protected string $FQN = QRMarkupSVG::class;
 
 	public static function moduleValueProvider():array{
 		return [

+ 3 - 3
tests/Output/QROutputTestAbstract.php

@@ -17,6 +17,7 @@ use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
 use PHPUnit\Framework\TestCase;
 
 use function file_exists, mkdir;
+use function str_replace;
 
 /**
  * Test abstract for the several (built-in) output modules,
@@ -30,7 +31,6 @@ abstract class QROutputTestAbstract extends TestCase{
 	protected QRMatrix          $matrix;
 	protected string            $builddir = __DIR__.'/../../.build/output_test';
 	protected string            $FQN;
-	protected string            $type;
 
 	/**
 	 * Attempts to create a directory under /.build and instances several required objects
@@ -42,7 +42,7 @@ abstract class QROutputTestAbstract extends TestCase{
 		}
 
 		$this->options             = new QROptions;
-		$this->options->outputType = $this->type;
+		$this->options->outputInterface = $this->FQN;
 		$this->matrix              = (new QRCode($this->options))->addByteSegment('testdata')->getQRMatrix();
 		$this->outputInterface     = new $this->FQN($this->options, $this->matrix);
 	}
@@ -94,7 +94,7 @@ abstract class QROutputTestAbstract extends TestCase{
 		$this->options->outputBase64 = false;
 		$this->outputInterface       = new $this->FQN($this->options, $this->matrix);
 		// create the cache file
-		$file = $this->builddir.'/test.output.'.$this->type;
+		$file = $this->builddir.'/test.output.'.str_replace('chillerlan\\QRCode\\Output\\', '', $this->FQN);
 		$data = $this->outputInterface->dump($file);
 
 		$this::assertSame($data, file_get_contents($file));

+ 1 - 3
tests/Output/QRStringJSONTest.php

@@ -10,7 +10,6 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QROutputInterface;
 use chillerlan\QRCode\Output\QRStringJSON;
 use function extension_loaded;
 
@@ -19,8 +18,7 @@ use function extension_loaded;
  */
 final class QRStringJSONTest extends QROutputTestAbstract{
 
-	protected string $type = QROutputInterface::STRING_JSON;
-	protected string $FQN  = QRStringJSON::class;
+	protected string $FQN = QRStringJSON::class;
 
 	/**
 	 * @inheritDoc

+ 1 - 3
tests/Output/QRStringTEXTTest.php

@@ -11,7 +11,6 @@
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
 use chillerlan\QRCode\Output\QRStringText;
 
 /**
@@ -19,8 +18,7 @@ use chillerlan\QRCode\Output\QRStringText;
  */
 final class QRStringTEXTTest extends QROutputTestAbstract{
 
-	protected string $type = QROutputInterface::STRING_TEXT;
-	protected string $FQN  = QRStringText::class;
+	protected string $FQN = QRStringText::class;
 
 	public static function moduleValueProvider():array{
 		return [

+ 3 - 4
tests/Performance/output.php

@@ -37,12 +37,11 @@ $generator = new class () {
 
 		for($v = 5; $v <= 40; $v += 5){
 			$version  = new Version($v);
-			foreach(QROutputInterface::MODES as $outputType => $FQN){
+			foreach(QROutputInterface::MODES as $FQN){
 				$name = str_replace('chillerlan\\QRCode\\Output\\', '', $FQN);
 
 				yield sprintf('version %2s: %-14s', $version, $name) => [
 					$version->getVersionNumber(),
-					$outputType,
 					$FQN,
 					substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
 					$name,
@@ -57,11 +56,11 @@ $generator = new class () {
 $test = new PerformanceTest(100);
 $json = [];
 
-foreach($generator->dataProvider() as $key => [$version, $outputType, $FQN, $data, $name]){
+foreach($generator->dataProvider() as $key => [$version, $FQN, $data, $name]){
 
 	$options = new QROptions([
 		'version'             => $version,
-		'outputType'          => $outputType,
+		'outputInterface'     => $FQN,
 		'connectPaths'        => true,
 		'drawLightModules'    => true,
 		'drawCircularModules' => true,

+ 4 - 4
tests/Performance/qrcode.php

@@ -12,7 +12,7 @@ namespace chillerlan\QRCodeTest\Performance;
 
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRStringJSON;
 use chillerlan\QRCodeTest\QRMaxLengthTrait;
 use Generator;
 use function file_put_contents;
@@ -70,9 +70,9 @@ $json = [];
 foreach($generator->dataProvider() as $key => [$version, $eccLevel, $dataModeInterface, $dataModeName, $data]){
 
 	$options = new QROptions([
-		'outputType' => QROutputInterface::STRING_JSON,
-		'version'    => $version,
-		'eccLevel'   => $eccLevel->getLevel(),
+		'outputInterface' => QRStringJSON::class,
+		'version'         => $version,
+		'eccLevel'        => $eccLevel->getLevel(),
 	]);
 
 	$test->run(fn() => (new QRCode($options))->addSegment(new $dataModeInterface($data))->render());

+ 4 - 4
tests/QRCodeReaderTestAbstract.php

@@ -15,7 +15,7 @@ namespace chillerlan\QRCodeTest;
 use chillerlan\QRCodeTest\Data\QRMatrixTest;
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\Output\QRGdImagePNG;
 use chillerlan\Settings\SettingsContainerInterface;
 use PHPUnit\Framework\TestCase;
 use Exception, Generator;
@@ -96,8 +96,8 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 	}
 
 	public function testReaderMultiMode():void{
-		$this->options->outputType   = QROutputInterface::GDIMAGE_PNG;
-		$this->options->outputBase64 = false;
+		$this->options->outputInterface = QRGdImagePNG::class;
+		$this->options->outputBase64    = false;
 
 		$numeric  = '123456789012345678901234567890';
 		$alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:';
@@ -145,7 +145,7 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 	 * @dataProvider dataTestProvider
 	 */
 	public function testReadData(Version $version, EccLevel $ecc, string $expected):void{
-		$this->options->outputType       = QROutputInterface::GDIMAGE_PNG;
+		$this->options->outputInterface  = QRGdImagePNG::class;
 		$this->options->imageTransparent = false;
 		$this->options->eccLevel         = $ecc->getLevel();
 		$this->options->version          = $version->getVersionNumber();

+ 2 - 15
tests/QRCodeTest.php

@@ -11,7 +11,7 @@
 namespace chillerlan\QRCodeTest;
 
 use chillerlan\QRCode\{QROptions, QRCode};
-use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
+use chillerlan\QRCode\Output\QRCodeOutputException;
 use PHPUnit\Framework\TestCase;
 use stdClass;
 use function file_get_contents;
@@ -33,18 +33,6 @@ final class QRCodeTest extends TestCase{
 		$this->options = new QROptions;
 	}
 
-	/**
-	 * tests if an exception is thrown when an invalid (built-in) output type is specified
-	 */
-	public function testInitOutputInterfaceException():void{
-		$this->expectException(QRCodeOutputException::class);
-		$this->expectExceptionMessage('invalid output module');
-
-		$this->options->outputType = 'foo';
-
-		$this->qrcode->setOptions($this->options)->render('test');
-	}
-
 	/**
 	 * tests if an exception is thrown if the given output class does not exist
 	 */
@@ -52,7 +40,7 @@ final class QRCodeTest extends TestCase{
 		$this->expectException(QRCodeOutputException::class);
 		$this->expectExceptionMessage('invalid output module');
 
-		$this->options->outputType = QROutputInterface::CUSTOM;
+		$this->options->outputInterface = 'foo';
 
 		$this->qrcode->setOptions($this->options)->render('test');
 	}
@@ -64,7 +52,6 @@ final class QRCodeTest extends TestCase{
 		$this->expectException(QRCodeOutputException::class);
 		$this->expectExceptionMessage('output module does not implement QROutputInterface');
 
-		$this->options->outputType      = QROutputInterface::CUSTOM;
 		$this->options->outputInterface = stdClass::class;
 
 		$this->qrcode->setOptions($this->options)->render('test');

+ 4 - 6
tests/QRMaxLengthTrait.php

@@ -25,7 +25,7 @@ trait QRMaxLengthTrait{
 	 *
 	 * @var int[][][]
 	 */
-	private static array $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]],
@@ -73,8 +73,6 @@ trait QRMaxLengthTrait{
 	/**
 	 * the maximum character count for the given $mode and $eccLevel
 	 *
-	 * @todo: this methood is only used in unit tests (incl. MAX_LENGTH table)
-	 *
 	 * @throws \chillerlan\QRCode\QRCodeException
 	 * @codeCoverageIgnore
 	 */
@@ -95,16 +93,16 @@ trait QRMaxLengthTrait{
 		$ver = $version->getVersionNumber();
 		$ecc = $eccLevel->getOrdinal();
 
-		if(!isset(static::$MAX_LENGTH[$ver])){
+		if(!isset(static::MAX_LENGTH[$ver])){
 			throw new QRCodeException('invalid $version');
 		}
 
-		if(!isset(static::$MAX_LENGTH[$ver][$dataMode][$ecc])){
+		if(!isset(static::MAX_LENGTH[$ver][$dataMode][$ecc])){
 			throw new QRCodeException('invalid $ecc');
 		}
 
 		/** @SuppressWarnings(PHPMD.UndefinedVariable) */
-		$maxlength = static::$MAX_LENGTH[$ver][$dataMode][$ecc];
+		$maxlength = static::MAX_LENGTH[$ver][$dataMode][$ecc];
 
 		// Hanzi mode sets an additional 4 bit long subset identifier
 		if($mode === Mode::HANZI){

+ 35 - 0
tests/QROptionsTest.php

@@ -12,6 +12,8 @@
 
 namespace chillerlan\QRCodeTest;
 
+use chillerlan\QRCode\Common\EccLevel;
+use chillerlan\QRCode\QRCodeException;
 use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Common\Version;
 use PHPUnit\Framework\TestCase;
@@ -68,6 +70,39 @@ final class QROptionsTest extends TestCase{
 		$this::assertSame($expectedMax, $o->versionMax);
 	}
 
+	/**
+	 * Tests setting the ECC level from string or int
+	 */
+	public function testSetEccLevel():void{
+		$o = new QROptions(['eccLevel' => EccLevel::H]);
+
+		$this::assertSame(EccLevel::H, $o->eccLevel);
+
+		$o->eccLevel = 'q';
+
+		$this::assertSame(EccLevel::Q, $o->eccLevel);
+	}
+
+	/**
+	 * Tests if an exception is thrown when attempting to set an invalid ECC level integer
+	 */
+	public function testSetEccLevelFromIntException():void{
+		$this->expectException(QRCodeException::class);
+		$this->expectExceptionMessage('Invalid ECC level: "42"');
+		/** @phan-suppress-next-line PhanNoopNew */
+		new QROptions(['eccLevel' => 42]);
+	}
+
+	/**
+	 * Tests if an exception is thrown when attempting to set an invalid ECC level string
+	 */
+	public function testSetEccLevelFromStringException():void{
+		$this->expectException(QRCodeException::class);
+		$this->expectExceptionMessage('Invalid ECC level: "FOO"');
+		/** @phan-suppress-next-line PhanNoopNew */
+		new QROptions(['eccLevel' => 'foo']);
+	}
+
 	/**
 	 * @return int[][][]
 	 */