Completed
Push — master ( 16e889...fa12b4 )
by smiley
04:22
created

QRCode::getMatrix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
/**
3
 * Class QRCode
4
 *
5
 * @filesource   QRCode.php
6
 * @created      26.11.2015
7
 * @package      chillerlan\QRCode
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode;
14
15
use chillerlan\QRCode\Data\{AlphaNum, Byte, Kanji, Number};
16
use chillerlan\QRCode\Output\QROutputInterface;
17
18
/**
19
 * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
20
 * @link http://www.thonky.com/qr-code-tutorial/
21
 */
22
class QRCode{
23
24
	/**
25
	 * API constants
26
	 */
27
	const OUTPUT_STRING_TEXT = 'txt';
28
	const OUTPUT_STRING_JSON = 'json';
29
30
	const OUTPUT_MARKUP_HTML = 'html';
31
	const OUTPUT_MARKUP_SVG  = 'svg';
32
#	const OUTPUT_MARKUP_XML  = 'xml'; // anyone?
33
34
	const OUTPUT_IMAGE_PNG = 'png';
35
	const OUTPUT_IMAGE_JPG = 'jpg';
36
	const OUTPUT_IMAGE_GIF = 'gif';
37
38
	const ERROR_CORRECT_LEVEL_L = 1; // 7%.
39
	const ERROR_CORRECT_LEVEL_M = 0; // 15%.
40
	const ERROR_CORRECT_LEVEL_Q = 3; // 25%.
41
	const ERROR_CORRECT_LEVEL_H = 2; // 30%.
42
43
	// max bits @ ec level L:07 M:15 Q:25 H:30 %
44
	const TYPE_01 =  1; //  152  128  104   72
45
	const TYPE_02 =  2; //  272  224  176  128
46
	const TYPE_03 =  3; //  440  352  272  208
47
	const TYPE_04 =  4; //  640  512  384  288
48
	const TYPE_05 =  5; //  864  688  496  368
49
	const TYPE_06 =  6; // 1088  864  608  480
50
	const TYPE_07 =  7; // 1248  992  704  528
51
	const TYPE_08 =  8; // 1552 1232  880  688
52
	const TYPE_09 =  9; // 1856 1456 1056  800
53
	const TYPE_10 = 10; // 2192 1728 1232  976
54
55
	/**
56
	 * @var array
57
	 */
58
	protected $matrix = [];
59
60
	/**
61
	 * @var int
62
	 */
63
	protected $pixelCount = 0;
64
65
	/**
66
	 * @var int
67
	 */
68
	protected $typeNumber;
69
70
	/**
71
	 * @var int
72
	 */
73
	protected $errorCorrectLevel;
74
75
	/**
76
	 * @var int
77
	 */
78
	protected $lostPoint;
79
80
	/**
81
	 * @var int
82
	 */
83
	protected $darkCount;
84
85
	/**
86
	 * @var float
87
	 */
88
	protected $minLostPoint;
89
90
	/**
91
	 * @var int
92
	 */
93
	protected $maskPattern;
94
95
	/**
96
	 * @var \chillerlan\QRCode\BitBuffer
97
	 */
98
	protected $bitBuffer;
99
100
	/**
101
	 * @var \chillerlan\QRCode\Data\QRDataInterface
102
	 */
103
	protected $qrDataInterface;
104
105
	/**
106
	 * @var \chillerlan\QRCode\Output\QROutputInterface
107
	 */
108
	protected $qrOutputInterface;
109
110
	/**
111
	 * QRCode constructor.
112
	 *
113
	 * @param string                                      $data
114
	 * @param \chillerlan\QRCode\Output\QROutputInterface $output
115
	 * @param \chillerlan\QRCode\QROptions|null           $options
116
	 */
117
	public function __construct($data, QROutputInterface $output, QROptions $options = null){
118
		$this->qrOutputInterface = $output;
119
		$this->bitBuffer = new BitBuffer;
120
		$this->setData($data, $options);
121
	}
122
123
	/**
124
	 * @param string                            $data
125
	 * @param \chillerlan\QRCode\QROptions|null $options
126
	 *
127
	 * @return \chillerlan\QRCode\QRCode
128
	 * @throws \chillerlan\QRCode\QRCodeException
129
	 */
130
	public function setData(string $data, QROptions $options = null):QRCode {
131
		$data = trim($data);
132
133
		if(empty($data)){
134
			throw new QRCodeException('No data given.');
135
		}
136
137
		if(!$options instanceof QROptions){
138
			$options = new QROptions;
139
		}
140
141
		if(!in_array($options->errorCorrectLevel, QRConst::RSBLOCK, true)){
142
			throw new QRCodeException('Invalid error correct level: '.$options->errorCorrectLevel);
143
		}
144
145
		$this->errorCorrectLevel = $options->errorCorrectLevel;
146
147
		switch(true){
148
			case Util::isAlphaNum($data):
149
				$mode = Util::isNumber($data) ? QRConst::MODE_NUMBER : QRConst::MODE_ALPHANUM;
150
				break;
151
			case Util::isKanji($data):
152
				$mode = QRConst::MODE_KANJI;
153
				break;
154
			default:
155
				$mode = QRConst::MODE_BYTE;
156
				break;
157
		}
158
159
		// see, Scrunitizer, it is concrete! :P
160
		$qrDataInterface = [
161
			QRConst::MODE_ALPHANUM => AlphaNum::class,
162
			QRConst::MODE_BYTE     => Byte::class,
163
			QRConst::MODE_KANJI    => Kanji::class,
164
			QRConst::MODE_NUMBER   => Number::class,
165
		][$mode];
166
167
		$this->qrDataInterface = new $qrDataInterface($data);
168
		$this->typeNumber = $options->typeNumber;
169
170
		if(!is_int($this->typeNumber) || $this->typeNumber < 1 || $this->typeNumber > 10){
171
			$this->typeNumber = $this->getTypeNumber($mode);
172
		}
173
174
		return $this;
175
	}
176
177
	/**
178
	 * @param int $mode
179
	 *
180
	 * @return int
181
	 * @throws \chillerlan\QRCode\QRCodeException
182
	 */
183
	protected function getTypeNumber(int $mode):int {
184
		$length = $this->qrDataInterface->dataLength;
0 ignored issues
show
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
185
186
		if($this->qrDataInterface->mode === QRConst::MODE_KANJI){
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
187
			$length = floor($length / 2);
188
		}
189
190
		foreach(range(1, 10) as $type){
191
			if($length <= Util::getMaxLength($type, $mode, $this->errorCorrectLevel)){
192
				return $type;
193
			}
194
		}
195
196
		throw new QRCodeException('Unable to determine type number.'); // @codeCoverageIgnore
197
	}
198
199
	/**
200
	 * @return mixed
201
	 */
202
	public function output(){
203
		$this->qrOutputInterface->setMatrix($this->getRawData());
204
205
		return $this->qrOutputInterface->dump();
206
	}
207
208
	/**
209
	 * @return array
210
	 */
211
	public function getRawData():array {
212
		$this->minLostPoint = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $minLostPoint was declared of type double, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
213
		$this->maskPattern = 0;
214
215
		for($pattern = 0; $pattern <= 7; $pattern++){
216
			$this->testPattern($pattern);
217
		}
218
219
		$this->getMatrix(false, $this->maskPattern);
220
221
		return $this->matrix;
222
	}
223
224
	/**
225
	 * @param array $range
226
	 *
227
	 * @return void
228
	 */
229
	protected function testLevel1(array $range){
230
231
		foreach($range as $row){
232
			foreach($range as $col){
233
				$sameCount = 0;
234
235
				foreach([-1, 0, 1] as $rr){
236
					if($row + $rr < 0 || $this->pixelCount <= $row + $rr){
237
						continue;
238
					}
239
240
					foreach([-1, 0, 1] as $cr){
241
242
						if(($rr === 0 && $cr === 0) || ($col + $cr < 0 || $this->pixelCount <= $col + $cr)){
243
							continue;
244
						}
245
246
						if($this->matrix[$row + $rr][$col + $cr] === $this->matrix[$row][$col]){
247
							$sameCount++;
248
						}
249
					}
250
				}
251
252
				if($sameCount > 5){
253
					$this->lostPoint += (3 + $sameCount - 5);
254
				}
255
256
			}
257
		}
258
259
	}
260
261
	/**
262
	 * @param array $range
263
	 *
264
	 * @return void
265
	 */
266
	protected function testLevel2(array $range){
267
268
		foreach($range as $row){
269
			foreach($range as $col){
270
				$count = 0;
271
272
				if(
273
					   $this->matrix[$row    ][$col    ]
274
					|| $this->matrix[$row    ][$col + 1]
275
					|| $this->matrix[$row + 1][$col    ]
276
					|| $this->matrix[$row + 1][$col + 1]
277
				){
278
					$count++;
279
				}
280
281
				if($count === 0 || $count === 4){
282
					$this->lostPoint += 3;
283
				}
284
285
			}
286
		}
287
288
	}
289
290
	/**
291
	 * @param array $range1
292
	 * @param array $range2
293
	 *
294
	 * @return void
295
	 */
296
	protected function testLevel3(array $range1, array $range2){
297
298 View Code Duplication
		foreach($range1 as $row){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
			foreach($range2 as $col){
300
301
				if(
302
					    $this->matrix[$row][$col    ]
303
					&& !$this->matrix[$row][$col + 1]
304
					&&  $this->matrix[$row][$col + 2]
305
					&&  $this->matrix[$row][$col + 3]
306
					&&  $this->matrix[$row][$col + 4]
307
					&& !$this->matrix[$row][$col + 5]
308
					&&  $this->matrix[$row][$col + 6]
309
				){
310
					$this->lostPoint += 40;
311
				}
312
313
			}
314
		}
315
316 View Code Duplication
		foreach($range1 as $col){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
			foreach($range2 as $row){
318
319
				if(
320
					    $this->matrix[$row    ][$col]
321
					&& !$this->matrix[$row + 1][$col]
322
					&&  $this->matrix[$row + 2][$col]
323
					&&  $this->matrix[$row + 3][$col]
324
					&&  $this->matrix[$row + 4][$col]
325
					&& !$this->matrix[$row + 5][$col]
326
					&&  $this->matrix[$row + 6][$col]
327
				){
328
					$this->lostPoint += 40;
329
				}
330
331
			}
332
		}
333
334
	}
335
336
	/**
337
	 * @param array $range
338
	 *
339
	 * @return void
340
	 */
341
	protected function testLevel4(array $range){
342
343
		foreach($range as $col){
344
			foreach($range as $row){
345
				if($this->matrix[$row][$col]){
346
					$this->darkCount++;
347
				}
348
			}
349
		}
350
351
	}
352
353
	/**
354
	 * @param int $pattern
355
	 *
356
	 * @return void
357
	 */
358
	protected function testPattern(int $pattern){
359
		$this->getMatrix(true, $pattern);
360
		$this->lostPoint = 0;
361
		$this->darkCount = 0;
362
363
		$range = range(0, $this->pixelCount-1);
364
365
		$this->testLevel1($range);
366
		$this->testLevel2(range(0, $this->pixelCount-2));
367
		$this->testLevel3($range, range(0, $this->pixelCount-7));
368
		$this->testLevel4($range);
369
370
		$this->lostPoint += (abs(100 * $this->darkCount / $this->pixelCount / $this->pixelCount - 50) / 5) * 10;
371
372
		if($pattern === 0 || $this->minLostPoint > $this->lostPoint){
373
			$this->minLostPoint = $this->lostPoint;
374
			$this->maskPattern = $pattern;
375
		}
376
377
	}
378
379
	/**
380
	 * @param bool $test
381
	 *
382
	 * @return void
383
	 */
384
	protected function setTypeNumber(bool $test){
385
		$bits = Util::getBCHTypeNumber($this->typeNumber);
386
387
		for($i = 0; $i < 18; $i++){
388
			$a = (int)floor($i / 3);
389
			$b = $i % 3 + $this->pixelCount - 8 - 3;
390
391
			$this->matrix[$a][$b] = $this->matrix[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
392
		}
393
394
	}
395
396
	/**
397
	 * @param bool $test
398
	 * @param int  $pattern
399
	 *
400
	 * @return void
401
	 */
402
	protected function setTypeInfo(bool $test, int $pattern){
403
		$this->setPattern();
404
		$bits = Util::getBCHTypeInfo(($this->errorCorrectLevel << 3) | $pattern);
405
406
		for($i = 0; $i < 15; $i++){
407
			$mod = !$test && (($bits >> $i) & 1) === 1;
408
409
			switch(true){
410
				case $i < 6: $this->matrix[$i    ][8] = $mod; break;
2 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
411
				case $i < 8: $this->matrix[$i + 1][8] = $mod; break;
2 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
412
				default:
413
					$this->matrix[$this->pixelCount - 15 + $i][8] = $mod;
414
			}
415
416
			switch(true){
417 View Code Duplication
				case $i < 8: $this->matrix[8][$this->pixelCount - $i - 1] = $mod; break;
2 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
418 View Code Duplication
				case $i < 9: $this->matrix[8][           15 + 1 - $i - 1] = $mod; break;
2 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
419
				default:
420
					$this->matrix[8][15 - $i - 1] = $mod;
421
			}
422
423
		}
424
425
		$this->matrix[$this->pixelCount - 8][8] = !$test;
426
	}
427
428
	/**
429
	 * @return void
430
	 * @throws \chillerlan\QRCode\QRCodeException
431
	 */
432
	protected function createData(){
433
		$this->bitBuffer->clear();
434
		$this->bitBuffer->put($this->qrDataInterface->mode, 4);
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
435
		$this->bitBuffer->put(
436
			$this->qrDataInterface->mode === QRConst::MODE_KANJI
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
437
				? floor($this->qrDataInterface->dataLength / 2)
0 ignored issues
show
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
438
				: $this->qrDataInterface->dataLength,
0 ignored issues
show
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
439
			$this->qrDataInterface->getLengthInBits($this->typeNumber)
440
		);
441
442
		$this->qrDataInterface->write($this->bitBuffer);
443
444
		$MAX_BITS = QRConst::MAX_BITS[$this->typeNumber][$this->errorCorrectLevel];
445
446
		if($this->bitBuffer->length > $MAX_BITS){
447
			throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.' > '.$MAX_BITS.'bit)');
448
		}
449
450
		// end code.
451
		if($this->bitBuffer->length + 4 <= $MAX_BITS){
452
			$this->bitBuffer->put(0, 4);
453
		}
454
455
		// padding
456
		while($this->bitBuffer->length % 8 !== 0){
457
			$this->bitBuffer->putBit(false);
458
		}
459
460
		// padding
461
		while(true){
462
463
			if($this->bitBuffer->length >= $MAX_BITS){
464
				break;
465
			}
466
467
			$this->bitBuffer->put(QRConst::PAD0, 8);
468
469
			if($this->bitBuffer->length >= $MAX_BITS){
470
				break;
471
			}
472
473
			$this->bitBuffer->put(QRConst::PAD1, 8);
474
		}
475
476
	}
477
478
	/**
479
	 * @return array
480
	 * @throws \chillerlan\QRCode\QRCodeException
481
	 */
482
	protected function createBytes():array {
483
		$totalCodeCount = $maxDcCount = $maxEcCount = $offset = $index = 0;
484
		$rsBlocks = Util::getRSBlocks($this->typeNumber, $this->errorCorrectLevel);
485
		$rsBlockCount = count($rsBlocks);
486
		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
487
488
		foreach($rsBlocks as $key => $value){
489
			$rsBlockTotal = $value[0];
490
			$rsBlockDataCount = $value[1];
491
492
			$maxDcCount = max($maxDcCount, $rsBlockDataCount);
493
			$maxEcCount = max($maxEcCount, $rsBlockTotal - $rsBlockDataCount);
494
495
			$dcdata[$key] = array_fill(0, $rsBlockDataCount, null);
496
497
			foreach($dcdata[$key] as $i => &$_dcdata){
498
				$bdata = $this->bitBuffer->buffer;
499
				$_dcdata = 0xff & $bdata[$i + $offset];
500
			}
501
502
			$offset += $rsBlockDataCount;
503
504
			$rsPoly = new Polynomial;
505
			$modPoly = new Polynomial;
506
507
			foreach(range(0, $rsBlockTotal - $rsBlockDataCount - 1) as $i){
508
				$modPoly->setNum([1, $modPoly->gexp($i)]);
509
				$rsPoly->multiply($modPoly->num);
510
			}
511
512
			$rsPolyCount = count($rsPoly->num);
513
			$modPoly->setNum($dcdata[$key], $rsPolyCount - 1)->mod($rsPoly->num);
514
			$ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
515
			$add = count($modPoly->num) - count($ecdata[$key]);
516
517
			foreach($ecdata[$key] as $i => &$_ecdata){
518
				$modIndex = $i + $add;
519
				$_ecdata = $modIndex >= 0 ? $modPoly->num[$modIndex] : 0;
520
			}
521
522
			$totalCodeCount += $rsBlockTotal;
523
		}
524
525
		$data = array_fill(0, $totalCodeCount, null);
526
		$rsrange = range(0, $rsBlockCount - 1);
527
528 View Code Duplication
		foreach(range(0, $maxDcCount - 1) as $i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
529
			foreach($rsrange as $key){
530
				if($i < count($dcdata[$key])){
531
					$data[$index++] = $dcdata[$key][$i];
532
				}
533
			}
534
		}
535
536 View Code Duplication
		foreach(range(0, $maxEcCount - 1) as $i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
537
			foreach($rsrange as $key){
538
				if($i < count($ecdata[$key])){
539
					$data[$index++] = $ecdata[$key][$i];
540
				}
541
			}
542
		}
543
544
		return $data;
545
	}
546
547
	/**
548
	 * @param int $pattern
549
	 *
550
	 * @return void
551
	 * @throws \chillerlan\QRCode\QRCodeException
552
	 */
553
	protected function mapData(int $pattern){
554
		$this->createData();
555
		$data = $this->createBytes();
556
		$inc = -1;
557
		$row = $this->pixelCount - 1;
558
		$bitIndex = 7;
559
		$byteIndex = 0;
560
		$dataCount = count($data);
561
562
		for($col = $this->pixelCount - 1; $col > 0; $col -= 2){
563
			if($col === 6){
564
				$col--;
565
			}
566
567
			while(true){
568
				foreach([0, 1] as $c){
569
					$_col = $col - $c;
570
571
					if($this->matrix[$row][$_col] === null){
572
						$dark = false;
573
574
						if($byteIndex < $dataCount){
575
							$dark = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
576
						}
577
578
						$a = $row + $_col;
579
						$m = $row * $_col;
580
						$MASK_PATTERN = [
581
							QRConst::MASK_PATTERN000 => $a % 2,
582
							QRConst::MASK_PATTERN001 => $row % 2,
583
							QRConst::MASK_PATTERN010 => $_col % 3,
584
							QRConst::MASK_PATTERN011 => $a % 3,
585
							QRConst::MASK_PATTERN100 => (floor($row / 2) + floor($_col / 3)) % 2,
586
							QRConst::MASK_PATTERN101 => $m % 2 + $m % 3,
587
							QRConst::MASK_PATTERN110 => ($m % 2 + $m % 3) % 2,
588
							QRConst::MASK_PATTERN111 => ($m % 3 + $a % 2) % 2,
589
						][$pattern];
590
591
						if($MASK_PATTERN === 0){
592
							$dark = !$dark;
593
						}
594
595
						$this->matrix[$row][$_col] = $dark;
596
597
						$bitIndex--;
598
						if($bitIndex === -1){
599
							$byteIndex++;
600
							$bitIndex = 7;
601
						}
602
					}
603
				}
604
605
				$row += $inc;
606
				if($row < 0 || $this->pixelCount <= $row){
607
					$row -= $inc;
608
					$inc = -$inc;
609
					break;
610
				}
611
			}
612
		}
613
614
	}
615
616
	/**
617
	 * @return void
618
	 */
619
	protected function setupPositionProbePattern(){
620
		$range = range(-1, 7);
621
622
		foreach([[0, 0], [$this->pixelCount - 7, 0], [0, $this->pixelCount - 7]] as $grid){
623
			$row = $grid[0];
624
			$col = $grid[1];
625
626
			foreach($range as $r){
627
				foreach($range as $c){
628
629
					if($row + $r <= -1 || $this->pixelCount <= $row + $r || $col + $c <= -1 || $this->pixelCount <= $col + $c){
630
						continue;
631
					}
632
633
					$this->matrix[$row + $r][$col + $c] =
634
						(0 <= $r && $r <= 6 && ($c === 0 || $c === 6))
635
						|| (0 <= $c && $c <= 6 && ($r === 0 || $r === 6))
636
						|| (2 <= $c && $c <= 4 &&  2 <= $r && $r <= 4);
637
				}
638
			}
639
		}
640
641
	}
642
643
	/**
644
	 * @return void
645
	 */
646
	protected function setupPositionAdjustPattern(){
647
		$range = QRConst::PATTERN_POSITION[$this->typeNumber - 1];
648
649
		foreach($range as $i => $posI){
650
			foreach($range as $j => $posJ){
651
				if($this->matrix[$posI][$posJ] !== null){
652
					continue;
653
				}
654
655
				for($row = -2; $row <= 2; $row++){
656
					for($col = -2; $col <= 2; $col++){
657
						$this->matrix[$posI + $row][$posJ + $col] =
658
							    $row === -2 || $row === 2
659
							||  $col === -2 || $col === 2
660
							|| ($row ===  0 && $col === 0);
661
					}
662
				}
663
			}
664
		}
665
666
	}
667
668
	/**
669
	 * @return void
670
	 * @throws \chillerlan\QRCode\QRCodeException
671
	 */
672
	protected function setPattern(){
673
		$this->setupPositionProbePattern();
674
		$this->setupPositionAdjustPattern();
675
676
		// setupTimingPattern
677
		for($i = 8; $i < $this->pixelCount - 8; $i++){
678
			if($this->matrix[$i][6] !== null){
679
				continue; // @codeCoverageIgnore
680
			}
681
682
			$this->matrix[$i][6] = $this->matrix[6][$i] = $i % 2 === 0;
683
		}
684
685
	}
686
687
	/**
688
	 * @param bool $test
689
	 * @param int  $maskPattern
690
	 *
691
	 * @throws \chillerlan\QRCode\QRCodeException
692
	 */
693
	protected function getMatrix(bool $test, int $maskPattern){
694
		$this->pixelCount = $this->typeNumber * 4 + 17;
695
		$this->matrix = array_fill(0, $this->pixelCount, array_fill(0, $this->pixelCount, null));
696
		$this->setTypeInfo($test, $maskPattern);
697
698
		if($this->typeNumber >= 7){
699
			$this->setTypeNumber($test);
700
		}
701
702
		$this->mapData($maskPattern);
703
	}
704
705
}
706