|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Spatie\Image; |
|
4
|
|
|
|
|
5
|
|
|
use FilesystemIterator; |
|
6
|
|
|
use League\Glide\Server; |
|
7
|
|
|
use League\Glide\ServerFactory; |
|
8
|
|
|
use Spatie\Image\Exceptions\CouldNotConvert; |
|
9
|
|
|
use Spatie\Image\Exceptions\DefectiveConfiguration; |
|
10
|
|
|
|
|
11
|
|
|
final class GlideConversion |
|
12
|
|
|
{ |
|
13
|
|
|
/** @var string */ |
|
14
|
|
|
private $inputImage; |
|
15
|
|
|
|
|
16
|
|
|
/** @var string */ |
|
17
|
|
|
private $imageDriver = 'gd'; |
|
18
|
|
|
|
|
19
|
|
|
/** @var string */ |
|
20
|
|
|
private $conversionResult = null; |
|
21
|
|
|
|
|
22
|
|
|
/** @var string */ |
|
23
|
|
|
private $temporaryDirectory = null; |
|
24
|
|
|
|
|
25
|
|
|
public static function create(string $inputImage, $config = []): self |
|
26
|
|
|
{ |
|
27
|
|
|
return new self($inputImage, $config); |
|
|
|
|
|
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
public function setTemporaryDirectory($tempDir) |
|
31
|
|
|
{ |
|
32
|
|
|
if (isset($tempDir)) { |
|
33
|
|
|
if (! is_dir($tempDir)) { |
|
34
|
|
|
try { |
|
35
|
|
|
mkdir($tempDir); |
|
36
|
|
|
} catch (\Exception $e) { |
|
37
|
|
|
throw DefectiveConfiguration::invalidTemporaryDirectory($tempDir); |
|
38
|
|
|
} |
|
39
|
|
|
} |
|
40
|
|
|
|
|
41
|
|
|
if (! self::isValidTemporaryDirectoryLocation($tempDir)) { |
|
42
|
|
|
throw DefectiveConfiguration::invalidTemporaryDirectory($tempDir); |
|
43
|
|
|
} |
|
44
|
|
|
|
|
45
|
|
|
$this->temporaryDirectory = $tempDir; |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
return $this; |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
public function getTemporaryDirectory(): string |
|
52
|
|
|
{ |
|
53
|
|
|
return $this->temporaryDirectory; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
private static function isValidTemporaryDirectoryLocation($dir): bool |
|
57
|
|
|
{ |
|
58
|
|
|
return isset($dir) && is_dir($dir) && is_writable($dir); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
public function __construct(string $inputImage) |
|
62
|
|
|
{ |
|
63
|
|
|
$this->inputImage = $inputImage; |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
public function useImageDriver(string $imageDriver): self |
|
67
|
|
|
{ |
|
68
|
|
|
$this->imageDriver = $imageDriver; |
|
69
|
|
|
|
|
70
|
|
|
return $this; |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
public function performManipulations(Manipulations $manipulations) |
|
74
|
|
|
{ |
|
75
|
|
|
foreach ($manipulations->getManipulationSequence() as $manipulationGroup) { |
|
76
|
|
|
$inputFile = $this->conversionResult ?? $this->inputImage; |
|
77
|
|
|
|
|
78
|
|
|
$watermarkPath = $this->extractWatermarkPath($manipulationGroup); |
|
79
|
|
|
|
|
80
|
|
|
$glideServer = $this->createGlideServer($inputFile, $watermarkPath); |
|
81
|
|
|
|
|
82
|
|
|
$glideServer->setGroupCacheInFolders(false); |
|
83
|
|
|
|
|
84
|
|
|
$this->conversionResult = $this->temporaryDirectory.DIRECTORY_SEPARATOR.$glideServer->makeImage( |
|
85
|
|
|
pathinfo($inputFile, PATHINFO_BASENAME), |
|
86
|
|
|
$this->prepareManipulations($manipulationGroup) |
|
87
|
|
|
); |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
return $this; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* Removes the watermark path from the manipulationGroup and returns it. This way it can be injected into the Glide |
|
95
|
|
|
* server as the `watermarks` path. |
|
96
|
|
|
* |
|
97
|
|
|
* @param $manipulationGroup |
|
98
|
|
|
* |
|
99
|
|
|
* @return null|string |
|
100
|
|
|
*/ |
|
101
|
|
|
private function extractWatermarkPath(&$manipulationGroup) |
|
102
|
|
|
{ |
|
103
|
|
|
if (array_key_exists('watermark', $manipulationGroup)) { |
|
104
|
|
|
$watermarkPath = dirname($manipulationGroup['watermark']); |
|
105
|
|
|
|
|
106
|
|
|
$manipulationGroup['watermark'] = basename($manipulationGroup['watermark']); |
|
107
|
|
|
|
|
108
|
|
|
return $watermarkPath; |
|
109
|
|
|
} |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
private function createGlideServer($inputFile, string $watermarkPath = null): Server |
|
113
|
|
|
{ |
|
114
|
|
|
$config = [ |
|
115
|
|
|
'source' => dirname($inputFile), |
|
116
|
|
|
'cache' => $this->temporaryDirectory, |
|
117
|
|
|
'driver' => $this->imageDriver, |
|
118
|
|
|
]; |
|
119
|
|
|
|
|
120
|
|
|
if ($watermarkPath) { |
|
|
|
|
|
|
121
|
|
|
$config['watermarks'] = $watermarkPath; |
|
122
|
|
|
} |
|
123
|
|
|
|
|
124
|
|
|
return ServerFactory::create($config); |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
public function save(string $outputFile) |
|
128
|
|
|
{ |
|
129
|
|
|
if ($this->conversionResult == '') { |
|
130
|
|
|
copy($this->inputImage, $outputFile); |
|
131
|
|
|
|
|
132
|
|
|
return; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
$conversionResultDirectory = pathinfo($this->conversionResult, PATHINFO_DIRNAME); |
|
136
|
|
|
|
|
137
|
|
|
copy($this->conversionResult, $outputFile); |
|
138
|
|
|
|
|
139
|
|
|
unlink($this->conversionResult); |
|
140
|
|
|
|
|
141
|
|
|
if ($this->directoryIsEmpty($conversionResultDirectory) && $conversionResultDirectory !== '/tmp') { |
|
142
|
|
|
rmdir($conversionResultDirectory); |
|
143
|
|
|
} |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
private function prepareManipulations(array $manipulationGroup): array |
|
147
|
|
|
{ |
|
148
|
|
|
$glideManipulations = []; |
|
149
|
|
|
|
|
150
|
|
|
foreach ($manipulationGroup as $name => $argument) { |
|
151
|
|
|
if ($name !== 'optimize') { |
|
152
|
|
|
$glideManipulations[$this->convertToGlideParameter($name)] = $argument; |
|
153
|
|
|
} |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
return $glideManipulations; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
private function convertToGlideParameter(string $manipulationName): string |
|
160
|
|
|
{ |
|
161
|
|
|
$conversions = [ |
|
162
|
|
|
'width' => 'w', |
|
163
|
|
|
'height' => 'h', |
|
164
|
|
|
'blur' => 'blur', |
|
165
|
|
|
'pixelate' => 'pixel', |
|
166
|
|
|
'crop' => 'fit', |
|
167
|
|
|
'manualCrop' => 'crop', |
|
168
|
|
|
'orientation' => 'or', |
|
169
|
|
|
'flip' => 'flip', |
|
170
|
|
|
'fit' => 'fit', |
|
171
|
|
|
'devicePixelRatio' => 'dpr', |
|
172
|
|
|
'brightness' => 'bri', |
|
173
|
|
|
'contrast' => 'con', |
|
174
|
|
|
'gamma' => 'gam', |
|
175
|
|
|
'sharpen' => 'sharp', |
|
176
|
|
|
'filter' => 'filt', |
|
177
|
|
|
'background' => 'bg', |
|
178
|
|
|
'border' => 'border', |
|
179
|
|
|
'quality' => 'q', |
|
180
|
|
|
'format' => 'fm', |
|
181
|
|
|
'watermark' => 'mark', |
|
182
|
|
|
'watermarkWidth' => 'markw', |
|
183
|
|
|
'watermarkHeight' => 'markh', |
|
184
|
|
|
'watermarkFit' => 'markfit', |
|
185
|
|
|
'watermarkPaddingX' => 'markx', |
|
186
|
|
|
'watermarkPaddingY' => 'marky', |
|
187
|
|
|
'watermarkPosition' => 'markpos', |
|
188
|
|
|
'watermarkOpacity' => 'markalpha', |
|
189
|
|
|
]; |
|
190
|
|
|
|
|
191
|
|
|
if (! isset($conversions[$manipulationName])) { |
|
192
|
|
|
throw CouldNotConvert::unknownManipulation($manipulationName); |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
return $conversions[$manipulationName]; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
private function directoryIsEmpty(string $directory): bool |
|
199
|
|
|
{ |
|
200
|
|
|
$iterator = new FilesystemIterator($directory); |
|
201
|
|
|
|
|
202
|
|
|
return ! $iterator->valid(); |
|
203
|
|
|
} |
|
204
|
|
|
} |
|
205
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more 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.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.