Passed
Push — main ( d21ed9...61128e )
by smiley
01:59
created

QRMatrix::setAlignmentPattern()   B

Complexity

Conditions 11
Paths 4

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 10
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 23
rs 7.3166

How to fix   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 QRMatrix
4
 *
5
 * @created      15.11.2017
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2017 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\QRCode\Data;
12
13
use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, ReedSolomonEncoder, Version};
14
use function array_fill, count, floor, range;
15
16
/**
17
 * Holds an array representation of the final QR Code that contains numerical values for later output modifications;
18
 * maps the ECC coded binary data and applies the mask pattern
19
 *
20
 * @see http://www.thonky.com/qr-code-tutorial/format-version-information
21
 */
22
class QRMatrix{
23
24
	/** @var int */
25
	public const M_NULL       = 0b000000000000;
26
	/** @var int */
27
	public const M_DARKMODULE = 0b000000000001;
28
	/** @var int */
29
	public const M_DATA       = 0b000000000010;
30
	/** @var int */
31
	public const M_FINDER     = 0b000000000100;
32
	/** @var int */
33
	public const M_SEPARATOR  = 0b000000001000;
34
	/** @var int */
35
	public const M_ALIGNMENT  = 0b000000010000;
36
	/** @var int */
37
	public const M_TIMING     = 0b000000100000;
38
	/** @var int */
39
	public const M_FORMAT     = 0b000001000000;
40
	/** @var int */
41
	public const M_VERSION    = 0b000010000000;
42
	/** @var int */
43
	public const M_QUIETZONE  = 0b000100000000;
44
	/** @var int */
45
	public const M_LOGO       = 0b001000000000;
46
	/** @var int */
47
	public const M_FINDER_DOT = 0b010000000000;
48
	/** @var int */
49
	public const M_TEST       = 0b011111111111;
50
	/** @var int */
51
	public const IS_DARK      = 0b100000000000;
52
53
	/**
54
	 * Map of flag => coord
55
	 *
56
	 * @see \chillerlan\QRCode\Data\QRMatrix::checkNeighbours()
57
	 *
58
	 * @var array
59
	 */
60
	protected const neighbours = [
61
		0b00000001 => [-1, -1],
62
		0b00000010 => [ 0, -1],
63
		0b00000100 => [ 1, -1],
64
		0b00001000 => [ 1,  0],
65
		0b00010000 => [ 1,  1],
66
		0b00100000 => [ 0,  1],
67
		0b01000000 => [-1,  1],
68
		0b10000000 => [-1,  0]
69
	];
70
71
	/**
72
	 * the used mask pattern, set via QRMatrix::mask()
73
	 */
74
	protected ?MaskPattern $maskPattern = null;
75
76
	/**
77
	 * the current ECC level
78
	 */
79
	protected ?EccLevel $eccLevel = null;
80
81
	/**
82
	 * a Version instance
83
	 */
84
	protected ?Version $version = null;
85
86
	/**
87
	 * the size (side length) of the matrix, including quiet zone (if created)
88
	 */
89
	protected int $moduleCount;
90
91
	/**
92
	 * the actual matrix data array
93
	 *
94
	 * @var int[][]
95
	 */
96
	protected array $matrix;
97
98
	/**
99
	 * QRMatrix constructor.
100
	 */
101
	public function __construct(Version $version, EccLevel $eccLevel, MaskPattern $maskPattern){
102
		$this->version     = $version;
103
		$this->eccLevel    = $eccLevel;
104
		$this->maskPattern = $maskPattern;
105
		$this->moduleCount = $this->version->getDimension();
106
		$this->matrix      = $this->createMatrix($this->moduleCount, $this::M_NULL);
107
	}
108
109
	/**
110
	 * Creates a 2-dimensional array (square) of the given $size
111
	 */
112
	protected function createMatrix(int $size, int $value):array{
113
		return array_fill(0, $size, array_fill(0, $size, $value));
114
	}
115
116
	/**
117
	 * shortcut to initialize the functional patterns
118
	 */
119
	public function initFunctionalPatterns():self{
120
		return $this
121
			->setFinderPattern()
122
			->setSeparators()
123
			->setAlignmentPattern()
124
			->setTimingPattern()
125
			->setDarkModule()
126
			->setVersionNumber()
127
			->setFormatInfo()
128
		;
129
	}
130
131
	/**
132
	 * Returns the data matrix, returns a pure boolean representation if $boolean is set to true
133
	 *
134
	 * @return int[][]|bool[][]
135
	 */
136
	public function matrix(bool $boolean = false):array{
137
138
		if(!$boolean){
139
			return $this->matrix;
140
		}
141
142
		$matrix = [];
143
144
		foreach($this->matrix as $y => $row){
145
			$matrix[$y] = [];
146
147
			foreach($row as $x => $val){
148
				$matrix[$y][$x] = ($val & $this::IS_DARK) === $this::IS_DARK;
149
			}
150
		}
151
152
		return $matrix;
153
	}
154
155
	/**
156
	 * Returns the current version number
157
	 */
158
	public function version():?Version{
159
		return $this->version;
160
	}
161
162
	/**
163
	 * Returns the current ECC level
164
	 */
165
	public function eccLevel():?EccLevel{
166
		return $this->eccLevel;
167
	}
168
169
	/**
170
	 * Returns the current mask pattern
171
	 */
172
	public function maskPattern():?MaskPattern{
173
		return $this->maskPattern;
174
	}
175
176
	/**
177
	 * Returns the absoulute size of the matrix, including quiet zone (after setting it).
178
	 *
179
	 * size = version * 4 + 17 [ + 2 * quietzone size]
180
	 */
181
	public function size():int{
182
		return $this->moduleCount;
183
	}
184
185
	/**
186
	 * Returns the value of the module at position [$x, $y] or -1 if the coordinate is outside of the matrix
187
	 */
188
	public function get(int $x, int $y):int{
189
190
		if(!isset($this->matrix[$y][$x])){
191
			return -1;
192
		}
193
194
		return $this->matrix[$y][$x];
195
	}
196
197
	/**
198
	 * Sets the $M_TYPE value for the module at position [$x, $y]
199
	 *
200
	 *   true  => $M_TYPE | 0x800
201
	 *   false => $M_TYPE
202
	 */
203
	public function set(int $x, int $y, bool $value, int $M_TYPE):self{
204
205
		if(isset($this->matrix[$y][$x])){
206
			$this->matrix[$y][$x] = $M_TYPE | ($value ? $this::IS_DARK : 0);
207
		}
208
209
		return $this;
210
	}
211
212
	/**
213
	 * Flips the value of the module
214
	 */
215
	public function flip(int $x, int $y):self{
216
217
		if(isset($this->matrix[$y][$x])){
218
			$this->matrix[$y][$x] ^= $this::IS_DARK;
219
		}
220
221
		return $this;
222
	}
223
224
	/**
225
	 * Checks whether a module is of the given $M_TYPE
226
	 *
227
	 *   true => $value & $M_TYPE === $M_TYPE
228
	 */
229
	public function checkType(int $x, int $y, int $M_TYPE):bool{
230
231
		if(!isset($this->matrix[$y][$x])){
232
			return false;
233
		}
234
235
		return ($this->matrix[$y][$x] & $M_TYPE) === $M_TYPE;
236
	}
237
238
	/**
239
	 * checks whether the module at ($x, $y) is in the given array of $M_TYPES,
240
	 * returns true if a match is found, otherwise false.
241
	 */
242
	public function checkTypeIn(int $x, int $y, array $M_TYPES):bool{
243
244
		foreach($M_TYPES as $type){
245
			if($this->checkType($x, $y, $type)){
246
				return true;
247
			}
248
		}
249
250
		return false;
251
	}
252
253
	/**
254
	 * Checks whether a module is true (dark) or false (light)
255
	 */
256
	public function check(int $x, int $y):bool{
257
		return $this->checkType($x, $y, $this::IS_DARK);
258
	}
259
260
	/**
261
	 * Checks the status neighbouring modules of the given module at ($x, $y) and returns a bitmask with the results.
262
	 *
263
	 * The 8 flags of the bitmask represent the status of each of the neighbouring fields,
264
	 * starting with the lowest bit for top left, going clockwise:
265
	 *
266
	 *   1 2 3
267
	 *   8 # 4
268
	 *   7 6 5
269
	 */
270
	public function checkNeighbours(int $x, int $y, int $M_TYPE_VALUE = null):int{
271
		$bits = 0;
272
273
		foreach($this::neighbours as $bit => $coord){
274
			[$ix, $iy] = $coord;
275
276
			// check if the field is the same type
277
			if($M_TYPE_VALUE !== null && ($this->get($x + $ix, $y + $iy) | $this::IS_DARK) !== ($M_TYPE_VALUE | $this::IS_DARK)){
278
				continue;
279
			}
280
281
			if($this->checkType($x + $ix, $y + $iy, $this::IS_DARK)){
282
				$bits |= $bit;
283
			}
284
		}
285
286
		return $bits;
287
	}
288
289
	/**
290
	 * Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
291
	 *
292
	 * 4 * version + 9 or moduleCount - 8
293
	 */
294
	public function setDarkModule():self{
295
		$this->set(8, $this->moduleCount - 8, true, $this::M_DARKMODULE);
296
297
		return $this;
298
	}
299
300
	/**
301
	 * Draws the 7x7 finder patterns in the corners top left/right and bottom left
302
	 *
303
	 * ISO/IEC 18004:2000 Section 7.3.2
304
	 */
305
	public function setFinderPattern():self{
306
307
		$pos = [
308
			[0, 0], // top left
309
			[$this->moduleCount - 7, 0], // top right
310
			[0, $this->moduleCount - 7], // bottom left
311
		];
312
313
		foreach($pos as $c){
314
			for($y = 0; $y < 7; $y++){
315
				for($x = 0; $x < 7; $x++){
316
					// outer (dark) 7*7 square
317
					if($x === 0 || $x === 6 || $y === 0 || $y === 6){
318
						$this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER);
319
					}
320
					// inner (light) 5*5 square
321
					elseif($x === 1 || $x === 5 || $y === 1 || $y === 5){
322
						$this->set($c[0] + $y, $c[1] + $x, false, $this::M_FINDER);
323
					}
324
					// 3*3 dot
325
					else{
326
						$this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER_DOT);
327
					}
328
				}
329
			}
330
		}
