| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- <?php
- /**
- * Class QRCode
- *
- * @filesource QRCode.php
- * @created 26.11.2015
- * @package chillerlan\QRCode
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2015 Smiley
- * @license MIT
- */
- namespace chillerlan\QRCode;
- use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Kanji, MaskPatternTester, Number, QRData, QRCodeDataException, QRMatrix};
- use chillerlan\QRCode\Common\Mode;
- use chillerlan\QRCode\Output\{
- QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
- };
- use chillerlan\Settings\SettingsContainerInterface;
- use function class_exists, in_array;
- /**
- * Turns a text string into a Model 2 QR Code
- *
- * @see https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
- * @see http://www.qrcode.com/en/codes/model12.html
- * @see https://www.swisseduc.ch/informatik/theoretische_informatik/qr_codes/docs/qr_standard.pdf
- * @see https://en.wikipedia.org/wiki/QR_code
- * @see http://www.thonky.com/qr-code-tutorial/
- */
- class QRCode{
- /** @var int */
- public const VERSION_AUTO = -1;
- /** @var int */
- public const MASK_PATTERN_AUTO = -1;
- // ISO/IEC 18004:2000 Tables 12, 25
- /** @var int */
- public const ECC_L = 0b01; // 7%.
- /** @var int */
- public const ECC_M = 0b00; // 15%.
- /** @var int */
- public const ECC_Q = 0b11; // 25%.
- /** @var int */
- public const ECC_H = 0b10; // 30%.
- /**
- * References to the keys of the following tables:
- *
- * @see \chillerlan\QRCode\Common\Version::MAX_BITS
- * @see \chillerlan\QRCode\Data\QRData::RSBLOCKS
- * @see \chillerlan\QRCode\Data\QRMatrix::formatPattern
- *
- * @var int[]
- */
- public const ECC_MODES = [
- self::ECC_L => 0,
- self::ECC_M => 1,
- self::ECC_Q => 2,
- self::ECC_H => 3,
- ];
- /** @var string */
- public const OUTPUT_MARKUP_HTML = 'html';
- /** @var string */
- public const OUTPUT_MARKUP_SVG = 'svg';
- /** @var string */
- public const OUTPUT_IMAGE_PNG = 'png';
- /** @var string */
- public const OUTPUT_IMAGE_JPG = 'jpg';
- /** @var string */
- public const OUTPUT_IMAGE_GIF = 'gif';
- /** @var string */
- public const OUTPUT_STRING_JSON = 'json';
- /** @var string */
- public const OUTPUT_STRING_TEXT = 'text';
- /** @var string */
- public const OUTPUT_IMAGICK = 'imagick';
- /** @var string */
- public const OUTPUT_FPDF = 'fpdf';
- /** @var string */
- public const OUTPUT_CUSTOM = 'custom';
- /**
- * Map of built-in output modules => capabilities
- *
- * @var string[][]
- */
- public const OUTPUT_MODES = [
- QRMarkup::class => [
- self::OUTPUT_MARKUP_SVG,
- self::OUTPUT_MARKUP_HTML,
- ],
- QRImage::class => [
- self::OUTPUT_IMAGE_PNG,
- self::OUTPUT_IMAGE_GIF,
- self::OUTPUT_IMAGE_JPG,
- ],
- QRString::class => [
- self::OUTPUT_STRING_JSON,
- self::OUTPUT_STRING_TEXT,
- ],
- QRImagick::class => [
- self::OUTPUT_IMAGICK,
- ],
- QRFpdf::class => [
- self::OUTPUT_FPDF,
- ],
- ];
- /**
- * A collection of one or more data segments of [classname, data] to write
- *
- * @see \chillerlan\QRCode\Data\QRDataModeInterface
- *
- * @var string[][]|int[][]
- */
- protected array $dataSegments = [];
- /**
- * The settings container
- *
- * @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
- */
- protected SettingsContainerInterface $options;
- /**
- * The selected data interface (Number, AlphaNum, Kanji, Byte)
- */
- protected QRData $dataInterface;
- /**
- * QRCode constructor.
- *
- * Sets the options instance, determines the current mb-encoding and sets it to UTF-8
- */
- public function __construct(SettingsContainerInterface $options = null){
- $this->options = $options ?? new QROptions;
- }
- /**
- * Renders a QR Code for the given $data and QROptions
- *
- * @return mixed
- */
- public function render(string $data = null, string $file = null){
- if($data !== null){
- /** @var \chillerlan\QRCode\Data\QRDataModeInterface $dataInterface */
- foreach(Mode::DATA_INTERFACES as $dataInterface){
- if($dataInterface::validateString($data)){
- $this->addSegment($data, $dataInterface);
- break;
- }
- }
- }
- return $this->initOutputInterface()->dump($file);
- }
- /**
- * Returns a QRMatrix object for the given $data and current QROptions
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
- */
- public function getMatrix():QRMatrix{
- if(empty($this->dataSegments)){
- throw new QRCodeDataException('QRCode::getMatrix() No data given.');
- }
- $this->dataInterface = new QRData($this->options, $this->dataSegments);
- $maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO
- ? (new MaskPatternTester($this->dataInterface))->getBestMaskPattern()
- : $this->options->maskPattern;
- $matrix = $this->dataInterface->initMatrix($maskPattern);
- if((bool)$this->options->addQuietzone){
- $matrix->setQuietZone($this->options->quietzoneSize);
- }
- return $matrix;
- }
- /**
- * returns a fresh (built-in) QROutputInterface
- *
- * @throws \chillerlan\QRCode\Output\QRCodeOutputException
- */
- protected function initOutputInterface():QROutputInterface{
- if($this->options->outputType === $this::OUTPUT_CUSTOM && class_exists($this->options->outputInterface)){
- return new $this->options->outputInterface($this->options, $this->getMatrix());
- }
- foreach($this::OUTPUT_MODES as $outputInterface => $modes){
- if(in_array($this->options->outputType, $modes, true) && class_exists($outputInterface)){
- return new $outputInterface($this->options, $this->getMatrix());
- }
- }
- throw new QRCodeOutputException('invalid output type');
- }
- /**
- * checks if a string qualifies as numeric (convenience method)
- *
- * @see Number::validateString()
- */
- public function isNumber(string $string):bool{
- return Number::validateString($string);
- }
- /**
- * checks if a string qualifies as alphanumeric (convenience method)
- *
- * @see AlphaNum::validateString()
- */
- public function isAlphaNum(string $string):bool{
- return AlphaNum::validateString($string);
- }
- /**
- * checks if a string qualifies as Kanji (convenience method)
- *
- * @see Kanji::validateString()
- */
- public function isKanji(string $string):bool{
- return Kanji::validateString($string);
- }
- /**
- * a dummy (convenience method)
- *
- * @see Byte::validateString()
- */
- public function isByte(string $string):bool{
- return Byte::validateString($string);
- }
- /**
- * @param string|int $data
- * @param string $classname
- *
- * @return void
- */
- protected function addSegment($data, string $classname):void{
- $this->dataSegments[] = [$classname, $data];
- }
- public function addNumberSegment(string $data):QRCode{
- $this->addSegment($data, Number::class);
- return $this;
- }
- public function addAlphaNumSegment(string $data):QRCode{
- $this->addSegment($data, AlphaNum::class);
- return $this;
- }
- public function addKanjiSegment(string $data):QRCode{
- $this->addSegment($data, Kanji::class);
- return $this;
- }
- public function addByteSegment(string $data):QRCode{
- $this->addSegment($data, Byte::class);
- return $this;
- }
- public function addEciDesignator(int $encoding):QRCode{
- $this->addSegment($encoding, ECI::class);
- return $this;
- }
- }
|