From ab358f2f38c22ca062afa1859f7f95833e4cf71a Mon Sep 17 00:00:00 2001 From: smiley Date: Wed, 25 Oct 2023 21:30:48 +0200 Subject: [PATCH] :octocat: fixes/implements #223 --- .phan/config.php | 2 + examples/imageWithLogo.php | 6 +- examples/imageWithRoundedShapes.php | 4 +- examples/imageWithText.php | 7 +- examples/text.php | 34 ++++---- src/Output/QREps.php | 2 + src/Output/QRFpdf.php | 4 +- src/Output/QRGdImage.php | 65 +++++++++----- src/Output/QRGdImageBMP.php | 33 ++++++++ src/Output/QRGdImageGIF.php | 33 ++++++++ src/Output/QRGdImageJPEG.php | 42 +++++++++ src/Output/QRGdImagePNG.php | 35 ++++++++ src/Output/QRGdImageWEBP.php | 35 ++++++++ src/Output/QRMarkupHTML.php | 2 + src/Output/QRMarkupSVG.php | 4 +- src/Output/QROutputAbstract.php | 4 +- src/Output/QROutputInterface.php | 113 ++++++++++++++++++++----- src/Output/QRString.php | 2 + src/Output/QRStringJSON.php | 64 ++++++++++++++ src/Output/QRStringText.php | 86 +++++++++++++++++++ src/QROptionsTrait.php | 33 ++++++-- tests/Output/QRGdImageBMPTest.php | 2 + tests/Output/QRGdImageGIFTest.php | 2 + tests/Output/QRGdImageJPGTest.php | 2 + tests/Output/QRGdImagePNGTest.php | 2 + tests/Output/QRGdImageTestAbstract.php | 12 ++- tests/Output/QRGdImageWEBPTest.php | 2 + tests/Output/QRStringJSONTest.php | 7 +- tests/Output/QRStringTEXTTest.php | 13 ++- tests/Output/QRStringTestAbstract.php | 31 ------- 30 files changed, 569 insertions(+), 114 deletions(-) create mode 100644 src/Output/QRGdImageBMP.php create mode 100644 src/Output/QRGdImageGIF.php create mode 100644 src/Output/QRGdImageJPEG.php create mode 100644 src/Output/QRGdImagePNG.php create mode 100644 src/Output/QRGdImageWEBP.php create mode 100644 src/Output/QRStringJSON.php create mode 100644 src/Output/QRStringText.php delete mode 100644 tests/Output/QRStringTestAbstract.php diff --git a/.phan/config.php b/.phan/config.php index 3e247d61a..72b796fa2 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -56,5 +56,7 @@ return [ 'suppress_issue_types' => [ 'PhanAccessMethodInternal', 'PhanAccessOverridesFinalConstant', + 'PhanDeprecatedClass', + 'PhanDeprecatedClassConstant', ], ]; diff --git a/examples/imageWithLogo.php b/examples/imageWithLogo.php index 1f7e925f5..2f159c773 100644 --- a/examples/imageWithLogo.php +++ b/examples/imageWithLogo.php @@ -13,7 +13,7 @@ use chillerlan\QRCode\{QRCode, QROptions}; use chillerlan\QRCode\Common\EccLevel; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\{QRGdImage, QRCodeOutputException}; +use chillerlan\QRCode\Output\{QRGdImagePNG, QRCodeOutputException}; require_once __DIR__.'/../vendor/autoload.php'; @@ -21,7 +21,7 @@ require_once __DIR__.'/../vendor/autoload.php'; * Class definition */ -class QRImageWithLogo extends QRGdImage{ +class QRImageWithLogo extends QRGdImagePNG{ /** * @param string|null $file @@ -64,7 +64,7 @@ class QRImageWithLogo extends QRGdImage{ $this->saveToFile($imageData, $file); if($this->options->outputBase64){ - $imageData = $this->toBase64DataURI($imageData, 'image/'.$this->options->outputType); + $imageData = $this->toBase64DataURI($imageData); } return $imageData; diff --git a/examples/imageWithRoundedShapes.php b/examples/imageWithRoundedShapes.php index a92582a84..7e05fbbf5 100644 --- a/examples/imageWithRoundedShapes.php +++ b/examples/imageWithRoundedShapes.php @@ -15,7 +15,7 @@ use chillerlan\QRCode\Common\EccLevel; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\QRGdImage; +use chillerlan\QRCode\Output\QRGdImagePNG; use chillerlan\QRCode\Output\QROutputInterface; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; @@ -27,7 +27,7 @@ require_once __DIR__ . '/../vendor/autoload.php'; // Class definition // -------------------- -class QRGdRounded extends QRGdImage{ +class QRGdRounded extends QRGdImagePNG{ /** @inheritDoc */ public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ diff --git a/examples/imageWithText.php b/examples/imageWithText.php index 1c74fd255..79a3ed326 100644 --- a/examples/imageWithText.php +++ b/examples/imageWithText.php @@ -13,7 +13,7 @@ */ use chillerlan\QRCode\{QRCode, QROptions}; -use chillerlan\QRCode\Output\{QROutputInterface, QRGdImage}; +use chillerlan\QRCode\Output\{QROutputInterface, QRGdImagePNG}; require_once __DIR__.'/../vendor/autoload.php'; @@ -21,7 +21,7 @@ require_once __DIR__.'/../vendor/autoload.php'; * Class definition */ -class QRImageWithText extends QRGdImage{ +class QRImageWithText extends QRGdImagePNG{ /** * @inheritDoc @@ -43,7 +43,7 @@ class QRImageWithText extends QRGdImage{ $this->saveToFile($imageData, $file); if($this->options->outputBase64){ - $imageData = $this->toBase64DataURI($imageData, 'image/'.$this->options->outputType); + $imageData = $this->toBase64DataURI($imageData); } return $imageData; @@ -97,7 +97,6 @@ class QRImageWithText extends QRGdImage{ $options = new QROptions; $options->version = 7; -$options->outputType = QROutputInterface::GDIMAGE_PNG; $options->scale = 3; $options->outputBase64 = false; diff --git a/examples/text.php b/examples/text.php index fe33ec5d7..aaa2b2ee3 100644 --- a/examples/text.php +++ b/examples/text.php @@ -10,7 +10,7 @@ use chillerlan\QRCode\{QRCode, QROptions}; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\{QROutputInterface, QRString}; +use chillerlan\QRCode\Output\{QROutputInterface, QRStringText}; require_once __DIR__.'/../vendor/autoload.php'; @@ -22,22 +22,22 @@ $options->outputType = QROutputInterface::STRING_TEXT; $options->eol = "\n"; $options->textLineStart = str_repeat(' ', 6); $options->moduleValues = [ - QRMatrix::M_FINDER_DARK => QRString::ansi8('██', 124), - QRMatrix::M_FINDER => QRString::ansi8('░░', 124), - QRMatrix::M_FINDER_DOT => QRString::ansi8('██', 124), - QRMatrix::M_ALIGNMENT_DARK => QRString::ansi8('██', 2), - QRMatrix::M_ALIGNMENT => QRString::ansi8('░░', 2), - QRMatrix::M_TIMING_DARK => QRString::ansi8('██', 184), - QRMatrix::M_TIMING => QRString::ansi8('░░', 184), - QRMatrix::M_FORMAT_DARK => QRString::ansi8('██', 200), - QRMatrix::M_FORMAT => QRString::ansi8('░░', 200), - QRMatrix::M_VERSION_DARK => QRString::ansi8('██', 21), - QRMatrix::M_VERSION => QRString::ansi8('░░', 21), - QRMatrix::M_DARKMODULE => QRString::ansi8('██', 53), - QRMatrix::M_DATA_DARK => QRString::ansi8('██', 166), - QRMatrix::M_DATA => QRString::ansi8('░░', 166), - QRMatrix::M_QUIETZONE => QRString::ansi8('░░', 253), - QRMatrix::M_SEPARATOR => QRString::ansi8('░░', 253), + QRMatrix::M_FINDER_DARK => QRStringText::ansi8('██', 124), + QRMatrix::M_FINDER => QRStringText::ansi8('░░', 124), + QRMatrix::M_FINDER_DOT => QRStringText::ansi8('██', 124), + QRMatrix::M_ALIGNMENT_DARK => QRStringText::ansi8('██', 2), + QRMatrix::M_ALIGNMENT => QRStringText::ansi8('░░', 2), + QRMatrix::M_TIMING_DARK => QRStringText::ansi8('██', 184), + QRMatrix::M_TIMING => QRStringText::ansi8('░░', 184), + QRMatrix::M_FORMAT_DARK => QRStringText::ansi8('██', 200), + QRMatrix::M_FORMAT => QRStringText::ansi8('░░', 200), + QRMatrix::M_VERSION_DARK => QRStringText::ansi8('██', 21), + QRMatrix::M_VERSION => QRStringText::ansi8('░░', 21), + QRMatrix::M_DARKMODULE => QRStringText::ansi8('██', 53), + QRMatrix::M_DATA_DARK => QRStringText::ansi8('██', 166), + QRMatrix::M_DATA => QRStringText::ansi8('░░', 166), + QRMatrix::M_QUIETZONE => QRStringText::ansi8('░░', 253), + QRMatrix::M_SEPARATOR => QRStringText::ansi8('░░', 253), ]; diff --git a/src/Output/QREps.php b/src/Output/QREps.php index 9702b9d06..a04f20d5b 100644 --- a/src/Output/QREps.php +++ b/src/Output/QREps.php @@ -22,6 +22,8 @@ use function array_values, count, date, implode, is_array, is_numeric, max, min, */ class QREps extends QROutputAbstract{ + public const MIME_TYPE = 'application/postscript'; + /** * @inheritDoc */ diff --git a/src/Output/QRFpdf.php b/src/Output/QRFpdf.php index 6b3732056..a80295411 100644 --- a/src/Output/QRFpdf.php +++ b/src/Output/QRFpdf.php @@ -25,6 +25,8 @@ use function array_values, class_exists, count, intval, is_array, is_numeric, ma */ class QRFpdf extends QROutputAbstract{ + public const MIME_TYPE = 'application/pdf'; + protected FPDF $fpdf; protected ?array $prevColor = null; @@ -146,7 +148,7 @@ class QRFpdf extends QROutputAbstract{ $this->saveToFile($pdfData, $file); if($this->options->outputBase64){ - $pdfData = $this->toBase64DataURI($pdfData, 'application/pdf'); + $pdfData = $this->toBase64DataURI($pdfData); } return $pdfData; diff --git a/src/Output/QRGdImage.php b/src/Output/QRGdImage.php index e7d9ed734..8158a419d 100644 --- a/src/Output/QRGdImage.php +++ b/src/Output/QRGdImage.php @@ -25,6 +25,10 @@ use function array_values, count, extension_loaded, gd_info, imagebmp, imagecolo * 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{ @@ -33,6 +37,8 @@ class QRGdImage extends QROutputAbstract{ * * @see imagecreatetruecolor() * @var resource|\GdImage + * + * @todo: add \GdImage type in v6 */ protected $image; @@ -209,6 +215,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); } @@ -261,6 +268,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){ return; } @@ -319,6 +327,42 @@ 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))); + } + + } + /** * Creates the final image by calling the desired GD output function * @@ -326,29 +370,12 @@ class QRGdImage extends QROutputAbstract{ */ protected function dumpImage():string{ $exception = null; + $imageData = null; ob_start(); try{ - - 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))); - } + $this->renderImage(); $imageData = ob_get_contents(); imagedestroy($this->image); diff --git a/src/Output/QRGdImageBMP.php b/src/Output/QRGdImageBMP.php new file mode 100644 index 000000000..268ebe7c2 --- /dev/null +++ b/src/Output/QRGdImageBMP.php @@ -0,0 +1,33 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function imagebmp; + +/** + * GdImage bmp output + * + * @see \imagebmp() + */ +class QRGdImageBMP extends QRGdImage{ + + public const MIME_TYPE = 'image/bmp'; + + /** + * @inheritDoc + */ + protected function renderImage():void{ + imagebmp($this->image, null, ($this->options->quality > 0)); + } + +} diff --git a/src/Output/QRGdImageGIF.php b/src/Output/QRGdImageGIF.php new file mode 100644 index 000000000..a02130907 --- /dev/null +++ b/src/Output/QRGdImageGIF.php @@ -0,0 +1,33 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function imagegif; + +/** + * GdImage gif output + * + * @see \imagegif() + */ +class QRGdImageGIF extends QRGdImage{ + + public const MIME_TYPE = 'image/gif'; + + /** + * @inheritDoc + */ + protected function renderImage():void{ + imagegif($this->image); + } + +} diff --git a/src/Output/QRGdImageJPEG.php b/src/Output/QRGdImageJPEG.php new file mode 100644 index 000000000..aa60f473e --- /dev/null +++ b/src/Output/QRGdImageJPEG.php @@ -0,0 +1,42 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function imagejpeg; +use function max; +use function min; + +/** + * GdImage jpeg output + * + * @see \imagejpeg() + */ +class QRGdImageJPEG extends QRGdImage{ + + public const MIME_TYPE = 'image/jpg'; + + /** + * @inheritDoc + */ + protected function setTransparencyColor():void{ + // noop - transparency is not supported + } + + /** + * @inheritDoc + */ + protected function renderImage():void{ + imagejpeg($this->image, null, max(-1, min(100, $this->options->quality))); + } + +} diff --git a/src/Output/QRGdImagePNG.php b/src/Output/QRGdImagePNG.php new file mode 100644 index 000000000..c97a868ee --- /dev/null +++ b/src/Output/QRGdImagePNG.php @@ -0,0 +1,35 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function imagepng; +use function max; +use function min; + +/** + * GdImage png output + * + * @see \imagepng() + */ +class QRGdImagePNG extends QRGdImage{ + + public const MIME_TYPE = 'image/png'; + + /** + * @inheritDoc + */ + protected function renderImage():void{ + imagepng($this->image, null, max(-1, min(9, $this->options->quality))); + } + +} diff --git a/src/Output/QRGdImageWEBP.php b/src/Output/QRGdImageWEBP.php new file mode 100644 index 000000000..4f00e3cb9 --- /dev/null +++ b/src/Output/QRGdImageWEBP.php @@ -0,0 +1,35 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function imagewebp; +use function max; +use function min; + +/** + * GdImage webp output + * + * @see \imagewebp() + */ +class QRGdImageWEBP extends QRGdImage{ + + public const MIME_TYPE = 'image/webp'; + + /** + * @inheritDoc + */ + protected function renderImage():void{ + imagewebp($this->image, null, max(-1, min(100, $this->options->quality))); + } + +} diff --git a/src/Output/QRMarkupHTML.php b/src/Output/QRMarkupHTML.php index 4c249b6d6..42dcf12f7 100644 --- a/src/Output/QRMarkupHTML.php +++ b/src/Output/QRMarkupHTML.php @@ -17,6 +17,8 @@ use function implode, sprintf; */ class QRMarkupHTML extends QRMarkup{ + public const MIME_TYPE = 'text/html'; + /** * @inheritDoc */ diff --git a/src/Output/QRMarkupSVG.php b/src/Output/QRMarkupSVG.php index 5be7cb13b..a7f85d49b 100644 --- a/src/Output/QRMarkupSVG.php +++ b/src/Output/QRMarkupSVG.php @@ -22,6 +22,8 @@ use function array_chunk, implode, is_string, preg_match, sprintf, trim; */ class QRMarkupSVG extends QRMarkup{ + public const MIME_TYPE = 'image/svg+xml'; + /** * @todo: XSS proof * @@ -69,7 +71,7 @@ class QRMarkupSVG extends QRMarkup{ // transform to data URI only when not saving to file if(!$saveToFile && $this->options->outputBase64){ - $svg = $this->toBase64DataURI($svg, 'image/svg+xml'); + $svg = $this->toBase64DataURI($svg); } return $svg; diff --git a/src/Output/QROutputAbstract.php b/src/Output/QROutputAbstract.php index d9a37689c..d8c44cc9a 100644 --- a/src/Output/QROutputAbstract.php +++ b/src/Output/QROutputAbstract.php @@ -188,8 +188,8 @@ abstract class QROutputAbstract implements QROutputInterface{ /** * Returns a base64 data URI for the given string and mime type */ - protected function toBase64DataURI(string $data, string $mime):string{ - return sprintf('data:%s;base64,%s', $mime, base64_encode($data)); + protected function toBase64DataURI(string $data, string $mime = null):string{ + return sprintf('data:%s;base64,%s', ($mime ?? $this::MIME_TYPE), base64_encode($data)); } /** diff --git a/src/Output/QROutputInterface.php b/src/Output/QROutputInterface.php index 20f4c3c8b..919f1ba75 100644 --- a/src/Output/QROutputInterface.php +++ b/src/Output/QROutputInterface.php @@ -17,48 +17,114 @@ use chillerlan\QRCode\Data\QRMatrix; */ interface QROutputInterface{ - /** @var string */ + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const MARKUP_HTML = 'html'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const MARKUP_SVG = 'svg'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const GDIMAGE_BMP = 'bmp'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const GDIMAGE_GIF = 'gif'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const GDIMAGE_JPG = 'jpg'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const GDIMAGE_PNG = 'png'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const GDIMAGE_WEBP = 'webp'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const STRING_JSON = 'json'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const STRING_TEXT = 'text'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const IMAGICK = 'imagick'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const FPDF = 'fpdf'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const EPS = 'eps'; - /** @var string */ + + /** + * @var string + * @deprecated 5.0.0 + * @see https://github.com/chillerlan/php-qrcode/issues/223 + */ public const CUSTOM = 'custom'; /** * Map of built-in output modes => class FQN * * @var string[] + * @deprecated 5.0.0 + * @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 => QRGdImage::class, - self::GDIMAGE_GIF => QRGdImage::class, - self::GDIMAGE_JPG => QRGdImage::class, - self::GDIMAGE_PNG => QRGdImage::class, - self::GDIMAGE_WEBP => QRGdImage::class, - self::STRING_JSON => QRString::class, - self::STRING_TEXT => QRString::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, @@ -130,6 +196,13 @@ interface QROutputInterface{ QRMatrix::M_FINDER_DOT => 'finder-dot', ]; + /** + * @var string + * @see \chillerlan\QRCode\Output\QROutputAbstract::toBase64DataURI() + * @internal do not call this constant from the interface, but rather from one of the child classes + */ + public const MIME_TYPE = ''; + /** * Determines whether the given value is valid * diff --git a/src/Output/QRString.php b/src/Output/QRString.php index 3da83d6e2..cb70a6cce 100644 --- a/src/Output/QRString.php +++ b/src/Output/QRString.php @@ -17,6 +17,8 @@ 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{ diff --git a/src/Output/QRStringJSON.php b/src/Output/QRStringJSON.php new file mode 100644 index 000000000..cd942a74e --- /dev/null +++ b/src/Output/QRStringJSON.php @@ -0,0 +1,64 @@ + + * @copyright 2023 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use function json_encode; + +/** + * + */ +class QRStringJSON extends QROutputAbstract{ + + public const MIME_TYPE = 'application/json'; + + /** + * @inheritDoc + * @throws \JsonException + */ + public function dump(string $file = null):string{ + $matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans); + $data = json_encode($matrix, $this->options->jsonFlags);; + + $this->saveToFile($data, $file); + + return $data; + } + + /** + * unused - required by interface + * + * @inheritDoc + */ + protected function prepareModuleValue($value):string{ + return ''; + } + + /** + * unused - required by interface + * + * @inheritDoc + */ + protected function getDefaultModuleValue(bool $isDark):string{ + return ''; + } + + /** + * unused - required by interface + * + * @inheritDoc + */ + public static function moduleValueIsValid($value):bool{ + return true; + } + +} diff --git a/src/Output/QRStringText.php b/src/Output/QRStringText.php new file mode 100644 index 000000000..94fc5c86f --- /dev/null +++ b/src/Output/QRStringText.php @@ -0,0 +1,86 @@ + + * @copyright 2023 smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use function implode; +use function is_string; +use function max; +use function min; +use function sprintf; + +/** + * + */ +class QRStringText extends QROutputAbstract{ + + public const MIME_TYPE = 'text/plain'; + + /** + * @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{ + $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); + } + + $data = implode($this->eol, $lines); + + $this->saveToFile($data, $file); + + return $data; + } + + /** + * 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); + } + +} diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index 81e5443f2..8e542b4fe 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -9,7 +9,7 @@ * @copyright 2018 smiley * @license MIT * - * @noinspection PhpUnused + * @noinspection PhpUnused, PhpComposerExtensionStubsInspection */ namespace chillerlan\QRCode; @@ -17,7 +17,7 @@ namespace chillerlan\QRCode; use chillerlan\QRCode\Output\QROutputInterface; use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version}; use function extension_loaded, in_array, max, min, strtolower; -use const PHP_EOL; +use const JSON_THROW_ON_ERROR, PHP_EOL; /** * The QRCode plug-in settings & setter functionality @@ -119,6 +119,9 @@ trait QROptionsTrait{ * @see \chillerlan\QRCode\Output\QRMarkupHTML * @see \chillerlan\QRCode\Output\QRMarkupSVG * @see \chillerlan\QRCode\Output\QRString + * + * @deprecated 5.0.0 use `QROptions::$outputInterface` instead + * @see https://github.com/chillerlan/php-qrcode/issues/223 */ protected string $outputType = QROutputInterface::MARKUP_SVG; @@ -126,6 +129,9 @@ trait QROptionsTrait{ * 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` */ protected ?string $outputInterface = null; @@ -325,11 +331,11 @@ trait QROptionsTrait{ * * The given value depends on the used output type: * - * - `QROutputInterface::GDIMAGE_BMP`: `[0...1]` - * - `QROutputInterface::GDIMAGE_JPG`: `[0...100]` - * - `QROutputInterface::GDIMAGE_WEBP`: `[0...9]` - * - `QROutputInterface::GDIMAGE_PNG`: `[0...100]` - * - `QROutputInterface::IMAGICK`: `[0...100]` + * - `QRGdImageBMP`: `[0...1]` + * - `QRGdImageJPEG`: `[0...100]` + * - `QRGdImageWEBP`: `[0...9]` + * - `QRGdImagePNG`: `[0...100]` + * - `QRImagick`: `[0...100]` * * @see \imagebmp() * @see \imagejpeg() @@ -410,7 +416,7 @@ trait QROptionsTrait{ protected bool $svgUseFillAttributes = true; /* - * QRString settings + * QRStringText settings */ /** @@ -418,6 +424,17 @@ trait QROptionsTrait{ */ protected string $textLineStart = ''; + /* + * QRStringJSON settings + */ + + /** + * Sets the flags to use for the `json_encode()` call + * + * @see https://www.php.net/manual/json.constants.php + */ + protected int $jsonFlags = JSON_THROW_ON_ERROR; + /** * Whether to return matrix values in JSON as booleans or `$M_TYPE` integers */ diff --git a/tests/Output/QRGdImageBMPTest.php b/tests/Output/QRGdImageBMPTest.php index b06eee6db..44f58bd2f 100644 --- a/tests/Output/QRGdImageBMPTest.php +++ b/tests/Output/QRGdImageBMPTest.php @@ -10,6 +10,7 @@ namespace chillerlan\QRCodeTest\Output; +use chillerlan\QRCode\Output\QRGdImageBMP; use chillerlan\QRCode\Output\QROutputInterface; /** @@ -18,5 +19,6 @@ use chillerlan\QRCode\Output\QROutputInterface; final class QRGdImageBMPTest extends QRGdImageTestAbstract{ protected string $type = QROutputInterface::GDIMAGE_BMP; + protected string $FQN = QRGdImageBMP::class; } diff --git a/tests/Output/QRGdImageGIFTest.php b/tests/Output/QRGdImageGIFTest.php index a9ce24e6e..0ac561895 100644 --- a/tests/Output/QRGdImageGIFTest.php +++ b/tests/Output/QRGdImageGIFTest.php @@ -10,6 +10,7 @@ namespace chillerlan\QRCodeTest\Output; +use chillerlan\QRCode\Output\QRGdImageGIF; use chillerlan\QRCode\Output\QROutputInterface; /** @@ -18,5 +19,6 @@ use chillerlan\QRCode\Output\QROutputInterface; final class QRGdImageGIFTest extends QRGdImageTestAbstract{ protected string $type = QROutputInterface::GDIMAGE_GIF; + protected string $FQN = QRGdImageGIF::class; } diff --git a/tests/Output/QRGdImageJPGTest.php b/tests/Output/QRGdImageJPGTest.php index 69b8ba85a..5700d38df 100644 --- a/tests/Output/QRGdImageJPGTest.php +++ b/tests/Output/QRGdImageJPGTest.php @@ -10,6 +10,7 @@ namespace chillerlan\QRCodeTest\Output; +use chillerlan\QRCode\Output\QRGdImageJPEG; use chillerlan\QRCode\Output\QROutputInterface; /** @@ -18,5 +19,6 @@ use chillerlan\QRCode\Output\QROutputInterface; final class QRGdImageJPGTest extends QRGdImageTestAbstract{ protected string $type = QROutputInterface::GDIMAGE_JPG; + protected string $FQN = QRGdImageJPEG::class; } diff --git a/tests/Output/QRGdImagePNGTest.php b/tests/Output/QRGdImagePNGTest.php index 5a5901271..5fc07544f 100644 --- a/tests/Output/QRGdImagePNGTest.php +++ b/tests/Output/QRGdImagePNGTest.php @@ -10,6 +10,7 @@ namespace chillerlan\QRCodeTest\Output; +use chillerlan\QRCode\Output\QRGdImagePNG; use chillerlan\QRCode\Output\QROutputInterface; /** @@ -18,5 +19,6 @@ use chillerlan\QRCode\Output\QROutputInterface; final class QRGdImagePNGTest extends QRGdImageTestAbstract{ protected string $type = QROutputInterface::GDIMAGE_PNG; + protected string $FQN = QRGdImagePNG::class; } diff --git a/tests/Output/QRGdImageTestAbstract.php b/tests/Output/QRGdImageTestAbstract.php index 69be0239d..f24db6c9f 100644 --- a/tests/Output/QRGdImageTestAbstract.php +++ b/tests/Output/QRGdImageTestAbstract.php @@ -11,7 +11,6 @@ namespace chillerlan\QRCodeTest\Output; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\QRGdImage; use const PHP_MAJOR_VERSION; /** @@ -19,8 +18,6 @@ use const PHP_MAJOR_VERSION; */ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{ - protected string $FQN = QRGdImage::class; - /** * @inheritDoc */ @@ -62,7 +59,7 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{ } /** - * + * @todo: remove php version check in v6 */ public function testOutputGetResource():void{ $this->options->returnResource = true; @@ -76,4 +73,11 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{ : $this::assertIsResource($actual); } + public function testBase64MimeType():void{ + $this->options->outputBase64 = true; + $this->outputInterface = new $this->FQN($this->options, $this->matrix); + + $this::assertStringContainsString($this->outputInterface::MIME_TYPE, $this->outputInterface->dump()); + } + } diff --git a/tests/Output/QRGdImageWEBPTest.php b/tests/Output/QRGdImageWEBPTest.php index b5513d70b..8c7b7866e 100644 --- a/tests/Output/QRGdImageWEBPTest.php +++ b/tests/Output/QRGdImageWEBPTest.php @@ -10,6 +10,7 @@ namespace chillerlan\QRCodeTest\Output; +use chillerlan\QRCode\Output\QRGdImageWEBP; use chillerlan\QRCode\Output\QROutputInterface; /** @@ -18,5 +19,6 @@ use chillerlan\QRCode\Output\QROutputInterface; final class QRGdImageWEBPTest extends QRGdImageTestAbstract{ protected string $type = QROutputInterface::GDIMAGE_WEBP; + protected string $FQN = QRGdImageWEBP::class; } diff --git a/tests/Output/QRStringJSONTest.php b/tests/Output/QRStringJSONTest.php index 118a420b9..17aad2f16 100644 --- a/tests/Output/QRStringJSONTest.php +++ b/tests/Output/QRStringJSONTest.php @@ -11,14 +11,16 @@ namespace chillerlan\QRCodeTest\Output; use chillerlan\QRCode\Output\QROutputInterface; +use chillerlan\QRCode\Output\QRStringJSON; use function extension_loaded; /** * */ -final class QRStringJSONTest extends QRStringTestAbstract{ +final class QRStringJSONTest extends QROutputTestAbstract{ protected string $type = QROutputInterface::STRING_JSON; + protected string $FQN = QRStringJSON::class; /** * @inheritDoc @@ -32,6 +34,9 @@ final class QRStringJSONTest extends QRStringTestAbstract{ parent::setUp(); } + public static function moduleValueProvider():array{ + return []; + } public function testSetModuleValues():void{ /** @noinspection PhpUnitTestFailedLineInspection */ diff --git a/tests/Output/QRStringTEXTTest.php b/tests/Output/QRStringTEXTTest.php index 75ff82669..4bb3f0e48 100644 --- a/tests/Output/QRStringTEXTTest.php +++ b/tests/Output/QRStringTEXTTest.php @@ -12,13 +12,24 @@ namespace chillerlan\QRCodeTest\Output; use chillerlan\QRCode\Data\QRMatrix; use chillerlan\QRCode\Output\QROutputInterface; +use chillerlan\QRCode\Output\QRStringText; /** * */ -final class QRStringTEXTTest extends QRStringTestAbstract{ +final class QRStringTEXTTest extends QROutputTestAbstract{ protected string $type = QROutputInterface::STRING_TEXT; + protected string $FQN = QRStringText::class; + + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type' => [[], false], + 'valid: string' => ['abc', true], + 'valid: zero length string' => ['', true], + 'valid: empty string' => [' ', true], + ]; + } /** * @inheritDoc diff --git a/tests/Output/QRStringTestAbstract.php b/tests/Output/QRStringTestAbstract.php deleted file mode 100644 index db29d667e..000000000 --- a/tests/Output/QRStringTestAbstract.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @copyright 2017 Smiley - * @license MIT - */ - -namespace chillerlan\QRCodeTest\Output; - -use chillerlan\QRCode\Output\QRString; - -/** - * Tests the QRString output module - */ -abstract class QRStringTestAbstract extends QROutputTestAbstract{ - - protected string $FQN = QRString::class; - - public static function moduleValueProvider():array{ - return [ - 'invalid: wrong type' => [[], false], - 'valid: string' => ['abc', true], - 'valid: zero length string' => ['', true], - 'valid: empty string' => [' ', true], - ]; - } - -}