331
332
		return $this;
333
	}
334
335
	/**
336
	 * Draws the separator lines around the finder patterns
337
	 *
338
	 * ISO/IEC 18004:2000 Section 7.3.3
339
	 */
340
	public function setSeparators():self{
341
342
		$h = [
343
			[7, 0],
344
			[$this->moduleCount - 8, 0],
345
			[7, $this->moduleCount - 8],
346
		];
347
348
		$v = [
349
			[7, 7],
350
			[$this->moduleCount - 1, 7],
351
			[7, $this->moduleCount - 8],
352
		];
353
354
		for($c = 0; $c < 3; $c++){
355
			for($i = 0; $i < 8; $i++){
356
				$this->set($h[$c][0]     , $h[$c][1] + $i, false, $this::M_SEPARATOR);
357
				$this->set($v[$c][0] - $i, $v[$c][1]     , false, $this::M_SEPARATOR);
358
			}
359
		}
360
361
		return $this;
362
	}
363
364
365
	/**
366
	 * Draws the 5x5 alignment patterns
367
	 *
368
	 * ISO/IEC 18004:2000 Section 7.3.5
369
	 */
370
	public function setAlignmentPattern():self{
371
		$alignmentPattern = $this->version->getAlignmentPattern();
0 ignored issues
show
Bug introduced by
The method getAlignmentPattern() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

371
		/** @scrutinizer ignore-call */ 
372
  $alignmentPattern = $this->version->getAlignmentPattern();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
372
373
		foreach($alignmentPattern as $y){
374
			foreach($alignmentPattern as $x){
375
376
				// skip existing patterns
377
				if($this->matrix[$y][$x] !== $this::M_NULL){
378
					continue;
379
				}
380
381
				for($ry = -2; $ry <= 2; $ry++){
382
					for($rx = -2; $rx <= 2; $rx++){
383
						$v = ($ry === 0 && $rx === 0) || $ry === 2 || $ry === -2 || $rx === 2 || $rx === -2;
384
385
						$this->set($x + $rx, $y + $ry, $v, $this::M_ALIGNMENT);
386
					}
387
				}
388
389
			}
390
		}
391
392
		return $this;
393
	}
