Passed
Push — develop ( 9333dd...574080 )
by BENARD
04:23
created

Picture::downloadPicture()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 2
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
namespace VideoGamesRecords\CoreBundle\File;
4
5
use Exception;
6
use GdImage;
7
8
class Picture
9
{
10
    protected $picture = null;
11
12
    protected array $mimeTypes = [
13
        'bmp'  => 'image/bmp',
14
        'gif'  => 'image/gif',
15
        'jpeg' => 'image/jpeg',
16
        'jpg'  => 'image/jpeg',
17
        'png'  => 'image/png',
18
        'wbmp' => 'image/vnd.wap.wbmp',
19
        'xbm'  => 'image/x-xbitmap',
20
    ];
21
22
    private const EXTENSIONS = [
23
        'gd'   => ['generate' => 'imagegd', 'create' => 'imagecreatefromgd'],
24
        'gd2'  => ['generate' => 'imagegd2', 'create' => 'imagecreatefromgd2'],
25
        'gif'  => ['generate' => 'imagegif', 'create' => 'imagecreatefromgif'],
26
        'jpeg' => ['generate' => 'imagejpeg', 'create' => 'imagecreatefromjpeg'],
27
        'jpg'  => ['generate' => 'imagejpeg', 'create' => 'imagecreatefromjpeg'],
28
        'png'  => ['generate' => 'imagepng', 'create' => 'imagecreatefrompng'],
29
        'wbmp' => ['generate' => 'imagewbmp', 'create' => 'imagecreatefromwbmp'],
30
        'bmp'  => ['generate' => 'imagewbmp', 'create' => 'imagecreatefromwbmp'],
31
        'xbm'  => ['generate' => 'imagexbm', 'create' => 'imagecreatefromxbm']
32
    ];
33
34
    protected array $fonts = [];
35
    protected array $colors = [];
36
37
    protected ?string $activeColor = null;
38
    protected ?string $activeFont = null;
39
40
41
    /**
42
     * Picture constructor.
43
     * @param      $width
44
     * @param      $height
45
     * @param bool $trueColor
46
     */
47
    public function __construct($width, $height, bool $trueColor = true)
48
    {
49
        $width = (int) $width;
50
        $height = (int) $height;
51
52
        if ($trueColor) {
53
            $this->picture = imagecreatetruecolor($width, $height);
54
        } else {
55
            $this->picture = imagecreate($width, $height);
56
        }
57
    }
58
59
    /**
60
     * @param bool    $keepTrueColor
61
     * @param GdImage $picture
62
     * @return self
63
     */
64
    public static function create(bool $keepTrueColor, GdImage $picture): Picture
65
    {
66
        $oSelf = new self(imagesx($picture), imagesy($picture));
67
        if ($keepTrueColor) {
68
            $oSrc = new self(imagesx($picture), imagesy($picture));
69
            $oSrc->setPicture($picture);
70
            $oSelf->copyResized($oSrc, 0, 0);
71
            unset($oSrc);
72
        } else {
73
            $oSelf->setPicture($picture);
74
        }
75
        return $oSelf;
76
    }
77
78
    /**
79
     *
80
     */
81
    public function __destruct()
82
    {
83
        imagedestroy($this->picture);
84
    }
85
86
    /**
87
     * @param $picture
88
     * @return Picture
89
     */
90
    public function setPicture($picture): Picture
91
    {
92
        $this->picture = $picture;
93
        return $this;
94
    }
95
96
    /**
97
     * @return int
98
     */
99
    public function getWidth():int
100
    {
101
        return imagesx($this->picture);
102
    }
103
104
    /**
105
     * @return int
106
     */
107
    public function getHeight(): int
108
    {
109
        return imagesy($this->picture);
110
    }
111
112
    /**
113
     * @return false|resource|null
114
     */
115
    public function getPicture()
116
    {
117
        return $this->picture;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->picture also could return the type GdImage which is incompatible with the documented return type false|null|resource.
Loading history...
118
    }
119
120
    /**
121
     * @param $name
122
     * @return mixed
123
     * @throws Exception
124
     */
125
    public function getColor($name): mixed
126
    {
127
        if (!isset($this->colors[$name])) {
128
            throw new Exception('Unknown color ' . $name . '.');
129
        }
130
        $this->activeColor = $this->colors[$name];
131
        return $this->colors[$name];
132
    }
133
134
    /**
135
     * @param $name
136
     * @return mixed
137
     * @throws Exception
138
     */
139
    public function getFont($name): mixed
140
    {
141
        if (!isset($this->fonts[$name])) {
142
            throw new Exception('Unknown font ' . $name . '.');
143
        }
144
        $this->activeFont = $this->fonts[$name];
145
        return $this->fonts[$name];
146
    }
147
148
    /**
149
     * @param $name
150
     * @param $filePath
151
     * @return Picture
152
     * @throws Exception
153
     */
154
    public function addFont($name, $filePath): Picture
155
    {
156
        $fontPath = realpath($filePath);
157
        if ($fontPath === false) {
158
            throw new Exception('Unable to load font file. The file does not exists.');
159
        }
160
        $this->fonts[$name] = $fontPath;
161
        $this->activeFont = $fontPath;
162
        return $this;
163
    }
164
165
    /**
166
     * @param      $name
167
     * @param      $red
168
     * @param      $green
169
     * @param      $blue
170
     * @param      $alpha
171
     * @return Picture
172
     */
173
    public function addColor($name, $red, $green, $blue, $alpha = null): Picture
174
    {
175
        $red %= 256;
176
        $green %= 256;
177
        $blue %= 256;
178
        if ($alpha !== null) {
179
            $alpha %= 128;
180
            $color = imagecolorallocatealpha($this->picture, $red, $green, $blue, $alpha);
181
        } else {
182
            $color = imagecolorallocate($this->picture, $red, $green, $blue);
183
        }
184
        $this->colors[$name] = $color;
185
        $this->activeColor = $color;
186
        return $this;
187
    }
188
189
    /**
190
     * @param Picture $pic
191
     * @param                   $dstX
192
     * @param                   $dstY
193
     * @param int               $srcX
194
     * @param int               $srcY
195
     * @param null              $dstW
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $dstW is correct as it would always require null to be passed?
Loading history...
196
     * @param null              $dstH
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $dstH is correct as it would always require null to be passed?
Loading history...
197
     * @param null              $srcW
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $srcW is correct as it would always require null to be passed?
Loading history...
198
     * @param null              $srcH
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $srcH is correct as it would always require null to be passed?
Loading history...
199
     * @return Picture
200
     */
201
    public function copyResized(
202
        Picture $pic,
203
        $dstX,
204
        $dstY,
205
        $srcX = 0,
206
        $srcY = 0,
207
        $dstW = null,
208
        $dstH = null,
209
        $srcW = null,
210
        $srcH = null
211
    ): Picture {
212
        $dstW = is_null($dstW) ? $pic->getWidth() : $dstW;
0 ignored issues
show
introduced by
The condition is_null($dstW) is always true.
Loading history...
213
        $dstH = is_null($dstH) ? $pic->getHeight() : $dstH;
0 ignored issues
show
introduced by
The condition is_null($dstH) is always true.
Loading history...
214
        $srcW = is_null($srcW) ? $pic->getWidth() : $srcW;
0 ignored issues
show
introduced by
The condition is_null($srcW) is always true.
Loading history...
215
        $srcH = is_null($srcH) ? $pic->getHeight() : $srcH;
0 ignored issues
show
introduced by
The condition is_null($srcH) is always true.
Loading history...
216
217
        imagecopyresized($this->picture, $pic->getPicture(), $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
218
        return $this;
219
    }
220
221
    /**
222
     * @param      $xA
223
     * @param      $yA
224
     * @param      $xB
225
     * @param      $yB
226
     * @param null $color
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $color is correct as it would always require null to be passed?
Loading history...
227
     * @return Picture
228
     * @throws Exception
229
     */
230
    public function addRectangle($xA, $yA, $xB, $yB, $color = null): Picture
231
    {
232
        if ($color === null) {
0 ignored issues
show
introduced by
The condition $color === null is always true.
Loading history...
233
            $color = $this->activeColor;
234
            if ($color === null) {
235
                throw new Exception('No active color defined.');
236
            }
237
        }
238
        imagefilledrectangle($this->picture, $xA, $yA, $xB, $yB, $color);
0 ignored issues
show
Bug introduced by
$color of type string is incompatible with the type integer expected by parameter $color of imagefilledrectangle(). ( Ignorable by Annotation )

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

238
        imagefilledrectangle($this->picture, $xA, $yA, $xB, $yB, /** @scrutinizer ignore-type */ $color);
Loading history...
239
        return $this;
240
    }
241
242
    /**
243
     * @param      $message
244
     * @param      $size
245
     * @param      $x
246
     * @param      $y
247
     * @param int  $angle
248
     * @param null $color
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $color is correct as it would always require null to be passed?
Loading history...
249
     * @param null $font
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $font is correct as it would always require null to be passed?
Loading history...
250
     * @return Picture
251
     * @throws Exception
252
     */
253
    public function write($message, $size, $x, $y, int $angle = 0, $color = null, $font = null): Picture
254
    {
255
        if ($color === null) {
0 ignored issues
show
introduced by
The condition $color === null is always true.
Loading history...
256
            $color = $this->activeColor;
257
            if ($color === null) {
258
                throw new Exception('No active color defined.');
259
            }
260
        }
261
        if ($font === null) {
0 ignored issues
show
introduced by
The condition $font === null is always true.
Loading history...
262
            $font = $this->activeFont;
263
            if ($font === null) {
264
                throw new Exception('No active font defined.');
265
            }
266
        }
267
        imagettftext($this->picture, $size, $angle, $x, $y, $color, $font, $message);
0 ignored issues
show
Bug introduced by
$color of type string is incompatible with the type integer expected by parameter $color of imagettftext(). ( Ignorable by Annotation )

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

267
        imagettftext($this->picture, $size, $angle, $x, $y, /** @scrutinizer ignore-type */ $color, $font, $message);
Loading history...
268
        return $this;
269
    }
270
271
    /**
272
     * @param      $file
273
     * @param bool $keepTrueColor
274
     * @return Picture
275
     * @throws Exception
276
     */
277
    public static function loadFile($file, bool $keepTrueColor = false): Picture
278
    {
279
        $file = realpath($file);
280
        if ($file === false) {
281
            throw new Exception('Unable to load picture file. The file does not exists.');
282
        }
283
284
        $extension = mb_strtolower(pathinfo($file, PATHINFO_EXTENSION));
0 ignored issues
show
Bug introduced by
It seems like pathinfo($file, VideoGam...ile\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of mb_strtolower() does only seem to accept string, 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

284
        $extension = mb_strtolower(/** @scrutinizer ignore-type */ pathinfo($file, PATHINFO_EXTENSION));
Loading history...
285
286
        if (!array_key_exists($extension, self::EXTENSIONS)) {
287
            throw new Exception('Unknown extension of file when converting to PHP resource.');
288
        }
289
290
        $method = self::getCreateMethod($extension);
291
        $picture = $method($file);
292
293
        return self::create($keepTrueColor, $picture);
294
    }
295
296
    /**
297
     * @param string $data
298
     * @param bool $keepTrueColor
299
     * @return Picture
300
     * @throws Exception
301
     */
302
    public static function loadFileFromStream(string $data, bool $keepTrueColor = false): Picture
303
    {
304
        $picture = imagecreatefromstring($data);
305
306
        return self::create($keepTrueColor, $picture);
0 ignored issues
show
Bug introduced by
It seems like $picture can also be of type resource; however, parameter $picture of VideoGamesRecords\CoreBu...\File\Picture::create() does only seem to accept GdImage, 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

306
        return self::create($keepTrueColor, /** @scrutinizer ignore-type */ $picture);
Loading history...
307
    }
308
309
    /**
310
     * @param        $type
311
     * @param string $filename
312
     * @throws Exception
313
     */
314
    public function downloadPicture($type, string $filename): void
315
    {
316
        header('Content-Disposition: "attachement"; filename="' . $filename . '"');
317
        $this->showPicture($type);
318
    }
319
320
    /**
321
     * @param $type
322
     * @throws Exception
323
     */
324
    public function showPicture($type)
325
    {
326
        $method = $this->getGererateMethod($type);
327
        $contentType = $this->getMimeType($type);
328
329
        header('Content-Type: ' . $contentType);
330
        $method($this->picture);
331
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
332
    }
333
334
    /**
335
     * @param $filename
336
     * @return $this
337
     * @throws Exception
338
     */
339
    public function savePicture($filename): static
340
    {
341
        $sExtension = pathinfo($filename, PATHINFO_EXTENSION);
342
        $method = $this->getGererateMethod($sExtension);
343
344
        $method($this->picture, $filename);
345
        return $this;
346
    }
347
348
    /**
349
     * @param $type
350
     * @return string
351
     * @throws Exception
352
     */
353
    protected function getGererateMethod($type): string
354
    {
355
        if (!array_key_exists($type, self::EXTENSIONS)) {
356
            throw new Exception('Unknown picture type.');
357
        }
358
        return self::EXTENSIONS[$type]['generate'];
359
    }
360
361
    /**
362
     * @param $type
363
     * @return string
364
     * @throws Exception
365
     */
366
    protected static function getCreateMethod($type): string
367
    {
368
        if (!array_key_exists($type, self::EXTENSIONS)) {
369
            throw new Exception('Unknown picture type.');
370
        }
371
        return self::EXTENSIONS[$type]['create'];
372
    }
373
374
    /**
375
     * @param $extension
376
     * @return mixed|string
377
     */
378
    public function getMimeType($extension): mixed
379
    {
380
        if (!isset($this->mimeTypes[$extension])) {
381
            return 'application/octet-stream';
382
        }
383
        return $this->mimeTypes[$extension];
384
    }
385
}
386