Completed
Push — master ( 275bd9...714d02 )
by smiley
02:38
created

QRCode::mapData()   C

Complexity

Conditions 11
Paths 43

Size

Total Lines 64
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
c 6
b 0
f 0
dl 0
loc 64
rs 6.0563
cc 11
eloc 44
nc 43
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
16
use chillerlan\QRCode\Data\Byte;
17
use chillerlan\QRCode\Data\Kanji;
18
use chillerlan\QRCode\Data\Number;
19
use chillerlan\QRCode\Output\QROutputInterface;
20
21
/**
22
 * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
23
 * @link http://www.thonky.com/qr-code-tutorial/
24
 */
25
class QRCode{
26
27
	/**
28
	 * API constants
29
	 */
30
	const OUTPUT_STRING_TEXT = 0;
31
	const OUTPUT_STRING_JSON = 1;
32
	const OUTPUT_STRING_HTML = 2;
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 $this
128
	 * @throws \chillerlan\QRCode\QRCodeException
129
	 */
130
	public function setData($data, QROptions $options = null){
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 = intval($options->typeNumber);
169
170
		if($this->typeNumber < 1 || $this->typeNumber > 10){
171
			$this->typeNumber = $this->getTypeNumber($mode);
172
		}
173
174
		return $this;
175
	}
176
177
	/**
178
	 * @param $mode
179
	 *
180
	 * @return int
181
	 * @throws \chillerlan\QRCode\QRCodeException
182
	 */
183
	protected function getTypeNumber($mode){
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($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...
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
		return $this->qrOutputInterface->dump();
205
	}
206
207
	/**
208
	 * @return array
209
	 */
210
	public function getRawData(){
211
		$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...
212
		$this->maskPattern = 0;
213
214
		foreach(range(0, 7) as $pattern){
215
			$this->testPattern($pattern);
216
		}
217
218
		$this->getMatrix(false, $this->maskPattern);
219
220
		return $this->matrix;
221
	}
222
223
	/**
224
	 * @param array $range
225
	 */
226
	protected function testLevel1(array $range){
227
228
		foreach($range as $row){
229
			foreach($range as $col){
230
				$sameCount = 0;
231
232
				foreach([-1, 0, 1] as $rr){
233
					if($row + $rr < 0 || $this->pixelCount <= $row + $rr){
234
						continue;
235
					}
236
237
					foreach([-1, 0, 1] as $cr){
238
239
						if(($rr === 0 && $cr === 0) || ($col + $cr < 0 || $this->pixelCount <= $col + $cr)){
240
							continue;
241
						}
242
243
						if($this->matrix[$row + $rr][$col + $cr] === $this->matrix[$row][$col]){
244
							$sameCount++;
245
						}
246
					}
247
				}
248
249
				if($sameCount > 5){
250
					$this->lostPoint += (3 + $sameCount - 5);
251
				}
252
253
			}
254
		}
255
256
	}
257
258
	/**
259
	 * @param array $range
260
	 */
261
	protected function testLevel2(array $range){
262
263
		foreach($range as $row){
264
			foreach($range as $col){
265
				$count = 0;
266
267
				if(
268
					   $this->matrix[$row    ][$col    ]
269
					|| $this->matrix[$row    ][$col + 1]
270
					|| $this->matrix[$row + 1][$col    ]
271
					|| $this->matrix[$row + 1][$col + 1]
272
				){
273
					$count++;
274
				}
275
276
				if($count === 0 || $count === 4){
277
					$this->lostPoint += 3;
278
				}
279
280
			}
281
		}
282
283
	}
284
285
	/**
286
	 * @param array $range1
287
	 * @param array $range2
288
	 */
289
	protected function testLevel3(array $range1, array $range2){
290
291 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...
292
			foreach($range2 as $col){
293
294
				if(
295
					    $this->matrix[$row][$col    ]
296
					&& !$this->matrix[$row][$col + 1]
297
					&&  $this->matrix[$row][$col + 2]
298
					&&  $this->matrix[$row][$col + 3]
299
					&&  $this->matrix[$row][$col + 4]
300
					&& !$this->matrix[$row][$col + 5]
301
					&&  $this->matrix[$row][$col + 6]
302
				){
303
					$this->lostPoint += 40;
304
				}
305
306
			}
307
		}
308
309 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...
310
			foreach($range2 as $row){
311
312
				if(
313
					    $this->matrix[$row    ][$col]
314
					&& !$this->matrix[$row + 1][$col]
315
					&&  $this->matrix[$row + 2][$col]
316
					&&  $this->matrix[$row + 3][$col]
317
					&&  $this->matrix[$row + 4][$col]
318
					&& !$this->matrix[$row + 5][$col]
319
					&&  $this->matrix[$row + 6][$col]
320
				){
321
					$this->lostPoint += 40;
322
				}
323
324
			}
325
		}
326
327
	}