394
395
396
	/**
397
	 * Draws the timing pattern (h/v checkered line between the finder patterns)
398
	 *
399
	 * ISO/IEC 18004:2000 Section 7.3.4
400
	 */
401
	public function setTimingPattern():self{
402
403
		foreach(range(8, $this->moduleCount - 8 - 1) as $i){
404
405
			if($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL){
406
				continue;
407
			}
408
409
			$v = $i % 2 === 0;
410
411
			$this->set($i, 6, $v, $this::M_TIMING); // h
412
			$this->set(6, $i, $v, $this::M_TIMING); // v
413
		}
414
415
		return $this;
416
	}
417
418
	/**
419
	 * Draws the version information, 2x 3x6 pixel
420
	 *
421
	 * ISO/IEC 18004:2000 Section 8.10
422
	 */
423
	public function setVersionNumber():self{
424
		$bits = $this->version->getVersionPattern();
425
426
		if($bits !== null){
427
428
			for($i = 0; $i < 18; $i++){
429
				$a = (int)($i / 3);
430
				$b = $i % 3 + $this->moduleCount - 8 - 3;
431
				$v = (($bits >> $i) & 1) === 1;
432
433
				$this->set($b, $a, $v, $this::M_VERSION); // ne
434
				$this->set($a, $b, $v, $this::M_VERSION); // sw
435
			}
436
437
		}
438
439
		return $this;
440
	}
