Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.24% |
60 / 63 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
ServeConvertedWebP | |
95.24% |
60 / 63 |
|
75.00% |
3 / 4 |
20 | |
0.00% |
0 / 1 |
processOptions | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 | |||
serveOriginal | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
serveDestination | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
warningHandler | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
serve | |
92.68% |
38 / 41 |
|
0.00% |
0 / 1 |
13.07 |
1 | <?php |
2 | namespace WebPConvert\Serve; |
3 | |
4 | use WebPConvert\Convert\Exceptions\ConversionFailedException; |
5 | use WebPConvert\Helpers\InputValidator; |
6 | use WebPConvert\Helpers\MimeType; |
7 | use WebPConvert\Helpers\PathChecker; |
8 | use WebPConvert\Serve\Exceptions\ServeFailedException; |
9 | use WebPConvert\Serve\Header; |
10 | use WebPConvert\Serve\Report; |
11 | use WebPConvert\Serve\ServeFile; |
12 | use WebPConvert\Options\ArrayOption; |
13 | use WebPConvert\Options\BooleanOption; |
14 | use WebPConvert\Options\Options; |
15 | use WebPConvert\Options\SensitiveArrayOption; |
16 | use WebPConvert\Options\Exceptions\InvalidOptionTypeException; |
17 | use WebPConvert\Options\Exceptions\InvalidOptionValueException; |
18 | use WebPConvert\WebPConvert; |
19 | |
20 | /** |
21 | * Serve a converted webp image. |
22 | * |
23 | * The webp that is served might end up being one of these: |
24 | * - a fresh convertion |
25 | * - the destionation |
26 | * - the original |
27 | * |
28 | * Exactly which is a decision based upon options, file sizes and file modification dates |
29 | * (see the serve method of this class for details) |
30 | * |
31 | * @package WebPConvert |
32 | * @author Bjørn Rosell <it@rosell.dk> |
33 | * @since Class available since Release 2.0.0 |
34 | */ |
35 | class ServeConvertedWebP |
36 | { |
37 | |
38 | /** |
39 | * Process options. |
40 | * |
41 | * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid |
42 | * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid |
43 | * @param array $options |
44 | */ |
45 | private static function processOptions($options) |
46 | { |
47 | $options2 = new Options(); |
48 | $options2->addOptions( |
49 | new BooleanOption('reconvert', false), |
50 | new BooleanOption('serve-original', false), |
51 | new BooleanOption('show-report', false), |
52 | new BooleanOption('suppress-warnings', true), |
53 | new BooleanOption('redirect-to-self-instead-of-serving', false), |
54 | new ArrayOption('serve-image', []), |
55 | new SensitiveArrayOption('convert', []) |
56 | ); |
57 | foreach ($options as $optionId => $optionValue) { |
58 | $options2->setOrCreateOption($optionId, $optionValue); |
59 | } |
60 | $options2->check(); |
61 | return $options2->getOptions(); |
62 | } |
63 | |
64 | /** |
65 | * Serve original file (source). |
66 | * |
67 | * @param string $source path to source file |
68 | * @param array $serveImageOptions (optional) options for serving an image |
69 | * Supported options: |
70 | * - All options supported by ServeFile::serve() |
71 | * @throws ServeFailedException if source is not an image or mime type cannot be determined |
72 | * @return void |
73 | */ |
74 | public static function serveOriginal($source, $serveImageOptions = []) |
75 | { |
76 | // PS: We do not use InputValidator::checkSource($source) because we want to be |
77 | // a bit more lenient here and allow any image to be served (even though ie webp does not |
78 | // qualify for being used as a source when converting) |
79 | |
80 | // Check that the filename is ok (no control chars, streamwrappers), and that the file exists |
81 | // and is not a dir |
82 | PathChecker::checkSourcePath($source); |
83 | |
84 | $contentType = MimeType::getMimeTypeDetectionResult($source); |
85 | if (is_null($contentType)) { |
86 | throw new ServeFailedException('Rejecting to serve original (mime type cannot be determined)'); |
87 | } elseif ($contentType === false) { |
88 | throw new ServeFailedException('Rejecting to serve original (it is not an image)'); |
89 | } else { |
90 | ServeFile::serve($source, $contentType, $serveImageOptions); |
91 | } |
92 | } |
93 | |
94 | /** |
95 | * Serve destination file. |
96 | * |
97 | * TODO: SHould this really be public? |
98 | * |
99 | * @param string $destination path to destination file |
100 | * @param array $serveImageOptions (optional) options for serving (such as which headers to add) |
101 | * Supported options: |
102 | * - All options supported by ServeFile::serve() |
103 | * @return void |
104 | */ |
105 | public static function serveDestination($destination, $serveImageOptions = []) |
106 | { |
107 | InputValidator::checkDestination($destination); |
108 | ServeFile::serve($destination, 'image/webp', $serveImageOptions); |
109 | } |
110 | |
111 | |
112 | public static function warningHandler() |
113 | { |
114 | // do nothing! - as we do not return anything, the warning is suppressed |
115 | } |
116 | |
117 | /** |
118 | * Serve converted webp. |
119 | * |
120 | * Serve a converted webp. If a file already exists at the destination, that is served (unless it is |
121 | * older than the source - in that case a fresh conversion will be made, or the file at the destination |
122 | * is larger than the source - in that case the source is served). Some options may alter this logic. |
123 | * In case no file exists at the destination, a fresh conversion is made and served. |
124 | * |
125 | * @param string $source path to source file |
126 | * @param string $destination path to destination |
127 | * @param array $options (optional) options for serving/converting |
128 | * Supported options: |
129 | * 'show-report' => (boolean) If true, the decision will always be 'report' |
130 | * 'serve-original' => (boolean) If true, the decision will be 'source' (unless above option is set) |
131 | * 'reconvert ' => (boolean) If true, the decision will be 'fresh-conversion' (unless one of the |
132 | * above options is set) |
133 | * - All options supported by WebPConvert::convert() |
134 | * - All options supported by ServeFile::serve() |
135 | * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional) |
136 | * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional) |
137 | * |
138 | * @throws \WebPConvert\Exceptions\WebPConvertException If something went wrong. |
139 | * @return void |
140 | */ |
141 | public static function serve($source, $destination, $options = [], $serveLogger = null, $convertLogger = null) |
142 | { |
143 | InputValidator::checkSourceAndDestination($source, $destination); |
144 | |
145 | $options = self::processOptions($options); |
146 | |
147 | if ($options['suppress-warnings']) { |
148 | set_error_handler( |
149 | array('\\WebPConvert\\Serve\\ServeConvertedWebP', "warningHandler"), |
150 | E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE |
151 | ); |
152 | } |
153 | |
154 | |
155 | //$options = array_merge(self::$defaultOptions, $options); |
156 | |
157 | // Step 1: Is there a file at the destination? If not, trigger conversion |
158 | // However 1: if "show-report" option is set, serve the report instead |
159 | // However 2: "reconvert" option should also trigger conversion |
160 | if ($options['show-report']) { |
161 | Header::addLogHeader('Showing report', $serveLogger); |
162 | Report::convertAndReport($source, $destination, $options); |
163 | return; |
164 | } |
165 | |
166 | if (!@file_exists($destination)) { |
167 | Header::addLogHeader('Converting (there were no file at destination)', $serveLogger); |
168 | WebPConvert::convert($source, $destination, $options['convert'], $convertLogger); |
169 | } elseif ($options['reconvert']) { |
170 | Header::addLogHeader('Converting (told to reconvert)', $serveLogger); |
171 | WebPConvert::convert($source, $destination, $options['convert'], $convertLogger); |
172 | } else { |
173 | // Step 2: Is the destination older than the source? |
174 | // If yes, trigger conversion (deleting destination is implicit) |
175 | $timestampSource = @filemtime($source); |
176 | $timestampDestination = @filemtime($destination); |
177 | if (($timestampSource !== false) && |
178 | ($timestampDestination !== false) && |
179 | ($timestampSource > $timestampDestination)) { |
180 | Header::addLogHeader('Converting (destination was older than the source)', $serveLogger); |
181 | WebPConvert::convert($source, $destination, $options['convert'], $convertLogger); |
182 | } |
183 | } |
184 | |
185 | // Step 3: Serve the smallest file (destination or source) |
186 | // However, first check if 'serve-original' is set |
187 | if ($options['serve-original']) { |
188 | Header::addLogHeader('Serving original (told to)', $serveLogger); |
189 | self::serveOriginal($source, $options['serve-image']); |
190 | return; |
191 | } |
192 | |
193 | if ($options['redirect-to-self-instead-of-serving']) { |
194 | Header::addLogHeader( |
195 | 'Redirecting to self! ' . |
196 | '(hope you got redirection to existing webps set up, otherwise you will get a loop!)', |
197 | $serveLogger |
198 | ); |
199 | header('Location: ?fresh', 302); |
200 | return; |
201 | } |
202 | |
203 | $filesizeDestination = @filesize($destination); |
204 | $filesizeSource = @filesize($source); |
205 | if (($filesizeSource !== false) && |
206 | ($filesizeDestination !== false) && |
207 | ($filesizeDestination > $filesizeSource)) { |
208 | Header::addLogHeader('Serving original (it is smaller)', $serveLogger); |
209 | self::serveOriginal($source, $options['serve-image']); |
210 | return; |
211 | } |
212 | |
213 | Header::addLogHeader('Serving converted file', $serveLogger); |
214 | self::serveDestination($destination, $options['serve-image']); |
215 | } |
216 | } |