PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.7
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.7
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / chillerlan / php-qrcode / src / QRCode.php

QRCode.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.7, at vendor_prefixed/chillerlan/php-qrcode/src/QRCode.php

265 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class QRCode
5 *
6 * @filesource QRCode.php
7 * @created 26.11.2015
8 * @package chillerlan\QRCode
9 * @author Smiley <[email protected]>
10 * @copyright 2015 Smiley
11 * @license MIT
12 */
13 namespace YoastSEO_Vendor\chillerlan\QRCode;
14
15 use YoastSEO_Vendor\chillerlan\QRCode\Data\AlphaNum;
16 use YoastSEO_Vendor\chillerlan\QRCode\Data\Byte;
17 use YoastSEO_Vendor\chillerlan\QRCode\Data\Kanji;
18 use YoastSEO_Vendor\chillerlan\QRCode\Data\MaskPatternTester;
19 use YoastSEO_Vendor\chillerlan\QRCode\Data\Number;
20 use YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException;
21 use YoastSEO_Vendor\chillerlan\QRCode\Data\QRDataInterface;
22 use YoastSEO_Vendor\chillerlan\QRCode\Output\QRCodeOutputException;
23 use YoastSEO_Vendor\chillerlan\QRCode\Output\QRImage;
24 use YoastSEO_Vendor\chillerlan\QRCode\Output\QRMarkup;
25 use YoastSEO_Vendor\chillerlan\QRCode\Output\QROutputInterface;
26 use YoastSEO_Vendor\chillerlan\QRCode\Output\QRString;
27 use YoastSEO_Vendor\chillerlan\QRCode\Traits\ClassLoader;
28 /**
29 * Turns a text string into a Model 2 QR Code
30 *
31 * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
32 * @link http://www.qrcode.com/en/codes/model12.html
33 * @link http://www.thonky.com/qr-code-tutorial/
34 */
35 class QRCode
36 {
37 use ClassLoader;
38 /**
39 * API constants
40 */
41 const OUTPUT_MARKUP_HTML = 'html';
42 const OUTPUT_MARKUP_SVG = 'svg';
43 # const OUTPUT_MARKUP_XML = 'xml'; // anyone?
44 const OUTPUT_IMAGE_PNG = 'png';
45 const OUTPUT_IMAGE_JPG = 'jpg';
46 const OUTPUT_IMAGE_GIF = 'gif';
47 const OUTPUT_STRING_JSON = 'json';
48 const OUTPUT_STRING_TEXT = 'text';
49 const OUTPUT_CUSTOM = 'custom';
50 const VERSION_AUTO = -1;
51 const MASK_PATTERN_AUTO = -1;
52 const ECC_L = 0b1;
53 // 7%.
54 const ECC_M = 0b0;
55 // 15%.
56 const ECC_Q = 0b11;
57 // 25%.
58 const ECC_H = 0b10;
59 // 30%.
60 const DATA_NUMBER = 0b1;
61 const DATA_ALPHANUM = 0b10;
62 const DATA_BYTE = 0b100;
63 const DATA_KANJI = 0b1000;
64 const ECC_MODES = [self::ECC_L => 0, self::ECC_M => 1, self::ECC_Q => 2, self::ECC_H => 3];
65 const DATA_MODES = [self::DATA_NUMBER => 0, self::DATA_ALPHANUM => 1, self::DATA_BYTE => 2, self::DATA_KANJI => 3];
66 const OUTPUT_MODES = [\YoastSEO_Vendor\chillerlan\QRCode\Output\QRMarkup::class => [self::OUTPUT_MARKUP_SVG, self::OUTPUT_MARKUP_HTML], \YoastSEO_Vendor\chillerlan\QRCode\Output\QRImage::class => [self::OUTPUT_IMAGE_PNG, self::OUTPUT_IMAGE_GIF, self::OUTPUT_IMAGE_JPG], \YoastSEO_Vendor\chillerlan\QRCode\Output\QRString::class => [self::OUTPUT_STRING_JSON, self::OUTPUT_STRING_TEXT]];
67 /**
68 * @var \chillerlan\QRCode\QROptions
69 */
70 protected $options;
71 /**
72 * @var \chillerlan\QRCode\Data\QRDataInterface
73 */
74 protected $dataInterface;
75 /**
76 * QRCode constructor.
77 *
78 * @param \chillerlan\QRCode\QROptions|null $options
79 */
80 public function __construct(\YoastSEO_Vendor\chillerlan\QRCode\QROptions $options = null)
81 {
82 \mb_internal_encoding('UTF-8');
83 $this->setOptions($options instanceof \YoastSEO_Vendor\chillerlan\QRCode\QROptions ? $options : new \YoastSEO_Vendor\chillerlan\QRCode\QROptions());
84 }
85 /**
86 * Sets the options, called internally by the constructor
87 *
88 * @param \chillerlan\QRCode\QROptions $options
89 *
90 * @return \chillerlan\QRCode\QRCode
91 * @throws \chillerlan\QRCode\QRCodeException
92 */
93 public function setOptions(\YoastSEO_Vendor\chillerlan\QRCode\QROptions $options)
94 {
95 if (!\array_key_exists($options->eccLevel, $this::ECC_MODES)) {
96 throw new \YoastSEO_Vendor\chillerlan\QRCode\QRCodeException('Invalid error correct level: ' . $options->eccLevel);
97 }
98 if (!\is_array($options->imageTransparencyBG) || \count($options->imageTransparencyBG) < 3) {
99 $options->imageTransparencyBG = [255, 255, 255];
100 }
101 $options->version = (int) $options->version;
102 // clamp min/max version number
103 $options->versionMin = (int) \min($options->versionMin, $options->versionMax);
104 $options->versionMax = (int) \max($options->versionMin, $options->versionMax);
105 $this->options = $options;
106 return $this;
107 }
108 /**
109 * Renders a QR Code for the given $data and QROptions
110 *
111 * @param string $data
112 *
113 * @return mixed
114 */
115 public function render($data)
116 {
117 return $this->initOutputInterface($data)->dump();
118 }
119 /**
120 * Returns a QRMatrix object for the given $data and current QROptions
121 *
122 * @param string $data
123 *
124 * @return \chillerlan\QRCode\Data\QRMatrix
125 * @throws \chillerlan\QRCode\Data\QRCodeDataException
126 */
127 public function getMatrix($data)
128 {
129 $data = \trim($data);
130 if (empty($data)) {
131 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('QRCode::getMatrix() No data given.');
132 }
133 $this->dataInterface = $this->initDataInterface($data);
134 $maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO ? $this->getBestMaskPattern() : \max(7, \min(0, (int) $this->options->maskPattern));
135 $matrix = $this->dataInterface->initMatrix($maskPattern);
136 if ((bool) $this->options->addQuietzone) {
137 $matrix->setQuietZone($this->options->quietzoneSize);
138 }
139 return $matrix;
140 }
141 /**
142 * shoves a QRMatrix through the MaskPatternTester to find the lowest penalty mask pattern
143 *
144 * @see \chillerlan\QRCode\Data\MaskPatternTester
145 *
146 * @return int
147 */
148 protected function getBestMaskPattern()
149 {
150 $penalties = [];
151 $tester = new \YoastSEO_Vendor\chillerlan\QRCode\Data\MaskPatternTester();
152 for ($testPattern = 0; $testPattern < 8; $testPattern++) {
153 $matrix = $this->dataInterface->initMatrix($testPattern, \true);
154 $tester->setMatrix($matrix);
155 $penalties[$testPattern] = $tester->testPattern();
156 }
157 return \array_search(\min($penalties), $penalties, \true);
158 }
159 /**
160 * returns a fresh QRDataInterface for the given $data
161 *
162 * @param string $data
163 *
164 * @return \chillerlan\QRCode\Data\QRDataInterface
165 * @throws \chillerlan\QRCode\Data\QRCodeDataException
166 */
167 public function initDataInterface($data)
168 {
169 $DATA_MODES = [\YoastSEO_Vendor\chillerlan\QRCode\Data\Number::class => 'Number', \YoastSEO_Vendor\chillerlan\QRCode\Data\AlphaNum::class => 'AlphaNum', \YoastSEO_Vendor\chillerlan\QRCode\Data\Kanji::class => 'Kanji', \YoastSEO_Vendor\chillerlan\QRCode\Data\Byte::class => 'Byte'];
170 foreach ($DATA_MODES as $dataInterface => $mode) {
171 if (\call_user_func_array([$this, 'is' . $mode], [$data]) === \true) {
172 return $this->loadClass($dataInterface, \YoastSEO_Vendor\chillerlan\QRCode\Data\QRDataInterface::class, $this->options, $data);
173 }
174 }
175 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('invalid data type');
176 // @codeCoverageIgnore
177 }
178 /**
179 * returns a fresh (built-in) QROutputInterface
180 *
181 * @param string $data
182 *
183 * @return \chillerlan\QRCode\Output\QROutputInterface
184 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
185 */
186 protected function initOutputInterface($data)
187 {
188 if ($this->options->outputType === $this::OUTPUT_CUSTOM && $this->options->outputInterface !== null) {
189 return $this->loadClass($this->options->outputInterface, \YoastSEO_Vendor\chillerlan\QRCode\Output\QROutputInterface::class, $this->options, $this->getMatrix($data));
190 }
191 foreach ($this::OUTPUT_MODES as $outputInterface => $modes) {
192 if (\in_array($this->options->outputType, $modes, \true)) {
193 return $this->loadClass($outputInterface, \YoastSEO_Vendor\chillerlan\QRCode\Output\QROutputInterface::class, $this->options, $this->getMatrix($data));
194 }
195 }
196 throw new \YoastSEO_Vendor\chillerlan\QRCode\Output\QRCodeOutputException('invalid output type');
197 }
198 /**
199 * checks if a string qualifies as numeric
200 *
201 * @param string $string
202 *
203 * @return bool
204 */
205 public function isNumber($string)
206 {
207 $len = \strlen($string);
208 $map = \str_split('0123456789');
209 for ($i = 0; $i < $len; $i++) {
210 if (!\in_array($string[$i], $map, \true)) {
211 return \false;
212 }
213 }
214 return \true;
215 }
216 /**
217 * checks if a string qualifies as alphanumeric
218 *
219 * @param string $string
220 *
221 * @return bool
222 */
223 public function isAlphaNum($string)
224 {
225 $len = \strlen($string);
226 for ($i = 0; $i < $len; $i++) {
227 if (!\in_array($string[$i], \YoastSEO_Vendor\chillerlan\QRCode\Data\AlphaNum::CHAR_MAP, \true)) {
228 return \false;
229 }
230 }
231 return \true;
232 }
233 /**
234 * checks if a string qualifies as Kanji
235 *
236 * @param string $string
237 *
238 * @return bool
239 */
240 public function isKanji($string)
241 {
242 $i = 0;
243 $len = \strlen($string);
244 while ($i + 1 < $len) {
245 $c = (0xff & \ord($string[$i])) << 8 | 0xff & \ord($string[$i + 1]);
246 if (!($c >= 0x8140 && $c <= 0x9ffc) && !($c >= 0xe040 && $c <= 0xebbf)) {
247 return \false;
248 }
249 $i += 2;
250 }
251 return !($i < $len);
252 }
253 /**
254 * a dummy
255 *
256 * @param $data
257 *
258 * @return bool
259 */
260 protected function isByte($data)
261 {
262 return !empty($data);
263 }
264 }
265