1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Styx::Image - Provides an Interface to the GD-Library for image manipulation |
4
|
|
|
* |
5
|
|
|
* @package Styx |
6
|
|
|
* @subpackage Utility |
7
|
|
|
* |
8
|
|
|
* @license MIT-style License |
9
|
|
|
* @author Christoph Pojer <[email protected]> |
10
|
|
|
* |
11
|
|
|
* @link http://www.bin-co.com/php/scripts/classes/gd_image/ Based on work by "Binny V A" |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
class Image |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* The path to the image file |
18
|
|
|
* |
19
|
|
|
* @var string |
20
|
|
|
*/ |
21
|
|
|
private $file; |
22
|
|
|
/** |
23
|
|
|
* The image resource |
24
|
|
|
* |
25
|
|
|
* @var resource |
26
|
|
|
*/ |
27
|
|
|
private $image; |
28
|
|
|
/** |
29
|
|
|
* Metadata regarding the image |
30
|
|
|
* |
31
|
|
|
* @var array |
32
|
|
|
*/ |
33
|
|
|
private $meta; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @param string $file The path to the image file |
37
|
|
|
*/ |
38
|
|
|
public function __construct($file) |
39
|
|
|
{ |
40
|
|
|
$file = realpath($file); |
41
|
|
|
if (!file_exists($file)) { |
42
|
|
|
return; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
$this->file = $file; |
46
|
|
|
$img = getimagesize($file); |
47
|
|
|
|
48
|
|
|
$this->meta = [ |
49
|
|
|
'width' => $img[0], |
50
|
|
|
'height' => $img[1], |
51
|
|
|
'mime' => $img['mime'], |
52
|
|
|
'ext' => end(explode('/', $img['mime'])), |
53
|
|
|
]; |
54
|
|
|
if ('jpg' == $this->meta['ext']) { |
55
|
|
|
$this->meta['ext'] = 'jpeg'; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
if (!in_array($this->meta['ext'], ['gif', 'png', 'jpeg'])) { |
59
|
|
|
return; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
if (in_array($this->meta['ext'], ['gif', 'png'])) { |
63
|
|
|
$this->image = $this->create(); |
64
|
|
|
|
65
|
|
|
$fn = 'imagecreatefrom' . $this->meta['ext']; |
66
|
|
|
$original = $fn($file); |
67
|
|
|
imagecopyresampled($this->image, $original, 0, 0, 0, 0, $this->meta['width'], $this->meta['height'], $this->meta['width'], $this->meta['height']); |
68
|
|
|
} else { |
69
|
|
|
$this->image = imagecreatefromjpeg($file); |
|
|
|
|
70
|
|
|
} |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
public function __destruct() |
74
|
|
|
{ |
75
|
|
|
if (!empty($this->image)) { |
76
|
|
|
imagedestroy($this->image); |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Returns the size of the image |
82
|
|
|
* |
83
|
|
|
* @return array |
84
|
|
|
*/ |
85
|
|
|
public function getSize() |
86
|
|
|
{ |
87
|
|
|
return [ |
88
|
|
|
'width' => $this->meta['width'], |
89
|
|
|
'height' => $this->meta['height'], |
90
|
|
|
]; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Creates a new, empty image with the desired size |
95
|
|
|
* |
96
|
|
|
* @param int $x |
97
|
|
|
* @param int $y |
98
|
|
|
* @param string $ext |
99
|
|
|
* @return resource |
100
|
|
|
*/ |
101
|
|
|
private function create($x = null, $y = null, $ext = null) |
102
|
|
|
{ |
103
|
|
|
if (!$x) { |
|
|
|
|
104
|
|
|
$x = $this->meta['width']; |
105
|
|
|
} |
106
|
|
|
if (!$y) { |
|
|
|
|
107
|
|
|
$y = $this->meta['height']; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$image = imagecreatetruecolor($x, $y); |
111
|
|
|
if (!$ext) { |
112
|
|
|
$ext = $this->meta['ext']; |
113
|
|
|
} |
114
|
|
|
if ('png' == $ext) { |
115
|
|
|
imagealphablending($image, false); |
116
|
|
|
imagefilledrectangle($image, 0, 0, $x, $y, imagecolorallocatealpha($image, 0, 0, 0, 127)); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
return $image; |
|
|
|
|
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Replaces the image resource with the given parameter |
124
|
|
|
* |
125
|
|
|
* @param resource $new |
126
|
|
|
*/ |
127
|
|
|
private function set($new) |
128
|
|
|
{ |
129
|
|
|
imagedestroy($this->image); |
130
|
|
|
$this->image = $new; |
131
|
|
|
|
132
|
|
|
$this->meta['width'] = imagesx($this->image); |
133
|
|
|
$this->meta['height'] = imagesy($this->image); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Returns the path to the image file |
138
|
|
|
* |
139
|
|
|
* @return string |
140
|
|
|
*/ |
141
|
|
|
public function getPathname() |
142
|
|
|
{ |
143
|
|
|
return $this->file; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Rotates the image by the given angle |
148
|
|
|
* |
149
|
|
|
* @param int $angle |
150
|
|
|
* @param array $bgcolor An indexed array with red/green/blue/alpha values |
151
|
|
|
* @return Image |
152
|
|
|
*/ |
153
|
|
|
public function rotate($angle, $bgcolor = null) |
154
|
|
|
{ |
155
|
|
|
if (empty($this->image) || !$angle || $angle >= 360) { |
156
|
|
|
return $this; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
$this->set(imagerotate($this->image, $angle, is_array($bgcolor) ? imagecolorallocatealpha($this->image, $bgcolor[0], $bgcolor[1], $bgcolor[2], !empty($bgcolor[3]) ? $bgcolor[3] : null) : $bgcolor)); |
|
|
|
|
160
|
|
|
|
161
|
|
|
return $this; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Resizes the image to the given size, automatically calculates |
166
|
|
|
* the new ratio if parameter {@link $ratio} is set to true |
167
|
|
|
* |
168
|
|
|
* @param int $x |
169
|
|
|
* @param int $y |
170
|
|
|
* @param bool $ratio |
171
|
|
|
* @return Image |
172
|
|
|
*/ |
173
|
|
|
public function resize($x = null, $y = null, $ratio = true) |
174
|
|
|
{ |
175
|
|
|
if (empty($this->image) || (!$x && !$y)) { |
|
|
|
|
176
|
|
|
return $this; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
if (!$y) { |
|
|
|
|
180
|
|
|
$y = $ratio ? $this->meta['height'] * $x / $this->meta['width'] : $this->meta['height']; |
181
|
|
|
} |
182
|
|
|
if (!$x) { |
|
|
|
|
183
|
|
|
$x = $ratio ? $this->meta['width'] * $y / $this->meta['height'] : $this->meta['width']; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$new = $this->create($x, $y); |
187
|
|
|
imagecopyresampled($new, $this->image, 0, 0, 0, 0, $x, $y, $this->meta['width'], $this->meta['height']); |
188
|
|
|
$this->set($new); |
189
|
|
|
|
190
|
|
|
return $this; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Crops the image. The values are given like margin/padding values in css |
195
|
|
|
* |
196
|
|
|
* <b>Example</b> |
197
|
|
|
* <ul> |
198
|
|
|
* <li>crop(10) - Crops by 10px on all sides</li> |
199
|
|
|
* <li>crop(10, 5) - Crops by 10px on top and bottom and by 5px on left and right sides</li> |
200
|
|
|
* <li>crop(10, 5, 5) - Crops by 10px on top and by 5px on left, right and bottom sides</li> |
201
|
|
|
* <li>crop(10, 5, 3, 2) - Crops by 10px on top, 5px by right, 3px by bottom and 2px by left sides</li> |
202
|
|
|
* </ul> |
203
|
|
|
* |
204
|
|
|
* @param int $top |
205
|
|
|
* @param int $right |
206
|
|
|
* @param int $bottom |
207
|
|
|
* @param int $left |
208
|
|
|
* @return Image |
209
|
|
|
*/ |
210
|
|
|
public function crop($top, $right = null, $bottom = null, $left = null) |
211
|
|
|
{ |
212
|
|
|
if (empty($this->image)) { |
213
|
|
|
return $this; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
if (!is_numeric($right) && !is_numeric($bottom) && !is_numeric($left)) { |
217
|
|
|
$right = $bottom = $left = $top; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
if (!is_numeric($bottom) && !is_numeric($left)) { |
221
|
|
|
$bottom = $top; |
222
|
|
|
$left = $right; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
if (!is_numeric($left)) { |
226
|
|
|
$left = $right; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
$x = $this->meta['width'] - $left - $right; |
230
|
|
|
$y = $this->meta['height'] - $top - $bottom; |
231
|
|
|
|
232
|
|
|
if ($x < 0 || $y < 0) { |
233
|
|
|
return $this; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
$new = $this->create($x, $y); |
237
|
|
|
imagecopy($new, $this->image, 0, 0, $left, $top, $x, $y); |
238
|
|
|
$this->set($new); |
239
|
|
|
|
240
|
|
|
return $this; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Flips the image horizontally or vertically. To Flip both just use ->rotate(180) |
245
|
|
|
* |
246
|
|
|
* @param string $type Either horizontal or vertical |
247
|
|
|
* @return Image |
248
|
|
|
* @see Image::rotate() |
249
|
|
|
*/ |
250
|
|
|
public function flip($type) |
251
|
|
|
{ |
252
|
|
|
if (empty($this->image) || !in_array($type, ['horizontal', 'vertical'])) { |
253
|
|
|
return $this; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$new = $this->create(); |
257
|
|
|
|
258
|
|
|
if ('horizontal' == $type) { |
259
|
|
|
for ($x = 0; $x < $this->meta['width']; $x++) { |
260
|
|
|
imagecopy($new, $this->image, $this->meta['width'] - $x - 1, 0, $x, 0, 1, $this->meta['height']); |
261
|
|
|
} |
262
|
|
|
} elseif ('vertical' == $type) { |
263
|
|
|
for ($y = 0; $y < $this->meta['height']; $y++) { |
264
|
|
|
imagecopy($new, $this->image, 0, $this->meta['height'] - $y - 1, 0, $y, $this->meta['width'], 1); |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$this->set($new); |
269
|
|
|
|
270
|
|
|
return $this; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Stores the image in the desired directory or outputs it |
275
|
|
|
* |
276
|
|
|
* @param string $ext |
277
|
|
|
* @param string $file |
278
|
|
|
*/ |
279
|
|
|
private function process($ext = null, $file = null) |
280
|
|
|
{ |
281
|
|
|
if (!$ext) { |
282
|
|
|
$ext = $this->meta['ext']; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
if ('png' == $ext) { |
286
|
|
|
imagesavealpha($this->image, true); |
287
|
|
|
} |
288
|
|
|
$fn = 'image' . $ext; |
289
|
|
|
$fn($this->image, $file); |
290
|
|
|
|
291
|
|
|
// If there is a new filename change the internal name too |
292
|
|
|
if ($file) { |
293
|
|
|
$this->file = $file; |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Saves the image to the given path |
299
|
|
|
* |
300
|
|
|
* @param string $file Leave empty to replace the original file |
301
|
|
|
* @return Image |
302
|
|
|
*/ |
303
|
|
|
public function save($file = null) |
304
|
|
|
{ |
305
|
|
|
if (empty($this->image)) { |
306
|
|
|
return $this; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
if (!$file) { |
310
|
|
|
$file = $this->file; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); |
|
|
|
|
314
|
|
|
if (!$ext) { |
315
|
|
|
$file .= '.' . $this->meta['ext']; |
316
|
|
|
$ext = $this->meta['ext']; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
if ('jpg' == $ext) { |
320
|
|
|
$ext = 'jpeg'; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
if (!in_array($ext, ['png', 'jpeg', 'gif'])) { |
324
|
|
|
return $this; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
$this->process($ext, $file); |
328
|
|
|
|
329
|
|
|
return $this; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Outputs the manipulated image |
334
|
|
|
* |
335
|
|
|
* @return Image |
336
|
|
|
*/ |
337
|
|
|
public function show() |
338
|
|
|
{ |
339
|
|
|
if (empty($this->image)) { |
340
|
|
|
return $this; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
header('Content-type: ' . $this->meta['mime']); |
344
|
|
|
$this->process(); |
345
|
|
|
|
346
|
|
|
return $this; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
} |
350
|
|
|
|
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 theid
property of an instance of theAccount
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.