mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-30 12:07:32 +00:00
:octocat: QRStringJSON overhaul
This commit is contained in:
+69
-23
@@ -18,16 +18,52 @@ use function json_encode;
|
||||
*
|
||||
*/
|
||||
class QRStringJSON extends QROutputAbstract{
|
||||
use CssColorModuleValueTrait;
|
||||
|
||||
final public const MIME_TYPE = 'application/json';
|
||||
final public const SCHEMA = 'https://raw.githubusercontent.com/chillerlan/php-qrcode/main/src/Output/qrcode.schema.json';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getOutputDimensions():array{
|
||||
return [$this->moduleCount, $this->moduleCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function dump(string $file = null):string{
|
||||
$matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans);
|
||||
$data = json_encode($matrix, $this->options->jsonFlags);;
|
||||
[$width, $height] = $this->getOutputDimensions();
|
||||
$version = $this->matrix->getVersion();
|
||||
$dimension = $version->getDimension();
|
||||
|
||||
$json = [
|
||||
'$schema' => $this::SCHEMA,
|
||||
'qrcode' => [
|
||||
'version' => $version->getVersionNumber(),
|
||||
'eccLevel' => (string)$this->matrix->getEccLevel(),
|
||||
'matrix' => [
|
||||
'size' => $dimension,
|
||||
'quietzoneSize' => (int)(($this->moduleCount - $dimension) / 2),
|
||||
'maskPattern' => $this->matrix->getMaskPattern()->getPattern(),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'rows' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach($this->matrix->getMatrix() as $y => $row){
|
||||
$matrixRow = $this->row($y, $row);
|
||||
|
||||
if($matrixRow !== null){
|
||||
$json['qrcode']['matrix']['rows'][] = $matrixRow;
|
||||
}
|
||||
}
|
||||
|
||||
$data = json_encode($json, $this->options->jsonFlags);;
|
||||
|
||||
$this->saveToFile($data, $file);
|
||||
|
||||
@@ -35,33 +71,43 @@ class QRStringJSON extends QROutputAbstract{
|
||||
}
|
||||
|
||||
/**
|
||||
* unused - required by interface
|
||||
*
|
||||
* @inheritDoc
|
||||
* @codeCoverageIgnore
|
||||
* Creates an array element for a matrix row
|
||||
*/
|
||||
protected function prepareModuleValue(mixed $value):string{
|
||||
return '';
|
||||
protected function row(int $y, array $row):array|null{
|
||||
$matrixRow = ['y' => $y, 'modules' => []];
|
||||
|
||||
foreach($row as $x => $M_TYPE){
|
||||
$module = $this->module($x, $y, $M_TYPE);
|
||||
|
||||
if($module !== null){
|
||||
$matrixRow['modules'][] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($matrixRow['modules'])){
|
||||
return $matrixRow;
|
||||
}
|
||||
|
||||
// skip empty rows
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* unused - required by interface
|
||||
*
|
||||
* @inheritDoc
|
||||
* @codeCoverageIgnore
|
||||
* Creates an array element for a single module
|
||||
*/
|
||||
protected function getDefaultModuleValue(bool $isDark):string{
|
||||
return '';
|
||||
}
|
||||
protected function module(int $x, int $y, int $M_TYPE):array|null{
|
||||
$isDark = $this->matrix->isDark($M_TYPE);
|
||||
|
||||
/**
|
||||
* unused - required by interface
|
||||
*
|
||||
* @inheritDoc
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function moduleValueIsValid(mixed $value):bool{
|
||||
return true;
|
||||
if(!$this->drawLightModules && !$isDark){
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'x' => $x,
|
||||
'dark' => $isDark,
|
||||
'layer' => ($this::LAYERNAMES[$M_TYPE] ?? ''),
|
||||
'value' => $this->getModuleValue($M_TYPE),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "chillerlan php-qrcode schema",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"qrcode"
|
||||
],
|
||||
"properties": {
|
||||
"qrcode": {
|
||||
"$ref": "#/$defs/qrcode"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"qrcode": {
|
||||
"description": "QR Code root element",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"eccLevel",
|
||||
"matrix",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"description": "The QR Code version: [1...40]",
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 40
|
||||
},
|
||||
"eccLevel": {
|
||||
"description": "The ECC level: [L, M, Q, H]",
|
||||
"enum": [
|
||||
"L",
|
||||
"M",
|
||||
"Q",
|
||||
"H"
|
||||
]
|
||||
},
|
||||
"matrix": {
|
||||
"$ref": "#/$defs/matrix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"matrix": {
|
||||
"description": "The matrix holds the encoded data in a 2-dimensional array of modules.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"size",
|
||||
"quietzoneSize",
|
||||
"maskPattern",
|
||||
"width",
|
||||
"height"
|
||||
],
|
||||
"properties": {
|
||||
"size": {
|
||||
"description": "The side length of the QR symbol, excluding the quiet zone (version * 4 + 17). [21...177]",
|
||||
"type": "number",
|
||||
"minimum": 21,
|
||||
"maximum": 177
|
||||
},
|
||||
"quietzoneSize": {
|
||||
"description": "The size of the quiet zone (margin around the QR symbol).",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"maskPattern": {
|
||||
"description": "The detected mask pattern that was used to mask this matrix. [0...7].",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"width": {
|
||||
"description": "The total width of the matrix, including the quiet zone.",
|
||||
"type": "number",
|
||||
"minimum": 21
|
||||
},
|
||||
"height": {
|
||||
"description": "The total height of the matrix, including the quiet zone.",
|
||||
"type": "number",
|
||||
"minimum": 21
|
||||
},
|
||||
"rows": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/row"
|
||||
},
|
||||
"minItems": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"row": {
|
||||
"description": "A row holds an array of modules",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"y",
|
||||
"modules"
|
||||
],
|
||||
"properties": {
|
||||
"y": {
|
||||
"description": "The 'y' (vertical) coordinate of this row.",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"modules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/module"
|
||||
},
|
||||
"minItems": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"description": "Represents a single module (pixel) of a QR symbol.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dark",
|
||||
"layer",
|
||||
"value",
|
||||
"x"
|
||||
],
|
||||
"properties": {
|
||||
"dark": {
|
||||
"description": "Indicates whether this module is dark.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"layer": {
|
||||
"description": "The layer (functional pattern) this module belongs to.",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "The value for this module.",
|
||||
"type": "string"
|
||||
},
|
||||
"x": {
|
||||
"description": "The 'x' (horizontal) coordinate of this module.",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,8 @@ namespace chillerlan\QRCode;
|
||||
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
|
||||
use chillerlan\QRCode\Output\QRMarkupSVG;
|
||||
use function constant, in_array, is_string, max, min, sprintf, strtolower, strtoupper, trim;
|
||||
use const JSON_THROW_ON_ERROR, PHP_EOL;
|
||||
use const JSON_THROW_ON_ERROR, JSON_UNESCAPED_SLASHES, PHP_EOL;
|
||||
|
||||
|
||||
/**
|
||||
* The QRCode plug-in settings & setter functionality
|
||||
@@ -408,12 +409,7 @@ trait QROptionsTrait{
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
protected bool $jsonAsBooleans = false;
|
||||
protected int $jsonFlags = JSON_THROW_ON_ERROR|JSON_UNESCAPED_SLASHES;
|
||||
|
||||
/*
|
||||
* QRFpdf settings
|
||||
|
||||
@@ -14,25 +14,12 @@ use chillerlan\QRCode\QROptions;
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\Output\{QROutputInterface, QRStringJSON};
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use function extension_loaded;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
final class QRStringJSONTest extends QROutputTestAbstract{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setUp():void{
|
||||
// just in case someone's running this on some weird distro that's been compiled without ext-json
|
||||
if(!extension_loaded('json')){
|
||||
$this::markTestSkipped('ext-json not loaded');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
use CssColorModuleValueProviderTrait;
|
||||
|
||||
protected function getOutputInterface(
|
||||
SettingsContainerInterface|QROptions $options,
|
||||
@@ -41,17 +28,22 @@ final class QRStringJSONTest extends QROutputTestAbstract{
|
||||
return new QRStringJSON($options, $matrix);
|
||||
}
|
||||
|
||||
public static function moduleValueProvider():array{
|
||||
return [[null, false]];
|
||||
}
|
||||
|
||||
#[DataProvider('moduleValueProvider')]
|
||||
public function testValidateModuleValues(mixed $value, bool $expected):void{
|
||||
$this::markTestSkipped('N/A (JSON test)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function testSetModuleValues():void{
|
||||
$this::markTestSkipped('N/A (JSON test)');
|
||||
|
||||
$this->options->moduleValues = [
|
||||
// data
|
||||
QRMatrix::M_DATA_DARK => '#AAA',
|
||||
QRMatrix::M_DATA => '#BBB',
|
||||
];
|
||||
|
||||
$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
|
||||
$data = $this->outputInterface->dump();
|
||||
|
||||
$this::assertStringContainsString('"layer":"data-dark","value":"#AAA"', $data);
|
||||
$this::assertStringContainsString('"layer":"data","value":"#BBB"', $data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user