QROutputAbstract::getModuleValue()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
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, null if $moduleValues[$M_TYPE] doesn't exist
98
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
99
	 */
100
	protected function getModuleValue(int $M_TYPE){
101
102
		if(!isset($this->moduleValues[$M_TYPE])){
103
			return null;
104
#			throw new QRCodeOutputException(sprintf('invalid M_TYPE: %024b', $M_TYPE));
105
		}
106
107
		return $this->moduleValues[$M_TYPE];
108
	}
109
110
	/**
111
	 * Returns the prepared module value at the given coordinate [$x, $y] (convenience)
112
	 *
113
	 * @return mixed|null
114
	 */
115
	protected function getModuleValueAt(int $x, int $y){
116
		return $this->getModuleValue($this->matrix->get($x, $y));
117
	}
118
119
	/**
120
	 * Prepares the value for the given input ()
121
	 *
122
	 * @param mixed $value
123
	 *
124
	 * @return mixed|null return value depends on the output class
125
	 */
126
	abstract protected function prepareModuleValue($value);
127
128
	/**
129
	 * Returns a default value for either dark or light modules
130
	 *
131
	 * @return mixed|null return value depends on the output class
132
	 */
133
	abstract protected function getDefaultModuleValue(bool $isDark);
134
135
	/**
136
	 * Returns a base64 data URI for the given string and mime type
137
	 */
138
	protected function toBase64DataURI(string $data, string $mime):string{
139
		return sprintf('data:%s;base64,%s', $mime, base64_encode($data));
140
	}
141
142
	/**
143
	 * Saves the qr $data to a $file. If $file is null, nothing happens.
144
	 *
145
	 * @see file_put_contents()
146
	 * @see \chillerlan\QRCode\QROptions::$cachefile
147
	 *
148
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
149
	 */
150
	protected function saveToFile(string $data, string $file = null):void{
151
152
		if($file === null){
153
			return;
154
		}
155
156
		if(!is_writable(dirname($file))){
157
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file));
158
		}
159
160
		if(file_put_contents($file, $data) === false){
161
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file));
162
		}
163
	}
164
165
	/**
166
	 * collects the modules per QRMatrix::M_* type and runs a $transform function on each module and
167
	 * returns an array with the transformed modules
168
	 *
169
	 * The transform callback is called with the following parameters:
170
	 *
171
	 *   $x            - current column
172
	 *   $y            - current row
173
	 *   $M_TYPE       - field value
174
	 *   $M_TYPE_LAYER - (possibly modified) field value that acts as layer id
175
	 */
176
	protected function collectModules(Closure $transform):array{
177
		$paths = [];
178
179
		// collect the modules for each type
180
		for($y = 0; $y < $this->moduleCount; $y++){
181
			for($x = 0; $x < $this->moduleCount; $x++){
182
				$M_TYPE       = $this->matrix->get($x, $y);
183
				$M_TYPE_LAYER = $M_TYPE;
184
185
				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

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