diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php new file mode 100644 index 00000000..bcde20c6 --- /dev/null +++ b/module/Core/src/Action/Model/QrCodeParams.php @@ -0,0 +1,113 @@ +getQueryParams(); + + return new self( + self::resolveSize($request, $query), + self::resolveMargin($query), + self::resolveWriter($query), + self::resolveErrorCorrection($query), + ); + } + + private static function resolveSize(Request $request, array $query): int + { + // FIXME Size attribute is deprecated. After v3.0.0, always use the query param instead + $size = (int) $request->getAttribute('size', $query['size'] ?? self::DEFAULT_SIZE); + if ($size < self::MIN_SIZE) { + return self::MIN_SIZE; + } + + return $size > self::MAX_SIZE ? self::MAX_SIZE : $size; + } + + private static function resolveMargin(array $query): int + { + $margin = $query['margin'] ?? null; + if ($margin === null) { + return 0; + } + + $intMargin = (int) $margin; + if ($margin !== (string) $intMargin) { + return 0; + } + + return $intMargin < 0 ? 0 : $intMargin; + } + + private static function resolveWriter(array $query): WriterInterface + { + $format = strtolower(trim($query['format'] ?? 'png')); + return match ($format) { + 'svg' => new SvgWriter(), + default => new PngWriter(), + }; + } + + private static function resolveErrorCorrection(array $query): ErrorCorrectionLevelInterface + { + $errorCorrectionLevel = strtoupper(trim($query['errorCorrection'] ?? '')); + return match ($errorCorrectionLevel) { + 'H' => new ErrorCorrectionLevelHigh(), + 'Q' => new ErrorCorrectionLevelQuartile(), + 'M' => new ErrorCorrectionLevelMedium(), + default => new ErrorCorrectionLevelLow(), // 'L' + }; + } + + public function size(): int + { + return $this->size; + } + + public function margin(): int + { + return $this->margin; + } + + public function writer(): WriterInterface + { + return $this->writer; + } + + public function errorCorrectionLevel(): ErrorCorrectionLevelInterface + { + return $this->errorCorrectionLevel; + } +} diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index a8dee3fd..2f816c98 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -5,41 +5,25 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Action; use Endroid\QrCode\Builder\Builder; -use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh; -use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelInterface; -use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow; -use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium; -use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile; -use Endroid\QrCode\Writer\SvgWriter; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; +use Shlinkio\Shlink\Core\Action\Model\QrCodeParams; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; -use function strtoupper; -use function trim; - class QrCodeAction implements MiddlewareInterface { - private const DEFAULT_SIZE = 300; - private const MIN_SIZE = 50; - private const MAX_SIZE = 1000; - - private LoggerInterface $logger; - public function __construct( private ShortUrlResolverInterface $urlResolver, private ShortUrlStringifierInterface $stringifier, - ?LoggerInterface $logger = null + private LoggerInterface $logger ) { - $this->logger = $logger ?? new NullLogger(); } public function process(Request $request, RequestHandlerInterface $handler): Response @@ -53,55 +37,14 @@ class QrCodeAction implements MiddlewareInterface return $handler->handle($request); } - $query = $request->getQueryParams(); + $params = QrCodeParams::fromRequest($request); $qrCodeBuilder = Builder::create() ->data($this->stringifier->stringify($shortUrl)) - ->size($this->resolveSize($request, $query)) - ->margin($this->resolveMargin($query)) - ->errorCorrectionLevel($this->resolveErrorCorrection($query)); - - $format = $query['format'] ?? 'png'; - if ($format === 'svg') { - $qrCodeBuilder->writer(new SvgWriter()); - } + ->size($params->size()) + ->margin($params->margin()) + ->writer($params->writer()) + ->errorCorrectionLevel($params->errorCorrectionLevel()); return new QrCodeResponse($qrCodeBuilder->build()); } - - private function resolveSize(Request $request, array $query): int - { - // Size attribute is deprecated. After v3.0.0, always use the query param instead - $size = (int) $request->getAttribute('size', $query['size'] ?? self::DEFAULT_SIZE); - if ($size < self::MIN_SIZE) { - return self::MIN_SIZE; - } - - return $size > self::MAX_SIZE ? self::MAX_SIZE : $size; - } - - private function resolveMargin(array $query): int - { - $margin = $query['margin'] ?? null; - if ($margin === null) { - return 0; - } - - $intMargin = (int) $margin; - if ($margin !== (string) $intMargin) { - return 0; - } - - return $intMargin < 0 ? 0 : $intMargin; - } - - private function resolveErrorCorrection(array $query): ErrorCorrectionLevelInterface - { - $errorCorrectionLevel = strtoupper(trim($query['errorCorrection'] ?? '')); - return match ($errorCorrectionLevel) { - 'H' => new ErrorCorrectionLevelHigh(), - 'Q' => new ErrorCorrectionLevelQuartile(), - 'M' => new ErrorCorrectionLevelMedium(), - default => new ErrorCorrectionLevelLow(), // 'L' - }; - } } diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index 7326c41c..0595734e 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -14,6 +14,7 @@ use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\QrCodeAction; use Shlinkio\Shlink\Core\Entity\ShortUrl; @@ -41,6 +42,7 @@ class QrCodeActionTest extends TestCase $this->action = new QrCodeAction( $this->urlResolver->reveal(), new ShortUrlStringifier(['domain' => 'doma.in']), + new NullLogger(), ); }