PHP Classes

File: src/Core/QRCodeGenerator.php

Recommend this page to a friend!
  Packages of Amirreza Ebrahimi   HeroQR Powerful PHP QR Code Library to generate PNG, SVG, PDF, Logos Ready to Use with Laravel   src/Core/QRCodeGenerator.php   Download  
File: src/Core/QRCodeGenerator.php
Role: Class source
Content type: text/plain
Description: Class source
Class: HeroQR Powerful PHP QR Code Library to generate PNG, SVG, PDF, Logos Ready to Use with Laravel
Generate QR code images in several formats
Author: By
Last change: Update of src/Core/QRCodeGenerator.php
Date: 6 months ago
Size: 18,039 bytes
 

Contents

Class file image Download
<?php namespace HeroQR\Core; use Endroid\QrCode\{Builder\Builder, Matrix\Matrix}; use Endroid\QrCode\{Color\ColorInterface, ErrorCorrectionLevel, Exception\ValidationException, RoundBlockSizeMode}; use Endroid\QrCode\Writer\{Result\ResultInterface, WriterInterface}; use HeroQR\{Contracts\QRCodeGeneratorInterface, DataTypes\DataType}; use HeroQR\Managers\{ColorManager, EncodingManager, LabelManager, LogoManager, OutputManager, WriterManager}; /** * Handles the generation of QR codes with customizable options. * * Provides functionality for generating QR codes in various formats * (PNG, SVG, PDF and More) with optional customization options such as shapes, * markers, and internal patterns. Ensures flexibility and ease of use * for both standard and advanced use cases. * * @package HeroQR/Core */ class QRCodeGenerator implements QrCodeGeneratorInterface { private ?ResultInterface $builder = null; private LabelManager $labelManager; private bool $roundBlockSizeModeExplicitly; /** * QRCodeGenerator constructor * Initializes the necessary services and sets default values for QR code properties * * @param string $outputFormat The output format (default is 'getDataUri') * @param int $size The size of the QR code (default is 800) * @param int $margin The margin around the QR code (default is 10) * @param LogoManager $logoManager The logo manager instance * @param ColorManager $colorManager The color manager instance * @param EncodingManager $encodingManager The encoding manager instance */ public function __construct( private int $size = 800, private int $margin = 10, private string $data = 'https://github.com/AmirezaEb/HeroQR', private readonly string $outputFormat = 'getDataUri', private readonly LogoManager $logoManager = new LogoManager(), private readonly ColorManager $colorManager = new ColorManager(), private readonly WriterManager $writerManager = new WriterManager(), private readonly OutputManager $outputManager = new OutputManager(), private readonly EncodingManager $encodingManager = new EncodingManager(), private RoundBlockSizeMode $roundBlockSizeMode = RoundBlockSizeMode::Margin, private ErrorCorrectionLevel $errorCorrectionLevel = ErrorCorrectionLevel::High ) { $this->labelManager = new LabelManager($this->colorManager); } /** * Magic method to return the QR code as a string, based on the specified output format * * @return string The generated QR code as a string */ public function __toString(): string { return $this->outputFormat === 'getDataUri' ? $this->getDataUri() : $this->getString(); } /** * Generates a QR code in the specified format with optional customization. * * @param string $format The desired format for the QR code ('gif', 'png', 'svg', 'webp', 'eps', 'pdf', 'binary'). * @param array $customs Optional customization parameters for the QR code. If provided, they must follow this structure: * - 'Shape' ('S1' to 'S4'): Defines the overall shape of the QR code. * - 'Marker' ('M1' to 'M4'): Specifies the corner marker styles. * - 'Cursor' ('C1' to 'C4'): Adjusts the internal patterns. * - Example: * - generate('png', [ * 'Shape' => 'S4', * 'Marker' => 'M4', * 'Cursor' => 'C4' * ]); * * @return self The current QRCodeGenerator instance for method chaining. * @throws \InvalidArgumentException|ValidationException If the specified format is not supported. */ public function generate(string $format = 'png', array $customs = []): self { $this->builder = (new Builder( writer: $this->validateWriter($format, $customs), writerOptions: [], validateResult: false, data: $this->getData(), encoding: $this->encodingManager->getEncoding(), errorCorrectionLevel: $this->getErrorCorrectionLevel(), size: $this->getSize(), margin: $this->getMargin(), roundBlockSizeMode: $this->getBlockSizeMode(), foregroundColor: $this->colorManager->getColor(), backgroundColor: $this->colorManager->getBackgroundColor(), labelText: $this->labelManager->getLabel(), labelFont: $this->labelManager->getLabelFont(), labelAlignment: $this->labelManager->getLabelAlign(), labelMargin: $this->labelManager->getLabelMargin(), labelTextColor: $this->labelManager->getLabelColor(), logoPath: $this->logoManager->getLogoPath(), logoResizeToWidth: $this->logoManager->getLogoSize(), logoResizeToHeight: $this->logoManager->getLogoSize(), logoPunchoutBackground: $this->logoManager->getLogoBackground(), ))->build(); return $this; } /** * Returns the QR code's matrix representation * The matrix is a grid of black and white cells representing the QR code * * @return Matrix The matrix representation of the QR code * @throws \RuntimeException If the QR code has not been generated yet */ public function getMatrix(): Matrix { $this->ensureBuilderExists(); return $this->outputManager->getMatrix($this->builder); } /** * Get the matrix as an array * * @return array The QR code matrix represented as a 2D array * @throws \RuntimeException If no QR code has been generated yet */ public function getMatrixAsArray(): array { $this->ensureBuilderExists(); return $this->outputManager->getMatrixAsArray($this->builder); } /** * Returns the QR code as a raw string * * @return string The raw string representation of the QR code * @throws \RuntimeException If the QR code has not been generated yet */ public function getString(): string { $this->ensureBuilderExists(); return $this->outputManager->getString($this->builder); } /** * Returns the QR code as a Base64-encoded data URI * * @return string The data URI representation of the QR code * @throws \RuntimeException If the QR code has not been generated yet */ public function getDataUri(): string { $this->ensureBuilderExists(); return $this->outputManager->getDataUri($this->builder); } /** * Save the generated QR code to a file * * @param string $path The path to save the QR code file * @return bool True if the file was saved successfully, false otherwise * @throws \InvalidArgumentException If the format is unsupported * @throws \RuntimeException If no QR code has been generated yet */ public function saveTo(string $path): bool { $this->ensureBuilderExists(); return $this->outputManager->saveTo($this->builder, $path); } /** * Set the data to be encoded in the QR code * * @param string $data The data to encode * @param DataType $type DataType auto validation (default = DataType::Text) * @return self */ public function setData(string $data, DataType $type = DataType::Text): self { $class = $type->value; if (!$class::validate($data)) { throw new \InvalidArgumentException("Invalid data for type: " . $class::getType()); } if (empty(trim($data))) { throw new \InvalidArgumentException('Data cannot be empty.'); } $this->data = $this->dataSanitizer($data, $type); return $this; } /** * Get the data value * * @return string|null The data if available, null otherwise */ public function getData(): ?string { return $this->data; } /** * Set the size of the QR code * * @param int $size The size of the QR code * @return self * @throws \InvalidArgumentException If the size is not a positive integer */ public function setSize(int $size): self { if ($size <= 0) { throw new \InvalidArgumentException('Size must be a positive integer.'); } $this->size = $size; return $this; } /** * Get the size value * * @return int The size value */ public function getSize(): int { return $this->size; } /** * Set the margin around the QR code * * @param int $margin The margin size * @return self * @throws \InvalidArgumentException If the margin is negative */ public function setMargin(int $margin): self { if ($margin < 0) { throw new \InvalidArgumentException('Margin cannot be negative.'); } $this->margin = $margin; return $this; } /** * Get the margin value * * @return int The margin value */ public function getMargin(): int { return $this->margin; } /** * Set the round block size mode * * The string can be either: * - The enum case name (e.g. "None", "Margin", "Enlarge", "Shrink") ? case-insensitive. * * @param string $mode The round block size mode as a string. * @return self * @throws \InvalidArgumentException If the given mode is invalid. */ public function setBlockSizeMode(string $mode): self { $mode = strtolower($mode); $validModes = array_merge( array_map('strtolower', array_column(RoundBlockSizeMode::cases(), 'name')), array_map('strtolower', array_column(RoundBlockSizeMode::cases(), 'value')) ); if (!in_array($mode, $validModes, true)) { throw new \InvalidArgumentException( 'Invalid round block size mode. Accepted values: ' . implode(', ', array_unique($validModes)) . '.' ); } foreach (RoundBlockSizeMode::cases() as $case) { if (strtolower($case->name) === $mode || strtolower($case->value) === $mode) { $this->roundBlockSizeMode = $case; break; } } return $this; } /** * Get the current round block size mode of the QR code. * * @return RoundBlockSizeMode The current block size mode enum instance. */ public function getBlockSizeMode(): RoundBlockSizeMode { return $this->roundBlockSizeMode; } /** * Set the error correction level for the QR code * * The string can be either: * - The enum case name (e.g. "Low", "Medium", "Quartile", "High") ? case-insensitive. * * @param string $level The error correction level as a string. * @return self * @throws \InvalidArgumentException If the given level is invalid. Accepted values: low, medium, quartile, high. */ public function setErrorCorrectionLevel(string $level): self { $level = strtolower($level); $validLevels = array_merge( array_map('strtolower', array_column(ErrorCorrectionLevel::cases(), 'name')), array_map('strtolower', array_column(ErrorCorrectionLevel::cases(), 'value')) ); if (!in_array($level, $validLevels, true)) { throw new \InvalidArgumentException( 'Invalid error correction level. Accepted values: ' . implode(', ', array_unique($validLevels)) . '.' ); } foreach (ErrorCorrectionLevel::cases() as $case) { if (strtolower($case->name) === $level || strtolower($case->value) === $level) { $this->errorCorrectionLevel = $case; break; } } return $this; } /** * Get the current error correction level of the QR code * * @return ErrorCorrectionLevel The current error correction level enum instance. */ public function getErrorCorrectionLevel(): ErrorCorrectionLevel { return $this->errorCorrectionLevel; } /** * Set the color of the QR code foreground * * @param string $hexColor The hexadecimal color code * @return self * @throws \InvalidArgumentException If the color format is invalid */ public function setColor(string $hexColor): self { $this->colorManager->setColor($hexColor); return $this; } /** * Get the color from the color manager * * @return ColorInterface The color object */ public function getColor(): ColorInterface { return $this->colorManager->getColor(); } /** * Set the background color of the QR code * * @param string $hexColor The hexadecimal color code * @return self * @throws \InvalidArgumentException If the color format is invalid */ public function setBackgroundColor(string $hexColor): self { $this->colorManager->setBackgroundColor($hexColor); return $this; } /** * Get the background color from the color manager * * @return ColorInterface The background color object */ public function getBackgroundColor(): ColorInterface { return $this->colorManager->getBackgroundColor(); } /** * Set the logo to be embedded in the QR code * * @param string $logoPath The path to the logo file * @param int $logoSize The size of the logo (default is 80) * @return self * @throws \InvalidArgumentException If the logo file does not exist */ public function setLogo(string $logoPath, int $logoSize = 80): self { if (!file_exists($logoPath)) { throw new \InvalidArgumentException('Logo File Does Not Exist'); } $this->logoManager->setLogo($logoPath); $this->logoManager->setLogoSize($logoSize); return $this; } /** * Get the logo path from the logo manager * * @return string|null The logo path if available, null otherwise */ public function getLogoPath(): ?string { return $this->logoManager->getLogoPath(); } /** * Set the label properties for the QR code * * @param string $label The text label to be displayed on the QR code * @param string $textAlign The text alignment for the label (default is 'center') * @param string $textColor The color of the label text in hexadecimal format (default is '#000000') * @param int $fontSize The font size of the label text (default is 50) * @param array $margin The margin around the label in the format [top, right, bottom, left] (default is [0, 10, 10, 10]) * @return self Returns the current instance for method chaining * @throws \InvalidArgumentException If the label text is empty */ public function setLabel( string $label, string $textAlign = 'center', string $textColor = '#000000', int $fontSize = 50, array $margin = [0, 10, 10, 10] ): self { if (empty($label)) { throw new \InvalidArgumentException('Label cannot be empty'); } $this->labelManager->setLabel($label); $this->labelManager->setLabelAlign($textAlign); $this->labelManager->setLabelColor($textColor); $this->labelManager->setLabelSize($fontSize); $this->labelManager->setLabelMargin($margin); return $this; } /** * Get the label from the label manager * * @return string|null The label if available, null otherwise */ public function getLabel(): ?string { return $this->labelManager->getLabel(); } /** * Set the encoding type for the QR code * * @param string $encoding The encoding type ('UTF-16' ,'UTF-8', 'ASCII', 'ISO-8859-1', 'ISO-8859-5', 'ISO-8859-15') and more... * @return self Returns the current instance for method chaining * @throws \Exception */ public function setEncoding(string $encoding): self { $this->encodingManager->setEncoding($encoding); return $this; } /** * Encodes the data with type-specific rules and sanitizes the input * Supports data types like Email, Phone, and Location * * @param string $data The raw data to encode * @param DataType $type The type of data being encoded (Url, WiFi, Location, Text, Email, Phone) * @return string Sanitized and properly formatted data string */ private function dataSanitizer(string $data, DataType $type): string { $data = htmlspecialchars($data); return match ($type) { DataType::Email => "mailto:{$data}", DataType::Phone => "tel:{$data}", DataType::Wifi => "$data", DataType::Location => "https://www.google.com/maps?q=$data", default => $data, }; } /** * Validates the writer format and returns an appropriate WriterInterface instance * Supports both standard and custom writer formats * * @param string $format The format of the writer ("svg", "png", "pdf",and more...) * @param array $customs An optional array of custom values for M Or C Or S * @return WriterInterface * @throws \InvalidArgumentException|\RuntimeException */ private function validateWriter(string $format, array $customs): WriterInterface { return $this->writerManager->getWriter($format, $customs); } /** * Helper method to ensure that the builder has been initialized * * @throws \RuntimeException If the builder has not been initialized */ private function ensureBuilderExists(): void { if ($this->builder === null) { throw new \Error('No QR Code has been generated. Call generate() first.'); } } }