Passed
Push — main ( 6b9f6f...a1f051 )
by smiley
02:03
created

QROutputAbstract::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 6
rs 10
1
<?php
2
/**
3
 * Class QROutputAbstract
4
 *
5
 * @created      09.12.2015
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2015 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\QRCode\Output;
12
13
use chillerlan\QRCode\Data\QRMatrix;
14
use chillerlan\Settings\SettingsContainerInterface;
15
use Closure;
16
use function base64_encode, dirname, file_put_contents, is_writable, ksort, sprintf;
17
18
/**
19
 * common output abstract
20
 */
21
abstract class QROutputAbstract implements QROutputInterface{
22
23
	/**
24
	 * the current size of the QR matrix
25
	 *
26
	 * @see \chillerlan\QRCode\Data\QRMatrix::getSize()
27
	 */
28
	protected int $moduleCount;
29
30
	/**
31
	 * the current scaling for a QR pixel
32
	 *
33
	 * @see \chillerlan\QRCode\QROptions::$scale
34
	 */
35
	protected int $scale;
36
37
	/**
38
	 * the side length of the QR image (modules * scale)
39
	 */
40
	protected int $length;
41
42
	/**
43
	 * an (optional) array of color values for the several QR matrix parts
44
	 */
45
	protected array $moduleValues;
46
47
	/**
48
	 * the (filled) data matrix object
49
	 */
50
	protected QRMatrix $matrix;
51
52
	/**
53
	 * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
54
	 */
55
	protected SettingsContainerInterface $options;
56
57
	/**
58
	 * QROutputAbstract constructor.
59
	 */
60
	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
61
		$this->options = $options;
62
		$this->matrix  = $matrix;
63
64
		$this->setMatrixDimensions();
65
		$this->setModuleValues();
66
	}
67
68
	/**
69
	 * Sets/updates the matrix dimensions
70
	 *
71
	 * Call this method if you modify the matrix from within your custom module in case the dimensions have been changed
72
	 */
73
	protected function setMatrixDimensions():void{
74
		$this->moduleCount = $this->matrix->getSize();
75
		$this->scale       = $this->options->scale;
76
		$this->length      = ($this->moduleCount * $this->scale);
77
	}
78
79
	/**
80
	 * Sets the initial module values
81
	 */
82
	protected function setModuleValues():void{
83
84
		foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
85
			$value = ($this->options->moduleValues[$M_TYPE] ?? null);
86
87
			$this->moduleValues[$M_TYPE] = $this::moduleValueIsValid($value)
88
				? $this->prepareModuleValue($value)
89
				: $this->getDefaultModuleValue($defaultValue);
90
		}
91
92
	}
93
94
	/**
95
	 * Returns the prepared value for the given $M_TYPE
96
	 *
97
	 * @return mixed|null return value depends on the output class
98
	 */
99
	protected function getModuleValue(int $M_TYPE){
100
		return ($this->moduleValues[$M_TYPE] ?? null);
101
	}
102
103
	/**
104
	 * Returns the prepared module value at the given coordinate [$x, $y] (convenience)
105
	 *
106
	 * @return mixed|null
107
	 */
108
	protected function getModuleValueAt(int $x, int $y){
109
		return $this->getModuleValue($this->matrix->get($x, $y));
110
	}
111
112
	/**
113
	 * Prepares the value for the given input ()
114
	 *
115
	 * @param mixed $value
116
	 *
117
	 * @return mixed|null return value depends on the output class
118
	 */
119
	abstract protected function prepareModuleValue($value);
120
121
	/**
122
	 * Returns a default value for either dark or light modules
123
	 *
124
	 * @return mixed|null return value depends on the output class
125
	 */
126
	abstract protected function getDefaultModuleValue(bool $isDark);
127
128
	/**
129
	 * Returns a base64 data URI for the given string and mime type
130
	 */
131
	protected function toBase64DataURI(string $data, string $mime):string{
132
		return sprintf('data:%s;base64,%s', $mime, base64_encode($data));
133
	}
134
135
	/**
136
	 * Saves the qr $data to a $file. If $file is null, nothing happens.
137
	 *
138
	 * @see file_put_contents()
139
	 * @see \chillerlan\QRCode\QROptions::$cachefile
140
	 *
141
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
142
	 */
143
	protected function saveToFile(string $data, string $file = null):void{
144
145
		if($file === null){
146
			return;
147
		}
148
149
		if(!is_writable(dirname($file))){
150
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file));
151
		}
152
153
		if(file_put_contents($file, $data) === false){
154
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file));
155
		}
156
	}
157
158
	/**
159
	 * collects the modules per QRMatrix::M_* type and runs a $transform function on each module and
160
	 * returns an array with the transformed modules
161
	 *
162
	 * The transform callback is called with the following parameters:
163
	 *
164
	 *   $x            - current column
165
	 *   $y            - current row
166
	 *   $M_TYPE       - field value
167
	 *   $M_TYPE_LAYER - (possibly modified) field value that acts as layer id
168
	 */
169
	protected function collectModules(Closure $transform):array{
170
		$paths = [];
171
172
		// collect the modules for each type
173
		for($y = 0; $y < $this->moduleCount; $y++){
174
			for($x = 0; $x < $this->moduleCount; $x++){
175
				$M_TYPE       = $this->matrix->get($x, $y);
176
				$M_TYPE_LAYER = $M_TYPE;
177
178
				if($this->options->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)){
0 ignored issues
show
Bug introduced by
It seems like $this->options->excludeFromConnect can also be of type null; however, parameter $M_TYPES of chillerlan\QRCode\Data\QRMatrix::checkTypeIn() does only seem to accept array, 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

178
				if($this->options->connectPaths && !$this->matrix->checkTypeIn($x, $y, /** @scrutinizer ignore-type */ $this->options->excludeFromConnect)){
Loading history...
179
					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
180
					$M_TYPE_LAYER = QRMatrix::M_DATA;
181
182
					if($this->matrix->check($x, $y)){
183
						$M_TYPE_LAYER |= QRMatrix::IS_DARK;
184
					}
185
				}
186
187
				// collect the modules per $M_TYPE
188
				$module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER);
189
190
				if(!empty($module)){
191
					$paths[$M_TYPE_LAYER][] = $module;
192
				}
193
			}
194
		}
195
196
		// beautify output
197
		ksort($paths);
198
199
		return $paths;
200
	}
201
202
}
203