328
329
	/**
330
	 * @param array $range
331
	 */
332
	protected function testLevel4(array $range){
333
334
		foreach($range as $col){
335
			foreach($range as $row){
336
				if($this->matrix[$row][$col]){
337
					$this->darkCount++;
338
				}
339
			}
340
		}
341
342
	}
343
344
	/**
345
	 * @param int $pattern
346
	 */
347
	protected function testPattern($pattern){
348
		$this->getMatrix(true, $pattern);
349
		$this->lostPoint = 0;
350
		$this->darkCount = 0;
351
352
		$range = range(0, $this->pixelCount-1);
353
354
		$this->testLevel1($range);
355
		$this->testLevel2(range(0, $this->pixelCount-2));
356
		$this->testLevel3($range, range(0, $this->pixelCount-7));
357
		$this->testLevel4($range);
358
359
		$this->lostPoint += (abs(100 * $this->darkCount / $this->pixelCount / $this->pixelCount - 50) / 5) * 10;
360
361
		if($pattern === 0 || $this->minLostPoint > $this->lostPoint){
362
			$this->minLostPoint = $this->lostPoint;
363
			$this->maskPattern = $pattern;
364
		}
365
366
	}
367
368
	/**
369
	 * @param bool $test
370
	 */
371
	protected function setTypeNumber($test){
372
		$bits = Util::getBCHTypeNumber($this->typeNumber);
373
		$i = 0;
374
		while($i < 18){
375
			$a = (int)floor($i / 3);
376
			$b = $i % 3 + $this->pixelCount - 8 - 3;
377
378
			$this->matrix[$a][$b] = $this->matrix[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
379
			$i++;
380
		}
381
382
	}
383
384
	/**
385
	 * @param bool $test
386
	 * @param int  $pattern
387
	 */
388
	protected function setTypeInfo($test, $pattern){
389
		$this->setPattern();
390
		$bits = Util::getBCHTypeInfo(($this->errorCorrectLevel << 3) | $pattern);
391
		$i = 0;
392
		while($i < 15){
393
			$mod = !$test && (($bits >> $i) & 1) === 1;
394
395
			switch(true){
396
				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...
397
				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...
398
				default:
399
					$this->matrix[$this->pixelCount - 15 + $i][8] = $mod;
400
			}
401
402
			switch(true){
403 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...
404 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...
405
				default:
406
					$this->matrix[8][15 - $i - 1] = $mod;
407
			}
408
			$i++;
409
		}
410
411
		$this->matrix[$this->pixelCount - 8][8] = !$test;
412
	}
413
414
	/**
415
	 * @throws \chillerlan\QRCode\QRCodeException
416
	 */
417
	protected function createData(){
418
		$this->bitBuffer->clear();
419
		$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...
420
		$this->bitBuffer->put(
421
			$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...
422
				? 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...
423
				: $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...
424
			$this->qrDataInterface->getLengthInBits($this->typeNumber)
425
		);
426
427
		$this->qrDataInterface->write($this->bitBuffer);
428
429
		$MAX_BITS = QRConst::MAX_BITS[$this->typeNumber][$this->errorCorrectLevel];
430
431
		if($this->bitBuffer->length > $MAX_BITS){
432
			throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.' > '.$MAX_BITS.'bit)');
433
		}
434
435
		// end code.
436
		if($this->bitBuffer->length + 4 <= $MAX_BITS){
437
			$this->bitBuffer->put(0, 4);
438
		}
439
440
		// padding
441
		while($this->bitBuffer->length % 8 !== 0){
442
			$this->bitBuffer->putBit(false);
443
		}
444
445
		// padding
446
		while(true){
447
448
			if($this->bitBuffer->length >= $MAX_BITS){
449
				break;
450
			}
451
452
			$this->bitBuffer->put(QRConst::PAD0, 8);
453
454
			if($this->bitBuffer->length >= $MAX_BITS){
455
				break;
456
			}
457
458
			$this->bitBuffer->put(QRConst::PAD1, 8);
459
		}
460
461
	}