441
442
	/**
443
	 * Draws the format info along the finder patterns
444
	 *
445
	 * ISO/IEC 18004:2000 Section 8.9
446
	 */
447
	public function setFormatInfo():self{
448
		$bits = $this->eccLevel->getformatPattern($this->maskPattern);
0 ignored issues
show
Bug introduced by
The method getformatPattern() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

448
		/** @scrutinizer ignore-call */ 
449
  $bits = $this->eccLevel->getformatPattern($this->maskPattern);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
It seems like $this->maskPattern can also be of type null; however, parameter $maskPattern of chillerlan\QRCode\Common...vel::getformatPattern() does only seem to accept chillerlan\QRCode\Common\MaskPattern, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

448
		$bits = $this->eccLevel->getformatPattern(/** @scrutinizer ignore-type */ $this->maskPattern);
Loading history...
449
450
		for($i = 0; $i < 15; $i++){
451
			$v = (($bits >> $i) & 1) === 1;
452
453
			if($i < 6){
454
				$this->set(8, $i, $v, $this::M_FORMAT);
455
			}
456
			elseif($i < 8){
457
				$this->set(8, $i + 1, $v, $this::M_FORMAT);
458
			}
459
			else{
460
				$this->set(8, $this->moduleCount - 15 + $i, $v, $this::M_FORMAT);
461
			}
462
463
			if($i < 8){
464
				$this->set($this->moduleCount - $i - 1, 8, $v, $this::M_FORMAT);
465
			}
466
			elseif($i < 9){
467
				$this->set(15 - $i, 8, $v, $this::M_FORMAT);
468
			}
469
			else{
470
				$this->set(15 - $i - 1, 8, $v, $this::M_FORMAT);
471
			}
472
473
		}
474
475
		return $this;
476
	}
