Passed
Push — master ( 514bca...891b04 )
by smiley
02:57
created

QRImage   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 1
Metric Value
eloc 56
dl 0
loc 147
rs 10
c 6
b 0
f 1
wmc 20

7 Methods

Rating   Name   Duplication   Size   Complexity  
A setModuleValues() 0 12 5
A jpg() 0 7 2
A gif() 0 2 1
A png() 0 7 2
A dump() 0 27 6
A setPixel() 0 8 1
A dumpImage() 0 25 3
1
<?php
2
/**
3
 * Class QRImage
4
 *
5
 * @filesource   QRImage.php
6
 * @created      05.12.2015
7
 * @package      chillerlan\QRCode\Output
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode\Output;
14
15
use chillerlan\QRCode\QRCode;
16
use Exception;
17
18
use function array_values, base64_encode, call_user_func, count, imagecolorallocate, imagecolortransparent,
19
	imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array,
20
	is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf;
21
22
/**
23
 * Converts the matrix into GD images, raw or base64 output
24
 * requires ext-gd
25
 * @link http://php.net/manual/book.image.php
26
 */
27
class QRImage extends QROutputAbstract{
28
29
	protected const TRANSPARENCY_TYPES = [
30
		QRCode::OUTPUT_IMAGE_PNG,
31
		QRCode::OUTPUT_IMAGE_GIF,
32
	];
33
34
	protected string $defaultMode = QRCode::OUTPUT_IMAGE_PNG;
35
36
	/**
37
	 * @see imagecreatetruecolor()
38
	 * @var resource
39
	 */
40
	protected $image;
41
42
	/**
43
	 * @inheritDoc
44
	 */
45
	protected function setModuleValues():void{
46
47
		foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
48
			$v = $this->options->moduleValues[$M_TYPE] ?? null;
49
50
			if(!is_array($v) || count($v) < 3){
51
				$this->moduleValues[$M_TYPE] = $defaultValue
52
					? [0, 0, 0]
53
					: [255, 255, 255];
54
			}
55
			else{
56
				$this->moduleValues[$M_TYPE] = array_values($v);
57
			}
58
59
		}
60
61
	}
62
63
	/**
64
	 * @inheritDoc
65
	 */
66
	public function dump(string $file = null):string{
67
		$this->image = imagecreatetruecolor($this->length, $this->length);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatetruecolor($th...>length, $this->length) can also be of type false. However, the property $image is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
68
69
		// avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect
70
		// https://stackoverflow.com/a/10455217
71
		$tbg = $this->options->imageTransparencyBG;
72
		$background  = imagecolorallocate($this->image, ...$tbg);
0 ignored issues
show
Bug introduced by
The call to imagecolorallocate() has too few arguments starting with green. ( Ignorable by Annotation )

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

72
		$background  = /** @scrutinizer ignore-call */ imagecolorallocate($this->image, ...$tbg);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like $this->image can also be of type false; however, parameter $image of imagecolorallocate() does only seem to accept resource, 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

72
		$background  = imagecolorallocate(/** @scrutinizer ignore-type */ $this->image, ...$tbg);
Loading history...
73
74
		if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
75
			imagecolortransparent($this->image, $background);
0 ignored issues
show
Bug introduced by
It seems like $this->image can also be of type false; however, parameter $image of imagecolortransparent() does only seem to accept resource, 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

75
			imagecolortransparent(/** @scrutinizer ignore-type */ $this->image, $background);
Loading history...
76
		}
77
78
		imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $background);
0 ignored issues
show
Bug introduced by
It seems like $this->image can also be of type false; however, parameter $image of imagefilledrectangle() does only seem to accept resource, 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

78
		imagefilledrectangle(/** @scrutinizer ignore-type */ $this->image, 0, 0, $this->length, $this->length, $background);
Loading history...
79
80
		foreach($this->matrix->matrix() as $y => $row){
81
			foreach($row as $x => $M_TYPE){
82
				$this->setPixel($x, $y, $this->moduleValues[$M_TYPE]);
83
			}
84
		}
85
86
		$imageData = $this->dumpImage($file);
87
88
		if((bool)$this->options->imageBase64){
89
			$imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData));
90
		}
91
92
		return $imageData;
93
	}
94
95
	/**
96
	 *
97
	 */
98
	protected function setPixel(int $x, int $y, array $rgb):void{
99
		imagefilledrectangle(
100
			$this->image,
101
			$x * $this->scale,
102
			$y * $this->scale,
103
			($x + 1) * $this->scale,
104
			($y + 1) * $this->scale,
105
			imagecolorallocate($this->image, ...$rgb)
106
		);
107
	}
108
109
	/**
110
	 * @param string|null $file
111
	 *
112
	 * @return string
113
114
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
115
	 */
116
	protected function dumpImage(string $file = null):string{
117
		$file ??= $this->options->cachefile;
118
119
		ob_start();
120
121
		try{
122
			call_user_func([$this, $this->outputMode ?? $this->defaultMode]);
123
		}
124
		// not going to cover edge cases
125
		// @codeCoverageIgnoreStart
126
		catch(Exception $e){
127
			throw new QRCodeOutputException($e->getMessage());
128
		}
129
		// @codeCoverageIgnoreEnd
130
131
		$imageData = ob_get_contents();
132
		imagedestroy($this->image);
133
134
		ob_end_clean();
135
136
		if($file !== null){
137
			$this->saveToFile($imageData, $file);
138
		}
139
140
		return $imageData;
141
	}
142
143
	/**
144
	 * @return void
145
	 */
146
	protected function png():void{
147
		imagepng(
148
			$this->image,
149
			null,
150
			in_array($this->options->pngCompression, range(-1, 9), true)
151
				? $this->options->pngCompression
152
				: -1
153
		);
154
	}
155
156
	/**
157
	 * Jiff - like... JitHub!
158
	 * @return void
159
	 */
160
	protected function gif():void{
161
		imagegif($this->image);
162
	}
163
164
	/**
165
	 * @return void
166
	 */
167
	protected function jpg():void{
168
		imagejpeg(
169
			$this->image,
170
			null,
171
			in_array($this->options->jpegQuality, range(0, 100), true)
172
				? $this->options->jpegQuality
173
				: 85
174
		);
175
	}
176
177
}
178