Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
186
src/Framework/QrCode/QrCodeGenerator.php
Normal file
186
src/Framework/QrCode/QrCodeGenerator.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\QrCode;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* QR Code Generator
|
||||
*
|
||||
* Main facade for generating QR codes from data.
|
||||
* Orchestrates encoding, matrix generation, and rendering.
|
||||
*/
|
||||
final readonly class QrCodeGenerator
|
||||
{
|
||||
public function __construct(
|
||||
private DataEncoder $dataEncoder,
|
||||
private MatrixGenerator $matrixGenerator,
|
||||
private SvgRenderer $svgRenderer
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QR code generator with default components
|
||||
*/
|
||||
public static function create(): self
|
||||
{
|
||||
return new self(
|
||||
dataEncoder: new DataEncoder(),
|
||||
matrixGenerator: new MatrixGenerator(),
|
||||
svgRenderer: new SvgRenderer()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create QR code generator for TOTP URLs
|
||||
*/
|
||||
public static function forTotp(): self
|
||||
{
|
||||
return new self(
|
||||
dataEncoder: new DataEncoder(),
|
||||
matrixGenerator: new MatrixGenerator(),
|
||||
svgRenderer: SvgRenderer::withModuleSize(4)->withQuietZone(2)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code as SVG string
|
||||
*/
|
||||
public function generateSvg(
|
||||
string $data,
|
||||
?ErrorCorrectionLevel $errorLevel = null,
|
||||
?QrCodeVersion $version = null
|
||||
): string {
|
||||
$matrix = $this->generateMatrix($data, $errorLevel, $version);
|
||||
|
||||
return $this->svgRenderer->render($matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code as SVG data URI
|
||||
*/
|
||||
public function generateDataUri(
|
||||
string $data,
|
||||
?ErrorCorrectionLevel $errorLevel = null,
|
||||
?QrCodeVersion $version = null
|
||||
): string {
|
||||
$matrix = $this->generateMatrix($data, $errorLevel, $version);
|
||||
|
||||
return $this->svgRenderer->renderAsDataUri($matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code matrix
|
||||
*/
|
||||
public function generateMatrix(
|
||||
string $data,
|
||||
?ErrorCorrectionLevel $errorLevel = null,
|
||||
?QrCodeVersion $version = null
|
||||
): QrCodeMatrix {
|
||||
if (empty($data)) {
|
||||
throw new InvalidArgumentException('QR code data cannot be empty');
|
||||
}
|
||||
|
||||
$errorLevel = $errorLevel ?? ErrorCorrectionLevel::forTotp();
|
||||
$version = $version ?? $this->determineOptimalVersion($data, $errorLevel);
|
||||
|
||||
// Encode data into bit stream
|
||||
$encodedData = $this->dataEncoder->encode($data, $version, $errorLevel);
|
||||
|
||||
// Generate matrix from encoded data
|
||||
return $this->matrixGenerator->generateMatrix($encodedData, $version, $errorLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create generator with custom renderer
|
||||
*/
|
||||
public function withRenderer(SvgRenderer $renderer): self
|
||||
{
|
||||
return new self(
|
||||
dataEncoder: $this->dataEncoder,
|
||||
matrixGenerator: $this->matrixGenerator,
|
||||
svgRenderer: $renderer
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recommended configuration for data
|
||||
*/
|
||||
public function analyzeData(string $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
throw new InvalidArgumentException('Cannot analyze empty data');
|
||||
}
|
||||
|
||||
$mode = DataMode::detectForData($data);
|
||||
$errorLevel = ErrorCorrectionLevel::forTotp();
|
||||
$version = $this->determineOptimalVersion($data, $errorLevel);
|
||||
|
||||
return [
|
||||
'data_length' => strlen($data),
|
||||
'detected_mode' => $mode,
|
||||
'recommended_error_level' => $errorLevel,
|
||||
'optimal_version' => $version->getVersion(),
|
||||
'matrix_size' => $version->getModuleCount(),
|
||||
'data_capacity' => $version->getDataCapacity($errorLevel),
|
||||
'efficiency' => $mode->getEfficiency(),
|
||||
'capacity_used' => round((strlen($data) / $version->getDataCapacity($errorLevel)) * 100, 1),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code for TOTP URI
|
||||
*/
|
||||
public function generateTotpQrCode(string $totpUri): string
|
||||
{
|
||||
// Validate TOTP URI format
|
||||
if (! str_starts_with($totpUri, 'otpauth://totp/')) {
|
||||
throw new InvalidArgumentException('Invalid TOTP URI format');
|
||||
}
|
||||
|
||||
// Use appropriate settings for TOTP (allow larger versions)
|
||||
$errorLevel = ErrorCorrectionLevel::M; // Medium error correction for TOTP
|
||||
|
||||
return $this->generateSvg($totpUri, $errorLevel, null); // Auto-detect version
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the optimal QR code version for given data
|
||||
*/
|
||||
private function determineOptimalVersion(string $data, ErrorCorrectionLevel $errorLevel): QrCodeVersion
|
||||
{
|
||||
$dataLength = strlen($data);
|
||||
|
||||
// Try to find the smallest version that can fit the data
|
||||
return QrCodeVersion::forDataLength($dataLength, $errorLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generator configuration
|
||||
*/
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
return [
|
||||
'encoder' => DataEncoder::class,
|
||||
'matrix_generator' => MatrixGenerator::class,
|
||||
'renderer' => $this->svgRenderer->getConfiguration(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate data before encoding
|
||||
*/
|
||||
private function validateData(string $data): void
|
||||
{
|
||||
if (strlen($data) > 2953) { // Max capacity for version 40, error level L
|
||||
throw new InvalidArgumentException('Data too large for QR code: ' . strlen($data) . ' bytes');
|
||||
}
|
||||
|
||||
// Check for null bytes or other problematic characters
|
||||
if (strpos($data, "\0") !== false) {
|
||||
throw new InvalidArgumentException('Data contains null bytes');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user