477
478
	/**
479
	 * Draws the "quiet zone" of $size around the matrix
480
	 *
481
	 * ISO/IEC 18004:2000 Section 7.3.7
482
	 *
483
	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
484
	 */
485
	public function setQuietZone(int $quietZoneSize):self{
486
487
		if($this->matrix[$this->moduleCount - 1][$this->moduleCount - 1] === $this::M_NULL){
488
			throw new QRCodeDataException('use only after writing data');
489
		}
490
491
		// create a matrix with the new size
492
		$newSize   = $this->moduleCount + ($quietZoneSize * 2);
493
		$newMatrix = $this->createMatrix($newSize, $this::M_QUIETZONE);
494
495
		// copy over the current matrix
496
		for($y = 0; $y < $this->moduleCount; $y++){
497
			for($x = 0; $x < $this->moduleCount; $x++){
498
				$newMatrix[$y + $quietZoneSize][$x + $quietZoneSize] = $this->matrix[$y][$x];
499
			}
500
		}
501
502
		// set the new values
503
		$this->moduleCount = $newSize;
504
		$this->matrix      = $newMatrix;
505
506
		return $this;
507
	}
508
509
	/**
510
	 * Clears a space of $width * $height in order to add a logo or text.
511
	 * If no $height is given, the space will be assumed a square of $width.
512
	 *
513
	 * Additionally, the logo space can be positioned within the QR Code - respecting the main functional patterns -
514
	 * using $startX and $startY. If either of these are null, the logo space will be centered in that direction.
515
	 * ECC level "H" (30%) is required.
516
	 *
517
	 * Please note that adding a logo space minimizes the error correction capacity of the QR Code and
518
	 * created images may become unreadable, especially when printed with a chance to receive damage.
519
	 * Please test thoroughly before using this feature in production.
520
	 *
521
	 * This method should be called from within an output module (after the matrix has been filled with data).
522
	 * Note that there is no restiction on how many times this method could be called on the same matrix instance.
523
	 *
524
	 * @link https://github.com/chillerlan/php-qrcode/issues/52
525
	 *
526
	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
527
	 */
528
	public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
529
530
		// for logos we operate in ECC H (30%) only
531
		if($this->eccLevel->getLevel() !== EccLevel::H){
532
			throw new QRCodeDataException('ECC level "H" required to add logo space');
533
		}
534
535
		if($height === null){
536
			$height = $width;
537
		}
538
539
		// if width and height happen to be negative or 0 (default value), just return - nothing to do
540
		if($width <= 0 || $height <= 0){
541
			return $this; // @codeCoverageIgnore
542
		}
543
544
		// $this->moduleCount includes the quiet zone (if created), we need the QR size here
545
		$length = $this->version->getDimension();
546
547
		// throw if the size is exceeds the qrcode size
548
		if($width > $length || $height > $length){
549
			throw new QRCodeDataException('logo dimensions exceed matrix size');
550
		}
551
552
		// we need uneven sizes to center the logo space, adjust if needed
553
		if($startX === null && ($width % 2) === 0){
554
			$width++;
555
		}
556
557
		if($startY === null && ($height % 2) === 0){
558
			$height++;
559
		}
560
561
		// throw if the logo space exceeds the maximum error correction capacity
562
		if($width * $height > floor($length * $length * 0.2)){
563
			throw new QRCodeDataException('logo space exceeds the maximum error correction capacity');
564
		}
565
566
		// quiet zone size
567
		$qz    = ($this->moduleCount - $length) / 2;