462
463
	/**
464
	 * @return array
465
	 * @throws \chillerlan\QRCode\QRCodeException
466
	 */
467
	protected function createBytes(){
468
		$totalCodeCount = $maxDcCount = $maxEcCount = $offset = $index = 0;
469
		$rsBlocks = Util::getRSBlocks($this->typeNumber, $this->errorCorrectLevel);
470
		$rsBlockCount = count($rsBlocks);
471
		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
472
473
		foreach($rsBlocks as $key => $value){
474
			$rsBlockTotal = $value[0];
475
			$rsBlockDataCount = $value[1];
476
477
			$maxDcCount = max($maxDcCount, $rsBlockDataCount);
478
			$maxEcCount = max($maxEcCount, $rsBlockTotal - $rsBlockDataCount);
479
480
			$dcdata[$key] = array_fill(0, $rsBlockDataCount, null);
481
482
			foreach($dcdata[$key] as $i => &$_dcdata){
483
				$bdata = $this->bitBuffer->buffer;
484
				$_dcdata = 0xff & $bdata[$i + $offset];
485
			}
486
487
			$offset += $rsBlockDataCount;
488
489
			$rsPoly = new Polynomial;
490
			$modPoly = new Polynomial;
491
492
			$i = 0;
493
			while($i < $rsBlockTotal - $rsBlockDataCount){
494
				$modPoly->setNum([1, $modPoly->gexp($i)]);
495
				$rsPoly->multiply($modPoly->num);
496
				$i++;
497
			}
498
499
			$rsPolyCount = count($rsPoly->num);
500
			$modPoly->setNum($dcdata[$key], $rsPolyCount - 1)->mod($rsPoly->num);
501
			$ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
502
			$add = count($modPoly->num) - count($ecdata[$key]);
503
504
			foreach($ecdata[$key] as $i => &$_ecdata){
505
				$modIndex = $i + $add;
506
				$_ecdata = $modIndex >= 0 ? $modPoly->num[$modIndex] : 0;
507
			}
508
509
			$totalCodeCount += $rsBlockTotal;
510
		}
511
512
		$data = array_fill(0, $totalCodeCount, null);
513
		$rsrange = range(0, $rsBlockCount - 1);
514
515
		$i = 0;
516 View Code Duplication
		while($i < $maxDcCount){
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...
517
			foreach($rsrange as $key){
518
				if($i < count($dcdata[$key])){
519
					$data[$index++] = $dcdata[$key][$i];
520
				}
521
			}
522
			$i++;
523
		}
524
525
		$i = 0;
526 View Code Duplication
		while($i < $maxEcCount){
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...
527
			foreach($rsrange as $key){
528
				if($i < count($ecdata[$key])){
529
					$data[$index++] = $ecdata[$key][$i];
530
				}
531
			}
532
			$i++;
533
		}
534
535
		return $data;
536
	}
537
538
	/**
539
	 * @param int $pattern
540
	 *
541
	 * @throws \chillerlan\QRCode\QRCodeException
542
	 */
