Passed
Push — main ( f15b0a...7bacc8 )
by smiley
01:55
created

QROutputAbstract   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 36
c 5
b 0
f 1
dl 0
loc 163
rs 10
wmc 16

6 Methods

Rating   Name   Duplication   Size   Complexity  
A saveToFile() 0 8 3
A setMatrixDimensions() 0 4 1
A setModuleValues() 0 8 3
A __construct() 0 6 1
B collectModules() 0 30 7
A toBase64DataURI() 0 2 1
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::size()
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->size();
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->getModuleValue($value)
89
				: $this->getDefaultModuleValue($defaultValue);
90
		}
91
92
	}
93
94
	/**
95
	 * Determines whether the given value is valid
96
	 *
97
	 * @param mixed|null $value
98
	 */
99
	abstract protected function moduleValueIsValid($value):bool;
100
101
	/**
102
	 * Returns the final value for the given input (return value depends on the output module)
103
	 *
104
	 * @param mixed $value
105
	 *
106
	 * @return mixed
107
	 */
108
	abstract protected function getModuleValue($value);
109
110
	/**
111
	 * Returns a default value for either dark or light modules (return value depends on the output module)
112
	 *
113
	 * @return mixed
114
	 */
115
	abstract protected function getDefaultModuleValue(bool $isDark);
116
117
	/**
118
	 * Returns a base64 data URI for the given string and mime type
119
	 */
120
	protected function toBase64DataURI(string $data, string $mime):string{
121
		return sprintf('data:%s;base64,%s', $mime, base64_encode($data));
122
	}
123
124
	/**
125
	 * saves the qr data to a file
126
	 *
127
	 * @see file_put_contents()
128
	 * @see \chillerlan\QRCode\QROptions::cachefile
129
	 *
130
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
131
	 */
132
	protected function saveToFile(string $data, string $file):void{
133
134
		if(!is_writable(dirname($file))){
135
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file));
136
		}
137
138
		if(file_put_contents($file, $data) === false){
139
			throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file));
140
		}
141
	}
142
143
	/**
144
	 * collects the modules per QRMatrix::M_* type and runs a $transform function on each module and
145
	 * returns an array with the transformed modules
146
	 *
147
	 * The transform callback is called with the following parameters:
148
	 *
149
	 *   $x            - current column
150
	 *   $y            - current row
151
	 *   $M_TYPE       - field value
152
	 *   $M_TYPE_LAYER - (possibly modified) field value that acts as layer id
153
	 */
154
	protected function collectModules(Closure $transform):array{
155
		$paths = [];
156
157
		// collect the modules for each type
158
		foreach($this->matrix->matrix() as $y => $row){
159
			foreach($row as $x => $M_TYPE){
160
				$M_TYPE_LAYER = $M_TYPE;
161
162
				if($this->options->connectPaths && $this->matrix->checkTypeNotIn($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::checkTypeNotIn() 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

162
				if($this->options->connectPaths && $this->matrix->checkTypeNotIn($x, $y, /** @scrutinizer ignore-type */ $this->options->excludeFromConnect)){
Loading history...
163
					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
164
					$M_TYPE_LAYER = QRMatrix::M_DATA;
165
166
					if($this->matrix->check($x, $y)){
167
						$M_TYPE_LAYER |= QRMatrix::IS_DARK;
168
					}
169
				}
170
171
				// collect the modules per $M_TYPE
172
				$module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER);
173
174
				if(!empty($module)){
175
					$paths[$M_TYPE_LAYER][] = $module;
176
				}
177
			}
178
		}
179
180
		// beautify output
181
		ksort($paths);
182
183
		return $paths;
184
	}
185
186
}
187