568
		// skip quiet zone and the first 9 rows/columns (finder-, mode-, version- and timing patterns)
569
		$start = $qz + 9;
570
		// skip quiet zone
571
		$end   = $this->moduleCount - $qz;
572
573
		// determine start coordinates
574
		$startX = ($startX !== null ? $startX : ($length - $width) / 2) + $qz;
575
		$startY = ($startY !== null ? $startY : ($length - $height) / 2) + $qz;
576
577
		// clear the space
578
		foreach($this->matrix as $y => $row){
579
			foreach($row as $x => $val){
580
				// out of bounds, skip
581
				if($x < $start || $y < $start ||$x >= $end || $y >= $end){
582
					continue;
583
				}
584
				// a match
585
				if($x >= $startX && $x < ($startX + $width) && $y >= $startY && $y < ($startY + $height)){
586
					$this->set($x, $y, false, $this::M_LOGO);
587
				}
588
			}
589
		}
590
591
		return $this;
592
	}
593
594
	/**
595
	 * Maps the interleaved binary $data on the matrix
596
	 */
597
	public function writeCodewords(BitBuffer $bitBuffer):self{
598
		$data      = (new ReedSolomonEncoder($this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
0 ignored issues
show
Bug introduced by
It seems like $this->version can also be of type null; however, parameter $version of chillerlan\QRCode\Common...nEncoder::__construct() does only seem to accept chillerlan\QRCode\Common\Version, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

598
		$data      = (new ReedSolomonEncoder(/** @scrutinizer ignore-type */ $this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
Loading history...
Bug introduced by
It seems like $this->eccLevel can also be of type null; however, parameter $eccLevel of chillerlan\QRCode\Common...nEncoder::__construct() does only seem to accept chillerlan\QRCode\Common\EccLevel, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

598
		$data      = (new ReedSolomonEncoder($this->version, /** @scrutinizer ignore-type */ $this->eccLevel))->interleaveEcBytes($bitBuffer);
Loading history...
599
		$byteCount = count($data);
600
		$iByte     = 0;
601
		$iBit      = 7;
602
		$direction = true;
603
604
		for($i = $this->moduleCount - 1; $i > 0; $i -= 2){
605
606
			// skip vertical alignment pattern
607
			if($i === 6){
608
				$i--;
609
			}
610
611
			for($count = 0; $count < $this->moduleCount; $count++){
612
				$y = $direction ? $this->moduleCount - 1 - $count : $count;
613
614
				for($col = 0; $col < 2; $col++){
615
					$x = $i - $col;
616
617
					// skip functional patterns
618
					if($this->get($x, $y) !== $this::M_NULL){
619
						continue;
620
					}
621
622
					$v = $iByte < $byteCount && (($data[$iByte] >> $iBit--) & 1) === 1;
623
624
					$this->set($x, $y, $v, $this::M_DATA);
625
626
					if($iBit === -1){
627
						$iByte++;
628
						$iBit = 7;
629
					}
630
				}
631
			}
632
633
			$direction = !$direction; // switch directions
0 ignored issues
show
introduced by
The condition $direction is always true.
Loading history...
634
		}
635
636
		return $this;
637
	}
638
639
	/**
640
	 * Applies/reverses the mask pattern
641
	 *
642
	 * ISO/IEC 18004:2000 Section 8.8.1
643
	 */
644
	public function mask():self{
645
		$mask = $this->maskPattern->getMask();
0 ignored issues
show
Bug introduced by
The method getMask() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

645
		/** @scrutinizer ignore-call */ 
646
  $mask = $this->maskPattern->getMask();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
646
647
		foreach($this->matrix as $y => $row){
648
			foreach($row as $x => $val){
649
				if($mask($x, $y) && ($val & $this::M_DATA) === $this::M_DATA){
650
					$this->flip($x, $y);
651
				}
652
			}
653
		}
654
655
		return $this;
656
	}
657
658
}
659