543
	protected function mapData($pattern){
544
		$this->createData();
545
		$data = $this->createBytes();
546
		$inc = -1;
547
		$row = $this->pixelCount - 1;
548
		$bitIndex = 7;
549
		$byteIndex = 0;
550
		$dataCount = count($data);
551
552
		for($col = $this->pixelCount - 1; $col > 0; $col -= 2){
553
			if($col === 6){
554
				$col--;
555
			}
556
557
			while(true){
558
				$c = 0;
559
				while($c < 2){
560
					$_col = $col - $c;
561
562
					if($this->matrix[$row][$_col] === null){
563
						$dark = false;
564
565
						if($byteIndex < $dataCount){
566
							$dark = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
567
						}
568
569
						$a = $row + $_col;
570
						$m = $row * $_col;
571
						$MASK_PATTERN = [
572
							QRConst::MASK_PATTERN000 => $a % 2,
573
							QRConst::MASK_PATTERN001 => $row % 2,
574
							QRConst::MASK_PATTERN010 => $_col % 3,
575
							QRConst::MASK_PATTERN011 => $a % 3,
576
							QRConst::MASK_PATTERN100 => (floor($row / 2) + floor($_col / 3)) % 2,
577
							QRConst::MASK_PATTERN101 => $m % 2 + $m % 3,
578
							QRConst::MASK_PATTERN110 => ($m % 2 + $m % 3) % 2,
579
							QRConst::MASK_PATTERN111 => ($m % 3 + $a % 2) % 2,
580
						][$pattern];
581
582
						if($MASK_PATTERN === 0){
583
							$dark = !$dark;
584
						}
585
586
						$this->matrix[$row][$_col] = $dark;
587
588
						$bitIndex--;
589
						if($bitIndex === -1){
590
							$byteIndex++;
591
							$bitIndex = 7;
592
						}
593
					}
594
					$c++;
595
				}
596
597
				$row += $inc;
598
				if($row < 0 || $this->pixelCount <= $row){
599
					$row -= $inc;
600
					$inc = -$inc;
601
					break;
602
				}
603
			}
604
		}
605
606
	}
607
608
	/**
609
	 *
610
	 */
611
	protected function setupPositionProbePattern(){
612
		$range = range(-1, 7);
613
614
		foreach([[0, 0], [$this->pixelCount - 7, 0], [0, $this->pixelCount - 7]] as $grid){
615
			$row = $grid[0];
616
			$col = $grid[1];
617
618
			$r = -1;
619
			while($r < 8){
620
				foreach($range as $c){
621
622
					if($row + $r <= -1 || $this->pixelCount <= $row + $r || $col + $c <= -1 || $this->pixelCount <= $col + $c){
623
						continue;
624
					}
625
626
					$this->matrix[$row + $r][$col + $c] =
627
						(0 <= $r && $r <= 6 && ($c === 0 || $c === 6))
628
						|| (0 <= $c && $c <= 6 && ($r === 0 || $r === 6))
629
						|| (2 <= $c && $c <= 4 &&  2 <= $r && $r <= 4);
630
				}
631
				$r++;
632
			}
633
		}
634
635
	}
636
637
	/**
638
	 *
639
	 */
640
	protected function setupPositionAdjustPattern(){
641
		$range = QRConst::PATTERN_POSITION[$this->typeNumber - 1];
642
643
		foreach($range as $i => $posI){
644
			foreach($range as $j => $posJ){
645
				if($this->matrix[$posI][$posJ] !== null){
646
					continue;
647
				}
648
649
				$row = $col = -2;
650
				while($row < 3){
651
					while($col < 3){
652
						$this->matrix[$posI + $row][$posJ + $col] =
653
							$row === -2 || $row ===  2 || $col === -2 || $col ===  2 ||($row ===  0 && $col === 0);
654
						$col++;
655
					}
656
					$row++;
657
				}
658
			}
659
		}
660
661
	}
662
663
	/**
664
	 * @throws \chillerlan\QRCode\QRCodeException
665
	 */
666
	protected function setPattern(){
667
		$this->setupPositionProbePattern();
668
		$this->setupPositionAdjustPattern();
669
670
		// setupTimingPattern
671
		for($i = 8; $i < $this->pixelCount - 8; $i++){
672
			if($this->matrix[$i][6] !== null){
673
				continue; // @codeCoverageIgnore
674
			}
675
676
			$this->matrix[$i][6] = $this->matrix[6][$i] = $i % 2 === 0;
677
		}
678
679
	}
680
681
	/**
682
	 * @param bool $test
683
	 * @param int  $maskPattern
684
	 *
685
	 * @throws \chillerlan\QRCode\QRCodeException
686
	 */
687
	protected function getMatrix($test, $maskPattern){
688
		$this->pixelCount = $this->typeNumber * 4 + 17;
689
		$this->matrix = array_fill(0, $this->pixelCount, array_fill(0, $this->pixelCount, null));
690
		$this->setTypeInfo($test, $maskPattern);
691
692
		if($this->typeNumber >= 7){
693
			$this->setTypeNumber($test);
694
		}
695
696
		$this->mapData($maskPattern);
697
	}
698
699
}
700