WebP Express - Version 0.14.7

Version Description

(released: 20 jun 2019)

  • Removed unneccesary files from webp-convert library
Download this release

Release Info

Developer rosell.dk
Plugin Icon 128x128 WebP Express
Version 0.14.7
Comparing to
See all releases

Code changes from version 0.14.6 to 0.14.7

Files changed (66) hide show
  1. README.txt +9 -1
  2. lib/classes/ConvertHelperIndependent.php +1 -3
  3. lib/options/submit.php +255 -52
  4. vendor/rosell-dk/webp-convert/build-scripts/PHPMerger.php +0 -93
  5. vendor/rosell-dk/webp-convert/build-scripts/build-all +0 -1
  6. vendor/rosell-dk/webp-convert/build-scripts/build.php +0 -138
  7. vendor/rosell-dk/webp-convert/build-tests-webp-convert/WebPConvertBuildTest.php +0 -56
  8. vendor/rosell-dk/webp-convert/build-tests-wod/WodBuildTest.php +0 -178
  9. vendor/rosell-dk/webp-convert/install-gmagick-with-webp.sh +0 -74
  10. vendor/rosell-dk/webp-convert/install-imagemagick-with-webp.sh +0 -40
  11. vendor/rosell-dk/webp-convert/install-vips.sh +0 -40
  12. vendor/rosell-dk/webp-convert/src-build/webp-convert.inc +0 -7167
  13. vendor/rosell-dk/webp-convert/src-build/webp-on-demand-1.inc +0 -1393
  14. vendor/rosell-dk/webp-convert/src-build/webp-on-demand-2.inc +0 -5775
  15. vendor/rosell-dk/webp-convert/tests/BaseExposer.php +0 -113
  16. vendor/rosell-dk/webp-convert/tests/Convert/Converters/AbstractConverterTest.php +0 -107
  17. vendor/rosell-dk/webp-convert/tests/Convert/Converters/BaseTraits/AutoQualityTraitTest.php +0 -172
  18. vendor/rosell-dk/webp-convert/tests/Convert/Converters/ConverterTestHelper.php +0 -188
  19. vendor/rosell-dk/webp-convert/tests/Convert/Converters/CwebpTest.php +0 -292
  20. vendor/rosell-dk/webp-convert/tests/Convert/Converters/EwwwTest.php +0 -97
  21. vendor/rosell-dk/webp-convert/tests/Convert/Converters/GdTest.php +0 -254
  22. vendor/rosell-dk/webp-convert/tests/Convert/Converters/GmagickTest.php +0 -29
  23. vendor/rosell-dk/webp-convert/tests/Convert/Converters/GraphicsMagickTest.php +0 -67
  24. vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImageMagickTest.php +0 -63
  25. vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImagickTest.php +0 -63
  26. vendor/rosell-dk/webp-convert/tests/Convert/Converters/StackTest.php +0 -56
  27. vendor/rosell-dk/webp-convert/tests/Convert/Converters/VipsTest.php +0 -234
  28. vendor/rosell-dk/webp-convert/tests/Convert/Converters/WPCTest.php +0 -262
  29. vendor/rosell-dk/webp-convert/tests/Convert/Converters/pretend.inc +0 -55
  30. vendor/rosell-dk/webp-convert/tests/Convert/Exposers/AbstractConverterExposer.php +0 -63
  31. vendor/rosell-dk/webp-convert/tests/Convert/Exposers/CwebpExposer.php +0 -23
  32. vendor/rosell-dk/webp-convert/tests/Convert/Exposers/GdExposer.php +0 -110
  33. vendor/rosell-dk/webp-convert/tests/Convert/Exposers/VipsExposer.php +0 -39
  34. vendor/rosell-dk/webp-convert/tests/Convert/Helpers/JpegQualityDetectorTest.php +0 -33
  35. vendor/rosell-dk/webp-convert/tests/Convert/Helpers/PhpIniSizesTest.php +0 -85
  36. vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExposedConverter.php +0 -34
  37. vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExtendedConverters/EwwwExtended.php +0 -14
  38. vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/FailureGuaranteedConverter.php +0 -14
  39. vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/SuccessGuaranteedConverter.php +0 -13
  40. vendor/rosell-dk/webp-convert/tests/Helpers/WarningsIntoExceptionsTest.php +0 -64
  41. vendor/rosell-dk/webp-convert/tests/Serve/HeaderTest.php +0 -49
  42. vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPExposer.php +0 -25
  43. vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPTest.php +0 -420
  44. vendor/rosell-dk/webp-convert/tests/Serve/ServeFileTest.php +0 -192
  45. vendor/rosell-dk/webp-convert/tests/Serve/mock-header.inc +0 -52
  46. vendor/rosell-dk/webp-convert/tests/WebPConvertTest.php +0 -238
  47. vendor/rosell-dk/webp-convert/tests/bootstrap-webp-convert-test.php +0 -20
  48. vendor/rosell-dk/webp-convert/tests/bootstrap-wod-test.php +0 -24
  49. vendor/rosell-dk/webp-convert/tests/images/not-true-color.png +0 -0
  50. vendor/rosell-dk/webp-convert/tests/images/plaintext-with-jpg-extension.jpg +0 -1
  51. vendor/rosell-dk/webp-convert/tests/images/png-with-jpeg-extension.jpg +0 -0
  52. vendor/rosell-dk/webp-convert/tests/images/png-without-extension +0 -0
  53. vendor/rosell-dk/webp-convert/tests/images/pre-converted/test-bigger.webp +0 -0
  54. vendor/rosell-dk/webp-convert/tests/images/pre-converted/test.webp +0 -0
  55. vendor/rosell-dk/webp-convert/tests/images/small-q61.jpg +0 -0
  56. vendor/rosell-dk/webp-convert/tests/images/test.jpg +0 -0
  57. vendor/rosell-dk/webp-convert/tests/images/test.png +0 -0
  58. vendor/rosell-dk/webp-convert/tests/images/text +0 -0
  59. vendor/rosell-dk/webp-convert/tests/images/text-with-jpg-extension.jpg +0 -0
  60. vendor/rosell-dk/webp-convert/tests/images/text.txt +0 -0
  61. vendor/rosell-dk/webp-convert/tests/images/with space.jpg +0 -0
  62. vendor/rosell-dk/webp-convert/tests/test.jpg +0 -0
  63. vendor/rosell-dk/webp-convert/tests/test.png +0 -0
  64. webp-express.php +1 -1
  65. wod/webp-on-demand.php +0 -11
  66. wod/webp-realizer.php +0 -10
README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://ko-fi.com/rosell
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
- Stable tag: 0.14.6
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
@@ -605,6 +605,11 @@ Easy enough! - [Go here!](https://ko-fi.com/rosell). Or [here](https://buymeacof
605
 
606
  == Changelog ==
607
 
 
 
 
 
 
608
  = 0.14.6 =
609
  *(released: 20 jun 2019)*
610
 
@@ -829,6 +834,9 @@ For older releases, check out changelog.txt
829
 
830
  == Upgrade Notice ==
831
 
 
 
 
832
  = 0.14.6 =
833
  Various fixes
834
 
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
+ Stable tag: 0.14.7
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
605
 
606
  == Changelog ==
607
 
608
+ = 0.14.7 =
609
+ *(released: 20 jun 2019)*
610
+
611
+ * Removed unneccesary files from webp-convert library
612
+
613
  = 0.14.6 =
614
  *(released: 20 jun 2019)*
615
 
834
 
835
  == Upgrade Notice ==
836
 
837
+ = 0.14.7 =
838
+ Removed unneccesary files from webp-convert library
839
+
840
  = 0.14.6 =
841
  Various fixes
842
 
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -244,8 +244,6 @@ class ConvertHelperIndependent
244
 
245
  public static function createLogDir($logDir)
246
  {
247
- $logDir = Paths::getLogDirAbs();
248
-
249
  if (!is_dir($logDir)) {
250
  @mkdir($logDir, 0775, true);
251
  @chmod($logDir, 0775);
@@ -272,7 +270,7 @@ APACHE
272
 
273
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
274
 
275
- $text = 'WebP Express 0.14.6. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
276
 
277
  $logFile = self::getLogFilename($source, $logDir);
278
 
244
 
245
  public static function createLogDir($logDir)
246
  {
 
 
247
  if (!is_dir($logDir)) {
248
  @mkdir($logDir, 0775, true);
249
  @chmod($logDir, 0775);
270
 
271
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
272
 
273
+ $text = 'WebP Express 0.14.7. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
274
 
275
  $logFile = self::getLogFilename($source, $logDir);
276
 
lib/options/submit.php CHANGED
@@ -18,47 +18,253 @@ DismissableMessages::dismissMessage('0.14.0/say-hello-to-vips');
18
  // https://premium.wpmudev.org/blog/handling-form-submissions/
19
  // checkout https://codex.wordpress.org/Function_Reference/sanitize_meta
20
 
21
- /* We want an integer value between 0-100. We round "77.5" to 78. */
22
- function webp_express_sanitize_quality_field($text) {
23
- $text = str_replace(',', '.', $text);
24
- $q = floatval(sanitize_text_field($text));
25
- $q = round($q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return max(0, min($q, 100));
27
  }
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  $config = Config::loadConfigAndFix(false); // false, because we do not need to test if quality detection is working
30
  $oldConfig = $config;
31
 
32
- // Note that "operation-mode" is actually the old mode. The new mode is posted in "change-operation-mode"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  // Set options that are available in all operation modes
35
  $config = array_merge($config, [
36
- 'operation-mode' => sanitize_text_field($_POST['operation-mode']),
37
 
38
  // redirection rules
39
- 'image-types' => sanitize_text_field($_POST['image-types']),
40
  'forward-query-string' => true,
41
-
42
  ]);
43
 
44
-
45
-
46
-
47
  // Set options that are available in all operation modes, except the "CDN friendly" mode
48
- if ($_POST['operation-mode'] != 'cdn-friendly') {
49
-
50
- $cacheControl = sanitize_text_field($_POST['cache-control']);
51
- $config['cache-control'] = $cacheControl;
52
-
53
- switch ($cacheControl) {
54
  case 'no-header':
55
  break;
56
  case 'set':
57
- $config['cache-control-max-age'] = sanitize_text_field($_POST['cache-control-max-age']);
58
- $config['cache-control-public'] = (sanitize_text_field($_POST['cache-control-public']) == 'public');
59
  break;
60
  case 'custom':
61
- $config['cache-control-custom'] = sanitize_text_field($_POST['cache-control-custom']);
62
  break;
63
  }
64
  }
@@ -67,71 +273,68 @@ if ($_POST['operation-mode'] != 'cdn-friendly') {
67
 
68
  // Alter HTML
69
  $config['alter-html'] = [];
70
- $config['alter-html']['enabled'] = isset($_POST['alter-html-enabled']);
71
- if ($_POST['alter-html-replacement'] == 'url') {
72
- $config['alter-html']['only-for-webp-enabled-browsers'] = isset($_POST['alter-html-only-for-webp-enabled-browsers']);
73
  } else {
74
  $config['alter-html']['only-for-webp-enabled-browsers'] = false;
75
  }
76
- if ($_POST['alter-html-replacement'] == 'picture') {
77
- $config['alter-html']['alter-html-add-picturefill-js'] = isset($_POST['alter-html-add-picturefill-js']);
78
  }
79
- if ($_POST['operation-mode'] != 'no-conversion') {
80
- $config['alter-html']['only-for-webps-that-exists'] = (!isset($_POST['alter-html-for-webps-that-has-yet-to-exist']));
81
  } else {
82
  $config['alter-html']['only-for-webps-that-exists'] = true;
83
  }
84
 
85
- $config['alter-html']['replacement'] = sanitize_text_field($_POST['alter-html-replacement']);
86
- $config['alter-html']['hooks'] = sanitize_text_field($_POST['alter-html-hooks']);
87
 
88
 
89
  // Set options that are available in all operation modes, except the "no-conversion" mode
90
  if ($_POST['operation-mode'] != 'no-conversion') {
91
 
92
-
93
- $config['enable-redirection-to-webp-realizer'] = isset($_POST['enable-redirection-to-webp-realizer']);
94
 
95
  // Metadata
96
  // --------
97
- $config['metadata'] = sanitize_text_field($_POST['metadata']);
98
 
99
  // Jpeg
100
  // --------
101
- $config['jpeg-encoding'] = sanitize_text_field($_POST['jpeg-encoding']);
102
 
103
- $auto = (isset($_POST['quality-auto']) && ($_POST['quality-auto'] == 'auto_on'));
104
  $config['quality-auto'] = $auto;
105
  if ($auto) {
106
- $config['max-quality'] = webp_express_sanitize_quality_field($_POST['max-quality']);
107
- $config['quality-specific'] = webp_express_sanitize_quality_field($_POST['quality-fallback']);
108
  } else {
109
  $config['max-quality'] = 80;
110
- $config['quality-specific'] = webp_express_sanitize_quality_field($_POST['quality-specific']);
111
  }
112
 
113
- $jpegEnableNearLossless = (isset($_POST['jpeg-enable-near-lossless']) && ($_POST['jpeg-enable-near-lossless'] == 'on'));
114
- $config['jpeg-enable-near-lossless'] = $jpegEnableNearLossless;
115
- $config['jpeg-near-lossless'] = webp_express_sanitize_quality_field($_POST['jpeg-near-lossless']);
116
 
117
 
118
  // Png
119
  // --------
120
- $config['png-encoding'] = sanitize_text_field($_POST['png-encoding']);
121
-
122
- // TODO: ERRORS!
123
- $config['png-quality'] = webp_express_sanitize_quality_field($_POST['png-quality']);
124
- $pngEnableNearLossless = (isset($_POST['png-enable-near-lossless']) && ($_POST['png-enable-near-lossless'] == 'on'));
125
- $config['png-enable-near-lossless'] = $pngEnableNearLossless;
126
- $config['png-near-lossless'] = webp_express_sanitize_quality_field($_POST['png-near-lossless']);
127
- $config['alpha-quality'] = webp_express_sanitize_quality_field($_POST['alpha-quality']);
128
-
129
 
 
130
  $config['convert-on-upload'] = isset($_POST['convert-on-upload']);
131
 
 
132
  // Web Service
133
  // -------------
134
 
 
135
  $whitelistPosted = json_decode(wp_unslash($_POST['whitelist']), true);
136
 
137
  // Sanitize whitelist
@@ -139,11 +342,11 @@ if ($_POST['operation-mode'] != 'no-conversion') {
139
  $whitelist['label'] = sanitize_text_field($whitelist['label']);
140
  $whitelist['ip'] = sanitize_text_field($whitelist['ip']);
141
  $whitelist['api-key'] = sanitize_text_field($whitelist['api-key']);
142
- }
143
 
144
  $config['web-service'] = [
145
  'enabled' => isset($_POST['web-service-enabled']),
146
- 'whitelist' => $whitelistPosted
147
  ];
148
 
149
  // Set existing api keys in web service (we removed them from the json array, for security purposes)
18
  // https://premium.wpmudev.org/blog/handling-form-submissions/
19
  // checkout https://codex.wordpress.org/Function_Reference/sanitize_meta
20
 
21
+ /**
22
+ * Get sanitized text (NUL removed too)
23
+ *
24
+ * General purpose for getting textual values from $_POST.
25
+ * If the POST value is not set, the fallback is returned
26
+ *
27
+ * For sanitizing, the wordpress function "sanitize_text_field" is used. However, before doing that, we
28
+ * remove any NUL characters. NUL characters can be used to trick input validation, so we better get rid of those
29
+ * right away
30
+ *
31
+ * @param string $keyInPOST key in $_POST
32
+ * @param int $fallback value to return if the POST does not match any in the set, or it is not send at all
33
+ * @param array $acceptableValues the set of values that we have to choose between
34
+ *
35
+ * @return string sanitized text, or fallback if value isn't set
36
+ */
37
+ function webpexpress_getSanitizedText($keyInPOST, $fallbackValue = '') {
38
+ if (!isset($_POST[$keyInPOST])) {
39
+ return $fallbackValue;
40
+ }
41
+ $value = $_POST[$keyInPOST];
42
+
43
+ // Keep in mind checking for NUL when dealing with user input
44
+ // see https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP
45
+ $value = str_replace(chr(0), '', $value);
46
+
47
+ return sanitize_text_field($value);
48
+ }
49
+
50
+ /**
51
+ * Get sanitized value from a set of values.
52
+ *
53
+ * Only allows values in the set given. If the value does not match, the fallback will be returned.
54
+ *
55
+ * @param string $keyInPOST key in $_POST
56
+ * @param int $fallback value to return if the POST does not match any in the set, or it is not send at all
57
+ * @param array $acceptableValues the set of values that we have to choose between
58
+ *
59
+ * @return mixed one of the items in the set - or fallback (which is usually also one in the set)
60
+ */
61
+ function webpexpress_getSanitizedChooseFromSet($keyInPOST, $fallbackValue, $acceptableValues) {
62
+ $value = webpexpress_getSanitizedText($keyInPOST, $fallbackValue);
63
+ if (in_array($value, $acceptableValues)) {
64
+ return $value;
65
+ }
66
+ return $fallbackValue;
67
+ }
68
+
69
+ function webpexpress_getSanitizedCacheControlHeader($keyInPOST) {
70
+ $value = webpexpress_getSanitizedText($keyInPOST);
71
+
72
+ // Example of valid header: "public, max-age=31536000, stale-while-revalidate=604800, stale-if-error=604800"
73
+ $value = strtolower($value);
74
+ return preg_replace('#[^a-z0-9=,\s_\-]#', '', $value);
75
+ }
76
+
77
+ /**
78
+ * Get sanitized integer
79
+ *
80
+ * @param string $keyInPOST key in $_POST
81
+ * @param int $fallback key in $_POST
82
+ *
83
+ * @return int the sanitized number.
84
+ */
85
+ function webpexpress_getSanitizedInt($keyInPOST, $fallback=0) {
86
+ $value = webpexpress_getSanitizedText($keyInPOST, strval($fallback));
87
+
88
+ // strip anything after and including comma
89
+ $value = preg_replace('#[\.\,].*#', '', $value);
90
+
91
+ // remove anything but digits
92
+ $value = preg_replace('#[^0-9]#', '', $value);
93
+
94
+ if ($value == '') {
95
+ return $fallback;
96
+ }
97
+
98
+ return intval($value);
99
+ }
100
+
101
+ /**
102
+ * Get sanitized quality (0-100).
103
+ *
104
+ * @param string $keyInPOST key in $_POST
105
+ *
106
+ * @return int quality (0-100)
107
+ */
108
+ function webpexpress_getSanitizedQuality($keyInPOST, $fallback = 75) {
109
+ $q = webpexpress_getSanitizedInt($keyInPOST, $fallback);
110
+ // return value between 0-100
111
  return max(0, min($q, 100));
112
  }
113
 
114
+
115
+ /**
116
+ * Get sanitized whitelist
117
+ *
118
+ * @param string $keyInPOST key in $_POST
119
+ *
120
+ * @return int quality (0-100)
121
+ */
122
+ function webpexpress_getSanitizedWhitelist() {
123
+ $whitelistPosted = (isset($_POST['whitelist']) ? $_POST['whitelist'] : '[]');
124
+
125
+ $whitelistPosted = json_decode(wp_unslash($whitelistPosted), true);
126
+ // TODO: check for json decode error
127
+
128
+ $whitelistSanitized = [];
129
+
130
+ // Sanitize whitelist
131
+ foreach ($whitelistPosted as $whitelist) {
132
+ if (
133
+ isset($whitelist['label']) &&
134
+ isset($whitelist['ip'])
135
+ // note: api-key is not neccessarily set
136
+ ) {
137
+ $obj = [
138
+ 'label' => sanitize_text_field($whitelist['label']),
139
+ 'ip' => sanitize_text_field($whitelist['ip']),
140
+ ];
141
+ if (isset($whitelist['new-api-key'])) {
142
+ $obj['new-api-key'] = sanitize_text_field($whitelist['new-api-key']);
143
+ }
144
+ if (isset($whitelist['uid'])) {
145
+ $obj['uid'] = sanitize_text_field($whitelist['uid']);
146
+ }
147
+ if (isset($whitelist['require-api-key-to-be-crypted-in-transfer'])) {
148
+ $obj['require-api-key-to-be-crypted-in-transfer'] = ($whitelist['require-api-key-to-be-crypted-in-transfer'] === true);
149
+ }
150
+
151
+ $whitelistSanitized[] = $obj;
152
+ }
153
+ }
154
+ return $whitelistSanitized;
155
+ }
156
+
157
+ // ---------------
158
  $config = Config::loadConfigAndFix(false); // false, because we do not need to test if quality detection is working
159
  $oldConfig = $config;
160
 
161
+
162
+
163
+ // Sanitizing
164
+ $sanitized = [
165
+ // General
166
+ // Note that "operation-mode" is actually the old mode. The new mode is posted in "change-operation-mode"
167
+ 'operation-mode' => webpexpress_getSanitizedChooseFromSet('operation-mode', 'varied-image-responses', [
168
+ 'varied-image-responses',
169
+ 'cdn-friendly',
170
+ 'no-conversion',
171
+ 'tweaked'
172
+ ]),
173
+ 'image-types' => intval(webpexpress_getSanitizedChooseFromSet('image-types', '3', [
174
+ '0',
175
+ '1',
176
+ '3'
177
+ ])),
178
+ 'cache-control' => webpexpress_getSanitizedChooseFromSet('cache-control', 'no-header', [
179
+ 'no-header',
180
+ 'set',
181
+ 'custom'
182
+ ]),
183
+ 'cache-control-max-age' => webpexpress_getSanitizedChooseFromSet('cache-control-max-age', 'one-hour', [
184
+ 'one-second',
185
+ 'one-hour',
186
+ 'one-day',
187
+ 'one-week',
188
+ 'one-month',
189
+ 'one-year',
190
+ ]),
191
+ 'cache-control-public' => webpexpress_getSanitizedChooseFromSet('cache-control-public', 'public', [
192
+ 'public',
193
+ 'private',
194
+ ]),
195
+ 'cache-control-custom' => webpexpress_getSanitizedCacheControlHeader('cache-control-custom'),
196
+
197
+ // Alter html
198
+ 'alter-html-enabled' => isset($_POST['alter-html-enabled']),
199
+ 'alter-html-only-for-webp-enabled-browsers' => isset($_POST['alter-html-only-for-webp-enabled-browsers']),
200
+ 'alter-html-add-picturefill-js' => isset($_POST['alter-html-add-picturefill-js']),
201
+ 'alter-html-for-webps-that-has-yet-to-exist' => isset($_POST['alter-html-for-webps-that-has-yet-to-exist']),
202
+ 'alter-html-replacement' => webpexpress_getSanitizedChooseFromSet('alter-html-replacement', 'picture', [
203
+ 'picture',
204
+ 'url'
205
+ ]),
206
+ 'alter-html-hooks' => webpexpress_getSanitizedChooseFromSet('alter-html-hooks', 'content-hooks', [
207
+ 'content-hooks',
208
+ 'ob'
209
+ ]),
210
+ 'enable-redirection-to-webp-realizer' => isset($_POST['enable-redirection-to-webp-realizer']),
211
+
212
+ // Conversion options
213
+ 'metadata' => webpexpress_getSanitizedChooseFromSet('metadata', 'none', [
214
+ 'none',
215
+ 'all'
216
+ ]),
217
+ 'jpeg-encoding' => webpexpress_getSanitizedChooseFromSet('jpeg-encoding', 'auto', [
218
+ 'lossy',
219
+ 'auto'
220
+ ]),
221
+ 'jpeg-enable-near-lossless' => webpexpress_getSanitizedChooseFromSet('jpeg-enable-near-lossless', 'on', [
222
+ 'on',
223
+ 'off'
224
+ ]),
225
+ 'quality-auto' => webpexpress_getSanitizedChooseFromSet('quality-auto', 'auto_on', [
226
+ 'auto_on',
227
+ 'auto_off'
228
+ ]),
229
+ 'max-quality' => webpexpress_getSanitizedQuality('max-quality', 80),
230
+ 'jpeg-near-lossless' => webpexpress_getSanitizedQuality('jpeg-near-lossless', 60),
231
+ 'quality-specific' => webpexpress_getSanitizedQuality('quality-specific', 70),
232
+ 'quality-fallback' => webpexpress_getSanitizedQuality('quality-fallback', 70),
233
+ 'png-near-lossless' => webpexpress_getSanitizedQuality('png-near-lossless', 60),
234
+ 'png-enable-near-lossless' => webpexpress_getSanitizedChooseFromSet('png-enable-near-lossless', 'on', [
235
+ 'on',
236
+ 'off'
237
+ ]),
238
+ 'png-quality' => webpexpress_getSanitizedQuality('png-quality', 85),
239
+ 'png-encoding' => webpexpress_getSanitizedChooseFromSet('png-encoding', 'auto', [
240
+ 'lossless',
241
+ 'auto'
242
+ ]),
243
+ 'alpha-quality' => webpexpress_getSanitizedQuality('png-quality', 80),
244
+ 'whitelist' => webpexpress_getSanitizedWhitelist(),
245
+ ];
246
 
247
  // Set options that are available in all operation modes
248
  $config = array_merge($config, [
249
+ 'operation-mode' => $sanitized['operation-mode'],
250
 
251
  // redirection rules
252
+ 'image-types' => $sanitized['image-types'],
253
  'forward-query-string' => true,
 
254
  ]);
255
 
 
 
 
256
  // Set options that are available in all operation modes, except the "CDN friendly" mode
257
+ if ($sanitized['operation-mode'] != 'cdn-friendly') {
258
+ $config['cache-control'] = $sanitized['cache-control'];
259
+ switch ($sanitized['cache-control']) {
 
 
 
260
  case 'no-header':
261
  break;
262
  case 'set':
263
+ $config['cache-control-max-age'] = $sanitized['cache-control-max-age'];
264
+ $config['cache-control-public'] = ($sanitized['cache-control-public'] == 'public');
265
  break;
266
  case 'custom':
267
+ $config['cache-control-custom'] = $sanitized['cache-control-custom'];
268
  break;
269
  }
270
  }
273
 
274
  // Alter HTML
275
  $config['alter-html'] = [];
276
+ $config['alter-html']['enabled'] = $sanitized['alter-html-enabled'];
277
+ if ($sanitized['alter-html-replacement'] == 'url') {
278
+ $config['alter-html']['only-for-webp-enabled-browsers'] = $sanitized['alter-html-only-for-webp-enabled-browsers'];
279
  } else {
280
  $config['alter-html']['only-for-webp-enabled-browsers'] = false;
281
  }
282
+ if ($sanitized['alter-html-replacement'] == 'picture') {
283
+ $config['alter-html']['alter-html-add-picturefill-js'] = $sanitized['alter-html-add-picturefill-js'];
284
  }
285
+ if ($sanitized['operation-mode'] != 'no-conversion') {
286
+ $config['alter-html']['only-for-webps-that-exists'] = (!$sanitized['alter-html-for-webps-that-has-yet-to-exist']);
287
  } else {
288
  $config['alter-html']['only-for-webps-that-exists'] = true;
289
  }
290
 
291
+ $config['alter-html']['replacement'] = $sanitized['alter-html-replacement'];
292
+ $config['alter-html']['hooks'] = $sanitized['alter-html-hooks'];
293
 
294
 
295
  // Set options that are available in all operation modes, except the "no-conversion" mode
296
  if ($_POST['operation-mode'] != 'no-conversion') {
297
 
298
+ $config['enable-redirection-to-webp-realizer'] = $sanitized['enable-redirection-to-webp-realizer'];
 
299
 
300
  // Metadata
301
  // --------
302
+ $config['metadata'] = $sanitized['metadata'];
303
 
304
  // Jpeg
305
  // --------
306
+ $config['jpeg-encoding'] = $sanitized['jpeg-encoding'];
307
 
308
+ $auto = ($sanitized['quality-auto'] == 'auto_on');
309
  $config['quality-auto'] = $auto;
310
  if ($auto) {
311
+ $config['max-quality'] = $sanitized['max-quality'];
312
+ $config['quality-specific'] = $sanitized['quality-fallback'];
313
  } else {
314
  $config['max-quality'] = 80;
315
+ $config['quality-specific'] = $sanitized['quality-specific'];
316
  }
317
 
318
+ $config['jpeg-enable-near-lossless'] = ($sanitized['jpeg-enable-near-lossless'] == 'on');
319
+ $config['jpeg-near-lossless'] = $sanitized['jpeg-near-lossless'];
 
320
 
321
 
322
  // Png
323
  // --------
324
+ $config['png-encoding'] = $sanitized['png-encoding'];
325
+ $config['png-quality'] = $sanitized['png-quality'];
326
+ $config['png-enable-near-lossless'] = ($sanitized['png-enable-near-lossless'] == 'on');
327
+ $config['png-near-lossless'] = $sanitized['png-near-lossless'];
328
+ $config['alpha-quality'] = $sanitized['alpha-quality'];
 
 
 
 
329
 
330
+ // Other
331
  $config['convert-on-upload'] = isset($_POST['convert-on-upload']);
332
 
333
+
334
  // Web Service
335
  // -------------
336
 
337
+ /*
338
  $whitelistPosted = json_decode(wp_unslash($_POST['whitelist']), true);
339
 
340
  // Sanitize whitelist
342
  $whitelist['label'] = sanitize_text_field($whitelist['label']);
343
  $whitelist['ip'] = sanitize_text_field($whitelist['ip']);
344
  $whitelist['api-key'] = sanitize_text_field($whitelist['api-key']);
345
+ }*/
346
 
347
  $config['web-service'] = [
348
  'enabled' => isset($_POST['web-service-enabled']),
349
+ 'whitelist' => $sanitized['whitelist']
350
  ];
351
 
352
  // Set existing api keys in web service (we removed them from the json array, for security purposes)
vendor/rosell-dk/webp-convert/build-scripts/PHPMerger.php DELETED
@@ -1,93 +0,0 @@
1
- <?php
2
- class PhpMerger
3
- {
4
- private static $required = [];
5
- private static function add_to_queue($path)
6
- {
7
- self::$required[] = $path;
8
- }
9
- private static function removeFromQueue($path)
10
- {
11
- $pathsToRemove = [
12
- $path
13
- ];
14
- self::$required = array_diff(self::$required, $pathsToRemove);
15
- }
16
- public static function generate($conf)
17
- {
18
- $success = true;
19
- self::$required = [];
20
- foreach ($conf['jobs'] as $def) {
21
- // untrail slash
22
- $def['root'] = preg_replace('/\/$/', '', $def['root']);
23
-
24
- // load base classes, which are required for other classes
25
- foreach ($def['files'] as $file) {
26
- self::add_to_queue($def['root'] . '/' . $file);
27
- }
28
-
29
- // load dirs in defined order. No recursion.
30
- foreach ($def['dirs'] as $dir) {
31
- $dirAbs = __DIR__ . '/' . $def['dir-root'] . '/' . $dir;
32
- if (!is_dir($dirAbs)) {
33
- echo 'Dir not found: ' . $dirAbs;
34
- return false;
35
- }
36
- $files = glob($dirAbs . '/*.php');
37
- foreach ($files as $file) {
38
- // only require files that begins with uppercase (A-Z)
39
- if (preg_match('/\/[A-Z][a-zA-Z]*\.php/', $file)) {
40
- $file = str_replace(__DIR__ . '/' . $def['dir-root'], '', $file);
41
- $file = str_replace('./', '', $file);
42
-
43
- //echo $file . "\n";
44
- self::add_to_queue($def['dir-root'] . $file);
45
- }
46
- }
47
- }
48
-
49
- // remove exclude files
50
- if (isset($def['exclude'])) {
51
- foreach ($def['exclude'] as $excludeFile) {
52
- self::removeFromQueue($def['root'] . $excludeFile);
53
- }
54
- }
55
- }
56
-
57
- // remove duplicates
58
- self::$required = array_unique(self::$required);
59
-
60
-
61
- //echo "included: \n" . implode("\n", self::$required) . "\n";
62
-
63
- // generate file content
64
- $data = '';
65
- $data .= "<?php \n";
66
- foreach (self::$required as $path) {
67
- try {
68
- $file = file_get_contents(__DIR__ . '/' . $path);
69
- //$file = str_replace('<' . '?php', '', $file);
70
- //$file = str_replace('<' . '?php', '?' . '><?' . 'php', $file);
71
- // prepend closing php tag before php tag (only if php tag is in beginning of file)
72
- $file = preg_replace('/^\<\?php/', '?><?' . 'php', $file);
73
- $data .= $file . "\n";
74
- } catch (\Exception $e) {
75
- $success = false;
76
- //throw $e;
77
- }
78
- }
79
-
80
- // generate file
81
- //$my_file = '../generated.inc';
82
- $handle = fopen(__DIR__ . '/' . $conf['destination'], 'w');
83
- if ($handle !== false) {
84
- fwrite($handle, $data);
85
- echo "saved to '" . $conf['destination'] . "'\n";
86
- } else {
87
- echo 'OH NO! - failed saving!!!';
88
- $success = false;
89
- }
90
- return $success;
91
-
92
- }
93
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/build-scripts/build-all DELETED
@@ -1 +0,0 @@
1
- php build-webp-on-demand.php
 
vendor/rosell-dk/webp-convert/build-scripts/build.php DELETED
@@ -1,138 +0,0 @@
1
- <?php
2
- error_reporting(E_ALL);
3
- ini_set("display_errors", 1);
4
-
5
- require_once('PHPMerger.php');
6
- //use PHPMerger;
7
-
8
-
9
- /*$filesInWod1 = [
10
- '/Serve/ServeBase.php',
11
- '/Serve/ServeExistingOrHandOver.php',
12
- '/WebPConvert.php'
13
- ];*/
14
-
15
- $filesInWod1 = [
16
- '/Serve/ServeConvertedWebP.php',
17
- '/Serve/ServeConvertedWebPWithErrorHandling.php',
18
- '/Serve/ServeFile.php',
19
- '/Exceptions/WebPConvertException.php',
20
- '/Exceptions/InvalidInputException.php',
21
- '/Exceptions/InvalidInput/InvalidImageTypeException.php',
22
- '/Exceptions/InvalidInput/TargetNotFoundException.php',
23
- '/Helpers/PathChecker.php',
24
- '/Helpers/InputValidator.php',
25
- '/Serve/Header.php',
26
- '/WebPConvert.php',
27
- //'../vendor/rosell-dk/image-mime-type-guesser/src/Detectors/AbstractDetector.php',
28
- ];
29
-
30
- // Build "webp-on-demand-1.php" (for non-composer projects)
31
-
32
- $success = PhpMerger::generate([
33
- 'destination' => '../src-build/webp-on-demand-1.inc',
34
-
35
- 'jobs' => [
36
- [
37
- 'root' => '../src/',
38
- 'files' => $filesInWod1,
39
- 'dir-root' => '..',
40
- 'dirs' => [
41
- // dirs will be required in specified order. There is no recursion, so you need to specify subdirs as well.
42
- 'vendor/rosell-dk/image-mime-type-guesser/src',
43
- 'vendor/rosell-dk/image-mime-type-guesser/src/Detectors',
44
- //'.',
45
- ]
46
- ]
47
- ]
48
- ]);
49
- if (!$success) {
50
- exit(255);
51
- }
52
- //exit(0);
53
- $jobsEverything = [
54
- [
55
- 'root' => '../src/',
56
- 'dir-root' => '../src',
57
- 'files' => [
58
- // put base classes here
59
- 'Options/Option.php',
60
- 'Convert/Converters/AbstractConverter.php',
61
- 'Exceptions/WebPConvertException.php',
62
- 'Convert/Exceptions/ConversionFailedException.php',
63
- //'Convert/BaseConverters',
64
- //'Convert/Converters',
65
- //'Convert/Exceptions',
66
- //'Loggers',
67
- //'Serve',
68
- ],
69
- 'dirs' => [
70
- // dirs will be required in specified order. There is no recursion, so you need to specify subdirs as well.
71
- // TODO: Implement recursion in PHPMerger.php,
72
- '.',
73
- 'Options',
74
- 'Convert/Converters/BaseTraits',
75
- 'Convert/Converters/ConverterTraits',
76
- 'Convert/Converters',
77
- 'Convert/Converters/BaseTraits',
78
- 'Convert/Converters/ConverterTraits',
79
- 'Convert/Exceptions',
80
- 'Convert/Exceptions/ConversionFailed',
81
- 'Convert/Exceptions/ConversionFailed/ConverterNotOperational',
82
- 'Convert/Exceptions/ConversionFailed/FileSystemProblems',
83
- 'Convert/Exceptions/ConversionFailed/InvalidInput',
84
- 'Convert/Helpers',
85
- 'Convert',
86
- 'Exceptions',
87
- 'Exceptions/InvalidInput',
88
- 'Helpers',
89
- 'Loggers',
90
- 'Serve',
91
- 'Serve/Exceptions',
92
- ],
93
- 'exclude' => [
94
- ]
95
- ],
96
- [
97
- 'root' => '../vendor/rosell-dk/image-mime-type-guesser/src/',
98
- 'dir-root' => '../vendor/rosell-dk/image-mime-type-guesser/src',
99
-
100
- 'files' => [
101
- // put base classes here
102
- 'Detectors/AbstractDetector.php',
103
- ],
104
- 'dirs' => [
105
- // dirs will be required in specified order. There is no recursion, so you need to specify subdirs as well.
106
- //'.',
107
- '.',
108
- 'Detectors',
109
- ],
110
- 'exclude' => [
111
- ]
112
- ],
113
- ];
114
-
115
- // Build "webp-convert.inc", containing the entire library (for the lazy ones)
116
- $success = PhpMerger::generate([
117
- 'destination' => '../src-build/webp-convert.inc',
118
- 'jobs' => $jobsEverything
119
- ]);
120
- if (!$success) {
121
- exit(255);
122
- }
123
-
124
- $jobsWod2 = $jobsEverything;
125
- $jobsWod2[0]['exclude'] = $filesInWod1;
126
-
127
- // remove second job (ImageMimeTypeGuesser is included in wod-1)
128
- unset($jobsWod2[1]);
129
-
130
- // Build "webp-on-demand-2.inc"
131
- // It must contain everything EXCEPT those classes that were included in 'webp-on-demand-1.inc'
132
- $success = PhpMerger::generate([
133
- 'destination' => '../src-build/webp-on-demand-2.inc',
134
- 'jobs' => $jobsWod2
135
- ]);
136
- if (!$success) {
137
- exit(255);
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/build-tests-webp-convert/WebPConvertBuildTest.php DELETED
@@ -1,56 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests;
4
-
5
- use WebPConvert\WebPConvert;
6
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
7
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
8
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
9
-
10
- use PHPUnit\Framework\TestCase;
11
-
12
- /**
13
- * Test the complete build (webp-convert.inc)
14
- */
15
- class WebPConvertBuildTest extends TestCase
16
- {
17
-
18
- public static function getImageFolder()
19
- {
20
- return realpath(__DIR__ . '/../tests/images');
21
- }
22
-
23
- public static function getImagePath($image)
24
- {
25
- return self::getImageFolder() . '/' . $image;
26
- }
27
-
28
- public function testWebPConvertBuildNotCompletelyBroken()
29
- {
30
- require __DIR__ . '/../src-build/webp-convert.inc';
31
-
32
- $source = self::getImagePath('png-without-extension');
33
- $this->assertTrue(file_exists($source));
34
-
35
- ob_start();
36
- WebPConvert::serveConverted(
37
- $source,
38
- $source . '.webp',
39
- [
40
- 'reconvert' => true,
41
- //'converters' => ['imagick'],
42
- 'converters' => [
43
- 'imagick',
44
- 'gd',
45
- 'cwebp',
46
- //'vips',
47
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
48
- ],
49
- ]
50
- );
51
- ob_end_clean();
52
- $this->addToAssertionCount(1);
53
-
54
- }
55
- }
56
- require_once(__DIR__ . '/../tests/Serve/mock-header.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/build-tests-wod/WodBuildTest.php DELETED
@@ -1,178 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests;
4
-
5
- use WebPConvert\WebPConvert;
6
-
7
- use PHPUnit\Framework\TestCase;
8
-
9
- /**
10
- * Test the webp-on-demand builds (webp-on-demand-1.inc and webp-on-demand-2.inc)
11
- */
12
- class WodBuildTest extends TestCase
13
- {
14
-
15
- private static $buildDir = __DIR__ . '/../src-build';
16
-
17
- public static function getImageFolder()
18
- {
19
- return realpath(__DIR__ . '/../tests/images');
20
- }
21
-
22
- public static function getImagePath($image)
23
- {
24
- return self::getImageFolder() . '/' . $image;
25
- }
26
-
27
-
28
- public function autoloadingDisallowed($class) {
29
- throw new \Exception('no autoloading expected! ' . $class);
30
- }
31
-
32
- public function autoloaderLoad($class) {
33
- if (strpos($class, 'WebPConvert\\') === 0) {
34
- require_once self::$buildDir . '/webp-on-demand-2.inc';
35
- }
36
- }
37
-
38
- /**
39
- * @runInSeparateProcess
40
- */
41
- public function testWodBuildWithoutAutoload()
42
- {
43
- // The following should NOT trigger autoloader, because ALL functionality for
44
- // serving existing is in webp-on-demand-1.php
45
-
46
- $wod1 = self::$buildDir . '/webp-on-demand-1.inc';
47
- $this->assertTrue(file_exists($wod1), 'webp-on-demand-1.inc not found!');
48
- require_once $wod1;
49
-
50
- spl_autoload_register([self::class, 'autoloaderLoad'], true, true);
51
-
52
- $source = self::getImagePath('png-without-extension');
53
- $this->assertTrue(file_exists($source));
54
-
55
- ob_start();
56
- WebPConvert::serveConverted(
57
- $source,
58
- $source . '.webp',
59
- [
60
- 'convert' => [
61
- 'converters' => [
62
- 'gd',
63
- 'imagick',
64
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
65
- // vips? - causes build error:
66
- // PHPUnit_Framework_Exception: (banana:9793): VIPS-WARNING **: near_lossless unsupported
67
-
68
- //'imagickbinary',
69
- // PHPUnit_Framework_Exception: convert: delegate failed `"cwebp" -quiet -q %Q "%i" -o "%o"' @ error/delegate.c/InvokeDelegate/1310.
70
- ],
71
- ]
72
- //'reconvert' => true,
73
- /* 'convert' => [
74
- 'converters' => ['imagick'],
75
- ]*/
76
- //
77
-
78
- ]
79
- );
80
- ob_end_clean();
81
- spl_autoload_unregister([self::class, 'autoloaderLoad']);
82
-
83
- }
84
-
85
- /**
86
- * @runInSeparateProcess
87
- */
88
- public function testWodBuildWithAutoload()
89
- {
90
- $wod1 = self::$buildDir . '/webp-on-demand-1.inc';
91
- $wod2 = self::$buildDir . '/webp-on-demand-2.inc';
92
-
93
- $this->assertTrue(file_exists(self::$buildDir), 'build dir not found!');
94
- $this->assertTrue(file_exists($wod1), 'webp-on-demand-1.inc not found!');
95
- $this->assertTrue(file_exists($wod2), 'webp-on-demand-2.inc not found!');
96
-
97
- /*
98
- As failed assertions exits the method, it is now safe to require the inc...
99
- If the code is unparsable, a parse error will be thrown, like this:
100
-
101
- There was 1 error:
102
-
103
- 1) WebPConvert\Tests\WodBuildTest::testWodBuildNotCompletelyBroken
104
- ParseError: syntax error, unexpected 'ServeBase' (T_STRING)
105
-
106
- /var/www/wc/wc0/webp-convert/build/webp-on-demand-1.inc:7
107
-
108
- ERRORS!
109
- Tests: 1, Assertions: 3, Errors: 1.
110
- Script phpunit handling the test event returned with error code 2
111
- */
112
- require_once $wod1;
113
-
114
-
115
- $source = self::getImagePath('png-without-extension');
116
- $this->assertTrue(file_exists($source));
117
-
118
- /*
119
- We do not try/catch the following.
120
- If it errors out (which it should not), or an exception is thrown (which ought not to happpen either),
121
- - It will be discovered, and cause the tests to fail.
122
-
123
- For example, if webp-on-demand-2 is unparsable, phpunit will fail like this:
124
-
125
- There was 1 error:
126
-
127
- /var/www/wc/wc0/webp-convert/build/webp-on-demand-2.inc:9
128
- /var/www/wc/wc0/webp-convert/build/webp-on-demand-1.inc:293
129
- /var/www/wc/wc0/webp-convert/tests/WodBuildTest.php:72
130
-
131
- ERRORS!
132
- Tests: 1, Assertions: 3, Errors: 1.
133
- Script phpunit handling the test event returned with error code 2
134
- */
135
-
136
- /*
137
- WebPConvert::convertAndServe(
138
- $source,
139
- $source . '.webp',
140
- [
141
- 'reconvert' => true,
142
- //'converters' => ['imagick'],
143
- 'aboutToServeImageCallBack' => function() {
144
- // Return false, in order to cancel serving
145
- return false;
146
- },
147
- 'aboutToPerformFailActionCallback' => function () {
148
- return false;
149
- }
150
- ]
151
- );
152
- */
153
-
154
- spl_autoload_register([self::class, 'autoloaderLoad'], true, true);
155
-
156
- ob_start();
157
- WebPConvert::serveConverted(
158
- $source,
159
- $source . '.webp',
160
- [
161
- 'reconvert' => true,
162
- 'convert' => [
163
- 'converters' => [
164
- 'imagick',
165
- 'gd',
166
- 'cwebp',
167
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
168
- ],
169
- ]
170
- ]
171
- );
172
- ob_end_clean();
173
- spl_autoload_unregister([self::class, 'autoloaderLoad']);
174
-
175
- $this->addToAssertionCount(1);
176
- }
177
- }
178
- require_once(__DIR__ . '/../tests/Serve/mock-header.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/install-gmagick-with-webp.sh DELETED
@@ -1,74 +0,0 @@
1
- # https://duntuk.com/how-install-graphicsmagick-gmagick-php-extension
2
- # https://gist.github.com/basimhennawi/21c39f9758b0b1cb5e0bd5ee08b5be58
3
- # https://github.com/rosell-dk/webp-convert/wiki/Installing-gmagick-extension
4
-
5
- #if [ -d "$HOME/vips/bin" ]; then
6
- #fi;
7
-
8
-
9
- $HOME/opt/bin/gm -version | grep -i 'WebP.*yes' && {
10
- gmagick_installed_with_webp=1
11
- }
12
-
13
- if [[ $gmagick_installed_with_webp == 1 ]]; then
14
- echo "Gmagick is already compiled with webp. Nothing to do :)"
15
- echo ":)"
16
- else
17
- echo "Gmagick is is not installed or not compiled with webp."
18
- compile_libwebp=1
19
- compile_gmagick=1
20
- fi;
21
- #ls $HOME/opt/bin
22
-
23
-
24
- cores=$(nproc)
25
- LIBWEBP_VERSION=1.0.2
26
-
27
- if [[ $compile_libwebp == 2 ]]; then
28
- echo "We are going to be compiling libwebp..."
29
- echo "Using $cores cores."
30
- echo "Downloading libwebp version $LIBWEBP_VERSION"
31
- cd /tmp
32
- curl -O https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP_VERSION.tar.gz
33
- tar xzf libwebp-$LIBWEBP_VERSION.tar.gz
34
- cd libwebp-*
35
-
36
- echo "./configure --prefix=$HOME/opt"
37
- ./configure --prefix=$HOME/opt
38
-
39
- echo "make -j$CORES"
40
- make -j$CORES
41
-
42
- echo "make install -j$CORES"
43
- make install -j$CORES
44
- fi;
45
-
46
- if [[ $compile_gmagick == 2 ]]; then
47
- echo "Compiling Gmagick"
48
- echo "Using $cores cores."
49
- cd /tmp
50
- echo "Downloading GraphicsMagick-LATEST.tar.gz"
51
- wget http://78.108.103.11/MIRROR/ftp/GraphicsMagick/GraphicsMagick-LATEST.tar.gz
52
- tar xfz GraphicsMagick-LATEST.tar.gz
53
- cd GraphicsMagick-*
54
-
55
- echo "Configuring"
56
- ./configure --prefix=$HOME/opt --enable-shared --with-webp=yes
57
-
58
- echo "make -j$CORES"
59
- make -j$CORES
60
-
61
- echo "make install -j$CORES"
62
- make install -j$CORES
63
- fi;
64
-
65
-
66
- #./configure --prefix=$HOME/opt --with-webp=yes &&
67
-
68
- #$HOME/opt/bin/gm -version
69
-
70
- #convert -version | grep 'webp' || {
71
-
72
- #convert -list delegate | grep 'webp =>' || {
73
- #}
74
- ##libgraphicsmagick1-dev
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/install-imagemagick-with-webp.sh DELETED
@@ -1,40 +0,0 @@
1
-
2
- # Install imagick with webp support (if not already there) and update library paths
3
- # Got the script from here:
4
- # https://stackoverflow.com/questions/41138404/how-to-install-newer-imagemagick-with-webp-support-in-travis-ci-container
5
-
6
- if ! [[ $IMAGEMAGICK_VERSION ]]; then
7
- export IMAGEMAGICK_VERSION="7.0.8-43"
8
- fi;
9
-
10
- convert -list delegate | grep 'webp =>' && {
11
- echo "Imagick is already compiled with webp. Nothing to do :)" &&
12
- echo ":)"
13
- }
14
-
15
- #convert -version | grep 'webp' || {
16
-
17
- convert -list delegate | grep 'webp =>' || {
18
- export CORES=$(nproc) &&
19
- export LIBWEBP_VERSION=1.0.2 &&
20
- echo "Using $CORES cores for compiling..." &&
21
- cd /tmp &&
22
- curl -O https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP_VERSION.tar.gz &&
23
- tar xzf libwebp-$LIBWEBP_VERSION.tar.gz &&
24
- cd libwebp-* &&
25
- ./configure --prefix=$HOME/opt &&
26
- make -j$CORES &&
27
- make install -j$CORES &&
28
- cd /tmp &&
29
- curl -O https://www.imagemagick.org/download/ImageMagick-$IMAGEMAGICK_VERSION.tar.gz &&
30
- tar xzf ImageMagick-$IMAGEMAGICK_VERSION.tar.gz &&
31
- cd ImageMagick-* &&
32
- ./configure --prefix=$HOME/opt --with-webp=yes &&
33
- make -j$CORES &&
34
- make install -j$CORES &&
35
- $HOME/opt/bin/magick -version | grep $IMAGEMAGICK_VERSION
36
- }
37
-
38
- export LD_FLAGS=-L$HOME/opt/lib
39
- export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:$HOME/opt/lib
40
- export CPATH=$CPATH:$HOME/opt/include
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/install-vips.sh DELETED
@@ -1,40 +0,0 @@
1
- #!/bin/bash
2
-
3
- if ! [[ $VIPS_VERSION ]]; then
4
- export VIPS_VERSION="8.7.4"
5
- fi;
6
-
7
- export PATH=$HOME/vips/bin:$PATH
8
- export LD_LIBRARY_PATH=$HOME/vips/lib:$LD_LIBRARY_PATH
9
- export PKG_CONFIG_PATH=$HOME/vips/lib/pkgconfig:$PKG_CONFIG_PATH
10
- export PYTHONPATH=$HOME/vips/lib/python2.7/site-packages:$PYTHONPATH
11
- export GI_TYPELIB_PATH=$HOME/vips/lib/girepository-1.0:$GI_TYPELIB_PATH
12
-
13
- vips_site=https://github.com/libvips/libvips/releases/download
14
-
15
- set -e
16
-
17
- # do we already have the correct vips built? early exit if yes
18
- # we could check the configure params as well I guess
19
- if [ -d "$HOME/vips/bin" ]; then
20
- installed_version=$($HOME/vips/bin/vips --version)
21
- escaped_version="${VIPS_VERSION//\./\\.}"
22
- echo "Need vips-$version"
23
- echo "Found $installed_version"
24
- if [[ "$installed_version" =~ ^vips-$escaped_version ]]; then
25
- echo "Using cached directory"
26
- exit 0
27
- fi
28
- fi
29
-
30
- rm -rf $HOME/vips
31
- echo "wget: $vips_site/v$VIPS_VERSION/vips-$VIPS_VERSION.tar.gz"
32
- wget $vips_site/v$VIPS_VERSION/vips-$VIPS_VERSION.tar.gz
33
- tar xf vips-$VIPS_VERSION.tar.gz
34
- cd vips-$VIPS_VERSION
35
- CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 ./configure --prefix=$HOME/vips --disable-debug --disable-dependency-tracking --disable-introspection --disable-static --enable-gtk-doc-html=no --enable-gtk-doc=no --enable-pyvips8=no --without-orc --without-python
36
- make && make install
37
-
38
- # Install PHP extension
39
- # ----------------------
40
- yes '' | pecl install vips
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/src-build/webp-convert.inc DELETED
@@ -1,7167 +0,0 @@
1
- <?php
2
- ?><?php
3
-
4
- namespace WebPConvert\Options;
5
-
6
- use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
7
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
8
-
9
- /**
10
- * (base) option class.
11
- *
12
- * @package WebPConvert
13
- * @author Bjørn Rosell <it@rosell.dk>
14
- * @since Class available since Release 2.0.0
15
- */
16
- class Option
17
- {
18
- /** @var string The id of the option */
19
- protected $id;
20
-
21
- /** @var mixed The default value of the option */
22
- protected $defaultValue;
23
-
24
- /** @var mixed The value of the option */
25
- protected $value;
26
-
27
- /** @var boolean Whether the value has been explicitly set */
28
- protected $isExplicitlySet = false;
29
-
30
- /**
31
- * Constructor.
32
- *
33
- * @param string $id id of the option
34
- * @param mixed $defaultValue default value for the option
35
- * @throws InvalidOptionValueException if the default value cannot pass the check
36
- * @throws InvalidOptionTypeException if the default value is wrong type
37
- * @return void
38
- */
39
- public function __construct($id, $defaultValue)
40
- {
41
- $this->id = $id;
42
- $this->defaultValue = $defaultValue;
43
-
44
- // Check that default value is ok
45
- $this->check();
46
- }
47
-
48
- /**
49
- * Get Id.
50
- *
51
- * @return string The id of the option
52
- */
53
- public function getId()
54
- {
55
- return $this->id;
56
- }
57
-
58
- /**
59
- * Get default value.
60
- *
61
- * @return mixed The default value for the option
62
- */
63
- public function getDefaultValue()
64
- {
65
- return $this->defaultValue;
66
- }
67
-
68
-
69
- /**
70
- * Get value, or default value if value has not been explicitly set.
71
- *
72
- * @return mixed The value/default value
73
- */
74
- public function getValue()
75
- {
76
- if (!$this->isExplicitlySet) {
77
- return $this->defaultValue;
78
- } else {
79
- return $this->value;
80
- }
81
- }
82
-
83
- /**
84
- * Get to know if value has been explicitly set.
85
- *
86
- * @return boolean Whether or not the value has been set explicitly
87
- */
88
- public function isValueExplicitlySet()
89
- {
90
- return $this->isExplicitlySet;
91
- }
92
-
93
- /**
94
- * Set value
95
- *
96
- * @param mixed $value The value
97
- * @return void
98
- */
99
- public function setValue($value)
100
- {
101
- $this->isExplicitlySet = true;
102
- $this->value = $value;
103
- }
104
-
105
- /**
106
- * Check if the value is valid.
107
- *
108
- * This base class does no checking, but this method is overridden by most other options.
109
- * @return void
110
- */
111
- public function check()
112
- {
113
- }
114
-
115
- /**
116
- * Helpful function for checking type - used by subclasses.
117
- *
118
- * @param string $expectedType The expected type, ie 'string'
119
- * @throws InvalidOptionTypeException If the type is invalid
120
- * @return void
121
- */
122
- protected function checkType($expectedType)
123
- {
124
- if (gettype($this->getValue()) != $expectedType) {
125
- throw new InvalidOptionTypeException(
126
- 'The "' . $this->id . '" option must be a ' . $expectedType .
127
- ' (you provided a ' . gettype($this->getValue()) . ')'
128
- );
129
- }
130
- }
131
-
132
- public function getValueForPrint()
133
- {
134
- return print_r($this->getValue(), true);
135
- }
136
- }
137
-
138
- ?><?php
139
-
140
- // TODO:
141
- // Read this: https://sourcemaking.com/design_patterns/strategy
142
-
143
- namespace WebPConvert\Convert\Converters;
144
-
145
- use WebPConvert\Helpers\InputValidator;
146
- use WebPConvert\Helpers\MimeType;
147
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
148
- use WebPConvert\Convert\Converters\BaseTraits\AutoQualityTrait;
149
- use WebPConvert\Convert\Converters\BaseTraits\DestinationPreparationTrait;
150
- use WebPConvert\Convert\Converters\BaseTraits\LoggerTrait;
151
- use WebPConvert\Convert\Converters\BaseTraits\OptionsTrait;
152
- use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
153
- use WebPConvert\Exceptions\WebPConvertException;
154
- use WebPConvert\Loggers\BaseLogger;
155
-
156
- /**
157
- * Base for all converter classes.
158
- *
159
- * @package WebPConvert
160
- * @author Bjørn Rosell <it@rosell.dk>
161
- * @since Class available since Release 2.0.0
162
- */
163
- abstract class AbstractConverter
164
- {
165
- use AutoQualityTrait;
166
- use OptionsTrait;
167
- use WarningLoggerTrait;
168
- use DestinationPreparationTrait;
169
- use LoggerTrait;
170
-
171
- /**
172
- * The actual conversion is be done by a concrete converter extending this class.
173
- *
174
- * At the stage this method is called, the abstract converter has taken preparational steps.
175
- * - It has created the destination folder (if neccesary)
176
- * - It has checked the input (valid mime type)
177
- * - It has set up an error handler, mostly in order to catch and log warnings during the doConvert fase
178
- *
179
- * Note: This method is not meant to be called from the outside. Use the static *convert* method for converting
180
- * or, if you wish, create an instance with ::createInstance() and then call ::doConvert()
181
- *
182
- * @throws ConversionFailedException in case conversion failed in an antipiciated way (or subclass)
183
- * @throws \Exception in case conversion failed in an unantipiciated way
184
- */
185
- abstract protected function doActualConvert();
186
-
187
- /**
188
- * Whether or not the converter supports lossless encoding (even for jpegs)
189
- *
190
- * PS: Converters that supports lossless encoding all use the EncodingAutoTrait, which
191
- * overrides this function.
192
- *
193
- * @return boolean Whether the converter supports lossless encoding (even for jpegs).
194
- */
195
- public function supportsLossless()
196
- {
197
- return false;
198
- }
199
-
200
- /** @var string The filename of the image to convert (complete path) */
201
- protected $source;
202
-
203
- /** @var string Where to save the webp (complete path) */
204
- protected $destination;
205
-
206
- /**
207
- * Check basis operationality
208
- *
209
- * Converters may override this method for the purpose of performing basic operationaly checks. It is for
210
- * running general operation checks for a conversion method.
211
- * If some requirement is not met, it should throw a ConverterNotOperationalException (or subtype)
212
- *
213
- * The method is called internally right before calling doActualConvert() method.
214
- * - It SHOULD take options into account when relevant. For example, a missing api key for a
215
- * cloud converter should be detected here
216
- * - It should NOT take the actual filename into consideration, as the purpose is *general*
217
- * For that pupose, converters should override checkConvertability
218
- * Also note that doConvert method is allowed to throw ConverterNotOperationalException too.
219
- *
220
- * @return void
221
- */
222
- public function checkOperationality()
223
- {
224
- }
225
-
226
- /**
227
- * Converters may override this for the purpose of performing checks on the concrete file.
228
- *
229
- * This can for example be used for rejecting big uploads in cloud converters or rejecting unsupported
230
- * image types.
231
- *
232
- * @return void
233
- */
234
- public function checkConvertability()
235
- {
236
- }
237
-
238
- /**
239
- * Constructor.
240
- *
241
- * @param string $source path to source file
242
- * @param string $destination path to destination
243
- * @param array $options (optional) options for conversion
244
- * @param BaseLogger $logger (optional)
245
- */
246
- public function __construct($source, $destination, $options = [], $logger = null)
247
- {
248
- InputValidator::checkSourceAndDestination($source, $destination);
249
-
250
- $this->source = $source;
251
- $this->destination = $destination;
252
-
253
- $this->setLogger($logger);
254
- $this->setProvidedOptions($options);
255
-
256
- if (!isset($this->options['_skip_input_check'])) {
257
- $this->log('WebP Convert 2.0.5', 'italic');
258
- $this->logLn(' ignited.');
259
- $this->logLn('- PHP version: ' . phpversion());
260
- if (isset($_SERVER['SERVER_SOFTWARE'])) {
261
- $this->logLn('- Server software: ' . $_SERVER['SERVER_SOFTWARE']);
262
- }
263
- $this->logLn('');
264
- $this->logLn(self::getConverterDisplayName() . ' converter ignited');
265
- }
266
- }
267
-
268
- /**
269
- * Get source.
270
- *
271
- * @return string The source.
272
- */
273
- public function getSource()
274
- {
275
- return $this->source;
276
- }
277
-
278
- /**
279
- * Get destination.
280
- *
281
- * @return string The destination.
282
- */
283
- public function getDestination()
284
- {
285
- return $this->destination;
286
- }
287
-
288
- /**
289
- * Set destination.
290
- *
291
- * @param string $destination path to destination
292
- * @return string The destination.
293
- */
294
- public function setDestination($destination)
295
- {
296
- $this->destination = $destination;
297
- }
298
-
299
-
300
- /**
301
- * Get converter name for display (defaults to the class name (short)).
302
- *
303
- * Converters can override this.
304
- *
305
- * @return string A display name, ie "Gd"
306
- */
307
- protected static function getConverterDisplayName()
308
- {
309
- // https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name/25308464
310
- return substr(strrchr('\\' . static::class, '\\'), 1);
311
- }
312
-
313
-
314
- /**
315
- * Get converter id (defaults to the class name lowercased)
316
- *
317
- * Converters can override this.
318
- *
319
- * @return string A display name, ie "Gd"
320
- */
321
- protected static function getConverterId()
322
- {
323
- return strtolower(self::getConverterDisplayName());
324
- }
325
-
326
-
327
- /**
328
- * Create an instance of this class
329
- *
330
- * @param string $source The path to the file to convert
331
- * @param string $destination The path to save the converted file to
332
- * @param array $options (optional)
333
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
334
- *
335
- * @return static
336
- */
337
- public static function createInstance($source, $destination, $options = [], $logger = null)
338
- {
339
-
340
- return new static($source, $destination, $options, $logger);
341
- }
342
-
343
- protected function logReduction($source, $destination)
344
- {
345
- $sourceSize = filesize($source);
346
- $destSize = filesize($destination);
347
- $this->log(round(($sourceSize - $destSize)/$sourceSize * 100) . '% ');
348
- if ($sourceSize < 10000) {
349
- $this->logLn('(went from ' . strval($sourceSize) . ' bytes to '. strval($destSize) . ' bytes)');
350
- } else {
351
- $this->logLn('(went from ' . round($sourceSize/1024) . ' kb to ' . round($destSize/1024) . ' kb)');
352
- }
353
- }
354
-
355
- /**
356
- * Run conversion.
357
- *
358
- * @return void
359
- */
360
- private function doConvertImplementation()
361
- {
362
- $beginTime = microtime(true);
363
-
364
- $this->activateWarningLogger();
365
-
366
- $this->checkOptions();
367
-
368
- // Prepare destination folder
369
- $this->createWritableDestinationFolder();
370
- $this->removeExistingDestinationIfExists();
371
-
372
- if (!isset($this->options['_skip_input_check'])) {
373
- // Check that a file can be written to destination
374
- $this->checkDestinationWritable();
375
- }
376
-
377
- $this->checkOperationality();
378
- $this->checkConvertability();
379
-
380
- if ($this->options['log-call-arguments']) {
381
- $this->logOptions();
382
- $this->logLn('');
383
- }
384
-
385
- $this->runActualConvert();
386
-
387
- $source = $this->source;
388
- $destination = $this->destination;
389
-
390
- if (!@file_exists($destination)) {
391
- throw new ConversionFailedException('Destination file is not there: ' . $destination);
392
- } elseif (@filesize($destination) === 0) {
393
- unlink($destination);
394
- throw new ConversionFailedException('Destination file was completely empty');
395
- } else {
396
- if (!isset($this->options['_suppress_success_message'])) {
397
- $this->ln();
398
- $this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
399
-
400
- $sourceSize = @filesize($source);
401
- if ($sourceSize !== false) {
402
- $this->log(', reducing file size with ');
403
- $this->logReduction($source, $destination);
404
- }
405
- }
406
- }
407
-
408
- $this->deactivateWarningLogger();
409
- }
410
-
411
- //private function logEx
412
- /**
413
- * Start conversion.
414
- *
415
- * Usually you would rather call the static convert method, but alternatively you can call
416
- * call ::createInstance to get an instance and then ::doConvert().
417
- *
418
- * @return void
419
- */
420
- public function doConvert()
421
- {
422
- try {
423
- //trigger_error('hello', E_USER_ERROR);
424
- $this->doConvertImplementation();
425
- } catch (WebPConvertException $e) {
426
- $this->logLn('');
427
- /*
428
- if (isset($e->description) && ($e->description != '')) {
429
- $this->log('Error: ' . $e->description . '. ', 'bold');
430
- } else {
431
- $this->log('Error: ', 'bold');
432
- }
433
- */
434
- $this->log('Error: ', 'bold');
435
- $this->logLn($e->getMessage(), 'bold');
436
- throw $e;
437
- } catch (\Exception $e) {
438
- $className = get_class($e);
439
-
440
- $classNameParts = explode("\\", $className);
441
- $shortClassName = array_pop($classNameParts);
442
-
443
- $this->logLn('');
444
- $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
445
- $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
446
- //$this->logLn('Exception class: ' . $className);
447
-
448
- $this->logLn('Trace:');
449
- foreach ($e->getTrace() as $trace) {
450
- //$this->logLn(print_r($trace, true));
451
- if (isset($trace['file']) && isset($trace['line'])) {
452
- $this->logLn(
453
- $trace['file'] . ':' . $trace['line']
454
- );
455
- }
456
- }
457
- throw $e;
458
- } /*catch (\Error $e) {
459
- $this->logLn('ERROR');
460
- }*/
461
- }
462
-
463
- /**
464
- * Runs the actual conversion (after setup and checks)
465
- * Simply calls the doActualConvert() of the actual converter.
466
- * However, in the EncodingAutoTrait, this method is overridden to make two conversions
467
- * and select the smallest.
468
- *
469
- * @return void
470
- */
471
- protected function runActualConvert()
472
- {
473
- $this->doActualConvert();
474
- }
475
-
476
- /**
477
- * Convert an image to webp.
478
- *
479
- * @param string $source path to source file
480
- * @param string $destination path to destination
481
- * @param array $options (optional) options for conversion
482
- * @param BaseLogger $logger (optional)
483
- *
484
- * @throws ConversionFailedException in case conversion fails in an antipiciated way
485
- * @throws \Exception in case conversion fails in an unantipiciated way
486
- * @return void
487
- */
488
- public static function convert($source, $destination, $options = [], $logger = null)
489
- {
490
- $c = self::createInstance($source, $destination, $options, $logger);
491
- $c->doConvert();
492
- //echo $instance->id;
493
- }
494
-
495
- /**
496
- * Get mime type for image (best guess).
497
- *
498
- * It falls back to using file extension. If that fails too, false is returned
499
- *
500
- * PS: Is it a security risk to fall back on file extension?
501
- * - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
502
- * hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
503
- *
504
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
505
- * false (if it is not an image type that the server knowns about)
506
- * or null (if nothing can be determined)
507
- */
508
- public function getMimeTypeOfSource()
509
- {
510
- return MimeType::getMimeTypeDetectionResult($this->source);
511
- }
512
- }
513
-
514
- ?><?php
515
-
516
- namespace WebPConvert\Exceptions;
517
-
518
- /**
519
- * WebPConvertException is the base exception for all exceptions in this library.
520
- *
521
- * Note that the parameters for the constructor differs from that of the Exception class.
522
- * We do not use exception code here, but are instead allowing two version of the error message:
523
- * a short version and a long version.
524
- * The short version may not contain special characters or dynamic content.
525
- * The detailed version may.
526
- * If the detailed version isn't provided, getDetailedMessage will return the short version.
527
- *
528
- */
529
- class WebPConvertException extends \Exception
530
- {
531
- public $description = '';
532
- protected $detailedMessage;
533
- protected $shortMessage;
534
-
535
- public function getDetailedMessage()
536
- {
537
- return $this->detailedMessage;
538
- }
539
-
540
- public function getShortMessage()
541
- {
542
- return $this->shortMessage;
543
- }
544
-
545
- public function __construct($shortMessage = "", $detailedMessage = "", $previous = null)
546
- {
547
- $detailedMessage = ($detailedMessage != '') ? $detailedMessage : $shortMessage;
548
- $this->detailedMessage = $detailedMessage;
549
- $this->shortMessage = $shortMessage;
550
-
551
- parent::__construct(
552
- $detailedMessage,
553
- 0,
554
- $previous
555
- );
556
- }
557
- }
558
-
559
- ?><?php
560
-
561
- namespace WebPConvert\Convert\Exceptions;
562
-
563
- use WebPConvert\Exceptions\WebPConvertException;
564
-
565
- /**
566
- * ConversionFailedException is the base exception in the hierarchy for conversion errors.
567
- *
568
- * Exception hierarchy from here:
569
- *
570
- * WebpConvertException
571
- * ConversionFailedException
572
- * ConversionSkippedException
573
- * ConverterNotOperationalException
574
- * InvalidApiKeyException
575
- * SystemRequirementsNotMetException
576
- * FileSystemProblemsException
577
- * CreateDestinationFileException
578
- * CreateDestinationFolderException
579
- * InvalidInputException
580
- * ConverterNotFoundException
581
- * InvalidImageTypeException
582
- * InvalidOptionValueException
583
- * TargetNotFoundException
584
- */
585
- class ConversionFailedException extends WebPConvertException
586
- {
587
- //public $description = 'Conversion failed';
588
- public $description = '';
589
- }
590
-
591
- ?><?php
592
-
593
- namespace WebPConvert;
594
-
595
- //use WebPConvert\Convert\Converters\ConverterHelper;
596
- use WebPConvert\Convert\Converters\Stack;
597
- //use WebPConvert\Serve\ServeExistingOrHandOver;
598
- use WebPConvert\Serve\ServeConvertedWebP;
599
- use WebPConvert\Serve\ServeConvertedWebPWithErrorHandling;
600
-
601
- /**
602
- * Convert images to webp and/or serve them.
603
- *
604
- * This class is just a couple of convenience methods for doing conversion and/or
605
- * serving.
606
- *
607
- * @package WebPConvert
608
- * @author Bjørn Rosell <it@rosell.dk>
609
- * @since Class available since Release 2.0.0
610
- */
611
- class WebPConvert
612
- {
613
-
614
- /**
615
- * Convert jpeg or png into webp
616
- *
617
- * Convenience method for calling Stack::convert.
618
- *
619
- * @param string $source The image to convert (absolute,no backslashes)
620
- * Image must be jpeg or png.
621
- * @param string $destination Where to store the converted file (absolute path, no backslashes).
622
- * @param array $options (optional) Array of named options
623
- * The options are documented here:
624
- * https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md
625
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
626
- *
627
- * @throws \WebPConvert\Convert\Exceptions\ConversionFailedException in case conversion fails
628
- * @return void
629
- */
630
- public static function convert($source, $destination, $options = [], $logger = null)
631
- {
632
- Stack::convert($source, $destination, $options, $logger);
633
- }
634
-
635
- /**
636
- * Serve webp image, converting first if neccessary.
637
- *
638
- * If an image already exists, it will be served, unless it is older or larger than the source. (If it is larger,
639
- * the original is served, if it is older, the existing webp will be deleted and a fresh conversion will be made
640
- * and served). In case of error, the action indicated in the 'fail' option will be triggered (default is to serve
641
- * the original). Look up the ServeConvertedWebP:serve() and the ServeConvertedWebPWithErrorHandling::serve()
642
- * methods to learn more.
643
- *
644
- * @param string $source path to source file
645
- * @param string $destination path to destination
646
- * @param array $options (optional) options for serving/converting. The options are documented in the
647
- * ServeConvertedWebPWithErrorHandling::serve() method
648
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
649
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
650
- * @return void
651
- */
652
- public static function serveConverted(
653
- $source,
654
- $destination,
655
- $options = [],
656
- $serveLogger = null,
657
- $convertLogger = null
658
- ) {
659
- //return ServeExistingOrHandOver::serveConverted($source, $destination, $options);
660
- //if (isset($options['handle-errors']) && $options['handle-errors'] === true) {
661
- if (isset($options['fail']) && ($options['fail'] != 'throw')) {
662
- ServeConvertedWebPWithErrorHandling::serve($source, $destination, $options, $serveLogger, $convertLogger);
663
- } else {
664
- ServeConvertedWebP::serve($source, $destination, $options, $serveLogger, $convertLogger);
665
- }
666
- }
667
- }
668
-
669
- ?><?php
670
-
671
- namespace WebPConvert\Options;
672
-
673
- use WebPConvert\Options\Option;
674
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
675
-
676
- /**
677
- * Abstract option class
678
- *
679
- * @package WebPConvert
680
- * @author Bjørn Rosell <it@rosell.dk>
681
- * @since Class available since Release 2.0.0
682
- */
683
- class ArrayOption extends Option
684
- {
685
-
686
- public function check()
687
- {
688
- $this->checkType('array');
689
- }
690
-
691
- public function getValueForPrint()
692
- {
693
- if (count($this->getValue()) == 0) {
694
- return '(empty array)';
695
- } else {
696
- return parent::getValueForPrint();
697
- }
698
- }
699
- }
700
-
701
- ?><?php
702
-
703
- namespace WebPConvert\Options;
704
-
705
- use WebPConvert\Options\Option;
706
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
707
-
708
- /**
709
- * Boolean option
710
- *
711
- * @package WebPConvert
712
- * @author Bjørn Rosell <it@rosell.dk>
713
- * @since Class available since Release 2.0.0
714
- */
715
- class BooleanOption extends Option
716
- {
717
-
718
- public function check()
719
- {
720
- $this->checkType('boolean');
721
- }
722
-
723
- public function getValueForPrint()
724
- {
725
- return ($this->getValue() === true ? 'true' : 'false');
726
- }
727
- }
728
-
729
- ?><?php
730
-
731
- namespace WebPConvert\Options;
732
-
733
- use WebPConvert\Options\Option;
734
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
735
-
736
- /**
737
- * Ghost option
738
- *
739
- * @package WebPConvert
740
- * @author Bjørn Rosell <it@rosell.dk>
741
- * @since Class available since Release 2.0.0
742
- */
743
- class GhostOption extends Option
744
- {
745
-
746
- public function getValueForPrint()
747
- {
748
- return '(not defined for this converter)';
749
- }
750
- }
751
-
752
- ?><?php
753
-
754
- namespace WebPConvert\Options;
755
-
756
- use WebPConvert\Options\Option;
757
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
758
-
759
- /**
760
- * Abstract option class
761
- *
762
- * @package WebPConvert
763
- * @author Bjørn Rosell <it@rosell.dk>
764
- * @since Class available since Release 2.0.0
765
- */
766
- class IntegerOption extends Option
767
- {
768
-
769
- protected $minValue;
770
- protected $maxValue;
771
-
772
- /**
773
- * Constructor.
774
- *
775
- * @param string $id id of the option
776
- * @param integer $defaultValue default value for the option
777
- * @throws InvalidOptionValueException if the default value cannot pass the check
778
- * @return void
779
- */
780
- public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
781
- {
782
- $this->minValue = $minValue;
783
- $this->maxValue = $maxValue;
784
- parent::__construct($id, $defaultValue);
785
- }
786
-
787
- protected function checkMin()
788
- {
789
- if (!is_null($this->minValue) && $this->getValue() < $this->minValue) {
790
- throw new InvalidOptionValueException(
791
- '"' . $this->id . '" option must be set to minimum ' . $this->minValue . '. ' .
792
- 'It was however set to: ' . $this->getValue()
793
- );
794
- }
795
- }
796
-
797
- protected function checkMax()
798
- {
799
- if (!is_null($this->maxValue) && $this->getValue() > $this->maxValue) {
800
- throw new InvalidOptionValueException(
801
- '"' . $this->id . '" option must be set to max ' . $this->maxValue . '. ' .
802
- 'It was however set to: ' . $this->getValue()
803
- );
804
- }
805
- }
806
-
807
- protected function checkMinMax()
808
- {
809
- $this->checkMin();
810
- $this->checkMax();
811
- }
812
-
813
- public function check()
814
- {
815
- $this->checkType('integer');
816
- $this->checkMinMax();
817
- }
818
- }
819
-
820
- ?><?php
821
-
822
- namespace WebPConvert\Options;
823
-
824
- use WebPConvert\Options\IntegerOption;
825
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
826
-
827
- /**
828
- * Abstract option class
829
- *
830
- * @package WebPConvert
831
- * @author Bjørn Rosell <it@rosell.dk>
832
- * @since Class available since Release 2.0.0
833
- */
834
- class IntegerOrNullOption extends IntegerOption
835
- {
836
-
837
- public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
838
- {
839
- parent::__construct($id, $defaultValue, $minValue, $maxValue);
840
- }
841
-
842
- public function check()
843
- {
844
- $this->checkMinMax();
845
-
846
- $valueType = gettype($this->getValue());
847
- if (!in_array($valueType, ['integer', 'NULL'])) {
848
- throw new InvalidOptionValueException(
849
- 'The "' . $this->id . '" option must be either integer or NULL. ' .
850
- 'You however provided a value of type: ' . $valueType
851
- );
852
- }
853
- }
854
-
855
- public function getValueForPrint()
856
- {
857
- if (gettype($this->getValue() == 'NULL')) {
858
- return 'null (not set)';
859
- }
860
- return parent::getValueForPrint();
861
- }
862
- }
863
-
864
- ?><?php
865
-
866
- namespace WebPConvert\Options;
867
-
868
- use WebPConvert\Options\StringOption;
869
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
870
-
871
- /**
872
- * Metadata option. A Comma-separated list ('all', 'none', 'exif', 'icc', 'xmp')
873
- *
874
- * @package WebPConvert
875
- * @author Bjørn Rosell <it@rosell.dk>
876
- * @since Class available since Release 2.0.0
877
- */
878
- class MetadataOption extends StringOption
879
- {
880
-
881
- public function __construct($id, $defaultValue)
882
- {
883
- parent::__construct($id, $defaultValue);
884
- }
885
-
886
- public function check()
887
- {
888
- parent::check();
889
-
890
- $value = $this->getValue();
891
-
892
- if (($value == 'all') || ($value == 'none')) {
893
- return;
894
- }
895
-
896
- foreach (explode(',', $value) as $item) {
897
- if (!in_array($value, ['exif', 'icc', 'xmp'])) {
898
- throw new InvalidOptionValueException(
899
- '"metadata" option must be "all", "none" or a comma-separated list of "exif", "icc" or "xmp". ' .
900
- 'It was however set to: "' . $value . '"'
901
- );
902
- }
903
- }
904
-
905
- //$this->checkType('string');
906
- }
907
- }
908
-
909
- ?><?php
910
-
911
- namespace WebPConvert\Options;
912
-
913
- use WebPConvert\Options\Option;
914
- use WebPConvert\Options\Exceptions\OptionNotFoundException;
915
-
916
- /**
917
- * Handles a collection of options.
918
- *
919
- * @package WebPConvert
920
- * @author Bjørn Rosell <it@rosell.dk>
921
- * @since Class available since Release 2.0.0
922
- */
923
- class Options
924
- {
925
-
926
- /** @var array A map of options, keyed by their id */
927
- private $options = [];
928
-
929
- /**
930
- * Add option.
931
- *
932
- * @param Option $option The option object to add to collection.
933
- * @return void
934
- */
935
- public function addOption($option)
936
- {
937
- $this->options[$option->getId()] = $option;
938
- }
939
-
940
- /**
941
- * Add options.
942
- *
943
- * Conveniently add several options in one call.
944
- *
945
- * @param Option[] ...$options Array of options objects to add
946
- * @return void
947
- */
948
- public function addOptions(...$options)
949
- {
950
- foreach ($options as $option) {
951
- $this->addOption($option);
952
- }
953
- }
954
-
955
- /**
956
- * Set the value of an option.
957
- *
958
- * @param string $id Id of the option
959
- * @param mixed $value Value of the option
960
- * @return void
961
- */
962
- public function setOption($id, $value)
963
- {
964
- if (!isset($this->options[$id])) {
965
- throw new OptionNotFoundException(
966
- 'Could not set option. There is no option called "' . $id . '" in the collection.'
967
- );
968
- }
969
- $option = $this->options[$id];
970
- $option->setValue($value);
971
- }
972
-
973
- /**
974
- * Set option, or create a new, if no such option exists.
975
- *
976
- * @param string $id Id of option to set/create
977
- * @param mixed $value Value of option
978
- * @return void
979
- */
980
- public function setOrCreateOption($id, $value)
981
- {
982
- if (!isset($this->options[$id])) {
983
- $newOption = new GhostOption($id, null);
984
- $newOption->setValue($value);
985
- //$newOption = new Option($id, $value);
986
- $this->addOption($newOption);
987
- } else {
988
- $this->setOption($id, $value);
989
- }
990
- }
991
-
992
- /**
993
- * Get the value of an option in the collection - by id.
994
- *
995
- * @param string $id Id of the option to get
996
- * @throws OptionNotFoundException if the option is not in the collection
997
- * @return mixed The value of the option
998
- */
999
- public function getOption($id)
1000
- {
1001
- if (!isset($this->options[$id])) {
1002
- throw new OptionNotFoundException(
1003
- 'There is no option called "' . $id . '" in the collection.'
1004
- );
1005
- }
1006
- $option = $this->options[$id];
1007
- return $option->getValue();
1008
- }
1009
-
1010
- /**
1011
- * Return map of option objects.
1012
- *
1013
- * @return array map of option objects
1014
- */
1015
- public function getOptionsMap()
1016
- {
1017
- return $this->options;
1018
- }
1019
-
1020
- /**
1021
- * Return flat associative array of options.
1022
- *
1023
- * @return array associative array of options
1024
- */
1025
- public function getOptions()
1026
- {
1027
- $values = [];
1028
- foreach ($this->options as $id => $option) {
1029
- $values[$id] = $option->getValue();
1030
- }
1031
- return $values;
1032
- }
1033
-
1034
- /**
1035
- * Check all options in the collection.
1036
- */
1037
- public function check()
1038
- {
1039
- foreach ($this->options as $id => $option) {
1040
- $option->check();
1041
- }
1042
- }
1043
- }
1044
-
1045
- ?><?php
1046
-
1047
- namespace WebPConvert\Options;
1048
-
1049
- use WebPConvert\Options\Option;
1050
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1051
-
1052
- /**
1053
- * Quality option.
1054
- *
1055
- * Quality can be a number between 0-100 or "auto"
1056
- *
1057
- * @package WebPConvert
1058
- * @author Bjørn Rosell <it@rosell.dk>
1059
- * @since Class available since Release 2.0.0
1060
- */
1061
- class QualityOption extends Option
1062
- {
1063
-
1064
- public function __construct($id, $defaultValue)
1065
- {
1066
- parent::__construct($id, $defaultValue);
1067
- }
1068
-
1069
- public function check()
1070
- {
1071
- $value = $this->getValue();
1072
- if (gettype($value) == 'string') {
1073
- if ($value != 'auto') {
1074
- throw new InvalidOptionValueException(
1075
- 'The "quality" option must be either "auto" or a number between 0-100. ' .
1076
- 'A string, different from "auto" was given'
1077
- );
1078
- }
1079
- } elseif (gettype($value) == 'integer') {
1080
- if (($value < 0) || ($value > 100)) {
1081
- throw new InvalidOptionValueException(
1082
- 'The "quality" option must be either "auto" or a number between 0-100. ' .
1083
- 'The number you provided (' . strval($value) . ') is out of range.'
1084
- );
1085
- }
1086
- } else {
1087
- throw new InvalidOptionValueException(
1088
- 'The "quality" option must be either "auto" or an integer. ' .
1089
- 'You however provided a value of type: ' . gettype($value)
1090
- );
1091
- }
1092
- }
1093
-
1094
- public function getValueForPrint()
1095
- {
1096
- if (gettype($this->getValue()) == 'string') {
1097
- return '"' . $this->getValue() . '"';
1098
- }
1099
- return $this->getValue();
1100
- }
1101
- }
1102
-
1103
- ?><?php
1104
-
1105
- namespace WebPConvert\Options;
1106
-
1107
- use WebPConvert\Options\StringOption;
1108
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1109
-
1110
- /**
1111
- * Abstract option class
1112
- *
1113
- * @package WebPConvert
1114
- * @author Bjørn Rosell <it@rosell.dk>
1115
- * @since Class available since Release 2.0.0
1116
- */
1117
- class SensitiveArrayOption extends ArrayOption
1118
- {
1119
-
1120
- public function check()
1121
- {
1122
- parent::check();
1123
- }
1124
-
1125
- public function getValueForPrint()
1126
- {
1127
- if (count($this->getValue()) == 0) {
1128
- return '(empty array)';
1129
- } else {
1130
- return '(array of ' . count($this->getValue()) . ' items)';
1131
- }
1132
- //return '*****';
1133
- }
1134
- }
1135
-
1136
- ?><?php
1137
-
1138
- namespace WebPConvert\Options;
1139
-
1140
- use WebPConvert\Options\StringOption;
1141
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1142
-
1143
- /**
1144
- * Abstract option class
1145
- *
1146
- * @package WebPConvert
1147
- * @author Bjørn Rosell <it@rosell.dk>
1148
- * @since Class available since Release 2.0.0
1149
- */
1150
- class SensitiveStringOption extends StringOption
1151
- {
1152
-
1153
- public function __construct($id, $defaultValue, $allowedValues = null)
1154
- {
1155
- parent::__construct($id, $defaultValue, $allowedValues);
1156
- }
1157
-
1158
- public function check()
1159
- {
1160
- parent::check();
1161
- }
1162
-
1163
- public function getValueForPrint()
1164
- {
1165
- if (strlen($this->getValue()) == 0) {
1166
- return '""';
1167
- }
1168
- return '*****';
1169
- }
1170
- }
1171
-
1172
- ?><?php
1173
-
1174
- namespace WebPConvert\Options;
1175
-
1176
- use WebPConvert\Options\Option;
1177
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1178
-
1179
- /**
1180
- * Abstract option class
1181
- *
1182
- * @package WebPConvert
1183
- * @author Bjørn Rosell <it@rosell.dk>
1184
- * @since Class available since Release 2.0.0
1185
- */
1186
- class StringOption extends Option
1187
- {
1188
-
1189
- public $allowedValues;
1190
-
1191
- public function __construct($id, $defaultValue, $allowedValues = null)
1192
- {
1193
- $this->allowedValues = $allowedValues;
1194
- parent::__construct($id, $defaultValue);
1195
- }
1196
-
1197
- public function check()
1198
- {
1199
- $this->checkType('string');
1200
-
1201
- if (!is_null($this->allowedValues) && (!in_array($this->getValue(), $this->allowedValues))) {
1202
- throw new InvalidOptionValueException(
1203
- '"' . $this->id . '" option must be on of these values: ' .
1204
- '[' . implode(', ', $this->allowedValues) . ']. ' .
1205
- 'It was however set to: "' . $this->getValue() . '"'
1206
- );
1207
- }
1208
- }
1209
-
1210
- public function getValueForPrint()
1211
- {
1212
- return '"' . $this->getValue() . '"';
1213
- }
1214
- }
1215
-
1216
- ?><?php
1217
-
1218
- namespace WebPConvert\Convert\Converters\BaseTraits;
1219
-
1220
- use WebPConvert\Convert\Helpers\JpegQualityDetector;
1221
-
1222
- /**
1223
- * Trait for handling the "quality:auto" option.
1224
- *
1225
- * This trait is only used in the AbstractConverter class. It has been extracted into a
1226
- * trait in order to bundle the methods concerning auto quality.
1227
- *
1228
- * @package WebPConvert
1229
- * @author Bjørn Rosell <it@rosell.dk>
1230
- * @since Class available since Release 2.0.0
1231
- */
1232
- trait AutoQualityTrait
1233
- {
1234
-
1235
- abstract public function logLn($msg, $style = '');
1236
- abstract public function getMimeTypeOfSource();
1237
-
1238
- /** @var boolean Whether the quality option has been processed or not */
1239
- private $processed = false;
1240
-
1241
- /** @var boolean Whether the quality of the source could be detected or not (set upon processing) */
1242
- private $qualityCouldNotBeDetected = false;
1243
-
1244
- /** @var integer The calculated quality (set upon processing - on successful detection) */
1245
- private $calculatedQuality;
1246
-
1247
-
1248
- /**
1249
- * Determine if quality detection is required but failing.
1250
- *
1251
- * It is considered "required" when:
1252
- * - Mime type is "image/jpeg"
1253
- * - Quality is set to "auto"
1254
- *
1255
- * If quality option hasn't been proccessed yet, it is triggered.
1256
- *
1257
- * @return boolean
1258
- */
1259
- public function isQualityDetectionRequiredButFailing()
1260
- {
1261
- $this->processQualityOptionIfNotAlready();
1262
- return $this->qualityCouldNotBeDetected;
1263
- }
1264
-
1265
- /**
1266
- * Get calculated quality.
1267
- *
1268
- * If the "quality" option is a number, that number is returned.
1269
- * If mime type of source is something else than "image/jpeg", the "default-quality" option is returned
1270
- * If quality is "auto" and source is a jpeg image, it will be attempted to detect jpeg quality.
1271
- * In case of failure, the value of the "default-quality" option is returned.
1272
- * In case of success, the detected quality is returned, or the value of the "max-quality" if that is lower.
1273
- *
1274
- * @return int
1275
- */
1276
- public function getCalculatedQuality()
1277
- {
1278
- $this->processQualityOptionIfNotAlready();
1279
- return $this->calculatedQuality;
1280
- }
1281
-
1282
- /**
1283
- * Process the quality option if it is not already processed.
1284
- *
1285
- * @return void
1286
- */
1287
- private function processQualityOptionIfNotAlready()
1288
- {
1289
- if (!$this->processed) {
1290
- $this->processed = true;
1291
- $this->processQualityOption();
1292
- }
1293
- }
1294
-
1295
- /**
1296
- * Process the quality option.
1297
- *
1298
- * Sets the private property "calculatedQuality" according to the description for the getCalculatedQuality
1299
- * function.
1300
- * In case quality detection was attempted and failed, the private property "qualityCouldNotBeDetected" is set
1301
- * to true. This is used by the "isQualityDetectionRequiredButFailing" (and documented there too).
1302
- *
1303
- * @return void
1304
- */
1305
- private function processQualityOption()
1306
- {
1307
- $options = $this->options;
1308
- $source = $this->source;
1309
-
1310
- $q = $options['quality'];
1311
- if ($q == 'auto') {
1312
- if (($this->/** @scrutinizer ignore-call */getMimeTypeOfSource() == 'image/jpeg')) {
1313
- $q = JpegQualityDetector::detectQualityOfJpg($source);
1314
- if (is_null($q)) {
1315
- $q = $options['default-quality'];
1316
- $this->/** @scrutinizer ignore-call */logLn(
1317
- 'Quality of source could not be established (Imagick or GraphicsMagick is required)' .
1318
- ' - Using default instead (' . $options['default-quality'] . ').'
1319
- );
1320
-
1321
- $this->qualityCouldNotBeDetected = true;
1322
- } else {
1323
- if ($q > $options['max-quality']) {
1324
- $this->logLn(
1325
- 'Quality of source is ' . $q . '. ' .
1326
- 'This is higher than max-quality, so using max-quality instead (' .
1327
- $options['max-quality'] . ')'
1328
- );
1329
- } else {
1330
- $this->logLn('Quality set to same as source: ' . $q);
1331
- }
1332
- }
1333
- $q = min($q, $options['max-quality']);
1334
- } else {
1335
- //$q = $options['default-quality'];
1336
- $q = min($options['default-quality'], $options['max-quality']);
1337
- $this->logLn('Quality: ' . $q . '. ');
1338
- }
1339
- } else {
1340
- $this->logLn(
1341
- 'Quality: ' . $q . '. '
1342
- );
1343
- if (($this->getMimeTypeOfSource() == 'image/jpeg')) {
1344
- $this->logLn(
1345
- 'Consider setting quality to "auto" instead. It is generally a better idea'
1346
- );
1347
- }
1348
- }
1349
- $this->calculatedQuality = $q;
1350
- }
1351
- }
1352
-
1353
- ?><?php
1354
-
1355
- namespace WebPConvert\Convert\Converters\BaseTraits;
1356
-
1357
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
1358
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
1359
-
1360
- /**
1361
- * Trait for handling options
1362
- *
1363
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1364
- * trait in order to bundle the methods concerning options.
1365
- *
1366
- * @package WebPConvert
1367
- * @author Bjørn Rosell <it@rosell.dk>
1368
- * @since Class available since Release 2.0.0
1369
- */
1370
- trait DestinationPreparationTrait
1371
- {
1372
-
1373
- abstract public function getDestination();
1374
- abstract public function logLn($msg, $style = '');
1375
-
1376
- /**
1377
- * Create writable folder in provided path (if it does not exist already)
1378
- *
1379
- * @throws CreateDestinationFolderException if folder cannot be removed
1380
- * @return void
1381
- */
1382
- private function createWritableDestinationFolder()
1383
- {
1384
- $destination = $this->getDestination();
1385
-
1386
- $folder = dirname($destination);
1387
- if (!file_exists($folder)) {
1388
- $this->logLn('Destination folder does not exist. Creating folder: ' . $folder);
1389
- // TODO: what if this is outside open basedir?
1390
- // see http://php.net/manual/en/ini.core.php#ini.open-basedir
1391
-
1392
- // Trying to create the given folder (recursively)
1393
- if (!mkdir($folder, 0777, true)) {
1394
- throw new CreateDestinationFolderException(
1395
- 'Failed creating folder. Check the permissions!',
1396
- 'Failed creating folder: ' . $folder . '. Check permissions!'
1397
- );
1398
- }
1399
- }
1400
- }
1401
-
1402
- /**
1403
- * Check that we can write file at destination.
1404
- *
1405
- * It is assumed that the folder already exists (that ::createWritableDestinationFolder() was called first)
1406
- *
1407
- * @throws CreateDestinationFileException if file cannot be created at destination
1408
- * @return void
1409
- */
1410
- private function checkDestinationWritable()
1411
- {
1412
- $destination = $this->getDestination();
1413
- $dirName = dirname($destination);
1414
-
1415
- if (@is_writable($dirName) && @is_executable($dirName)) {
1416
- // all is well
1417
- return;
1418
- }
1419
-
1420
- // The above might fail on Windows, even though dir is writable
1421
- // So, to be absolute sure that we cannot write, we make an actual write test (writing a dummy file)
1422
- // No harm in doing that for non-Windows systems either.
1423
- if (file_put_contents($destination, 'dummy') !== false) {
1424
- // all is well, after all
1425
- unlink($destination);
1426
- return;
1427
- }
1428
-
1429
- throw new CreateDestinationFileException(
1430
- 'Cannot create file: ' . basename($destination) . ' in dir:' . dirname($destination)
1431
- );
1432
- }
1433
-
1434
- /**
1435
- * Remove existing destination.
1436
- *
1437
- * @throws CreateDestinationFileException if file cannot be removed
1438
- * @return void
1439
- */
1440
- private function removeExistingDestinationIfExists()
1441
- {
1442
- $destination = $this->getDestination();
1443
- if (file_exists($destination)) {
1444
- // A file already exists in this folder...
1445
- // We delete it, to make way for a new webp
1446
- if (!unlink($destination)) {
1447
- throw new CreateDestinationFileException(
1448
- 'Existing file cannot be removed: ' . basename($destination)
1449
- );
1450
- }
1451
- }
1452
- }
1453
- }
1454
-
1455
- ?><?php
1456
-
1457
- namespace WebPConvert\Convert\Converters\BaseTraits;
1458
-
1459
- /**
1460
- * Trait for providing logging capabilities.
1461
- *
1462
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1463
- * trait in order to bundle the methods concerning logging.
1464
- *
1465
- * @package WebPConvert
1466
- * @author Bjørn Rosell <it@rosell.dk>
1467
- * @since Class available since Release 2.0.0
1468
- */
1469
- trait LoggerTrait
1470
- {
1471
-
1472
- /** @var \WebPConvert\Loggers\BaseLogger The logger (or null if not set) */
1473
- protected $logger;
1474
-
1475
- /**
1476
- * Set logger
1477
- *
1478
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional) $logger
1479
- * @return void
1480
- */
1481
- public function setLogger($logger = null)
1482
- {
1483
- $this->logger = $logger;
1484
- }
1485
-
1486
- /**
1487
- * Write a line to the logger.
1488
- *
1489
- * @param string $msg The line to write.
1490
- * @param string $style (optional) Ie "italic" or "bold"
1491
- * @return void
1492
- */
1493
- protected function logLn($msg, $style = '')
1494
- {
1495
- if (isset($this->logger)) {
1496
- $this->logger->logLn($msg, $style);
1497
- }
1498
- }
1499
-
1500
- /**
1501
- * New line
1502
- *
1503
- * @return void
1504
- */
1505
- protected function ln()
1506
- {
1507
- if (isset($this->logger)) {
1508
- $this->logger->ln();
1509
- }
1510
- }
1511
-
1512
- /**
1513
- * Write to the logger, without newline
1514
- *
1515
- * @param string $msg What to write.
1516
- * @param string $style (optional) Ie "italic" or "bold"
1517
- * @return void
1518
- */
1519
- protected function log($msg, $style = '')
1520
- {
1521
- if (isset($this->logger)) {
1522
- $this->logger->log($msg, $style);
1523
- }
1524
- }
1525
- }
1526
-
1527
- ?><?php
1528
-
1529
- namespace WebPConvert\Convert\Converters\BaseTraits;
1530
-
1531
- use WebPConvert\Convert\Converters\Stack;
1532
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
1533
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1534
- use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
1535
-
1536
- use WebPConvert\Options\ArrayOption;
1537
- use WebPConvert\Options\BooleanOption;
1538
- use WebPConvert\Options\GhostOption;
1539
- use WebPConvert\Options\IntegerOption;
1540
- use WebPConvert\Options\IntegerOrNullOption;
1541
- use WebPConvert\Options\MetadataOption;
1542
- use WebPConvert\Options\Options;
1543
- use WebPConvert\Options\StringOption;
1544
- use WebPConvert\Options\QualityOption;
1545
-
1546
- /**
1547
- * Trait for handling options
1548
- *
1549
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1550
- * trait in order to bundle the methods concerning options.
1551
- *
1552
- * @package WebPConvert
1553
- * @author Bjørn Rosell <it@rosell.dk>
1554
- * @since Class available since Release 2.0.0
1555
- */
1556
- trait OptionsTrait
1557
- {
1558
-
1559
- abstract public function log($msg, $style = '');
1560
- abstract public function logLn($msg, $style = '');
1561
- abstract protected function getMimeTypeOfSource();
1562
-
1563
- /** @var array Provided conversion options */
1564
- public $providedOptions;
1565
-
1566
- /** @var array Calculated conversion options (merge of default options and provided options)*/
1567
- protected $options;
1568
-
1569
- /** @var Options */
1570
- protected $options2;
1571
-
1572
-
1573
- /**
1574
- * Create options.
1575
- *
1576
- * The options created here will be available to all converters.
1577
- * Individual converters may add options by overriding this method.
1578
- *
1579
- * @return void
1580
- */
1581
- protected function createOptions()
1582
- {
1583
- $isPng = ($this->getMimeTypeOfSource() == 'image/png');
1584
-
1585
- $this->options2 = new Options();
1586
- $this->options2->addOptions(
1587
- new IntegerOption('alpha-quality', 85, 0, 100),
1588
- new BooleanOption('auto-filter', false),
1589
- new IntegerOption('default-quality', ($isPng ? 85 : 75), 0, 100),
1590
- new StringOption('encoding', 'auto', ['lossy', 'lossless', 'auto']),
1591
- new BooleanOption('low-memory', false),
1592
- new BooleanOption('log-call-arguments', false),
1593
- new IntegerOption('max-quality', 85, 0, 100),
1594
- new MetadataOption('metadata', 'none'),
1595
- new IntegerOption('method', 6, 0, 6),
1596
- new IntegerOption('near-lossless', 60, 0, 100),
1597
- new StringOption('preset', 'none', ['none', 'default', 'photo', 'picture', 'drawing', 'icon', 'text']),
1598
- new QualityOption('quality', ($isPng ? 85 : 'auto')),
1599
- new IntegerOrNullOption('size-in-percentage', null, 0, 100),
1600
- new BooleanOption('skip', false),
1601
- new BooleanOption('use-nice', false),
1602
- new ArrayOption('jpeg', []),
1603
- new ArrayOption('png', [])
1604
- );
1605
- }
1606
-
1607
- /**
1608
- * Set "provided options" (options provided by the user when calling convert().
1609
- *
1610
- * This also calculates the protected options array, by merging in the default options, merging
1611
- * jpeg and png options and merging prefixed options (such as 'vips-quality').
1612
- * The resulting options array are set in the protected property $this->options and can be
1613
- * retrieved using the public ::getOptions() function.
1614
- *
1615
- * @param array $providedOptions (optional)
1616
- * @return void
1617
- */
1618
- public function setProvidedOptions($providedOptions = [])
1619
- {
1620
- $this->createOptions();
1621
-
1622
- $this->providedOptions = $providedOptions;
1623
-
1624
- if (isset($this->providedOptions['png'])) {
1625
- if ($this->getMimeTypeOfSource() == 'image/png') {
1626
- $this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['png']);
1627
- // $this->logLn(print_r($this->providedOptions, true));
1628
- unset($this->providedOptions['png']);
1629
- }
1630
- }
1631
-
1632
- if (isset($this->providedOptions['jpeg'])) {
1633
- if ($this->getMimeTypeOfSource() == 'image/jpeg') {
1634
- $this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['jpeg']);
1635
- unset($this->providedOptions['jpeg']);
1636
- }
1637
- }
1638
-
1639
- // merge down converter-prefixed options
1640
- $converterId = self::getConverterId();
1641
- $strLen = strlen($converterId);
1642
- foreach ($this->providedOptions as $optionKey => $optionValue) {
1643
- if (substr($optionKey, 0, $strLen + 1) == ($converterId . '-')) {
1644
- $this->providedOptions[substr($optionKey, $strLen + 1)] = $optionValue;
1645
- }
1646
- }
1647
-
1648
- // Create options (Option objects)
1649
- foreach ($this->providedOptions as $optionId => $optionValue) {
1650
- $this->options2->setOrCreateOption($optionId, $optionValue);
1651
- }
1652
- //$this->logLn(print_r($this->options2->getOptions(), true));
1653
- //$this->logLn($this->options2->getOption('hello'));
1654
-
1655
- // Create flat associative array of options
1656
- $this->options = $this->options2->getOptions();
1657
-
1658
- // - Merge $defaultOptions into provided options
1659
- //$this->options = array_merge($this->getDefaultOptions(), $this->providedOptions);
1660
-
1661
- //$this->logOptions();
1662
- }
1663
-
1664
- /**
1665
- * Get the resulting options after merging provided options with default options.
1666
- *
1667
- * Note that the defaults depends on the mime type of the source. For example, the default value for quality
1668
- * is "auto" for jpegs, and 85 for pngs.
1669
- *
1670
- * @return array An associative array of options: ['metadata' => 'none', ...]
1671
- */
1672
- public function getOptions()
1673
- {
1674
- return $this->options;
1675
- }
1676
-
1677
- /**
1678
- * Change an option specifically.
1679
- *
1680
- * This method is probably rarely neeeded. We are using it to change the "encoding" option temporarily
1681
- * in the EncodingAutoTrait.
1682
- *
1683
- * @param string $id Id of option (ie "metadata")
1684
- * @param mixed $value The new value.
1685
- * @return void
1686
- */
1687
- protected function setOption($id, $value)
1688
- {
1689
- $this->options[$id] = $value;
1690
- $this->options2->setOrCreateOption($id, $value);
1691
- }
1692
-
1693
- /**
1694
- * Check options.
1695
- *
1696
- * @throws InvalidOptionTypeException if an option have wrong type
1697
- * @throws InvalidOptionValueException if an option value is out of range
1698
- * @throws ConversionSkippedException if 'skip' option is set to true
1699
- * @return void
1700
- */
1701
- protected function checkOptions()
1702
- {
1703
- $this->options2->check();
1704
-
1705
- if ($this->options['skip']) {
1706
- if (($this->getMimeTypeOfSource() == 'image/png') && isset($this->options['png']['skip'])) {
1707
- throw new ConversionSkippedException(
1708
- 'skipped conversion (configured to do so for PNG)'
1709
- );
1710
- } else {
1711
- throw new ConversionSkippedException(
1712
- 'skipped conversion (configured to do so)'
1713
- );
1714
- }
1715
- }
1716
- }
1717
-
1718
- public function logOptions()
1719
- {
1720
- $this->logLn('');
1721
- $this->logLn('Options:');
1722
- $this->logLn('------------');
1723
- $this->logLn(
1724
- 'The following options have been set explicitly. ' .
1725
- 'Note: it is the resulting options after merging down the "jpeg" and "png" options and any ' .
1726
- 'converter-prefixed options.'
1727
- );
1728
- $this->logLn('- source: ' . $this->source);
1729
- $this->logLn('- destination: ' . $this->destination);
1730
-
1731
- $unsupported = $this->getUnsupportedDefaultOptions();
1732
- //$this->logLn('Unsupported:' . print_r($this->getUnsupportedDefaultOptions(), true));
1733
- $ignored = [];
1734
- $implicit = [];
1735
- foreach ($this->options2->getOptionsMap() as $id => $option) {
1736
- if (($id == 'png') || ($id == 'jpeg')) {
1737
- continue;
1738
- }
1739
- if ($option->isValueExplicitlySet()) {
1740
- if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
1741
- //$this->log(' (note: this option is ignored by this converter)');
1742
- if (($id != '_skip_input_check') && ($id != '_suppress_success_message')) {
1743
- $ignored[] = $option;
1744
- }
1745
- } else {
1746
- $this->log('- ' . $id . ': ');
1747
- $this->log($option->getValueForPrint());
1748
- $this->logLn('');
1749
- }
1750
- } else {
1751
- if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
1752
- } else {
1753
- $implicit[] = $option;
1754
- }
1755
- }
1756
- }
1757
-
1758
- if (count($implicit) > 0) {
1759
- $this->logLn('');
1760
- $this->logLn(
1761
- 'The following options have not been explicitly set, so using the following defaults:'
1762
- );
1763
- foreach ($implicit as $option) {
1764
- $this->log('- ' . $option->getId() . ': ');
1765
- $this->log($option->getValueForPrint());
1766
- $this->logLn('');
1767
- }
1768
- }
1769
- if (count($ignored) > 0) {
1770
- $this->logLn('');
1771
- if ($this instanceof Stack) {
1772
- $this->logLn(
1773
- 'The following options were supplied and are passed on to the converters in the stack:'
1774
- );
1775
- foreach ($ignored as $option) {
1776
- $this->log('- ' . $option->getId() . ': ');
1777
- $this->log($option->getValueForPrint());
1778
- $this->logLn('');
1779
- }
1780
- } else {
1781
- $this->logLn(
1782
- 'The following options were supplied but are ignored because they are not supported by this ' .
1783
- 'converter:'
1784
- );
1785
- foreach ($ignored as $option) {
1786
- $this->logLn('- ' . $option->getId());
1787
- }
1788
- }
1789
- }
1790
- $this->logLn('------------');
1791
- }
1792
-
1793
- // to be overridden by converters
1794
- protected function getUnsupportedDefaultOptions()
1795
- {
1796
- return [];
1797
- }
1798
- }
1799
-
1800
- ?><?php
1801
-
1802
- namespace WebPConvert\Convert\Converters\BaseTraits;
1803
-
1804
- /**
1805
- * Trait for handling warnings (by logging them)
1806
- *
1807
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1808
- * trait in order to bundle the methods concerning options.
1809
- *
1810
- * @package WebPConvert
1811
- * @author Bjørn Rosell <it@rosell.dk>
1812
- * @since Class available since Release 2.0.0
1813
- */
1814
- trait WarningLoggerTrait
1815
- {
1816
- abstract protected function logLn($msg, $style = '');
1817
-
1818
- /** @var string|array|null Previous error handler (stored in order to be able pass warnings on) */
1819
- private $previousErrorHandler;
1820
-
1821
- /**
1822
- * Handle warnings and notices during conversion by logging them and passing them on.
1823
- *
1824
- * The function is a callback used with "set_error_handler".
1825
- * It is declared public because it needs to be accessible from the point where the warning happened.
1826
- *
1827
- * @param integer $errno
1828
- * @param string $errstr
1829
- * @param string $errfile
1830
- * @param integer $errline
1831
- *
1832
- * @return false|null
1833
- */
1834
- public function warningHandler($errno, $errstr, $errfile, $errline)
1835
- {
1836
- /*
1837
- We do NOT do the following (even though it is generally recommended):
1838
-
1839
- if (!(error_reporting() & $errno)) {
1840
- // This error code is not included in error_reporting, so let it fall
1841
- // through to the standard PHP error handler
1842
- return false;
1843
- }
1844
-
1845
- - Because we want to log all warnings and errors (also the ones that was suppressed with @)
1846
- https://secure.php.net/manual/en/language.operators.errorcontrol.php
1847
- */
1848
-
1849
- $errorTypes = [
1850
- E_WARNING => "Warning",
1851
- E_NOTICE => "Notice",
1852
- E_STRICT => "Strict Notice",
1853
- E_DEPRECATED => "Deprecated",
1854
- E_USER_DEPRECATED => "User Deprecated",
1855
-
1856
- /*
1857
- The following can never be catched by a custom error handler:
1858
- E_PARSE, E_ERROR, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
1859
-
1860
- We do do not currently trigger the following:
1861
- E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
1862
-
1863
- But we may want to do that at some point, like this:
1864
- trigger_error('Your version of Gd is very old', E_USER_WARNING);
1865
- in that case, remember to add them to this array
1866
- */
1867
- ];
1868
-
1869
- if (isset($errorTypes[$errno])) {
1870
- $errType = $errorTypes[$errno];
1871
- } else {
1872
- $errType = "Unknown error/warning/notice ($errno)";
1873
- }
1874
-
1875
- $msg = $errType . ': ' . $errstr . ' in ' . $errfile . ', line ' . $errline . ', PHP ' . PHP_VERSION .
1876
- ' (' . PHP_OS . ')';
1877
- $this->logLn('');
1878
- $this->logLn($msg, 'italic');
1879
- $this->logLn('');
1880
-
1881
- //echo 'previously defined handler:' . print_r($this->previousErrorHandler, true);
1882
-
1883
- if (!is_null($this->previousErrorHandler)) {
1884
- return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline);
1885
- } else {
1886
- return false;
1887
- }
1888
- }
1889
-
1890
- /**
1891
- * Activate warning logger.
1892
- *
1893
- * Sets the error handler and stores the previous so our error handler can bubble up warnings
1894
- *
1895
- * @return void
1896
- */
1897
- protected function activateWarningLogger()
1898
- {
1899
- $this->previousErrorHandler = set_error_handler(
1900
- array($this, "warningHandler"),
1901
- E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
1902
- );
1903
- }
1904
-
1905
- /**
1906
- * Deactivate warning logger.
1907
- *
1908
- * Restores the previous error handler.
1909
- *
1910
- * @return void
1911
- */
1912
- protected function deactivateWarningLogger()
1913
- {
1914
- restore_error_handler();
1915
- }
1916
- }
1917
-
1918
- ?><?php
1919
-
1920
- namespace WebPConvert\Convert\Converters\ConverterTraits;
1921
-
1922
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
1923
- use WebPConvert\Convert\Converters\AbstractConverter;
1924
- use WebPConvert\Convert\Helpers\PhpIniSizes;
1925
-
1926
- /**
1927
- * Trait for converters that works by uploading to a cloud service.
1928
- *
1929
- * The trait adds a method for checking against upload limits.
1930
- *
1931
- * @package WebPConvert
1932
- * @author Bjørn Rosell <it@rosell.dk>
1933
- * @since Class available since Release 2.0.0
1934
- */
1935
- trait CloudConverterTrait
1936
- {
1937
-
1938
- /**
1939
- * Test that filesize is below "upload_max_filesize" and "post_max_size" values in php.ini.
1940
- *
1941
- * @param string $iniSettingId Id of ini setting (ie "upload_max_filesize")
1942
- *
1943
- * @throws ConversionFailedException if filesize is larger than the ini setting
1944
- * @return void
1945
- */
1946
- private function checkFileSizeVsIniSetting($iniSettingId)
1947
- {
1948
- $fileSize = @filesize($this->source);
1949
- if ($fileSize === false) {
1950
- return;
1951
- }
1952
- $sizeInIni = PhpIniSizes::getIniBytes($iniSettingId);
1953
- if ($sizeInIni === false) {
1954
- // Not sure if we should throw an exception here, or not...
1955
- return;
1956
- }
1957
- if ($sizeInIni < $fileSize) {
1958
- throw new ConversionFailedException(
1959
- 'File is larger than your ' . $iniSettingId . ' (set in your php.ini). File size:' .
1960
- round($fileSize/1024) . ' kb. ' .
1961
- $iniSettingId . ' in php.ini: ' . ini_get($iniSettingId) .
1962
- ' (parsed as ' . round($sizeInIni/1024) . ' kb)'
1963
- );
1964
- }
1965
- }
1966
-
1967
- /**
1968
- * Check convertability of cloud converters (that file is not bigger than limits set in php.ini).
1969
- *
1970
- * Performs the same as ::Convertability(). It is here so converters that overrides the
1971
- * ::Convertability() still has a chance to do the checks.
1972
- *
1973
- * @throws ConversionFailedException if filesize is larger than "upload_max_filesize" or "post_max_size"
1974
- * @return void
1975
- */
1976
- public function checkConvertabilityCloudConverterTrait()
1977
- {
1978
- $this->checkFileSizeVsIniSetting('upload_max_filesize');
1979
- $this->checkFileSizeVsIniSetting('post_max_size');
1980
- }
1981
-
1982
- /**
1983
- * Check convertability of cloud converters (file upload limits).
1984
- */
1985
- public function checkConvertability()
1986
- {
1987
- $this->checkConvertabilityCloudConverterTrait();
1988
- }
1989
- }
1990
-
1991
- ?><?php
1992
-
1993
- namespace WebPConvert\Convert\Converters\ConverterTraits;
1994
-
1995
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
1996
- use WebPConvert\Convert\Converters\AbstractConverter;
1997
-
1998
- /**
1999
- * Trait for converters that works by uploading to a cloud service.
2000
- *
2001
- * The trait adds a method for checking against upload limits.
2002
- *
2003
- * @package WebPConvert
2004
- * @author Bjørn Rosell <it@rosell.dk>
2005
- * @since Class available since Release 2.0.0
2006
- */
2007
- trait CurlTrait
2008
- {
2009
-
2010
- /**
2011
- * Check basis operationality for converters relying on curl.
2012
- *
2013
- * Performs the same as ::checkOperationality(). It is here so converters that overrides the
2014
- * ::checkOperationality() still has a chance to do the checks.
2015
- *
2016
- * @throws SystemRequirementsNotMetException
2017
- * @return void
2018
- */
2019
- public function checkOperationalityForCurlTrait()
2020
- {
2021
- if (!extension_loaded('curl')) {
2022
- throw new SystemRequirementsNotMetException('Required cURL extension is not available.');
2023
- }
2024
-
2025
- if (!function_exists('curl_init')) {
2026
- throw new SystemRequirementsNotMetException('Required url_init() function is not available.');
2027
- }
2028
-
2029
- if (!function_exists('curl_file_create')) {
2030
- throw new SystemRequirementsNotMetException(
2031
- 'Required curl_file_create() function is not available (requires PHP > 5.5).'
2032
- );
2033
- }
2034
- }
2035
-
2036
- /**
2037
- * Check basis operationality for converters relying on curl
2038
- *
2039
- * @throws SystemRequirementsNotMetException
2040
- * @return void
2041
- */
2042
- public function checkOperationality()
2043
- {
2044
- $this->checkOperationalityForCurlTrait();
2045
- }
2046
-
2047
- /**
2048
- * Init curl.
2049
- *
2050
- * @throws SystemRequirementsNotMetException if curl could not be initialized
2051
- * @return resource curl handle
2052
- */
2053
- protected static function initCurl()
2054
- {
2055
- // Get curl handle
2056
- $ch = curl_init();
2057
- if ($ch === false) {
2058
- throw new SystemRequirementsNotMetException('Could not initialise cURL.');
2059
- }
2060
- return $ch;
2061
- }
2062
- }
2063
-
2064
- ?><?php
2065
-
2066
- //namespace WebPConvert\Convert\Converters\BaseTraits;
2067
- namespace WebPConvert\Convert\Converters\ConverterTraits;
2068
-
2069
- /**
2070
- * Trait for converters that supports lossless encoding and thus the "lossless:auto" option.
2071
- *
2072
- * @package WebPConvert
2073
- * @author Bjørn Rosell <it@rosell.dk>
2074
- * @since Class available since Release 2.0.0
2075
- */
2076
- trait EncodingAutoTrait
2077
- {
2078
-
2079
- abstract protected function doActualConvert();
2080
- abstract public function getSource();
2081
- abstract public function getDestination();
2082
- abstract public function setDestination($destination);
2083
- abstract public function getOptions();
2084
- abstract protected function setOption($optionName, $optionValue);
2085
- abstract protected function logLn($msg, $style = '');
2086
- abstract protected function log($msg, $style = '');
2087
- abstract protected function ln();
2088
- abstract protected function logReduction($source, $destination);
2089
-
2090
- public function supportsLossless()
2091
- {
2092
- return true;
2093
- }
2094
-
2095
- /** Default is to not pass "lossless:auto" on, but implement it.
2096
- *
2097
- * The Stack converter passes it on (it does not even use this trait)
2098
- * WPC currently implements it, but this might be configurable in the future.
2099
- *
2100
- */
2101
- public function passOnEncodingAuto()
2102
- {
2103
- return false;
2104
- }
2105
-
2106
- private function convertTwoAndSelectSmallest()
2107
- {
2108
- $destination = $this->getDestination();
2109
- $destinationLossless = $destination . '.lossless.webp';
2110
- $destinationLossy = $destination . '.lossy.webp';
2111
-
2112
- $this->logLn(
2113
- 'Encoding is set to auto - converting to both lossless and lossy and selecting the smallest file'
2114
- );
2115
-
2116
- $this->ln();
2117
- $this->logLn('Converting to lossy');
2118
- $this->setDestination($destinationLossy);
2119
- $this->setOption('encoding', 'lossy');
2120
- $this->doActualConvert();
2121
- $this->log('Reduction: ');
2122
- $this->logReduction($this->getSource(), $destinationLossy);
2123
- $this->ln();
2124
-
2125
- $this->logLn('Converting to lossless');
2126
- $this->setDestination($destinationLossless);
2127
- $this->setOption('encoding', 'lossless');
2128
- $this->doActualConvert();
2129
- $this->log('Reduction: ');
2130
- $this->logReduction($this->getSource(), $destinationLossless);
2131
- $this->ln();
2132
-
2133
- if (filesize($destinationLossless) > filesize($destinationLossy)) {
2134
- $this->logLn('Picking lossy');
2135
- unlink($destinationLossless);
2136
- rename($destinationLossy, $destination);
2137
- } else {
2138
- $this->logLn('Picking lossless');
2139
- unlink($destinationLossy);
2140
- rename($destinationLossless, $destination);
2141
- }
2142
- $this->setDestination($destination);
2143
- $this->setOption('encoding', 'auto');
2144
- }
2145
-
2146
- protected function runActualConvert()
2147
- {
2148
- if (!$this->passOnEncodingAuto() && ($this->getOptions()['encoding'] == 'auto') && $this->supportsLossless()) {
2149
- $this->convertTwoAndSelectSmallest();
2150
- } else {
2151
- $this->doActualConvert();
2152
- }
2153
- }
2154
- }
2155
-
2156
- ?><?php
2157
-
2158
- namespace WebPConvert\Convert\Converters\ConverterTraits;
2159
-
2160
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2161
-
2162
- /**
2163
- * Trait for converters that uses exec()
2164
- *
2165
- * @package WebPConvert
2166
- * @author Bjørn Rosell <it@rosell.dk>
2167
- * @since Class available since Release 2.0.0
2168
- */
2169
- trait ExecTrait
2170
- {
2171
-
2172
- abstract protected function logLn($msg, $style = '');
2173
-
2174
- /**
2175
- * Helper function for examining if "nice" command is available
2176
- *
2177
- * @return boolean true if nice is available
2178
- */
2179
- protected static function hasNiceSupport()
2180
- {
2181
- exec("nice 2>&1", $niceOutput);
2182
-
2183
- if (is_array($niceOutput) && isset($niceOutput[0])) {
2184
- if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
2185
- /*
2186
- * Nice is available - default niceness (+10)
2187
- * https://www.lifewire.com/uses-of-commands-nice-renice-2201087
2188
- * https://www.computerhope.com/unix/unice.htm
2189
- */
2190
-
2191
- return true;
2192
- }
2193
- return false;
2194
- }
2195
- }
2196
-
2197
- /**
2198
- * Logs output from the exec call.
2199
- *
2200
- * @param array $output
2201
- *
2202
- * @return void
2203
- */
2204
- protected function logExecOutput($output)
2205
- {
2206
- if (is_array($output) && count($output) > 0) {
2207
- $this->logLn('');
2208
- $this->logLn('Output:', 'italic');
2209
- foreach ($output as $line) {
2210
- $this->logLn(print_r($line, true));
2211
- }
2212
- $this->logLn('');
2213
- }
2214
- }
2215
-
2216
- /**
2217
- * Check basic operationality of exec converters (that the "exec" function is available)
2218
- *
2219
- * @throws WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException
2220
- * @return void
2221
- */
2222
- public function checkOperationalityExecTrait()
2223
- {
2224
- if (!function_exists('exec')) {
2225
- throw new SystemRequirementsNotMetException('exec() is not enabled.');
2226
- }
2227
- }
2228
- }
2229
-
2230
- ?><?php
2231
-
2232
- namespace WebPConvert\Convert\Converters;
2233
-
2234
- use WebPConvert\Convert\Converters\AbstractConverter;
2235
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
2236
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
2237
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2238
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
2239
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
2240
- use WebPConvert\Options\BooleanOption;
2241
- use WebPConvert\Options\SensitiveStringOption;
2242
- use WebPConvert\Options\StringOption;
2243
-
2244
- /**
2245
- * Convert images to webp by calling cwebp binary.
2246
- *
2247
- * @package WebPConvert
2248
- * @author Bjørn Rosell <it@rosell.dk>
2249
- * @since Class available since Release 2.0.0
2250
- */
2251
- class Cwebp extends AbstractConverter
2252
- {
2253
-
2254
- use EncodingAutoTrait;
2255
- use ExecTrait;
2256
-
2257
- protected function getUnsupportedDefaultOptions()
2258
- {
2259
- return [];
2260
- }
2261
-
2262
- protected function createOptions()
2263
- {
2264
- parent::createOptions();
2265
-
2266
- $this->options2->addOptions(
2267
- new StringOption('command-line-options', ''),
2268
- new SensitiveStringOption('rel-path-to-precompiled-binaries', './Binaries'),
2269
- new BooleanOption('try-common-system-paths', true),
2270
- new BooleanOption('try-supplied-binary-for-os', true)
2271
- );
2272
- }
2273
-
2274
- // System paths to look for cwebp binary
2275
- private static $cwebpDefaultPaths = [
2276
- 'cwebp',
2277
- '/usr/bin/cwebp',
2278
- '/usr/local/bin/cwebp',
2279
- '/usr/gnu/bin/cwebp',
2280
- '/usr/syno/bin/cwebp'
2281
- ];
2282
-
2283
- // OS-specific binaries included in this library, along with hashes
2284
- // If other binaries are going to be added, notice that the first argument is what PHP_OS returns.
2285
- // (possible values, see here: https://stackoverflow.com/questions/738823/possible-values-for-php-os)
2286
- // Got the precompiled binaries here: https://developers.google.com/speed/webp/docs/precompiled
2287
- private static $suppliedBinariesInfo = [
2288
- 'WINNT' => [['cwebp.exe', '49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873']],
2289
- 'Darwin' => [['cwebp-mac12', 'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379']],
2290
- 'SunOS' => [['cwebp-sol', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f']],
2291
- 'FreeBSD' => [['cwebp-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573']],
2292
- 'Linux' => [
2293
- // Dynamically linked executable.
2294
- // It seems it is slightly faster than the statically linked
2295
- ['cwebp-linux-1.0.2-shared', 'd6142e9da2f1cab541de10a31527c597225fff5644e66e31d62bb391c41bfbf4'],
2296
-
2297
- // Statically linked executable
2298
- // It may be that it on some systems works, where the dynamically linked does not (see #196)
2299
- ['cwebp-linux-1.0.2-static', 'a67092563d9de0fbced7dde61b521d60d10c0ad613327a42a81845aefa612b29'],
2300
-
2301
- // Old executable for systems where both of the above fails
2302
- ['cwebp-linux-0.6.1', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568'],
2303
- ]
2304
- ];
2305
-
2306
- public function checkOperationality()
2307
- {
2308
- $this->checkOperationalityExecTrait();
2309
-
2310
- $options = $this->options;
2311
- if (!$options['try-supplied-binary-for-os'] && !$options['try-common-system-paths']) {
2312
- throw new ConverterNotOperationalException(
2313
- 'Configured to neither look for cweb binaries in common system locations, ' .
2314
- 'nor to use one of the supplied precompiled binaries. But these are the only ways ' .
2315
- 'this converter can convert images. No conversion can be made!'
2316
- );
2317
- }
2318
- }
2319
-
2320
- private function executeBinary($binary, $commandOptions, $useNice)
2321
- {
2322
- //$version = $this->detectVersion($binary);
2323
-
2324
- $command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions;
2325
-
2326
- //$logger->logLn('command options:' . $commandOptions);
2327
- $this->logLn('Trying to convert by executing the following command:');
2328
- $this->logLn($command);
2329
- exec($command, $output, $returnCode);
2330
- $this->logExecOutput($output);
2331
- /*
2332
- if ($returnCode == 255) {
2333
- if (isset($output[0])) {
2334
- // Could be an error like 'Error! Cannot open output file' or 'Error! ...preset... '
2335
- $this->logLn(print_r($output[0], true));
2336
- }
2337
- }*/
2338
- //$logger->logLn(self::msgForExitCode($returnCode));
2339
- return intval($returnCode);
2340
- }
2341
-
2342
- /**
2343
- * Use "escapeshellarg()" on all arguments in a commandline string of options
2344
- *
2345
- * For example, passing '-sharpness 5 -crop 10 10 40 40 -low_memory' will result in:
2346
- * [
2347
- * "-sharpness '5'"
2348
- * "-crop '10' '10' '40' '40'"
2349
- * "-low_memory"
2350
- * ]
2351
- * @param string $commandLineOptions string which can contain multiple commandline options
2352
- * @return array Array of command options
2353
- */
2354
- private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
2355
- {
2356
- $cmdOptions = [];
2357
- $arr = explode(' -', ' ' . $commandLineOptions);
2358
- foreach ($arr as $cmdOption) {
2359
- $pos = strpos($cmdOption, ' ');
2360
- $cName = '';
2361
- if (!$pos) {
2362
- $cName = $cmdOption;
2363
- if ($cName == '') {
2364
- continue;
2365
- }
2366
- $cmdOptions[] = '-' . $cName;
2367
- } else {
2368
- $cName = substr($cmdOption, 0, $pos);
2369
- $cValues = substr($cmdOption, $pos + 1);
2370
- $cValuesArr = explode(' ', $cValues);
2371
- foreach ($cValuesArr as &$cArg) {
2372
- $cArg = escapeshellarg($cArg);
2373
- }
2374
- $cValues = implode(' ', $cValuesArr);
2375
- $cmdOptions[] = '-' . $cName . ' ' . $cValues;
2376
- }
2377
- }
2378
- return $cmdOptions;
2379
- }
2380
-
2381
- /**
2382
- * Build command line options for a given version of cwebp.
2383
- *
2384
- * The "-near_lossless" param is not supported on older versions of cwebp, so skip on those.
2385
- *
2386
- * @param string $version Version of cwebp.
2387
- * @return string
2388
- */
2389
- private function createCommandLineOptions($version)
2390
- {
2391
-
2392
- $this->logLn('Creating command line options for version: ' . $version);
2393
-
2394
- // we only need two decimal places for version.
2395
- // convert to number to make it easier to compare
2396
- $version = preg_match('#^\d+\.\d+#', $version, $matches);
2397
- $versionNum = 0;
2398
- if (isset($matches[0])) {
2399
- $versionNum = floatval($matches[0]);
2400
- } else {
2401
- $this->logLn(
2402
- 'Could not extract version number from the following version string: ' . $version,
2403
- 'bold'
2404
- );
2405
- }
2406
-
2407
- //$this->logLn('version:' . strval($versionNum));
2408
-
2409
- $options = $this->options;
2410
-
2411
- $cmdOptions = [];
2412
-
2413
- // Metadata (all, exif, icc, xmp or none (default))
2414
- // Comma-separated list of existing metadata to copy from input to output
2415
- if ($versionNum >= 0.3) {
2416
- $cmdOptions[] = '-metadata ' . $options['metadata'];
2417
- }
2418
-
2419
- // preset. Appears first in the list as recommended in the docs
2420
- if (!is_null($options['preset'])) {
2421
- if ($options['preset'] != 'none') {
2422
- $cmdOptions[] = '-preset ' . $options['preset'];
2423
- }
2424
- }
2425
-
2426
- // Size
2427
- $addedSizeOption = false;
2428
- if (!is_null($options['size-in-percentage'])) {
2429
- $sizeSource = filesize($this->source);
2430
- if ($sizeSource !== false) {
2431
- $targetSize = floor($sizeSource * $options['size-in-percentage'] / 100);
2432
- $cmdOptions[] = '-size ' . $targetSize;
2433
- $addedSizeOption = true;
2434
- }
2435
- }
2436
-
2437
- // quality
2438
- if (!$addedSizeOption) {
2439
- $cmdOptions[] = '-q ' . $this->getCalculatedQuality();
2440
- }
2441
-
2442
- // alpha-quality
2443
- if ($this->options['alpha-quality'] !== 100) {
2444
- $cmdOptions[] = '-alpha_q ' . escapeshellarg($this->options['alpha-quality']);
2445
- }
2446
-
2447
- // Losless PNG conversion
2448
- if ($options['encoding'] == 'lossless') {
2449
- // No need to add -lossless when near-lossless is used (on version >= 0.5)
2450
- if (($options['near-lossless'] === 100) || ($versionNum < 0.5)) {
2451
- $cmdOptions[] = '-lossless';
2452
- }
2453
- }
2454
-
2455
- // Near-lossles
2456
- if ($options['near-lossless'] !== 100) {
2457
- if ($versionNum < 0.5) {
2458
- $this->logLn(
2459
- 'The near-lossless option is not supported on this (rather old) version of cwebp' .
2460
- '- skipping it.',
2461
- 'italic'
2462
- );
2463
- } else {
2464
- // We only let near_lossless have effect when encoding is set to "lossless"
2465
- // otherwise encoding=auto would not work as expected
2466
-
2467
- if ($options['encoding'] == 'lossless') {
2468
- $cmdOptions[] ='-near_lossless ' . $options['near-lossless'];
2469
- } else {
2470
- $this->logLn(
2471
- 'The near-lossless option ignored for lossy'
2472
- );
2473
- }
2474
- }
2475
- }
2476
-
2477
- if ($options['auto-filter'] === true) {
2478
- $cmdOptions[] = '-af';
2479
- }
2480
-
2481
- // Built-in method option
2482
- $cmdOptions[] = '-m ' . strval($options['method']);
2483
-
2484
- // Built-in low memory option
2485
- if ($options['low-memory']) {
2486
- $cmdOptions[] = '-low_memory';
2487
- }
2488
-
2489
- // command-line-options
2490
- if ($options['command-line-options']) {
2491
- array_push(
2492
- $cmdOptions,
2493
- ...self::escapeShellArgOnCommandLineOptions($options['command-line-options'])
2494
- );
2495
- }
2496
-
2497
- // Source file
2498
- $cmdOptions[] = escapeshellarg($this->source);
2499
-
2500
- // Output
2501
- $cmdOptions[] = '-o ' . escapeshellarg($this->destination);
2502
-
2503
- // Redirect stderr to same place as stdout
2504
- // https://www.brianstorti.com/understanding-shell-script-idiom-redirect/
2505
- $cmdOptions[] = '2>&1';
2506
-
2507
- $commandOptions = implode(' ', $cmdOptions);
2508
- //$this->logLn('command line options:' . $commandOptions);
2509
-
2510
- return $commandOptions;
2511
- }
2512
-
2513
- /**
2514
- * Get path for supplied binary for current OS - and validate hash.
2515
- *
2516
- * @return array Array of supplied binaries (which actually exists, and where hash validates)
2517
- */
2518
- private function getSuppliedBinaryPathForOS()
2519
- {
2520
- $this->log('Checking if we have a supplied binary for OS: ' . PHP_OS . '... ');
2521
-
2522
- // Try supplied binary (if available for OS, and hash is correct)
2523
- $options = $this->options;
2524
- if (!isset(self::$suppliedBinariesInfo[PHP_OS])) {
2525
- $this->logLn('No we dont - not for that OS');
2526
- return [];
2527
- }
2528
- $this->logLn('We do.');
2529
-
2530
- $result = [];
2531
- $files = self::$suppliedBinariesInfo[PHP_OS];
2532
- if (count($files) > 0) {
2533
- $this->logLn('We in fact have ' . count($files));
2534
- }
2535
-
2536
- foreach ($files as $i => list($file, $hash)) {
2537
- //$file = $info[0];
2538
- //$hash = $info[1];
2539
-
2540
- $binaryFile = __DIR__ . '/' . $options['rel-path-to-precompiled-binaries'] . '/' . $file;
2541
-
2542
- // Replace "/./" with "/" in path (we could alternatively use realpath)
2543
- //$binaryFile = preg_replace('#\/\.\/#', '/', $binaryFile);
2544
- // The file should exist, but may have been removed manually.
2545
- /*
2546
- if (!file_exists($binaryFile)) {
2547
- $this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
2548
- return false;
2549
- }*/
2550
-
2551
- $realPathResult = realpath($binaryFile);
2552
- if ($realPathResult === false) {
2553
- $this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
2554
- continue;
2555
- }
2556
- $binaryFile = $realPathResult;
2557
-
2558
- // File exists, now generate its hash
2559
- // hash_file() is normally available, but it is not always
2560
- // - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash
2561
- // If available, validate that hash is correct.
2562
-
2563
- if (function_exists('hash_file')) {
2564
- $binaryHash = hash_file('sha256', $binaryFile);
2565
-
2566
- if ($binaryHash != $hash) {
2567
- $this->logLn(
2568
- 'Binary checksum of supplied binary is invalid! ' .
2569
- 'Did you transfer with FTP, but not in binary mode? ' .
2570
- 'File:' . $binaryFile . '. ' .
2571
- 'Expected checksum: ' . $hash . '. ' .
2572
- 'Actual checksum:' . $binaryHash . '.',
2573
- 'bold'
2574
- );
2575
- continue;
2576
- }
2577
- }
2578
- $result[] = $binaryFile;
2579
- }
2580
-
2581
- return $result;
2582
- }
2583
-
2584
- private function discoverBinaries()
2585
- {
2586
- $this->logLn('Locating cwebp binaries');
2587
-
2588
- if (defined('WEBPCONVERT_CWEBP_PATH')) {
2589
- $this->logLn('WEBPCONVERT_CWEBP_PATH was defined, so using that path and ignoring any other');
2590
- //$this->logLn('Value: "' . getenv('WEBPCONVERT_CWEBP_PATH') . '"');
2591
- return [constant('WEBPCONVERT_CWEBP_PATH')];
2592
- }
2593
- if (!empty(getenv('WEBPCONVERT_CWEBP_PATH'))) {
2594
- $this->logLn(
2595
- 'WEBPCONVERT_CWEBP_PATH environment variable was set, so using that path and ignoring any other'
2596
- );
2597
- //$this->logLn('Value: "' . getenv('WEBPCONVERT_CWEBP_PATH') . '"');
2598
- return [getenv('WEBPCONVERT_CWEBP_PATH')];
2599
- }
2600
-
2601
- $binaries = [];
2602
- if ($this->options['try-common-system-paths']) {
2603
- foreach (self::$cwebpDefaultPaths as $binary) {
2604
- if (@file_exists($binary)) {
2605
- $binaries[] = $binary;
2606
- }
2607
- }
2608
- if (count($binaries) == 0) {
2609
- $this->logLn('No cwebp binaries where located in common system locations');
2610
- } else {
2611
- $this->logLn(strval(count($binaries)) . ' cwebp binaries found in common system locations');
2612
- }
2613
- }
2614
- // TODO: exec('whereis cwebp');
2615
- if ($this->options['try-supplied-binary-for-os']) {
2616
- $suppliedBinaries = $this->getSuppliedBinaryPathForOS();
2617
- foreach ($suppliedBinaries as $suppliedBinary) {
2618
- $binaries[] = $suppliedBinary;
2619
- }
2620
- } else {
2621
- $this->logLn('Configured not to try the cwebp binary that comes bundled with webp-convert');
2622
- }
2623
-
2624
- if (count($binaries) == 0) {
2625
- $this->logLn('No cwebp binaries to try!');
2626
- }
2627
- $this->logLn('A total of ' . strval(count($binaries)) . ' cwebp binaries where found');
2628
- return $binaries;
2629
- }
2630
-
2631
- /**
2632
- *
2633
- * @return string|int Version string (ie "1.0.2") OR return code, in case of failure
2634
- */
2635
- private function detectVersion($binary)
2636
- {
2637
- //$this->logLn('Examining binary: ' . $binary);
2638
- $command = $binary . ' -version';
2639
- $this->log('Executing: ' . $command);
2640
- exec($command, $output, $returnCode);
2641
-
2642
- if ($returnCode == 0) {
2643
- //$this->logLn('Success');
2644
- if (isset($output[0])) {
2645
- $this->logLn('. Result: version: ' . $output[0]);
2646
- return $output[0];
2647
- }
2648
- } else {
2649
- $this->logExecOutput($output);
2650
- $this->logLn('');
2651
- if ($returnCode == 127) {
2652
- $this->logLn('Exec failed (the cwebp binary was not found at path: ' . $binary. ')');
2653
- } else {
2654
- $this->logLn(
2655
- 'Exec failed (return code: ' . $returnCode . ')'
2656
- );
2657
- if ($returnCode == 126) {
2658
- $this->logLn(
2659
- 'PS: Return code 126 means "Permission denied". The user that the command was run with does ' .
2660
- 'not have permission to execute that binary.'
2661
- );
2662
- // TODO: further info: shell_exec('whoami')
2663
- }
2664
- }
2665
- return $returnCode;
2666
- }
2667
- }
2668
-
2669
- /**
2670
- * Check versions for binaries, and return array (indexed by the binary, value being the version of the binary).
2671
- *
2672
- * @return array
2673
- */
2674
- private function detectVersions($binaries)
2675
- {
2676
- $binariesWithVersions = [];
2677
- $binariesWithFailCodes = [];
2678
-
2679
- $this->logLn(
2680
- 'Detecting versions of the cwebp binaries found (and verifying that they can be executed in the process)'
2681
- );
2682
- foreach ($binaries as $binary) {
2683
- $versionStringOrFailCode = $this->detectVersion($binary);
2684
- // $this->logLn($binary . ': ' . $versionString);
2685
- if (gettype($versionStringOrFailCode) == 'string') {
2686
- $binariesWithVersions[$binary] = $versionStringOrFailCode;
2687
- } else {
2688
- $binariesWithFailCodes[$binary] = $versionStringOrFailCode;
2689
- }
2690
- }
2691
- return ['detected' => $binariesWithVersions, 'failed' => $binariesWithFailCodes];
2692
- }
2693
-
2694
- /**
2695
- * @return boolean success or not.
2696
- */
2697
- private function tryBinary($binary, $version, $useNice)
2698
- {
2699
-
2700
- //$this->logLn('Trying binary: ' . $binary);
2701
- $commandOptions = $this->createCommandLineOptions($version);
2702
-
2703
- $returnCode = $this->executeBinary($binary, $commandOptions, $useNice);
2704
- if ($returnCode == 0) {
2705
- $this->logLn('Success');
2706
- return true;
2707
- } else {
2708
- $this->logLn(
2709
- 'Exec failed (return code: ' . $returnCode . ')'
2710
- );
2711
- return false;
2712
- }
2713
- }
2714
-
2715
- protected function doActualConvert()
2716
- {
2717
- $binaries = $this->discoverBinaries();
2718
-
2719
- if (count($binaries) == 0) {
2720
- throw new SystemRequirementsNotMetException(
2721
- 'No cwebp binaries located. Check the conversion log for details.'
2722
- );
2723
- }
2724
-
2725
- $versions = $this->detectVersions($binaries);
2726
- if (count($versions['detected']) == 0) {
2727
- //$this->logLn('None of the cwebp files located can be executed.');
2728
- if (count($binaries) == 1) {
2729
- $errorMsg = 'The cwebp file found cannot be can be executed.';
2730
- } else {
2731
- $errorMsg = 'None of the cwebp files located can be executed.';
2732
- }
2733
- $uniqueFailCodes = array_unique(array_values($versions['failed']));
2734
- if (count($uniqueFailCodes) == 1) {
2735
- $errorMsg .= ' ' . (count($binaries) == 1 ? 'It' : 'All') .
2736
- ' failed with return code ' . $uniqueFailCodes[0];
2737
- if ($uniqueFailCodes[0] == 126) {
2738
- $errorMsg .= ' (permission denied)';
2739
- }
2740
- } else {
2741
- $errorMsg .= ' Failure codes : ' . implode(', ', $uniqueFailCodes);
2742
- }
2743
-
2744
- throw new SystemRequirementsNotMetException($errorMsg);
2745
- }
2746
-
2747
- $binaryVersions = $versions['detected'];
2748
-
2749
- if (count($binaries) > 1) {
2750
- $this->logLn(
2751
- 'Trying executing the cwebs found until success. Starting with the ones with highest version number.'
2752
- );
2753
- }
2754
- //$this->logLn('binary versions: ' . print_r($binaryVersions, true));
2755
-
2756
- // Sort binaries so those with highest numbers comes first
2757
- arsort($binaryVersions);
2758
-
2759
- //$this->logLn('binary versions (ordered by version): ' . print_r($binaryVersions, true));
2760
-
2761
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport());
2762
-
2763
- $success = false;
2764
- foreach ($binaryVersions as $binary => $version) {
2765
- if ($this->tryBinary($binary, $version, $useNice)) {
2766
- $success = true;
2767
- break;
2768
- }
2769
- }
2770
-
2771
- // cwebp sets file permissions to 664 but instead ..
2772
- // .. $destination's parent folder's permissions should be used (except executable bits)
2773
- // (or perhaps the current umask instead? https://www.php.net/umask)
2774
-
2775
- if ($success) {
2776
- $destinationParent = dirname($this->destination);
2777
- $fileStatistics = stat($destinationParent);
2778
- if ($fileStatistics !== false) {
2779
- // Apply same permissions as parent folder but strip off the executable bits
2780
- $permissions = $fileStatistics['mode'] & 0000666;
2781
- chmod($this->destination, $permissions);
2782
- }
2783
- } else {
2784
- throw new SystemRequirementsNotMetException('Failed converting. Check the conversion log for details.');
2785
- }
2786
- }
2787
- }
2788
-
2789
- ?><?php
2790
-
2791
- namespace WebPConvert\Convert\Converters;
2792
-
2793
- use WebPConvert\Convert\Converters\AbstractConverter;
2794
- use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
2795
- use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
2796
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
2797
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
2798
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
2799
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2800
- use WebPConvert\Options\SensitiveStringOption;
2801
-
2802
- /**
2803
- * Convert images to webp using ewww cloud service.
2804
- *
2805
- * @package WebPConvert
2806
- * @author Bjørn Rosell <it@rosell.dk>
2807
- * @since Class available since Release 2.0.0
2808
- */
2809
- class Ewww extends AbstractConverter
2810
- {
2811
- use CloudConverterTrait;
2812
- use CurlTrait;
2813
-
2814
- protected function getUnsupportedDefaultOptions()
2815
- {
2816
- return [
2817
- 'alpha-quality',
2818
- 'auto-filter',
2819
- 'encoding',
2820
- 'low-memory',
2821
- 'use-nice'
2822
- ];
2823
- }
2824
-
2825
- protected function createOptions()
2826
- {
2827
- parent::createOptions();
2828
-
2829
- $this->options2->addOptions(
2830
- new SensitiveStringOption('api-key', '')
2831
- );
2832
- }
2833
-
2834
- /**
2835
- * Get api key from options or environment variable
2836
- *
2837
- * @return string|false api key or false if none is set
2838
- */
2839
- private function getKey()
2840
- {
2841
- if (!empty($this->options['api-key'])) {
2842
- return $this->options['api-key'];
2843
- }
2844
- if (defined('WEBPCONVERT_EWWW_API_KEY')) {
2845
- return constant('WEBPCONVERT_EWWW_API_KEY');
2846
- }
2847
- if (!empty(getenv('WEBPCONVERT_EWWW_API_KEY'))) {
2848
- return getenv('WEBPCONVERT_EWWW_API_KEY');
2849
- }
2850
- return false;
2851
- }
2852
-
2853
-
2854
- /**
2855
- * Check operationality of Ewww converter.
2856
- *
2857
- * @throws SystemRequirementsNotMetException if system requirements are not met (curl)
2858
- * @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
2859
- */
2860
- public function checkOperationality()
2861
- {
2862
-
2863
- $apiKey = $this->getKey();
2864
-
2865
- if ($apiKey === false) {
2866
- if (isset($this->options['key'])) {
2867
- throw new InvalidApiKeyException(
2868
- 'The "key" option has been renamed to "api-key" in webp-convert 2.0. ' .
2869
- 'You must change the configuration accordingly.'
2870
- );
2871
- }
2872
-
2873
- throw new InvalidApiKeyException('Missing API key.');
2874
- }
2875
-
2876
- if (strlen($apiKey) < 20) {
2877
- throw new InvalidApiKeyException(
2878
- 'Api key is invalid. Api keys are supposed to be 32 characters long - ' .
2879
- 'the provided api key is much shorter'
2880
- );
2881
- }
2882
-
2883
- // Check for curl requirements
2884
- $this->checkOperationalityForCurlTrait();
2885
-
2886
- $keyStatus = self::getKeyStatus($apiKey);
2887
- switch ($keyStatus) {
2888
- case 'great':
2889
- break;
2890
- case 'exceeded':
2891
- throw new ConverterNotOperationalException('Quota has exceeded');
2892
- break;
2893
- case 'invalid':
2894
- throw new InvalidApiKeyException('Api key is invalid');
2895
- break;
2896
- }
2897
- }
2898
-
2899
- /*
2900
- public function checkConvertability()
2901
- {
2902
- // check upload limits
2903
- $this->checkConvertabilityCloudConverterTrait();
2904
- }
2905
- */
2906
-
2907
- // Although this method is public, do not call directly.
2908
- // You should rather call the static convert() function, defined in AbstractConverter, which
2909
- // takes care of preparing stuff before calling doConvert, and validating after.
2910
- protected function doActualConvert()
2911
- {
2912
-
2913
- $options = $this->options;
2914
-
2915
- $ch = self::initCurl();
2916
-
2917
- //$this->logLn('api key:' . $this->getKey());
2918
-
2919
- $postData = [
2920
- 'api_key' => $this->getKey(),
2921
- 'webp' => '1',
2922
- 'file' => curl_file_create($this->source),
2923
- 'quality' => $this->getCalculatedQuality(),
2924
- 'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
2925
- ];
2926
-
2927
- curl_setopt_array(
2928
- $ch,
2929
- [
2930
- CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
2931
- CURLOPT_HTTPHEADER => [
2932
- 'User-Agent: WebPConvert',
2933
- 'Accept: image/*'
2934
- ],
2935
- CURLOPT_POSTFIELDS => $postData,
2936
- CURLOPT_BINARYTRANSFER => true,
2937
- CURLOPT_RETURNTRANSFER => true,
2938
- CURLOPT_HEADER => false,
2939
- CURLOPT_SSL_VERIFYPEER => false
2940
- ]
2941
- );
2942
-
2943
- $response = curl_exec($ch);
2944
-
2945
- if (curl_errno($ch)) {
2946
- throw new ConversionFailedException(curl_error($ch));
2947
- }
2948
-
2949
- // The API does not always return images.
2950
- // For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
2951
- // Messages has a http content type of ie 'text/html; charset=UTF-8
2952
- // Images has application/octet-stream.
2953
- // So verify that we got an image back.
2954
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
2955
- //echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
2956
- curl_close($ch);
2957
-
2958
- /* May return this: {"error":"invalid","t":"exceeded"} */
2959
- $responseObj = json_decode($response);
2960
- if (isset($responseObj->error)) {
2961
- //echo 'error:' . $responseObj->error . '<br>';
2962
- //echo $response;
2963
- //self::blacklistKey($key);
2964
- //throw new SystemRequirementsNotMetException('The key is invalid. Blacklisted it!');
2965
- throw new InvalidApiKeyException('The api key is invalid');
2966
- }
2967
-
2968
- throw new ConversionFailedException(
2969
- 'ewww api did not return an image. It could be that the key is invalid. Response: '
2970
- . $response
2971
- );
2972
- }
2973
-
2974
- // Not sure this can happen. So just in case
2975
- if ($response == '') {
2976
- throw new ConversionFailedException('ewww api did not return anything');
2977
- }
2978
-
2979
- $success = file_put_contents($this->destination, $response);
2980
-
2981
- if (!$success) {
2982
- throw new ConversionFailedException('Error saving file');
2983
- }
2984
- }
2985
-
2986
- /**
2987
- * Keep subscription alive by optimizing a jpeg
2988
- * (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
2989
- */
2990
- public static function keepSubscriptionAlive($source, $key)
2991
- {
2992
- try {
2993
- $ch = curl_init();
2994
- } catch (\Exception $e) {
2995
- return 'curl is not installed';
2996
- }
2997
- if ($ch === false) {
2998
- return 'curl could not be initialized';
2999
- }
3000
- curl_setopt_array(
3001
- $ch,
3002
- [
3003
- CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
3004
- CURLOPT_HTTPHEADER => [
3005
- 'User-Agent: WebPConvert',
3006
- 'Accept: image/*'
3007
- ],
3008
- CURLOPT_POSTFIELDS => [
3009
- 'api_key' => $key,
3010
- 'webp' => '0',
3011
- 'file' => curl_file_create($source),
3012
- 'domain' => $_SERVER['HTTP_HOST'],
3013
- 'quality' => 60,
3014
- 'metadata' => 0
3015
- ],
3016
- CURLOPT_BINARYTRANSFER => true,
3017
- CURLOPT_RETURNTRANSFER => true,
3018
- CURLOPT_HEADER => false,
3019
- CURLOPT_SSL_VERIFYPEER => false
3020
- ]
3021
- );
3022
-
3023
- $response = curl_exec($ch);
3024
- if (curl_errno($ch)) {
3025
- return 'curl error' . curl_error($ch);
3026
- }
3027
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
3028
- curl_close($ch);
3029
-
3030
- /* May return this: {"error":"invalid","t":"exceeded"} */
3031
- $responseObj = json_decode($response);
3032
- if (isset($responseObj->error)) {
3033
- return 'The key is invalid';
3034
- }
3035
-
3036
- return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
3037
- }
3038
-
3039
- // Not sure this can happen. So just in case
3040
- if ($response == '') {
3041
- return 'ewww api did not return anything';
3042
- }
3043
-
3044
- return true;
3045
- }
3046
-
3047
- /*
3048
- public static function blacklistKey($key)
3049
- {
3050
- }
3051
-
3052
- public static function isKeyBlacklisted($key)
3053
- {
3054
- }*/
3055
-
3056
- /**
3057
- * Return "great", "exceeded" or "invalid"
3058
- */
3059
- public static function getKeyStatus($key)
3060
- {
3061
- $ch = self::initCurl();
3062
-
3063
- curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
3064
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
3065
- curl_setopt(
3066
- $ch,
3067
- CURLOPT_POSTFIELDS,
3068
- [
3069
- 'api_key' => $key
3070
- ]
3071
- );
3072
-
3073
- // The 403 forbidden is avoided with this line.
3074
- curl_setopt(
3075
- $ch,
3076
- CURLOPT_USERAGENT,
3077
- 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
3078
- );
3079
-
3080
- $response = curl_exec($ch);
3081
- // echo $response;
3082
- if (curl_errno($ch)) {
3083
- throw new \Exception(curl_error($ch));
3084
- }
3085
- curl_close($ch);
3086
-
3087
- // Possible responses:
3088
- // “great” = verification successful
3089
- // “exceeded” = indicates a valid key with no remaining image credits.
3090
- // an empty response indicates that the key is not valid
3091
-
3092
- if ($response == '') {
3093
- return 'invalid';
3094
- }
3095
- $responseObj = json_decode($response);
3096
- if (isset($responseObj->error)) {
3097
- if ($responseObj->error == 'invalid') {
3098
- return 'invalid';
3099
- } else {
3100
- throw new \Exception('Ewww returned unexpected error: ' . $response);
3101
- }
3102
- }
3103
- if (!isset($responseObj->status)) {
3104
- throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
3105
- }
3106
- switch ($responseObj->status) {
3107
- case 'great':
3108
- case 'exceeded':
3109
- return $responseObj->status;
3110
- }
3111
- throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
3112
- }
3113
-
3114
- public static function isWorkingKey($key)
3115
- {
3116
- return (self::getKeyStatus($key) == 'great');
3117
- }
3118
-
3119
- public static function isValidKey($key)
3120
- {
3121
- return (self::getKeyStatus($key) != 'invalid');
3122
- }
3123
-
3124
- public static function getQuota($key)
3125
- {
3126
- $ch = self::initCurl();
3127
-
3128
- curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
3129
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
3130
- curl_setopt(
3131
- $ch,
3132
- CURLOPT_POSTFIELDS,
3133
- [
3134
- 'api_key' => $key
3135
- ]
3136
- );
3137
- curl_setopt(
3138
- $ch,
3139
- CURLOPT_USERAGENT,
3140
- 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
3141
- );
3142
-
3143
- $response = curl_exec($ch);
3144
- return $response; // ie -830 23. Seems to return empty for invalid keys
3145
- // or empty
3146
- //echo $response;
3147
- }
3148
- }
3149
-
3150
- ?><?php
3151
-
3152
- namespace WebPConvert\Convert\Converters;
3153
-
3154
- use WebPConvert\Convert\Converters\AbstractConverter;
3155
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3156
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
3157
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3158
-
3159
- /**
3160
- * Convert images to webp using gd extension.
3161
- *
3162
- * @package WebPConvert
3163
- * @author Bjørn Rosell <it@rosell.dk>
3164
- * @since Class available since Release 2.0.0
3165
- */
3166
- class Gd extends AbstractConverter
3167
- {
3168
- public function supportsLossless()
3169
- {
3170
- return false;
3171
- }
3172
-
3173
- protected function getUnsupportedDefaultOptions()
3174
- {
3175
- return [
3176
- 'alpha-quality',
3177
- 'auto-filter',
3178
- 'encoding',
3179
- 'low-memory',
3180
- 'metadata',
3181
- 'method',
3182
- 'near-lossless',
3183
- 'preset',
3184
- 'size-in-percentage',
3185
- 'use-nice'
3186
- ];
3187
- }
3188
-
3189
- private $errorMessageWhileCreating = '';
3190
- private $errorNumberWhileCreating;
3191
-
3192
- /**
3193
- * Check (general) operationality of Gd converter.
3194
- *
3195
- * @throws SystemRequirementsNotMetException if system requirements are not met
3196
- */
3197
- public function checkOperationality()
3198
- {
3199
- if (!extension_loaded('gd')) {
3200
- throw new SystemRequirementsNotMetException('Required Gd extension is not available.');
3201
- }
3202
-
3203
- if (!function_exists('imagewebp')) {
3204
- throw new SystemRequirementsNotMetException(
3205
- 'Gd has been compiled without webp support.'
3206
- );
3207
- }
3208
- }
3209
-
3210
- /**
3211
- * Check if specific file is convertable with current converter / converter settings.
3212
- *
3213
- * @throws SystemRequirementsNotMetException if Gd has been compiled without support for image type
3214
- */
3215
- public function checkConvertability()
3216
- {
3217
- $mimeType = $this->getMimeTypeOfSource();
3218
- switch ($mimeType) {
3219
- case 'image/png':
3220
- if (!function_exists('imagecreatefrompng')) {
3221
- throw new SystemRequirementsNotMetException(
3222
- 'Gd has been compiled without PNG support and can therefore not convert this PNG image.'
3223
- );
3224
- }
3225
- break;
3226
-
3227
- case 'image/jpeg':
3228
- if (!function_exists('imagecreatefromjpeg')) {
3229
- throw new SystemRequirementsNotMetException(
3230
- 'Gd has been compiled without Jpeg support and can therefore not convert this jpeg image.'
3231
- );
3232
- }
3233
- }
3234
- }
3235
-
3236
- /**
3237
- * Find out if all functions exists.
3238
- *
3239
- * @return boolean
3240
- */
3241
- private static function functionsExist($functionNamesArr)
3242
- {
3243
- foreach ($functionNamesArr as $functionName) {
3244
- if (!function_exists($functionName)) {
3245
- return false;
3246
- }
3247
- }
3248
- return true;
3249
- }
3250
-
3251
- /**
3252
- * Try to convert image pallette to true color on older systems that does not have imagepalettetotruecolor().
3253
- *
3254
- * The aim is to function as imagepalettetotruecolor, but for older systems.
3255
- * So, if the image is already rgb, nothing will be done, and true will be returned
3256
- * PS: Got the workaround here: https://secure.php.net/manual/en/function.imagepalettetotruecolor.php
3257
- *
3258
- * @param resource $image
3259
- * @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
3260
- * otherwise FALSE is returned.
3261
- */
3262
- private function makeTrueColorUsingWorkaround(&$image)
3263
- {
3264
- //return $this->makeTrueColor($image);
3265
- /*
3266
- if (function_exists('imageistruecolor') && imageistruecolor($image)) {
3267
- return true;
3268
- }*/
3269
- if (self::functionsExist(['imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
3270
- 'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'])) {
3271
- $dst = imagecreatetruecolor(imagesx($image), imagesy($image));
3272
-
3273
- if ($dst === false) {
3274
- return false;
3275
- }
3276
-
3277
- //prevent blending with default black
3278
- if (imagealphablending($dst, false) === false) {
3279
- return false;
3280
- }
3281
-
3282
- //change the RGB values if you need, but leave alpha at 127
3283
- $transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
3284
-
3285
- if ($transparent === false) {
3286
- return false;
3287
- }
3288
-
3289
- //simpler than flood fill
3290
- if (imagefilledrectangle($dst, 0, 0, imagesx($image), imagesy($image), $transparent) === false) {
3291
- return false;
3292
- }
3293
-
3294
- //restore default blending
3295
- if (imagealphablending($dst, true) === false) {
3296
- return false;
3297
- };
3298
-
3299
- if (imagecopy($dst, $image, 0, 0, 0, 0, imagesx($image), imagesy($image)) === false) {
3300
- return false;
3301
- }
3302
- imagedestroy($image);
3303
-
3304
- $image = $dst;
3305
- return true;
3306
- } else {
3307
- // The necessary methods for converting color palette are not avalaible
3308
- return false;
3309
- }
3310
- }
3311
-
3312
- /**
3313
- * Try to convert image pallette to true color.
3314
- *
3315
- * Try to convert image pallette to true color. If imagepalettetotruecolor() exists, that is used (available from
3316
- * PHP >= 5.5.0). Otherwise using workaround found on the net.
3317
- *
3318
- * @param resource $image
3319
- * @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
3320
- * otherwise FALSE is returned.
3321
- */
3322
- private function makeTrueColor(&$image)
3323
- {
3324
- if (function_exists('imagepalettetotruecolor')) {
3325
- return imagepalettetotruecolor($image);
3326
- } else {
3327
- // imagepalettetotruecolor() is not available on this system. Using custom implementation instead
3328
- return $this->makeTrueColorUsingWorkaround($image);
3329
- }
3330
- }
3331
-
3332
- /**
3333
- * Create Gd image resource from source
3334
- *
3335
- * @throws InvalidInputException if mime type is unsupported or could not be detected
3336
- * @throws ConversionFailedException if imagecreatefrompng or imagecreatefromjpeg fails
3337
- * @return resource $image The created image
3338
- */
3339
- private function createImageResource()
3340
- {
3341
- // In case of failure, image will be false
3342
-
3343
- $mimeType = $this->getMimeTypeOfSource();
3344
-
3345
- if ($mimeType == 'image/png') {
3346
- $image = imagecreatefrompng($this->source);
3347
- if ($image === false) {
3348
- throw new ConversionFailedException(
3349
- 'Gd failed when trying to load/create image (imagecreatefrompng() failed)'
3350
- );
3351
- }
3352
- return $image;
3353
- }
3354
-
3355
- if ($mimeType == 'image/jpeg') {
3356
- $image = imagecreatefromjpeg($this->source);
3357
- if ($image === false) {
3358
- throw new ConversionFailedException(
3359
- 'Gd failed when trying to load/create image (imagecreatefromjpeg() failed)'
3360
- );
3361
- }
3362
- return $image;
3363
- }
3364
-
3365
- /*
3366
- throw new InvalidInputException(
3367
- 'Unsupported mime type:' . $mimeType
3368
- );*/
3369
- }
3370
-
3371
- /**
3372
- * Try to make image resource true color if it is not already.
3373
- *
3374
- * @param resource $image The image to work on
3375
- * @return void
3376
- */
3377
- protected function tryToMakeTrueColorIfNot(&$image)
3378
- {
3379
- $mustMakeTrueColor = false;
3380
- if (function_exists('imageistruecolor')) {
3381
- if (imageistruecolor($image)) {
3382
- $this->logLn('image is true color');
3383
- } else {
3384
- $this->logLn('image is not true color');
3385
- $mustMakeTrueColor = true;
3386
- }
3387
- } else {
3388
- $this->logLn('It can not be determined if image is true color');
3389
- $mustMakeTrueColor = true;
3390
- }
3391
-
3392
- if ($mustMakeTrueColor) {
3393
- $this->logLn('converting color palette to true color');
3394
- $success = $this->makeTrueColor($image);
3395
- if (!$success) {
3396
- $this->logLn(
3397
- 'Warning: FAILED converting color palette to true color. ' .
3398
- 'Continuing, but this does NOT look good.'
3399
- );
3400
- }
3401
- }
3402
- }
3403
-
3404
- /**
3405
- *
3406
- * @param resource $image
3407
- * @return boolean true if alpha blending was set successfully, false otherwise
3408
- */
3409
- protected function trySettingAlphaBlending($image)
3410
- {
3411
- if (function_exists('imagealphablending')) {
3412
- if (!imagealphablending($image, true)) {
3413
- $this->logLn('Warning: imagealphablending() failed');
3414
- return false;
3415
- }
3416
- } else {
3417
- $this->logLn(
3418
- 'Warning: imagealphablending() is not available on your system.' .
3419
- ' Converting PNGs with transparency might fail on some systems'
3420
- );
3421
- return false;
3422
- }
3423
-
3424
- if (function_exists('imagesavealpha')) {
3425
- if (!imagesavealpha($image, true)) {
3426
- $this->logLn('Warning: imagesavealpha() failed');
3427
- return false;
3428
- }
3429
- } else {
3430
- $this->logLn(
3431
- 'Warning: imagesavealpha() is not available on your system. ' .
3432
- 'Converting PNGs with transparency might fail on some systems'
3433
- );
3434
- return false;
3435
- }
3436
- return true;
3437
- }
3438
-
3439
- protected function errorHandlerWhileCreatingWebP($errno, $errstr, $errfile, $errline)
3440
- {
3441
- $this->errorNumberWhileCreating = $errno;
3442
- $this->errorMessageWhileCreating = $errstr . ' in ' . $errfile . ', line ' . $errline .
3443
- ', PHP ' . PHP_VERSION . ' (' . PHP_OS . ')';
3444
- //return false;
3445
- }
3446
-
3447
- /**
3448
- *
3449
- * @param resource $image
3450
- * @return void
3451
- */
3452
- protected function destroyAndRemove($image)
3453
- {
3454
- imagedestroy($image);
3455
- if (file_exists($this->destination)) {
3456
- unlink($this->destination);
3457
- }
3458
- }
3459
-
3460
- /**
3461
- *
3462
- * @param resource $image
3463
- * @return void
3464
- */
3465
- protected function tryConverting($image)
3466
- {
3467
-
3468
- // Danger zone!
3469
- // Using output buffering to generate image.
3470
- // In this zone, Do NOT do anything that might produce unwanted output
3471
- // Do NOT call $this->logLn
3472
- // --------------------------------- (start of danger zone)
3473
-
3474
- $addedZeroPadding = false;
3475
- set_error_handler(array($this, "errorHandlerWhileCreatingWebP"));
3476
-
3477
- // This line may trigger log, so we need to do it BEFORE ob_start() !
3478
- $q = $this->getCalculatedQuality();
3479
-
3480
- ob_start();
3481
-
3482
- //$success = imagewebp($image, $this->destination, $q);
3483
- $success = imagewebp($image, null, $q);
3484
-
3485
- if (!$success) {
3486
- $this->destroyAndRemove($image);
3487
- ob_end_clean();
3488
- restore_error_handler();
3489
- throw new ConversionFailedException(
3490
- 'Failed creating image. Call to imagewebp() failed.',
3491
- $this->errorMessageWhileCreating
3492
- );
3493
- }
3494
-
3495
-
3496
- // The following hack solves an `imagewebp` bug
3497
- // See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
3498
- if (ob_get_length() % 2 == 1) {
3499
- echo "\0";
3500
- $addedZeroPadding = true;
3501
- }
3502
- $output = ob_get_clean();
3503
- restore_error_handler();
3504
-
3505
- if ($output == '') {
3506
- $this->destroyAndRemove($image);
3507
- throw new ConversionFailedException(
3508
- 'Gd failed: imagewebp() returned empty string'
3509
- );
3510
- }
3511
-
3512
- // --------------------------------- (end of danger zone).
3513
-
3514
-
3515
- if ($this->errorMessageWhileCreating != '') {
3516
- switch ($this->errorNumberWhileCreating) {
3517
- case E_WARNING:
3518
- $this->logLn('An warning was produced during conversion: ' . $this->errorMessageWhileCreating);
3519
- break;
3520
- case E_NOTICE:
3521
- $this->logLn('An notice was produced during conversion: ' . $this->errorMessageWhileCreating);
3522
- break;
3523
- default:
3524
- $this->destroyAndRemove($image);
3525
- throw new ConversionFailedException(
3526
- 'An error was produced during conversion',
3527
- $this->errorMessageWhileCreating
3528
- );
3529
- break;
3530
- }
3531
- }
3532
-
3533
- if ($addedZeroPadding) {
3534
- $this->logLn(
3535
- 'Fixing corrupt webp by adding a zero byte ' .
3536
- '(older versions of Gd had a bug, but this hack fixes it)'
3537
- );
3538
- }
3539
-
3540
- $success = file_put_contents($this->destination, $output);
3541
-
3542
- if (!$success) {
3543
- $this->destroyAndRemove($image);
3544
- throw new ConversionFailedException(
3545
- 'Gd failed when trying to save the image. Check file permissions!'
3546
- );
3547
- }
3548
-
3549
- /*
3550
- Previous code was much simpler, but on a system, the hack was not activated (a file with uneven number of bytes
3551
- was created). This is puzzeling. And the old code did not provide any insights.
3552
- Also, perhaps having two subsequent writes to the same file could perhaps cause a problem.
3553
- In the new code, there is only one write.
3554
- However, a bad thing about the new code is that the entire webp file is read into memory. This might cause
3555
- memory overflow with big files.
3556
- Perhaps we should check the filesize of the original and only use the new code when it is smaller than
3557
- memory limit set in PHP by a certain factor.
3558
- Or perhaps only use the new code on older versions of Gd
3559
- https://wordpress.org/support/topic/images-not-seen-on-chrome/#post-11390284
3560
-
3561
- Here is the old code:
3562
-
3563
- $success = imagewebp($image, $this->destination, $this->getCalculatedQuality());
3564
-
3565
- if (!$success) {
3566
- throw new ConversionFailedException(
3567
- 'Gd failed when trying to save the image as webp (call to imagewebp() failed). ' .
3568
- 'It probably failed writing file. Check file permissions!'
3569
- );
3570
- }
3571
-
3572
-
3573
- // This hack solves an `imagewebp` bug
3574
- // See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
3575
- if (filesize($this->destination) % 2 == 1) {
3576
- file_put_contents($this->destination, "\0", FILE_APPEND);
3577
- }
3578
- */
3579
- }
3580
-
3581
- // Although this method is public, do not call directly.
3582
- // You should rather call the static convert() function, defined in AbstractConverter, which
3583
- // takes care of preparing stuff before calling doConvert, and validating after.
3584
- protected function doActualConvert()
3585
- {
3586
-
3587
- $this->logLn('GD Version: ' . gd_info()["GD Version"]);
3588
-
3589
- // Btw: Check out processWebp here:
3590
- // https://github.com/Intervention/image/blob/master/src/Intervention/Image/Gd/Encoder.php
3591
-
3592
- // Create image resource
3593
- $image = $this->createImageResource();
3594
-
3595
- // Try to convert color palette if it is not true color
3596
- $this->tryToMakeTrueColorIfNot($image);
3597
-
3598
-
3599
- if ($this->getMimeTypeOfSource() == 'image/png') {
3600
- // Try to set alpha blending
3601
- $this->trySettingAlphaBlending($image);
3602
- }
3603
-
3604
- // Try to convert it to webp
3605
- $this->tryConverting($image);
3606
-
3607
- // End of story
3608
- imagedestroy($image);
3609
- }
3610
- }
3611
-
3612
- ?><?php
3613
-
3614
- namespace WebPConvert\Convert\Converters;
3615
-
3616
- use WebPConvert\Convert\Converters\AbstractConverter;
3617
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3618
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3619
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3620
-
3621
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3622
-
3623
- /**
3624
- * Convert images to webp using Gmagick extension.
3625
- *
3626
- * @package WebPConvert
3627
- * @author Bjørn Rosell <it@rosell.dk>
3628
- * @since Class available since Release 2.0.0
3629
- */
3630
- class Gmagick extends AbstractConverter
3631
- {
3632
- use EncodingAutoTrait;
3633
-
3634
- protected function getUnsupportedDefaultOptions()
3635
- {
3636
- return [
3637
- 'near-lossless',
3638
- 'preset',
3639
- 'size-in-percentage',
3640
- 'use-nice'
3641
- ];
3642
- }
3643
-
3644
- /**
3645
- * Check (general) operationality of Gmagick converter.
3646
- *
3647
- * Note:
3648
- * It may be that Gd has been compiled without jpeg support or png support.
3649
- * We do not check for this here, as the converter could still be used for the other.
3650
- *
3651
- * @throws SystemRequirementsNotMetException if system requirements are not met
3652
- */
3653
- public function checkOperationality()
3654
- {
3655
- if (!extension_loaded('Gmagick')) {
3656
- throw new SystemRequirementsNotMetException('Required Gmagick extension is not available.');
3657
- }
3658
-
3659
- if (!class_exists('Gmagick')) {
3660
- throw new SystemRequirementsNotMetException(
3661
- 'Gmagick is installed, but not correctly. The class Gmagick is not available'
3662
- );
3663
- }
3664
-
3665
- $im = new \Gmagick($this->source);
3666
-
3667
- if (!in_array('WEBP', $im->queryformats())) {
3668
- throw new SystemRequirementsNotMetException('Gmagick was compiled without WebP support.');
3669
- }
3670
- }
3671
-
3672
- /**
3673
- * Check if specific file is convertable with current converter / converter settings.
3674
- *
3675
- * @throws SystemRequirementsNotMetException if Gmagick does not support image type
3676
- */
3677
- public function checkConvertability()
3678
- {
3679
- $im = new \Gmagick();
3680
- $mimeType = $this->getMimeTypeOfSource();
3681
- switch ($mimeType) {
3682
- case 'image/png':
3683
- if (!in_array('PNG', $im->queryFormats())) {
3684
- throw new SystemRequirementsNotMetException(
3685
- 'Gmagick has been compiled without PNG support and can therefore not convert this PNG image.'
3686
- );
3687
- }
3688
- break;
3689
- case 'image/jpeg':
3690
- if (!in_array('JPEG', $im->queryFormats())) {
3691
- throw new SystemRequirementsNotMetException(
3692
- 'Gmagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
3693
- );
3694
- }
3695
- break;
3696
- }
3697
- }
3698
-
3699
- // Although this method is public, do not call directly.
3700
- // You should rather call the static convert() function, defined in AbstractConverter, which
3701
- // takes care of preparing stuff before calling doConvert, and validating after.
3702
- protected function doActualConvert()
3703
- {
3704
-
3705
- $options = $this->options;
3706
-
3707
- try {
3708
- $im = new \Gmagick($this->source);
3709
- } catch (\Exception $e) {
3710
- throw new ConversionFailedException(
3711
- 'Failed creating Gmagick object of file',
3712
- 'Failed creating Gmagick object of file: "' . $this->source . '" - Gmagick threw an exception.',
3713
- $e
3714
- );
3715
- }
3716
-
3717
- $im->setimageformat('WEBP');
3718
-
3719
- // Not completely sure if setimageoption() has always been there, so lets check first. #169
3720
- if (method_exists($im, 'setimageoption')) {
3721
- // Finally cracked setting webp options.
3722
- // See #167
3723
- // - and https://stackoverflow.com/questions/47294962/how-to-write-lossless-webp-files-with-perlmagick
3724
- $im->setimageoption('webp', 'method', $options['method']);
3725
- $im->setimageoption('webp', 'lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
3726
- $im->setimageoption('webp', 'alpha-quality', $options['alpha-quality']);
3727
-
3728
- if ($options['auto-filter'] === true) {
3729
- $im->setimageoption('webp', 'auto-filter', 'true');
3730
- }
3731
- }
3732
-
3733
- /*
3734
- low-memory seems not to be supported:
3735
- $im->setimageoption('webp', 'low-memory', $options['low-memory'] ? true : false);
3736
- */
3737
-
3738
- if ($options['metadata'] == 'none') {
3739
- // Strip metadata and profiles
3740
- $im->stripImage();
3741
- }
3742
-
3743
- // Ps: Imagick automatically uses same quality as source, when no quality is set
3744
- // This feature is however not present in Gmagick
3745
- // TODO: However, it might be possible after all - see #91
3746
- $im->setcompressionquality($this->getCalculatedQuality());
3747
-
3748
- // We call getImageBlob().
3749
- // That method is undocumented, but it is there!
3750
- // - just like it is in imagick, as pointed out here:
3751
- // https://www.php.net/manual/ru/gmagick.readimageblob.php
3752
-
3753
- /** @scrutinizer ignore-call */
3754
- $imageBlob = $im->getImageBlob();
3755
-
3756
- $success = @file_put_contents($this->destination, $imageBlob);
3757
-
3758
- if (!$success) {
3759
- throw new ConversionFailedException('Failed writing file');
3760
- }
3761
- }
3762
- }
3763
-
3764
- ?><?php
3765
-
3766
- namespace WebPConvert\Convert\Converters;
3767
-
3768
- use WebPConvert\Convert\Converters\AbstractConverter;
3769
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3770
-
3771
- /**
3772
- * Non-functional converter, just here to tell you that it has been renamed.
3773
- *
3774
- * @package WebPConvert
3775
- * @author Bjørn Rosell <it@rosell.dk>
3776
- * @since Class available since Release 2.0.0
3777
- */
3778
- class GmagickBinary extends AbstractConverter
3779
- {
3780
- public function checkOperationality()
3781
- {
3782
- throw new ConversionFailedException(
3783
- 'This converter has changed ID from "gmagickbinary" to "graphicsmagick". You need to change!'
3784
- );
3785
- }
3786
-
3787
- protected function doActualConvert()
3788
- {
3789
- $this->checkOperationality();
3790
- }
3791
- }
3792
-
3793
- ?><?php
3794
-
3795
- namespace WebPConvert\Convert\Converters;
3796
-
3797
- use WebPConvert\Convert\Converters\AbstractConverter;
3798
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3799
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
3800
-
3801
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3802
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3803
-
3804
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3805
-
3806
- /**
3807
- * Convert images to webp by calling gmagick binary (gm).
3808
- *
3809
- * @package WebPConvert
3810
- * @author Bjørn Rosell <it@rosell.dk>
3811
- * @since Class available since Release 2.0.0
3812
- */
3813
- class GraphicsMagick extends AbstractConverter
3814
- {
3815
- use ExecTrait;
3816
- use EncodingAutoTrait;
3817
-
3818
- protected function getUnsupportedDefaultOptions()
3819
- {
3820
- return [
3821
- 'auto-filter',
3822
- 'near-lossless',
3823
- 'preset',
3824
- 'size-in-percentage',
3825
- ];
3826
- }
3827
-
3828
- private function getPath()
3829
- {
3830
- if (defined('WEBPCONVERT_GRAPHICSMAGICK_PATH')) {
3831
- return constant('WEBPCONVERT_GRAPHICSMAGICK_PATH');
3832
- }
3833
- if (!empty(getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH'))) {
3834
- return getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH');
3835
- }
3836
- return 'gm';
3837
- }
3838
-
3839
- public function isInstalled()
3840
- {
3841
- exec($this->getPath() . ' -version', $output, $returnCode);
3842
- return ($returnCode == 0);
3843
- }
3844
-
3845
- public function getVersion()
3846
- {
3847
- exec($this->getPath() . ' -version', $output, $returnCode);
3848
- if (($returnCode == 0) && isset($output[0])) {
3849
- return preg_replace('#http.*#', '', $output[0]);
3850
- }
3851
- return 'unknown';
3852
- }
3853
-
3854
- // Check if webp delegate is installed
3855
- public function isWebPDelegateInstalled()
3856
- {
3857
- exec($this->getPath() . ' -version', $output, $returnCode);
3858
- foreach ($output as $line) {
3859
- if (preg_match('#WebP.*yes#i', $line)) {
3860
- return true;
3861
- }
3862
- }
3863
- return false;
3864
- }
3865
-
3866
- /**
3867
- * Check (general) operationality of imagack converter executable
3868
- *
3869
- * @throws SystemRequirementsNotMetException if system requirements are not met
3870
- */
3871
- public function checkOperationality()
3872
- {
3873
- $this->checkOperationalityExecTrait();
3874
-
3875
- if (!$this->isInstalled()) {
3876
- throw new SystemRequirementsNotMetException('gmagick is not installed');
3877
- }
3878
- if (!$this->isWebPDelegateInstalled()) {
3879
- throw new SystemRequirementsNotMetException('webp delegate missing');
3880
- }
3881
- }
3882
-
3883
- /**
3884
- * Build command line options
3885
- *
3886
- * @return string
3887
- */
3888
- private function createCommandLineOptions()
3889
- {
3890
- $commandArguments = [];
3891
-
3892
- // Unlike imagick binary, it seems gmagick binary uses a fixed
3893
- // quality (75) when quality is omitted
3894
- $commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
3895
-
3896
- // encoding
3897
- if ($this->options['encoding'] == 'lossless') {
3898
- // Btw:
3899
- // I am not sure if we should set "quality" for lossless.
3900
- // Quality should not apply to lossless, but my tests shows that it does in some way for gmagick
3901
- // setting it low, you get bad visual quality and small filesize. Setting it high, you get the opposite
3902
- // Some claim it is a bad idea to set quality, but I'm not so sure.
3903
- // https://stackoverflow.com/questions/4228027/
3904
- // First, I do not just get bigger images when setting quality, as toc777 does.
3905
- // Secondly, the answer is very old and that bad behaviour is probably fixed by now.
3906
- $commandArguments[] = '-define webp:lossless=true';
3907
- } else {
3908
- $commandArguments[] = '-define webp:lossless=false';
3909
- }
3910
-
3911
- if ($this->options['alpha-quality'] !== 100) {
3912
- $commandArguments[] = '-define webp:alpha-quality=' . strval($this->options['alpha-quality']);
3913
- }
3914
-
3915
- if ($this->options['low-memory']) {
3916
- $commandArguments[] = '-define webp:low-memory=true';
3917
- }
3918
-
3919
- if ($this->options['metadata'] == 'none') {
3920
- $commandArguments[] = '-strip';
3921
- }
3922
-
3923
- $commandArguments[] = '-define webp:method=' . $this->options['method'];
3924
-
3925
- $commandArguments[] = escapeshellarg($this->source);
3926
- $commandArguments[] = escapeshellarg('webp:' . $this->destination);
3927
-
3928
- return implode(' ', $commandArguments);
3929
- }
3930
-
3931
- protected function doActualConvert()
3932
- {
3933
- //$this->logLn('Using quality:' . $this->getCalculatedQuality());
3934
-
3935
- $this->logLn('Version: ' . $this->getVersion());
3936
-
3937
- $command = $this->getPath() . ' convert ' . $this->createCommandLineOptions();
3938
-
3939
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport()) ? true : false;
3940
- if ($useNice) {
3941
- $this->logLn('using nice');
3942
- $command = 'nice ' . $command;
3943
- }
3944
- $this->logLn('Executing command: ' . $command);
3945
- exec($command, $output, $returnCode);
3946
-
3947
- $this->logExecOutput($output);
3948
- if ($returnCode == 0) {
3949
- $this->logLn('success');
3950
- } else {
3951
- $this->logLn('return code: ' . $returnCode);
3952
- }
3953
-
3954
- if ($returnCode == 127) {
3955
- throw new SystemRequirementsNotMetException('gmagick is not installed');
3956
- }
3957
- if ($returnCode != 0) {
3958
- $this->logLn('command:' . $command);
3959
- $this->logLn('return code:' . $returnCode);
3960
- $this->logLn('output:' . print_r(implode("\n", $output), true));
3961
- throw new SystemRequirementsNotMetException('The exec call failed');
3962
- }
3963
- }
3964
- }
3965
-
3966
- ?><?php
3967
-
3968
- namespace WebPConvert\Convert\Converters;
3969
-
3970
- use WebPConvert\Convert\Converters\AbstractConverter;
3971
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
3972
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3973
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3974
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3975
-
3976
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3977
-
3978
- /**
3979
- * Convert images to webp by calling imagemagick binary.
3980
- *
3981
- * @package WebPConvert
3982
- * @author Bjørn Rosell <it@rosell.dk>
3983
- * @since Class available since Release 2.0.0
3984
- */
3985
- class ImageMagick extends AbstractConverter
3986
- {
3987
- use ExecTrait;
3988
- use EncodingAutoTrait;
3989
-
3990
- protected function getUnsupportedDefaultOptions()
3991
- {
3992
- return [
3993
- 'near-lossless',
3994
- 'preset',
3995
- 'size-in-percentage',
3996
- ];
3997
- }
3998
-
3999
- // To futher improve this converter, I could check out:
4000
- // https://github.com/Orbitale/ImageMagickPHP
4001
-
4002
- private function getPath()
4003
- {
4004
- if (defined('WEBPCONVERT_IMAGEMAGICK_PATH')) {
4005
- return constant('WEBPCONVERT_IMAGEMAGICK_PATH');
4006
- }
4007
- if (!empty(getenv('WEBPCONVERT_IMAGEMAGICK_PATH'))) {
4008
- return getenv('WEBPCONVERT_IMAGEMAGICK_PATH');
4009
- }
4010
- return 'convert';
4011
- }
4012
-
4013
- private function getVersion()
4014
- {
4015
- exec($this->getPath() . ' -version', $output, $returnCode);
4016
- if (($returnCode == 0) && isset($output[0])) {
4017
- return $output[0];
4018
- } else {
4019
- return 'unknown';
4020
- }
4021
- }
4022
-
4023
- public function isInstalled()
4024
- {
4025
- exec($this->getPath() . ' -version', $output, $returnCode);
4026
- return ($returnCode == 0);
4027
- }
4028
-
4029
- // Check if webp delegate is installed
4030
- public function isWebPDelegateInstalled()
4031
- {
4032
-
4033
- exec('convert -list delegate', $output, $returnCode);
4034
- foreach ($output as $line) {
4035
- if (preg_match('#webp\\s*=#i', $line)) {
4036
- return true;
4037
- }
4038
- }
4039
-
4040
- // try other command
4041
- exec('convert -list configure', $output, $returnCode);
4042
- foreach ($output as $line) {
4043
- if (preg_match('#DELEGATE.*webp#i', $line)) {
4044
- return true;
4045
- }
4046
- }
4047
-
4048
- return false;
4049
-
4050
- // PS, convert -version does not output delegates on travis, so it is not reliable
4051
- }
4052
-
4053
- /**
4054
- * Check (general) operationality of imagack converter executable
4055
- *
4056
- * @throws SystemRequirementsNotMetException if system requirements are not met
4057
- */
4058
- public function checkOperationality()
4059
- {
4060
- $this->checkOperationalityExecTrait();
4061
-
4062
- if (!$this->isInstalled()) {
4063
- throw new SystemRequirementsNotMetException(
4064
- 'imagemagick is not installed (cannot execute: "' . $this->getPath() . '")'
4065
- );
4066
- }
4067
- if (!$this->isWebPDelegateInstalled()) {
4068
- throw new SystemRequirementsNotMetException('webp delegate missing');
4069
- }
4070
- }
4071
-
4072
- /**
4073
- * Build command line options
4074
- *
4075
- * @return string
4076
- */
4077
- private function createCommandLineOptions()
4078
- {
4079
- // PS: Available webp options for imagemagick are documented here:
4080
- // https://imagemagick.org/script/webp.php
4081
-
4082
- $commandArguments = [];
4083
- if ($this->isQualityDetectionRequiredButFailing()) {
4084
- // quality:auto was specified, but could not be determined.
4085
- // we cannot apply the max-quality logic, but we can provide auto quality
4086
- // simply by not specifying the quality option.
4087
- } else {
4088
- $commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
4089
- }
4090
- if ($this->options['encoding'] == 'lossless') {
4091
- $commandArguments[] = '-define webp:lossless=true';
4092
- }
4093
- if ($this->options['low-memory']) {
4094
- $commandArguments[] = '-define webp:low-memory=true';
4095
- }
4096
- if ($this->options['auto-filter'] === true) {
4097
- $commandArguments[] = '-define webp:auto-filter=true';
4098
- }
4099
- if ($this->options['metadata'] == 'none') {
4100
- $commandArguments[] = '-strip';
4101
- }
4102
- if ($this->options['alpha-quality'] !== 100) {
4103
- $commandArguments[] = '-define webp:alpha-quality=' . strval($this->options['alpha-quality']);
4104
- }
4105
-
4106
- // Unfortunately, near-lossless does not seem to be supported.
4107
- // it does have a "preprocessing" option, which may be doing something similar
4108
-
4109
- $commandArguments[] = '-define webp:method=' . $this->options['method'];
4110
-
4111
- $commandArguments[] = escapeshellarg($this->source);
4112
- $commandArguments[] = escapeshellarg('webp:' . $this->destination);
4113
-
4114
- return implode(' ', $commandArguments);
4115
- }
4116
-
4117
- protected function doActualConvert()
4118
- {
4119
- $this->logLn($this->getVersion());
4120
-
4121
- $command = $this->getPath() . ' ' . $this->createCommandLineOptions();
4122
-
4123
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport()) ? true : false;
4124
- if ($useNice) {
4125
- $this->logLn('using nice');
4126
- $command = 'nice ' . $command;
4127
- }
4128
- $this->logLn('Executing command: ' . $command);
4129
- exec($command, $output, $returnCode);
4130
-
4131
- $this->logExecOutput($output);
4132
- if ($returnCode == 0) {
4133
- $this->logLn('success');
4134
- } else {
4135
- $this->logLn('return code: ' . $returnCode);
4136
- }
4137
-
4138
- if ($returnCode == 127) {
4139
- throw new SystemRequirementsNotMetException('imagemagick is not installed');
4140
- }
4141
- if ($returnCode != 0) {
4142
- throw new SystemRequirementsNotMetException('The exec call failed');
4143
- }
4144
- }
4145
- }
4146
-
4147
- ?><?php
4148
-
4149
- namespace WebPConvert\Convert\Converters;
4150
-
4151
- use WebPConvert\Convert\Converters\AbstractConverter;
4152
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4153
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
4154
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4155
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4156
-
4157
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
4158
-
4159
- /**
4160
- * Convert images to webp using Imagick extension.
4161
- *
4162
- * @package WebPConvert
4163
- * @author Bjørn Rosell <it@rosell.dk>
4164
- * @since Class available since Release 2.0.0
4165
- */
4166
- class Imagick extends AbstractConverter
4167
- {
4168
- use EncodingAutoTrait;
4169
-
4170
- protected function getUnsupportedDefaultOptions()
4171
- {
4172
- return [
4173
- 'near-lossless',
4174
- 'preset',
4175
- 'size-in-percentage',
4176
- 'use-nice'
4177
- ];
4178
- }
4179
-
4180
- /**
4181
- * Check operationality of Imagick converter.
4182
- *
4183
- * Note:
4184
- * It may be that Gd has been compiled without jpeg support or png support.
4185
- * We do not check for this here, as the converter could still be used for the other.
4186
- *
4187
- * @throws SystemRequirementsNotMetException if system requirements are not met
4188
- * @return void
4189
- */
4190
- public function checkOperationality()
4191
- {
4192
- if (!extension_loaded('imagick')) {
4193
- throw new SystemRequirementsNotMetException('Required iMagick extension is not available.');
4194
- }
4195
-
4196
- if (!class_exists('\\Imagick')) {
4197
- throw new SystemRequirementsNotMetException(
4198
- 'iMagick is installed, but not correctly. The class Imagick is not available'
4199
- );
4200
- }
4201
-
4202
- $im = new \Imagick();
4203
- if (!in_array('WEBP', $im->queryFormats('WEBP'))) {
4204
- throw new SystemRequirementsNotMetException('iMagick was compiled without WebP support.');
4205
- }
4206
- }
4207
-
4208
- /**
4209
- * Check if specific file is convertable with current converter / converter settings.
4210
- *
4211
- * @throws SystemRequirementsNotMetException if Imagick does not support image type
4212
- */
4213
- public function checkConvertability()
4214
- {
4215
- $im = new \Imagick();
4216
- $mimeType = $this->getMimeTypeOfSource();
4217
- switch ($mimeType) {
4218
- case 'image/png':
4219
- if (!in_array('PNG', $im->queryFormats('PNG'))) {
4220
- throw new SystemRequirementsNotMetException(
4221
- 'Imagick has been compiled without PNG support and can therefore not convert this PNG image.'
4222
- );
4223
- }
4224
- break;
4225
- case 'image/jpeg':
4226
- if (!in_array('JPEG', $im->queryFormats('JPEG'))) {
4227
- throw new SystemRequirementsNotMetException(
4228
- 'Imagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
4229
- );
4230
- }
4231
- break;
4232
- }
4233
- }
4234
-
4235
- /**
4236
- *
4237
- * It may also throw an ImagickException if imagick throws an exception
4238
- * @throws CreateDestinationFileException if imageblob could not be saved to file
4239
- */
4240
- protected function doActualConvert()
4241
- {
4242
- /*
4243
- * More about iMagick's WebP options:
4244
- * - Inspect source code: https://github.com/ImageMagick/ImageMagick/blob/master/coders/webp.c#L559
4245
- * (search for "webp:")
4246
- * - http://www.imagemagick.org/script/webp.php
4247
- * - https://developers.google.com/speed/webp/docs/cwebp
4248
- * - https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
4249
- */
4250
-
4251
- $options = $this->options;
4252
-
4253
- // This might throw - we let it!
4254
- $im = new \Imagick($this->source);
4255
-
4256
- //$im = new \Imagick();
4257
- //$im->pingImage($this->source);
4258
- //$im->readImage($this->source);
4259
-
4260
- $im->setImageFormat('WEBP');
4261
-
4262
- $im->setOption('webp:method', $options['method']);
4263
- $im->setOption('webp:lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
4264
- $im->setOption('webp:low-memory', $options['low-memory'] ? 'true' : 'false');
4265
- $im->setOption('webp:alpha-quality', $options['alpha-quality']);
4266
-
4267
- if ($options['auto-filter'] === true) {
4268
- $im->setOption('webp:auto-filter', 'true');
4269
- }
4270
-
4271
- if ($options['metadata'] == 'none') {
4272
- // Strip metadata and profiles
4273
- $im->stripImage();
4274
- }
4275
-
4276
- if ($this->isQualityDetectionRequiredButFailing()) {
4277
- // Luckily imagick is a big boy, and automatically converts with same quality as
4278
- // source, when the quality isn't set.
4279
- // So we simply do not set quality.
4280
- // This actually kills the max-quality functionality. But I deem that this is more important
4281
- // because setting image quality to something higher than source generates bigger files,
4282
- // but gets you no extra quality. When failing to limit quality, you at least get something
4283
- // out of it
4284
- $this->logLn('Converting without setting quality in order to achieve auto quality');
4285
- } else {
4286
- $im->setImageCompressionQuality($this->getCalculatedQuality());
4287
- }
4288
-
4289
- // https://stackoverflow.com/questions/29171248/php-imagick-jpeg-optimization
4290
- // setImageFormat
4291
-
4292
- // TODO: Read up on
4293
- // https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
4294
- // https://github.com/nwtn/php-respimg
4295
-
4296
- // TODO:
4297
- // Should we set alpha channel for PNG's like suggested here:
4298
- // https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
4299
- // It seems that alpha channel works without... (at least I see completely transparerent pixels)
4300
-
4301
- // We used to use writeImageFile() method. But we now use getImageBlob(). See issue #43
4302
-
4303
- // This might throw - we let it!
4304
- $imageBlob = $im->getImageBlob();
4305
-
4306
- $success = file_put_contents($this->destination, $imageBlob);
4307
-
4308
- if (!$success) {
4309
- throw new CreateDestinationFileException('Failed writing file');
4310
- }
4311
-
4312
- // Btw: check out processWebp() method here:
4313
- // https://github.com/Intervention/image/blob/master/src/Intervention/Image/Imagick/Encoder.php
4314
- }
4315
- }
4316
-
4317
- ?><?php
4318
-
4319
- namespace WebPConvert\Convert\Converters;
4320
-
4321
- use WebPConvert\Convert\Converters\AbstractConverter;
4322
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4323
-
4324
- /**
4325
- * Non-functional converter, just here to tell you that it has been renamed.
4326
- *
4327
- * @package WebPConvert
4328
- * @author Bjørn Rosell <it@rosell.dk>
4329
- * @since Class available since Release 2.0.0
4330
- */
4331
- class ImagickBinary extends AbstractConverter
4332
- {
4333
- public function checkOperationality()
4334
- {
4335
- throw new ConversionFailedException(
4336
- 'This converter has changed ID from "imagickbinary" to "imagemagick". You need to change!'
4337
- );
4338
- }
4339
-
4340
- protected function doActualConvert()
4341
- {
4342
- $this->checkOperationality();
4343
- }
4344
- }
4345
-
4346
- ?><?php
4347
-
4348
- namespace WebPConvert\Convert\Converters;
4349
-
4350
- use WebPConvert\Convert\ConverterFactory;
4351
- use WebPConvert\Convert\Converters\AbstractConverter;
4352
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4353
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
4354
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4355
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
4356
- use WebPConvert\Options\BooleanOption;
4357
- use WebPConvert\Options\ArrayOption;
4358
- use WebPConvert\Options\GhostOption;
4359
- use WebPConvert\Options\SensitiveArrayOption;
4360
-
4361
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
4362
-
4363
- /**
4364
- * Convert images to webp by trying a stack of converters until success.
4365
- *
4366
- * @package WebPConvert
4367
- * @author Bjørn Rosell <it@rosell.dk>
4368
- * @since Class available since Release 2.0.0
4369
- */
4370
- class Stack extends AbstractConverter
4371
- {
4372
-
4373
- protected function getUnsupportedDefaultOptions()
4374
- {
4375
- return [
4376
- 'alpha-quality',
4377
- 'auto-filter',
4378
- 'encoding',
4379
- 'low-memory',
4380
- 'metadata',
4381
- 'method',
4382
- 'near-lossless',
4383
- 'preset',
4384
- 'size-in-percentage',
4385
- 'use-nice',
4386
- 'skip',
4387
- 'default-quality',
4388
- 'quality',
4389
- 'max-quality',
4390
- ];
4391
- }
4392
-
4393
- protected function createOptions()
4394
- {
4395
- parent::createOptions();
4396
-
4397
- $this->options2->addOptions(
4398
- new SensitiveArrayOption('converters', self::getAvailableConverters()),
4399
- new SensitiveArrayOption('converter-options', []),
4400
- new BooleanOption('shuffle', false),
4401
- new ArrayOption('preferred-converters', []),
4402
- new SensitiveArrayOption('extra-converters', [])
4403
- );
4404
- }
4405
-
4406
- /**
4407
- * Get available converters (ids) - ordered by awesomeness.
4408
- *
4409
- * @return array An array of ids of converters that comes with this library
4410
- */
4411
- public static function getAvailableConverters()
4412
- {
4413
- return [
4414
- 'cwebp', 'vips', 'imagick', 'gmagick', 'imagemagick', 'graphicsmagick', 'wpc', 'ewww', 'gd'
4415
- ];
4416
- }
4417
-
4418
- /**
4419
- * Check (general) operationality of imagack converter executable
4420
- *
4421
- * @throws SystemRequirementsNotMetException if system requirements are not met
4422
- */
4423
- public function checkOperationality()
4424
- {
4425
- if (count($this->options['converters']) == 0) {
4426
- throw new ConverterNotOperationalException(
4427
- 'Converter stack is empty! - no converters to try, no conversion can be made!'
4428
- );
4429
- }
4430
-
4431
- // TODO: We should test if all converters are found in order to detect problems early
4432
-
4433
- //$this->logLn('Stack converter ignited');
4434
- }
4435
-
4436
- protected function doActualConvert()
4437
- {
4438
- $options = $this->options;
4439
-
4440
- $beginTimeStack = microtime(true);
4441
-
4442
- $anyRuntimeErrors = false;
4443
-
4444
- $converters = $options['converters'];
4445
- if (count($options['extra-converters']) > 0) {
4446
- $converters = array_merge($converters, $options['extra-converters']);
4447
- /*foreach ($options['extra-converters'] as $extra) {
4448
- $converters[] = $extra;
4449
- }*/
4450
- }
4451
-
4452
- // preferred-converters
4453
- if (count($options['preferred-converters']) > 0) {
4454
- foreach (array_reverse($options['preferred-converters']) as $prioritizedConverter) {
4455
- foreach ($converters as $i => $converter) {
4456
- if (is_array($converter)) {
4457
- $converterId = $converter['converter'];
4458
- } else {
4459
- $converterId = $converter;
4460
- }
4461
- if ($converterId == $prioritizedConverter) {
4462
- unset($converters[$i]);
4463
- array_unshift($converters, $converter);
4464
- break;
4465
- }
4466
- }
4467
- }
4468
- // perhaps write the order to the log? (without options) - but this requires some effort
4469
- }
4470
-
4471
- // shuffle
4472
- if ($options['shuffle']) {
4473
- shuffle($converters);
4474
- }
4475
-
4476
- //$this->logLn(print_r($converters));
4477
- //$options['converters'] = $converters;
4478
- //$defaultConverterOptions = $options;
4479
- $defaultConverterOptions = [];
4480
-
4481
- foreach ($this->options2->getOptionsMap() as $id => $option) {
4482
- if ($option->isValueExplicitlySet() && ! ($option instanceof GhostOption)) {
4483
- //$this->logLn('hi' . $id);
4484
- $defaultConverterOptions[$id] = $option->getValue();
4485
- }
4486
- }
4487
-
4488
- //unset($defaultConverterOptions['converters']);
4489
- //unset($defaultConverterOptions['converter-options']);
4490
- $defaultConverterOptions['_skip_input_check'] = true;
4491
- $defaultConverterOptions['_suppress_success_message'] = true;
4492
- unset($defaultConverterOptions['converters']);
4493
- unset($defaultConverterOptions['extra-converters']);
4494
- unset($defaultConverterOptions['converter-options']);
4495
- unset($defaultConverterOptions['preferred-converters']);
4496
- unset($defaultConverterOptions['shuffle']);
4497
-
4498
- // $this->logLn('converters: ' . print_r($converters, true));
4499
-
4500
- //return;
4501
- foreach ($converters as $converter) {
4502
- if (is_array($converter)) {
4503
- $converterId = $converter['converter'];
4504
- $converterOptions = isset($converter['options']) ? $converter['options'] : [];
4505
- } else {
4506
- $converterId = $converter;
4507
- $converterOptions = [];
4508
- if (isset($options['converter-options'][$converterId])) {
4509
- // Note: right now, converter-options are not meant to be used,
4510
- // when you have several converters of the same type
4511
- $converterOptions = $options['converter-options'][$converterId];
4512
- }
4513
- }
4514
- $converterOptions = array_merge($defaultConverterOptions, $converterOptions);
4515
- /*
4516
- if ($converterId != 'stack') {
4517
- //unset($converterOptions['converters']);
4518
- //unset($converterOptions['converter-options']);
4519
- } else {
4520
- //$converterOptions['converter-options'] =
4521
- $this->logLn('STACK');
4522
- $this->logLn('converterOptions: ' . print_r($converterOptions, true));
4523
- }*/
4524
-
4525
- $beginTime = microtime(true);
4526
-
4527
- $this->ln();
4528
- $this->logLn('Trying: ' . $converterId, 'italic');
4529
-
4530
- $converter = ConverterFactory::makeConverter(
4531
- $converterId,
4532
- $this->source,
4533
- $this->destination,
4534
- $converterOptions,
4535
- $this->logger
4536
- );
4537
-
4538
- try {
4539
- $converter->doConvert();
4540
-
4541
- //self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
4542
-
4543
- $this->logLn($converterId . ' succeeded :)');
4544
- //throw new ConverterNotOperationalException('...');
4545
- return;
4546
- } catch (ConverterNotOperationalException $e) {
4547
- $this->logLn($e->getMessage());
4548
- } catch (ConversionFailedException $e) {
4549
- $this->logLn($e->getMessage(), 'italic');
4550
- $prev = $e->getPrevious();
4551
- if (!is_null($prev)) {
4552
- $this->logLn($prev->getMessage(), 'italic');
4553
- $this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
4554
- $this->ln();
4555
- }
4556
- //$this->logLn($e->getTraceAsString());
4557
- $anyRuntimeErrors = true;
4558
- } catch (ConversionSkippedException $e) {
4559
- $this->logLn($e->getMessage());
4560
- }
4561
-
4562
- $this->logLn($converterId . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
4563
- }
4564
-
4565
- $this->ln();
4566
- $this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
4567
-
4568
- // Hm, Scrutinizer complains that $anyRuntimeErrors is always false. But that is not true!
4569
- if ($anyRuntimeErrors) {
4570
- // At least one converter failed
4571
- throw new ConversionFailedException(
4572
- 'None of the converters in the stack could convert the image.'
4573
- );
4574
- } else {
4575
- // All converters threw a SystemRequirementsNotMetException
4576
- throw new ConverterNotOperationalException('None of the converters in the stack are operational');
4577
- }
4578
- }
4579
- }
4580
-
4581
- ?><?php
4582
-
4583
- namespace WebPConvert\Convert\Converters;
4584
-
4585
- use WebPConvert\Convert\Converters\AbstractConverter;
4586
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4587
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4588
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4589
- use WebPConvert\Options\BooleanOption;
4590
-
4591
- //require '/home/rosell/.composer/vendor/autoload.php';
4592
-
4593
- /**
4594
- * Convert images to webp using Vips extension.
4595
- *
4596
- * @package WebPConvert
4597
- * @author Bjørn Rosell <it@rosell.dk>
4598
- * @since Class available since Release 2.0.0
4599
- */
4600
- class Vips extends AbstractConverter
4601
- {
4602
- use EncodingAutoTrait;
4603
-
4604
- protected function getUnsupportedDefaultOptions()
4605
- {
4606
- return [
4607
- 'size-in-percentage',
4608
- 'use-nice'
4609
- ];
4610
- }
4611
-
4612
- protected function createOptions()
4613
- {
4614
- parent::createOptions();
4615
-
4616
- $this->options2->addOptions(
4617
- new BooleanOption('smart-subsample', false)
4618
- );
4619
- }
4620
-
4621
- /**
4622
- * Check operationality of Vips converter.
4623
- *
4624
- * @throws SystemRequirementsNotMetException if system requirements are not met
4625
- */
4626
- public function checkOperationality()
4627
- {
4628
- if (!extension_loaded('vips')) {
4629
- throw new SystemRequirementsNotMetException('Required Vips extension is not available.');
4630
- }
4631
-
4632
- if (!function_exists('vips_image_new_from_file')) {
4633
- throw new SystemRequirementsNotMetException(
4634
- 'Vips extension seems to be installed, however something is not right: ' .
4635
- 'the function "vips_image_new_from_file" is not available.'
4636
- );
4637
- }
4638
-
4639
- // TODO: Should we also test if webp is available? (It seems not to be neccessary - it seems
4640
- // that webp be well intergrated part of vips)
4641
- }
4642
-
4643
- /**
4644
- * Check if specific file is convertable with current converter / converter settings.
4645
- *
4646
- * @throws SystemRequirementsNotMetException if Vips does not support image type
4647
- */
4648
- public function checkConvertability()
4649
- {
4650
- // It seems that png and jpeg are always supported by Vips
4651
- // - so nothing needs to be done here
4652
-
4653
- if (function_exists('vips_version')) {
4654
- $this->logLn('vipslib version: ' . vips_version());
4655
- }
4656
- $this->logLn('vips extension version: ' . phpversion('vips'));
4657
- }
4658
-
4659
- /**
4660
- * Create vips image resource from source file
4661
- *
4662
- * @throws ConversionFailedException if image resource cannot be created
4663
- * @return resource vips image resource
4664
- */
4665
- private function createImageResource()
4666
- {
4667
- // We are currently using vips_image_new_from_file(), but we could consider
4668
- // calling vips_jpegload / vips_pngload instead
4669
- $result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []);
4670
- if ($result === -1) {
4671
- /*throw new ConversionFailedException(
4672
- 'Failed creating new vips image from file: ' . $this->source
4673
- );*/
4674
- $message = /** @scrutinizer ignore-call */ vips_error_buffer();
4675
- throw new ConversionFailedException($message);
4676
- }
4677
-
4678
- if (!is_array($result)) {
4679
- throw new ConversionFailedException(
4680
- 'vips_image_new_from_file did not return an array, which we expected'
4681
- );
4682
- }
4683
-
4684
- if (count($result) != 1) {
4685
- throw new ConversionFailedException(
4686
- 'vips_image_new_from_file did not return an array of length 1 as we expected ' .
4687
- '- length was: ' . count($result)
4688
- );
4689
- }
4690
-
4691
- $im = array_shift($result);
4692
- return $im;
4693
- }
4694
-
4695
- /**
4696
- * Create parameters for webpsave
4697
- *
4698
- * @return array the parameters as an array
4699
- */
4700
- private function createParamsForVipsWebPSave()
4701
- {
4702
- // webpsave options are described here:
4703
- // v 8.8.0: https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
4704
- // v ?.?.?: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
4705
- // near_lossless option is described here: https://github.com/libvips/libvips/pull/430
4706
-
4707
- // Note that "method" is currently not supported (27 may 2019)
4708
-
4709
- $options = [
4710
- "Q" => $this->getCalculatedQuality(),
4711
- 'lossless' => ($this->options['encoding'] == 'lossless'),
4712
- 'strip' => $this->options['metadata'] == 'none',
4713
- ];
4714
-
4715
- // Only set the following options if they differ from the default of vipslib
4716
- // This ensures we do not get warning if that property isn't supported
4717
- if ($this->options['smart-subsample'] !== false) {
4718
- $options['smart_subsample'] = $this->options['smart-subsample'];
4719
- }
4720
- if ($this->options['alpha-quality'] !== 100) {
4721
- $options['alpha_q'] = $this->options['alpha-quality'];
4722
- }
4723
-
4724
- if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) {
4725
- // preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last
4726
- $options['preset'] = array_search(
4727
- $this->options['preset'],
4728
- ['default', 'picture', 'photo', 'drawing', 'icon', 'text']
4729
- );
4730
- }
4731
- if ($this->options['near-lossless'] !== 100) {
4732
- if ($this->options['encoding'] == 'lossless') {
4733
- // We only let near_lossless have effect when encoding is set to lossless
4734
- // otherwise encoding=auto would not work as expected
4735
- // Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016
4736
- // seems it corresponds to release 8.4.2
4737
- $options['near_lossless'] = true;
4738
-
4739
- // In Vips, the near-lossless value is controlled by Q.
4740
- // this differs from how it is done in cwebp, where it is an integer.
4741
- // We have chosen same option syntax as cwebp
4742
- $options['Q'] = $this->options['near-lossless'];
4743
- }
4744
- }
4745
-
4746
- return $options;
4747
- }
4748
-
4749
- /**
4750
- * Convert with vips extension.
4751
- *
4752
- * Tries to create image resource and save it as webp using the calculated options.
4753
- * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
4754
- * (recursively call itself until there is no more of these kind of errors).
4755
- *
4756
- * @param resource $im A vips image resource to save
4757
- * @throws ConversionFailedException if conversion fails.
4758
- */
4759
- private function webpsave($im, $options)
4760
- {
4761
- $result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options);
4762
-
4763
- //trigger_error('test-warning', E_USER_WARNING);
4764
- if ($result === -1) {
4765
- $message = /** @scrutinizer ignore-call */ vips_error_buffer();
4766
-
4767
- $nameOfPropertyNotFound = '';
4768
- if (preg_match("#no property named .(.*).#", $message, $matches)) {
4769
- $nameOfPropertyNotFound = $matches[1];
4770
- } elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) {
4771
- // Actually, I am not quite sure if this ever happens.
4772
- // I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning
4773
- if (in_array($matches[1], ['lossless', 'alpha_q', 'near_lossless', 'smart_subsample'])) {
4774
- $nameOfPropertyNotFound = $matches[1];
4775
- }
4776
- }
4777
-
4778
- if ($nameOfPropertyNotFound != '') {
4779
- $this->logLn(
4780
- 'Your version of vipslib does not support the "' . $nameOfPropertyNotFound . '" property. ' .
4781
- 'The option is ignored.'
4782
- );
4783
- unset($options[$nameOfPropertyNotFound]);
4784
- $this->webpsave($im, $options);
4785
- } else {
4786
- throw new ConversionFailedException($message);
4787
- }
4788
- }
4789
- }
4790
-
4791
- /**
4792
- * Convert with vips extension.
4793
- *
4794
- * Tries to create image resource and save it as webp using the calculated options.
4795
- * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
4796
- * (repeat until success).
4797
- *
4798
- * @throws ConversionFailedException if conversion fails.
4799
- */
4800
- protected function doActualConvert()
4801
- {
4802
- /*
4803
- $im = \Jcupitt\Vips\Image::newFromFile($this->source);
4804
- //$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]);
4805
-
4806
- $im->webpsave($this->destination, [
4807
- "Q" => 80,
4808
- //'near_lossless' => true
4809
- ]);
4810
- return;*/
4811
-
4812
- $im = $this->createImageResource();
4813
- $options = $this->createParamsForVipsWebPSave();
4814
- $this->webpsave($im, $options);
4815
- }
4816
- }
4817
-
4818
- ?><?php
4819
-
4820
- namespace WebPConvert\Convert\Converters;
4821
-
4822
- use WebPConvert\Convert\Converters\AbstractConverter;
4823
- use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
4824
- use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
4825
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4826
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4827
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
4828
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4829
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
4830
- use WebPConvert\Options\BooleanOption;
4831
- use WebPConvert\Options\IntegerOption;
4832
- use WebPConvert\Options\SensitiveStringOption;
4833
-
4834
- /**
4835
- * Convert images to webp using Wpc (a cloud converter based on WebP Convert).
4836
- *
4837
- * @package WebPConvert
4838
- * @author Bjørn Rosell <it@rosell.dk>
4839
- * @since Class available since Release 2.0.0
4840
- */
4841
- class Wpc extends AbstractConverter
4842
- {
4843
- use CloudConverterTrait;
4844
- use CurlTrait;
4845
- use EncodingAutoTrait;
4846
-
4847
- protected function getUnsupportedDefaultOptions()
4848
- {
4849
- return [];
4850
- }
4851
-
4852
- public function supportsLossless()
4853
- {
4854
- return ($this->options['api-version'] >= 2);
4855
- }
4856
-
4857
- public function passOnEncodingAuto()
4858
- {
4859
- // We could make this configurable. But I guess passing it on is always to be preferred
4860
- // for api >= 2.
4861
- return ($this->options['api-version'] >= 2);
4862
- }
4863
-
4864
- protected function createOptions()
4865
- {
4866
- parent::createOptions();
4867
-
4868
- $this->options2->addOptions(
4869
- new SensitiveStringOption('api-key', ''), /* for communicating with wpc api v.1+ */
4870
- new SensitiveStringOption('secret', ''), /* for communicating with wpc api v.0 */
4871
- new SensitiveStringOption('api-url', ''),
4872
- new SensitiveStringOption('url', ''), /* DO NOT USE. Only here to keep the protection */
4873
- new IntegerOption('api-version', 2, 0, 2),
4874
- new BooleanOption('crypt-api-key-in-transfer', false) /* new in api v.1 */
4875
- );
4876
- }
4877
-
4878
- private static function createRandomSaltForBlowfish()
4879
- {
4880
- $salt = '';
4881
- $validCharsForSalt = array_merge(
4882
- range('A', 'Z'),
4883
- range('a', 'z'),
4884
- range('0', '9'),
4885
- ['.', '/']
4886
- );
4887
-
4888
- for ($i=0; $i<22; $i++) {
4889
- $salt .= $validCharsForSalt[array_rand($validCharsForSalt)];
4890
- }
4891
- return $salt;
4892
- }
4893
-
4894
- /**
4895
- * Get api key from options or environment variable
4896
- *
4897
- * @return string api key or empty string if none is set
4898
- */
4899
- private function getApiKey()
4900
- {
4901
- if ($this->options['api-version'] == 0) {
4902
- if (!empty($this->options['secret'])) {
4903
- return $this->options['secret'];
4904
- }
4905
- } elseif ($this->options['api-version'] >= 1) {
4906
- if (!empty($this->options['api-key'])) {
4907
- return $this->options['api-key'];
4908
- }
4909
- }
4910
- if (defined('WEBPCONVERT_WPC_API_KEY')) {
4911
- return constant('WEBPCONVERT_WPC_API_KEY');
4912
- }
4913
- if (!empty(getenv('WEBPCONVERT_WPC_API_KEY'))) {
4914
- return getenv('WEBPCONVERT_WPC_API_KEY');
4915
- }
4916
- return '';
4917
- }
4918
-
4919
- /**
4920
- * Get url from options or environment variable
4921
- *
4922
- * @return string URL to WPC or empty string if none is set
4923
- */
4924
- private function getApiUrl()
4925
- {
4926
- if (!empty($this->options['api-url'])) {
4927
- return $this->options['api-url'];
4928
- }
4929
- if (defined('WEBPCONVERT_WPC_API_URL')) {
4930
- return constant('WEBPCONVERT_WPC_API_URL');
4931
- }
4932
- if (!empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
4933
- return getenv('WEBPCONVERT_WPC_API_URL');
4934
- }
4935
- return '';
4936
- }
4937
-
4938
-
4939
- /**
4940
- * Check operationality of Wpc converter.
4941
- *
4942
- * @throws SystemRequirementsNotMetException if system requirements are not met (curl)
4943
- * @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
4944
- */
4945
- public function checkOperationality()
4946
- {
4947
-
4948
- $options = $this->options;
4949
-
4950
- $apiVersion = $options['api-version'];
4951
-
4952
- if ($this->getApiUrl() == '') {
4953
- if (isset($this->options['url']) && ($this->options['url'] != '')) {
4954
- throw new ConverterNotOperationalException(
4955
- 'The "url" option has been renamed to "api-url" in webp-convert 2.0. ' .
4956
- 'You must change the configuration accordingly.'
4957
- );
4958
- }
4959
- throw new ConverterNotOperationalException(
4960
- 'Missing URL. You must install Webp Convert Cloud Service on a server, ' .
4961
- 'or the WebP Express plugin for Wordpress - and supply the url.'
4962
- );
4963
- }
4964
-
4965
- if ($apiVersion == 0) {
4966
- if (!empty($this->getApiKey())) {
4967
- // if secret is set, we need md5() and md5_file() functions
4968
- if (!function_exists('md5')) {
4969
- throw new ConverterNotOperationalException(
4970
- 'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
4971
- 'contents. ' .
4972
- 'But the required md5() PHP function is not available.'
4973
- );
4974
- }
4975
- if (!function_exists('md5_file')) {
4976
- throw new ConverterNotOperationalException(
4977
- 'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
4978
- 'contents. But the required md5_file() PHP function is not available.'
4979
- );
4980
- }
4981
- }
4982
- } elseif ($apiVersion >= 1) {
4983
- if ($options['crypt-api-key-in-transfer']) {
4984
- if (!function_exists('crypt')) {
4985
- throw new ConverterNotOperationalException(
4986
- 'Configured to crypt the api-key, but crypt() function is not available.'
4987
- );
4988
- }
4989
-
4990
- if (!defined('CRYPT_BLOWFISH')) {
4991
- throw new ConverterNotOperationalException(
4992
- 'Configured to crypt the api-key. ' .
4993
- 'That requires Blowfish encryption, which is not available on your current setup.'
4994
- );
4995
- }
4996
- }
4997
- }
4998
-
4999
- // Check for curl requirements
5000
- $this->checkOperationalityForCurlTrait();
5001
- }
5002
-
5003
- /*
5004
- public function checkConvertability()
5005
- {
5006
- // check upload limits
5007
- $this->checkConvertabilityCloudConverterTrait();
5008
-
5009
- // TODO: some from below can be moved up here
5010
- }
5011
- */
5012
-
5013
- private function createOptionsToSend()
5014
- {
5015
- $optionsToSend = $this->options;
5016
-
5017
- if ($this->isQualityDetectionRequiredButFailing()) {
5018
- // quality was set to "auto", but we could not meassure the quality of the jpeg locally
5019
- // Ask the cloud service to do it, rather than using what we came up with.
5020
- $optionsToSend['quality'] = 'auto';
5021
- } else {
5022
- $optionsToSend['quality'] = $this->getCalculatedQuality();
5023
- }
5024
-
5025
- // The following are unset for security reasons.
5026
- unset($optionsToSend['converters']);
5027
- unset($optionsToSend['secret']);
5028
- unset($optionsToSend['api-key']);
5029
- unset($optionsToSend['api-url']);
5030
-
5031
- $apiVersion = $optionsToSend['api-version'];
5032
-
5033
- if ($apiVersion == 1) {
5034
- // Lossless can be "auto" in api 2, but in api 1 "auto" is not supported
5035
- //unset($optionsToSend['lossless']);
5036
- } elseif ($apiVersion == 2) {
5037
- //unset($optionsToSend['png']);
5038
- //unset($optionsToSend['jpeg']);
5039
-
5040
- // The following are unset for security reasons.
5041
- unset($optionsToSend['cwebp-command-line-options']);
5042
- unset($optionsToSend['command-line-options']);
5043
- }
5044
-
5045
- return $optionsToSend;
5046
- }
5047
-
5048
- private function createPostData()
5049
- {
5050
- $options = $this->options;
5051
-
5052
- $postData = [
5053
- 'file' => curl_file_create($this->source),
5054
- 'options' => json_encode($this->createOptionsToSend()),
5055
- 'servername' => (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')
5056
- ];
5057
-
5058
- $apiVersion = $options['api-version'];
5059
-
5060
- $apiKey = $this->getApiKey();
5061
-
5062
- if ($apiVersion == 0) {
5063
- $postData['hash'] = md5(md5_file($this->source) . $apiKey);
5064
- } elseif ($apiVersion == 1) {
5065
- //$this->logLn('api key: ' . $apiKey);
5066
-
5067
- if ($options['crypt-api-key-in-transfer']) {
5068
- $salt = self::createRandomSaltForBlowfish();
5069
- $postData['salt'] = $salt;
5070
-
5071
- // Strip off the first 28 characters (the first 6 are always "$2y$10$". The next 22 is the salt)
5072
- $postData['api-key-crypted'] = substr(crypt($apiKey, '$2y$10$' . $salt . '$'), 28);
5073
- } else {
5074
- $postData['api-key'] = $apiKey;
5075
- }
5076
- }
5077
- return $postData;
5078
- }
5079
-
5080
- protected function doActualConvert()
5081
- {
5082
- $ch = self::initCurl();
5083
-
5084
- //$this->logLn('api url: ' . $this->getApiUrl());
5085
-
5086
- curl_setopt_array($ch, [
5087
- CURLOPT_URL => $this->getApiUrl(),
5088
- CURLOPT_POST => 1,
5089
- CURLOPT_POSTFIELDS => $this->createPostData(),
5090
- CURLOPT_BINARYTRANSFER => true,
5091
- CURLOPT_RETURNTRANSFER => true,
5092
- CURLOPT_HEADER => false,
5093
- CURLOPT_SSL_VERIFYPEER => false
5094
- ]);
5095
-
5096
- $response = curl_exec($ch);
5097
- if (curl_errno($ch)) {
5098
- $this->logLn('Curl error: ', 'bold');
5099
- $this->logLn(curl_error($ch));
5100
- throw new ConverterNotOperationalException('Curl error:');
5101
- }
5102
-
5103
- // Check if we got a 404
5104
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
5105
- if ($httpCode == 404) {
5106
- curl_close($ch);
5107
- throw new ConversionFailedException(
5108
- 'WPC was not found at the specified URL - we got a 404 response.'
5109
- );
5110
- }
5111
-
5112
- // Check for empty response
5113
- if (empty($response)) {
5114
- throw new ConversionFailedException(
5115
- 'Error: Unexpected result. We got nothing back. ' .
5116
- 'HTTP CODE: ' . $httpCode . '. ' .
5117
- 'Content type:' . curl_getinfo($ch, CURLINFO_CONTENT_TYPE)
5118
- );
5119
- };
5120
-
5121
- // The WPC cloud service either returns an image or an error message
5122
- // Images has application/octet-stream.
5123
- // Verify that we got an image back.
5124
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
5125
- curl_close($ch);
5126
-
5127
- if (substr($response, 0, 1) == '{') {
5128
- $responseObj = json_decode($response, true);
5129
- if (isset($responseObj['errorCode'])) {
5130
- switch ($responseObj['errorCode']) {
5131
- case 0:
5132
- throw new ConverterNotOperationalException(
5133
- 'There are problems with the server setup: "' .
5134
- $responseObj['errorMessage'] . '"'
5135
- );
5136
- case 1:
5137
- throw new InvalidApiKeyException(
5138
- 'Access denied. ' . $responseObj['errorMessage']
5139
- );
5140
- default:
5141
- throw new ConversionFailedException(
5142
- 'Conversion failed: "' . $responseObj['errorMessage'] . '"'
5143
- );
5144
- }
5145
- }
5146
- }
5147
-
5148
- // WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
5149
- if (substr($response, 0, 7) == 'failed!') {
5150
- throw new ConversionFailedException(
5151
- 'WPC failed converting image: "' . substr($response, 7) . '"'
5152
- );
5153
- }
5154
-
5155
- $this->logLn('Bummer, we did not receive an image');
5156
- $this->log('What we received starts with: "');
5157
- $this->logLn(
5158
- str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400)))) . '..."'
5159
- );
5160
-
5161
- throw new ConversionFailedException('Unexpected result. We did not receive an image but something else.');
5162
- //throw new ConverterNotOperationalException($response);
5163
- }
5164
-
5165
- $success = file_put_contents($this->destination, $response);
5166
- curl_close($ch);
5167
-
5168
- if (!$success) {
5169
- throw new ConversionFailedException('Error saving file. Check file permissions');
5170
- }
5171
- }
5172
- }
5173
-
5174
- ?><?php
5175
-
5176
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5177
-
5178
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5179
-
5180
- class ConversionSkippedException extends ConversionFailedException
5181
- {
5182
- public $description = 'The converter declined converting';
5183
- }
5184
-
5185
- ?><?php
5186
-
5187
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5188
-
5189
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5190
-
5191
- class ConverterNotOperationalException extends ConversionFailedException
5192
- {
5193
- public $description = 'The converter is not operational';
5194
- }
5195
-
5196
- ?><?php
5197
-
5198
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5199
-
5200
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5201
-
5202
- class FileSystemProblemsException extends ConversionFailedException
5203
- {
5204
- public $description = 'Filesystem problems';
5205
- }
5206
-
5207
- ?><?php
5208
-
5209
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5210
-
5211
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5212
-
5213
- class InvalidInputException extends ConversionFailedException
5214
- {
5215
- public $description = 'Invalid input';
5216
- }
5217
-
5218
- ?><?php
5219
-
5220
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
5221
-
5222
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
5223
-
5224
- class InvalidApiKeyException extends ConverterNotOperationalException
5225
- {
5226
- public $description = 'The converter is not operational (access denied)';
5227
- }
5228
-
5229
- ?><?php
5230
-
5231
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
5232
-
5233
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
5234
-
5235
- class SystemRequirementsNotMetException extends ConverterNotOperationalException
5236
- {
5237
- public $description = 'The converter is not operational (system requirements not met)';
5238
- }
5239
-
5240
- ?><?php
5241
-
5242
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
5243
-
5244
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
5245
-
5246
- class CreateDestinationFileException extends FileSystemProblemsException
5247
- {
5248
- public $description = 'The converter could not create destination file. Check file permisions!';
5249
- }
5250
-
5251
- ?><?php
5252
-
5253
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
5254
-
5255
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
5256
-
5257
- class CreateDestinationFolderException extends FileSystemProblemsException
5258
- {
5259
- public $description = 'The converter could not create destination folder. Check file permisions!';
5260
- }
5261
-
5262
- ?><?php
5263
-
5264
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5265
-
5266
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5267
-
5268
- class ConverterNotFoundException extends InvalidInputException
5269
- {
5270
- public $description = 'The converter does not exist.';
5271
- }
5272
-
5273
- ?><?php
5274
-
5275
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5276
-
5277
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5278
-
5279
- class InvalidImageTypeException extends InvalidInputException
5280
- {
5281
- public $description = 'The converter does not handle the supplied image type';
5282
- }
5283
-
5284
- ?><?php
5285
-
5286
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5287
-
5288
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5289
-
5290
- class TargetNotFoundException extends InvalidInputException
5291
- {
5292
- public $description = 'The converter could not locate source file';
5293
- }
5294
-
5295
- ?><?php
5296
-
5297
- namespace WebPConvert\Convert\Helpers;
5298
-
5299
- /**
5300
- * Try to detect quality of a jpeg image using various tools.
5301
- *
5302
- * @package WebPConvert
5303
- * @author Bjørn Rosell <it@rosell.dk>
5304
- * @since Class available since Release 2.0.0
5305
- */
5306
- class JpegQualityDetector
5307
- {
5308
-
5309
- /**
5310
- * Try to detect quality of jpeg using imagick extension
5311
- *
5312
- * @param string $filename A complete file path to file to be examined
5313
- * @return int|null Quality, or null if it was not possible to detect quality
5314
- */
5315
- private static function detectQualityOfJpgUsingImagick($filename)
5316
- {
5317
- if (extension_loaded('imagick') && class_exists('\\Imagick')) {
5318
- try {
5319
- $img = new \Imagick($filename);
5320
-
5321
- // The required function is available as from PECL imagick v2.2.2
5322
- if (method_exists($img, 'getImageCompressionQuality')) {
5323
- return $img->getImageCompressionQuality();
5324
- }
5325
- } catch (\Exception $e) {
5326
- // Well well, it just didn't work out.
5327
- // - But perhaps next method will work...
5328
- }
5329
- }
5330
- return null;
5331
- }
5332
-
5333
-
5334
- /**
5335
- * Try to detect quality of jpeg using imagick binary
5336
- *
5337
- * @param string $filename A complete file path to file to be examined
5338
- * @return int|null Quality, or null if it was not possible to detect quality
5339
- */
5340
- private static function detectQualityOfJpgUsingImageMagick($filename)
5341
- {
5342
- if (function_exists('exec')) {
5343
- // Try Imagick using exec, and routing stderr to stdout (the "2>$1" magic)
5344
- exec("identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", $output, $returnCode);
5345
- //echo 'out:' . print_r($output, true);
5346
- if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
5347
- return intval($output[0]);
5348
- }
5349
- }
5350
- return null;
5351
- }
5352
-
5353
-
5354
- /**
5355
- * Try to detect quality of jpeg using gmagick binary
5356
- *
5357
- * @param string $filename A complete file path to file to be examined
5358
- * @return int|null Quality, or null if it was not possible to detect quality
5359
- */
5360
- private static function detectQualityOfJpgUsingGraphicsMagick($filename)
5361
- {
5362
- if (function_exists('exec')) {
5363
- // Try GraphicsMagick
5364
- exec("gm identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", $output, $returnCode);
5365
- if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
5366
- return intval($output[0]);
5367
- }
5368
- }
5369
- return null;
5370
- }
5371
-
5372
-
5373
- /**
5374
- * Try to detect quality of jpeg.
5375
- *
5376
- * Note: This method does not throw errors, but might dispatch warnings.
5377
- * You can use the WarningsIntoExceptions class if it is critical to you that nothing gets "printed"
5378
- *
5379
- * @param string $filename A complete file path to file to be examined
5380
- * @return int|null Quality, or null if it was not possible to detect quality
5381
- */
5382
- public static function detectQualityOfJpg($filename)
5383
- {
5384
-
5385
- //trigger_error('warning test', E_USER_WARNING);
5386
-
5387
- // Test that file exists in order not to break things.
5388
- if (!file_exists($filename)) {
5389
- // One could argue that it would be better to throw an Exception...?
5390
- return null;
5391
- }
5392
-
5393
- // Try Imagick extension, if available
5394
- $quality = self::detectQualityOfJpgUsingImagick($filename);
5395
-
5396
- if (is_null($quality)) {
5397
- $quality = self::detectQualityOfJpgUsingImageMagick($filename);
5398
- }
5399
-
5400
- if (is_null($quality)) {
5401
- $quality = self::detectQualityOfJpgUsingGraphicsMagick($filename);
5402
- }
5403
-
5404
- return $quality;
5405
- }
5406
- }
5407
-
5408
- ?><?php
5409
-
5410
- namespace WebPConvert\Convert\Helpers;
5411
-
5412
- /**
5413
- * Get/parse shorthandsize strings from php.ini as bytes.
5414
- *
5415
- * Parse strings like "1k" into bytes (1024).
5416
- *
5417
- * @package WebPConvert
5418
- * @author Bjørn Rosell <it@rosell.dk>
5419
- * @since Class available since Release 2.0.0
5420
- */
5421
- class PhpIniSizes
5422
- {
5423
-
5424
- /**
5425
- * Parse a shordhandsize string as the ones returned by ini_get()
5426
- *
5427
- * Parse a shorthandsize string having the syntax allowed in php.ini and returned by ini_get().
5428
- * Ie "1K" => 1024.
5429
- * Strings without units are also accepted.
5430
- * The shorthandbytes syntax is described here: https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
5431
- *
5432
- * @param string $shortHandSize A size string of the type returned by ini_get()
5433
- * @return float|false The parsed size (beware: it is float, do not check high numbers for equality),
5434
- * or false if parse error
5435
- */
5436
- public static function parseShortHandSize($shortHandSize)
5437
- {
5438
-
5439
- $result = preg_match("#^\\s*(\\d+(?:\\.\\d+)?)([bkmgtpezy]?)\\s*$#i", $shortHandSize, $matches);
5440
- if ($result !== 1) {
5441
- return false;
5442
- }
5443
-
5444
- // Truncate, because that is what php does.
5445
- $digitsValue = floor($matches[1]);
5446
-
5447
- if ((count($matches) >= 3) && ($matches[2] != '')) {
5448
- $unit = $matches[2];
5449
-
5450
- // Find the position of the unit in the ordered string which is the power
5451
- // of magnitude to multiply a kilobyte by.
5452
- $position = stripos('bkmgtpezy', $unit);
5453
-
5454
- return floatval($digitsValue * pow(1024, $position));
5455
- } else {
5456
- return $digitsValue;
5457
- }
5458
- }
5459
-
5460
- /*
5461
- * Get the size of an php.ini option.
5462
- *
5463
- * Calls ini_get() and parses the size to a number.
5464
- * If the configuration option is null, does not exist, or cannot be parsed as a shorthandsize, false is returned
5465
- *
5466
- * @param string $varname The configuration option name.
5467
- * @return float|false The parsed size or false if the configuration option does not exist
5468
- */
5469
- public static function getIniBytes($iniVarName)
5470
- {
5471
- $iniVarValue = ini_get($iniVarName);
5472
- if (($iniVarValue == '') || $iniVarValue === false) {
5473
- return false;
5474
- }
5475
- return self::parseShortHandSize($iniVarValue);
5476
- }
5477
- }
5478
-
5479
- ?><?php
5480
-
5481
- namespace WebPConvert\Convert;
5482
-
5483
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
5484
- use WebPConvert\Convert\Converters\AbstractConverter;
5485
-
5486
- /**
5487
- * Make converters from their ids.
5488
- *
5489
- * @package WebPConvert
5490
- * @author Bjørn Rosell <it@rosell.dk>
5491
- * @since Class available since Release 2.0.0
5492
- */
5493
- class ConverterFactory
5494
- {
5495
- /**
5496
- * Get classname of a converter (by id)
5497
- *
5498
- * @param string $converterId Id of converter (ie "cwebp")
5499
- *
5500
- * @throws ConverterNotFoundException If there is no converter with that id.
5501
- * @return string Fully qualified class name of converter
5502
- */
5503
- public static function converterIdToClassname($converterId)
5504
- {
5505
- switch ($converterId) {
5506
- case 'imagickbinary':
5507
- $classNameShort = 'ImagickBinary';
5508
- break;
5509
- case 'imagemagick':
5510
- $classNameShort = 'ImageMagick';
5511
- break;
5512
- case 'gmagickbinary':
5513
- $classNameShort = 'GmagickBinary';
5514
- break;
5515
- case 'graphicsmagick':
5516
- $classNameShort = 'GraphicsMagick';
5517
- break;
5518
- default:
5519
- $classNameShort = ucfirst($converterId);
5520
- }
5521
- $className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
5522
- if (is_callable([$className, 'convert'])) {
5523
- return $className;
5524
- } else {
5525
- throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
5526
- }
5527
- }
5528
-
5529
- /**
5530
- * Make a converter instance by class name.
5531
- *
5532
- * @param string $converterClassName Fully qualified class name
5533
- * @param string $source The path to the file to convert
5534
- * @param string $destination The path to save the converted file to
5535
- * @param array $options (optional)
5536
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
5537
- *
5538
- * @throws ConverterNotFoundException If the specified converter class isn't found
5539
- * @return AbstractConverter An instance of the specified converter
5540
- */
5541
- public static function makeConverterFromClassname(
5542
- $converterClassName,
5543
- $source,
5544
- $destination,
5545
- $options = [],
5546
- $logger = null
5547
- ) {
5548
- if (!is_callable([$converterClassName, 'convert'])) {
5549
- throw new ConverterNotFoundException(
5550
- 'There is no converter with class name:' . $converterClassName . ' (or it is not a converter)'
5551
- );
5552
- }
5553
- //$converter = new $converterClassName($source, $destination, $options, $logger);
5554
-
5555
- return call_user_func(
5556
- [$converterClassName, 'createInstance'],
5557
- $source,
5558
- $destination,
5559
- $options,
5560
- $logger
5561
- );
5562
- }
5563
-
5564
- /**
5565
- * Make a converter instance by either id or class name.
5566
- *
5567
- * @param string $converterIdOrClassName Either a converter ID or a fully qualified class name
5568
- * @param string $source The path to the file to convert
5569
- * @param string $destination The path to save the converted file to
5570
- * @param array $options (optional)
5571
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
5572
- *
5573
- * @throws ConverterNotFoundException If the specified converter class isn't found
5574
- * @return AbstractConverter An instance of the specified converter
5575
- */
5576
- public static function makeConverter($converterIdOrClassName, $source, $destination, $options = [], $logger = null)
5577
- {
5578
- // We take it that all lowercase means it is an id rather than a class name
5579
- if (strtolower($converterIdOrClassName) == $converterIdOrClassName) {
5580
- $converterClassName = self::converterIdToClassname($converterIdOrClassName);
5581
- } else {
5582
- $converterClassName = $converterIdOrClassName;
5583
- }
5584
-
5585
- return self::makeConverterFromClassname($converterClassName, $source, $destination, $options, $logger);
5586
- }
5587
- }
5588
-
5589
- ?><?php
5590
-
5591
- namespace WebPConvert\Exceptions;
5592
-
5593
- use WebPConvert\Exceptions\WebPConvertException;
5594
-
5595
- class InvalidInputException extends WebPConvertException
5596
- {
5597
- public $description = 'Invalid input';
5598
- }
5599
-
5600
- ?><?php
5601
-
5602
- namespace WebPConvert\Exceptions\InvalidInput;
5603
-
5604
- use WebPConvert\Exceptions\InvalidInputException;
5605
-
5606
- class InvalidImageTypeException extends InvalidInputException
5607
- {
5608
- public $description = 'The converter does not handle the supplied image type';
5609
- }
5610
-
5611
- ?><?php
5612
-
5613
- namespace WebPConvert\Exceptions\InvalidInput;
5614
-
5615
- use WebPConvert\Exceptions\InvalidInputException;
5616
-
5617
- class TargetNotFoundException extends InvalidInputException
5618
- {
5619
- public $description = 'The converter could not locate source file';
5620
- }
5621
-
5622
- ?><?php
5623
-
5624
- namespace WebPConvert\Helpers;
5625
-
5626
- use WebPConvert\Helpers\MimeType;
5627
- use WebPConvert\Helpers\PathChecker;
5628
- use WebPConvert\Exceptions\InvalidInputException;
5629
- use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
5630
-
5631
- /**
5632
- * Functions for sanitizing.
5633
- *
5634
- * @package WebPConvert
5635
- * @author Bjørn Rosell <it@rosell.dk>
5636
- * @since Class available since Release 2.0.6
5637
- */
5638
- class InputValidator
5639
- {
5640
-
5641
- private static $allowedMimeTypes = [
5642
- 'image/jpeg',
5643
- 'image/png'
5644
- ];
5645
-
5646
- /**
5647
- * Check mimetype and if file path is ok and exists
5648
- */
5649
- public static function checkMimeType($filePath, $allowedMimeTypes = null)
5650
- {
5651
- if (is_null($allowedMimeTypes)) {
5652
- $allowedMimeTypes = self::$allowedMimeTypes;
5653
- }
5654
- // the following also tests that file path is ok and file exists
5655
- $fileMimeType = MimeType::getMimeTypeDetectionResult($filePath);
5656
-
5657
- if (is_null($fileMimeType)) {
5658
- throw new InvalidImageTypeException('Image type could not be detected');
5659
- } elseif ($fileMimeType === false) {
5660
- throw new InvalidImageTypeException('File seems not to be an image.');
5661
- } elseif (!in_array($fileMimeType, $allowedMimeTypes)) {
5662
- throw new InvalidImageTypeException('Unsupported mime type: ' . $fileMimeType);
5663
- }
5664
- }
5665
-
5666
- public static function checkSource($source)
5667
- {
5668
- PathChecker::checkSourcePath($source);
5669
- self::checkMimeType($source);
5670
- }
5671
-
5672
- public static function checkDestination($destination)
5673
- {
5674
- PathChecker::checkDestinationPath($destination);
5675
- }
5676
-
5677
- public static function checkSourceAndDestination($source, $destination)
5678
- {
5679
- self::checkSource($source);
5680
- self::checkDestination($destination);
5681
- }
5682
- }
5683
-
5684
- ?><?php
5685
-
5686
- namespace WebPConvert\Helpers;
5687
-
5688
- use ImageMimeTypeGuesser\ImageMimeTypeGuesser;
5689
-
5690
- use WebPConvert\Exceptions\InvalidInputException;
5691
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
5692
-
5693
- /**
5694
- * Get MimeType, results cached by path.
5695
- *
5696
- * @package WebPConvert
5697
- * @author Bjørn Rosell <it@rosell.dk>
5698
- * @since Class available since Release 2.0.6
5699
- */
5700
- class MimeType
5701
- {
5702
- private static $cachedDetections = [];
5703
-
5704
- /**
5705
- * Get mime type for image (best guess).
5706
- *
5707
- * It falls back to using file extension. If that fails too, false is returned
5708
- *
5709
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
5710
- * false (if it is not an image type that the server knowns about)
5711
- * or null (if nothing can be determined)
5712
- */
5713
- public static function getMimeTypeDetectionResult($absFilePath)
5714
- {
5715
- PathChecker::checkAbsolutePathAndExists($absFilePath);
5716
-
5717
- if (isset(self::$cachedDetections[$absFilePath])) {
5718
- return self::$cachedDetections[$absFilePath];
5719
- }
5720
- $cachedDetections[$absFilePath] = ImageMimeTypeGuesser::lenientGuess($absFilePath);
5721
- return $cachedDetections[$absFilePath];
5722
- }
5723
- }
5724
-
5725
- ?><?php
5726
-
5727
- namespace WebPConvert\Helpers;
5728
-
5729
- use WebPConvert\Exceptions\InvalidInputException;
5730
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
5731
-
5732
- /**
5733
- * Functions for sanitizing.
5734
- *
5735
- * @package WebPConvert
5736
- * @author Bjørn Rosell <it@rosell.dk>
5737
- * @since Class available since Release 2.0.6
5738
- */
5739
- class PathChecker
5740
- {
5741
-
5742
- /**
5743
- * Check absolute file path to prevent attacks.
5744
- *
5745
- * - Prevents non printable characters
5746
- * - Prevents stream wrappers
5747
- * - Prevents directory traversal
5748
- *
5749
- * Preventing non printable characters is especially done to prevent the NUL character, which can be used
5750
- * to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
5751
- *
5752
- * Preventeng stream wrappers is especially done to protect against Phar Deserialization.
5753
- * See https://blog.ripstech.com/2018/new-php-exploitation-technique/
5754
- *
5755
- * @param string $absFilePath
5756
- * @return string sanitized file path
5757
- */
5758
- public static function checkAbsolutePath($absFilePath, $text = 'file')
5759
- {
5760
- if (empty($absFilePath)) {
5761
- throw new InvalidInputException('Empty filepath for ' . $text);
5762
- }
5763
-
5764
- // Prevent non printable characters
5765
- if (!ctype_print($absFilePath)) {
5766
- throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
5767
- }
5768
-
5769
- // Prevent directory traversal
5770
- if (preg_match('#\.\.\/#', $absFilePath)) {
5771
- throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path');
5772
- }
5773
-
5774
- // Prevent stream wrappers ("phar://", "php://" and the like)
5775
- // https://www.php.net/manual/en/wrappers.phar.php
5776
- if (preg_match('#^\\w+://#', $absFilePath)) {
5777
- throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path');
5778
- }
5779
- }
5780
-
5781
- public static function checkAbsolutePathAndExists($absFilePath, $text = 'file')
5782
- {
5783
- if (empty($absFilePath)) {
5784
- throw new TargetNotFoundException($text . ' argument missing');
5785
- }
5786
- self::checkAbsolutePath($absFilePath, $text);
5787
- if (@!file_exists($absFilePath)) {
5788
- throw new TargetNotFoundException($text . ' file was not found');
5789
- }
5790
- if (@is_dir($absFilePath)) {
5791
- throw new InvalidInputException($text . ' is a directory');
5792
- }
5793
- }
5794
-
5795
- /**
5796
- * Checks that source path is secure, file exists and it is not a dir.
5797
- *
5798
- * To also check mime type, use InputValidator::checkSource
5799
- */
5800
- public static function checkSourcePath($source)
5801
- {
5802
- self::checkAbsolutePathAndExists($source, 'source');
5803
- }
5804
-
5805
- public static function checkDestinationPath($destination)
5806
- {
5807
- if (empty($destination)) {
5808
- throw new InvalidInputException('Destination argument missing');
5809
- }
5810
- self::checkAbsolutePath($destination, 'destination');
5811
- if (@is_dir($destination)) {
5812
- throw new InvalidInputException('Destination is a directory');
5813
- }
5814
- }
5815
-
5816
- public static function checkSourceAndDestinationPaths($source, $destination)
5817
- {
5818
- self::checkSourcePath($source);
5819
- self::checkDestinationPath($destination);
5820
- }
5821
- }
5822
-
5823
- ?><?php
5824
-
5825
- namespace WebPConvert\Loggers;
5826
-
5827
- /**
5828
- * Base for all logger classes.
5829
- *
5830
- * WebPConvert can provide insights into the conversion process by means of accepting a logger which
5831
- * extends this class.
5832
- *
5833
- * @package WebPConvert
5834
- * @author Bjørn Rosell <it@rosell.dk>
5835
- * @since Class available since Release 2.0.0
5836
- */
5837
- abstract class BaseLogger
5838
- {
5839
- /**
5840
- * Write a message to the log
5841
- *
5842
- * @param string $msg message to log
5843
- * @param string $style style (null | bold | italic)
5844
- * @return void
5845
- */
5846
- abstract public function log($msg, $style = '');
5847
-
5848
- /**
5849
- * Add new line to the log
5850
- * @return void
5851
- */
5852
- abstract public function ln();
5853
-
5854
- /**
5855
- * Write a line to the log
5856
- *
5857
- * @param string $msg message to log
5858
- * @param string $style style (null | bold | italic)
5859
- * @return void
5860
- */
5861
- public function logLn($msg, $style = '')
5862
- {
5863
- $this->log($msg, $style);
5864
- $this->ln();
5865
- }
5866
- }
5867
-
5868
- ?><?php
5869
-
5870
- namespace WebPConvert\Loggers;
5871
-
5872
- use WebPConvert\Loggers\BaseLogger;
5873
-
5874
- /**
5875
- * Collect the logging and retrieve it later in HTML or plain text format.
5876
- *
5877
- * @package WebPConvert
5878
- * @author Bjørn Rosell <it@rosell.dk>
5879
- * @since Class available since Release 2.0.0
5880
- */
5881
- class BufferLogger extends BaseLogger
5882
- {
5883
- public $entries = array();
5884
-
5885
- /**
5886
- * Write a message to the buffer - all entries can later be retrieved with getText() or getHtlm().
5887
- *
5888
- * @param string $msg message to log
5889
- * @param string $style style (null | bold | italic)
5890
- * @return void
5891
- */
5892
- public function log($msg, $style = '')
5893
- {
5894
- $this->entries[] = [$msg, $style];
5895
- }
5896
-
5897
- /**
5898
- * Write a new line to the buffer.
5899
- *
5900
- * @return void
5901
- */
5902
- public function ln()
5903
- {
5904
- $this->entries[] = '';
5905
- }
5906
-
5907
- /**
5908
- * Get everything logged - as HTML.
5909
- *
5910
- * @return string The log, formatted as HTML.
5911
- */
5912
- public function getHtml()
5913
- {
5914
- $html = '';
5915
- foreach ($this->entries as $entry) {
5916
- if ($entry == '') {
5917
- $html .= '<br>';
5918
- } else {
5919
- list($msg, $style) = $entry;
5920
- $msg = htmlspecialchars($msg);
5921
- if ($style == 'bold') {
5922
- $html .= '<b>' . $msg . '</b>';
5923
- } elseif ($style == 'italic') {
5924
- $html .= '<i>' . $msg . '</i>';
5925
- } else {
5926
- $html .= $msg;
5927
- }
5928
- }
5929
- }
5930
- return $html;
5931
- }
5932
-
5933
- /**
5934
- * Get everything logged - as markdown.
5935
- *
5936
- * @return string The log, formatted as MarkDown.
5937
- */
5938
- public function getMarkDown($newLineChar = "\n\r")
5939
- {
5940
- $md = '';
5941
- foreach ($this->entries as $entry) {
5942
- if ($entry == '') {
5943
- $md .= $newLineChar;
5944
- } else {
5945
- list($msg, $style) = $entry;
5946
- if ($style == 'bold') {
5947
- $md .= '**' . $msg . '** ';
5948
- } elseif ($style == 'italic') {
5949
- $md .= '*' . $msg . '* ';
5950
- } else {
5951
- $md .= $msg;
5952
- }
5953
- }
5954
- }
5955
- return $md;
5956
- }
5957
-
5958
- /**
5959
- * Get everything logged - as plain text.
5960
- *
5961
- * @param string $newLineChar. The character used for new lines.
5962
- * @return string The log, formatted as plain text.
5963
- */
5964
- public function getText($newLineChar = ' ')
5965
- {
5966
- $text = '';
5967
- foreach ($this->entries as $entry) {
5968
- if ($entry == '') { // empty string means new line
5969
- if (substr($text, -2) != '.' . $newLineChar) {
5970
- $text .= '.' . $newLineChar;
5971
- }
5972
- } else {
5973
- list($msg, $style) = $entry;
5974
- $text .= $msg;
5975
- }
5976
- }
5977
-
5978
- return $text;
5979
- }
5980
- }
5981
-
5982
- ?><?php
5983
-
5984
- namespace WebPConvert\Loggers;
5985
-
5986
- /**
5987
- * Echo the logs immediately (in HTML)
5988
- *
5989
- * @package WebPConvert
5990
- * @author Bjørn Rosell <it@rosell.dk>
5991
- * @since Class available since Release 2.0.0
5992
- */
5993
- class EchoLogger extends BaseLogger
5994
- {
5995
-
5996
- /**
5997
- * Handle log() by echoing the message.
5998
- *
5999
- * @param string $msg message to log
6000
- * @param string $style style (null | bold | italic)
6001
- * @return void
6002
- */
6003
- public function log($msg, $style = '')
6004
- {
6005
- $msg = htmlspecialchars($msg);
6006
- if ($style == 'bold') {
6007
- echo '<b>' . $msg . '</b>';
6008
- } elseif ($style == 'italic') {
6009
- echo '<i>' . $msg . '</i>';
6010
- } else {
6011
- echo $msg;
6012
- }
6013
- }
6014
-
6015
- /**
6016
- * Handle ln by echoing a <br> tag.
6017
- *
6018
- * @return void
6019
- */
6020
- public function ln()
6021
- {
6022
- echo '<br>';
6023
- }
6024
- }
6025
-
6026
- ?><?php
6027
- namespace WebPConvert\Serve;
6028
-
6029
- /**
6030
- * Add / Set HTTP header.
6031
- *
6032
- * This class does nothing more than adding two convenience functions for calling the "header" function.
6033
- *
6034
- * @package WebPConvert
6035
- * @author Bjørn Rosell <it@rosell.dk>
6036
- * @since Class available since Release 2.0.0
6037
- */
6038
- class Header
6039
- {
6040
- /**
6041
- * Convenience function for adding header (append).
6042
- *
6043
- * @param string $header The header to add.
6044
- * @return void
6045
- */
6046
- public static function addHeader($header)
6047
- {
6048
- header($header, false);
6049
- }
6050
-
6051
- /**
6052
- * Convenience function for replacing header.
6053
- *
6054
- * @param string $header The header to set.
6055
- * @return void
6056
- */
6057
- public static function setHeader($header)
6058
- {
6059
- header($header, true);
6060
- }
6061
-
6062
- /**
6063
- * Add log header and optionally send it to a logger as well.
6064
- *
6065
- * @param string $msg Message to add to "X-WebP-Convert-Log" header
6066
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
6067
- * @return void
6068
- */
6069
- public static function addLogHeader($msg, $logger = null)
6070
- {
6071
- self::addHeader('X-WebP-Convert-Log: ' . $msg);
6072
- if (!is_null($logger)) {
6073
- $logger->logLn($msg);
6074
- }
6075
- }
6076
- }
6077
-
6078
- ?><?php
6079
- namespace WebPConvert\Serve;
6080
-
6081
- use WebPConvert\Helpers\InputValidator;
6082
- use WebPConvert\Loggers\EchoLogger;
6083
- use WebPConvert\WebPConvert;
6084
-
6085
- /**
6086
- * Class for generating a HTML report of converting an image.
6087
- *
6088
- * @package WebPConvert
6089
- * @author Bjørn Rosell <it@rosell.dk>
6090
- * @since Class available since Release 2.0.0
6091
- */
6092
- class Report
6093
- {
6094
- public static function convertAndReport($source, $destination, $options)
6095
- {
6096
- InputValidator::checkSourceAndDestination($source, $destination);
6097
- ?>
6098
- <html>
6099
- <head>
6100
- <style>td {vertical-align: top} table {color: #666}</style>
6101
- <script>
6102
- function showOptions(elToHide) {
6103
- document.getElementById('options').style.display='block';
6104
- elToHide.style.display='none';
6105
- }
6106
- </script>
6107
- </head>
6108
- <body>
6109
- <table>
6110
- <tr><td><i>source:</i></td><td><?php echo htmlentities($source) ?></td></tr>
6111
- <tr><td><i>destination:</i></td><td><?php echo htmlentities($destination) ?><td></tr>
6112
- </table>
6113
- <br>
6114
- <?php
6115
- try {
6116
- $echoLogger = new EchoLogger();
6117
- $options['log-call-arguments'] = true;
6118
- WebPConvert::convert($source, $destination, $options, $echoLogger);
6119
- } catch (\Exception $e) {
6120
- $msg = $e->getMessage();
6121
- echo '<b>' . $msg . '</b>';
6122
-
6123
- //echo '<p>Rethrowing exception for your convenience</p>';
6124
- //throw ($e);
6125
- }
6126
- ?>
6127
- </body>
6128
- </html>
6129
- <?php
6130
- }
6131
- }
6132
-
6133
- ?><?php
6134
- namespace WebPConvert\Serve;
6135
-
6136
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
6137
- use WebPConvert\Helpers\InputValidator;
6138
- use WebPConvert\Helpers\MimeType;
6139
- use WebPConvert\Serve\Exceptions\ServeFailedException;
6140
- use WebPConvert\Serve\Header;
6141
- use WebPConvert\Serve\Report;
6142
- use WebPConvert\Serve\ServeFile;
6143
- use WebPConvert\Options\ArrayOption;
6144
- use WebPConvert\Options\BooleanOption;
6145
- use WebPConvert\Options\Options;
6146
- use WebPConvert\Options\SensitiveArrayOption;
6147
- use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
6148
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
6149
- use WebPConvert\WebPConvert;
6150
-
6151
- /**
6152
- * Serve a converted webp image.
6153
- *
6154
- * The webp that is served might end up being one of these:
6155
- * - a fresh convertion
6156
- * - the destionation
6157
- * - the original
6158
- *
6159
- * Exactly which is a decision based upon options, file sizes and file modification dates
6160
- * (see the serve method of this class for details)
6161
- *
6162
- * @package WebPConvert
6163
- * @author Bjørn Rosell <it@rosell.dk>
6164
- * @since Class available since Release 2.0.0
6165
- */
6166
- class ServeConvertedWebP
6167
- {
6168
-
6169
- /**
6170
- * Process options.
6171
- *
6172
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
6173
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
6174
- * @param array $options
6175
- */
6176
- private static function processOptions($options)
6177
- {
6178
- $options2 = new Options();
6179
- $options2->addOptions(
6180
- new BooleanOption('reconvert', false),
6181
- new BooleanOption('serve-original', false),
6182
- new BooleanOption('show-report', false),
6183
- new BooleanOption('suppress-warnings', true),
6184
- new ArrayOption('serve-image', []),
6185
- new SensitiveArrayOption('convert', [])
6186
- );
6187
- foreach ($options as $optionId => $optionValue) {
6188
- $options2->setOrCreateOption($optionId, $optionValue);
6189
- }
6190
- $options2->check();
6191
- return $options2->getOptions();
6192
- }
6193
-
6194
- /**
6195
- * Serve original file (source).
6196
- *
6197
- * @param string $source path to source file
6198
- * @param array $serveImageOptions (optional) options for serving an image
6199
- * Supported options:
6200
- * - All options supported by ServeFile::serve()
6201
- * @throws ServeFailedException if source is not an image or mime type cannot be determined
6202
- * @return void
6203
- */
6204
- public static function serveOriginal($source, $serveImageOptions = [])
6205
- {
6206
- InputValidator::checkSource($source);
6207
- $contentType = MimeType::getMimeTypeDetectionResult($source);
6208
- if (is_null($contentType)) {
6209
- throw new ServeFailedException('Rejecting to serve original (mime type cannot be determined)');
6210
- } elseif ($contentType === false) {
6211
- throw new ServeFailedException('Rejecting to serve original (it is not an image)');
6212
- } else {
6213
- ServeFile::serve($source, $contentType, $serveImageOptions);
6214
- }
6215
- }
6216
-
6217
- /**
6218
- * Serve destination file.
6219
- *
6220
- * TODO: SHould this really be public?
6221
- *
6222
- * @param string $destination path to destination file
6223
- * @param array $serveImageOptions (optional) options for serving (such as which headers to add)
6224
- * Supported options:
6225
- * - All options supported by ServeFile::serve()
6226
- * @return void
6227
- */
6228
- public static function serveDestination($destination, $serveImageOptions = [])
6229
- {
6230
- InputValidator::checkDestination($destination);
6231
- ServeFile::serve($destination, 'image/webp', $serveImageOptions);
6232
- }
6233
-
6234
-
6235
- public static function warningHandler()
6236
- {
6237
- // do nothing! - as we do not return anything, the warning is suppressed
6238
- }
6239
-
6240
- /**
6241
- * Serve converted webp.
6242
- *
6243
- * Serve a converted webp. If a file already exists at the destination, that is served (unless it is
6244
- * older than the source - in that case a fresh conversion will be made, or the file at the destination
6245
- * is larger than the source - in that case the source is served). Some options may alter this logic.
6246
- * In case no file exists at the destination, a fresh conversion is made and served.
6247
- *
6248
- * @param string $source path to source file
6249
- * @param string $destination path to destination
6250
- * @param array $options (optional) options for serving/converting
6251
- * Supported options:
6252
- * 'show-report' => (boolean) If true, the decision will always be 'report'
6253
- * 'serve-original' => (boolean) If true, the decision will be 'source' (unless above option is set)
6254
- * 'reconvert ' => (boolean) If true, the decision will be 'fresh-conversion' (unless one of the
6255
- * above options is set)
6256
- * - All options supported by WebPConvert::convert()
6257
- * - All options supported by ServeFile::serve()
6258
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
6259
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
6260
- *
6261
- * @throws \WebPConvert\Exceptions\WebPConvertException If something went wrong.
6262
- * @return void
6263
- */
6264
- public static function serve($source, $destination, $options = [], $serveLogger = null, $convertLogger = null)
6265
- {
6266
- InputValidator::checkSourceAndDestination($source, $destination);
6267
-
6268
- $options = self::processOptions($options);
6269
-
6270
- if ($options['suppress-warnings']) {
6271
- set_error_handler(
6272
- array('\\WebPConvert\\Serve\\ServeConvertedWebP', "warningHandler"),
6273
- E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
6274
- );
6275
- }
6276
-
6277
-
6278
- //$options = array_merge(self::$defaultOptions, $options);
6279
-
6280
- // Step 1: Is there a file at the destination? If not, trigger conversion
6281
- // However 1: if "show-report" option is set, serve the report instead
6282
- // However 2: "reconvert" option should also trigger conversion
6283
- if ($options['show-report']) {
6284
- Header::addLogHeader('Showing report', $serveLogger);
6285
- Report::convertAndReport($source, $destination, $options);
6286
- return;
6287
- }
6288
-
6289
- if (!@file_exists($destination)) {
6290
- Header::addLogHeader('Converting (there were no file at destination)', $serveLogger);
6291
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
6292
- } elseif ($options['reconvert']) {
6293
- Header::addLogHeader('Converting (told to reconvert)', $serveLogger);
6294
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
6295
- } else {
6296
- // Step 2: Is the destination older than the source?
6297
- // If yes, trigger conversion (deleting destination is implicit)
6298
- $timestampSource = @filemtime($source);
6299
- $timestampDestination = @filemtime($destination);
6300
- if (($timestampSource !== false) &&
6301
- ($timestampDestination !== false) &&
6302
- ($timestampSource > $timestampDestination)) {
6303
- Header::addLogHeader('Converting (destination was older than the source)', $serveLogger);
6304
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
6305
- }
6306
- }
6307
-
6308
- // Step 3: Serve the smallest file (destination or source)
6309
- // However, first check if 'serve-original' is set
6310
- if ($options['serve-original']) {
6311
- Header::addLogHeader('Serving original (told to)', $serveLogger);
6312
- self::serveOriginal($source, $options['serve-image']);
6313
- }
6314
-
6315
- $filesizeDestination = @filesize($destination);
6316
- $filesizeSource = @filesize($source);
6317
- if (($filesizeSource !== false) &&
6318
- ($filesizeDestination !== false) &&
6319
- ($filesizeDestination > $filesizeSource)) {
6320
- Header::addLogHeader('Serving original (it is smaller)', $serveLogger);
6321
- self::serveOriginal($source, $options['serve-image']);
6322
- }
6323
-
6324
- Header::addLogHeader('Serving converted file', $serveLogger);
6325
- self::serveDestination($destination, $options['serve-image']);
6326
- }
6327
- }
6328
-
6329
- ?><?php
6330
- namespace WebPConvert\Serve;
6331
-
6332
- use WebPConvert\Helpers\InputValidator;
6333
- use WebPConvert\Options\Options;
6334
- use WebPConvert\Options\StringOption;
6335
- use WebPConvert\Serve\Header;
6336
- use WebPConvert\Serve\Report;
6337
- use WebPConvert\Serve\ServeConvertedWeb;
6338
- use WebPConvert\Serve\Exceptions\ServeFailedException;
6339
- use WebPConvert\Exceptions\WebPConvertException;
6340
-
6341
- /**
6342
- * Serve a converted webp image and handle errors.
6343
- *
6344
- * @package WebPConvert
6345
- * @author Bjørn Rosell <it@rosell.dk>
6346
- * @since Class available since Release 2.0.0
6347
- */
6348
- class ServeConvertedWebPWithErrorHandling
6349
- {
6350
-
6351
- /**
6352
- * Process options.
6353
- *
6354
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
6355
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
6356
- * @param array $options
6357
- */
6358
- private static function processOptions($options)
6359
- {
6360
- $options2 = new Options();
6361
- $options2->addOptions(
6362
- new StringOption('fail', 'original', ['original', '404', 'throw', 'report']),
6363
- new StringOption('fail-when-fail-fails', 'throw', ['original', '404', 'throw', 'report'])
6364
- );
6365
- foreach ($options as $optionId => $optionValue) {
6366
- $options2->setOrCreateOption($optionId, $optionValue);
6367
- }
6368
- $options2->check();
6369
- return $options2->getOptions();
6370
- }
6371
-
6372
- /**
6373
- * Add headers for preventing caching.
6374
- *
6375
- * @return void
6376
- */
6377
- private static function addHeadersPreventingCaching()
6378
- {
6379
- Header::setHeader("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
6380
- Header::addHeader("Cache-Control: post-check=0, pre-check=0");
6381
- Header::setHeader("Pragma: no-cache");
6382
- }
6383
-
6384
- /**
6385
- * Perform fail action.
6386
- *
6387
- * @param string $fail Action to perform (original | 404 | report)
6388
- * @param string $failIfFailFails Action to perform if $fail action fails
6389
- * @param string $source path to source file
6390
- * @param string $destination path to destination
6391
- * @param array $options (optional) options for serving/converting
6392
- * @param \Exception $e exception that was thrown when trying to serve
6393
- * @param string $serveClass (optional) Full class name to a class that has a serveOriginal() method
6394
- * @return void
6395
- */
6396
- public static function performFailAction($fail, $failIfFailFails, $source, $destination, $options, $e, $serveClass)
6397
- {
6398
- self::addHeadersPreventingCaching();
6399
-
6400
- Header::addLogHeader('Performing fail action: ' . $fail);
6401
-
6402
- switch ($fail) {
6403
- case 'original':
6404
- try {
6405
- //ServeConvertedWebP::serveOriginal($source, $options);
6406
- call_user_func($serveClass . '::serveOriginal', $source, $options);
6407
- } catch (\Exception $e) {
6408
- self::performFailAction($failIfFailFails, '404', $source, $destination, $options, $e, $serveClass);
6409
- }
6410
- break;
6411
-
6412
- case '404':
6413
- $protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
6414
- Header::setHeader($protocol . " 404 Not Found");
6415
- break;
6416
-
6417
- case 'report':
6418
- $options['show-report'] = true;
6419
- Report::convertAndReport($source, $destination, $options);
6420
- break;
6421
-
6422
- case 'throw':
6423
- throw $e;
6424
- break;
6425
-
6426
- case 'report-as-image':
6427
- // TODO: Implement or discard ?
6428
- break;
6429
- }
6430
- }
6431
-
6432
- /**
6433
- * Serve webp image and handle errors as specified in the 'fail' option.
6434
- *
6435
- * This method basically wraps ServeConvertedWebP:serve in order to provide exception handling.
6436
- * The error handling is set with the 'fail' option and can be either '404', 'original' or 'report'.
6437
- * If set to '404', errors results in 404 Not Found headers being issued. If set to 'original', an
6438
- * error results in the original being served.
6439
- * Look up the ServeConvertedWebP:serve method to learn more.
6440
- *
6441
- * @param string $source path to source file
6442
- * @param string $destination path to destination
6443
- * @param array $options (optional) options for serving/converting
6444
- * Supported options:
6445
- * - 'fail' => (string) Action to take on failure (404 | original | report | throw).
6446
- * "404" or "throw" is recommended for development and "original" is recommended for production.
6447
- * Default: 'original'.
6448
- * - 'fail-when-fail-fails' => (string) Action to take if fail action also fails. Default: '404'.
6449
- * - All options supported by WebPConvert::convert()
6450
- * - All options supported by ServeFile::serve()
6451
- * - All options supported by DecideWhatToServe::decide)
6452
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
6453
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
6454
- * @param string $serveClass (optional) Full class name to a class that has a serve() method and a
6455
- * serveOriginal() method
6456
- * @return void
6457
- */
6458
- public static function serve(
6459
- $source,
6460
- $destination,
6461
- $options = [],
6462
- $serveLogger = null,
6463
- $convertLogger = null,
6464
- $serveClass = '\\WebPConvert\\Serve\\ServeConvertedWebP'
6465
- ) {
6466
-
6467
- $options = self::processOptions($options);
6468
- try {
6469
- InputValidator::checkSourceAndDestination($source, $destination);
6470
- //ServeConvertedWebP::serve($source, $destination, $options, $serveLogger);
6471
- call_user_func($serveClass . '::serve', $source, $destination, $options, $serveLogger, $convertLogger);
6472
- } catch (\Exception $e) {
6473
- if ($e instanceof \WebPConvert\Exceptions\WebPConvertException) {
6474
- Header::addLogHeader($e->getShortMessage(), $serveLogger);
6475
- }
6476
-
6477
- self::performFailAction(
6478
- $options['fail'],
6479
- $options['fail-when-fail-fails'],
6480
- $source,
6481
- $destination,
6482
- $options,
6483
- $e,
6484
- $serveClass
6485
- );
6486
- }
6487
- }
6488
- }
6489
-
6490
- ?><?php
6491
- namespace WebPConvert\Serve;
6492
-
6493
- //use WebPConvert\Serve\Report;
6494
- use WebPConvert\Helpers\InputValidator;
6495
- use WebPConvert\Options\ArrayOption;
6496
- use WebPConvert\Options\BooleanOption;
6497
- use WebPConvert\Options\Options;
6498
- use WebPConvert\Options\StringOption;
6499
- use WebPConvert\Serve\Header;
6500
- use WebPConvert\Serve\Exceptions\ServeFailedException;
6501
-
6502
- /**
6503
- * Serve a file (send to standard output)
6504
- *
6505
- * @package WebPConvert
6506
- * @author Bjørn Rosell <it@rosell.dk>
6507
- * @since Class available since Release 2.0.0
6508
- */
6509
- class ServeFile
6510
- {
6511
-
6512
- /**
6513
- * Process options.
6514
- *
6515
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
6516
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
6517
- * @param array $options
6518
- */
6519
- private static function processOptions($options)
6520
- {
6521
- $options2 = new Options();
6522
- $options2->addOptions(
6523
- new ArrayOption('headers', []),
6524
- new StringOption('cache-control-header', 'public, max-age=31536000')
6525
- );
6526
- foreach ($options as $optionId => $optionValue) {
6527
- $options2->setOrCreateOption($optionId, $optionValue);
6528
- }
6529
- $options2->check();
6530
- $options = $options2->getOptions();
6531
-
6532
- // headers option
6533
- // --------------
6534
-
6535
- $headerOptions = new Options();
6536
- $headerOptions->addOptions(
6537
- new BooleanOption('cache-control', false),
6538
- new BooleanOption('content-length', true),
6539
- new BooleanOption('content-type', true),
6540
- new BooleanOption('expires', false),
6541
- new BooleanOption('last-modified', true),
6542
- new BooleanOption('vary-accept', false)
6543
- );
6544
- foreach ($options['headers'] as $optionId => $optionValue) {
6545
- $headerOptions->setOrCreateOption($optionId, $optionValue);
6546
- }
6547
- $options['headers'] = $headerOptions->getOptions();
6548
- return $options;
6549
- }
6550
-
6551
- /**
6552
- * Serve existing file.
6553
- *
6554
- * @param string $filename File to serve (absolute path)
6555
- * @param string $contentType Content-type (used to set header).
6556
- * Only used when the "set-content-type-header" option is set.
6557
- * Set to ie "image/jpeg" for serving jpeg file.
6558
- * @param array $options Array of named options (optional).
6559
- * Supported options:
6560
- * 'add-vary-accept-header' => (boolean) Whether to add *Vary: Accept* header or not. Default: true.
6561
- * 'set-content-type-header' => (boolean) Whether to set *Content-Type* header or not. Default: true.
6562
- * 'set-last-modified-header' => (boolean) Whether to set *Last-Modified* header or not. Default: true.
6563
- * 'set-cache-control-header' => (boolean) Whether to set *Cache-Control* header or not. Default: true.
6564
- * 'cache-control-header' => string Cache control header. Default: "public, max-age=86400"
6565
- *
6566
- * @throws ServeFailedException if serving failed
6567
- * @return void
6568
- */
6569
- public static function serve($filename, $contentType, $options = [])
6570
- {
6571
- // Check mimetype - this also checks that path is secure and file exists
6572
- InputValidator::checkMimeType($filename, [
6573
- 'image/jpeg',
6574
- 'image/png',
6575
- 'image/webp',
6576
- 'image/gif'
6577
- ]);
6578
-
6579
- /*
6580
- if (!file_exists($filename)) {
6581
- Header::addHeader('X-WebP-Convert-Error: Could not read file');
6582
- throw new ServeFailedException('Could not read file');
6583
- }*/
6584
-
6585
- $options = self::processOptions($options);
6586
-
6587
- if ($options['headers']['last-modified']) {
6588
- Header::setHeader("Last-Modified: " . gmdate("D, d M Y H:i:s", @filemtime($filename)) ." GMT");
6589
- }
6590
-
6591
- if ($options['headers']['content-type']) {
6592
- Header::setHeader('Content-Type: ' . $contentType);
6593
- }
6594
-
6595
- if ($options['headers']['vary-accept']) {
6596
- Header::addHeader('Vary: Accept');
6597
- }
6598
-
6599
- if (!empty($options['cache-control-header'])) {
6600
- if ($options['headers']['cache-control']) {
6601
- Header::setHeader('Cache-Control: ' . $options['cache-control-header']);
6602
- }
6603
- if ($options['headers']['expires']) {
6604
- // Add exprires header too (#126)
6605
- // Check string for something like this: max-age:86400
6606
- if (preg_match('#max-age\\s*=\\s*(\\d*)#', $options['cache-control-header'], $matches)) {
6607
- $seconds = $matches[1];
6608
- Header::setHeader('Expires: '. gmdate('D, d M Y H:i:s \G\M\T', time() + intval($seconds)));
6609
- }
6610
- }
6611
- }
6612
-
6613
- if ($options['headers']['content-length']) {
6614
- Header::setHeader('Content-Length: ' . filesize($filename));
6615
- }
6616
-
6617
- if (@readfile($filename) === false) {
6618
- Header::addHeader('X-WebP-Convert-Error: Could not read file');
6619
- throw new ServeFailedException('Could not read file');
6620
- }
6621
- }
6622
- }
6623
-
6624
- ?><?php
6625
-
6626
- namespace WebPConvert\Serve\Exceptions;
6627
-
6628
- use WebPConvert\Exceptions\WebPConvertException;
6629
-
6630
- class ServeFailedException extends WebPConvertException
6631
- {
6632
- public $description = 'Failed serving';
6633
- }
6634
-
6635
- ?><?php
6636
-
6637
- namespace ImageMimeTypeGuesser\Detectors;
6638
-
6639
- use ImageMimeTypeGuesser\Detectors\AbstractDetector;
6640
-
6641
- abstract class AbstractDetector
6642
- {
6643
- /**
6644
- * Try to detect mime type of image
6645
- *
6646
- * Returns:
6647
- * - mime type (string) (if it is in fact an image, and type could be determined)
6648
- * - false (if it is not an image type that the server knowns about)
6649
- * - null (if nothing can be determined)
6650
- *
6651
- * @param string $filePath The path to the file
6652
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6653
- * false (if it is not an image type that the server knowns about)
6654
- * or null (if nothing can be determined)
6655
- */
6656
- abstract protected function doDetect($filePath);
6657
-
6658
- /**
6659
- * Create an instance of this class
6660
- *
6661
- * @param string $filePath The path to the file
6662
- * @return static
6663
- */
6664
- public static function createInstance()
6665
- {
6666
- return new static();
6667
- }
6668
-
6669
- /**
6670
- * Detect mime type of file (for images only)
6671
- *
6672
- * Returns:
6673
- * - mime type (string) (if it is in fact an image, and type could be determined)
6674
- * - false (if it is not an image type that the server knowns about)
6675
- * - null (if nothing can be determined)
6676
- *
6677
- * @param string $filePath The path to the file
6678
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6679
- * false (if it is not an image type that the server knowns about)
6680
- * or null (if nothing can be determined)
6681
- */
6682
- public static function detect($filePath)
6683
- {
6684
- if (!@file_exists($filePath)) {
6685
- return false;
6686
- }
6687
- return self::createInstance()->doDetect($filePath);
6688
- }
6689
- }
6690
-
6691
- ?><?php
6692
-
6693
- /**
6694
- * ImageMimeTypeGuesser - Detect / guess mime type of an image
6695
- *
6696
- * @link https://github.com/rosell-dk/image-mime-type-guesser
6697
- * @license MIT
6698
- */
6699
-
6700
- namespace ImageMimeTypeGuesser;
6701
-
6702
- class GuessFromExtension
6703
- {
6704
-
6705
-
6706
- /**
6707
- * Make a wild guess based on file extension.
6708
- *
6709
- * - and I mean wild!
6710
- *
6711
- * Only most popular image types are recognized.
6712
- * Many are not. See this list: https://www.iana.org/assignments/media-types/media-types.xhtml
6713
- * - and the constants here: https://secure.php.net/manual/en/function.exif-imagetype.php
6714
- *
6715
- * If no mapping found, nothing is returned
6716
- *
6717
- * TODO: jp2, jpx, ...
6718
- * Returns:
6719
- * - mimetype (if file extension could be mapped to an image type),
6720
- * - false (if file extension could be mapped to a type known not to be an image type)
6721
- * - null (if file extension could not be mapped to any mime type, using our little list)
6722
- *
6723
- * @param string $filePath The path to the file
6724
- * @return string|false|null mimetype (if file extension could be mapped to an image type),
6725
- * false (if file extension could be mapped to a type known not to be an image type)
6726
- * or null (if file extension could not be mapped to any mime type, using our little list)
6727
- */
6728
- public static function guess($filePath)
6729
- {
6730
- if (!@file_exists($filePath)) {
6731
- return false;
6732
- }
6733
- /*
6734
- Not using pathinfo, as it is locale aware, and I'm not sure if that could lead to problems
6735
-
6736
- if (!function_exists('pathinfo')) {
6737
- // This is really a just in case! - We do not expect this to happen.
6738
- // - in fact we have a test case asserting that this does not happen.
6739
- return null;
6740
- //
6741
- $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
6742
- $fileExtension = strtolower($fileExtension);
6743
- }*/
6744
-
6745
- $result = preg_match('#\\.([^.]*)$#', $filePath, $matches);
6746
- if ($result !== 1) {
6747
- return null;
6748
- }
6749
- $fileExtension = $matches[1];
6750
-
6751
- // Trivial image mime types
6752
- if (in_array($fileExtension, ['bmp', 'gif', 'jpeg', 'png', 'tiff', 'webp'])) {
6753
- return 'image/' . $fileExtension;
6754
- }
6755
-
6756
- // Common extensions that are definitely not images
6757
- if (in_array($fileExtension, ['txt', 'doc', 'zip', 'gz', 'exe'])) {
6758
- return false;
6759
- }
6760
-
6761
- // Non-trivial image mime types
6762
- switch ($fileExtension) {
6763
- case 'ico':
6764
- return 'image/vnd.microsoft.icon'; // or perhaps 'x-icon' ?
6765
-
6766
- case 'jpg':
6767
- return 'image/jpeg';
6768
-
6769
- case 'svg':
6770
- return 'image/svg+xml';
6771
-
6772
- case 'tif':
6773
- return 'image/tiff';
6774
- }
6775
-
6776
- // We do not know this extension, return null
6777
- return null;
6778
- }
6779
-
6780
- }
6781
-
6782
- ?><?php
6783
-
6784
- /**
6785
- * ImageMimeTypeGuesser - Detect / guess mime type of an image
6786
- *
6787
- * The library is born out of a discussion here:
6788
- * https://github.com/rosell-dk/webp-convert/issues/98
6789
- *
6790
- * @link https://github.com/rosell-dk/image-mime-type-guesser
6791
- * @license MIT
6792
- */
6793
-
6794
- namespace ImageMimeTypeGuesser;
6795
-
6796
- use \ImageMimeTypeGuesser\Detectors\Stack;
6797
-
6798
- class ImageMimeTypeGuesser
6799
- {
6800
-
6801
-
6802
- /**
6803
- * Try to detect mime type of image using all available detectors (the "stack" detector).
6804
- *
6805
- * Returns:
6806
- * - mime type (string) (if it is in fact an image, and type could be determined)
6807
- * - false (if it is not an image type that the server knowns about)
6808
- * - null (if nothing can be determined)
6809
- *
6810
- * @param string $filePath The path to the file
6811
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6812
- * false (if it is not an image type that the server knowns about)
6813
- * or null (if nothing can be determined)
6814
- */
6815
- public static function detect($filePath)
6816
- {
6817
- return Stack::detect($filePath);
6818
- }
6819
-
6820
- /**
6821
- * Try to detect mime type of image. If that fails, make a guess based on the file extension.
6822
- *
6823
- * Try to detect mime type of image using "stack" detector (all available methods, until one succeeds)
6824
- * If that fails (null), fall back to wild west guessing based solely on file extension.
6825
- *
6826
- * Returns:
6827
- * - mime type (string) (if it is an image, and type could be determined / mapped from file extension))
6828
- * - false (if it is not an image type that the server knowns about)
6829
- * - null (if nothing can be determined)
6830
- *
6831
- * @param string $filePath The path to the file
6832
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6833
- * false (if it is not an image type that the server knowns about)
6834
- * or null (if nothing can be determined)
6835
- */
6836
- public static function guess($filePath)
6837
- {
6838
- $detectionResult = self::detect($filePath);
6839
- if (!is_null($detectionResult)) {
6840
- return $detectionResult;
6841
- }
6842
-
6843
- // fall back to the wild west method
6844
- return GuessFromExtension::guess($filePath);
6845
- }
6846
-
6847
- /**
6848
- * Try to detect mime type of image. If that fails, make a guess based on the file extension.
6849
- *
6850
- * Try to detect mime type of image using "stack" detector (all available methods, until one succeeds)
6851
- * If that fails (false or null), fall back to wild west guessing based solely on file extension.
6852
- *
6853
- * Returns:
6854
- * - mime type (string) (if it is an image, and type could be determined / mapped from file extension)
6855
- * - false (if it is not an image type that the server knowns about)
6856
- * - null (if nothing can be determined)
6857
- *
6858
- * @param string $filePath The path to the file
6859
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
6860
- * false (if it is not an image type that the server knowns about)
6861
- * or null (if nothing can be determined)
6862
- */
6863
- public static function lenientGuess($filePath)
6864
- {
6865
- $detectResult = self::detect($filePath);
6866
- if ($detectResult === false) {
6867
- // The server does not recognize this image type.
6868
- // - but perhaps it is because it does not know about this image type.
6869
- // - so we turn to mapping the file extension
6870
- return GuessFromExtension::guess($filePath);
6871
- } elseif (is_null($detectResult)) {
6872
- // the mime type could not be determined
6873
- // perhaps we also in this case want to turn to mapping the file extension
6874
- return GuessFromExtension::guess($filePath);
6875
- }
6876
- return $detectResult;
6877
- }
6878
-
6879
-
6880
- /**
6881
- * Check if the *detected* mime type is in a list of accepted mime types.
6882
- *
6883
- * @param string $filePath The path to the file
6884
- * @param string[] $mimeTypes Mime types to accept
6885
- * @return bool Whether the detected mime type is in the $mimeTypes array or not
6886
- */
6887
- public static function detectIsIn($filePath, $mimeTypes)
6888
- {
6889
- return in_array(self::detect($filePath), $mimeTypes);
6890
- }
6891
-
6892
- /**
6893
- * Check if the *guessed* mime type is in a list of accepted mime types.
6894
- *
6895
- * @param string $filePath The path to the file
6896
- * @param string[] $mimeTypes Mime types to accept
6897
- * @return bool Whether the detected / guessed mime type is in the $mimeTypes array or not
6898
- */
6899
- public static function guessIsIn($filePath, $mimeTypes)
6900
- {
6901
- return in_array(self::guess($filePath), $mimeTypes);
6902
- }
6903
-
6904
- /**
6905
- * Check if the *leniently guessed* mime type is in a list of accepted mime types.
6906
- *
6907
- * @param string $filePath The path to the file
6908
- * @param string[] $mimeTypes Mime types to accept
6909
- * @return bool Whether the detected / leniently guessed mime type is in the $mimeTypes array or not
6910
- */
6911
- public static function lenientGuessIsIn($filePath, $mimeTypes)
6912
- {
6913
- return in_array(self::lenientGuess($filePath), $mimeTypes);
6914
- }
6915
- }
6916
-
6917
- ?><?php
6918
-
6919
- namespace ImageMimeTypeGuesser\Detectors;
6920
-
6921
- use \ImageMimeTypeGuesser\Detectors\AbstractDetector;
6922
-
6923
- class ExifImageType extends AbstractDetector
6924
- {
6925
-
6926
- /**
6927
- * Try to detect mime type of image using *exif_imagetype*.
6928
- *
6929
- * Returns:
6930
- * - mime type (string) (if it is in fact an image, and type could be determined)
6931
- * - false (if it is not an image type that the server knowns about)
6932
- * - null (if nothing can be determined)
6933
- *
6934
- * @param string $filePath The path to the file
6935
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6936
- * false (if it is not an image type that the server knowns about)
6937
- * or null (if nothing can be determined)
6938
- */
6939
- protected function doDetect($filePath)
6940
- {
6941
- // exif_imagetype is fast, however not available on all systems,
6942
- // It may return false. In that case we can rely on that the file is not an image (and return false)
6943
- if (function_exists('exif_imagetype')) {
6944
- try {
6945
- $imageType = exif_imagetype($filePath);
6946
- return ($imageType ? image_type_to_mime_type($imageType) : false);
6947
- } catch (\Exception $e) {
6948
- // Might for example get "Read error!"
6949
- // well well, don't let this stop us
6950
- //echo $e->getMessage();
6951
- // throw($e);
6952
- }
6953
- }
6954
- return null;
6955
- }
6956
- }
6957
-
6958
- ?><?php
6959
-
6960
- namespace ImageMimeTypeGuesser\Detectors;
6961
-
6962
- class FInfo extends AbstractDetector
6963
- {
6964
-
6965
- /**
6966
- * Try to detect mime type of image using *finfo* class.
6967
- *
6968
- * Returns:
6969
- * - mime type (string) (if it is in fact an image, and type could be determined)
6970
- * - false (if it is not an image type that the server knowns about)
6971
- * - null (if nothing can be determined)
6972
- *
6973
- * @param string $filePath The path to the file
6974
- * @return string|false|null mimetype (if it is an image, and type could be determined),
6975
- * false (if it is not an image type that the server knowns about)
6976
- * or null (if nothing can be determined)
6977
- */
6978
- protected function doDetect($filePath)
6979
- {
6980
-
6981
- if (class_exists('finfo')) {
6982
- // phpcs:ignore PHPCompatibility.PHP.NewClasses.finfoFound
6983
- $finfo = new \finfo(FILEINFO_MIME);
6984
- $mime = explode('; ', $finfo->file($filePath));
6985
- $result = $mime[0];
6986
-
6987
- if (strpos($result, 'image/') === 0) {
6988
- return $result;
6989
- } else {
6990
- return false;
6991
- }
6992
- }
6993
- return null;
6994
- }
6995
- }
6996
-
6997
- ?><?php
6998
-
6999
- namespace ImageMimeTypeGuesser\Detectors;
7000
-
7001
- class GetImageSize extends AbstractDetector
7002
- {
7003
-
7004
- /**
7005
- * Try to detect mime type of image using *getimagesize()*.
7006
- *
7007
- * Returns:
7008
- * - mime type (string) (if it is in fact an image, and type could be determined)
7009
- * - false (if it is not an image type that the server knowns about)
7010
- * - null (if nothing can be determined)
7011
- *
7012
- * @param string $filePath The path to the file
7013
- * @return string|false|null mimetype (if it is an image, and type could be determined),
7014
- * false (if it is not an image type that the server knowns about)
7015
- * or null (if nothing can be determined)
7016
- */
7017
- protected function doDetect($filePath)
7018
- {
7019
- // getimagesize is slower than exif_imagetype
7020
- // It may not return "mime". In that case we can rely on that the file is not an image (and return false)
7021
- if (function_exists('getimagesize')) {
7022
- try {
7023
- $imageSize = getimagesize($filePath);
7024
- return (isset($imageSize['mime']) ? $imageSize['mime'] : false);
7025
- } catch (\Exception $e) {
7026
- // well well, don't let this stop us either
7027
- return null;
7028
- }
7029
- }
7030
- return null;
7031
- }
7032
- }
7033
-
7034
- ?><?php
7035
-
7036
- namespace ImageMimeTypeGuesser\Detectors;
7037
-
7038
- class MimeContentType extends AbstractDetector
7039
- {
7040
-
7041
- /**
7042
- * Try to detect mime type of image using *mime_content_type()*.
7043
- *
7044
- * Returns:
7045
- * - mime type (string) (if it is in fact an image, and type could be determined)
7046
- * - false (if it is not an image type that the server knowns about)
7047
- * - null (if nothing can be determined)
7048
- *
7049
- * @param string $filePath The path to the file
7050
- * @return string|false|null mimetype (if it is an image, and type could be determined),
7051
- * false (if it is not an image type that the server knowns about)
7052
- * or null (if nothing can be determined)
7053
- */
7054
- protected function doDetect($filePath)
7055
- {
7056
- // mime_content_type supposedly used to be deprecated, but it seems it isn't anymore
7057
- // it may return false on failure.
7058
- if (function_exists('mime_content_type')) {
7059
- try {
7060
- $result = mime_content_type($filePath);
7061
- if ($result !== false) {
7062
- if (strpos($result, 'image/') === 0) {
7063
- return $result;
7064
- } else {
7065
- return false;
7066
- }
7067
- }
7068
- } catch (\Exception $e) {
7069
- // we are unstoppable!
7070
- }
7071
- }
7072
- return null;
7073
- }
7074
- }
7075
-
7076
- ?><?php
7077
-
7078
- namespace ImageMimeTypeGuesser\Detectors;
7079
-
7080
- use \ImageMimeTypeGuesser\Detectors\AbstractDetector;
7081
-
7082
- class SniffFirstFourBytes extends AbstractDetector
7083
- {
7084
-
7085
- /**
7086
- * Try to detect mime type by sniffing the first four bytes.
7087
- *
7088
- * Credits: Based on the code here: http://phil.lavin.me.uk/2011/12/php-accurately-detecting-the-type-of-a-file/
7089
- *
7090
- * Returns:
7091
- * - mime type (string) (if it is in fact an image, and type could be determined)
7092
- * - false (if it is not an image type that the server knowns about)
7093
- * - null (if nothing can be determined)
7094
- *
7095
- * @param string $filePath The path to the file
7096
- * @return string|false|null mimetype (if it is an image, and type could be determined),
7097
- * false (if it is not an image type that the server knowns about)
7098
- * or null (if nothing can be determined)
7099
- */
7100
- protected function doDetect($filePath)
7101
- {
7102
- // PNG, GIF, JFIF JPEG, EXIF JPEF (respectively)
7103
- $known = [
7104
- '89504E47' => 'image/png',
7105
- '47494638' => 'image/gif',
7106
- 'FFD8FFE0' => 'image/jpeg', // JFIF JPEG
7107
- 'FFD8FFE1' => 'image/jpeg', // EXIF JPEG
7108
- ];
7109
-
7110
- $handle = @fopen($filePath, 'r');
7111
- if ($handle === false) {
7112
- return null;
7113
- }
7114
- $firstFour = @fread($handle, 4);
7115
- if ($firstFour === false) {
7116
- return null;
7117
- }
7118
- $key = strtoupper(bin2hex($firstFour));
7119
- if (isset($known[$key])) {
7120
- return $known[$key];
7121
- }
7122
- }
7123
- }
7124
-
7125
- ?><?php
7126
-
7127
- namespace ImageMimeTypeGuesser\Detectors;
7128
-
7129
- class Stack extends AbstractDetector
7130
- {
7131
- /**
7132
- * Try to detect mime type of image using all available detectors.
7133
- *
7134
- * Returns:
7135
- * - mime type (string) (if it is in fact an image, and type could be determined)
7136
- * - false (if it is not an image type that the server knowns about)
7137
- * - null (if nothing can be determined)
7138
- *
7139
- * @param string $filePath The path to the file
7140
- * @return string|false|null mimetype (if it is an image, and type could be determined),
7141
- * false (if it is not an image type that the server knowns about)
7142
- * or null (if nothing can be determined)
7143
- */
7144
- protected function doDetect($filePath)
7145
- {
7146
- $detectors = [
7147
- 'ExifImageType',
7148
- 'FInfo',
7149
- 'SniffFirstFourBytes',
7150
- 'GetImageSize',
7151
- 'MimeContentType',
7152
- ];
7153
-
7154
- foreach ($detectors as $className) {
7155
- $result = call_user_func(
7156
- array("\\ImageMimeTypeGuesser\\Detectors\\" . $className, 'detect'),
7157
- $filePath
7158
- );
7159
- if (!is_null($result)) {
7160
- return $result;
7161
- }
7162
- }
7163
-
7164
- return null; // undetermined
7165
- }
7166
- }
7167
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/src-build/webp-on-demand-1.inc DELETED
@@ -1,1393 +0,0 @@
1
- <?php
2
- ?><?php
3
- namespace WebPConvert\Serve;
4
-
5
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
6
- use WebPConvert\Helpers\InputValidator;
7
- use WebPConvert\Helpers\MimeType;
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 ArrayOption('serve-image', []),
54
- new SensitiveArrayOption('convert', [])
55
- );
56
- foreach ($options as $optionId => $optionValue) {
57
- $options2->setOrCreateOption($optionId, $optionValue);
58
- }
59
- $options2->check();
60
- return $options2->getOptions();
61
- }
62
-
63
- /**
64
- * Serve original file (source).
65
- *
66
- * @param string $source path to source file
67
- * @param array $serveImageOptions (optional) options for serving an image
68
- * Supported options:
69
- * - All options supported by ServeFile::serve()
70
- * @throws ServeFailedException if source is not an image or mime type cannot be determined
71
- * @return void
72
- */
73
- public static function serveOriginal($source, $serveImageOptions = [])
74
- {
75
- InputValidator::checkSource($source);
76
- $contentType = MimeType::getMimeTypeDetectionResult($source);
77
- if (is_null($contentType)) {
78
- throw new ServeFailedException('Rejecting to serve original (mime type cannot be determined)');
79
- } elseif ($contentType === false) {
80
- throw new ServeFailedException('Rejecting to serve original (it is not an image)');
81
- } else {
82
- ServeFile::serve($source, $contentType, $serveImageOptions);
83
- }
84
- }
85
-
86
- /**
87
- * Serve destination file.
88
- *
89
- * TODO: SHould this really be public?
90
- *
91
- * @param string $destination path to destination file
92
- * @param array $serveImageOptions (optional) options for serving (such as which headers to add)
93
- * Supported options:
94
- * - All options supported by ServeFile::serve()
95
- * @return void
96
- */
97
- public static function serveDestination($destination, $serveImageOptions = [])
98
- {
99
- InputValidator::checkDestination($destination);
100
- ServeFile::serve($destination, 'image/webp', $serveImageOptions);
101
- }
102
-
103
-
104
- public static function warningHandler()
105
- {
106
- // do nothing! - as we do not return anything, the warning is suppressed
107
- }
108
-
109
- /**
110
- * Serve converted webp.
111
- *
112
- * Serve a converted webp. If a file already exists at the destination, that is served (unless it is
113
- * older than the source - in that case a fresh conversion will be made, or the file at the destination
114
- * is larger than the source - in that case the source is served). Some options may alter this logic.
115
- * In case no file exists at the destination, a fresh conversion is made and served.
116
- *
117
- * @param string $source path to source file
118
- * @param string $destination path to destination
119
- * @param array $options (optional) options for serving/converting
120
- * Supported options:
121
- * 'show-report' => (boolean) If true, the decision will always be 'report'
122
- * 'serve-original' => (boolean) If true, the decision will be 'source' (unless above option is set)
123
- * 'reconvert ' => (boolean) If true, the decision will be 'fresh-conversion' (unless one of the
124
- * above options is set)
125
- * - All options supported by WebPConvert::convert()
126
- * - All options supported by ServeFile::serve()
127
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
128
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
129
- *
130
- * @throws \WebPConvert\Exceptions\WebPConvertException If something went wrong.
131
- * @return void
132
- */
133
- public static function serve($source, $destination, $options = [], $serveLogger = null, $convertLogger = null)
134
- {
135
- InputValidator::checkSourceAndDestination($source, $destination);
136
-
137
- $options = self::processOptions($options);
138
-
139
- if ($options['suppress-warnings']) {
140
- set_error_handler(
141
- array('\\WebPConvert\\Serve\\ServeConvertedWebP', "warningHandler"),
142
- E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
143
- );
144
- }
145
-
146
-
147
- //$options = array_merge(self::$defaultOptions, $options);
148
-
149
- // Step 1: Is there a file at the destination? If not, trigger conversion
150
- // However 1: if "show-report" option is set, serve the report instead
151
- // However 2: "reconvert" option should also trigger conversion
152
- if ($options['show-report']) {
153
- Header::addLogHeader('Showing report', $serveLogger);
154
- Report::convertAndReport($source, $destination, $options);
155
- return;
156
- }
157
-
158
- if (!@file_exists($destination)) {
159
- Header::addLogHeader('Converting (there were no file at destination)', $serveLogger);
160
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
161
- } elseif ($options['reconvert']) {
162
- Header::addLogHeader('Converting (told to reconvert)', $serveLogger);
163
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
164
- } else {
165
- // Step 2: Is the destination older than the source?
166
- // If yes, trigger conversion (deleting destination is implicit)
167
- $timestampSource = @filemtime($source);
168
- $timestampDestination = @filemtime($destination);
169
- if (($timestampSource !== false) &&
170
- ($timestampDestination !== false) &&
171
- ($timestampSource > $timestampDestination)) {
172
- Header::addLogHeader('Converting (destination was older than the source)', $serveLogger);
173
- WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
174
- }
175
- }
176
-
177
- // Step 3: Serve the smallest file (destination or source)
178
- // However, first check if 'serve-original' is set
179
- if ($options['serve-original']) {
180
- Header::addLogHeader('Serving original (told to)', $serveLogger);
181
- self::serveOriginal($source, $options['serve-image']);
182
- }
183
-
184
- $filesizeDestination = @filesize($destination);
185
- $filesizeSource = @filesize($source);
186
- if (($filesizeSource !== false) &&
187
- ($filesizeDestination !== false) &&
188
- ($filesizeDestination > $filesizeSource)) {
189
- Header::addLogHeader('Serving original (it is smaller)', $serveLogger);
190
- self::serveOriginal($source, $options['serve-image']);
191
- }
192
-
193
- Header::addLogHeader('Serving converted file', $serveLogger);
194
- self::serveDestination($destination, $options['serve-image']);
195
- }
196
- }
197
-
198
- ?><?php
199
- namespace WebPConvert\Serve;
200
-
201
- use WebPConvert\Helpers\InputValidator;
202
- use WebPConvert\Options\Options;
203
- use WebPConvert\Options\StringOption;
204
- use WebPConvert\Serve\Header;
205
- use WebPConvert\Serve\Report;
206
- use WebPConvert\Serve\ServeConvertedWeb;
207
- use WebPConvert\Serve\Exceptions\ServeFailedException;
208
- use WebPConvert\Exceptions\WebPConvertException;
209
-
210
- /**
211
- * Serve a converted webp image and handle errors.
212
- *
213
- * @package WebPConvert
214
- * @author Bjørn Rosell <it@rosell.dk>
215
- * @since Class available since Release 2.0.0
216
- */
217
- class ServeConvertedWebPWithErrorHandling
218
- {
219
-
220
- /**
221
- * Process options.
222
- *
223
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
224
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
225
- * @param array $options
226
- */
227
- private static function processOptions($options)
228
- {
229
- $options2 = new Options();
230
- $options2->addOptions(
231
- new StringOption('fail', 'original', ['original', '404', 'throw', 'report']),
232
- new StringOption('fail-when-fail-fails', 'throw', ['original', '404', 'throw', 'report'])
233
- );
234
- foreach ($options as $optionId => $optionValue) {
235
- $options2->setOrCreateOption($optionId, $optionValue);
236
- }
237
- $options2->check();
238
- return $options2->getOptions();
239
- }
240
-
241
- /**
242
- * Add headers for preventing caching.
243
- *
244
- * @return void
245
- */
246
- private static function addHeadersPreventingCaching()
247
- {
248
- Header::setHeader("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
249
- Header::addHeader("Cache-Control: post-check=0, pre-check=0");
250
- Header::setHeader("Pragma: no-cache");
251
- }
252
-
253
- /**
254
- * Perform fail action.
255
- *
256
- * @param string $fail Action to perform (original | 404 | report)
257
- * @param string $failIfFailFails Action to perform if $fail action fails
258
- * @param string $source path to source file
259
- * @param string $destination path to destination
260
- * @param array $options (optional) options for serving/converting
261
- * @param \Exception $e exception that was thrown when trying to serve
262
- * @param string $serveClass (optional) Full class name to a class that has a serveOriginal() method
263
- * @return void
264
- */
265
- public static function performFailAction($fail, $failIfFailFails, $source, $destination, $options, $e, $serveClass)
266
- {
267
- self::addHeadersPreventingCaching();
268
-
269
- Header::addLogHeader('Performing fail action: ' . $fail);
270
-
271
- switch ($fail) {
272
- case 'original':
273
- try {
274
- //ServeConvertedWebP::serveOriginal($source, $options);
275
- call_user_func($serveClass . '::serveOriginal', $source, $options);
276
- } catch (\Exception $e) {
277
- self::performFailAction($failIfFailFails, '404', $source, $destination, $options, $e, $serveClass);
278
- }
279
- break;
280
-
281
- case '404':
282
- $protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
283
- Header::setHeader($protocol . " 404 Not Found");
284
- break;
285
-
286
- case 'report':
287
- $options['show-report'] = true;
288
- Report::convertAndReport($source, $destination, $options);
289
- break;
290
-
291
- case 'throw':
292
- throw $e;
293
- break;
294
-
295
- case 'report-as-image':
296
- // TODO: Implement or discard ?
297
- break;
298
- }
299
- }
300
-
301
- /**
302
- * Serve webp image and handle errors as specified in the 'fail' option.
303
- *
304
- * This method basically wraps ServeConvertedWebP:serve in order to provide exception handling.
305
- * The error handling is set with the 'fail' option and can be either '404', 'original' or 'report'.
306
- * If set to '404', errors results in 404 Not Found headers being issued. If set to 'original', an
307
- * error results in the original being served.
308
- * Look up the ServeConvertedWebP:serve method to learn more.
309
- *
310
- * @param string $source path to source file
311
- * @param string $destination path to destination
312
- * @param array $options (optional) options for serving/converting
313
- * Supported options:
314
- * - 'fail' => (string) Action to take on failure (404 | original | report | throw).
315
- * "404" or "throw" is recommended for development and "original" is recommended for production.
316
- * Default: 'original'.
317
- * - 'fail-when-fail-fails' => (string) Action to take if fail action also fails. Default: '404'.
318
- * - All options supported by WebPConvert::convert()
319
- * - All options supported by ServeFile::serve()
320
- * - All options supported by DecideWhatToServe::decide)
321
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
322
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
323
- * @param string $serveClass (optional) Full class name to a class that has a serve() method and a
324
- * serveOriginal() method
325
- * @return void
326
- */
327
- public static function serve(
328
- $source,
329
- $destination,
330
- $options = [],
331
- $serveLogger = null,
332
- $convertLogger = null,
333
- $serveClass = '\\WebPConvert\\Serve\\ServeConvertedWebP'
334
- ) {
335
-
336
- $options = self::processOptions($options);
337
- try {
338
- InputValidator::checkSourceAndDestination($source, $destination);
339
- //ServeConvertedWebP::serve($source, $destination, $options, $serveLogger);
340
- call_user_func($serveClass . '::serve', $source, $destination, $options, $serveLogger, $convertLogger);
341
- } catch (\Exception $e) {
342
- if ($e instanceof \WebPConvert\Exceptions\WebPConvertException) {
343
- Header::addLogHeader($e->getShortMessage(), $serveLogger);
344
- }
345
-
346
- self::performFailAction(
347
- $options['fail'],
348
- $options['fail-when-fail-fails'],
349
- $source,
350
- $destination,
351
- $options,
352
- $e,
353
- $serveClass
354
- );
355
- }
356
- }
357
- }
358
-
359
- ?><?php
360
- namespace WebPConvert\Serve;
361
-
362
- //use WebPConvert\Serve\Report;
363
- use WebPConvert\Helpers\InputValidator;
364
- use WebPConvert\Options\ArrayOption;
365
- use WebPConvert\Options\BooleanOption;
366
- use WebPConvert\Options\Options;
367
- use WebPConvert\Options\StringOption;
368
- use WebPConvert\Serve\Header;
369
- use WebPConvert\Serve\Exceptions\ServeFailedException;
370
-
371
- /**
372
- * Serve a file (send to standard output)
373
- *
374
- * @package WebPConvert
375
- * @author Bjørn Rosell <it@rosell.dk>
376
- * @since Class available since Release 2.0.0
377
- */
378
- class ServeFile
379
- {
380
-
381
- /**
382
- * Process options.
383
- *
384
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
385
- * @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
386
- * @param array $options
387
- */
388
- private static function processOptions($options)
389
- {
390
- $options2 = new Options();
391
- $options2->addOptions(
392
- new ArrayOption('headers', []),
393
- new StringOption('cache-control-header', 'public, max-age=31536000')
394
- );
395
- foreach ($options as $optionId => $optionValue) {
396
- $options2->setOrCreateOption($optionId, $optionValue);
397
- }
398
- $options2->check();
399
- $options = $options2->getOptions();
400
-
401
- // headers option
402
- // --------------
403
-
404
- $headerOptions = new Options();
405
- $headerOptions->addOptions(
406
- new BooleanOption('cache-control', false),
407
- new BooleanOption('content-length', true),
408
- new BooleanOption('content-type', true),
409
- new BooleanOption('expires', false),
410
- new BooleanOption('last-modified', true),
411
- new BooleanOption('vary-accept', false)
412
- );
413
- foreach ($options['headers'] as $optionId => $optionValue) {
414
- $headerOptions->setOrCreateOption($optionId, $optionValue);
415
- }
416
- $options['headers'] = $headerOptions->getOptions();
417
- return $options;
418
- }
419
-
420
- /**
421
- * Serve existing file.
422
- *
423
- * @param string $filename File to serve (absolute path)
424
- * @param string $contentType Content-type (used to set header).
425
- * Only used when the "set-content-type-header" option is set.
426
- * Set to ie "image/jpeg" for serving jpeg file.
427
- * @param array $options Array of named options (optional).
428
- * Supported options:
429
- * 'add-vary-accept-header' => (boolean) Whether to add *Vary: Accept* header or not. Default: true.
430
- * 'set-content-type-header' => (boolean) Whether to set *Content-Type* header or not. Default: true.
431
- * 'set-last-modified-header' => (boolean) Whether to set *Last-Modified* header or not. Default: true.
432
- * 'set-cache-control-header' => (boolean) Whether to set *Cache-Control* header or not. Default: true.
433
- * 'cache-control-header' => string Cache control header. Default: "public, max-age=86400"
434
- *
435
- * @throws ServeFailedException if serving failed
436
- * @return void
437
- */
438
- public static function serve($filename, $contentType, $options = [])
439
- {
440
- // Check mimetype - this also checks that path is secure and file exists
441
- InputValidator::checkMimeType($filename, [
442
- 'image/jpeg',
443
- 'image/png',
444
- 'image/webp',
445
- 'image/gif'
446
- ]);
447
-
448
- /*
449
- if (!file_exists($filename)) {
450
- Header::addHeader('X-WebP-Convert-Error: Could not read file');
451
- throw new ServeFailedException('Could not read file');
452
- }*/
453
-
454
- $options = self::processOptions($options);
455
-
456
- if ($options['headers']['last-modified']) {
457
- Header::setHeader("Last-Modified: " . gmdate("D, d M Y H:i:s", @filemtime($filename)) ." GMT");
458
- }
459
-
460
- if ($options['headers']['content-type']) {
461
- Header::setHeader('Content-Type: ' . $contentType);
462
- }
463
-
464
- if ($options['headers']['vary-accept']) {
465
- Header::addHeader('Vary: Accept');
466
- }
467
-
468
- if (!empty($options['cache-control-header'])) {
469
- if ($options['headers']['cache-control']) {
470
- Header::setHeader('Cache-Control: ' . $options['cache-control-header']);
471
- }
472
- if ($options['headers']['expires']) {
473
- // Add exprires header too (#126)
474
- // Check string for something like this: max-age:86400
475
- if (preg_match('#max-age\\s*=\\s*(\\d*)#', $options['cache-control-header'], $matches)) {
476
- $seconds = $matches[1];
477
- Header::setHeader('Expires: '. gmdate('D, d M Y H:i:s \G\M\T', time() + intval($seconds)));
478
- }
479
- }
480
- }
481
-
482
- if ($options['headers']['content-length']) {
483
- Header::setHeader('Content-Length: ' . filesize($filename));
484
- }
485
-
486
- if (@readfile($filename) === false) {
487
- Header::addHeader('X-WebP-Convert-Error: Could not read file');
488
- throw new ServeFailedException('Could not read file');
489
- }
490
- }
491
- }
492
-
493
- ?><?php
494
-
495
- namespace WebPConvert\Exceptions;
496
-
497
- /**
498
- * WebPConvertException is the base exception for all exceptions in this library.
499
- *
500
- * Note that the parameters for the constructor differs from that of the Exception class.
501
- * We do not use exception code here, but are instead allowing two version of the error message:
502
- * a short version and a long version.
503
- * The short version may not contain special characters or dynamic content.
504
- * The detailed version may.
505
- * If the detailed version isn't provided, getDetailedMessage will return the short version.
506
- *
507
- */
508
- class WebPConvertException extends \Exception
509
- {
510
- public $description = '';
511
- protected $detailedMessage;
512
- protected $shortMessage;
513
-
514
- public function getDetailedMessage()
515
- {
516
- return $this->detailedMessage;
517
- }
518
-
519
- public function getShortMessage()
520
- {
521
- return $this->shortMessage;
522
- }
523
-
524
- public function __construct($shortMessage = "", $detailedMessage = "", $previous = null)
525
- {
526
- $detailedMessage = ($detailedMessage != '') ? $detailedMessage : $shortMessage;
527
- $this->detailedMessage = $detailedMessage;
528
- $this->shortMessage = $shortMessage;
529
-
530
- parent::__construct(
531
- $detailedMessage,
532
- 0,
533
- $previous
534
- );
535
- }
536
- }
537
-
538
- ?><?php
539
-
540
- namespace WebPConvert\Exceptions;
541
-
542
- use WebPConvert\Exceptions\WebPConvertException;
543
-
544
- class InvalidInputException extends WebPConvertException
545
- {
546
- public $description = 'Invalid input';
547
- }
548
-
549
- ?><?php
550
-
551
- namespace WebPConvert\Exceptions\InvalidInput;
552
-
553
- use WebPConvert\Exceptions\InvalidInputException;
554
-
555
- class InvalidImageTypeException extends InvalidInputException
556
- {
557
- public $description = 'The converter does not handle the supplied image type';
558
- }
559
-
560
- ?><?php
561
-
562
- namespace WebPConvert\Exceptions\InvalidInput;
563
-
564
- use WebPConvert\Exceptions\InvalidInputException;
565
-
566
- class TargetNotFoundException extends InvalidInputException
567
- {
568
- public $description = 'The converter could not locate source file';
569
- }
570
-
571
- ?><?php
572
-
573
- namespace WebPConvert\Helpers;
574
-
575
- use WebPConvert\Exceptions\InvalidInputException;
576
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
577
-
578
- /**
579
- * Functions for sanitizing.
580
- *
581
- * @package WebPConvert
582
- * @author Bjørn Rosell <it@rosell.dk>
583
- * @since Class available since Release 2.0.6
584
- */
585
- class PathChecker
586
- {
587
-
588
- /**
589
- * Check absolute file path to prevent attacks.
590
- *
591
- * - Prevents non printable characters
592
- * - Prevents stream wrappers
593
- * - Prevents directory traversal
594
- *
595
- * Preventing non printable characters is especially done to prevent the NUL character, which can be used
596
- * to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
597
- *
598
- * Preventeng stream wrappers is especially done to protect against Phar Deserialization.
599
- * See https://blog.ripstech.com/2018/new-php-exploitation-technique/
600
- *
601
- * @param string $absFilePath
602
- * @return string sanitized file path
603
- */
604
- public static function checkAbsolutePath($absFilePath, $text = 'file')
605
- {
606
- if (empty($absFilePath)) {
607
- throw new InvalidInputException('Empty filepath for ' . $text);
608
- }
609
-
610
- // Prevent non printable characters
611
- if (!ctype_print($absFilePath)) {
612
- throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
613
- }
614
-
615
- // Prevent directory traversal
616
- if (preg_match('#\.\.\/#', $absFilePath)) {
617
- throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path');
618
- }
619
-
620
- // Prevent stream wrappers ("phar://", "php://" and the like)
621
- // https://www.php.net/manual/en/wrappers.phar.php
622
- if (preg_match('#^\\w+://#', $absFilePath)) {
623
- throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path');
624
- }
625
- }
626
-
627
- public static function checkAbsolutePathAndExists($absFilePath, $text = 'file')
628
- {
629
- if (empty($absFilePath)) {
630
- throw new TargetNotFoundException($text . ' argument missing');
631
- }
632
- self::checkAbsolutePath($absFilePath, $text);
633
- if (@!file_exists($absFilePath)) {
634
- throw new TargetNotFoundException($text . ' file was not found');
635
- }
636
- if (@is_dir($absFilePath)) {
637
- throw new InvalidInputException($text . ' is a directory');
638
- }
639
- }
640
-
641
- /**
642
- * Checks that source path is secure, file exists and it is not a dir.
643
- *
644
- * To also check mime type, use InputValidator::checkSource
645
- */
646
- public static function checkSourcePath($source)
647
- {
648
- self::checkAbsolutePathAndExists($source, 'source');
649
- }
650
-
651
- public static function checkDestinationPath($destination)
652
- {
653
- if (empty($destination)) {
654
- throw new InvalidInputException('Destination argument missing');
655
- }
656
- self::checkAbsolutePath($destination, 'destination');
657
- if (@is_dir($destination)) {
658
- throw new InvalidInputException('Destination is a directory');
659
- }
660
- }
661
-
662
- public static function checkSourceAndDestinationPaths($source, $destination)
663
- {
664
- self::checkSourcePath($source);
665
- self::checkDestinationPath($destination);
666
- }
667
- }
668
-
669
- ?><?php
670
-
671
- namespace WebPConvert\Helpers;
672
-
673
- use WebPConvert\Helpers\MimeType;
674
- use WebPConvert\Helpers\PathChecker;
675
- use WebPConvert\Exceptions\InvalidInputException;
676
- use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
677
-
678
- /**
679
- * Functions for sanitizing.
680
- *
681
- * @package WebPConvert
682
- * @author Bjørn Rosell <it@rosell.dk>
683
- * @since Class available since Release 2.0.6
684
- */
685
- class InputValidator
686
- {
687
-
688
- private static $allowedMimeTypes = [
689
- 'image/jpeg',
690
- 'image/png'
691
- ];
692
-
693
- /**
694
- * Check mimetype and if file path is ok and exists
695
- */
696
- public static function checkMimeType($filePath, $allowedMimeTypes = null)
697
- {
698
- if (is_null($allowedMimeTypes)) {
699
- $allowedMimeTypes = self::$allowedMimeTypes;
700
- }
701
- // the following also tests that file path is ok and file exists
702
- $fileMimeType = MimeType::getMimeTypeDetectionResult($filePath);
703
-
704
- if (is_null($fileMimeType)) {
705
- throw new InvalidImageTypeException('Image type could not be detected');
706
- } elseif ($fileMimeType === false) {
707
- throw new InvalidImageTypeException('File seems not to be an image.');
708
- } elseif (!in_array($fileMimeType, $allowedMimeTypes)) {
709
- throw new InvalidImageTypeException('Unsupported mime type: ' . $fileMimeType);
710
- }
711
- }
712
-
713
- public static function checkSource($source)
714
- {
715
- PathChecker::checkSourcePath($source);
716
- self::checkMimeType($source);
717
- }
718
-
719
- public static function checkDestination($destination)
720
- {
721
- PathChecker::checkDestinationPath($destination);
722
- }
723
-
724
- public static function checkSourceAndDestination($source, $destination)
725
- {
726
- self::checkSource($source);
727
- self::checkDestination($destination);
728
- }
729
- }
730
-
731
- ?><?php
732
- namespace WebPConvert\Serve;
733
-
734
- /**
735
- * Add / Set HTTP header.
736
- *
737
- * This class does nothing more than adding two convenience functions for calling the "header" function.
738
- *
739
- * @package WebPConvert
740
- * @author Bjørn Rosell <it@rosell.dk>
741
- * @since Class available since Release 2.0.0
742
- */
743
- class Header
744
- {
745
- /**
746
- * Convenience function for adding header (append).
747
- *
748
- * @param string $header The header to add.
749
- * @return void
750
- */
751
- public static function addHeader($header)
752
- {
753
- header($header, false);
754
- }
755
-
756
- /**
757
- * Convenience function for replacing header.
758
- *
759
- * @param string $header The header to set.
760
- * @return void
761
- */
762
- public static function setHeader($header)
763
- {
764
- header($header, true);
765
- }
766
-
767
- /**
768
- * Add log header and optionally send it to a logger as well.
769
- *
770
- * @param string $msg Message to add to "X-WebP-Convert-Log" header
771
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
772
- * @return void
773
- */
774
- public static function addLogHeader($msg, $logger = null)
775
- {
776
- self::addHeader('X-WebP-Convert-Log: ' . $msg);
777
- if (!is_null($logger)) {
778
- $logger->logLn($msg);
779
- }
780
- }
781
- }
782
-
783
- ?><?php
784
-
785
- namespace WebPConvert;
786
-
787
- //use WebPConvert\Convert\Converters\ConverterHelper;
788
- use WebPConvert\Convert\Converters\Stack;
789
- //use WebPConvert\Serve\ServeExistingOrHandOver;
790
- use WebPConvert\Serve\ServeConvertedWebP;
791
- use WebPConvert\Serve\ServeConvertedWebPWithErrorHandling;
792
-
793
- /**
794
- * Convert images to webp and/or serve them.
795
- *
796
- * This class is just a couple of convenience methods for doing conversion and/or
797
- * serving.
798
- *
799
- * @package WebPConvert
800
- * @author Bjørn Rosell <it@rosell.dk>
801
- * @since Class available since Release 2.0.0
802
- */
803
- class WebPConvert
804
- {
805
-
806
- /**
807
- * Convert jpeg or png into webp
808
- *
809
- * Convenience method for calling Stack::convert.
810
- *
811
- * @param string $source The image to convert (absolute,no backslashes)
812
- * Image must be jpeg or png.
813
- * @param string $destination Where to store the converted file (absolute path, no backslashes).
814
- * @param array $options (optional) Array of named options
815
- * The options are documented here:
816
- * https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md
817
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
818
- *
819
- * @throws \WebPConvert\Convert\Exceptions\ConversionFailedException in case conversion fails
820
- * @return void
821
- */
822
- public static function convert($source, $destination, $options = [], $logger = null)
823
- {
824
- Stack::convert($source, $destination, $options, $logger);
825
- }
826
-
827
- /**
828
- * Serve webp image, converting first if neccessary.
829
- *
830
- * If an image already exists, it will be served, unless it is older or larger than the source. (If it is larger,
831
- * the original is served, if it is older, the existing webp will be deleted and a fresh conversion will be made
832
- * and served). In case of error, the action indicated in the 'fail' option will be triggered (default is to serve
833
- * the original). Look up the ServeConvertedWebP:serve() and the ServeConvertedWebPWithErrorHandling::serve()
834
- * methods to learn more.
835
- *
836
- * @param string $source path to source file
837
- * @param string $destination path to destination
838
- * @param array $options (optional) options for serving/converting. The options are documented in the
839
- * ServeConvertedWebPWithErrorHandling::serve() method
840
- * @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
841
- * @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
842
- * @return void
843
- */
844
- public static function serveConverted(
845
- $source,
846
- $destination,
847
- $options = [],
848
- $serveLogger = null,
849
- $convertLogger = null
850
- ) {
851
- //return ServeExistingOrHandOver::serveConverted($source, $destination, $options);
852
- //if (isset($options['handle-errors']) && $options['handle-errors'] === true) {
853
- if (isset($options['fail']) && ($options['fail'] != 'throw')) {
854
- ServeConvertedWebPWithErrorHandling::serve($source, $destination, $options, $serveLogger, $convertLogger);
855
- } else {
856
- ServeConvertedWebP::serve($source, $destination, $options, $serveLogger, $convertLogger);
857
- }
858
- }
859
- }
860
-
861
- ?><?php
862
-
863
- /**
864
- * ImageMimeTypeGuesser - Detect / guess mime type of an image
865
- *
866
- * @link https://github.com/rosell-dk/image-mime-type-guesser
867
- * @license MIT
868
- */
869
-
870
- namespace ImageMimeTypeGuesser;
871
-
872
- class GuessFromExtension
873
- {
874
-
875
-
876
- /**
877
- * Make a wild guess based on file extension.
878
- *
879
- * - and I mean wild!
880
- *
881
- * Only most popular image types are recognized.
882
- * Many are not. See this list: https://www.iana.org/assignments/media-types/media-types.xhtml
883
- * - and the constants here: https://secure.php.net/manual/en/function.exif-imagetype.php
884
- *
885
- * If no mapping found, nothing is returned
886
- *
887
- * TODO: jp2, jpx, ...
888
- * Returns:
889
- * - mimetype (if file extension could be mapped to an image type),
890
- * - false (if file extension could be mapped to a type known not to be an image type)
891
- * - null (if file extension could not be mapped to any mime type, using our little list)
892
- *
893
- * @param string $filePath The path to the file
894
- * @return string|false|null mimetype (if file extension could be mapped to an image type),
895
- * false (if file extension could be mapped to a type known not to be an image type)
896
- * or null (if file extension could not be mapped to any mime type, using our little list)
897
- */
898
- public static function guess($filePath)
899
- {
900
- if (!@file_exists($filePath)) {
901
- return false;
902
- }
903
- /*
904
- Not using pathinfo, as it is locale aware, and I'm not sure if that could lead to problems
905
-
906
- if (!function_exists('pathinfo')) {
907
- // This is really a just in case! - We do not expect this to happen.
908
- // - in fact we have a test case asserting that this does not happen.
909
- return null;
910
- //
911
- $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
912
- $fileExtension = strtolower($fileExtension);
913
- }*/
914
-
915
- $result = preg_match('#\\.([^.]*)$#', $filePath, $matches);
916
- if ($result !== 1) {
917
- return null;
918
- }
919
- $fileExtension = $matches[1];
920
-
921
- // Trivial image mime types
922
- if (in_array($fileExtension, ['bmp', 'gif', 'jpeg', 'png', 'tiff', 'webp'])) {
923
- return 'image/' . $fileExtension;
924
- }
925
-
926
- // Common extensions that are definitely not images
927
- if (in_array($fileExtension, ['txt', 'doc', 'zip', 'gz', 'exe'])) {
928
- return false;
929
- }
930
-
931
- // Non-trivial image mime types
932
- switch ($fileExtension) {
933
- case 'ico':
934
- return 'image/vnd.microsoft.icon'; // or perhaps 'x-icon' ?
935
-
936
- case 'jpg':
937
- return 'image/jpeg';
938
-
939
- case 'svg':
940
- return 'image/svg+xml';
941
-
942
- case 'tif':
943
- return 'image/tiff';
944
- }
945
-
946
- // We do not know this extension, return null
947
- return null;
948
- }
949
-
950
- }
951
-
952
- ?><?php
953
-
954
- /**
955
- * ImageMimeTypeGuesser - Detect / guess mime type of an image
956
- *
957
- * The library is born out of a discussion here:
958
- * https://github.com/rosell-dk/webp-convert/issues/98
959
- *
960
- * @link https://github.com/rosell-dk/image-mime-type-guesser
961
- * @license MIT
962
- */
963
-
964
- namespace ImageMimeTypeGuesser;
965
-
966
- use \ImageMimeTypeGuesser\Detectors\Stack;
967
-
968
- class ImageMimeTypeGuesser
969
- {
970
-
971
-
972
- /**
973
- * Try to detect mime type of image using all available detectors (the "stack" detector).
974
- *
975
- * Returns:
976
- * - mime type (string) (if it is in fact an image, and type could be determined)
977
- * - false (if it is not an image type that the server knowns about)
978
- * - null (if nothing can be determined)
979
- *
980
- * @param string $filePath The path to the file
981
- * @return string|false|null mimetype (if it is an image, and type could be determined),
982
- * false (if it is not an image type that the server knowns about)
983
- * or null (if nothing can be determined)
984
- */
985
- public static function detect($filePath)
986
- {
987
- return Stack::detect($filePath);
988
- }
989
-
990
- /**
991
- * Try to detect mime type of image. If that fails, make a guess based on the file extension.
992
- *
993
- * Try to detect mime type of image using "stack" detector (all available methods, until one succeeds)
994
- * If that fails (null), fall back to wild west guessing based solely on file extension.
995
- *
996
- * Returns:
997
- * - mime type (string) (if it is an image, and type could be determined / mapped from file extension))
998
- * - false (if it is not an image type that the server knowns about)
999
- * - null (if nothing can be determined)
1000
- *
1001
- * @param string $filePath The path to the file
1002
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1003
- * false (if it is not an image type that the server knowns about)
1004
- * or null (if nothing can be determined)
1005
- */
1006
- public static function guess($filePath)
1007
- {
1008
- $detectionResult = self::detect($filePath);
1009
- if (!is_null($detectionResult)) {
1010
- return $detectionResult;
1011
- }
1012
-
1013
- // fall back to the wild west method
1014
- return GuessFromExtension::guess($filePath);
1015
- }
1016
-
1017
- /**
1018
- * Try to detect mime type of image. If that fails, make a guess based on the file extension.
1019
- *
1020
- * Try to detect mime type of image using "stack" detector (all available methods, until one succeeds)
1021
- * If that fails (false or null), fall back to wild west guessing based solely on file extension.
1022
- *
1023
- * Returns:
1024
- * - mime type (string) (if it is an image, and type could be determined / mapped from file extension)
1025
- * - false (if it is not an image type that the server knowns about)
1026
- * - null (if nothing can be determined)
1027
- *
1028
- * @param string $filePath The path to the file
1029
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
1030
- * false (if it is not an image type that the server knowns about)
1031
- * or null (if nothing can be determined)
1032
- */
1033
- public static function lenientGuess($filePath)
1034
- {
1035
- $detectResult = self::detect($filePath);
1036
- if ($detectResult === false) {
1037
- // The server does not recognize this image type.
1038
- // - but perhaps it is because it does not know about this image type.
1039
- // - so we turn to mapping the file extension
1040
- return GuessFromExtension::guess($filePath);
1041
- } elseif (is_null($detectResult)) {
1042
- // the mime type could not be determined
1043
- // perhaps we also in this case want to turn to mapping the file extension
1044
- return GuessFromExtension::guess($filePath);
1045
- }
1046
- return $detectResult;
1047
- }
1048
-
1049
-
1050
- /**
1051
- * Check if the *detected* mime type is in a list of accepted mime types.
1052
- *
1053
- * @param string $filePath The path to the file
1054
- * @param string[] $mimeTypes Mime types to accept
1055
- * @return bool Whether the detected mime type is in the $mimeTypes array or not
1056
- */
1057
- public static function detectIsIn($filePath, $mimeTypes)
1058
- {
1059
- return in_array(self::detect($filePath), $mimeTypes);
1060
- }
1061
-
1062
- /**
1063
- * Check if the *guessed* mime type is in a list of accepted mime types.
1064
- *
1065
- * @param string $filePath The path to the file
1066
- * @param string[] $mimeTypes Mime types to accept
1067
- * @return bool Whether the detected / guessed mime type is in the $mimeTypes array or not
1068
- */
1069
- public static function guessIsIn($filePath, $mimeTypes)
1070
- {
1071
- return in_array(self::guess($filePath), $mimeTypes);
1072
- }
1073
-
1074
- /**
1075
- * Check if the *leniently guessed* mime type is in a list of accepted mime types.
1076
- *
1077
- * @param string $filePath The path to the file
1078
- * @param string[] $mimeTypes Mime types to accept
1079
- * @return bool Whether the detected / leniently guessed mime type is in the $mimeTypes array or not
1080
- */
1081
- public static function lenientGuessIsIn($filePath, $mimeTypes)
1082
- {
1083
- return in_array(self::lenientGuess($filePath), $mimeTypes);
1084
- }
1085
- }
1086
-
1087
- ?><?php
1088
-
1089
- namespace ImageMimeTypeGuesser\Detectors;
1090
-
1091
- use ImageMimeTypeGuesser\Detectors\AbstractDetector;
1092
-
1093
- abstract class AbstractDetector
1094
- {
1095
- /**
1096
- * Try to detect mime type of image
1097
- *
1098
- * Returns:
1099
- * - mime type (string) (if it is in fact an image, and type could be determined)
1100
- * - false (if it is not an image type that the server knowns about)
1101
- * - null (if nothing can be determined)
1102
- *
1103
- * @param string $filePath The path to the file
1104
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1105
- * false (if it is not an image type that the server knowns about)
1106
- * or null (if nothing can be determined)
1107
- */
1108
- abstract protected function doDetect($filePath);
1109
-
1110
- /**
1111
- * Create an instance of this class
1112
- *
1113
- * @param string $filePath The path to the file
1114
- * @return static
1115
- */
1116
- public static function createInstance()
1117
- {
1118
- return new static();
1119
- }
1120
-
1121
- /**
1122
- * Detect mime type of file (for images only)
1123
- *
1124
- * Returns:
1125
- * - mime type (string) (if it is in fact an image, and type could be determined)
1126
- * - false (if it is not an image type that the server knowns about)
1127
- * - null (if nothing can be determined)
1128
- *
1129
- * @param string $filePath The path to the file
1130
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1131
- * false (if it is not an image type that the server knowns about)
1132
- * or null (if nothing can be determined)
1133
- */
1134
- public static function detect($filePath)
1135
- {
1136
- if (!@file_exists($filePath)) {
1137
- return false;
1138
- }
1139
- return self::createInstance()->doDetect($filePath);
1140
- }
1141
- }
1142
-
1143
- ?><?php
1144
-
1145
- namespace ImageMimeTypeGuesser\Detectors;
1146
-
1147
- use \ImageMimeTypeGuesser\Detectors\AbstractDetector;
1148
-
1149
- class ExifImageType extends AbstractDetector
1150
- {
1151
-
1152
- /**
1153
- * Try to detect mime type of image using *exif_imagetype*.
1154
- *
1155
- * Returns:
1156
- * - mime type (string) (if it is in fact an image, and type could be determined)
1157
- * - false (if it is not an image type that the server knowns about)
1158
- * - null (if nothing can be determined)
1159
- *
1160
- * @param string $filePath The path to the file
1161
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1162
- * false (if it is not an image type that the server knowns about)
1163
- * or null (if nothing can be determined)
1164
- */
1165
- protected function doDetect($filePath)
1166
- {
1167
- // exif_imagetype is fast, however not available on all systems,
1168
- // It may return false. In that case we can rely on that the file is not an image (and return false)
1169
- if (function_exists('exif_imagetype')) {
1170
- try {
1171
- $imageType = exif_imagetype($filePath);
1172
- return ($imageType ? image_type_to_mime_type($imageType) : false);
1173
- } catch (\Exception $e) {
1174
- // Might for example get "Read error!"
1175
- // well well, don't let this stop us
1176
- //echo $e->getMessage();
1177
- // throw($e);
1178
- }
1179
- }
1180
- return null;
1181
- }
1182
- }
1183
-
1184
- ?><?php
1185
-
1186
- namespace ImageMimeTypeGuesser\Detectors;
1187
-
1188
- class FInfo extends AbstractDetector
1189
- {
1190
-
1191
- /**
1192
- * Try to detect mime type of image using *finfo* class.
1193
- *
1194
- * Returns:
1195
- * - mime type (string) (if it is in fact an image, and type could be determined)
1196
- * - false (if it is not an image type that the server knowns about)
1197
- * - null (if nothing can be determined)
1198
- *
1199
- * @param string $filePath The path to the file
1200
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1201
- * false (if it is not an image type that the server knowns about)
1202
- * or null (if nothing can be determined)
1203
- */
1204
- protected function doDetect($filePath)
1205
- {
1206
-
1207
- if (class_exists('finfo')) {
1208
- // phpcs:ignore PHPCompatibility.PHP.NewClasses.finfoFound
1209
- $finfo = new \finfo(FILEINFO_MIME);
1210
- $mime = explode('; ', $finfo->file($filePath));
1211
- $result = $mime[0];
1212
-
1213
- if (strpos($result, 'image/') === 0) {
1214
- return $result;
1215
- } else {
1216
- return false;
1217
- }
1218
- }
1219
- return null;
1220
- }
1221
- }
1222
-
1223
- ?><?php
1224
-
1225
- namespace ImageMimeTypeGuesser\Detectors;
1226
-
1227
- class GetImageSize extends AbstractDetector
1228
- {
1229
-
1230
- /**
1231
- * Try to detect mime type of image using *getimagesize()*.
1232
- *
1233
- * Returns:
1234
- * - mime type (string) (if it is in fact an image, and type could be determined)
1235
- * - false (if it is not an image type that the server knowns about)
1236
- * - null (if nothing can be determined)
1237
- *
1238
- * @param string $filePath The path to the file
1239
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1240
- * false (if it is not an image type that the server knowns about)
1241
- * or null (if nothing can be determined)
1242
- */
1243
- protected function doDetect($filePath)
1244
- {
1245
- // getimagesize is slower than exif_imagetype
1246
- // It may not return "mime". In that case we can rely on that the file is not an image (and return false)
1247
- if (function_exists('getimagesize')) {
1248
- try {
1249
- $imageSize = getimagesize($filePath);
1250
- return (isset($imageSize['mime']) ? $imageSize['mime'] : false);
1251
- } catch (\Exception $e) {
1252
- // well well, don't let this stop us either
1253
- return null;
1254
- }
1255
- }
1256
- return null;
1257
- }
1258
- }
1259
-
1260
- ?><?php
1261
-
1262
- namespace ImageMimeTypeGuesser\Detectors;
1263
-
1264
- class MimeContentType extends AbstractDetector
1265
- {
1266
-
1267
- /**
1268
- * Try to detect mime type of image using *mime_content_type()*.
1269
- *
1270
- * Returns:
1271
- * - mime type (string) (if it is in fact an image, and type could be determined)
1272
- * - false (if it is not an image type that the server knowns about)
1273
- * - null (if nothing can be determined)
1274
- *
1275
- * @param string $filePath The path to the file
1276
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1277
- * false (if it is not an image type that the server knowns about)
1278
- * or null (if nothing can be determined)
1279
- */
1280
- protected function doDetect($filePath)
1281
- {
1282
- // mime_content_type supposedly used to be deprecated, but it seems it isn't anymore
1283
- // it may return false on failure.
1284
- if (function_exists('mime_content_type')) {
1285
- try {
1286
- $result = mime_content_type($filePath);
1287
- if ($result !== false) {
1288
- if (strpos($result, 'image/') === 0) {
1289
- return $result;
1290
- } else {
1291
- return false;
1292
- }
1293
- }
1294
- } catch (\Exception $e) {
1295
- // we are unstoppable!
1296
- }
1297
- }
1298
- return null;
1299
- }
1300
- }
1301
-
1302
- ?><?php
1303
-
1304
- namespace ImageMimeTypeGuesser\Detectors;
1305
-
1306
- use \ImageMimeTypeGuesser\Detectors\AbstractDetector;
1307
-
1308
- class SniffFirstFourBytes extends AbstractDetector
1309
- {
1310
-
1311
- /**
1312
- * Try to detect mime type by sniffing the first four bytes.
1313
- *
1314
- * Credits: Based on the code here: http://phil.lavin.me.uk/2011/12/php-accurately-detecting-the-type-of-a-file/
1315
- *
1316
- * Returns:
1317
- * - mime type (string) (if it is in fact an image, and type could be determined)
1318
- * - false (if it is not an image type that the server knowns about)
1319
- * - null (if nothing can be determined)
1320
- *
1321
- * @param string $filePath The path to the file
1322
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1323
- * false (if it is not an image type that the server knowns about)
1324
- * or null (if nothing can be determined)
1325
- */
1326
- protected function doDetect($filePath)
1327
- {
1328
- // PNG, GIF, JFIF JPEG, EXIF JPEF (respectively)
1329
- $known = [
1330
- '89504E47' => 'image/png',
1331
- '47494638' => 'image/gif',
1332
- 'FFD8FFE0' => 'image/jpeg', // JFIF JPEG
1333
- 'FFD8FFE1' => 'image/jpeg', // EXIF JPEG
1334
- ];
1335
-
1336
- $handle = @fopen($filePath, 'r');
1337
- if ($handle === false) {
1338
- return null;
1339
- }
1340
- $firstFour = @fread($handle, 4);
1341
- if ($firstFour === false) {
1342
- return null;
1343
- }
1344
- $key = strtoupper(bin2hex($firstFour));
1345
- if (isset($known[$key])) {
1346
- return $known[$key];
1347
- }
1348
- }
1349
- }
1350
-
1351
- ?><?php
1352
-
1353
- namespace ImageMimeTypeGuesser\Detectors;
1354
-
1355
- class Stack extends AbstractDetector
1356
- {
1357
- /**
1358
- * Try to detect mime type of image using all available detectors.
1359
- *
1360
- * Returns:
1361
- * - mime type (string) (if it is in fact an image, and type could be determined)
1362
- * - false (if it is not an image type that the server knowns about)
1363
- * - null (if nothing can be determined)
1364
- *
1365
- * @param string $filePath The path to the file
1366
- * @return string|false|null mimetype (if it is an image, and type could be determined),
1367
- * false (if it is not an image type that the server knowns about)
1368
- * or null (if nothing can be determined)
1369
- */
1370
- protected function doDetect($filePath)
1371
- {
1372
- $detectors = [
1373
- 'ExifImageType',
1374
- 'FInfo',
1375
- 'SniffFirstFourBytes',
1376
- 'GetImageSize',
1377
- 'MimeContentType',
1378
- ];
1379
-
1380
- foreach ($detectors as $className) {
1381
- $result = call_user_func(
1382
- array("\\ImageMimeTypeGuesser\\Detectors\\" . $className, 'detect'),
1383
- $filePath
1384
- );
1385
- if (!is_null($result)) {
1386
- return $result;
1387
- }
1388
- }
1389
-
1390
- return null; // undetermined
1391
- }
1392
- }
1393
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/src-build/webp-on-demand-2.inc DELETED
@@ -1,5775 +0,0 @@
1
- <?php
2
- ?><?php
3
-
4
- namespace WebPConvert\Options;
5
-
6
- use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
7
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
8
-
9
- /**
10
- * (base) option class.
11
- *
12
- * @package WebPConvert
13
- * @author Bjørn Rosell <it@rosell.dk>
14
- * @since Class available since Release 2.0.0
15
- */
16
- class Option
17
- {
18
- /** @var string The id of the option */
19
- protected $id;
20
-
21
- /** @var mixed The default value of the option */
22
- protected $defaultValue;
23
-
24
- /** @var mixed The value of the option */
25
- protected $value;
26
-
27
- /** @var boolean Whether the value has been explicitly set */
28
- protected $isExplicitlySet = false;
29
-
30
- /**
31
- * Constructor.
32
- *
33
- * @param string $id id of the option
34
- * @param mixed $defaultValue default value for the option
35
- * @throws InvalidOptionValueException if the default value cannot pass the check
36
- * @throws InvalidOptionTypeException if the default value is wrong type
37
- * @return void
38
- */
39
- public function __construct($id, $defaultValue)
40
- {
41
- $this->id = $id;
42
- $this->defaultValue = $defaultValue;
43
-
44
- // Check that default value is ok
45
- $this->check();
46
- }
47
-
48
- /**
49
- * Get Id.
50
- *
51
- * @return string The id of the option
52
- */
53
- public function getId()
54
- {
55
- return $this->id;
56
- }
57
-
58
- /**
59
- * Get default value.
60
- *
61
- * @return mixed The default value for the option
62
- */
63
- public function getDefaultValue()
64
- {
65
- return $this->defaultValue;
66
- }
67
-
68
-
69
- /**
70
- * Get value, or default value if value has not been explicitly set.
71
- *
72
- * @return mixed The value/default value
73
- */
74
- public function getValue()
75
- {
76
- if (!$this->isExplicitlySet) {
77
- return $this->defaultValue;
78
- } else {
79
- return $this->value;
80
- }
81
- }
82
-
83
- /**
84
- * Get to know if value has been explicitly set.
85
- *
86
- * @return boolean Whether or not the value has been set explicitly
87
- */
88
- public function isValueExplicitlySet()
89
- {
90
- return $this->isExplicitlySet;
91
- }
92
-
93
- /**
94
- * Set value
95
- *
96
- * @param mixed $value The value
97
- * @return void
98
- */
99
- public function setValue($value)
100
- {
101
- $this->isExplicitlySet = true;
102
- $this->value = $value;
103
- }
104
-
105
- /**
106
- * Check if the value is valid.
107
- *
108
- * This base class does no checking, but this method is overridden by most other options.
109
- * @return void
110
- */
111
- public function check()
112
- {
113
- }
114
-
115
- /**
116
- * Helpful function for checking type - used by subclasses.
117
- *
118
- * @param string $expectedType The expected type, ie 'string'
119
- * @throws InvalidOptionTypeException If the type is invalid
120
- * @return void
121
- */
122
- protected function checkType($expectedType)
123
- {
124
- if (gettype($this->getValue()) != $expectedType) {
125
- throw new InvalidOptionTypeException(
126
- 'The "' . $this->id . '" option must be a ' . $expectedType .
127
- ' (you provided a ' . gettype($this->getValue()) . ')'
128
- );
129
- }
130
- }
131
-
132
- public function getValueForPrint()
133
- {
134
- return print_r($this->getValue(), true);
135
- }
136
- }
137
-
138
- ?><?php
139
-
140
- // TODO:
141
- // Read this: https://sourcemaking.com/design_patterns/strategy
142
-
143
- namespace WebPConvert\Convert\Converters;
144
-
145
- use WebPConvert\Helpers\InputValidator;
146
- use WebPConvert\Helpers\MimeType;
147
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
148
- use WebPConvert\Convert\Converters\BaseTraits\AutoQualityTrait;
149
- use WebPConvert\Convert\Converters\BaseTraits\DestinationPreparationTrait;
150
- use WebPConvert\Convert\Converters\BaseTraits\LoggerTrait;
151
- use WebPConvert\Convert\Converters\BaseTraits\OptionsTrait;
152
- use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
153
- use WebPConvert\Exceptions\WebPConvertException;
154
- use WebPConvert\Loggers\BaseLogger;
155
-
156
- /**
157
- * Base for all converter classes.
158
- *
159
- * @package WebPConvert
160
- * @author Bjørn Rosell <it@rosell.dk>
161
- * @since Class available since Release 2.0.0
162
- */
163
- abstract class AbstractConverter
164
- {
165
- use AutoQualityTrait;
166
- use OptionsTrait;
167
- use WarningLoggerTrait;
168
- use DestinationPreparationTrait;
169
- use LoggerTrait;
170
-
171
- /**
172
- * The actual conversion is be done by a concrete converter extending this class.
173
- *
174
- * At the stage this method is called, the abstract converter has taken preparational steps.
175
- * - It has created the destination folder (if neccesary)
176
- * - It has checked the input (valid mime type)
177
- * - It has set up an error handler, mostly in order to catch and log warnings during the doConvert fase
178
- *
179
- * Note: This method is not meant to be called from the outside. Use the static *convert* method for converting
180
- * or, if you wish, create an instance with ::createInstance() and then call ::doConvert()
181
- *
182
- * @throws ConversionFailedException in case conversion failed in an antipiciated way (or subclass)
183
- * @throws \Exception in case conversion failed in an unantipiciated way
184
- */
185
- abstract protected function doActualConvert();
186
-
187
- /**
188
- * Whether or not the converter supports lossless encoding (even for jpegs)
189
- *
190
- * PS: Converters that supports lossless encoding all use the EncodingAutoTrait, which
191
- * overrides this function.
192
- *
193
- * @return boolean Whether the converter supports lossless encoding (even for jpegs).
194
- */
195
- public function supportsLossless()
196
- {
197
- return false;
198
- }
199
-
200
- /** @var string The filename of the image to convert (complete path) */
201
- protected $source;
202
-
203
- /** @var string Where to save the webp (complete path) */
204
- protected $destination;
205
-
206
- /**
207
- * Check basis operationality
208
- *
209
- * Converters may override this method for the purpose of performing basic operationaly checks. It is for
210
- * running general operation checks for a conversion method.
211
- * If some requirement is not met, it should throw a ConverterNotOperationalException (or subtype)
212
- *
213
- * The method is called internally right before calling doActualConvert() method.
214
- * - It SHOULD take options into account when relevant. For example, a missing api key for a
215
- * cloud converter should be detected here
216
- * - It should NOT take the actual filename into consideration, as the purpose is *general*
217
- * For that pupose, converters should override checkConvertability
218
- * Also note that doConvert method is allowed to throw ConverterNotOperationalException too.
219
- *
220
- * @return void
221
- */
222
- public function checkOperationality()
223
- {
224
- }
225
-
226
- /**
227
- * Converters may override this for the purpose of performing checks on the concrete file.
228
- *
229
- * This can for example be used for rejecting big uploads in cloud converters or rejecting unsupported
230
- * image types.
231
- *
232
- * @return void
233
- */
234
- public function checkConvertability()
235
- {
236
- }
237
-
238
- /**
239
- * Constructor.
240
- *
241
- * @param string $source path to source file
242
- * @param string $destination path to destination
243
- * @param array $options (optional) options for conversion
244
- * @param BaseLogger $logger (optional)
245
- */
246
- public function __construct($source, $destination, $options = [], $logger = null)
247
- {
248
- InputValidator::checkSourceAndDestination($source, $destination);
249
-
250
- $this->source = $source;
251
- $this->destination = $destination;
252
-
253
- $this->setLogger($logger);
254
- $this->setProvidedOptions($options);
255
-
256
- if (!isset($this->options['_skip_input_check'])) {
257
- $this->log('WebP Convert 2.0.5', 'italic');
258
- $this->logLn(' ignited.');
259
- $this->logLn('- PHP version: ' . phpversion());
260
- if (isset($_SERVER['SERVER_SOFTWARE'])) {
261
- $this->logLn('- Server software: ' . $_SERVER['SERVER_SOFTWARE']);
262
- }
263
- $this->logLn('');
264
- $this->logLn(self::getConverterDisplayName() . ' converter ignited');
265
- }
266
- }
267
-
268
- /**
269
- * Get source.
270
- *
271
- * @return string The source.
272
- */
273
- public function getSource()
274
- {
275
- return $this->source;
276
- }
277
-
278
- /**
279
- * Get destination.
280
- *
281
- * @return string The destination.
282
- */
283
- public function getDestination()
284
- {
285
- return $this->destination;
286
- }
287
-
288
- /**
289
- * Set destination.
290
- *
291
- * @param string $destination path to destination
292
- * @return string The destination.
293
- */
294
- public function setDestination($destination)
295
- {
296
- $this->destination = $destination;
297
- }
298
-
299
-
300
- /**
301
- * Get converter name for display (defaults to the class name (short)).
302
- *
303
- * Converters can override this.
304
- *
305
- * @return string A display name, ie "Gd"
306
- */
307
- protected static function getConverterDisplayName()
308
- {
309
- // https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name/25308464
310
- return substr(strrchr('\\' . static::class, '\\'), 1);
311
- }
312
-
313
-
314
- /**
315
- * Get converter id (defaults to the class name lowercased)
316
- *
317
- * Converters can override this.
318
- *
319
- * @return string A display name, ie "Gd"
320
- */
321
- protected static function getConverterId()
322
- {
323
- return strtolower(self::getConverterDisplayName());
324
- }
325
-
326
-
327
- /**
328
- * Create an instance of this class
329
- *
330
- * @param string $source The path to the file to convert
331
- * @param string $destination The path to save the converted file to
332
- * @param array $options (optional)
333
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
334
- *
335
- * @return static
336
- */
337
- public static function createInstance($source, $destination, $options = [], $logger = null)
338
- {
339
-
340
- return new static($source, $destination, $options, $logger);
341
- }
342
-
343
- protected function logReduction($source, $destination)
344
- {
345
- $sourceSize = filesize($source);
346
- $destSize = filesize($destination);
347
- $this->log(round(($sourceSize - $destSize)/$sourceSize * 100) . '% ');
348
- if ($sourceSize < 10000) {
349
- $this->logLn('(went from ' . strval($sourceSize) . ' bytes to '. strval($destSize) . ' bytes)');
350
- } else {
351
- $this->logLn('(went from ' . round($sourceSize/1024) . ' kb to ' . round($destSize/1024) . ' kb)');
352
- }
353
- }
354
-
355
- /**
356
- * Run conversion.
357
- *
358
- * @return void
359
- */
360
- private function doConvertImplementation()
361
- {
362
- $beginTime = microtime(true);
363
-
364
- $this->activateWarningLogger();
365
-
366
- $this->checkOptions();
367
-
368
- // Prepare destination folder
369
- $this->createWritableDestinationFolder();
370
- $this->removeExistingDestinationIfExists();
371
-
372
- if (!isset($this->options['_skip_input_check'])) {
373
- // Check that a file can be written to destination
374
- $this->checkDestinationWritable();
375
- }
376
-
377
- $this->checkOperationality();
378
- $this->checkConvertability();
379
-
380
- if ($this->options['log-call-arguments']) {
381
- $this->logOptions();
382
- $this->logLn('');
383
- }
384
-
385
- $this->runActualConvert();
386
-
387
- $source = $this->source;
388
- $destination = $this->destination;
389
-
390
- if (!@file_exists($destination)) {
391
- throw new ConversionFailedException('Destination file is not there: ' . $destination);
392
- } elseif (@filesize($destination) === 0) {
393
- unlink($destination);
394
- throw new ConversionFailedException('Destination file was completely empty');
395
- } else {
396
- if (!isset($this->options['_suppress_success_message'])) {
397
- $this->ln();
398
- $this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
399
-
400
- $sourceSize = @filesize($source);
401
- if ($sourceSize !== false) {
402
- $this->log(', reducing file size with ');
403
- $this->logReduction($source, $destination);
404
- }
405
- }
406
- }
407
-
408
- $this->deactivateWarningLogger();
409
- }
410
-
411
- //private function logEx
412
- /**
413
- * Start conversion.
414
- *
415
- * Usually you would rather call the static convert method, but alternatively you can call
416
- * call ::createInstance to get an instance and then ::doConvert().
417
- *
418
- * @return void
419
- */
420
- public function doConvert()
421
- {
422
- try {
423
- //trigger_error('hello', E_USER_ERROR);
424
- $this->doConvertImplementation();
425
- } catch (WebPConvertException $e) {
426
- $this->logLn('');
427
- /*
428
- if (isset($e->description) && ($e->description != '')) {
429
- $this->log('Error: ' . $e->description . '. ', 'bold');
430
- } else {
431
- $this->log('Error: ', 'bold');
432
- }
433
- */
434
- $this->log('Error: ', 'bold');
435
- $this->logLn($e->getMessage(), 'bold');
436
- throw $e;
437
- } catch (\Exception $e) {
438
- $className = get_class($e);
439
-
440
- $classNameParts = explode("\\", $className);
441
- $shortClassName = array_pop($classNameParts);
442
-
443
- $this->logLn('');
444
- $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
445
- $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
446
- //$this->logLn('Exception class: ' . $className);
447
-
448
- $this->logLn('Trace:');
449
- foreach ($e->getTrace() as $trace) {
450
- //$this->logLn(print_r($trace, true));
451
- if (isset($trace['file']) && isset($trace['line'])) {
452
- $this->logLn(
453
- $trace['file'] . ':' . $trace['line']
454
- );
455
- }
456
- }
457
- throw $e;
458
- } /*catch (\Error $e) {
459
- $this->logLn('ERROR');
460
- }*/
461
- }
462
-
463
- /**
464
- * Runs the actual conversion (after setup and checks)
465
- * Simply calls the doActualConvert() of the actual converter.
466
- * However, in the EncodingAutoTrait, this method is overridden to make two conversions
467
- * and select the smallest.
468
- *
469
- * @return void
470
- */
471
- protected function runActualConvert()
472
- {
473
- $this->doActualConvert();
474
- }
475
-
476
- /**
477
- * Convert an image to webp.
478
- *
479
- * @param string $source path to source file
480
- * @param string $destination path to destination
481
- * @param array $options (optional) options for conversion
482
- * @param BaseLogger $logger (optional)
483
- *
484
- * @throws ConversionFailedException in case conversion fails in an antipiciated way
485
- * @throws \Exception in case conversion fails in an unantipiciated way
486
- * @return void
487
- */
488
- public static function convert($source, $destination, $options = [], $logger = null)
489
- {
490
- $c = self::createInstance($source, $destination, $options, $logger);
491
- $c->doConvert();
492
- //echo $instance->id;
493
- }
494
-
495
- /**
496
- * Get mime type for image (best guess).
497
- *
498
- * It falls back to using file extension. If that fails too, false is returned
499
- *
500
- * PS: Is it a security risk to fall back on file extension?
501
- * - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
502
- * hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
503
- *
504
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
505
- * false (if it is not an image type that the server knowns about)
506
- * or null (if nothing can be determined)
507
- */
508
- public function getMimeTypeOfSource()
509
- {
510
- return MimeType::getMimeTypeDetectionResult($this->source);
511
- }
512
- }
513
-
514
- ?><?php
515
-
516
- namespace WebPConvert\Convert\Exceptions;
517
-
518
- use WebPConvert\Exceptions\WebPConvertException;
519
-
520
- /**
521
- * ConversionFailedException is the base exception in the hierarchy for conversion errors.
522
- *
523
- * Exception hierarchy from here:
524
- *
525
- * WebpConvertException
526
- * ConversionFailedException
527
- * ConversionSkippedException
528
- * ConverterNotOperationalException
529
- * InvalidApiKeyException
530
- * SystemRequirementsNotMetException
531
- * FileSystemProblemsException
532
- * CreateDestinationFileException
533
- * CreateDestinationFolderException
534
- * InvalidInputException
535
- * ConverterNotFoundException
536
- * InvalidImageTypeException
537
- * InvalidOptionValueException
538
- * TargetNotFoundException
539
- */
540
- class ConversionFailedException extends WebPConvertException
541
- {
542
- //public $description = 'Conversion failed';
543
- public $description = '';
544
- }
545
-
546
- ?><?php
547
-
548
- namespace WebPConvert\Options;
549
-
550
- use WebPConvert\Options\Option;
551
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
552
-
553
- /**
554
- * Abstract option class
555
- *
556
- * @package WebPConvert
557
- * @author Bjørn Rosell <it@rosell.dk>
558
- * @since Class available since Release 2.0.0
559
- */
560
- class ArrayOption extends Option
561
- {
562
-
563
- public function check()
564
- {
565
- $this->checkType('array');
566
- }
567
-
568
- public function getValueForPrint()
569
- {
570
- if (count($this->getValue()) == 0) {
571
- return '(empty array)';
572
- } else {
573
- return parent::getValueForPrint();
574
- }
575
- }
576
- }
577
-
578
- ?><?php
579
-
580
- namespace WebPConvert\Options;
581
-
582
- use WebPConvert\Options\Option;
583
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
584
-
585
- /**
586
- * Boolean option
587
- *
588
- * @package WebPConvert
589
- * @author Bjørn Rosell <it@rosell.dk>
590
- * @since Class available since Release 2.0.0
591
- */
592
- class BooleanOption extends Option
593
- {
594
-
595
- public function check()
596
- {
597
- $this->checkType('boolean');
598
- }
599
-
600
- public function getValueForPrint()
601
- {
602
- return ($this->getValue() === true ? 'true' : 'false');
603
- }
604
- }
605
-
606
- ?><?php
607
-
608
- namespace WebPConvert\Options;
609
-
610
- use WebPConvert\Options\Option;
611
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
612
-
613
- /**
614
- * Ghost option
615
- *
616
- * @package WebPConvert
617
- * @author Bjørn Rosell <it@rosell.dk>
618
- * @since Class available since Release 2.0.0
619
- */
620
- class GhostOption extends Option
621
- {
622
-
623
- public function getValueForPrint()
624
- {
625
- return '(not defined for this converter)';
626
- }
627
- }
628
-
629
- ?><?php
630
-
631
- namespace WebPConvert\Options;
632
-
633
- use WebPConvert\Options\Option;
634
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
635
-
636
- /**
637
- * Abstract option class
638
- *
639
- * @package WebPConvert
640
- * @author Bjørn Rosell <it@rosell.dk>
641
- * @since Class available since Release 2.0.0
642
- */
643
- class IntegerOption extends Option
644
- {
645
-
646
- protected $minValue;
647
- protected $maxValue;
648
-
649
- /**
650
- * Constructor.
651
- *
652
- * @param string $id id of the option
653
- * @param integer $defaultValue default value for the option
654
- * @throws InvalidOptionValueException if the default value cannot pass the check
655
- * @return void
656
- */
657
- public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
658
- {
659
- $this->minValue = $minValue;
660
- $this->maxValue = $maxValue;
661
- parent::__construct($id, $defaultValue);
662
- }
663
-
664
- protected function checkMin()
665
- {
666
- if (!is_null($this->minValue) && $this->getValue() < $this->minValue) {
667
- throw new InvalidOptionValueException(
668
- '"' . $this->id . '" option must be set to minimum ' . $this->minValue . '. ' .
669
- 'It was however set to: ' . $this->getValue()
670
- );
671
- }
672
- }
673
-
674
- protected function checkMax()
675
- {
676
- if (!is_null($this->maxValue) && $this->getValue() > $this->maxValue) {
677
- throw new InvalidOptionValueException(
678
- '"' . $this->id . '" option must be set to max ' . $this->maxValue . '. ' .
679
- 'It was however set to: ' . $this->getValue()
680
- );
681
- }
682
- }
683
-
684
- protected function checkMinMax()
685
- {
686
- $this->checkMin();
687
- $this->checkMax();
688
- }
689
-
690
- public function check()
691
- {
692
- $this->checkType('integer');
693
- $this->checkMinMax();
694
- }
695
- }
696
-
697
- ?><?php
698
-
699
- namespace WebPConvert\Options;
700
-
701
- use WebPConvert\Options\IntegerOption;
702
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
703
-
704
- /**
705
- * Abstract option class
706
- *
707
- * @package WebPConvert
708
- * @author Bjørn Rosell <it@rosell.dk>
709
- * @since Class available since Release 2.0.0
710
- */
711
- class IntegerOrNullOption extends IntegerOption
712
- {
713
-
714
- public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
715
- {
716
- parent::__construct($id, $defaultValue, $minValue, $maxValue);
717
- }
718
-
719
- public function check()
720
- {
721
- $this->checkMinMax();
722
-
723
- $valueType = gettype($this->getValue());
724
- if (!in_array($valueType, ['integer', 'NULL'])) {
725
- throw new InvalidOptionValueException(
726
- 'The "' . $this->id . '" option must be either integer or NULL. ' .
727
- 'You however provided a value of type: ' . $valueType
728
- );
729
- }
730
- }
731
-
732
- public function getValueForPrint()
733
- {
734
- if (gettype($this->getValue() == 'NULL')) {
735
- return 'null (not set)';
736
- }
737
- return parent::getValueForPrint();
738
- }
739
- }
740
-
741
- ?><?php
742
-
743
- namespace WebPConvert\Options;
744
-
745
- use WebPConvert\Options\StringOption;
746
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
747
-
748
- /**
749
- * Metadata option. A Comma-separated list ('all', 'none', 'exif', 'icc', 'xmp')
750
- *
751
- * @package WebPConvert
752
- * @author Bjørn Rosell <it@rosell.dk>
753
- * @since Class available since Release 2.0.0
754
- */
755
- class MetadataOption extends StringOption
756
- {
757
-
758
- public function __construct($id, $defaultValue)
759
- {
760
- parent::__construct($id, $defaultValue);
761
- }
762
-
763
- public function check()
764
- {
765
- parent::check();
766
-
767
- $value = $this->getValue();
768
-
769
- if (($value == 'all') || ($value == 'none')) {
770
- return;
771
- }
772
-
773
- foreach (explode(',', $value) as $item) {
774
- if (!in_array($value, ['exif', 'icc', 'xmp'])) {
775
- throw new InvalidOptionValueException(
776
- '"metadata" option must be "all", "none" or a comma-separated list of "exif", "icc" or "xmp". ' .
777
- 'It was however set to: "' . $value . '"'
778
- );
779
- }
780
- }
781
-
782
- //$this->checkType('string');
783
- }
784
- }
785
-
786
- ?><?php
787
-
788
- namespace WebPConvert\Options;
789
-
790
- use WebPConvert\Options\Option;
791
- use WebPConvert\Options\Exceptions\OptionNotFoundException;
792
-
793
- /**
794
- * Handles a collection of options.
795
- *
796
- * @package WebPConvert
797
- * @author Bjørn Rosell <it@rosell.dk>
798
- * @since Class available since Release 2.0.0
799
- */
800
- class Options
801
- {
802
-
803
- /** @var array A map of options, keyed by their id */
804
- private $options = [];
805
-
806
- /**
807
- * Add option.
808
- *
809
- * @param Option $option The option object to add to collection.
810
- * @return void
811
- */
812
- public function addOption($option)
813
- {
814
- $this->options[$option->getId()] = $option;
815
- }
816
-
817
- /**
818
- * Add options.
819
- *
820
- * Conveniently add several options in one call.
821
- *
822
- * @param Option[] ...$options Array of options objects to add
823
- * @return void
824
- */
825
- public function addOptions(...$options)
826
- {
827
- foreach ($options as $option) {
828
- $this->addOption($option);
829
- }
830
- }
831
-
832
- /**
833
- * Set the value of an option.
834
- *
835
- * @param string $id Id of the option
836
- * @param mixed $value Value of the option
837
- * @return void
838
- */
839
- public function setOption($id, $value)
840
- {
841
- if (!isset($this->options[$id])) {
842
- throw new OptionNotFoundException(
843
- 'Could not set option. There is no option called "' . $id . '" in the collection.'
844
- );
845
- }
846
- $option = $this->options[$id];
847
- $option->setValue($value);
848
- }
849
-
850
- /**
851
- * Set option, or create a new, if no such option exists.
852
- *
853
- * @param string $id Id of option to set/create
854
- * @param mixed $value Value of option
855
- * @return void
856
- */
857
- public function setOrCreateOption($id, $value)
858
- {
859
- if (!isset($this->options[$id])) {
860
- $newOption = new GhostOption($id, null);
861
- $newOption->setValue($value);
862
- //$newOption = new Option($id, $value);
863
- $this->addOption($newOption);
864
- } else {
865
- $this->setOption($id, $value);
866
- }
867
- }
868
-
869
- /**
870
- * Get the value of an option in the collection - by id.
871
- *
872
- * @param string $id Id of the option to get
873
- * @throws OptionNotFoundException if the option is not in the collection
874
- * @return mixed The value of the option
875
- */
876
- public function getOption($id)
877
- {
878
- if (!isset($this->options[$id])) {
879
- throw new OptionNotFoundException(
880
- 'There is no option called "' . $id . '" in the collection.'
881
- );
882
- }
883
- $option = $this->options[$id];
884
- return $option->getValue();
885
- }
886
-
887
- /**
888
- * Return map of option objects.
889
- *
890
- * @return array map of option objects
891
- */
892
- public function getOptionsMap()
893
- {
894
- return $this->options;
895
- }
896
-
897
- /**
898
- * Return flat associative array of options.
899
- *
900
- * @return array associative array of options
901
- */
902
- public function getOptions()
903
- {
904
- $values = [];
905
- foreach ($this->options as $id => $option) {
906
- $values[$id] = $option->getValue();
907
- }
908
- return $values;
909
- }
910
-
911
- /**
912
- * Check all options in the collection.
913
- */
914
- public function check()
915
- {
916
- foreach ($this->options as $id => $option) {
917
- $option->check();
918
- }
919
- }
920
- }
921
-
922
- ?><?php
923
-
924
- namespace WebPConvert\Options;
925
-
926
- use WebPConvert\Options\Option;
927
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
928
-
929
- /**
930
- * Quality option.
931
- *
932
- * Quality can be a number between 0-100 or "auto"
933
- *
934
- * @package WebPConvert
935
- * @author Bjørn Rosell <it@rosell.dk>
936
- * @since Class available since Release 2.0.0
937
- */
938
- class QualityOption extends Option
939
- {
940
-
941
- public function __construct($id, $defaultValue)
942
- {
943
- parent::__construct($id, $defaultValue);
944
- }
945
-
946
- public function check()
947
- {
948
- $value = $this->getValue();
949
- if (gettype($value) == 'string') {
950
- if ($value != 'auto') {
951
- throw new InvalidOptionValueException(
952
- 'The "quality" option must be either "auto" or a number between 0-100. ' .
953
- 'A string, different from "auto" was given'
954
- );
955
- }
956
- } elseif (gettype($value) == 'integer') {
957
- if (($value < 0) || ($value > 100)) {
958
- throw new InvalidOptionValueException(
959
- 'The "quality" option must be either "auto" or a number between 0-100. ' .
960
- 'The number you provided (' . strval($value) . ') is out of range.'
961
- );
962
- }
963
- } else {
964
- throw new InvalidOptionValueException(
965
- 'The "quality" option must be either "auto" or an integer. ' .
966
- 'You however provided a value of type: ' . gettype($value)
967
- );
968
- }
969
- }
970
-
971
- public function getValueForPrint()
972
- {
973
- if (gettype($this->getValue()) == 'string') {
974
- return '"' . $this->getValue() . '"';
975
- }
976
- return $this->getValue();
977
- }
978
- }
979
-
980
- ?><?php
981
-
982
- namespace WebPConvert\Options;
983
-
984
- use WebPConvert\Options\StringOption;
985
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
986
-
987
- /**
988
- * Abstract option class
989
- *
990
- * @package WebPConvert
991
- * @author Bjørn Rosell <it@rosell.dk>
992
- * @since Class available since Release 2.0.0
993
- */
994
- class SensitiveArrayOption extends ArrayOption
995
- {
996
-
997
- public function check()
998
- {
999
- parent::check();
1000
- }
1001
-
1002
- public function getValueForPrint()
1003
- {
1004
- if (count($this->getValue()) == 0) {
1005
- return '(empty array)';
1006
- } else {
1007
- return '(array of ' . count($this->getValue()) . ' items)';
1008
- }
1009
- //return '*****';
1010
- }
1011
- }
1012
-
1013
- ?><?php
1014
-
1015
- namespace WebPConvert\Options;
1016
-
1017
- use WebPConvert\Options\StringOption;
1018
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1019
-
1020
- /**
1021
- * Abstract option class
1022
- *
1023
- * @package WebPConvert
1024
- * @author Bjørn Rosell <it@rosell.dk>
1025
- * @since Class available since Release 2.0.0
1026
- */
1027
- class SensitiveStringOption extends StringOption
1028
- {
1029
-
1030
- public function __construct($id, $defaultValue, $allowedValues = null)
1031
- {
1032
- parent::__construct($id, $defaultValue, $allowedValues);
1033
- }
1034
-
1035
- public function check()
1036
- {
1037
- parent::check();
1038
- }
1039
-
1040
- public function getValueForPrint()
1041
- {
1042
- if (strlen($this->getValue()) == 0) {
1043
- return '""';
1044
- }
1045
- return '*****';
1046
- }
1047
- }
1048
-
1049
- ?><?php
1050
-
1051
- namespace WebPConvert\Options;
1052
-
1053
- use WebPConvert\Options\Option;
1054
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1055
-
1056
- /**
1057
- * Abstract option class
1058
- *
1059
- * @package WebPConvert
1060
- * @author Bjørn Rosell <it@rosell.dk>
1061
- * @since Class available since Release 2.0.0
1062
- */
1063
- class StringOption extends Option
1064
- {
1065
-
1066
- public $allowedValues;
1067
-
1068
- public function __construct($id, $defaultValue, $allowedValues = null)
1069
- {
1070
- $this->allowedValues = $allowedValues;
1071
- parent::__construct($id, $defaultValue);
1072
- }
1073
-
1074
- public function check()
1075
- {
1076
- $this->checkType('string');
1077
-
1078
- if (!is_null($this->allowedValues) && (!in_array($this->getValue(), $this->allowedValues))) {
1079
- throw new InvalidOptionValueException(
1080
- '"' . $this->id . '" option must be on of these values: ' .
1081
- '[' . implode(', ', $this->allowedValues) . ']. ' .
1082
- 'It was however set to: "' . $this->getValue() . '"'
1083
- );
1084
- }
1085
- }
1086
-
1087
- public function getValueForPrint()
1088
- {
1089
- return '"' . $this->getValue() . '"';
1090
- }
1091
- }
1092
-
1093
- ?><?php
1094
-
1095
- namespace WebPConvert\Convert\Converters\BaseTraits;
1096
-
1097
- use WebPConvert\Convert\Helpers\JpegQualityDetector;
1098
-
1099
- /**
1100
- * Trait for handling the "quality:auto" option.
1101
- *
1102
- * This trait is only used in the AbstractConverter class. It has been extracted into a
1103
- * trait in order to bundle the methods concerning auto quality.
1104
- *
1105
- * @package WebPConvert
1106
- * @author Bjørn Rosell <it@rosell.dk>
1107
- * @since Class available since Release 2.0.0
1108
- */
1109
- trait AutoQualityTrait
1110
- {
1111
-
1112
- abstract public function logLn($msg, $style = '');
1113
- abstract public function getMimeTypeOfSource();
1114
-
1115
- /** @var boolean Whether the quality option has been processed or not */
1116
- private $processed = false;
1117
-
1118
- /** @var boolean Whether the quality of the source could be detected or not (set upon processing) */
1119
- private $qualityCouldNotBeDetected = false;
1120
-
1121
- /** @var integer The calculated quality (set upon processing - on successful detection) */
1122
- private $calculatedQuality;
1123
-
1124
-
1125
- /**
1126
- * Determine if quality detection is required but failing.
1127
- *
1128
- * It is considered "required" when:
1129
- * - Mime type is "image/jpeg"
1130
- * - Quality is set to "auto"
1131
- *
1132
- * If quality option hasn't been proccessed yet, it is triggered.
1133
- *
1134
- * @return boolean
1135
- */
1136
- public function isQualityDetectionRequiredButFailing()
1137
- {
1138
- $this->processQualityOptionIfNotAlready();
1139
- return $this->qualityCouldNotBeDetected;
1140
- }
1141
-
1142
- /**
1143
- * Get calculated quality.
1144
- *
1145
- * If the "quality" option is a number, that number is returned.
1146
- * If mime type of source is something else than "image/jpeg", the "default-quality" option is returned
1147
- * If quality is "auto" and source is a jpeg image, it will be attempted to detect jpeg quality.
1148
- * In case of failure, the value of the "default-quality" option is returned.
1149
- * In case of success, the detected quality is returned, or the value of the "max-quality" if that is lower.
1150
- *
1151
- * @return int
1152
- */
1153
- public function getCalculatedQuality()
1154
- {
1155
- $this->processQualityOptionIfNotAlready();
1156
- return $this->calculatedQuality;
1157
- }
1158
-
1159
- /**
1160
- * Process the quality option if it is not already processed.
1161
- *
1162
- * @return void
1163
- */
1164
- private function processQualityOptionIfNotAlready()
1165
- {
1166
- if (!$this->processed) {
1167
- $this->processed = true;
1168
- $this->processQualityOption();
1169
- }
1170
- }
1171
-
1172
- /**
1173
- * Process the quality option.
1174
- *
1175
- * Sets the private property "calculatedQuality" according to the description for the getCalculatedQuality
1176
- * function.
1177
- * In case quality detection was attempted and failed, the private property "qualityCouldNotBeDetected" is set
1178
- * to true. This is used by the "isQualityDetectionRequiredButFailing" (and documented there too).
1179
- *
1180
- * @return void
1181
- */
1182
- private function processQualityOption()
1183
- {
1184
- $options = $this->options;
1185
- $source = $this->source;
1186
-
1187
- $q = $options['quality'];
1188
- if ($q == 'auto') {
1189
- if (($this->/** @scrutinizer ignore-call */getMimeTypeOfSource() == 'image/jpeg')) {
1190
- $q = JpegQualityDetector::detectQualityOfJpg($source);
1191
- if (is_null($q)) {
1192
- $q = $options['default-quality'];
1193
- $this->/** @scrutinizer ignore-call */logLn(
1194
- 'Quality of source could not be established (Imagick or GraphicsMagick is required)' .
1195
- ' - Using default instead (' . $options['default-quality'] . ').'
1196
- );
1197
-
1198
- $this->qualityCouldNotBeDetected = true;
1199
- } else {
1200
- if ($q > $options['max-quality']) {
1201
- $this->logLn(
1202
- 'Quality of source is ' . $q . '. ' .
1203
- 'This is higher than max-quality, so using max-quality instead (' .
1204
- $options['max-quality'] . ')'
1205
- );
1206
- } else {
1207
- $this->logLn('Quality set to same as source: ' . $q);
1208
- }
1209
- }
1210
- $q = min($q, $options['max-quality']);
1211
- } else {
1212
- //$q = $options['default-quality'];
1213
- $q = min($options['default-quality'], $options['max-quality']);
1214
- $this->logLn('Quality: ' . $q . '. ');
1215
- }
1216
- } else {
1217
- $this->logLn(
1218
- 'Quality: ' . $q . '. '
1219
- );
1220
- if (($this->getMimeTypeOfSource() == 'image/jpeg')) {
1221
- $this->logLn(
1222
- 'Consider setting quality to "auto" instead. It is generally a better idea'
1223
- );
1224
- }
1225
- }
1226
- $this->calculatedQuality = $q;
1227
- }
1228
- }
1229
-
1230
- ?><?php
1231
-
1232
- namespace WebPConvert\Convert\Converters\BaseTraits;
1233
-
1234
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
1235
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
1236
-
1237
- /**
1238
- * Trait for handling options
1239
- *
1240
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1241
- * trait in order to bundle the methods concerning options.
1242
- *
1243
- * @package WebPConvert
1244
- * @author Bjørn Rosell <it@rosell.dk>
1245
- * @since Class available since Release 2.0.0
1246
- */
1247
- trait DestinationPreparationTrait
1248
- {
1249
-
1250
- abstract public function getDestination();
1251
- abstract public function logLn($msg, $style = '');
1252
-
1253
- /**
1254
- * Create writable folder in provided path (if it does not exist already)
1255
- *
1256
- * @throws CreateDestinationFolderException if folder cannot be removed
1257
- * @return void
1258
- */
1259
- private function createWritableDestinationFolder()
1260
- {
1261
- $destination = $this->getDestination();
1262
-
1263
- $folder = dirname($destination);
1264
- if (!file_exists($folder)) {
1265
- $this->logLn('Destination folder does not exist. Creating folder: ' . $folder);
1266
- // TODO: what if this is outside open basedir?
1267
- // see http://php.net/manual/en/ini.core.php#ini.open-basedir
1268
-
1269
- // Trying to create the given folder (recursively)
1270
- if (!mkdir($folder, 0777, true)) {
1271
- throw new CreateDestinationFolderException(
1272
- 'Failed creating folder. Check the permissions!',
1273
- 'Failed creating folder: ' . $folder . '. Check permissions!'
1274
- );
1275
- }
1276
- }
1277
- }
1278
-
1279
- /**
1280
- * Check that we can write file at destination.
1281
- *
1282
- * It is assumed that the folder already exists (that ::createWritableDestinationFolder() was called first)
1283
- *
1284
- * @throws CreateDestinationFileException if file cannot be created at destination
1285
- * @return void
1286
- */
1287
- private function checkDestinationWritable()
1288
- {
1289
- $destination = $this->getDestination();
1290
- $dirName = dirname($destination);
1291
-
1292
- if (@is_writable($dirName) && @is_executable($dirName)) {
1293
- // all is well
1294
- return;
1295
- }
1296
-
1297
- // The above might fail on Windows, even though dir is writable
1298
- // So, to be absolute sure that we cannot write, we make an actual write test (writing a dummy file)
1299
- // No harm in doing that for non-Windows systems either.
1300
- if (file_put_contents($destination, 'dummy') !== false) {
1301
- // all is well, after all
1302
- unlink($destination);
1303
- return;
1304
- }
1305
-
1306
- throw new CreateDestinationFileException(
1307
- 'Cannot create file: ' . basename($destination) . ' in dir:' . dirname($destination)
1308
- );
1309
- }
1310
-
1311
- /**
1312
- * Remove existing destination.
1313
- *
1314
- * @throws CreateDestinationFileException if file cannot be removed
1315
- * @return void
1316
- */
1317
- private function removeExistingDestinationIfExists()
1318
- {
1319
- $destination = $this->getDestination();
1320
- if (file_exists($destination)) {
1321
- // A file already exists in this folder...
1322
- // We delete it, to make way for a new webp
1323
- if (!unlink($destination)) {
1324
- throw new CreateDestinationFileException(
1325
- 'Existing file cannot be removed: ' . basename($destination)
1326
- );
1327
- }
1328
- }
1329
- }
1330
- }
1331
-
1332
- ?><?php
1333
-
1334
- namespace WebPConvert\Convert\Converters\BaseTraits;
1335
-
1336
- /**
1337
- * Trait for providing logging capabilities.
1338
- *
1339
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1340
- * trait in order to bundle the methods concerning logging.
1341
- *
1342
- * @package WebPConvert
1343
- * @author Bjørn Rosell <it@rosell.dk>
1344
- * @since Class available since Release 2.0.0
1345
- */
1346
- trait LoggerTrait
1347
- {
1348
-
1349
- /** @var \WebPConvert\Loggers\BaseLogger The logger (or null if not set) */
1350
- protected $logger;
1351
-
1352
- /**
1353
- * Set logger
1354
- *
1355
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional) $logger
1356
- * @return void
1357
- */
1358
- public function setLogger($logger = null)
1359
- {
1360
- $this->logger = $logger;
1361
- }
1362
-
1363
- /**
1364
- * Write a line to the logger.
1365
- *
1366
- * @param string $msg The line to write.
1367
- * @param string $style (optional) Ie "italic" or "bold"
1368
- * @return void
1369
- */
1370
- protected function logLn($msg, $style = '')
1371
- {
1372
- if (isset($this->logger)) {
1373
- $this->logger->logLn($msg, $style);
1374
- }
1375
- }
1376
-
1377
- /**
1378
- * New line
1379
- *
1380
- * @return void
1381
- */
1382
- protected function ln()
1383
- {
1384
- if (isset($this->logger)) {
1385
- $this->logger->ln();
1386
- }
1387
- }
1388
-
1389
- /**
1390
- * Write to the logger, without newline
1391
- *
1392
- * @param string $msg What to write.
1393
- * @param string $style (optional) Ie "italic" or "bold"
1394
- * @return void
1395
- */
1396
- protected function log($msg, $style = '')
1397
- {
1398
- if (isset($this->logger)) {
1399
- $this->logger->log($msg, $style);
1400
- }
1401
- }
1402
- }
1403
-
1404
- ?><?php
1405
-
1406
- namespace WebPConvert\Convert\Converters\BaseTraits;
1407
-
1408
- use WebPConvert\Convert\Converters\Stack;
1409
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
1410
- use WebPConvert\Options\Exceptions\InvalidOptionValueException;
1411
- use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
1412
-
1413
- use WebPConvert\Options\ArrayOption;
1414
- use WebPConvert\Options\BooleanOption;
1415
- use WebPConvert\Options\GhostOption;
1416
- use WebPConvert\Options\IntegerOption;
1417
- use WebPConvert\Options\IntegerOrNullOption;
1418
- use WebPConvert\Options\MetadataOption;
1419
- use WebPConvert\Options\Options;
1420
- use WebPConvert\Options\StringOption;
1421
- use WebPConvert\Options\QualityOption;
1422
-
1423
- /**
1424
- * Trait for handling options
1425
- *
1426
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1427
- * trait in order to bundle the methods concerning options.
1428
- *
1429
- * @package WebPConvert
1430
- * @author Bjørn Rosell <it@rosell.dk>
1431
- * @since Class available since Release 2.0.0
1432
- */
1433
- trait OptionsTrait
1434
- {
1435
-
1436
- abstract public function log($msg, $style = '');
1437
- abstract public function logLn($msg, $style = '');
1438
- abstract protected function getMimeTypeOfSource();
1439
-
1440
- /** @var array Provided conversion options */
1441
- public $providedOptions;
1442
-
1443
- /** @var array Calculated conversion options (merge of default options and provided options)*/
1444
- protected $options;
1445
-
1446
- /** @var Options */
1447
- protected $options2;
1448
-
1449
-
1450
- /**
1451
- * Create options.
1452
- *
1453
- * The options created here will be available to all converters.
1454
- * Individual converters may add options by overriding this method.
1455
- *
1456
- * @return void
1457
- */
1458
- protected function createOptions()
1459
- {
1460
- $isPng = ($this->getMimeTypeOfSource() == 'image/png');
1461
-
1462
- $this->options2 = new Options();
1463
- $this->options2->addOptions(
1464
- new IntegerOption('alpha-quality', 85, 0, 100),
1465
- new BooleanOption('auto-filter', false),
1466
- new IntegerOption('default-quality', ($isPng ? 85 : 75), 0, 100),
1467
- new StringOption('encoding', 'auto', ['lossy', 'lossless', 'auto']),
1468
- new BooleanOption('low-memory', false),
1469
- new BooleanOption('log-call-arguments', false),
1470
- new IntegerOption('max-quality', 85, 0, 100),
1471
- new MetadataOption('metadata', 'none'),
1472
- new IntegerOption('method', 6, 0, 6),
1473
- new IntegerOption('near-lossless', 60, 0, 100),
1474
- new StringOption('preset', 'none', ['none', 'default', 'photo', 'picture', 'drawing', 'icon', 'text']),
1475
- new QualityOption('quality', ($isPng ? 85 : 'auto')),
1476
- new IntegerOrNullOption('size-in-percentage', null, 0, 100),
1477
- new BooleanOption('skip', false),
1478
- new BooleanOption('use-nice', false),
1479
- new ArrayOption('jpeg', []),
1480
- new ArrayOption('png', [])
1481
- );
1482
- }
1483
-
1484
- /**
1485
- * Set "provided options" (options provided by the user when calling convert().
1486
- *
1487
- * This also calculates the protected options array, by merging in the default options, merging
1488
- * jpeg and png options and merging prefixed options (such as 'vips-quality').
1489
- * The resulting options array are set in the protected property $this->options and can be
1490
- * retrieved using the public ::getOptions() function.
1491
- *
1492
- * @param array $providedOptions (optional)
1493
- * @return void
1494
- */
1495
- public function setProvidedOptions($providedOptions = [])
1496
- {
1497
- $this->createOptions();
1498
-
1499
- $this->providedOptions = $providedOptions;
1500
-
1501
- if (isset($this->providedOptions['png'])) {
1502
- if ($this->getMimeTypeOfSource() == 'image/png') {
1503
- $this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['png']);
1504
- // $this->logLn(print_r($this->providedOptions, true));
1505
- unset($this->providedOptions['png']);
1506
- }
1507
- }
1508
-
1509
- if (isset($this->providedOptions['jpeg'])) {
1510
- if ($this->getMimeTypeOfSource() == 'image/jpeg') {
1511
- $this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['jpeg']);
1512
- unset($this->providedOptions['jpeg']);
1513
- }
1514
- }
1515
-
1516
- // merge down converter-prefixed options
1517
- $converterId = self::getConverterId();
1518
- $strLen = strlen($converterId);
1519
- foreach ($this->providedOptions as $optionKey => $optionValue) {
1520
- if (substr($optionKey, 0, $strLen + 1) == ($converterId . '-')) {
1521
- $this->providedOptions[substr($optionKey, $strLen + 1)] = $optionValue;
1522
- }
1523
- }
1524
-
1525
- // Create options (Option objects)
1526
- foreach ($this->providedOptions as $optionId => $optionValue) {
1527
- $this->options2->setOrCreateOption($optionId, $optionValue);
1528
- }
1529
- //$this->logLn(print_r($this->options2->getOptions(), true));
1530
- //$this->logLn($this->options2->getOption('hello'));
1531
-
1532
- // Create flat associative array of options
1533
- $this->options = $this->options2->getOptions();
1534
-
1535
- // - Merge $defaultOptions into provided options
1536
- //$this->options = array_merge($this->getDefaultOptions(), $this->providedOptions);
1537
-
1538
- //$this->logOptions();
1539
- }
1540
-
1541
- /**
1542
- * Get the resulting options after merging provided options with default options.
1543
- *
1544
- * Note that the defaults depends on the mime type of the source. For example, the default value for quality
1545
- * is "auto" for jpegs, and 85 for pngs.
1546
- *
1547
- * @return array An associative array of options: ['metadata' => 'none', ...]
1548
- */
1549
- public function getOptions()
1550
- {
1551
- return $this->options;
1552
- }
1553
-
1554
- /**
1555
- * Change an option specifically.
1556
- *
1557
- * This method is probably rarely neeeded. We are using it to change the "encoding" option temporarily
1558
- * in the EncodingAutoTrait.
1559
- *
1560
- * @param string $id Id of option (ie "metadata")
1561
- * @param mixed $value The new value.
1562
- * @return void
1563
- */
1564
- protected function setOption($id, $value)
1565
- {
1566
- $this->options[$id] = $value;
1567
- $this->options2->setOrCreateOption($id, $value);
1568
- }
1569
-
1570
- /**
1571
- * Check options.
1572
- *
1573
- * @throws InvalidOptionTypeException if an option have wrong type
1574
- * @throws InvalidOptionValueException if an option value is out of range
1575
- * @throws ConversionSkippedException if 'skip' option is set to true
1576
- * @return void
1577
- */
1578
- protected function checkOptions()
1579
- {
1580
- $this->options2->check();
1581
-
1582
- if ($this->options['skip']) {
1583
- if (($this->getMimeTypeOfSource() == 'image/png') && isset($this->options['png']['skip'])) {
1584
- throw new ConversionSkippedException(
1585
- 'skipped conversion (configured to do so for PNG)'
1586
- );
1587
- } else {
1588
- throw new ConversionSkippedException(
1589
- 'skipped conversion (configured to do so)'
1590
- );
1591
- }
1592
- }
1593
- }
1594
-
1595
- public function logOptions()
1596
- {
1597
- $this->logLn('');
1598
- $this->logLn('Options:');
1599
- $this->logLn('------------');
1600
- $this->logLn(
1601
- 'The following options have been set explicitly. ' .
1602
- 'Note: it is the resulting options after merging down the "jpeg" and "png" options and any ' .
1603
- 'converter-prefixed options.'
1604
- );
1605
- $this->logLn('- source: ' . $this->source);
1606
- $this->logLn('- destination: ' . $this->destination);
1607
-
1608
- $unsupported = $this->getUnsupportedDefaultOptions();
1609
- //$this->logLn('Unsupported:' . print_r($this->getUnsupportedDefaultOptions(), true));
1610
- $ignored = [];
1611
- $implicit = [];
1612
- foreach ($this->options2->getOptionsMap() as $id => $option) {
1613
- if (($id == 'png') || ($id == 'jpeg')) {
1614
- continue;
1615
- }
1616
- if ($option->isValueExplicitlySet()) {
1617
- if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
1618
- //$this->log(' (note: this option is ignored by this converter)');
1619
- if (($id != '_skip_input_check') && ($id != '_suppress_success_message')) {
1620
- $ignored[] = $option;
1621
- }
1622
- } else {
1623
- $this->log('- ' . $id . ': ');
1624
- $this->log($option->getValueForPrint());
1625
- $this->logLn('');
1626
- }
1627
- } else {
1628
- if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
1629
- } else {
1630
- $implicit[] = $option;
1631
- }
1632
- }
1633
- }
1634
-
1635
- if (count($implicit) > 0) {
1636
- $this->logLn('');
1637
- $this->logLn(
1638
- 'The following options have not been explicitly set, so using the following defaults:'
1639
- );
1640
- foreach ($implicit as $option) {
1641
- $this->log('- ' . $option->getId() . ': ');
1642
- $this->log($option->getValueForPrint());
1643
- $this->logLn('');
1644
- }
1645
- }
1646
- if (count($ignored) > 0) {
1647
- $this->logLn('');
1648
- if ($this instanceof Stack) {
1649
- $this->logLn(
1650
- 'The following options were supplied and are passed on to the converters in the stack:'
1651
- );
1652
- foreach ($ignored as $option) {
1653
- $this->log('- ' . $option->getId() . ': ');
1654
- $this->log($option->getValueForPrint());
1655
- $this->logLn('');
1656
- }
1657
- } else {
1658
- $this->logLn(
1659
- 'The following options were supplied but are ignored because they are not supported by this ' .
1660
- 'converter:'
1661
- );
1662
- foreach ($ignored as $option) {
1663
- $this->logLn('- ' . $option->getId());
1664
- }
1665
- }
1666
- }
1667
- $this->logLn('------------');
1668
- }
1669
-
1670
- // to be overridden by converters
1671
- protected function getUnsupportedDefaultOptions()
1672
- {
1673
- return [];
1674
- }
1675
- }
1676
-
1677
- ?><?php
1678
-
1679
- namespace WebPConvert\Convert\Converters\BaseTraits;
1680
-
1681
- /**
1682
- * Trait for handling warnings (by logging them)
1683
- *
1684
- * This trait is currently only used in the AbstractConverter class. It has been extracted into a
1685
- * trait in order to bundle the methods concerning options.
1686
- *
1687
- * @package WebPConvert
1688
- * @author Bjørn Rosell <it@rosell.dk>
1689
- * @since Class available since Release 2.0.0
1690
- */
1691
- trait WarningLoggerTrait
1692
- {
1693
- abstract protected function logLn($msg, $style = '');
1694
-
1695
- /** @var string|array|null Previous error handler (stored in order to be able pass warnings on) */
1696
- private $previousErrorHandler;
1697
-
1698
- /**
1699
- * Handle warnings and notices during conversion by logging them and passing them on.
1700
- *
1701
- * The function is a callback used with "set_error_handler".
1702
- * It is declared public because it needs to be accessible from the point where the warning happened.
1703
- *
1704
- * @param integer $errno
1705
- * @param string $errstr
1706
- * @param string $errfile
1707
- * @param integer $errline
1708
- *
1709
- * @return false|null
1710
- */
1711
- public function warningHandler($errno, $errstr, $errfile, $errline)
1712
- {
1713
- /*
1714
- We do NOT do the following (even though it is generally recommended):
1715
-
1716
- if (!(error_reporting() & $errno)) {
1717
- // This error code is not included in error_reporting, so let it fall
1718
- // through to the standard PHP error handler
1719
- return false;
1720
- }
1721
-
1722
- - Because we want to log all warnings and errors (also the ones that was suppressed with @)
1723
- https://secure.php.net/manual/en/language.operators.errorcontrol.php
1724
- */
1725
-
1726
- $errorTypes = [
1727
- E_WARNING => "Warning",
1728
- E_NOTICE => "Notice",
1729
- E_STRICT => "Strict Notice",
1730
- E_DEPRECATED => "Deprecated",
1731
- E_USER_DEPRECATED => "User Deprecated",
1732
-
1733
- /*
1734
- The following can never be catched by a custom error handler:
1735
- E_PARSE, E_ERROR, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
1736
-
1737
- We do do not currently trigger the following:
1738
- E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
1739
-
1740
- But we may want to do that at some point, like this:
1741
- trigger_error('Your version of Gd is very old', E_USER_WARNING);
1742
- in that case, remember to add them to this array
1743
- */
1744
- ];
1745
-
1746
- if (isset($errorTypes[$errno])) {
1747
- $errType = $errorTypes[$errno];
1748
- } else {
1749
- $errType = "Unknown error/warning/notice ($errno)";
1750
- }
1751
-
1752
- $msg = $errType . ': ' . $errstr . ' in ' . $errfile . ', line ' . $errline . ', PHP ' . PHP_VERSION .
1753
- ' (' . PHP_OS . ')';
1754
- $this->logLn('');
1755
- $this->logLn($msg, 'italic');
1756
- $this->logLn('');
1757
-
1758
- //echo 'previously defined handler:' . print_r($this->previousErrorHandler, true);
1759
-
1760
- if (!is_null($this->previousErrorHandler)) {
1761
- return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline);
1762
- } else {
1763
- return false;
1764
- }
1765
- }
1766
-
1767
- /**
1768
- * Activate warning logger.
1769
- *
1770
- * Sets the error handler and stores the previous so our error handler can bubble up warnings
1771
- *
1772
- * @return void
1773
- */
1774
- protected function activateWarningLogger()
1775
- {
1776
- $this->previousErrorHandler = set_error_handler(
1777
- array($this, "warningHandler"),
1778
- E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
1779
- );
1780
- }
1781
-
1782
- /**
1783
- * Deactivate warning logger.
1784
- *
1785
- * Restores the previous error handler.
1786
- *
1787
- * @return void
1788
- */
1789
- protected function deactivateWarningLogger()
1790
- {
1791
- restore_error_handler();
1792
- }
1793
- }
1794
-
1795
- ?><?php
1796
-
1797
- namespace WebPConvert\Convert\Converters\ConverterTraits;
1798
-
1799
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
1800
- use WebPConvert\Convert\Converters\AbstractConverter;
1801
- use WebPConvert\Convert\Helpers\PhpIniSizes;
1802
-
1803
- /**
1804
- * Trait for converters that works by uploading to a cloud service.
1805
- *
1806
- * The trait adds a method for checking against upload limits.
1807
- *
1808
- * @package WebPConvert
1809
- * @author Bjørn Rosell <it@rosell.dk>
1810
- * @since Class available since Release 2.0.0
1811
- */
1812
- trait CloudConverterTrait
1813
- {
1814
-
1815
- /**
1816
- * Test that filesize is below "upload_max_filesize" and "post_max_size" values in php.ini.
1817
- *
1818
- * @param string $iniSettingId Id of ini setting (ie "upload_max_filesize")
1819
- *
1820
- * @throws ConversionFailedException if filesize is larger than the ini setting
1821
- * @return void
1822
- */
1823
- private function checkFileSizeVsIniSetting($iniSettingId)
1824
- {
1825
- $fileSize = @filesize($this->source);
1826
- if ($fileSize === false) {
1827
- return;
1828
- }
1829
- $sizeInIni = PhpIniSizes::getIniBytes($iniSettingId);
1830
- if ($sizeInIni === false) {
1831
- // Not sure if we should throw an exception here, or not...
1832
- return;
1833
- }
1834
- if ($sizeInIni < $fileSize) {
1835
- throw new ConversionFailedException(
1836
- 'File is larger than your ' . $iniSettingId . ' (set in your php.ini). File size:' .
1837
- round($fileSize/1024) . ' kb. ' .
1838
- $iniSettingId . ' in php.ini: ' . ini_get($iniSettingId) .
1839
- ' (parsed as ' . round($sizeInIni/1024) . ' kb)'
1840
- );
1841
- }
1842
- }
1843
-
1844
- /**
1845
- * Check convertability of cloud converters (that file is not bigger than limits set in php.ini).
1846
- *
1847
- * Performs the same as ::Convertability(). It is here so converters that overrides the
1848
- * ::Convertability() still has a chance to do the checks.
1849
- *
1850
- * @throws ConversionFailedException if filesize is larger than "upload_max_filesize" or "post_max_size"
1851
- * @return void
1852
- */
1853
- public function checkConvertabilityCloudConverterTrait()
1854
- {
1855
- $this->checkFileSizeVsIniSetting('upload_max_filesize');
1856
- $this->checkFileSizeVsIniSetting('post_max_size');
1857
- }
1858
-
1859
- /**
1860
- * Check convertability of cloud converters (file upload limits).
1861
- */
1862
- public function checkConvertability()
1863
- {
1864
- $this->checkConvertabilityCloudConverterTrait();
1865
- }
1866
- }
1867
-
1868
- ?><?php
1869
-
1870
- namespace WebPConvert\Convert\Converters\ConverterTraits;
1871
-
1872
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
1873
- use WebPConvert\Convert\Converters\AbstractConverter;
1874
-
1875
- /**
1876
- * Trait for converters that works by uploading to a cloud service.
1877
- *
1878
- * The trait adds a method for checking against upload limits.
1879
- *
1880
- * @package WebPConvert
1881
- * @author Bjørn Rosell <it@rosell.dk>
1882
- * @since Class available since Release 2.0.0
1883
- */
1884
- trait CurlTrait
1885
- {
1886
-
1887
- /**
1888
- * Check basis operationality for converters relying on curl.
1889
- *
1890
- * Performs the same as ::checkOperationality(). It is here so converters that overrides the
1891
- * ::checkOperationality() still has a chance to do the checks.
1892
- *
1893
- * @throws SystemRequirementsNotMetException
1894
- * @return void
1895
- */
1896
- public function checkOperationalityForCurlTrait()
1897
- {
1898
- if (!extension_loaded('curl')) {
1899
- throw new SystemRequirementsNotMetException('Required cURL extension is not available.');
1900
- }
1901
-
1902
- if (!function_exists('curl_init')) {
1903
- throw new SystemRequirementsNotMetException('Required url_init() function is not available.');
1904
- }
1905
-
1906
- if (!function_exists('curl_file_create')) {
1907
- throw new SystemRequirementsNotMetException(
1908
- 'Required curl_file_create() function is not available (requires PHP > 5.5).'
1909
- );
1910
- }
1911
- }
1912
-
1913
- /**
1914
- * Check basis operationality for converters relying on curl
1915
- *
1916
- * @throws SystemRequirementsNotMetException
1917
- * @return void
1918
- */
1919
- public function checkOperationality()
1920
- {
1921
- $this->checkOperationalityForCurlTrait();
1922
- }
1923
-
1924
- /**
1925
- * Init curl.
1926
- *
1927
- * @throws SystemRequirementsNotMetException if curl could not be initialized
1928
- * @return resource curl handle
1929
- */
1930
- protected static function initCurl()
1931
- {
1932
- // Get curl handle
1933
- $ch = curl_init();
1934
- if ($ch === false) {
1935
- throw new SystemRequirementsNotMetException('Could not initialise cURL.');
1936
- }
1937
- return $ch;
1938
- }
1939
- }
1940
-
1941
- ?><?php
1942
-
1943
- //namespace WebPConvert\Convert\Converters\BaseTraits;
1944
- namespace WebPConvert\Convert\Converters\ConverterTraits;
1945
-
1946
- /**
1947
- * Trait for converters that supports lossless encoding and thus the "lossless:auto" option.
1948
- *
1949
- * @package WebPConvert
1950
- * @author Bjørn Rosell <it@rosell.dk>
1951
- * @since Class available since Release 2.0.0
1952
- */
1953
- trait EncodingAutoTrait
1954
- {
1955
-
1956
- abstract protected function doActualConvert();
1957
- abstract public function getSource();
1958
- abstract public function getDestination();
1959
- abstract public function setDestination($destination);
1960
- abstract public function getOptions();
1961
- abstract protected function setOption($optionName, $optionValue);
1962
- abstract protected function logLn($msg, $style = '');
1963
- abstract protected function log($msg, $style = '');
1964
- abstract protected function ln();
1965
- abstract protected function logReduction($source, $destination);
1966
-
1967
- public function supportsLossless()
1968
- {
1969
- return true;
1970
- }
1971
-
1972
- /** Default is to not pass "lossless:auto" on, but implement it.
1973
- *
1974
- * The Stack converter passes it on (it does not even use this trait)
1975
- * WPC currently implements it, but this might be configurable in the future.
1976
- *
1977
- */
1978
- public function passOnEncodingAuto()
1979
- {
1980
- return false;
1981
- }
1982
-
1983
- private function convertTwoAndSelectSmallest()
1984
- {
1985
- $destination = $this->getDestination();
1986
- $destinationLossless = $destination . '.lossless.webp';
1987
- $destinationLossy = $destination . '.lossy.webp';
1988
-
1989
- $this->logLn(
1990
- 'Encoding is set to auto - converting to both lossless and lossy and selecting the smallest file'
1991
- );
1992
-
1993
- $this->ln();
1994
- $this->logLn('Converting to lossy');
1995
- $this->setDestination($destinationLossy);
1996
- $this->setOption('encoding', 'lossy');
1997
- $this->doActualConvert();
1998
- $this->log('Reduction: ');
1999
- $this->logReduction($this->getSource(), $destinationLossy);
2000
- $this->ln();
2001
-
2002
- $this->logLn('Converting to lossless');
2003
- $this->setDestination($destinationLossless);
2004
- $this->setOption('encoding', 'lossless');
2005
- $this->doActualConvert();
2006
- $this->log('Reduction: ');
2007
- $this->logReduction($this->getSource(), $destinationLossless);
2008
- $this->ln();
2009
-
2010
- if (filesize($destinationLossless) > filesize($destinationLossy)) {
2011
- $this->logLn('Picking lossy');
2012
- unlink($destinationLossless);
2013
- rename($destinationLossy, $destination);
2014
- } else {
2015
- $this->logLn('Picking lossless');
2016
- unlink($destinationLossy);
2017
- rename($destinationLossless, $destination);
2018
- }
2019
- $this->setDestination($destination);
2020
- $this->setOption('encoding', 'auto');
2021
- }
2022
-
2023
- protected function runActualConvert()
2024
- {
2025
- if (!$this->passOnEncodingAuto() && ($this->getOptions()['encoding'] == 'auto') && $this->supportsLossless()) {
2026
- $this->convertTwoAndSelectSmallest();
2027
- } else {
2028
- $this->doActualConvert();
2029
- }
2030
- }
2031
- }
2032
-
2033
- ?><?php
2034
-
2035
- namespace WebPConvert\Convert\Converters\ConverterTraits;
2036
-
2037
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2038
-
2039
- /**
2040
- * Trait for converters that uses exec()
2041
- *
2042
- * @package WebPConvert
2043
- * @author Bjørn Rosell <it@rosell.dk>
2044
- * @since Class available since Release 2.0.0
2045
- */
2046
- trait ExecTrait
2047
- {
2048
-
2049
- abstract protected function logLn($msg, $style = '');
2050
-
2051
- /**
2052
- * Helper function for examining if "nice" command is available
2053
- *
2054
- * @return boolean true if nice is available
2055
- */
2056
- protected static function hasNiceSupport()
2057
- {
2058
- exec("nice 2>&1", $niceOutput);
2059
-
2060
- if (is_array($niceOutput) && isset($niceOutput[0])) {
2061
- if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
2062
- /*
2063
- * Nice is available - default niceness (+10)
2064
- * https://www.lifewire.com/uses-of-commands-nice-renice-2201087
2065
- * https://www.computerhope.com/unix/unice.htm
2066
- */
2067
-
2068
- return true;
2069
- }
2070
- return false;
2071
- }
2072
- }
2073
-
2074
- /**
2075
- * Logs output from the exec call.
2076
- *
2077
- * @param array $output
2078
- *
2079
- * @return void
2080
- */
2081
- protected function logExecOutput($output)
2082
- {
2083
- if (is_array($output) && count($output) > 0) {
2084
- $this->logLn('');
2085
- $this->logLn('Output:', 'italic');
2086
- foreach ($output as $line) {
2087
- $this->logLn(print_r($line, true));
2088
- }
2089
- $this->logLn('');
2090
- }
2091
- }
2092
-
2093
- /**
2094
- * Check basic operationality of exec converters (that the "exec" function is available)
2095
- *
2096
- * @throws WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException
2097
- * @return void
2098
- */
2099
- public function checkOperationalityExecTrait()
2100
- {
2101
- if (!function_exists('exec')) {
2102
- throw new SystemRequirementsNotMetException('exec() is not enabled.');
2103
- }
2104
- }
2105
- }
2106
-
2107
- ?><?php
2108
-
2109
- namespace WebPConvert\Convert\Converters;
2110
-
2111
- use WebPConvert\Convert\Converters\AbstractConverter;
2112
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
2113
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
2114
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2115
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
2116
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
2117
- use WebPConvert\Options\BooleanOption;
2118
- use WebPConvert\Options\SensitiveStringOption;
2119
- use WebPConvert\Options\StringOption;
2120
-
2121
- /**
2122
- * Convert images to webp by calling cwebp binary.
2123
- *
2124
- * @package WebPConvert
2125
- * @author Bjørn Rosell <it@rosell.dk>
2126
- * @since Class available since Release 2.0.0
2127
- */
2128
- class Cwebp extends AbstractConverter
2129
- {
2130
-
2131
- use EncodingAutoTrait;
2132
- use ExecTrait;
2133
-
2134
- protected function getUnsupportedDefaultOptions()
2135
- {
2136
- return [];
2137
- }
2138
-
2139
- protected function createOptions()
2140
- {
2141
- parent::createOptions();
2142
-
2143
- $this->options2->addOptions(
2144
- new StringOption('command-line-options', ''),
2145
- new SensitiveStringOption('rel-path-to-precompiled-binaries', './Binaries'),
2146
- new BooleanOption('try-common-system-paths', true),
2147
- new BooleanOption('try-supplied-binary-for-os', true)
2148
- );
2149
- }
2150
-
2151
- // System paths to look for cwebp binary
2152
- private static $cwebpDefaultPaths = [
2153
- 'cwebp',
2154
- '/usr/bin/cwebp',
2155
- '/usr/local/bin/cwebp',
2156
- '/usr/gnu/bin/cwebp',
2157
- '/usr/syno/bin/cwebp'
2158
- ];
2159
-
2160
- // OS-specific binaries included in this library, along with hashes
2161
- // If other binaries are going to be added, notice that the first argument is what PHP_OS returns.
2162
- // (possible values, see here: https://stackoverflow.com/questions/738823/possible-values-for-php-os)
2163
- // Got the precompiled binaries here: https://developers.google.com/speed/webp/docs/precompiled
2164
- private static $suppliedBinariesInfo = [
2165
- 'WINNT' => [['cwebp.exe', '49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873']],
2166
- 'Darwin' => [['cwebp-mac12', 'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379']],
2167
- 'SunOS' => [['cwebp-sol', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f']],
2168
- 'FreeBSD' => [['cwebp-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573']],
2169
- 'Linux' => [
2170
- // Dynamically linked executable.
2171
- // It seems it is slightly faster than the statically linked
2172
- ['cwebp-linux-1.0.2-shared', 'd6142e9da2f1cab541de10a31527c597225fff5644e66e31d62bb391c41bfbf4'],
2173
-
2174
- // Statically linked executable
2175
- // It may be that it on some systems works, where the dynamically linked does not (see #196)
2176
- ['cwebp-linux-1.0.2-static', 'a67092563d9de0fbced7dde61b521d60d10c0ad613327a42a81845aefa612b29'],
2177
-
2178
- // Old executable for systems where both of the above fails
2179
- ['cwebp-linux-0.6.1', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568'],
2180
- ]
2181
- ];
2182
-
2183
- public function checkOperationality()
2184
- {
2185
- $this->checkOperationalityExecTrait();
2186
-
2187
- $options = $this->options;
2188
- if (!$options['try-supplied-binary-for-os'] && !$options['try-common-system-paths']) {
2189
- throw new ConverterNotOperationalException(
2190
- 'Configured to neither look for cweb binaries in common system locations, ' .
2191
- 'nor to use one of the supplied precompiled binaries. But these are the only ways ' .
2192
- 'this converter can convert images. No conversion can be made!'
2193
- );
2194
- }
2195
- }
2196
-
2197
- private function executeBinary($binary, $commandOptions, $useNice)
2198
- {
2199
- //$version = $this->detectVersion($binary);
2200
-
2201
- $command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions;
2202
-
2203
- //$logger->logLn('command options:' . $commandOptions);
2204
- $this->logLn('Trying to convert by executing the following command:');
2205
- $this->logLn($command);
2206
- exec($command, $output, $returnCode);
2207
- $this->logExecOutput($output);
2208
- /*
2209
- if ($returnCode == 255) {
2210
- if (isset($output[0])) {
2211
- // Could be an error like 'Error! Cannot open output file' or 'Error! ...preset... '
2212
- $this->logLn(print_r($output[0], true));
2213
- }
2214
- }*/
2215
- //$logger->logLn(self::msgForExitCode($returnCode));
2216
- return intval($returnCode);
2217
- }
2218
-
2219
- /**
2220
- * Use "escapeshellarg()" on all arguments in a commandline string of options
2221
- *
2222
- * For example, passing '-sharpness 5 -crop 10 10 40 40 -low_memory' will result in:
2223
- * [
2224
- * "-sharpness '5'"
2225
- * "-crop '10' '10' '40' '40'"
2226
- * "-low_memory"
2227
- * ]
2228
- * @param string $commandLineOptions string which can contain multiple commandline options
2229
- * @return array Array of command options
2230
- */
2231
- private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
2232
- {
2233
- $cmdOptions = [];
2234
- $arr = explode(' -', ' ' . $commandLineOptions);
2235
- foreach ($arr as $cmdOption) {
2236
- $pos = strpos($cmdOption, ' ');
2237
- $cName = '';
2238
- if (!$pos) {
2239
- $cName = $cmdOption;
2240
- if ($cName == '') {
2241
- continue;
2242
- }
2243
- $cmdOptions[] = '-' . $cName;
2244
- } else {
2245
- $cName = substr($cmdOption, 0, $pos);
2246
- $cValues = substr($cmdOption, $pos + 1);
2247
- $cValuesArr = explode(' ', $cValues);
2248
- foreach ($cValuesArr as &$cArg) {
2249
- $cArg = escapeshellarg($cArg);
2250
- }
2251
- $cValues = implode(' ', $cValuesArr);
2252
- $cmdOptions[] = '-' . $cName . ' ' . $cValues;
2253
- }
2254
- }
2255
- return $cmdOptions;
2256
- }
2257
-
2258
- /**
2259
- * Build command line options for a given version of cwebp.
2260
- *
2261
- * The "-near_lossless" param is not supported on older versions of cwebp, so skip on those.
2262
- *
2263
- * @param string $version Version of cwebp.
2264
- * @return string
2265
- */
2266
- private function createCommandLineOptions($version)
2267
- {
2268
-
2269
- $this->logLn('Creating command line options for version: ' . $version);
2270
-
2271
- // we only need two decimal places for version.
2272
- // convert to number to make it easier to compare
2273
- $version = preg_match('#^\d+\.\d+#', $version, $matches);
2274
- $versionNum = 0;
2275
- if (isset($matches[0])) {
2276
- $versionNum = floatval($matches[0]);
2277
- } else {
2278
- $this->logLn(
2279
- 'Could not extract version number from the following version string: ' . $version,
2280
- 'bold'
2281
- );
2282
- }
2283
-
2284
- //$this->logLn('version:' . strval($versionNum));
2285
-
2286
- $options = $this->options;
2287
-
2288
- $cmdOptions = [];
2289
-
2290
- // Metadata (all, exif, icc, xmp or none (default))
2291
- // Comma-separated list of existing metadata to copy from input to output
2292
- if ($versionNum >= 0.3) {
2293
- $cmdOptions[] = '-metadata ' . $options['metadata'];
2294
- }
2295
-
2296
- // preset. Appears first in the list as recommended in the docs
2297
- if (!is_null($options['preset'])) {
2298
- if ($options['preset'] != 'none') {
2299
- $cmdOptions[] = '-preset ' . $options['preset'];
2300
- }
2301
- }
2302
-
2303
- // Size
2304
- $addedSizeOption = false;
2305
- if (!is_null($options['size-in-percentage'])) {
2306
- $sizeSource = filesize($this->source);
2307
- if ($sizeSource !== false) {
2308
- $targetSize = floor($sizeSource * $options['size-in-percentage'] / 100);
2309
- $cmdOptions[] = '-size ' . $targetSize;
2310
- $addedSizeOption = true;
2311
- }
2312
- }
2313
-
2314
- // quality
2315
- if (!$addedSizeOption) {
2316
- $cmdOptions[] = '-q ' . $this->getCalculatedQuality();
2317
- }
2318
-
2319
- // alpha-quality
2320
- if ($this->options['alpha-quality'] !== 100) {
2321
- $cmdOptions[] = '-alpha_q ' . escapeshellarg($this->options['alpha-quality']);
2322
- }
2323
-
2324
- // Losless PNG conversion
2325
- if ($options['encoding'] == 'lossless') {
2326
- // No need to add -lossless when near-lossless is used (on version >= 0.5)
2327
- if (($options['near-lossless'] === 100) || ($versionNum < 0.5)) {
2328
- $cmdOptions[] = '-lossless';
2329
- }
2330
- }
2331
-
2332
- // Near-lossles
2333
- if ($options['near-lossless'] !== 100) {
2334
- if ($versionNum < 0.5) {
2335
- $this->logLn(
2336
- 'The near-lossless option is not supported on this (rather old) version of cwebp' .
2337
- '- skipping it.',
2338
- 'italic'
2339
- );
2340
- } else {
2341
- // We only let near_lossless have effect when encoding is set to "lossless"
2342
- // otherwise encoding=auto would not work as expected
2343
-
2344
- if ($options['encoding'] == 'lossless') {
2345
- $cmdOptions[] ='-near_lossless ' . $options['near-lossless'];
2346
- } else {
2347
- $this->logLn(
2348
- 'The near-lossless option ignored for lossy'
2349
- );
2350
- }
2351
- }
2352
- }
2353
-
2354
- if ($options['auto-filter'] === true) {
2355
- $cmdOptions[] = '-af';
2356
- }
2357
-
2358
- // Built-in method option
2359
- $cmdOptions[] = '-m ' . strval($options['method']);
2360
-
2361
- // Built-in low memory option
2362
- if ($options['low-memory']) {
2363
- $cmdOptions[] = '-low_memory';
2364
- }
2365
-
2366
- // command-line-options
2367
- if ($options['command-line-options']) {
2368
- array_push(
2369
- $cmdOptions,
2370
- ...self::escapeShellArgOnCommandLineOptions($options['command-line-options'])
2371
- );
2372
- }
2373
-
2374
- // Source file
2375
- $cmdOptions[] = escapeshellarg($this->source);
2376
-
2377
- // Output
2378
- $cmdOptions[] = '-o ' . escapeshellarg($this->destination);
2379
-
2380
- // Redirect stderr to same place as stdout
2381
- // https://www.brianstorti.com/understanding-shell-script-idiom-redirect/
2382
- $cmdOptions[] = '2>&1';
2383
-
2384
- $commandOptions = implode(' ', $cmdOptions);
2385
- //$this->logLn('command line options:' . $commandOptions);
2386
-
2387
- return $commandOptions;
2388
- }
2389
-
2390
- /**
2391
- * Get path for supplied binary for current OS - and validate hash.
2392
- *
2393
- * @return array Array of supplied binaries (which actually exists, and where hash validates)
2394
- */
2395
- private function getSuppliedBinaryPathForOS()
2396
- {
2397
- $this->log('Checking if we have a supplied binary for OS: ' . PHP_OS . '... ');
2398
-
2399
- // Try supplied binary (if available for OS, and hash is correct)
2400
- $options = $this->options;
2401
- if (!isset(self::$suppliedBinariesInfo[PHP_OS])) {
2402
- $this->logLn('No we dont - not for that OS');
2403
- return [];
2404
- }
2405
- $this->logLn('We do.');
2406
-
2407
- $result = [];
2408
- $files = self::$suppliedBinariesInfo[PHP_OS];
2409
- if (count($files) > 0) {
2410
- $this->logLn('We in fact have ' . count($files));
2411
- }
2412
-
2413
- foreach ($files as $i => list($file, $hash)) {
2414
- //$file = $info[0];
2415
- //$hash = $info[1];
2416
-
2417
- $binaryFile = __DIR__ . '/' . $options['rel-path-to-precompiled-binaries'] . '/' . $file;
2418
-
2419
- // Replace "/./" with "/" in path (we could alternatively use realpath)
2420
- //$binaryFile = preg_replace('#\/\.\/#', '/', $binaryFile);
2421
- // The file should exist, but may have been removed manually.
2422
- /*
2423
- if (!file_exists($binaryFile)) {
2424
- $this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
2425
- return false;
2426
- }*/
2427
-
2428
- $realPathResult = realpath($binaryFile);
2429
- if ($realPathResult === false) {
2430
- $this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
2431
- continue;
2432
- }
2433
- $binaryFile = $realPathResult;
2434
-
2435
- // File exists, now generate its hash
2436
- // hash_file() is normally available, but it is not always
2437
- // - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash
2438
- // If available, validate that hash is correct.
2439
-
2440
- if (function_exists('hash_file')) {
2441
- $binaryHash = hash_file('sha256', $binaryFile);
2442
-
2443
- if ($binaryHash != $hash) {
2444
- $this->logLn(
2445
- 'Binary checksum of supplied binary is invalid! ' .
2446
- 'Did you transfer with FTP, but not in binary mode? ' .
2447
- 'File:' . $binaryFile . '. ' .
2448
- 'Expected checksum: ' . $hash . '. ' .
2449
- 'Actual checksum:' . $binaryHash . '.',
2450
- 'bold'
2451
- );
2452
- continue;
2453
- }
2454
- }
2455
- $result[] = $binaryFile;
2456
- }
2457
-
2458
- return $result;
2459
- }
2460
-
2461
- private function discoverBinaries()
2462
- {
2463
- $this->logLn('Locating cwebp binaries');
2464
-
2465
- if (defined('WEBPCONVERT_CWEBP_PATH')) {
2466
- $this->logLn('WEBPCONVERT_CWEBP_PATH was defined, so using that path and ignoring any other');
2467
- //$this->logLn('Value: "' . getenv('WEBPCONVERT_CWEBP_PATH') . '"');
2468
- return [constant('WEBPCONVERT_CWEBP_PATH')];
2469
- }
2470
- if (!empty(getenv('WEBPCONVERT_CWEBP_PATH'))) {
2471
- $this->logLn(
2472
- 'WEBPCONVERT_CWEBP_PATH environment variable was set, so using that path and ignoring any other'
2473
- );
2474
- //$this->logLn('Value: "' . getenv('WEBPCONVERT_CWEBP_PATH') . '"');
2475
- return [getenv('WEBPCONVERT_CWEBP_PATH')];
2476
- }
2477
-
2478
- $binaries = [];
2479
- if ($this->options['try-common-system-paths']) {
2480
- foreach (self::$cwebpDefaultPaths as $binary) {
2481
- if (@file_exists($binary)) {
2482
- $binaries[] = $binary;
2483
- }
2484
- }
2485
- if (count($binaries) == 0) {
2486
- $this->logLn('No cwebp binaries where located in common system locations');
2487
- } else {
2488
- $this->logLn(strval(count($binaries)) . ' cwebp binaries found in common system locations');
2489
- }
2490
- }
2491
- // TODO: exec('whereis cwebp');
2492
- if ($this->options['try-supplied-binary-for-os']) {
2493
- $suppliedBinaries = $this->getSuppliedBinaryPathForOS();
2494
- foreach ($suppliedBinaries as $suppliedBinary) {
2495
- $binaries[] = $suppliedBinary;
2496
- }
2497
- } else {
2498
- $this->logLn('Configured not to try the cwebp binary that comes bundled with webp-convert');
2499
- }
2500
-
2501
- if (count($binaries) == 0) {
2502
- $this->logLn('No cwebp binaries to try!');
2503
- }
2504
- $this->logLn('A total of ' . strval(count($binaries)) . ' cwebp binaries where found');
2505
- return $binaries;
2506
- }
2507
-
2508
- /**
2509
- *
2510
- * @return string|int Version string (ie "1.0.2") OR return code, in case of failure
2511
- */
2512
- private function detectVersion($binary)
2513
- {
2514
- //$this->logLn('Examining binary: ' . $binary);
2515
- $command = $binary . ' -version';
2516
- $this->log('Executing: ' . $command);
2517
- exec($command, $output, $returnCode);
2518
-
2519
- if ($returnCode == 0) {
2520
- //$this->logLn('Success');
2521
- if (isset($output[0])) {
2522
- $this->logLn('. Result: version: ' . $output[0]);
2523
- return $output[0];
2524
- }
2525
- } else {
2526
- $this->logExecOutput($output);
2527
- $this->logLn('');
2528
- if ($returnCode == 127) {
2529
- $this->logLn('Exec failed (the cwebp binary was not found at path: ' . $binary. ')');
2530
- } else {
2531
- $this->logLn(
2532
- 'Exec failed (return code: ' . $returnCode . ')'
2533
- );
2534
- if ($returnCode == 126) {
2535
- $this->logLn(
2536
- 'PS: Return code 126 means "Permission denied". The user that the command was run with does ' .
2537
- 'not have permission to execute that binary.'
2538
- );
2539
- // TODO: further info: shell_exec('whoami')
2540
- }
2541
- }
2542
- return $returnCode;
2543
- }
2544
- }
2545
-
2546
- /**
2547
- * Check versions for binaries, and return array (indexed by the binary, value being the version of the binary).
2548
- *
2549
- * @return array
2550
- */
2551
- private function detectVersions($binaries)
2552
- {
2553
- $binariesWithVersions = [];
2554
- $binariesWithFailCodes = [];
2555
-
2556
- $this->logLn(
2557
- 'Detecting versions of the cwebp binaries found (and verifying that they can be executed in the process)'
2558
- );
2559
- foreach ($binaries as $binary) {
2560
- $versionStringOrFailCode = $this->detectVersion($binary);
2561
- // $this->logLn($binary . ': ' . $versionString);
2562
- if (gettype($versionStringOrFailCode) == 'string') {
2563
- $binariesWithVersions[$binary] = $versionStringOrFailCode;
2564
- } else {
2565
- $binariesWithFailCodes[$binary] = $versionStringOrFailCode;
2566
- }
2567
- }
2568
- return ['detected' => $binariesWithVersions, 'failed' => $binariesWithFailCodes];
2569
- }
2570
-
2571
- /**
2572
- * @return boolean success or not.
2573
- */
2574
- private function tryBinary($binary, $version, $useNice)
2575
- {
2576
-
2577
- //$this->logLn('Trying binary: ' . $binary);
2578
- $commandOptions = $this->createCommandLineOptions($version);
2579
-
2580
- $returnCode = $this->executeBinary($binary, $commandOptions, $useNice);
2581
- if ($returnCode == 0) {
2582
- $this->logLn('Success');
2583
- return true;
2584
- } else {
2585
- $this->logLn(
2586
- 'Exec failed (return code: ' . $returnCode . ')'
2587
- );
2588
- return false;
2589
- }
2590
- }
2591
-
2592
- protected function doActualConvert()
2593
- {
2594
- $binaries = $this->discoverBinaries();
2595
-
2596
- if (count($binaries) == 0) {
2597
- throw new SystemRequirementsNotMetException(
2598
- 'No cwebp binaries located. Check the conversion log for details.'
2599
- );
2600
- }
2601
-
2602
- $versions = $this->detectVersions($binaries);
2603
- if (count($versions['detected']) == 0) {
2604
- //$this->logLn('None of the cwebp files located can be executed.');
2605
- if (count($binaries) == 1) {
2606
- $errorMsg = 'The cwebp file found cannot be can be executed.';
2607
- } else {
2608
- $errorMsg = 'None of the cwebp files located can be executed.';
2609
- }
2610
- $uniqueFailCodes = array_unique(array_values($versions['failed']));
2611
- if (count($uniqueFailCodes) == 1) {
2612
- $errorMsg .= ' ' . (count($binaries) == 1 ? 'It' : 'All') .
2613
- ' failed with return code ' . $uniqueFailCodes[0];
2614
- if ($uniqueFailCodes[0] == 126) {
2615
- $errorMsg .= ' (permission denied)';
2616
- }
2617
- } else {
2618
- $errorMsg .= ' Failure codes : ' . implode(', ', $uniqueFailCodes);
2619
- }
2620
-
2621
- throw new SystemRequirementsNotMetException($errorMsg);
2622
- }
2623
-
2624
- $binaryVersions = $versions['detected'];
2625
-
2626
- if (count($binaries) > 1) {
2627
- $this->logLn(
2628
- 'Trying executing the cwebs found until success. Starting with the ones with highest version number.'
2629
- );
2630
- }
2631
- //$this->logLn('binary versions: ' . print_r($binaryVersions, true));
2632
-
2633
- // Sort binaries so those with highest numbers comes first
2634
- arsort($binaryVersions);
2635
-
2636
- //$this->logLn('binary versions (ordered by version): ' . print_r($binaryVersions, true));
2637
-
2638
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport());
2639
-
2640
- $success = false;
2641
- foreach ($binaryVersions as $binary => $version) {
2642
- if ($this->tryBinary($binary, $version, $useNice)) {
2643
- $success = true;
2644
- break;
2645
- }
2646
- }
2647
-
2648
- // cwebp sets file permissions to 664 but instead ..
2649
- // .. $destination's parent folder's permissions should be used (except executable bits)
2650
- // (or perhaps the current umask instead? https://www.php.net/umask)
2651
-
2652
- if ($success) {
2653
- $destinationParent = dirname($this->destination);
2654
- $fileStatistics = stat($destinationParent);
2655
- if ($fileStatistics !== false) {
2656
- // Apply same permissions as parent folder but strip off the executable bits
2657
- $permissions = $fileStatistics['mode'] & 0000666;
2658
- chmod($this->destination, $permissions);
2659
- }
2660
- } else {
2661
- throw new SystemRequirementsNotMetException('Failed converting. Check the conversion log for details.');
2662
- }
2663
- }
2664
- }
2665
-
2666
- ?><?php
2667
-
2668
- namespace WebPConvert\Convert\Converters;
2669
-
2670
- use WebPConvert\Convert\Converters\AbstractConverter;
2671
- use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
2672
- use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
2673
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
2674
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
2675
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
2676
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
2677
- use WebPConvert\Options\SensitiveStringOption;
2678
-
2679
- /**
2680
- * Convert images to webp using ewww cloud service.
2681
- *
2682
- * @package WebPConvert
2683
- * @author Bjørn Rosell <it@rosell.dk>
2684
- * @since Class available since Release 2.0.0
2685
- */
2686
- class Ewww extends AbstractConverter
2687
- {
2688
- use CloudConverterTrait;
2689
- use CurlTrait;
2690
-
2691
- protected function getUnsupportedDefaultOptions()
2692
- {
2693
- return [
2694
- 'alpha-quality',
2695
- 'auto-filter',
2696
- 'encoding',
2697
- 'low-memory',
2698
- 'use-nice'
2699
- ];
2700
- }
2701
-
2702
- protected function createOptions()
2703
- {
2704
- parent::createOptions();
2705
-
2706
- $this->options2->addOptions(
2707
- new SensitiveStringOption('api-key', '')
2708
- );
2709
- }
2710
-
2711
- /**
2712
- * Get api key from options or environment variable
2713
- *
2714
- * @return string|false api key or false if none is set
2715
- */
2716
- private function getKey()
2717
- {
2718
- if (!empty($this->options['api-key'])) {
2719
- return $this->options['api-key'];
2720
- }
2721
- if (defined('WEBPCONVERT_EWWW_API_KEY')) {
2722
- return constant('WEBPCONVERT_EWWW_API_KEY');
2723
- }
2724
- if (!empty(getenv('WEBPCONVERT_EWWW_API_KEY'))) {
2725
- return getenv('WEBPCONVERT_EWWW_API_KEY');
2726
- }
2727
- return false;
2728
- }
2729
-
2730
-
2731
- /**
2732
- * Check operationality of Ewww converter.
2733
- *
2734
- * @throws SystemRequirementsNotMetException if system requirements are not met (curl)
2735
- * @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
2736
- */
2737
- public function checkOperationality()
2738
- {
2739
-
2740
- $apiKey = $this->getKey();
2741
-
2742
- if ($apiKey === false) {
2743
- if (isset($this->options['key'])) {
2744
- throw new InvalidApiKeyException(
2745
- 'The "key" option has been renamed to "api-key" in webp-convert 2.0. ' .
2746
- 'You must change the configuration accordingly.'
2747
- );
2748
- }
2749
-
2750
- throw new InvalidApiKeyException('Missing API key.');
2751
- }
2752
-
2753
- if (strlen($apiKey) < 20) {
2754
- throw new InvalidApiKeyException(
2755
- 'Api key is invalid. Api keys are supposed to be 32 characters long - ' .
2756
- 'the provided api key is much shorter'
2757
- );
2758
- }
2759
-
2760
- // Check for curl requirements
2761
- $this->checkOperationalityForCurlTrait();
2762
-
2763
- $keyStatus = self::getKeyStatus($apiKey);
2764
- switch ($keyStatus) {
2765
- case 'great':
2766
- break;
2767
- case 'exceeded':
2768
- throw new ConverterNotOperationalException('Quota has exceeded');
2769
- break;
2770
- case 'invalid':
2771
- throw new InvalidApiKeyException('Api key is invalid');
2772
- break;
2773
- }
2774
- }
2775
-
2776
- /*
2777
- public function checkConvertability()
2778
- {
2779
- // check upload limits
2780
- $this->checkConvertabilityCloudConverterTrait();
2781
- }
2782
- */
2783
-
2784
- // Although this method is public, do not call directly.
2785
- // You should rather call the static convert() function, defined in AbstractConverter, which
2786
- // takes care of preparing stuff before calling doConvert, and validating after.
2787
- protected function doActualConvert()
2788
- {
2789
-
2790
- $options = $this->options;
2791
-
2792
- $ch = self::initCurl();
2793
-
2794
- //$this->logLn('api key:' . $this->getKey());
2795
-
2796
- $postData = [
2797
- 'api_key' => $this->getKey(),
2798
- 'webp' => '1',
2799
- 'file' => curl_file_create($this->source),
2800
- 'quality' => $this->getCalculatedQuality(),
2801
- 'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
2802
- ];
2803
-
2804
- curl_setopt_array(
2805
- $ch,
2806
- [
2807
- CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
2808
- CURLOPT_HTTPHEADER => [
2809
- 'User-Agent: WebPConvert',
2810
- 'Accept: image/*'
2811
- ],
2812
- CURLOPT_POSTFIELDS => $postData,
2813
- CURLOPT_BINARYTRANSFER => true,
2814
- CURLOPT_RETURNTRANSFER => true,
2815
- CURLOPT_HEADER => false,
2816
- CURLOPT_SSL_VERIFYPEER => false
2817
- ]
2818
- );
2819
-
2820
- $response = curl_exec($ch);
2821
-
2822
- if (curl_errno($ch)) {
2823
- throw new ConversionFailedException(curl_error($ch));
2824
- }
2825
-
2826
- // The API does not always return images.
2827
- // For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
2828
- // Messages has a http content type of ie 'text/html; charset=UTF-8
2829
- // Images has application/octet-stream.
2830
- // So verify that we got an image back.
2831
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
2832
- //echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
2833
- curl_close($ch);
2834
-
2835
- /* May return this: {"error":"invalid","t":"exceeded"} */
2836
- $responseObj = json_decode($response);
2837
- if (isset($responseObj->error)) {
2838
- //echo 'error:' . $responseObj->error . '<br>';
2839
- //echo $response;
2840
- //self::blacklistKey($key);
2841
- //throw new SystemRequirementsNotMetException('The key is invalid. Blacklisted it!');
2842
- throw new InvalidApiKeyException('The api key is invalid');
2843
- }
2844
-
2845
- throw new ConversionFailedException(
2846
- 'ewww api did not return an image. It could be that the key is invalid. Response: '
2847
- . $response
2848
- );
2849
- }
2850
-
2851
- // Not sure this can happen. So just in case
2852
- if ($response == '') {
2853
- throw new ConversionFailedException('ewww api did not return anything');
2854
- }
2855
-
2856
- $success = file_put_contents($this->destination, $response);
2857
-
2858
- if (!$success) {
2859
- throw new ConversionFailedException('Error saving file');
2860
- }
2861
- }
2862
-
2863
- /**
2864
- * Keep subscription alive by optimizing a jpeg
2865
- * (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
2866
- */
2867
- public static function keepSubscriptionAlive($source, $key)
2868
- {
2869
- try {
2870
- $ch = curl_init();
2871
- } catch (\Exception $e) {
2872
- return 'curl is not installed';
2873
- }
2874
- if ($ch === false) {
2875
- return 'curl could not be initialized';
2876
- }
2877
- curl_setopt_array(
2878
- $ch,
2879
- [
2880
- CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
2881
- CURLOPT_HTTPHEADER => [
2882
- 'User-Agent: WebPConvert',
2883
- 'Accept: image/*'
2884
- ],
2885
- CURLOPT_POSTFIELDS => [
2886
- 'api_key' => $key,
2887
- 'webp' => '0',
2888
- 'file' => curl_file_create($source),
2889
- 'domain' => $_SERVER['HTTP_HOST'],
2890
- 'quality' => 60,
2891
- 'metadata' => 0
2892
- ],
2893
- CURLOPT_BINARYTRANSFER => true,
2894
- CURLOPT_RETURNTRANSFER => true,
2895
- CURLOPT_HEADER => false,
2896
- CURLOPT_SSL_VERIFYPEER => false
2897
- ]
2898
- );
2899
-
2900
- $response = curl_exec($ch);
2901
- if (curl_errno($ch)) {
2902
- return 'curl error' . curl_error($ch);
2903
- }
2904
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
2905
- curl_close($ch);
2906
-
2907
- /* May return this: {"error":"invalid","t":"exceeded"} */
2908
- $responseObj = json_decode($response);
2909
- if (isset($responseObj->error)) {
2910
- return 'The key is invalid';
2911
- }
2912
-
2913
- return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
2914
- }
2915
-
2916
- // Not sure this can happen. So just in case
2917
- if ($response == '') {
2918
- return 'ewww api did not return anything';
2919
- }
2920
-
2921
- return true;
2922
- }
2923
-
2924
- /*
2925
- public static function blacklistKey($key)
2926
- {
2927
- }
2928
-
2929
- public static function isKeyBlacklisted($key)
2930
- {
2931
- }*/
2932
-
2933
- /**
2934
- * Return "great", "exceeded" or "invalid"
2935
- */
2936
- public static function getKeyStatus($key)
2937
- {
2938
- $ch = self::initCurl();
2939
-
2940
- curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
2941
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2942
- curl_setopt(
2943
- $ch,
2944
- CURLOPT_POSTFIELDS,
2945
- [
2946
- 'api_key' => $key
2947
- ]
2948
- );
2949
-
2950
- // The 403 forbidden is avoided with this line.
2951
- curl_setopt(
2952
- $ch,
2953
- CURLOPT_USERAGENT,
2954
- 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
2955
- );
2956
-
2957
- $response = curl_exec($ch);
2958
- // echo $response;
2959
- if (curl_errno($ch)) {
2960
- throw new \Exception(curl_error($ch));
2961
- }
2962
- curl_close($ch);
2963
-
2964
- // Possible responses:
2965
- // “great” = verification successful
2966
- // “exceeded” = indicates a valid key with no remaining image credits.
2967
- // an empty response indicates that the key is not valid
2968
-
2969
- if ($response == '') {
2970
- return 'invalid';
2971
- }
2972
- $responseObj = json_decode($response);
2973
- if (isset($responseObj->error)) {
2974
- if ($responseObj->error == 'invalid') {
2975
- return 'invalid';
2976
- } else {
2977
- throw new \Exception('Ewww returned unexpected error: ' . $response);
2978
- }
2979
- }
2980
- if (!isset($responseObj->status)) {
2981
- throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
2982
- }
2983
- switch ($responseObj->status) {
2984
- case 'great':
2985
- case 'exceeded':
2986
- return $responseObj->status;
2987
- }
2988
- throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
2989
- }
2990
-
2991
- public static function isWorkingKey($key)
2992
- {
2993
- return (self::getKeyStatus($key) == 'great');
2994
- }
2995
-
2996
- public static function isValidKey($key)
2997
- {
2998
- return (self::getKeyStatus($key) != 'invalid');
2999
- }
3000
-
3001
- public static function getQuota($key)
3002
- {
3003
- $ch = self::initCurl();
3004
-
3005
- curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
3006
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
3007
- curl_setopt(
3008
- $ch,
3009
- CURLOPT_POSTFIELDS,
3010
- [
3011
- 'api_key' => $key
3012
- ]
3013
- );
3014
- curl_setopt(
3015
- $ch,
3016
- CURLOPT_USERAGENT,
3017
- 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
3018
- );
3019
-
3020
- $response = curl_exec($ch);
3021
- return $response; // ie -830 23. Seems to return empty for invalid keys
3022
- // or empty
3023
- //echo $response;
3024
- }
3025
- }
3026
-
3027
- ?><?php
3028
-
3029
- namespace WebPConvert\Convert\Converters;
3030
-
3031
- use WebPConvert\Convert\Converters\AbstractConverter;
3032
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3033
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
3034
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3035
-
3036
- /**
3037
- * Convert images to webp using gd extension.
3038
- *
3039
- * @package WebPConvert
3040
- * @author Bjørn Rosell <it@rosell.dk>
3041
- * @since Class available since Release 2.0.0
3042
- */
3043
- class Gd extends AbstractConverter
3044
- {
3045
- public function supportsLossless()
3046
- {
3047
- return false;
3048
- }
3049
-
3050
- protected function getUnsupportedDefaultOptions()
3051
- {
3052
- return [
3053
- 'alpha-quality',
3054
- 'auto-filter',
3055
- 'encoding',
3056
- 'low-memory',
3057
- 'metadata',
3058
- 'method',
3059
- 'near-lossless',
3060
- 'preset',
3061
- 'size-in-percentage',
3062
- 'use-nice'
3063
- ];
3064
- }
3065
-
3066
- private $errorMessageWhileCreating = '';
3067
- private $errorNumberWhileCreating;
3068
-
3069
- /**
3070
- * Check (general) operationality of Gd converter.
3071
- *
3072
- * @throws SystemRequirementsNotMetException if system requirements are not met
3073
- */
3074
- public function checkOperationality()
3075
- {
3076
- if (!extension_loaded('gd')) {
3077
- throw new SystemRequirementsNotMetException('Required Gd extension is not available.');
3078
- }
3079
-
3080
- if (!function_exists('imagewebp')) {
3081
- throw new SystemRequirementsNotMetException(
3082
- 'Gd has been compiled without webp support.'
3083
- );
3084
- }
3085
- }
3086
-
3087
- /**
3088
- * Check if specific file is convertable with current converter / converter settings.
3089
- *
3090
- * @throws SystemRequirementsNotMetException if Gd has been compiled without support for image type
3091
- */
3092
- public function checkConvertability()
3093
- {
3094
- $mimeType = $this->getMimeTypeOfSource();
3095
- switch ($mimeType) {
3096
- case 'image/png':
3097
- if (!function_exists('imagecreatefrompng')) {
3098
- throw new SystemRequirementsNotMetException(
3099
- 'Gd has been compiled without PNG support and can therefore not convert this PNG image.'
3100
- );
3101
- }
3102
- break;
3103
-
3104
- case 'image/jpeg':
3105
- if (!function_exists('imagecreatefromjpeg')) {
3106
- throw new SystemRequirementsNotMetException(
3107
- 'Gd has been compiled without Jpeg support and can therefore not convert this jpeg image.'
3108
- );
3109
- }
3110
- }
3111
- }
3112
-
3113
- /**
3114
- * Find out if all functions exists.
3115
- *
3116
- * @return boolean
3117
- */
3118
- private static function functionsExist($functionNamesArr)
3119
- {
3120
- foreach ($functionNamesArr as $functionName) {
3121
- if (!function_exists($functionName)) {
3122
- return false;
3123
- }
3124
- }
3125
- return true;
3126
- }
3127
-
3128
- /**
3129
- * Try to convert image pallette to true color on older systems that does not have imagepalettetotruecolor().
3130
- *
3131
- * The aim is to function as imagepalettetotruecolor, but for older systems.
3132
- * So, if the image is already rgb, nothing will be done, and true will be returned
3133
- * PS: Got the workaround here: https://secure.php.net/manual/en/function.imagepalettetotruecolor.php
3134
- *
3135
- * @param resource $image
3136
- * @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
3137
- * otherwise FALSE is returned.
3138
- */
3139
- private function makeTrueColorUsingWorkaround(&$image)
3140
- {
3141
- //return $this->makeTrueColor($image);
3142
- /*
3143
- if (function_exists('imageistruecolor') && imageistruecolor($image)) {
3144
- return true;
3145
- }*/
3146
- if (self::functionsExist(['imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
3147
- 'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'])) {
3148
- $dst = imagecreatetruecolor(imagesx($image), imagesy($image));
3149
-
3150
- if ($dst === false) {
3151
- return false;
3152
- }
3153
-
3154
- //prevent blending with default black
3155
- if (imagealphablending($dst, false) === false) {
3156
- return false;
3157
- }
3158
-
3159
- //change the RGB values if you need, but leave alpha at 127
3160
- $transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
3161
-
3162
- if ($transparent === false) {
3163
- return false;
3164
- }
3165
-
3166
- //simpler than flood fill
3167
- if (imagefilledrectangle($dst, 0, 0, imagesx($image), imagesy($image), $transparent) === false) {
3168
- return false;
3169
- }
3170
-
3171
- //restore default blending
3172
- if (imagealphablending($dst, true) === false) {
3173
- return false;
3174
- };
3175
-
3176
- if (imagecopy($dst, $image, 0, 0, 0, 0, imagesx($image), imagesy($image)) === false) {
3177
- return false;
3178
- }
3179
- imagedestroy($image);
3180
-
3181
- $image = $dst;
3182
- return true;
3183
- } else {
3184
- // The necessary methods for converting color palette are not avalaible
3185
- return false;
3186
- }
3187
- }
3188
-
3189
- /**
3190
- * Try to convert image pallette to true color.
3191
- *
3192
- * Try to convert image pallette to true color. If imagepalettetotruecolor() exists, that is used (available from
3193
- * PHP >= 5.5.0). Otherwise using workaround found on the net.
3194
- *
3195
- * @param resource $image
3196
- * @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
3197
- * otherwise FALSE is returned.
3198
- */
3199
- private function makeTrueColor(&$image)
3200
- {
3201
- if (function_exists('imagepalettetotruecolor')) {
3202
- return imagepalettetotruecolor($image);
3203
- } else {
3204
- // imagepalettetotruecolor() is not available on this system. Using custom implementation instead
3205
- return $this->makeTrueColorUsingWorkaround($image);
3206
- }
3207
- }
3208
-
3209
- /**
3210
- * Create Gd image resource from source
3211
- *
3212
- * @throws InvalidInputException if mime type is unsupported or could not be detected
3213
- * @throws ConversionFailedException if imagecreatefrompng or imagecreatefromjpeg fails
3214
- * @return resource $image The created image
3215
- */
3216
- private function createImageResource()
3217
- {
3218
- // In case of failure, image will be false
3219
-
3220
- $mimeType = $this->getMimeTypeOfSource();
3221
-
3222
- if ($mimeType == 'image/png') {
3223
- $image = imagecreatefrompng($this->source);
3224
- if ($image === false) {
3225
- throw new ConversionFailedException(
3226
- 'Gd failed when trying to load/create image (imagecreatefrompng() failed)'
3227
- );
3228
- }
3229
- return $image;
3230
- }
3231
-
3232
- if ($mimeType == 'image/jpeg') {
3233
- $image = imagecreatefromjpeg($this->source);
3234
- if ($image === false) {
3235
- throw new ConversionFailedException(
3236
- 'Gd failed when trying to load/create image (imagecreatefromjpeg() failed)'
3237
- );
3238
- }
3239
- return $image;
3240
- }
3241
-
3242
- /*
3243
- throw new InvalidInputException(
3244
- 'Unsupported mime type:' . $mimeType
3245
- );*/
3246
- }
3247
-
3248
- /**
3249
- * Try to make image resource true color if it is not already.
3250
- *
3251
- * @param resource $image The image to work on
3252
- * @return void
3253
- */
3254
- protected function tryToMakeTrueColorIfNot(&$image)
3255
- {
3256
- $mustMakeTrueColor = false;
3257
- if (function_exists('imageistruecolor')) {
3258
- if (imageistruecolor($image)) {
3259
- $this->logLn('image is true color');
3260
- } else {
3261
- $this->logLn('image is not true color');
3262
- $mustMakeTrueColor = true;
3263
- }
3264
- } else {
3265
- $this->logLn('It can not be determined if image is true color');
3266
- $mustMakeTrueColor = true;
3267
- }
3268
-
3269
- if ($mustMakeTrueColor) {
3270
- $this->logLn('converting color palette to true color');
3271
- $success = $this->makeTrueColor($image);
3272
- if (!$success) {
3273
- $this->logLn(
3274
- 'Warning: FAILED converting color palette to true color. ' .
3275
- 'Continuing, but this does NOT look good.'
3276
- );
3277
- }
3278
- }
3279
- }
3280
-
3281
- /**
3282
- *
3283
- * @param resource $image
3284
- * @return boolean true if alpha blending was set successfully, false otherwise
3285
- */
3286
- protected function trySettingAlphaBlending($image)
3287
- {
3288
- if (function_exists('imagealphablending')) {
3289
- if (!imagealphablending($image, true)) {
3290
- $this->logLn('Warning: imagealphablending() failed');
3291
- return false;
3292
- }
3293
- } else {
3294
- $this->logLn(
3295
- 'Warning: imagealphablending() is not available on your system.' .
3296
- ' Converting PNGs with transparency might fail on some systems'
3297
- );
3298
- return false;
3299
- }
3300
-
3301
- if (function_exists('imagesavealpha')) {
3302
- if (!imagesavealpha($image, true)) {
3303
- $this->logLn('Warning: imagesavealpha() failed');
3304
- return false;
3305
- }
3306
- } else {
3307
- $this->logLn(
3308
- 'Warning: imagesavealpha() is not available on your system. ' .
3309
- 'Converting PNGs with transparency might fail on some systems'
3310
- );
3311
- return false;
3312
- }
3313
- return true;
3314
- }
3315
-
3316
- protected function errorHandlerWhileCreatingWebP($errno, $errstr, $errfile, $errline)
3317
- {
3318
- $this->errorNumberWhileCreating = $errno;
3319
- $this->errorMessageWhileCreating = $errstr . ' in ' . $errfile . ', line ' . $errline .
3320
- ', PHP ' . PHP_VERSION . ' (' . PHP_OS . ')';
3321
- //return false;
3322
- }
3323
-
3324
- /**
3325
- *
3326
- * @param resource $image
3327
- * @return void
3328
- */
3329
- protected function destroyAndRemove($image)
3330
- {
3331
- imagedestroy($image);
3332
- if (file_exists($this->destination)) {
3333
- unlink($this->destination);
3334
- }
3335
- }
3336
-
3337
- /**
3338
- *
3339
- * @param resource $image
3340
- * @return void
3341
- */
3342
- protected function tryConverting($image)
3343
- {
3344
-
3345
- // Danger zone!
3346
- // Using output buffering to generate image.
3347
- // In this zone, Do NOT do anything that might produce unwanted output
3348
- // Do NOT call $this->logLn
3349
- // --------------------------------- (start of danger zone)
3350
-
3351
- $addedZeroPadding = false;
3352
- set_error_handler(array($this, "errorHandlerWhileCreatingWebP"));
3353
-
3354
- // This line may trigger log, so we need to do it BEFORE ob_start() !
3355
- $q = $this->getCalculatedQuality();
3356
-
3357
- ob_start();
3358
-
3359
- //$success = imagewebp($image, $this->destination, $q);
3360
- $success = imagewebp($image, null, $q);
3361
-
3362
- if (!$success) {
3363
- $this->destroyAndRemove($image);
3364
- ob_end_clean();
3365
- restore_error_handler();
3366
- throw new ConversionFailedException(
3367
- 'Failed creating image. Call to imagewebp() failed.',
3368
- $this->errorMessageWhileCreating
3369
- );
3370
- }
3371
-
3372
-
3373
- // The following hack solves an `imagewebp` bug
3374
- // See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
3375
- if (ob_get_length() % 2 == 1) {
3376
- echo "\0";
3377
- $addedZeroPadding = true;
3378
- }
3379
- $output = ob_get_clean();
3380
- restore_error_handler();
3381
-
3382
- if ($output == '') {
3383
- $this->destroyAndRemove($image);
3384
- throw new ConversionFailedException(
3385
- 'Gd failed: imagewebp() returned empty string'
3386
- );
3387
- }
3388
-
3389
- // --------------------------------- (end of danger zone).
3390
-
3391
-
3392
- if ($this->errorMessageWhileCreating != '') {
3393
- switch ($this->errorNumberWhileCreating) {
3394
- case E_WARNING:
3395
- $this->logLn('An warning was produced during conversion: ' . $this->errorMessageWhileCreating);
3396
- break;
3397
- case E_NOTICE:
3398
- $this->logLn('An notice was produced during conversion: ' . $this->errorMessageWhileCreating);
3399
- break;
3400
- default:
3401
- $this->destroyAndRemove($image);
3402
- throw new ConversionFailedException(
3403
- 'An error was produced during conversion',
3404
- $this->errorMessageWhileCreating
3405
- );
3406
- break;
3407
- }
3408
- }
3409
-
3410
- if ($addedZeroPadding) {
3411
- $this->logLn(
3412
- 'Fixing corrupt webp by adding a zero byte ' .
3413
- '(older versions of Gd had a bug, but this hack fixes it)'
3414
- );
3415
- }
3416
-
3417
- $success = file_put_contents($this->destination, $output);
3418
-
3419
- if (!$success) {
3420
- $this->destroyAndRemove($image);
3421
- throw new ConversionFailedException(
3422
- 'Gd failed when trying to save the image. Check file permissions!'
3423
- );
3424
- }
3425
-
3426
- /*
3427
- Previous code was much simpler, but on a system, the hack was not activated (a file with uneven number of bytes
3428
- was created). This is puzzeling. And the old code did not provide any insights.
3429
- Also, perhaps having two subsequent writes to the same file could perhaps cause a problem.
3430
- In the new code, there is only one write.
3431
- However, a bad thing about the new code is that the entire webp file is read into memory. This might cause
3432
- memory overflow with big files.
3433
- Perhaps we should check the filesize of the original and only use the new code when it is smaller than
3434
- memory limit set in PHP by a certain factor.
3435
- Or perhaps only use the new code on older versions of Gd
3436
- https://wordpress.org/support/topic/images-not-seen-on-chrome/#post-11390284
3437
-
3438
- Here is the old code:
3439
-
3440
- $success = imagewebp($image, $this->destination, $this->getCalculatedQuality());
3441
-
3442
- if (!$success) {
3443
- throw new ConversionFailedException(
3444
- 'Gd failed when trying to save the image as webp (call to imagewebp() failed). ' .
3445
- 'It probably failed writing file. Check file permissions!'
3446
- );
3447
- }
3448
-
3449
-
3450
- // This hack solves an `imagewebp` bug
3451
- // See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
3452
- if (filesize($this->destination) % 2 == 1) {
3453
- file_put_contents($this->destination, "\0", FILE_APPEND);
3454
- }
3455
- */
3456
- }
3457
-
3458
- // Although this method is public, do not call directly.
3459
- // You should rather call the static convert() function, defined in AbstractConverter, which
3460
- // takes care of preparing stuff before calling doConvert, and validating after.
3461
- protected function doActualConvert()
3462
- {
3463
-
3464
- $this->logLn('GD Version: ' . gd_info()["GD Version"]);
3465
-
3466
- // Btw: Check out processWebp here:
3467
- // https://github.com/Intervention/image/blob/master/src/Intervention/Image/Gd/Encoder.php
3468
-
3469
- // Create image resource
3470
- $image = $this->createImageResource();
3471
-
3472
- // Try to convert color palette if it is not true color
3473
- $this->tryToMakeTrueColorIfNot($image);
3474
-
3475
-
3476
- if ($this->getMimeTypeOfSource() == 'image/png') {
3477
- // Try to set alpha blending
3478
- $this->trySettingAlphaBlending($image);
3479
- }
3480
-
3481
- // Try to convert it to webp
3482
- $this->tryConverting($image);
3483
-
3484
- // End of story
3485
- imagedestroy($image);
3486
- }
3487
- }
3488
-
3489
- ?><?php
3490
-
3491
- namespace WebPConvert\Convert\Converters;
3492
-
3493
- use WebPConvert\Convert\Converters\AbstractConverter;
3494
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3495
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3496
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3497
-
3498
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3499
-
3500
- /**
3501
- * Convert images to webp using Gmagick extension.
3502
- *
3503
- * @package WebPConvert
3504
- * @author Bjørn Rosell <it@rosell.dk>
3505
- * @since Class available since Release 2.0.0
3506
- */
3507
- class Gmagick extends AbstractConverter
3508
- {
3509
- use EncodingAutoTrait;
3510
-
3511
- protected function getUnsupportedDefaultOptions()
3512
- {
3513
- return [
3514
- 'near-lossless',
3515
- 'preset',
3516
- 'size-in-percentage',
3517
- 'use-nice'
3518
- ];
3519
- }
3520
-
3521
- /**
3522
- * Check (general) operationality of Gmagick converter.
3523
- *
3524
- * Note:
3525
- * It may be that Gd has been compiled without jpeg support or png support.
3526
- * We do not check for this here, as the converter could still be used for the other.
3527
- *
3528
- * @throws SystemRequirementsNotMetException if system requirements are not met
3529
- */
3530
- public function checkOperationality()
3531
- {
3532
- if (!extension_loaded('Gmagick')) {
3533
- throw new SystemRequirementsNotMetException('Required Gmagick extension is not available.');
3534
- }
3535
-
3536
- if (!class_exists('Gmagick')) {
3537
- throw new SystemRequirementsNotMetException(
3538
- 'Gmagick is installed, but not correctly. The class Gmagick is not available'
3539
- );
3540
- }
3541
-
3542
- $im = new \Gmagick($this->source);
3543
-
3544
- if (!in_array('WEBP', $im->queryformats())) {
3545
- throw new SystemRequirementsNotMetException('Gmagick was compiled without WebP support.');
3546
- }
3547
- }
3548
-
3549
- /**
3550
- * Check if specific file is convertable with current converter / converter settings.
3551
- *
3552
- * @throws SystemRequirementsNotMetException if Gmagick does not support image type
3553
- */
3554
- public function checkConvertability()
3555
- {
3556
- $im = new \Gmagick();
3557
- $mimeType = $this->getMimeTypeOfSource();
3558
- switch ($mimeType) {
3559
- case 'image/png':
3560
- if (!in_array('PNG', $im->queryFormats())) {
3561
- throw new SystemRequirementsNotMetException(
3562
- 'Gmagick has been compiled without PNG support and can therefore not convert this PNG image.'
3563
- );
3564
- }
3565
- break;
3566
- case 'image/jpeg':
3567
- if (!in_array('JPEG', $im->queryFormats())) {
3568
- throw new SystemRequirementsNotMetException(
3569
- 'Gmagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
3570
- );
3571
- }
3572
- break;
3573
- }
3574
- }
3575
-
3576
- // Although this method is public, do not call directly.
3577
- // You should rather call the static convert() function, defined in AbstractConverter, which
3578
- // takes care of preparing stuff before calling doConvert, and validating after.
3579
- protected function doActualConvert()
3580
- {
3581
-
3582
- $options = $this->options;
3583
-
3584
- try {
3585
- $im = new \Gmagick($this->source);
3586
- } catch (\Exception $e) {
3587
- throw new ConversionFailedException(
3588
- 'Failed creating Gmagick object of file',
3589
- 'Failed creating Gmagick object of file: "' . $this->source . '" - Gmagick threw an exception.',
3590
- $e
3591
- );
3592
- }
3593
-
3594
- $im->setimageformat('WEBP');
3595
-
3596
- // Not completely sure if setimageoption() has always been there, so lets check first. #169
3597
- if (method_exists($im, 'setimageoption')) {
3598
- // Finally cracked setting webp options.
3599
- // See #167
3600
- // - and https://stackoverflow.com/questions/47294962/how-to-write-lossless-webp-files-with-perlmagick
3601
- $im->setimageoption('webp', 'method', $options['method']);
3602
- $im->setimageoption('webp', 'lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
3603
- $im->setimageoption('webp', 'alpha-quality', $options['alpha-quality']);
3604
-
3605
- if ($options['auto-filter'] === true) {
3606
- $im->setimageoption('webp', 'auto-filter', 'true');
3607
- }
3608
- }
3609
-
3610
- /*
3611
- low-memory seems not to be supported:
3612
- $im->setimageoption('webp', 'low-memory', $options['low-memory'] ? true : false);
3613
- */
3614
-
3615
- if ($options['metadata'] == 'none') {
3616
- // Strip metadata and profiles
3617
- $im->stripImage();
3618
- }
3619
-
3620
- // Ps: Imagick automatically uses same quality as source, when no quality is set
3621
- // This feature is however not present in Gmagick
3622
- // TODO: However, it might be possible after all - see #91
3623
- $im->setcompressionquality($this->getCalculatedQuality());
3624
-
3625
- // We call getImageBlob().
3626
- // That method is undocumented, but it is there!
3627
- // - just like it is in imagick, as pointed out here:
3628
- // https://www.php.net/manual/ru/gmagick.readimageblob.php
3629
-
3630
- /** @scrutinizer ignore-call */
3631
- $imageBlob = $im->getImageBlob();
3632
-
3633
- $success = @file_put_contents($this->destination, $imageBlob);
3634
-
3635
- if (!$success) {
3636
- throw new ConversionFailedException('Failed writing file');
3637
- }
3638
- }
3639
- }
3640
-
3641
- ?><?php
3642
-
3643
- namespace WebPConvert\Convert\Converters;
3644
-
3645
- use WebPConvert\Convert\Converters\AbstractConverter;
3646
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3647
-
3648
- /**
3649
- * Non-functional converter, just here to tell you that it has been renamed.
3650
- *
3651
- * @package WebPConvert
3652
- * @author Bjørn Rosell <it@rosell.dk>
3653
- * @since Class available since Release 2.0.0
3654
- */
3655
- class GmagickBinary extends AbstractConverter
3656
- {
3657
- public function checkOperationality()
3658
- {
3659
- throw new ConversionFailedException(
3660
- 'This converter has changed ID from "gmagickbinary" to "graphicsmagick". You need to change!'
3661
- );
3662
- }
3663
-
3664
- protected function doActualConvert()
3665
- {
3666
- $this->checkOperationality();
3667
- }
3668
- }
3669
-
3670
- ?><?php
3671
-
3672
- namespace WebPConvert\Convert\Converters;
3673
-
3674
- use WebPConvert\Convert\Converters\AbstractConverter;
3675
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3676
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
3677
-
3678
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3679
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3680
-
3681
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3682
-
3683
- /**
3684
- * Convert images to webp by calling gmagick binary (gm).
3685
- *
3686
- * @package WebPConvert
3687
- * @author Bjørn Rosell <it@rosell.dk>
3688
- * @since Class available since Release 2.0.0
3689
- */
3690
- class GraphicsMagick extends AbstractConverter
3691
- {
3692
- use ExecTrait;
3693
- use EncodingAutoTrait;
3694
-
3695
- protected function getUnsupportedDefaultOptions()
3696
- {
3697
- return [
3698
- 'auto-filter',
3699
- 'near-lossless',
3700
- 'preset',
3701
- 'size-in-percentage',
3702
- ];
3703
- }
3704
-
3705
- private function getPath()
3706
- {
3707
- if (defined('WEBPCONVERT_GRAPHICSMAGICK_PATH')) {
3708
- return constant('WEBPCONVERT_GRAPHICSMAGICK_PATH');
3709
- }
3710
- if (!empty(getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH'))) {
3711
- return getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH');
3712
- }
3713
- return 'gm';
3714
- }
3715
-
3716
- public function isInstalled()
3717
- {
3718
- exec($this->getPath() . ' -version', $output, $returnCode);
3719
- return ($returnCode == 0);
3720
- }
3721
-
3722
- public function getVersion()
3723
- {
3724
- exec($this->getPath() . ' -version', $output, $returnCode);
3725
- if (($returnCode == 0) && isset($output[0])) {
3726
- return preg_replace('#http.*#', '', $output[0]);
3727
- }
3728
- return 'unknown';
3729
- }
3730
-
3731
- // Check if webp delegate is installed
3732
- public function isWebPDelegateInstalled()
3733
- {
3734
- exec($this->getPath() . ' -version', $output, $returnCode);
3735
- foreach ($output as $line) {
3736
- if (preg_match('#WebP.*yes#i', $line)) {
3737
- return true;
3738
- }
3739
- }
3740
- return false;
3741
- }
3742
-
3743
- /**
3744
- * Check (general) operationality of imagack converter executable
3745
- *
3746
- * @throws SystemRequirementsNotMetException if system requirements are not met
3747
- */
3748
- public function checkOperationality()
3749
- {
3750
- $this->checkOperationalityExecTrait();
3751
-
3752
- if (!$this->isInstalled()) {
3753
- throw new SystemRequirementsNotMetException('gmagick is not installed');
3754
- }
3755
- if (!$this->isWebPDelegateInstalled()) {
3756
- throw new SystemRequirementsNotMetException('webp delegate missing');
3757
- }
3758
- }
3759
-
3760
- /**
3761
- * Build command line options
3762
- *
3763
- * @return string
3764
- */
3765
- private function createCommandLineOptions()
3766
- {
3767
- $commandArguments = [];
3768
-
3769
- // Unlike imagick binary, it seems gmagick binary uses a fixed
3770
- // quality (75) when quality is omitted
3771
- $commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
3772
-
3773
- // encoding
3774
- if ($this->options['encoding'] == 'lossless') {
3775
- // Btw:
3776
- // I am not sure if we should set "quality" for lossless.
3777
- // Quality should not apply to lossless, but my tests shows that it does in some way for gmagick
3778
- // setting it low, you get bad visual quality and small filesize. Setting it high, you get the opposite
3779
- // Some claim it is a bad idea to set quality, but I'm not so sure.
3780
- // https://stackoverflow.com/questions/4228027/
3781
- // First, I do not just get bigger images when setting quality, as toc777 does.
3782
- // Secondly, the answer is very old and that bad behaviour is probably fixed by now.
3783
- $commandArguments[] = '-define webp:lossless=true';
3784
- } else {
3785
- $commandArguments[] = '-define webp:lossless=false';
3786
- }
3787
-
3788
- if ($this->options['alpha-quality'] !== 100) {
3789
- $commandArguments[] = '-define webp:alpha-quality=' . strval($this->options['alpha-quality']);
3790
- }
3791
-
3792
- if ($this->options['low-memory']) {
3793
- $commandArguments[] = '-define webp:low-memory=true';
3794
- }
3795
-
3796
- if ($this->options['metadata'] == 'none') {
3797
- $commandArguments[] = '-strip';
3798
- }
3799
-
3800
- $commandArguments[] = '-define webp:method=' . $this->options['method'];
3801
-
3802
- $commandArguments[] = escapeshellarg($this->source);
3803
- $commandArguments[] = escapeshellarg('webp:' . $this->destination);
3804
-
3805
- return implode(' ', $commandArguments);
3806
- }
3807
-
3808
- protected function doActualConvert()
3809
- {
3810
- //$this->logLn('Using quality:' . $this->getCalculatedQuality());
3811
-
3812
- $this->logLn('Version: ' . $this->getVersion());
3813
-
3814
- $command = $this->getPath() . ' convert ' . $this->createCommandLineOptions();
3815
-
3816
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport()) ? true : false;
3817
- if ($useNice) {
3818
- $this->logLn('using nice');
3819
- $command = 'nice ' . $command;
3820
- }
3821
- $this->logLn('Executing command: ' . $command);
3822
- exec($command, $output, $returnCode);
3823
-
3824
- $this->logExecOutput($output);
3825
- if ($returnCode == 0) {
3826
- $this->logLn('success');
3827
- } else {
3828
- $this->logLn('return code: ' . $returnCode);
3829
- }
3830
-
3831
- if ($returnCode == 127) {
3832
- throw new SystemRequirementsNotMetException('gmagick is not installed');
3833
- }
3834
- if ($returnCode != 0) {
3835
- $this->logLn('command:' . $command);
3836
- $this->logLn('return code:' . $returnCode);
3837
- $this->logLn('output:' . print_r(implode("\n", $output), true));
3838
- throw new SystemRequirementsNotMetException('The exec call failed');
3839
- }
3840
- }
3841
- }
3842
-
3843
- ?><?php
3844
-
3845
- namespace WebPConvert\Convert\Converters;
3846
-
3847
- use WebPConvert\Convert\Converters\AbstractConverter;
3848
- use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
3849
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
3850
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
3851
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
3852
-
3853
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
3854
-
3855
- /**
3856
- * Convert images to webp by calling imagemagick binary.
3857
- *
3858
- * @package WebPConvert
3859
- * @author Bjørn Rosell <it@rosell.dk>
3860
- * @since Class available since Release 2.0.0
3861
- */
3862
- class ImageMagick extends AbstractConverter
3863
- {
3864
- use ExecTrait;
3865
- use EncodingAutoTrait;
3866
-
3867
- protected function getUnsupportedDefaultOptions()
3868
- {
3869
- return [
3870
- 'near-lossless',
3871
- 'preset',
3872
- 'size-in-percentage',
3873
- ];
3874
- }
3875
-
3876
- // To futher improve this converter, I could check out:
3877
- // https://github.com/Orbitale/ImageMagickPHP
3878
-
3879
- private function getPath()
3880
- {
3881
- if (defined('WEBPCONVERT_IMAGEMAGICK_PATH')) {
3882
- return constant('WEBPCONVERT_IMAGEMAGICK_PATH');
3883
- }
3884
- if (!empty(getenv('WEBPCONVERT_IMAGEMAGICK_PATH'))) {
3885
- return getenv('WEBPCONVERT_IMAGEMAGICK_PATH');
3886
- }
3887
- return 'convert';
3888
- }
3889
-
3890
- private function getVersion()
3891
- {
3892
- exec($this->getPath() . ' -version', $output, $returnCode);
3893
- if (($returnCode == 0) && isset($output[0])) {
3894
- return $output[0];
3895
- } else {
3896
- return 'unknown';
3897
- }
3898
- }
3899
-
3900
- public function isInstalled()
3901
- {
3902
- exec($this->getPath() . ' -version', $output, $returnCode);
3903
- return ($returnCode == 0);
3904
- }
3905
-
3906
- // Check if webp delegate is installed
3907
- public function isWebPDelegateInstalled()
3908
- {
3909
-
3910
- exec('convert -list delegate', $output, $returnCode);
3911
- foreach ($output as $line) {
3912
- if (preg_match('#webp\\s*=#i', $line)) {
3913
- return true;
3914
- }
3915
- }
3916
-
3917
- // try other command
3918
- exec('convert -list configure', $output, $returnCode);
3919
- foreach ($output as $line) {
3920
- if (preg_match('#DELEGATE.*webp#i', $line)) {
3921
- return true;
3922
- }
3923
- }
3924
-
3925
- return false;
3926
-
3927
- // PS, convert -version does not output delegates on travis, so it is not reliable
3928
- }
3929
-
3930
- /**
3931
- * Check (general) operationality of imagack converter executable
3932
- *
3933
- * @throws SystemRequirementsNotMetException if system requirements are not met
3934
- */
3935
- public function checkOperationality()
3936
- {
3937
- $this->checkOperationalityExecTrait();
3938
-
3939
- if (!$this->isInstalled()) {
3940
- throw new SystemRequirementsNotMetException(
3941
- 'imagemagick is not installed (cannot execute: "' . $this->getPath() . '")'
3942
- );
3943
- }
3944
- if (!$this->isWebPDelegateInstalled()) {
3945
- throw new SystemRequirementsNotMetException('webp delegate missing');
3946
- }
3947
- }
3948
-
3949
- /**
3950
- * Build command line options
3951
- *
3952
- * @return string
3953
- */
3954
- private function createCommandLineOptions()
3955
- {
3956
- // PS: Available webp options for imagemagick are documented here:
3957
- // https://imagemagick.org/script/webp.php
3958
-
3959
- $commandArguments = [];
3960
- if ($this->isQualityDetectionRequiredButFailing()) {
3961
- // quality:auto was specified, but could not be determined.
3962
- // we cannot apply the max-quality logic, but we can provide auto quality
3963
- // simply by not specifying the quality option.
3964
- } else {
3965
- $commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
3966
- }
3967
- if ($this->options['encoding'] == 'lossless') {
3968
- $commandArguments[] = '-define webp:lossless=true';
3969
- }
3970
- if ($this->options['low-memory']) {
3971
- $commandArguments[] = '-define webp:low-memory=true';
3972
- }
3973
- if ($this->options['auto-filter'] === true) {
3974
- $commandArguments[] = '-define webp:auto-filter=true';
3975
- }
3976
- if ($this->options['metadata'] == 'none') {
3977
- $commandArguments[] = '-strip';
3978
- }
3979
- if ($this->options['alpha-quality'] !== 100) {
3980
- $commandArguments[] = '-define webp:alpha-quality=' . strval($this->options['alpha-quality']);
3981
- }
3982
-
3983
- // Unfortunately, near-lossless does not seem to be supported.
3984
- // it does have a "preprocessing" option, which may be doing something similar
3985
-
3986
- $commandArguments[] = '-define webp:method=' . $this->options['method'];
3987
-
3988
- $commandArguments[] = escapeshellarg($this->source);
3989
- $commandArguments[] = escapeshellarg('webp:' . $this->destination);
3990
-
3991
- return implode(' ', $commandArguments);
3992
- }
3993
-
3994
- protected function doActualConvert()
3995
- {
3996
- $this->logLn($this->getVersion());
3997
-
3998
- $command = $this->getPath() . ' ' . $this->createCommandLineOptions();
3999
-
4000
- $useNice = (($this->options['use-nice']) && self::hasNiceSupport()) ? true : false;
4001
- if ($useNice) {
4002
- $this->logLn('using nice');
4003
- $command = 'nice ' . $command;
4004
- }
4005
- $this->logLn('Executing command: ' . $command);
4006
- exec($command, $output, $returnCode);
4007
-
4008
- $this->logExecOutput($output);
4009
- if ($returnCode == 0) {
4010
- $this->logLn('success');
4011
- } else {
4012
- $this->logLn('return code: ' . $returnCode);
4013
- }
4014
-
4015
- if ($returnCode == 127) {
4016
- throw new SystemRequirementsNotMetException('imagemagick is not installed');
4017
- }
4018
- if ($returnCode != 0) {
4019
- throw new SystemRequirementsNotMetException('The exec call failed');
4020
- }
4021
- }
4022
- }
4023
-
4024
- ?><?php
4025
-
4026
- namespace WebPConvert\Convert\Converters;
4027
-
4028
- use WebPConvert\Convert\Converters\AbstractConverter;
4029
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4030
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
4031
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4032
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4033
-
4034
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
4035
-
4036
- /**
4037
- * Convert images to webp using Imagick extension.
4038
- *
4039
- * @package WebPConvert
4040
- * @author Bjørn Rosell <it@rosell.dk>
4041
- * @since Class available since Release 2.0.0
4042
- */
4043
- class Imagick extends AbstractConverter
4044
- {
4045
- use EncodingAutoTrait;
4046
-
4047
- protected function getUnsupportedDefaultOptions()
4048
- {
4049
- return [
4050
- 'near-lossless',
4051
- 'preset',
4052
- 'size-in-percentage',
4053
- 'use-nice'
4054
- ];
4055
- }
4056
-
4057
- /**
4058
- * Check operationality of Imagick converter.
4059
- *
4060
- * Note:
4061
- * It may be that Gd has been compiled without jpeg support or png support.
4062
- * We do not check for this here, as the converter could still be used for the other.
4063
- *
4064
- * @throws SystemRequirementsNotMetException if system requirements are not met
4065
- * @return void
4066
- */
4067
- public function checkOperationality()
4068
- {
4069
- if (!extension_loaded('imagick')) {
4070
- throw new SystemRequirementsNotMetException('Required iMagick extension is not available.');
4071
- }
4072
-
4073
- if (!class_exists('\\Imagick')) {
4074
- throw new SystemRequirementsNotMetException(
4075
- 'iMagick is installed, but not correctly. The class Imagick is not available'
4076
- );
4077
- }
4078
-
4079
- $im = new \Imagick();
4080
- if (!in_array('WEBP', $im->queryFormats('WEBP'))) {
4081
- throw new SystemRequirementsNotMetException('iMagick was compiled without WebP support.');
4082
- }
4083
- }
4084
-
4085
- /**
4086
- * Check if specific file is convertable with current converter / converter settings.
4087
- *
4088
- * @throws SystemRequirementsNotMetException if Imagick does not support image type
4089
- */
4090
- public function checkConvertability()
4091
- {
4092
- $im = new \Imagick();
4093
- $mimeType = $this->getMimeTypeOfSource();
4094
- switch ($mimeType) {
4095
- case 'image/png':
4096
- if (!in_array('PNG', $im->queryFormats('PNG'))) {
4097
- throw new SystemRequirementsNotMetException(
4098
- 'Imagick has been compiled without PNG support and can therefore not convert this PNG image.'
4099
- );
4100
- }
4101
- break;
4102
- case 'image/jpeg':
4103
- if (!in_array('JPEG', $im->queryFormats('JPEG'))) {
4104
- throw new SystemRequirementsNotMetException(
4105
- 'Imagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
4106
- );
4107
- }
4108
- break;
4109
- }
4110
- }
4111
-
4112
- /**
4113
- *
4114
- * It may also throw an ImagickException if imagick throws an exception
4115
- * @throws CreateDestinationFileException if imageblob could not be saved to file
4116
- */
4117
- protected function doActualConvert()
4118
- {
4119
- /*
4120
- * More about iMagick's WebP options:
4121
- * - Inspect source code: https://github.com/ImageMagick/ImageMagick/blob/master/coders/webp.c#L559
4122
- * (search for "webp:")
4123
- * - http://www.imagemagick.org/script/webp.php
4124
- * - https://developers.google.com/speed/webp/docs/cwebp
4125
- * - https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
4126
- */
4127
-
4128
- $options = $this->options;
4129
-
4130
- // This might throw - we let it!
4131
- $im = new \Imagick($this->source);
4132
-
4133
- //$im = new \Imagick();
4134
- //$im->pingImage($this->source);
4135
- //$im->readImage($this->source);
4136
-
4137
- $im->setImageFormat('WEBP');
4138
-
4139
- $im->setOption('webp:method', $options['method']);
4140
- $im->setOption('webp:lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
4141
- $im->setOption('webp:low-memory', $options['low-memory'] ? 'true' : 'false');
4142
- $im->setOption('webp:alpha-quality', $options['alpha-quality']);
4143
-
4144
- if ($options['auto-filter'] === true) {
4145
- $im->setOption('webp:auto-filter', 'true');
4146
- }
4147
-
4148
- if ($options['metadata'] == 'none') {
4149
- // Strip metadata and profiles
4150
- $im->stripImage();
4151
- }
4152
-
4153
- if ($this->isQualityDetectionRequiredButFailing()) {
4154
- // Luckily imagick is a big boy, and automatically converts with same quality as
4155
- // source, when the quality isn't set.
4156
- // So we simply do not set quality.
4157
- // This actually kills the max-quality functionality. But I deem that this is more important
4158
- // because setting image quality to something higher than source generates bigger files,
4159
- // but gets you no extra quality. When failing to limit quality, you at least get something
4160
- // out of it
4161
- $this->logLn('Converting without setting quality in order to achieve auto quality');
4162
- } else {
4163
- $im->setImageCompressionQuality($this->getCalculatedQuality());
4164
- }
4165
-
4166
- // https://stackoverflow.com/questions/29171248/php-imagick-jpeg-optimization
4167
- // setImageFormat
4168
-
4169
- // TODO: Read up on
4170
- // https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
4171
- // https://github.com/nwtn/php-respimg
4172
-
4173
- // TODO:
4174
- // Should we set alpha channel for PNG's like suggested here:
4175
- // https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
4176
- // It seems that alpha channel works without... (at least I see completely transparerent pixels)
4177
-
4178
- // We used to use writeImageFile() method. But we now use getImageBlob(). See issue #43
4179
-
4180
- // This might throw - we let it!
4181
- $imageBlob = $im->getImageBlob();
4182
-
4183
- $success = file_put_contents($this->destination, $imageBlob);
4184
-
4185
- if (!$success) {
4186
- throw new CreateDestinationFileException('Failed writing file');
4187
- }
4188
-
4189
- // Btw: check out processWebp() method here:
4190
- // https://github.com/Intervention/image/blob/master/src/Intervention/Image/Imagick/Encoder.php
4191
- }
4192
- }
4193
-
4194
- ?><?php
4195
-
4196
- namespace WebPConvert\Convert\Converters;
4197
-
4198
- use WebPConvert\Convert\Converters\AbstractConverter;
4199
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4200
-
4201
- /**
4202
- * Non-functional converter, just here to tell you that it has been renamed.
4203
- *
4204
- * @package WebPConvert
4205
- * @author Bjørn Rosell <it@rosell.dk>
4206
- * @since Class available since Release 2.0.0
4207
- */
4208
- class ImagickBinary extends AbstractConverter
4209
- {
4210
- public function checkOperationality()
4211
- {
4212
- throw new ConversionFailedException(
4213
- 'This converter has changed ID from "imagickbinary" to "imagemagick". You need to change!'
4214
- );
4215
- }
4216
-
4217
- protected function doActualConvert()
4218
- {
4219
- $this->checkOperationality();
4220
- }
4221
- }
4222
-
4223
- ?><?php
4224
-
4225
- namespace WebPConvert\Convert\Converters;
4226
-
4227
- use WebPConvert\Convert\ConverterFactory;
4228
- use WebPConvert\Convert\Converters\AbstractConverter;
4229
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4230
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
4231
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4232
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
4233
- use WebPConvert\Options\BooleanOption;
4234
- use WebPConvert\Options\ArrayOption;
4235
- use WebPConvert\Options\GhostOption;
4236
- use WebPConvert\Options\SensitiveArrayOption;
4237
-
4238
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
4239
-
4240
- /**
4241
- * Convert images to webp by trying a stack of converters until success.
4242
- *
4243
- * @package WebPConvert
4244
- * @author Bjørn Rosell <it@rosell.dk>
4245
- * @since Class available since Release 2.0.0
4246
- */
4247
- class Stack extends AbstractConverter
4248
- {
4249
-
4250
- protected function getUnsupportedDefaultOptions()
4251
- {
4252
- return [
4253
- 'alpha-quality',
4254
- 'auto-filter',
4255
- 'encoding',
4256
- 'low-memory',
4257
- 'metadata',
4258
- 'method',
4259
- 'near-lossless',
4260
- 'preset',
4261
- 'size-in-percentage',
4262
- 'use-nice',
4263
- 'skip',
4264
- 'default-quality',
4265
- 'quality',
4266
- 'max-quality',
4267
- ];
4268
- }
4269
-
4270
- protected function createOptions()
4271
- {
4272
- parent::createOptions();
4273
-
4274
- $this->options2->addOptions(
4275
- new SensitiveArrayOption('converters', self::getAvailableConverters()),
4276
- new SensitiveArrayOption('converter-options', []),
4277
- new BooleanOption('shuffle', false),
4278
- new ArrayOption('preferred-converters', []),
4279
- new SensitiveArrayOption('extra-converters', [])
4280
- );
4281
- }
4282
-
4283
- /**
4284
- * Get available converters (ids) - ordered by awesomeness.
4285
- *
4286
- * @return array An array of ids of converters that comes with this library
4287
- */
4288
- public static function getAvailableConverters()
4289
- {
4290
- return [
4291
- 'cwebp', 'vips', 'imagick', 'gmagick', 'imagemagick', 'graphicsmagick', 'wpc', 'ewww', 'gd'
4292
- ];
4293
- }
4294
-
4295
- /**
4296
- * Check (general) operationality of imagack converter executable
4297
- *
4298
- * @throws SystemRequirementsNotMetException if system requirements are not met
4299
- */
4300
- public function checkOperationality()
4301
- {
4302
- if (count($this->options['converters']) == 0) {
4303
- throw new ConverterNotOperationalException(
4304
- 'Converter stack is empty! - no converters to try, no conversion can be made!'
4305
- );
4306
- }
4307
-
4308
- // TODO: We should test if all converters are found in order to detect problems early
4309
-
4310
- //$this->logLn('Stack converter ignited');
4311
- }
4312
-
4313
- protected function doActualConvert()
4314
- {
4315
- $options = $this->options;
4316
-
4317
- $beginTimeStack = microtime(true);
4318
-
4319
- $anyRuntimeErrors = false;
4320
-
4321
- $converters = $options['converters'];
4322
- if (count($options['extra-converters']) > 0) {
4323
- $converters = array_merge($converters, $options['extra-converters']);
4324
- /*foreach ($options['extra-converters'] as $extra) {
4325
- $converters[] = $extra;
4326
- }*/
4327
- }
4328
-
4329
- // preferred-converters
4330
- if (count($options['preferred-converters']) > 0) {
4331
- foreach (array_reverse($options['preferred-converters']) as $prioritizedConverter) {
4332
- foreach ($converters as $i => $converter) {
4333
- if (is_array($converter)) {
4334
- $converterId = $converter['converter'];
4335
- } else {
4336
- $converterId = $converter;
4337
- }
4338
- if ($converterId == $prioritizedConverter) {
4339
- unset($converters[$i]);
4340
- array_unshift($converters, $converter);
4341
- break;
4342
- }
4343
- }
4344
- }
4345
- // perhaps write the order to the log? (without options) - but this requires some effort
4346
- }
4347
-
4348
- // shuffle
4349
- if ($options['shuffle']) {
4350
- shuffle($converters);
4351
- }
4352
-
4353
- //$this->logLn(print_r($converters));
4354
- //$options['converters'] = $converters;
4355
- //$defaultConverterOptions = $options;
4356
- $defaultConverterOptions = [];
4357
-
4358
- foreach ($this->options2->getOptionsMap() as $id => $option) {
4359
- if ($option->isValueExplicitlySet() && ! ($option instanceof GhostOption)) {
4360
- //$this->logLn('hi' . $id);
4361
- $defaultConverterOptions[$id] = $option->getValue();
4362
- }
4363
- }
4364
-
4365
- //unset($defaultConverterOptions['converters']);
4366
- //unset($defaultConverterOptions['converter-options']);
4367
- $defaultConverterOptions['_skip_input_check'] = true;
4368
- $defaultConverterOptions['_suppress_success_message'] = true;
4369
- unset($defaultConverterOptions['converters']);
4370
- unset($defaultConverterOptions['extra-converters']);
4371
- unset($defaultConverterOptions['converter-options']);
4372
- unset($defaultConverterOptions['preferred-converters']);
4373
- unset($defaultConverterOptions['shuffle']);
4374
-
4375
- // $this->logLn('converters: ' . print_r($converters, true));
4376
-
4377
- //return;
4378
- foreach ($converters as $converter) {
4379
- if (is_array($converter)) {
4380
- $converterId = $converter['converter'];
4381
- $converterOptions = isset($converter['options']) ? $converter['options'] : [];
4382
- } else {
4383
- $converterId = $converter;
4384
- $converterOptions = [];
4385
- if (isset($options['converter-options'][$converterId])) {
4386
- // Note: right now, converter-options are not meant to be used,
4387
- // when you have several converters of the same type
4388
- $converterOptions = $options['converter-options'][$converterId];
4389
- }
4390
- }
4391
- $converterOptions = array_merge($defaultConverterOptions, $converterOptions);
4392
- /*
4393
- if ($converterId != 'stack') {
4394
- //unset($converterOptions['converters']);
4395
- //unset($converterOptions['converter-options']);
4396
- } else {
4397
- //$converterOptions['converter-options'] =
4398
- $this->logLn('STACK');
4399
- $this->logLn('converterOptions: ' . print_r($converterOptions, true));
4400
- }*/
4401
-
4402
- $beginTime = microtime(true);
4403
-
4404
- $this->ln();
4405
- $this->logLn('Trying: ' . $converterId, 'italic');
4406
-
4407
- $converter = ConverterFactory::makeConverter(
4408
- $converterId,
4409
- $this->source,
4410
- $this->destination,
4411
- $converterOptions,
4412
- $this->logger
4413
- );
4414
-
4415
- try {
4416
- $converter->doConvert();
4417
-
4418
- //self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
4419
-
4420
- $this->logLn($converterId . ' succeeded :)');
4421
- //throw new ConverterNotOperationalException('...');
4422
- return;
4423
- } catch (ConverterNotOperationalException $e) {
4424
- $this->logLn($e->getMessage());
4425
- } catch (ConversionFailedException $e) {
4426
- $this->logLn($e->getMessage(), 'italic');
4427
- $prev = $e->getPrevious();
4428
- if (!is_null($prev)) {
4429
- $this->logLn($prev->getMessage(), 'italic');
4430
- $this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
4431
- $this->ln();
4432
- }
4433
- //$this->logLn($e->getTraceAsString());
4434
- $anyRuntimeErrors = true;
4435
- } catch (ConversionSkippedException $e) {
4436
- $this->logLn($e->getMessage());
4437
- }
4438
-
4439
- $this->logLn($converterId . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
4440
- }
4441
-
4442
- $this->ln();
4443
- $this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
4444
-
4445
- // Hm, Scrutinizer complains that $anyRuntimeErrors is always false. But that is not true!
4446
- if ($anyRuntimeErrors) {
4447
- // At least one converter failed
4448
- throw new ConversionFailedException(
4449
- 'None of the converters in the stack could convert the image.'
4450
- );
4451
- } else {
4452
- // All converters threw a SystemRequirementsNotMetException
4453
- throw new ConverterNotOperationalException('None of the converters in the stack are operational');
4454
- }
4455
- }
4456
- }
4457
-
4458
- ?><?php
4459
-
4460
- namespace WebPConvert\Convert\Converters;
4461
-
4462
- use WebPConvert\Convert\Converters\AbstractConverter;
4463
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4464
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4465
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4466
- use WebPConvert\Options\BooleanOption;
4467
-
4468
- //require '/home/rosell/.composer/vendor/autoload.php';
4469
-
4470
- /**
4471
- * Convert images to webp using Vips extension.
4472
- *
4473
- * @package WebPConvert
4474
- * @author Bjørn Rosell <it@rosell.dk>
4475
- * @since Class available since Release 2.0.0
4476
- */
4477
- class Vips extends AbstractConverter
4478
- {
4479
- use EncodingAutoTrait;
4480
-
4481
- protected function getUnsupportedDefaultOptions()
4482
- {
4483
- return [
4484
- 'size-in-percentage',
4485
- 'use-nice'
4486
- ];
4487
- }
4488
-
4489
- protected function createOptions()
4490
- {
4491
- parent::createOptions();
4492
-
4493
- $this->options2->addOptions(
4494
- new BooleanOption('smart-subsample', false)
4495
- );
4496
- }
4497
-
4498
- /**
4499
- * Check operationality of Vips converter.
4500
- *
4501
- * @throws SystemRequirementsNotMetException if system requirements are not met
4502
- */
4503
- public function checkOperationality()
4504
- {
4505
- if (!extension_loaded('vips')) {
4506
- throw new SystemRequirementsNotMetException('Required Vips extension is not available.');
4507
- }
4508
-
4509
- if (!function_exists('vips_image_new_from_file')) {
4510
- throw new SystemRequirementsNotMetException(
4511
- 'Vips extension seems to be installed, however something is not right: ' .
4512
- 'the function "vips_image_new_from_file" is not available.'
4513
- );
4514
- }
4515
-
4516
- // TODO: Should we also test if webp is available? (It seems not to be neccessary - it seems
4517
- // that webp be well intergrated part of vips)
4518
- }
4519
-
4520
- /**
4521
- * Check if specific file is convertable with current converter / converter settings.
4522
- *
4523
- * @throws SystemRequirementsNotMetException if Vips does not support image type
4524
- */
4525
- public function checkConvertability()
4526
- {
4527
- // It seems that png and jpeg are always supported by Vips
4528
- // - so nothing needs to be done here
4529
-
4530
- if (function_exists('vips_version')) {
4531
- $this->logLn('vipslib version: ' . vips_version());
4532
- }
4533
- $this->logLn('vips extension version: ' . phpversion('vips'));
4534
- }
4535
-
4536
- /**
4537
- * Create vips image resource from source file
4538
- *
4539
- * @throws ConversionFailedException if image resource cannot be created
4540
- * @return resource vips image resource
4541
- */
4542
- private function createImageResource()
4543
- {
4544
- // We are currently using vips_image_new_from_file(), but we could consider
4545
- // calling vips_jpegload / vips_pngload instead
4546
- $result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []);
4547
- if ($result === -1) {
4548
- /*throw new ConversionFailedException(
4549
- 'Failed creating new vips image from file: ' . $this->source
4550
- );*/
4551
- $message = /** @scrutinizer ignore-call */ vips_error_buffer();
4552
- throw new ConversionFailedException($message);
4553
- }
4554
-
4555
- if (!is_array($result)) {
4556
- throw new ConversionFailedException(
4557
- 'vips_image_new_from_file did not return an array, which we expected'
4558
- );
4559
- }
4560
-
4561
- if (count($result) != 1) {
4562
- throw new ConversionFailedException(
4563
- 'vips_image_new_from_file did not return an array of length 1 as we expected ' .
4564
- '- length was: ' . count($result)
4565
- );
4566
- }
4567
-
4568
- $im = array_shift($result);
4569
- return $im;
4570
- }
4571
-
4572
- /**
4573
- * Create parameters for webpsave
4574
- *
4575
- * @return array the parameters as an array
4576
- */
4577
- private function createParamsForVipsWebPSave()
4578
- {
4579
- // webpsave options are described here:
4580
- // v 8.8.0: https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
4581
- // v ?.?.?: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
4582
- // near_lossless option is described here: https://github.com/libvips/libvips/pull/430
4583
-
4584
- // Note that "method" is currently not supported (27 may 2019)
4585
-
4586
- $options = [
4587
- "Q" => $this->getCalculatedQuality(),
4588
- 'lossless' => ($this->options['encoding'] == 'lossless'),
4589
- 'strip' => $this->options['metadata'] == 'none',
4590
- ];
4591
-
4592
- // Only set the following options if they differ from the default of vipslib
4593
- // This ensures we do not get warning if that property isn't supported
4594
- if ($this->options['smart-subsample'] !== false) {
4595
- $options['smart_subsample'] = $this->options['smart-subsample'];
4596
- }
4597
- if ($this->options['alpha-quality'] !== 100) {
4598
- $options['alpha_q'] = $this->options['alpha-quality'];
4599
- }
4600
-
4601
- if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) {
4602
- // preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last
4603
- $options['preset'] = array_search(
4604
- $this->options['preset'],
4605
- ['default', 'picture', 'photo', 'drawing', 'icon', 'text']
4606
- );
4607
- }
4608
- if ($this->options['near-lossless'] !== 100) {
4609
- if ($this->options['encoding'] == 'lossless') {
4610
- // We only let near_lossless have effect when encoding is set to lossless
4611
- // otherwise encoding=auto would not work as expected
4612
- // Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016
4613
- // seems it corresponds to release 8.4.2
4614
- $options['near_lossless'] = true;
4615
-
4616
- // In Vips, the near-lossless value is controlled by Q.
4617
- // this differs from how it is done in cwebp, where it is an integer.
4618
- // We have chosen same option syntax as cwebp
4619
- $options['Q'] = $this->options['near-lossless'];
4620
- }
4621
- }
4622
-
4623
- return $options;
4624
- }
4625
-
4626
- /**
4627
- * Convert with vips extension.
4628
- *
4629
- * Tries to create image resource and save it as webp using the calculated options.
4630
- * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
4631
- * (recursively call itself until there is no more of these kind of errors).
4632
- *
4633
- * @param resource $im A vips image resource to save
4634
- * @throws ConversionFailedException if conversion fails.
4635
- */
4636
- private function webpsave($im, $options)
4637
- {
4638
- $result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options);
4639
-
4640
- //trigger_error('test-warning', E_USER_WARNING);
4641
- if ($result === -1) {
4642
- $message = /** @scrutinizer ignore-call */ vips_error_buffer();
4643
-
4644
- $nameOfPropertyNotFound = '';
4645
- if (preg_match("#no property named .(.*).#", $message, $matches)) {
4646
- $nameOfPropertyNotFound = $matches[1];
4647
- } elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) {
4648
- // Actually, I am not quite sure if this ever happens.
4649
- // I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning
4650
- if (in_array($matches[1], ['lossless', 'alpha_q', 'near_lossless', 'smart_subsample'])) {
4651
- $nameOfPropertyNotFound = $matches[1];
4652
- }
4653
- }
4654
-
4655
- if ($nameOfPropertyNotFound != '') {
4656
- $this->logLn(
4657
- 'Your version of vipslib does not support the "' . $nameOfPropertyNotFound . '" property. ' .
4658
- 'The option is ignored.'
4659
- );
4660
- unset($options[$nameOfPropertyNotFound]);
4661
- $this->webpsave($im, $options);
4662
- } else {
4663
- throw new ConversionFailedException($message);
4664
- }
4665
- }
4666
- }
4667
-
4668
- /**
4669
- * Convert with vips extension.
4670
- *
4671
- * Tries to create image resource and save it as webp using the calculated options.
4672
- * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
4673
- * (repeat until success).
4674
- *
4675
- * @throws ConversionFailedException if conversion fails.
4676
- */
4677
- protected function doActualConvert()
4678
- {
4679
- /*
4680
- $im = \Jcupitt\Vips\Image::newFromFile($this->source);
4681
- //$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]);
4682
-
4683
- $im->webpsave($this->destination, [
4684
- "Q" => 80,
4685
- //'near_lossless' => true
4686
- ]);
4687
- return;*/
4688
-
4689
- $im = $this->createImageResource();
4690
- $options = $this->createParamsForVipsWebPSave();
4691
- $this->webpsave($im, $options);
4692
- }
4693
- }
4694
-
4695
- ?><?php
4696
-
4697
- namespace WebPConvert\Convert\Converters;
4698
-
4699
- use WebPConvert\Convert\Converters\AbstractConverter;
4700
- use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
4701
- use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
4702
- use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
4703
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
4704
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
4705
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
4706
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
4707
- use WebPConvert\Options\BooleanOption;
4708
- use WebPConvert\Options\IntegerOption;
4709
- use WebPConvert\Options\SensitiveStringOption;
4710
-
4711
- /**
4712
- * Convert images to webp using Wpc (a cloud converter based on WebP Convert).
4713
- *
4714
- * @package WebPConvert
4715
- * @author Bjørn Rosell <it@rosell.dk>
4716
- * @since Class available since Release 2.0.0
4717
- */
4718
- class Wpc extends AbstractConverter
4719
- {
4720
- use CloudConverterTrait;
4721
- use CurlTrait;
4722
- use EncodingAutoTrait;
4723
-
4724
- protected function getUnsupportedDefaultOptions()
4725
- {
4726
- return [];
4727
- }
4728
-
4729
- public function supportsLossless()
4730
- {
4731
- return ($this->options['api-version'] >= 2);
4732
- }
4733
-
4734
- public function passOnEncodingAuto()
4735
- {
4736
- // We could make this configurable. But I guess passing it on is always to be preferred
4737
- // for api >= 2.
4738
- return ($this->options['api-version'] >= 2);
4739
- }
4740
-
4741
- protected function createOptions()
4742
- {
4743
- parent::createOptions();
4744
-
4745
- $this->options2->addOptions(
4746
- new SensitiveStringOption('api-key', ''), /* for communicating with wpc api v.1+ */
4747
- new SensitiveStringOption('secret', ''), /* for communicating with wpc api v.0 */
4748
- new SensitiveStringOption('api-url', ''),
4749
- new SensitiveStringOption('url', ''), /* DO NOT USE. Only here to keep the protection */
4750
- new IntegerOption('api-version', 2, 0, 2),
4751
- new BooleanOption('crypt-api-key-in-transfer', false) /* new in api v.1 */
4752
- );
4753
- }
4754
-
4755
- private static function createRandomSaltForBlowfish()
4756
- {
4757
- $salt = '';
4758
- $validCharsForSalt = array_merge(
4759
- range('A', 'Z'),
4760
- range('a', 'z'),
4761
- range('0', '9'),
4762
- ['.', '/']
4763
- );
4764
-
4765
- for ($i=0; $i<22; $i++) {
4766
- $salt .= $validCharsForSalt[array_rand($validCharsForSalt)];
4767
- }
4768
- return $salt;
4769
- }
4770
-
4771
- /**
4772
- * Get api key from options or environment variable
4773
- *
4774
- * @return string api key or empty string if none is set
4775
- */
4776
- private function getApiKey()
4777
- {
4778
- if ($this->options['api-version'] == 0) {
4779
- if (!empty($this->options['secret'])) {
4780
- return $this->options['secret'];
4781
- }
4782
- } elseif ($this->options['api-version'] >= 1) {
4783
- if (!empty($this->options['api-key'])) {
4784
- return $this->options['api-key'];
4785
- }
4786
- }
4787
- if (defined('WEBPCONVERT_WPC_API_KEY')) {
4788
- return constant('WEBPCONVERT_WPC_API_KEY');
4789
- }
4790
- if (!empty(getenv('WEBPCONVERT_WPC_API_KEY'))) {
4791
- return getenv('WEBPCONVERT_WPC_API_KEY');
4792
- }
4793
- return '';
4794
- }
4795
-
4796
- /**
4797
- * Get url from options or environment variable
4798
- *
4799
- * @return string URL to WPC or empty string if none is set
4800
- */
4801
- private function getApiUrl()
4802
- {
4803
- if (!empty($this->options['api-url'])) {
4804
- return $this->options['api-url'];
4805
- }
4806
- if (defined('WEBPCONVERT_WPC_API_URL')) {
4807
- return constant('WEBPCONVERT_WPC_API_URL');
4808
- }
4809
- if (!empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
4810
- return getenv('WEBPCONVERT_WPC_API_URL');
4811
- }
4812
- return '';
4813
- }
4814
-
4815
-
4816
- /**
4817
- * Check operationality of Wpc converter.
4818
- *
4819
- * @throws SystemRequirementsNotMetException if system requirements are not met (curl)
4820
- * @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
4821
- */
4822
- public function checkOperationality()
4823
- {
4824
-
4825
- $options = $this->options;
4826
-
4827
- $apiVersion = $options['api-version'];
4828
-
4829
- if ($this->getApiUrl() == '') {
4830
- if (isset($this->options['url']) && ($this->options['url'] != '')) {
4831
- throw new ConverterNotOperationalException(
4832
- 'The "url" option has been renamed to "api-url" in webp-convert 2.0. ' .
4833
- 'You must change the configuration accordingly.'
4834
- );
4835
- }
4836
- throw new ConverterNotOperationalException(
4837
- 'Missing URL. You must install Webp Convert Cloud Service on a server, ' .
4838
- 'or the WebP Express plugin for Wordpress - and supply the url.'
4839
- );
4840
- }
4841
-
4842
- if ($apiVersion == 0) {
4843
- if (!empty($this->getApiKey())) {
4844
- // if secret is set, we need md5() and md5_file() functions
4845
- if (!function_exists('md5')) {
4846
- throw new ConverterNotOperationalException(
4847
- 'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
4848
- 'contents. ' .
4849
- 'But the required md5() PHP function is not available.'
4850
- );
4851
- }
4852
- if (!function_exists('md5_file')) {
4853
- throw new ConverterNotOperationalException(
4854
- 'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
4855
- 'contents. But the required md5_file() PHP function is not available.'
4856
- );
4857
- }
4858
- }
4859
- } elseif ($apiVersion >= 1) {
4860
- if ($options['crypt-api-key-in-transfer']) {
4861
- if (!function_exists('crypt')) {
4862
- throw new ConverterNotOperationalException(
4863
- 'Configured to crypt the api-key, but crypt() function is not available.'
4864
- );
4865
- }
4866
-
4867
- if (!defined('CRYPT_BLOWFISH')) {
4868
- throw new ConverterNotOperationalException(
4869
- 'Configured to crypt the api-key. ' .
4870
- 'That requires Blowfish encryption, which is not available on your current setup.'
4871
- );
4872
- }
4873
- }
4874
- }
4875
-
4876
- // Check for curl requirements
4877
- $this->checkOperationalityForCurlTrait();
4878
- }
4879
-
4880
- /*
4881
- public function checkConvertability()
4882
- {
4883
- // check upload limits
4884
- $this->checkConvertabilityCloudConverterTrait();
4885
-
4886
- // TODO: some from below can be moved up here
4887
- }
4888
- */
4889
-
4890
- private function createOptionsToSend()
4891
- {
4892
- $optionsToSend = $this->options;
4893
-
4894
- if ($this->isQualityDetectionRequiredButFailing()) {
4895
- // quality was set to "auto", but we could not meassure the quality of the jpeg locally
4896
- // Ask the cloud service to do it, rather than using what we came up with.
4897
- $optionsToSend['quality'] = 'auto';
4898
- } else {
4899
- $optionsToSend['quality'] = $this->getCalculatedQuality();
4900
- }
4901
-
4902
- // The following are unset for security reasons.
4903
- unset($optionsToSend['converters']);
4904
- unset($optionsToSend['secret']);
4905
- unset($optionsToSend['api-key']);
4906
- unset($optionsToSend['api-url']);
4907
-
4908
- $apiVersion = $optionsToSend['api-version'];
4909
-
4910
- if ($apiVersion == 1) {
4911
- // Lossless can be "auto" in api 2, but in api 1 "auto" is not supported
4912
- //unset($optionsToSend['lossless']);
4913
- } elseif ($apiVersion == 2) {
4914
- //unset($optionsToSend['png']);
4915
- //unset($optionsToSend['jpeg']);
4916
-
4917
- // The following are unset for security reasons.
4918
- unset($optionsToSend['cwebp-command-line-options']);
4919
- unset($optionsToSend['command-line-options']);
4920
- }
4921
-
4922
- return $optionsToSend;
4923
- }
4924
-
4925
- private function createPostData()
4926
- {
4927
- $options = $this->options;
4928
-
4929
- $postData = [
4930
- 'file' => curl_file_create($this->source),
4931
- 'options' => json_encode($this->createOptionsToSend()),
4932
- 'servername' => (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')
4933
- ];
4934
-
4935
- $apiVersion = $options['api-version'];
4936
-
4937
- $apiKey = $this->getApiKey();
4938
-
4939
- if ($apiVersion == 0) {
4940
- $postData['hash'] = md5(md5_file($this->source) . $apiKey);
4941
- } elseif ($apiVersion == 1) {
4942
- //$this->logLn('api key: ' . $apiKey);
4943
-
4944
- if ($options['crypt-api-key-in-transfer']) {
4945
- $salt = self::createRandomSaltForBlowfish();
4946
- $postData['salt'] = $salt;
4947
-
4948
- // Strip off the first 28 characters (the first 6 are always "$2y$10$". The next 22 is the salt)
4949
- $postData['api-key-crypted'] = substr(crypt($apiKey, '$2y$10$' . $salt . '$'), 28);
4950
- } else {
4951
- $postData['api-key'] = $apiKey;
4952
- }
4953
- }
4954
- return $postData;
4955
- }
4956
-
4957
- protected function doActualConvert()
4958
- {
4959
- $ch = self::initCurl();
4960
-
4961
- //$this->logLn('api url: ' . $this->getApiUrl());
4962
-
4963
- curl_setopt_array($ch, [
4964
- CURLOPT_URL => $this->getApiUrl(),
4965
- CURLOPT_POST => 1,
4966
- CURLOPT_POSTFIELDS => $this->createPostData(),
4967
- CURLOPT_BINARYTRANSFER => true,
4968
- CURLOPT_RETURNTRANSFER => true,
4969
- CURLOPT_HEADER => false,
4970
- CURLOPT_SSL_VERIFYPEER => false
4971
- ]);
4972
-
4973
- $response = curl_exec($ch);
4974
- if (curl_errno($ch)) {
4975
- $this->logLn('Curl error: ', 'bold');
4976
- $this->logLn(curl_error($ch));
4977
- throw new ConverterNotOperationalException('Curl error:');
4978
- }
4979
-
4980
- // Check if we got a 404
4981
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
4982
- if ($httpCode == 404) {
4983
- curl_close($ch);
4984
- throw new ConversionFailedException(
4985
- 'WPC was not found at the specified URL - we got a 404 response.'
4986
- );
4987
- }
4988
-
4989
- // Check for empty response
4990
- if (empty($response)) {
4991
- throw new ConversionFailedException(
4992
- 'Error: Unexpected result. We got nothing back. ' .
4993
- 'HTTP CODE: ' . $httpCode . '. ' .
4994
- 'Content type:' . curl_getinfo($ch, CURLINFO_CONTENT_TYPE)
4995
- );
4996
- };
4997
-
4998
- // The WPC cloud service either returns an image or an error message
4999
- // Images has application/octet-stream.
5000
- // Verify that we got an image back.
5001
- if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
5002
- curl_close($ch);
5003
-
5004
- if (substr($response, 0, 1) == '{') {
5005
- $responseObj = json_decode($response, true);
5006
- if (isset($responseObj['errorCode'])) {
5007
- switch ($responseObj['errorCode']) {
5008
- case 0:
5009
- throw new ConverterNotOperationalException(
5010
- 'There are problems with the server setup: "' .
5011
- $responseObj['errorMessage'] . '"'
5012
- );
5013
- case 1:
5014
- throw new InvalidApiKeyException(
5015
- 'Access denied. ' . $responseObj['errorMessage']
5016
- );
5017
- default:
5018
- throw new ConversionFailedException(
5019
- 'Conversion failed: "' . $responseObj['errorMessage'] . '"'
5020
- );
5021
- }
5022
- }
5023
- }
5024
-
5025
- // WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
5026
- if (substr($response, 0, 7) == 'failed!') {
5027
- throw new ConversionFailedException(
5028
- 'WPC failed converting image: "' . substr($response, 7) . '"'
5029
- );
5030
- }
5031
-
5032
- $this->logLn('Bummer, we did not receive an image');
5033
- $this->log('What we received starts with: "');
5034
- $this->logLn(
5035
- str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400)))) . '..."'
5036
- );
5037
-
5038
- throw new ConversionFailedException('Unexpected result. We did not receive an image but something else.');
5039
- //throw new ConverterNotOperationalException($response);
5040
- }
5041
-
5042
- $success = file_put_contents($this->destination, $response);
5043
- curl_close($ch);
5044
-
5045
- if (!$success) {
5046
- throw new ConversionFailedException('Error saving file. Check file permissions');
5047
- }
5048
- }
5049
- }
5050
-
5051
- ?><?php
5052
-
5053
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5054
-
5055
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5056
-
5057
- class ConversionSkippedException extends ConversionFailedException
5058
- {
5059
- public $description = 'The converter declined converting';
5060
- }
5061
-
5062
- ?><?php
5063
-
5064
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5065
-
5066
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5067
-
5068
- class ConverterNotOperationalException extends ConversionFailedException
5069
- {
5070
- public $description = 'The converter is not operational';
5071
- }
5072
-
5073
- ?><?php
5074
-
5075
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5076
-
5077
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5078
-
5079
- class FileSystemProblemsException extends ConversionFailedException
5080
- {
5081
- public $description = 'Filesystem problems';
5082
- }
5083
-
5084
- ?><?php
5085
-
5086
- namespace WebPConvert\Convert\Exceptions\ConversionFailed;
5087
-
5088
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
5089
-
5090
- class InvalidInputException extends ConversionFailedException
5091
- {
5092
- public $description = 'Invalid input';
5093
- }
5094
-
5095
- ?><?php
5096
-
5097
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
5098
-
5099
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
5100
-
5101
- class InvalidApiKeyException extends ConverterNotOperationalException
5102
- {
5103
- public $description = 'The converter is not operational (access denied)';
5104
- }
5105
-
5106
- ?><?php
5107
-
5108
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
5109
-
5110
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
5111
-
5112
- class SystemRequirementsNotMetException extends ConverterNotOperationalException
5113
- {
5114
- public $description = 'The converter is not operational (system requirements not met)';
5115
- }
5116
-
5117
- ?><?php
5118
-
5119
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
5120
-
5121
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
5122
-
5123
- class CreateDestinationFileException extends FileSystemProblemsException
5124
- {
5125
- public $description = 'The converter could not create destination file. Check file permisions!';
5126
- }
5127
-
5128
- ?><?php
5129
-
5130
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
5131
-
5132
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
5133
-
5134
- class CreateDestinationFolderException extends FileSystemProblemsException
5135
- {
5136
- public $description = 'The converter could not create destination folder. Check file permisions!';
5137
- }
5138
-
5139
- ?><?php
5140
-
5141
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5142
-
5143
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5144
-
5145
- class ConverterNotFoundException extends InvalidInputException
5146
- {
5147
- public $description = 'The converter does not exist.';
5148
- }
5149
-
5150
- ?><?php
5151
-
5152
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5153
-
5154
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5155
-
5156
- class InvalidImageTypeException extends InvalidInputException
5157
- {
5158
- public $description = 'The converter does not handle the supplied image type';
5159
- }
5160
-
5161
- ?><?php
5162
-
5163
- namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
5164
-
5165
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
5166
-
5167
- class TargetNotFoundException extends InvalidInputException
5168
- {
5169
- public $description = 'The converter could not locate source file';
5170
- }
5171
-
5172
- ?><?php
5173
-
5174
- namespace WebPConvert\Convert\Helpers;
5175
-
5176
- /**
5177
- * Try to detect quality of a jpeg image using various tools.
5178
- *
5179
- * @package WebPConvert
5180
- * @author Bjørn Rosell <it@rosell.dk>
5181
- * @since Class available since Release 2.0.0
5182
- */
5183
- class JpegQualityDetector
5184
- {
5185
-
5186
- /**
5187
- * Try to detect quality of jpeg using imagick extension
5188
- *
5189
- * @param string $filename A complete file path to file to be examined
5190
- * @return int|null Quality, or null if it was not possible to detect quality
5191
- */
5192
- private static function detectQualityOfJpgUsingImagick($filename)
5193
- {
5194
- if (extension_loaded('imagick') && class_exists('\\Imagick')) {
5195
- try {
5196
- $img = new \Imagick($filename);
5197
-
5198
- // The required function is available as from PECL imagick v2.2.2
5199
- if (method_exists($img, 'getImageCompressionQuality')) {
5200
- return $img->getImageCompressionQuality();
5201
- }
5202
- } catch (\Exception $e) {
5203
- // Well well, it just didn't work out.
5204
- // - But perhaps next method will work...
5205
- }
5206
- }
5207
- return null;
5208
- }
5209
-
5210
-
5211
- /**
5212
- * Try to detect quality of jpeg using imagick binary
5213
- *
5214
- * @param string $filename A complete file path to file to be examined
5215
- * @return int|null Quality, or null if it was not possible to detect quality
5216
- */
5217
- private static function detectQualityOfJpgUsingImageMagick($filename)
5218
- {
5219
- if (function_exists('exec')) {
5220
- // Try Imagick using exec, and routing stderr to stdout (the "2>$1" magic)
5221
- exec("identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", $output, $returnCode);
5222
- //echo 'out:' . print_r($output, true);
5223
- if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
5224
- return intval($output[0]);
5225
- }
5226
- }
5227
- return null;
5228
- }
5229
-
5230
-
5231
- /**
5232
- * Try to detect quality of jpeg using gmagick binary
5233
- *
5234
- * @param string $filename A complete file path to file to be examined
5235
- * @return int|null Quality, or null if it was not possible to detect quality
5236
- */
5237
- private static function detectQualityOfJpgUsingGraphicsMagick($filename)
5238
- {
5239
- if (function_exists('exec')) {
5240
- // Try GraphicsMagick
5241
- exec("gm identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", $output, $returnCode);
5242
- if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
5243
- return intval($output[0]);
5244
- }
5245
- }
5246
- return null;
5247
- }
5248
-
5249
-
5250
- /**
5251
- * Try to detect quality of jpeg.
5252
- *
5253
- * Note: This method does not throw errors, but might dispatch warnings.
5254
- * You can use the WarningsIntoExceptions class if it is critical to you that nothing gets "printed"
5255
- *
5256
- * @param string $filename A complete file path to file to be examined
5257
- * @return int|null Quality, or null if it was not possible to detect quality
5258
- */
5259
- public static function detectQualityOfJpg($filename)
5260
- {
5261
-
5262
- //trigger_error('warning test', E_USER_WARNING);
5263
-
5264
- // Test that file exists in order not to break things.
5265
- if (!file_exists($filename)) {
5266
- // One could argue that it would be better to throw an Exception...?
5267
- return null;
5268
- }
5269
-
5270
- // Try Imagick extension, if available
5271
- $quality = self::detectQualityOfJpgUsingImagick($filename);
5272
-
5273
- if (is_null($quality)) {
5274
- $quality = self::detectQualityOfJpgUsingImageMagick($filename);
5275
- }
5276
-
5277
- if (is_null($quality)) {
5278
- $quality = self::detectQualityOfJpgUsingGraphicsMagick($filename);
5279
- }
5280
-
5281
- return $quality;
5282
- }
5283
- }
5284
-
5285
- ?><?php
5286
-
5287
- namespace WebPConvert\Convert\Helpers;
5288
-
5289
- /**
5290
- * Get/parse shorthandsize strings from php.ini as bytes.
5291
- *
5292
- * Parse strings like "1k" into bytes (1024).
5293
- *
5294
- * @package WebPConvert
5295
- * @author Bjørn Rosell <it@rosell.dk>
5296
- * @since Class available since Release 2.0.0
5297
- */
5298
- class PhpIniSizes
5299
- {
5300
-
5301
- /**
5302
- * Parse a shordhandsize string as the ones returned by ini_get()
5303
- *
5304
- * Parse a shorthandsize string having the syntax allowed in php.ini and returned by ini_get().
5305
- * Ie "1K" => 1024.
5306
- * Strings without units are also accepted.
5307
- * The shorthandbytes syntax is described here: https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
5308
- *
5309
- * @param string $shortHandSize A size string of the type returned by ini_get()
5310
- * @return float|false The parsed size (beware: it is float, do not check high numbers for equality),
5311
- * or false if parse error
5312
- */
5313
- public static function parseShortHandSize($shortHandSize)
5314
- {
5315
-
5316
- $result = preg_match("#^\\s*(\\d+(?:\\.\\d+)?)([bkmgtpezy]?)\\s*$#i", $shortHandSize, $matches);
5317
- if ($result !== 1) {
5318
- return false;
5319
- }
5320
-
5321
- // Truncate, because that is what php does.
5322
- $digitsValue = floor($matches[1]);
5323
-
5324
- if ((count($matches) >= 3) && ($matches[2] != '')) {
5325
- $unit = $matches[2];
5326
-
5327
- // Find the position of the unit in the ordered string which is the power
5328
- // of magnitude to multiply a kilobyte by.
5329
- $position = stripos('bkmgtpezy', $unit);
5330
-
5331
- return floatval($digitsValue * pow(1024, $position));
5332
- } else {
5333
- return $digitsValue;
5334
- }
5335
- }
5336
-
5337
- /*
5338
- * Get the size of an php.ini option.
5339
- *
5340
- * Calls ini_get() and parses the size to a number.
5341
- * If the configuration option is null, does not exist, or cannot be parsed as a shorthandsize, false is returned
5342
- *
5343
- * @param string $varname The configuration option name.
5344
- * @return float|false The parsed size or false if the configuration option does not exist
5345
- */
5346
- public static function getIniBytes($iniVarName)
5347
- {
5348
- $iniVarValue = ini_get($iniVarName);
5349
- if (($iniVarValue == '') || $iniVarValue === false) {
5350
- return false;
5351
- }
5352
- return self::parseShortHandSize($iniVarValue);
5353
- }
5354
- }
5355
-
5356
- ?><?php
5357
-
5358
- namespace WebPConvert\Convert;
5359
-
5360
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
5361
- use WebPConvert\Convert\Converters\AbstractConverter;
5362
-
5363
- /**
5364
- * Make converters from their ids.
5365
- *
5366
- * @package WebPConvert
5367
- * @author Bjørn Rosell <it@rosell.dk>
5368
- * @since Class available since Release 2.0.0
5369
- */
5370
- class ConverterFactory
5371
- {
5372
- /**
5373
- * Get classname of a converter (by id)
5374
- *
5375
- * @param string $converterId Id of converter (ie "cwebp")
5376
- *
5377
- * @throws ConverterNotFoundException If there is no converter with that id.
5378
- * @return string Fully qualified class name of converter
5379
- */
5380
- public static function converterIdToClassname($converterId)
5381
- {
5382
- switch ($converterId) {
5383
- case 'imagickbinary':
5384
- $classNameShort = 'ImagickBinary';
5385
- break;
5386
- case 'imagemagick':
5387
- $classNameShort = 'ImageMagick';
5388
- break;
5389
- case 'gmagickbinary':
5390
- $classNameShort = 'GmagickBinary';
5391
- break;
5392
- case 'graphicsmagick':
5393
- $classNameShort = 'GraphicsMagick';
5394
- break;
5395
- default:
5396
- $classNameShort = ucfirst($converterId);
5397
- }
5398
- $className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
5399
- if (is_callable([$className, 'convert'])) {
5400
- return $className;
5401
- } else {
5402
- throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
5403
- }
5404
- }
5405
-
5406
- /**
5407
- * Make a converter instance by class name.
5408
- *
5409
- * @param string $converterClassName Fully qualified class name
5410
- * @param string $source The path to the file to convert
5411
- * @param string $destination The path to save the converted file to
5412
- * @param array $options (optional)
5413
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
5414
- *
5415
- * @throws ConverterNotFoundException If the specified converter class isn't found
5416
- * @return AbstractConverter An instance of the specified converter
5417
- */
5418
- public static function makeConverterFromClassname(
5419
- $converterClassName,
5420
- $source,
5421
- $destination,
5422
- $options = [],
5423
- $logger = null
5424
- ) {
5425
- if (!is_callable([$converterClassName, 'convert'])) {
5426
- throw new ConverterNotFoundException(
5427
- 'There is no converter with class name:' . $converterClassName . ' (or it is not a converter)'
5428
- );
5429
- }
5430
- //$converter = new $converterClassName($source, $destination, $options, $logger);
5431
-
5432
- return call_user_func(
5433
- [$converterClassName, 'createInstance'],
5434
- $source,
5435
- $destination,
5436
- $options,
5437
- $logger
5438
- );
5439
- }
5440
-
5441
- /**
5442
- * Make a converter instance by either id or class name.
5443
- *
5444
- * @param string $converterIdOrClassName Either a converter ID or a fully qualified class name
5445
- * @param string $source The path to the file to convert
5446
- * @param string $destination The path to save the converted file to
5447
- * @param array $options (optional)
5448
- * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
5449
- *
5450
- * @throws ConverterNotFoundException If the specified converter class isn't found
5451
- * @return AbstractConverter An instance of the specified converter
5452
- */
5453
- public static function makeConverter($converterIdOrClassName, $source, $destination, $options = [], $logger = null)
5454
- {
5455
- // We take it that all lowercase means it is an id rather than a class name
5456
- if (strtolower($converterIdOrClassName) == $converterIdOrClassName) {
5457
- $converterClassName = self::converterIdToClassname($converterIdOrClassName);
5458
- } else {
5459
- $converterClassName = $converterIdOrClassName;
5460
- }
5461
-
5462
- return self::makeConverterFromClassname($converterClassName, $source, $destination, $options, $logger);
5463
- }
5464
- }
5465
-
5466
- ?><?php
5467
-
5468
- namespace WebPConvert\Helpers;
5469
-
5470
- use ImageMimeTypeGuesser\ImageMimeTypeGuesser;
5471
-
5472
- use WebPConvert\Exceptions\InvalidInputException;
5473
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
5474
-
5475
- /**
5476
- * Get MimeType, results cached by path.
5477
- *
5478
- * @package WebPConvert
5479
- * @author Bjørn Rosell <it@rosell.dk>
5480
- * @since Class available since Release 2.0.6
5481
- */
5482
- class MimeType
5483
- {
5484
- private static $cachedDetections = [];
5485
-
5486
- /**
5487
- * Get mime type for image (best guess).
5488
- *
5489
- * It falls back to using file extension. If that fails too, false is returned
5490
- *
5491
- * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
5492
- * false (if it is not an image type that the server knowns about)
5493
- * or null (if nothing can be determined)
5494
- */
5495
- public static function getMimeTypeDetectionResult($absFilePath)
5496
- {
5497
- PathChecker::checkAbsolutePathAndExists($absFilePath);
5498
-
5499
- if (isset(self::$cachedDetections[$absFilePath])) {
5500
- return self::$cachedDetections[$absFilePath];
5501
- }
5502
- $cachedDetections[$absFilePath] = ImageMimeTypeGuesser::lenientGuess($absFilePath);
5503
- return $cachedDetections[$absFilePath];
5504
- }
5505
- }
5506
-
5507
- ?><?php
5508
-
5509
- namespace WebPConvert\Loggers;
5510
-
5511
- /**
5512
- * Base for all logger classes.
5513
- *
5514
- * WebPConvert can provide insights into the conversion process by means of accepting a logger which
5515
- * extends this class.
5516
- *
5517
- * @package WebPConvert
5518
- * @author Bjørn Rosell <it@rosell.dk>
5519
- * @since Class available since Release 2.0.0
5520
- */
5521
- abstract class BaseLogger
5522
- {
5523
- /**
5524
- * Write a message to the log
5525
- *
5526
- * @param string $msg message to log
5527
- * @param string $style style (null | bold | italic)
5528
- * @return void
5529
- */
5530
- abstract public function log($msg, $style = '');
5531
-
5532
- /**
5533
- * Add new line to the log
5534
- * @return void
5535
- */
5536
- abstract public function ln();
5537
-
5538
- /**
5539
- * Write a line to the log
5540
- *
5541
- * @param string $msg message to log
5542
- * @param string $style style (null | bold | italic)
5543
- * @return void
5544
- */
5545
- public function logLn($msg, $style = '')
5546
- {
5547
- $this->log($msg, $style);
5548
- $this->ln();
5549
- }
5550
- }
5551
-
5552
- ?><?php
5553
-
5554
- namespace WebPConvert\Loggers;
5555
-
5556
- use WebPConvert\Loggers\BaseLogger;
5557
-
5558
- /**
5559
- * Collect the logging and retrieve it later in HTML or plain text format.
5560
- *
5561
- * @package WebPConvert
5562
- * @author Bjørn Rosell <it@rosell.dk>
5563
- * @since Class available since Release 2.0.0
5564
- */
5565
- class BufferLogger extends BaseLogger
5566
- {
5567
- public $entries = array();
5568
-
5569
- /**
5570
- * Write a message to the buffer - all entries can later be retrieved with getText() or getHtlm().
5571
- *
5572
- * @param string $msg message to log
5573
- * @param string $style style (null | bold | italic)
5574
- * @return void
5575
- */
5576
- public function log($msg, $style = '')
5577
- {
5578
- $this->entries[] = [$msg, $style];
5579
- }
5580
-
5581
- /**
5582
- * Write a new line to the buffer.
5583
- *
5584
- * @return void
5585
- */
5586
- public function ln()
5587
- {
5588
- $this->entries[] = '';
5589
- }
5590
-
5591
- /**
5592
- * Get everything logged - as HTML.
5593
- *
5594
- * @return string The log, formatted as HTML.
5595
- */
5596
- public function getHtml()
5597
- {
5598
- $html = '';
5599
- foreach ($this->entries as $entry) {
5600
- if ($entry == '') {
5601
- $html .= '<br>';
5602
- } else {
5603
- list($msg, $style) = $entry;
5604
- $msg = htmlspecialchars($msg);
5605
- if ($style == 'bold') {
5606
- $html .= '<b>' . $msg . '</b>';
5607
- } elseif ($style == 'italic') {
5608
- $html .= '<i>' . $msg . '</i>';
5609
- } else {
5610
- $html .= $msg;
5611
- }
5612
- }
5613
- }
5614
- return $html;
5615
- }
5616
-
5617
- /**
5618
- * Get everything logged - as markdown.
5619
- *
5620
- * @return string The log, formatted as MarkDown.
5621
- */
5622
- public function getMarkDown($newLineChar = "\n\r")
5623
- {
5624
- $md = '';
5625
- foreach ($this->entries as $entry) {
5626
- if ($entry == '') {
5627
- $md .= $newLineChar;
5628
- } else {
5629
- list($msg, $style) = $entry;
5630
- if ($style == 'bold') {
5631
- $md .= '**' . $msg . '** ';
5632
- } elseif ($style == 'italic') {
5633
- $md .= '*' . $msg . '* ';
5634
- } else {
5635
- $md .= $msg;
5636
- }
5637
- }
5638
- }
5639
- return $md;
5640
- }
5641
-
5642
- /**
5643
- * Get everything logged - as plain text.
5644
- *
5645
- * @param string $newLineChar. The character used for new lines.
5646
- * @return string The log, formatted as plain text.
5647
- */
5648
- public function getText($newLineChar = ' ')
5649
- {
5650
- $text = '';
5651
- foreach ($this->entries as $entry) {
5652
- if ($entry == '') { // empty string means new line
5653
- if (substr($text, -2) != '.' . $newLineChar) {
5654
- $text .= '.' . $newLineChar;
5655
- }
5656
- } else {
5657
- list($msg, $style) = $entry;
5658
- $text .= $msg;
5659
- }
5660
- }
5661
-
5662
- return $text;
5663
- }
5664
- }
5665
-
5666
- ?><?php
5667
-
5668
- namespace WebPConvert\Loggers;
5669
-
5670
- /**
5671
- * Echo the logs immediately (in HTML)
5672
- *
5673
- * @package WebPConvert
5674
- * @author Bjørn Rosell <it@rosell.dk>
5675
- * @since Class available since Release 2.0.0
5676
- */
5677
- class EchoLogger extends BaseLogger
5678
- {
5679
-
5680
- /**
5681
- * Handle log() by echoing the message.
5682
- *
5683
- * @param string $msg message to log
5684
- * @param string $style style (null | bold | italic)
5685
- * @return void
5686
- */
5687
- public function log($msg, $style = '')
5688
- {
5689
- $msg = htmlspecialchars($msg);
5690
- if ($style == 'bold') {
5691
- echo '<b>' . $msg . '</b>';
5692
- } elseif ($style == 'italic') {
5693
- echo '<i>' . $msg . '</i>';
5694
- } else {
5695
- echo $msg;
5696
- }
5697
- }
5698
-
5699
- /**
5700
- * Handle ln by echoing a <br> tag.
5701
- *
5702
- * @return void
5703
- */
5704
- public function ln()
5705
- {
5706
- echo '<br>';
5707
- }
5708
- }
5709
-
5710
- ?><?php
5711
- namespace WebPConvert\Serve;
5712
-
5713
- use WebPConvert\Helpers\InputValidator;
5714
- use WebPConvert\Loggers\EchoLogger;
5715
- use WebPConvert\WebPConvert;
5716
-
5717
- /**
5718
- * Class for generating a HTML report of converting an image.
5719
- *
5720
- * @package WebPConvert
5721
- * @author Bjørn Rosell <it@rosell.dk>
5722
- * @since Class available since Release 2.0.0
5723
- */
5724
- class Report
5725
- {
5726
- public static function convertAndReport($source, $destination, $options)
5727
- {
5728
- InputValidator::checkSourceAndDestination($source, $destination);
5729
- ?>
5730
- <html>
5731
- <head>
5732
- <style>td {vertical-align: top} table {color: #666}</style>
5733
- <script>
5734
- function showOptions(elToHide) {
5735
- document.getElementById('options').style.display='block';
5736
- elToHide.style.display='none';
5737
- }
5738
- </script>
5739
- </head>
5740
- <body>
5741
- <table>
5742
- <tr><td><i>source:</i></td><td><?php echo htmlentities($source) ?></td></tr>
5743
- <tr><td><i>destination:</i></td><td><?php echo htmlentities($destination) ?><td></tr>
5744
- </table>
5745
- <br>
5746
- <?php
5747
- try {
5748
- $echoLogger = new EchoLogger();
5749
- $options['log-call-arguments'] = true;
5750
- WebPConvert::convert($source, $destination, $options, $echoLogger);
5751
- } catch (\Exception $e) {
5752
- $msg = $e->getMessage();
5753
- echo '<b>' . $msg . '</b>';
5754
-
5755
- //echo '<p>Rethrowing exception for your convenience</p>';
5756
- //throw ($e);
5757
- }
5758
- ?>
5759
- </body>
5760
- </html>
5761
- <?php
5762
- }
5763
- }
5764
-
5765
- ?><?php
5766
-
5767
- namespace WebPConvert\Serve\Exceptions;
5768
-
5769
- use WebPConvert\Exceptions\WebPConvertException;
5770
-
5771
- class ServeFailedException extends WebPConvertException
5772
- {
5773
- public $description = 'Failed serving';
5774
- }
5775
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/BaseExposer.php DELETED
@@ -1,113 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests;
4
-
5
- /**
6
- * Class for exposing otherwise unaccessible methods of classes
7
- * - so they can be tested
8
- */
9
- class BaseExposer {
10
-
11
- //public static $extraOptions = [];
12
-
13
- public $objectToExposeFrom;
14
- public static $currentlyCalling;
15
- public static $currentlyStealing;
16
-
17
- public function __construct($objectToExposeFrom)
18
- {
19
- $this->objectToExposeFrom = $objectToExposeFrom;
20
- }
21
-
22
- protected function bindDynamicFunctionToObjectAndCallIt($functionToBindToObject, $class = null, ...$args)
23
- {
24
- if (is_null($class)) {
25
- $class = get_class($this->objectToExposeFrom);
26
- }
27
- //$functionNowBinded = $functionToBindToObject->bindTo($this->objectToExposeFrom, AbstractConverter::class);
28
- $functionNowBinded = $functionToBindToObject->bindTo($this->objectToExposeFrom, $class);
29
- //$functionNowBinded = $functionToBindToObject->bindTo($this->objectToExposeFrom, get_class($this->objectToExposeFrom));
30
- return $functionNowBinded(...$args);
31
- }
32
-
33
- /**
34
- * @param string $functionNameToCall
35
- * @param string $class The class to inject into, ie a base class of the object to expose from (optional). If none is specified, it will be the class of the exposed object
36
- */
37
- protected function callPrivateFunction($functionNameToCall, $class = null, ...$args)
38
- {
39
- self::$currentlyCalling = $functionNameToCall;
40
- $cb = function() {
41
- return call_user_func_array(
42
- array($this, BaseExposer::$currentlyCalling),
43
- func_get_args()
44
- );
45
- };
46
- return $this->bindDynamicFunctionToObjectAndCallIt($cb, $class, ...$args);
47
- }
48
-
49
- protected function callPrivateFunctionByRef($functionNameToCall, $class = null, &$arg1)
50
- {
51
- self::$currentlyCalling = $functionNameToCall;
52
- $cb = function(&$arg1) {
53
- //echo 'callback...' . gettype($arg1);
54
- return $this->{BaseExposer::$currentlyCalling}($arg1);
55
- /*
56
- return call_user_func_array(
57
- array($this, BaseExposer::$currentlyCalling),
58
- $arg1
59
- );*/
60
- };
61
- $class = get_class($this->objectToExposeFrom);
62
- $functionNowBinded = $cb->bindTo($this->objectToExposeFrom, $class);
63
-
64
- return $functionNowBinded($arg1);
65
- //return $this->bindDynamicFunctionToObjectAndCallIt($cb, $class, $arg1);
66
- }
67
-
68
- /* work in progress
69
- protected function callPrivateStaticFunction($functionNameToCall, $class = null)
70
- {
71
- self::$currentlyCalling = $functionNameToCall;
72
-
73
- $cb = function() {
74
- return call_user_func_array(
75
- array(self, BaseExposer::$currentlyCalling),
76
- func_get_args()
77
- );
78
- };
79
- return $this->bindDynamicFunctionToObjectAndCallIt($cb, $class);
80
- }*/
81
-
82
-
83
- /**
84
- * @param string $propertyToSteal
85
- */
86
- protected function getPrivateProperty($propertyToSteal, $class = null)
87
- {
88
- self::$currentlyStealing = $propertyToSteal;
89
-
90
- $thief = function() {
91
- return $this->{BaseExposer::$currentlyStealing};
92
- };
93
-
94
- return $this->bindDynamicFunctionToObjectAndCallIt($thief, $class);
95
- }
96
-
97
- /**
98
- * @param string $propertyToSteal
99
- */
100
- protected function getPrivateStaticProperty($propertyToSteal, $class = null)
101
- {
102
- self::$currentlyStealing = $propertyToSteal;
103
-
104
- $thief = function() {
105
- $propertyName = BaseExposer::$currentlyStealing;
106
- return static::$$propertyName;
107
- };
108
-
109
- return $this->bindDynamicFunctionToObjectAndCallIt($thief, $class);
110
- }
111
-
112
-
113
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/AbstractConverterTest.php DELETED
@@ -1,107 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters;
4
-
5
- use WebPConvert\Convert\Converters\AbstractConverter;
6
- use WebPConvert\Tests\Convert\Exposers\AbstractConverterExposer;
7
- use WebPConvert\Tests\Convert\TestConverters\ExposedConverter;
8
- use WebPConvert\Tests\Convert\TestConverters\SuccessGuaranteedConverter;
9
-
10
- use PHPUnit\Framework\TestCase;
11
-
12
- class AbstractConverterTest extends TestCase
13
- {
14
-
15
- public static function getImageFolder()
16
- {
17
- return realpath(__DIR__ . '/../../images');
18
- }
19
-
20
- public static function getImagePath($image)
21
- {
22
- return self::getImageFolder() . '/' . $image;
23
- }
24
-
25
-
26
- public function testConvert()
27
- {
28
- SuccessGuaranteedConverter::convert(
29
- self::getImagePath('test.jpg'),
30
- self::getImagePath('test.webp')
31
- );
32
- $this->addToAssertionCount(1);
33
- }
34
-
35
- public function testMimeTypeGuesser()
36
- {
37
-
38
- //$this->assertEquals('image/jpeg', ExposedConverter::exposedGetMimeType(self::$imgDir . '/test.jpg'));
39
- //$this->assertEquals('image/png', ExposedConverter::exposedGetMimeType(self::$imgDir . '/test.png'));
40
- //$mimeTypeMaybeDetected = ExposedConverter::exposedGetMimeType(self::$imgDir . '/png-without-extension');
41
-
42
- $successConverterJpeg = SuccessGuaranteedConverter::createInstance(
43
- self::getImagePath('test.jpg'),
44
- self::getImagePath('test.jpg.webp')
45
- );
46
- $this->assertEquals('image/jpeg', $successConverterJpeg->getMimeTypeOfSource());
47
-
48
- $successConverterPng = SuccessGuaranteedConverter::createInstance(
49
- self::getImagePath('test.png'),
50
- self::getImagePath('test.png.webp')
51
- );
52
- $this->assertEquals('image/png', $successConverterPng->getMimeTypeOfSource());
53
-
54
- $successConverterPngMaybeDetected = SuccessGuaranteedConverter::createInstance(
55
- self::getImagePath('png-without-extension'),
56
- self::getImagePath('png-without-extension.webp')
57
- );
58
-
59
- $mimeTypeMaybeDetected = $successConverterPngMaybeDetected->getMimeTypeOfSource();
60
-
61
- if ($mimeTypeMaybeDetected === false) {
62
- // It was not detected, and that is ok!
63
- // - it is not possible to detect mime type on all platforms. In case it could not be detected,
64
- // - and file extension could not be mapped either, the method returns false.
65
- $this->addToAssertionCount(1);
66
- } else {
67
- $this->assertEquals('image/png', $mimeTypeMaybeDetected);
68
- }
69
- }
70
-
71
- public function testDefaultOptions()
72
- {
73
- $converter = new SuccessGuaranteedConverter(
74
- self::getImagePath('test.jpg'),
75
- self::getImagePath('test.jpg.webp')
76
- );
77
-
78
- $exposer = new AbstractConverterExposer($converter);
79
-
80
- $defaultOptions = $exposer->getOptions();
81
-
82
- $this->assertSame('auto', $defaultOptions['quality']);
83
- $this->assertSame(85, $defaultOptions['max-quality']);
84
- $this->assertSame(75, $defaultOptions['default-quality']);
85
- $this->assertSame('none', $defaultOptions['metadata']);
86
- }
87
-
88
-
89
- public function testOptionMerging()
90
- {
91
- $converter = new SuccessGuaranteedConverter(
92
- self::getImagePath('test.jpg'),
93
- self::getImagePath('test.webp'),
94
- [
95
- 'quality' => 80
96
- ]
97
- );
98
-
99
- $exposer = new AbstractConverterExposer($converter);
100
-
101
- //$exposer->prepareOptions();
102
-
103
- $mergedOptions = $exposer->getOptions();
104
-
105
- $this->assertSame(80, $mergedOptions['quality']);
106
- }
107
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/BaseTraits/AutoQualityTraitTest.php DELETED
@@ -1,172 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters\BaseTraits;
4
-
5
- //use WebPConvert\Convert\BaseConverters\AbstractCloudConverter;
6
- use WebPConvert\Tests\Convert\TestConverters\SuccessGuaranteedConverter;
7
-
8
- use PHPUnit\Framework\TestCase;
9
-
10
- class AutoQualityTraitTest extends TestCase
11
- {
12
-
13
- public static function getImageFolder()
14
- {
15
- return realpath(__DIR__ . '/../../../images');
16
- }
17
-
18
- public static function getImagePath($image)
19
- {
20
- return self::getImageFolder() . '/' . $image;
21
- }
22
-
23
- public function testFixedQuality()
24
- {
25
- $converter = SuccessGuaranteedConverter::createInstance(
26
- self::getImagePath('small-q61.jpg'),
27
- self::getImagePath('small-q61.jpg.webp'),
28
- [
29
- 'max-quality' => 80,
30
- 'quality' => 75,
31
- 'default-quality' => 70,
32
- ]
33
- );
34
-
35
- $result = $converter->getCalculatedQuality();
36
- $this->assertSame(75, $result);
37
-
38
- $this->assertFalse($converter->isQualityDetectionRequiredButFailing());
39
-
40
- // Test that it is still the same (testing caching)
41
- $this->assertFalse($converter->isQualityDetectionRequiredButFailing());
42
-
43
- }
44
-
45
- /*
46
- public function testAutoQualityWhenQualityCannotBeDetected()
47
- {
48
- $converter = SuccessGuaranteedConverter::createInstance(
49
- self::$imgDir . '/non-existant',
50
- self::$imgDir . '/non-existant.webp',
51
- [
52
- 'max-quality' => 80,
53
- 'quality' => 'auto',
54
- 'default-quality' => 70,
55
- ]
56
- );
57
-
58
- $result = $converter->getCalculatedQuality();
59
-
60
- $this->assertSame(70, $result);
61
- }*/
62
-
63
- public function testAutoQuality()
64
- {
65
- $converter = SuccessGuaranteedConverter::createInstance(
66
- self::getImagePath('small-q61.jpg'),
67
- self::getImagePath('small-q61.jpg.webp'),
68
- [
69
- 'max-quality' => 80,
70
- 'quality' => 'auto',
71
- 'default-quality' => 61,
72
- ]
73
- );
74
-
75
- $result = $converter->getCalculatedQuality();
76
-
77
- // "Cheating" a bit here...
78
- // - If quality detection fails, it will be 61 (because default-quality is set to 61)
79
- // - If quality detection succeeds, it will also be 61
80
- $this->assertSame(61, $result);
81
- }
82
-
83
- public function testAutoQualityMaxQuality()
84
- {
85
- $converter = SuccessGuaranteedConverter::createInstance(
86
- self::getImagePath('small-q61.jpg'),
87
- self::getImagePath('small-q61.jpg.webp'),
88
- [
89
- 'max-quality' => 60,
90
- 'quality' => 'auto',
91
- 'default-quality' => 61,
92
- ]
93
- );
94
-
95
- //$this->assertTrue(file_exists(self::$imgDir . '/small-q61.jpg'));
96
- //$this->assertEquals('image/jpeg', $converter->getMimeTypeOfSource());
97
-
98
- $this->assertSame(60, $converter->getCalculatedQuality());
99
-
100
- // Test that it is still the same (testing caching)
101
- $this->assertSame(60, $converter->getCalculatedQuality());
102
- }
103
-
104
- public function testAutoQualityMaxQualityOnNonJpeg()
105
- {
106
- $converter = SuccessGuaranteedConverter::createInstance(
107
- self::getImagePath('test.png'),
108
- self::getImagePath('test.png.webp'),
109
- [
110
- 'max-quality' => 60,
111
- 'quality' => 'auto',
112
- 'default-quality' => 70,
113
- ]
114
- );
115
-
116
- $this->assertSame(60, $converter->getCalculatedQuality());
117
- $this->assertFalse($converter->isQualityDetectionRequiredButFailing());
118
- }
119
- /*
120
- public function testAutoQualityOnQualityDetectionFail1()
121
- {
122
- $converter = SuccessGuaranteedConverter::createInstance(
123
- self::$imgDir . '/non-existing.jpg',
124
- self::$imgDir . '/non-existant.webp',
125
- [
126
- 'max-quality' => 70,
127
- 'quality' => 'auto',
128
- 'default-quality' => 60,
129
- ]
130
- );
131
-
132
- $this->assertFalse(file_exists(self::$imgDir . '/non-existing.jpg'));
133
-
134
- // MimeType guesser returns false when mime type cannot be established.
135
- $this->assertEquals(false, $converter->getMimeTypeOfSource());
136
-
137
- // - so this can actually not be used for testing isQualityDetectionRequiredButFailing
138
-
139
- //$this->assertSame(60, $converter->getCalculatedQuality());
140
- //$this->assertTrue($converter->isQualityDetectionRequiredButFailing());
141
- }
142
- */
143
- public function testAutoQualityOnQualityDetectionFail2()
144
- {
145
- $converter = SuccessGuaranteedConverter::createInstance(
146
- self::getImagePath('text-with-jpg-extension.jpg'),
147
- self::getImagePath('text-with-jpg-extension.jpg.webp'),
148
- [
149
- 'max-quality' => 70,
150
- 'quality' => 'auto',
151
- 'default-quality' => 60,
152
- ]
153
- );
154
-
155
- $this->assertFalse(file_exists(self::getImagePath('non-existing.jpg')));
156
-
157
- // We are using the lenient MimeType guesser.
158
- // So we get "image/jpeg" even though the file is not a jpeg file
159
- $this->assertEquals('image/jpeg', $converter->getMimeTypeOfSource());
160
-
161
- // Now we got a file that we should not be able to detect quality of
162
- // lets validate that statement:
163
-
164
- $this->assertTrue($converter->isQualityDetectionRequiredButFailing());
165
-
166
- // Test that it is still the same (testing caching)
167
- $this->assertTrue($converter->isQualityDetectionRequiredButFailing());
168
-
169
- $this->assertSame(60, $converter->getCalculatedQuality());
170
- }
171
-
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/ConverterTestHelper.php DELETED
@@ -1,188 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
5
- *
6
- * @link https://github.com/rosell-dk/webp-convert
7
- * @license MIT
8
- */
9
-
10
- namespace WebPConvert\Tests\Convert\Converters;
11
-
12
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
13
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
14
- use WebPConvert\Convert\Exceptions\ConversionFailed\UnhandledException;
15
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
16
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
17
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
18
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
19
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
20
-
21
- class ConverterTestHelper
22
- {
23
-
24
- /*
25
- public function testPNGDeclined()
26
- {
27
- try {
28
- $source = __DIR__ . '/../test.png';
29
- $destination = __DIR__ . '/../test.png.webp';
30
- Gd::convert($source, $destination, array(
31
- 'skip-pngs' => true,
32
- ));
33
- $testCase->fail('The conversion should have failed, because PNG should have been skipped');
34
-
35
- } catch (SystemRequirementsNotMetException $e) {
36
- // System requirements are not met, so could not make the test
37
- return;
38
- } catch (ConversionSkippedException $e) {
39
- // Yeah, this is what we wanted to test. And it went well!
40
- $testCase->assertTrue(true);
41
- } catch (ConversionFailedException $e) {
42
- $testCase->fail("A ConversionFailedException was thrown (and it was not the SystemRequirementsNotMetException)");
43
- } catch (\Exception $e) {
44
- $testCase->fail("An unexpected exception was thrown");
45
- }
46
- }
47
- */
48
- public static function getImageFolder()
49
- {
50
- return realpath(__DIR__ . '/../../images');
51
- }
52
-
53
- public static function getImagePath($image)
54
- {
55
- return self::getImageFolder() . '/' . $image;
56
- }
57
-
58
- private static function callConvert($converterClassName, $source, $destination, $converterOptions)
59
- {
60
- return call_user_func(
61
- ['WebPConvert\\Convert\\Converters\\' . $converterClassName, 'convert'],
62
- $source,
63
- $destination,
64
- $converterOptions
65
- );
66
- //$logger
67
-
68
- /*
69
- TODO: Consider using mikey179/vfsStream
70
- https://github.com/mikey179/vfsStream
71
- https://phpunit.de/manual/6.5/en/test-doubles.html#test-doubles.mocking-the-filesystem
72
- */
73
- }
74
-
75
- public static function testInvalidDestinationFolder($testCase, $converterClassName, $converterOptions)
76
- {
77
- $testCase->expectException(CreateDestinationFolderException::class);
78
-
79
- try {
80
- $source = self::getImagePath('test.jpg');
81
- $destination = '/you-can-delete-me/';
82
- $result = self::callConvert($converterClassName, $source, $destination);
83
- } catch (ConverterNotOperationalException $e) {
84
- // Converter not operational, and that is ok!
85
- // We shall pretend that the expected exception was thrown, by throwing it!
86
- throw new CreateDestinationFolderException();
87
- }
88
- /*
89
- try {
90
- // We can only do this test, if the converter is operational.
91
- // In order to test that, we first do a normal conversion
92
- $source = (__DIR__ . '/../../test.jpg');
93
- $destination = (__DIR__ . '/../../test.webp');
94
-
95
- Imagick::convert($source, $destination);
96
-
97
- // if we are here, it means that the converter is operational.
98
- // Now do something that tests that the converter fails the way it should,
99
- // when it cannot create the destination file
100
-
101
- $this->expectException(\WebPConvert\Convert\Exceptions\ConverterFailedException::class);
102
-
103
- // I here assume that no system grants write access to their root folder
104
- // this is perhaps wrong to assume?
105
- $destinationFolder = '/you-can-delete-me/';
106
-
107
- Imagick::convert(__DIR__ . '/../test.jpg', $destinationFolder . 'you-can-delete-me.webp');
108
- } catch (\Exception $e) {
109
- // its ok...
110
- }*/
111
- }
112
-
113
- public static function testTargetNotFound($testCase, $converterClassName, $converterOptions)
114
- {
115
- $testCase->expectException(TargetNotFoundException::class);
116
-
117
- try {
118
- $result = self::callConvert(
119
- $converterClassName,
120
- __DIR__ . '/i-dont-exist.jpg',
121
- __DIR__ . '/i-dont-exist.webp',
122
- $converterOptions
123
- );
124
- } catch (ConverterNotOperationalException $e) {
125
- // Converter not operational, and that is ok!
126
- // We shall pretend that the expected exception was thrown, by throwing it!
127
- throw new TargetNotFoundException();
128
- }
129
- }
130
-
131
- /**
132
- * Test convert.
133
- * - It must either make a successful conversion, or throw the SystemRequirementsNotMetException
134
- * Other exceptions are unexpected and will result in test failure
135
- * - It must not return anything (as of 2.0, there is no return value)
136
- * - If conversion is successful, there must be a file at the destination
137
- */
138
- public static function testConvert($src, $testCase, $converterClassName, $converterOptions)
139
- {
140
-
141
- try {
142
- $source = self::getImagePath($src);
143
- $destination = self::getImagePath($src . '.webp');
144
-
145
- $result = self::callConvert($converterClassName, $source, $destination, $converterOptions);
146
-
147
- // Conversion was successful.
148
-
149
- // make sure the function did not return anything (as of 2.0)
150
- $testCase->assertEmpty($result, 'The doActualConvert() method returned something. As of 2.0, converters should never return anything');
151
-
152
- // verify that there indeed is a file
153
- $testCase->assertTrue(file_exists($destination), 'There is not a converted file at the destinaiton');
154
-
155
- } catch (ConverterNotOperationalException $e) {
156
- // Converter not operational, and that is ok!
157
- // (ie if system requirements are not met, or the quota of a cloud converter is used up)
158
- } catch (UnhandledException $e) {
159
- // Handle the UnhandledException specially, so we can display the original error
160
- $prevEx = $e->getPrevious();
161
- $testCase->fail(
162
- 'An UnhandledException was thrown: ' .
163
- get_class($prevEx). '. ' .
164
- $prevEx->getMessage() . '. ' .
165
- $prevEx->getFile() . ', line:' . $prevEx->getLine()
166
- //'Trace:' . $prevEx->getTraceAsString()
167
- );
168
- } catch (ConversionFailedException $e) {
169
- $testCase->fail(
170
- "A ConversionFailedException was thrown (and it was not a ConverterNotOperationalException). The exception was: " .
171
- get_class($e) .
172
- ". The message was: '" . $e->getMessage() . "'");
173
- } catch (\Exception $e) {
174
- $testCase->fail("An unexpected exception was thrown:" . get_class($e) . '. Message:' . $e->getMessage());
175
- }
176
- }
177
-
178
- public static function runAllConvertTests($testCase, $converterClassName, $converterOptions = [])
179
- {
180
- $converterOptions['encoding'] = 'auto';
181
- self::testConvert('test.jpg', $testCase, $converterClassName, $converterOptions);
182
- self::testConvert('test.png', $testCase, $converterClassName, $converterOptions);
183
- //self::testConvert('not-true-color.png', $testCase, $converterClassName, $converterOptions);
184
-
185
- self::testTargetNotFound($testCase, $converterClassName, $converterOptions);
186
- self::testInvalidDestinationFolder($testCase, $converterClassName, $converterOptions);
187
- }
188
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/CwebpTest.php DELETED
@@ -1,292 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
5
- *
6
- * @link https://github.com/rosell-dk/webp-convert
7
- * @license MIT
8
- */
9
-
10
- namespace WebPConvert\Tests\Convert\Converters;
11
-
12
- use WebPConvert\Convert\Converters\Cwebp;
13
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
14
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
15
-
16
- use WebPConvert\Tests\Convert\Exposers\CwebpExposer;
17
-
18
- use PHPUnit\Framework\TestCase;
19
-
20
- /**
21
- * @coversDefaultClass WebPConvert\Convert\Converters\Cwebp
22
- * @covers WebPConvert\Convert\Converters\Cwebp
23
- */
24
- class CwebpTest extends TestCase
25
- {
26
-
27
- public static function getImageFolder()
28
- {
29
- return realpath(__DIR__ . '/../../images');
30
- }
31
-
32
- public static function getImagePath($image)
33
- {
34
- return self::getImageFolder() . '/' . $image;
35
- }
36
-
37
- public function testConvert()
38
- {
39
- ConverterTestHelper::runAllConvertTests($this, 'Cwebp');
40
- }
41
-
42
- public function testSource()
43
- {
44
- $source = self::getImagePath('test.png');
45
- $cwebp = new Cwebp($source, $source . '.webp');
46
- $cwebpExposer = new CwebpExposer($cwebp);
47
-
48
- $this->assertEquals($source, $cwebpExposer->getSource());
49
- $this->assertTrue(file_exists($source), 'source does not exist');
50
- }
51
-
52
- /**
53
- * @covers ::createCommandLineOptions
54
- */
55
- public function testCreateCommandLineOptions()
56
- {
57
- $source = self::getImagePath('test.png');
58
- $options = [
59
- 'quality' => 'auto',
60
- 'method' => 3,
61
- 'command-line-options' => '-sharpness 5 -crop 10 10 40 40 -low_memory',
62
- ];
63
- $cwebp = new Cwebp($source, $source . '.webp', $options);
64
- $cwebpExposer = new CwebpExposer($cwebp);
65
-
66
- //$cwebpExposer->prepareOptions();
67
-
68
- $commandLineOptions = $cwebpExposer->createCommandLineOptions();
69
- //$this->assertEquals('e', $commandLineOption); // use this to quickly see it...
70
-
71
- // Per default we have no preset set
72
- $this->assertNotRegExp('#-preset#', $commandLineOptions);
73
-
74
- // Metadata is per default none
75
- $this->assertRegExp('#-metadata none#', $commandLineOptions);
76
-
77
- // We passed the method option and set it to 3
78
- $this->assertRegExp('#-m 3#', $commandLineOptions);
79
-
80
- // There must be an output option, and it must be quoted
81
- $this->assertRegExp('#-o \'#', $commandLineOptions);
82
-
83
- // There must be a quality option, and it must be digits
84
- $this->assertRegExp('#-q \\d+#', $commandLineOptions);
85
-
86
- // -sharpness '5'
87
- $this->assertRegExp('#-sharpness \'5\'#', $commandLineOptions);
88
-
89
- // Extra command line option with multiple values. Each are escapeshellarg'ed
90
- $this->assertRegExp('#-crop \'10\' \'10\' \'40\' \'40\'#', $commandLineOptions);
91
-
92
- // Command line option (flag)
93
- $this->assertRegExp('#-low_memory#', $commandLineOptions);
94
-
95
- // -sharpness '5'
96
- $this->assertRegExp('#-sharpness \'5\'#', $commandLineOptions);
97
- }
98
-
99
- /**
100
- * @covers ::createCommandLineOptions
101
- */
102
- public function testCreateCommandLineOptions2()
103
- {
104
- $source = self::getImagePath('test.png');
105
- $options = [
106
- 'quality' => 70,
107
- 'method' => 3,
108
- 'size-in-percentage' => 55,
109
- 'preset' => 'picture'
110
- ];
111
- $cwebp = new Cwebp($source, $source . '.webp', $options);
112
- $cwebpExposer = new CwebpExposer($cwebp);
113
-
114
- //$cwebpExposer->prepareOptions();
115
-
116
- $commandLineOptions = $cwebpExposer->createCommandLineOptions();
117
-
118
- // Preset
119
- $this->assertRegExp('#-preset picture#', $commandLineOptions);
120
-
121
- // Size
122
- $fileSizeInBytes = floor($options['size-in-percentage']/100 * filesize($source));
123
- $this->assertEquals(1714, $fileSizeInBytes);
124
- $this->assertRegExp('#-size ' . $fileSizeInBytes . '#', $commandLineOptions);
125
-
126
- // There must be no quality option, because -size overrules it.
127
- $this->assertNotRegExp('#-q \\d+#', $commandLineOptions);
128
- }
129
-
130
- /**
131
- * @covers ::createCommandLineOptions
132
- */
133
- public function testCreateCommandLineOptions3()
134
- {
135
- $source = self::getImagePath('test.png');
136
- $options = [
137
- 'encoding' => 'lossless',
138
- 'near-lossless' => 75,
139
- 'auto-filter' => true,
140
- ];
141
- $cwebp = new Cwebp($source, $source . '.webp', $options);
142
- $cwebpExposer = new CwebpExposer($cwebp);
143
-
144
- $commandLineOptions = $cwebpExposer->createCommandLineOptions();
145
-
146
- // near-lossless
147
- $this->assertRegExp('#-near_lossless 75#', $commandLineOptions);
148
-
149
- // There must be no -lossless option, because -near-lossless overrules it.
150
- $this->assertNotRegExp('#-lossless#', $commandLineOptions);
151
-
152
- // auto-filter
153
- $this->assertRegExp('#-af#', $commandLineOptions);
154
-
155
- // no low-memory
156
- $this->assertNotRegExp('#-low_memory#', $commandLineOptions);
157
- }
158
-
159
- /**
160
- * @covers ::createCommandLineOptions
161
- */
162
- public function testCreateCommandLineOptions4()
163
- {
164
- $source = self::getImagePath('test.png');
165
- $options = [
166
- 'encoding' => 'lossless',
167
- 'near-lossless' => 100,
168
- 'low-memory' => true,
169
- ];
170
- $cwebp = new Cwebp($source, $source . '.webp', $options);
171
- $cwebpExposer = new CwebpExposer($cwebp);
172
-
173
- $commandLineOptions = $cwebpExposer->createCommandLineOptions();
174
-
175
- // lossless
176
- $this->assertRegExp('#-lossless#', $commandLineOptions);
177
-
178
- // There must be no -near_lossless option, because -lossless overrules it.
179
- $this->assertNotRegExp('#-near_lossless#', $commandLineOptions);
180
-
181
- // low-memory
182
- $this->assertRegExp('#-low_memory#', $commandLineOptions);
183
-
184
- // No auto-filter
185
- $this->assertNotRegExp('#-af#', $commandLineOptions);
186
- }
187
-
188
- /**
189
- * @covers ::checkOperationality
190
- */
191
- public function testOperatinalityException()
192
- {
193
- $source = self::getImagePath('test.png');
194
- $options = [
195
- 'try-supplied-binary-for-os' => false,
196
- 'try-common-system-paths' => false,
197
- ];
198
- $this->expectException(ConverterNotOperationalException::class);
199
- //$cwebp = new Cwebp($source, $source . '.webp', $options);
200
- Cwebp::convert($source, $source . '.webp', $options);
201
- }
202
-
203
- public function testUsingSuppliedBinaryForOS()
204
- {
205
- $source = self::getImagePath('test.png');
206
- $options = [
207
- 'try-supplied-binary-for-os' => true,
208
- 'try-common-system-paths' => false,
209
- ];
210
- //$this->expectException(ConverterNotOperationalException::class);
211
- //$cwebp = new Cwebp($source, $source . '.webp', $options);
212
- try {
213
- Cwebp::convert($source, $source . '.webp', $options);
214
- } catch (ConversionFailedException $e) {
215
- // this is ok.
216
- // - but other exceptions are not!
217
- }
218
- $this->addToAssertionCount(1);
219
-
220
- }
221
-
222
- /*
223
- public function testCwebpDefaultPaths()
224
- {
225
- $default = [
226
- '/usr/bin/cwebp',
227
- '/usr/local/bin/cwebp',
228
- '/usr/gnu/bin/cwebp',
229
- '/usr/syno/bin/cwebp'
230
- ];
231
-
232
- foreach ($default as $key) {
233
- $this->assertContains($key, Cwebp::$cwebpDefaultPaths);
234
- }
235
- }*/
236
-
237
- /**
238
- * @expectedException \Exception
239
- */
240
- /*
241
- public function testUpdateBinariesInvalidFile()
242
- {
243
- $array = [];
244
-
245
- Cwebp::updateBinaries('InvalidFile', 'Hash', $array);
246
- }*/
247
-
248
- /**
249
- * @expectedException \Exception
250
- */
251
- /*
252
- public function testUpdateBinariesInvalidHash()
253
- {
254
- $array = [];
255
-
256
- Cwebp::updateBinaries('cwebp-linux', 'InvalidHash', $array);
257
- }
258
-
259
- public function testUpdateBinaries()
260
- {
261
- $file = 'cwebp.exe';
262
- $filePath = realpath(__DIR__ . '/../../Converters/Binaries/' . $file);
263
- $hash = hash_file('sha256', $filePath);
264
- $array = [];
265
-
266
- $this->assertContains($filePath, Cwebp::updateBinaries($file, $hash, $array));
267
- }
268
-
269
- public function testEscapeFilename()
270
- {
271
- $wrong = '/path/to/file Na<>me."ext"';
272
- $right = '/path/to/file\\\ Name.\&#34;ext\&#34;';
273
-
274
- $this->assertEquals($right, Cwebp::escapeFilename($wrong));
275
- }
276
-
277
- public function testHasNiceSupport()
278
- {
279
- $this->assertNotNull(Cwebp::hasNiceSupport());
280
- }*/
281
- /*
282
- public function testConvert()
283
- {
284
- $source = realpath(__DIR__ . '/../test.jpg');
285
- $destination = realpath(__DIR__ . '/../test.webp');
286
- $quality = 85;
287
- $stripMetadata = true;
288
-
289
- $this->assertTrue(Cwebp::convert($source, $destination, $quality, $stripMetadata));
290
- }*/
291
-
292
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/EwwwTest.php DELETED
@@ -1,97 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
5
- *
6
- * @link https://github.com/rosell-dk/webp-convert
7
- * @license MIT
8
- */
9
-
10
- namespace WebPConvert\Tests\Convert\Converters;
11
-
12
- use WebPConvert\Convert\Converters\Ewww;
13
- use PHPUnit\Framework\TestCase;
14
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
15
- use WebPConvert\Tests\Convert\TestConverters\ExtendedConverters\EwwwExtended;
16
-
17
- class EwwwTest extends TestCase
18
- {
19
-
20
- public static function getImageFolder()
21
- {
22
- return realpath(__DIR__ . '/../../images');
23
- }
24
-
25
- public static function getImagePath($image)
26
- {
27
- return self::getImageFolder() . '/' . $image;
28
- }
29
-
30
- public function testConvert()
31
- {
32
- ConverterTestHelper::runAllConvertTests($this, 'Ewww', [
33
- //'api-key' => ''
34
- ]);
35
- }
36
-
37
- public function testConvertInvalidKeyLessThan20()
38
- {
39
- $this->expectException(InvalidApiKeyException::class);
40
-
41
- $source = self::getImagePath('test.png');
42
- Ewww::convert($source, $source . '.webp', [
43
- 'api-key' => 'wrong-key!'
44
- ]);
45
- }
46
-
47
- public function testConvertInvalidKeyLess32()
48
- {
49
- $this->expectException(InvalidApiKeyException::class);
50
-
51
- $wrongKeyRightLength = 'invalid-key-but-hasright-length';
52
-
53
- $source = self::getImagePath('test.png');
54
- Ewww::convert($source, $source . '.webp', [
55
- 'api-key' => $wrongKeyRightLength
56
- ]);
57
- }
58
-
59
- public function testConvertInvalidKeyDuringConversion()
60
- {
61
- $this->expectException(InvalidApiKeyException::class);
62
-
63
- $wrongKeyRightLength = 'invalid-key-but-hasright-length';
64
-
65
- $source = self::getImagePath('test.png');
66
-
67
- $ee = EwwwExtended::createInstance($source, $source . '.webp', [
68
- 'api-key' => $wrongKeyRightLength
69
- ]);
70
-
71
- $ee->callDoActualConvert();
72
- }
73
-
74
-
75
- public function testIsValidKey()
76
- {
77
- $invalidKey = 'notvalidno';
78
- $this->assertFalse(Ewww::isValidKey($invalidKey));
79
-
80
- $demoKey = 'abc123';
81
- $this->assertTrue(Ewww::isValidKey($demoKey));
82
-
83
-
84
- //InvalidApiKeyException
85
- }
86
-
87
- public function testIsWorkingKey()
88
- {
89
- $invalidKey = 'notvalidno';
90
- $this->assertFalse(Ewww::isWorkingKey($invalidKey));
91
-
92
- if (!empty(getenv('WEBPCONVERT_EWWW_API_KEY'))) {
93
- $realWorkingKey = getenv('WEBPCONVERT_EWWW_API_KEY');
94
- $this->assertTrue(Ewww::isWorkingKey($realWorkingKey));
95
- }
96
- }
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/GdTest.php DELETED
@@ -1,254 +0,0 @@
1
- <?php
2
- namespace WebPConvert\Tests\Convert\Converters;
3
-
4
-
5
- use WebPConvert\Tests\Convert\Exposers\GdExposer;
6
- use WebPConvert\Convert\Converters\Gd;
7
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
8
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
9
-
10
- use PHPUnit\Framework\TestCase;
11
-
12
- class GdTest extends TestCase
13
- {
14
-
15
- public static function getImageFolder()
16
- {
17
- return realpath(__DIR__ . '/../../images');
18
- }
19
-
20
- public static function getImagePath($image)
21
- {
22
- return self::getImageFolder() . '/' . $image;
23
- }
24
-
25
- public function __construct()
26
- {
27
-
28
- }
29
-
30
- public function testConvert()
31
- {
32
-
33
- ConverterTestHelper::runAllConvertTests($this, 'Gd');
34
- }
35
-
36
- private function createGd($src)
37
- {
38
- $source = self::getImagePath($src);
39
- $this->assertTrue(file_exists($source), 'source does not exist:' . $source);
40
-
41
- return new Gd($source, $source . '.webp');
42
- }
43
-
44
- private function createGdExposer($src)
45
- {
46
- $gd = $this->createGd($src);
47
- return new GdExposer($gd);
48
- }
49
-
50
- private static function resetPretending()
51
- {
52
- reset_pretending();
53
- }
54
-
55
- // pretend imagewebp is missing
56
- public function testNotOperational1()
57
- {
58
- global $pretend;
59
-
60
- $gd = $this->createGd('test.png');
61
- self::resetPretending();
62
-
63
- $pretend['functionsNotExisting'] = ['imagewebp'];
64
- $this->expectException(SystemRequirementsNotMetException::class);
65
- $gd->checkOperationality();
66
- }
67
-
68
- // pretend gd is not loaded
69
- public function testNotOperational2()
70
- {
71
- global $pretend;
72
-
73
- $gd = $this->createGd('test.png');
74
- self::resetPretending();
75
-
76
- $pretend['extensionsNotExisting'] = ['gd'];
77
- $this->expectException(SystemRequirementsNotMetException::class);
78
- $gd->checkOperationality();
79
- $pretend['extensionsNotExisting'] = [];
80
- }
81
-
82
- // pretend imagecreatefrompng is missing
83
- public function testCheckConvertability1()
84
- {
85
- global $pretend;
86
-
87
- $gd = $this->createGd('test.png');
88
- self::resetPretending();
89
-
90
- $pretend['functionsNotExisting'] = ['imagecreatefrompng'];
91
- $this->expectException(SystemRequirementsNotMetException::class);
92
- $gd->checkConvertability();
93
- $pretend['functionsNotExisting'] = [];
94
- }
95
-
96
- // pretend imagecreatefrompng is working
97
- public function testCheckConvertability2()
98
- {
99
- global $pretend;
100
-
101
- $gd = $this->createGd('test.png');
102
- self::resetPretending();
103
-
104
- $pretend['functionsExisting'] = ['imagecreatefrompng'];
105
- $gd->checkConvertability();
106
- $pretend['functionsExisting'] = [];
107
- }
108
-
109
- // pretend imagecreatefromjpeg is missing
110
- public function testCheckConvertability3()
111
- {
112
- global $pretend;
113
-
114
- $gd = $this->createGd('test.jpg');
115
- self::resetPretending();
116
-
117
- $pretend['functionsNotExisting'] = ['imagecreatefromjpeg'];
118
- $this->expectException(SystemRequirementsNotMetException::class);
119
- $gd->checkConvertability();
120
- $pretend['functionsNotExisting'] = [];
121
- }
122
-
123
- public function testSource()
124
- {
125
-
126
- $source = self::getImagePath('test.png');
127
- $gd = new Gd($source, $source . '.webp');
128
-
129
- self::resetPretending();
130
-
131
- $gdExposer = new GdExposer($gd);
132
-
133
- $this->assertEquals($source, $gdExposer->getSource());
134
- $this->assertTrue(file_exists($source), 'source does not exist');
135
- }
136
-
137
- public function testCreateImageResource()
138
- {
139
- $gd = $this->createGd('test.png');
140
- self::resetPretending();
141
-
142
- $gdExposer = new GdExposer($gd);
143
-
144
- if (!$gdExposer->isOperating()) {
145
- //$this->assertTrue(false);
146
- return;
147
- }
148
-
149
- // It is operating and image should be ok.
150
- // - so it should be able to create image resource
151
- $image = $gdExposer->createImageResource();
152
- $this->assertEquals(gettype($image), 'resource');
153
-
154
- /*
155
- // Try the workaround method.
156
- $result = $gdExposer->makeTrueColorUsingWorkaround($image);
157
-
158
- // As the workaround is pretty sturdy, let us assert that it simply works.
159
- // It would be good to find out if it doesn't, anyway!
160
- $this->assertTrue($result); */
161
-
162
- //$gdExposer->tryToMakeTrueColorIfNot($image);
163
- $this->assertTrue(imageistruecolor($image), 'image is not true color');
164
-
165
- $result = $gdExposer->trySettingAlphaBlending($image);
166
- $this->assertTrue($result, 'failed setting alpha blending');
167
- }
168
-
169
- public function testStuffOnNotTrueColor()
170
- {
171
- $gd = $this->createGd('not-true-color.png');
172
- self::resetPretending();
173
-
174
- $gdExposer = new GdExposer($gd);
175
-
176
- if (!$gdExposer->isOperating()) {
177
- return;
178
- }
179
-
180
- // It is operating and image should be ok.
181
- // - so it should be able to create image resource
182
- $image = $gdExposer->createImageResource();
183
- $this->assertEquals(gettype($image), 'resource');
184
- $this->assertFalse(imageistruecolor($image), 'image is already true color');
185
- $gdExposer->tryToMakeTrueColorIfNot($image);
186
- $this->assertTrue(imageistruecolor($image), 'image is not true color after trying to make it');
187
- $result = $gdExposer->trySettingAlphaBlending($image);
188
- $this->assertTrue($result, 'failed setting alpha blending');
189
-
190
- // Test the workaround method.
191
- $gd = $this->createGd('not-true-color.png');
192
- $gdExposer = new GdExposer($gd);
193
- $image = $gdExposer->createImageResource();
194
- $this->assertFalse(imageistruecolor($image), 'image is already true color');
195
-
196
- //$image = imagecreatetruecolor(imagesx($image), imagesy($image));
197
- $result = $gdExposer->makeTrueColorUsingWorkaround($image);
198
- //$result = $gd->makeTrueColorUsingWorkaround($image);
199
- $this->assertTrue($result);
200
- $this->assertTrue(imageistruecolor($image), 'image is not true color after trying to make it (with workaround method)');
201
- $result = $gdExposer->trySettingAlphaBlending($image);
202
- $this->assertTrue($result, 'failed setting alpha blending');
203
- }
204
-
205
- public function testConvertFailure()
206
- {
207
- $gdExposer = $this->createGdExposer('not-true-color.png');
208
- self::resetPretending();
209
-
210
- // The next requires imagewebp...
211
- if (!function_exists('imagewebp')) {
212
- return;
213
- }
214
-
215
- $image = $gdExposer->createImageResource();
216
-
217
- // This image is not true color.
218
- // Trying to convert it fails (empty string is generated)
219
- // Assert that I am right!
220
- ob_start();
221
-
222
- // suppress the warning,
223
- // which is:
224
- // Warning: imagewebp(): Palette image not supported by webp in /var/www/wc/wc0/webp-convert/tests/Convert/Converters/GdTest.php on line 215
225
- @imagewebp($image, null, 80);
226
- $output = ob_get_clean();
227
- $this->assertEquals($output, '');
228
-
229
- // similary, we should get an exception when calling tryConverting ('Gd failed: imagewebp() returned empty string')
230
- $this->expectException(ConversionFailedException::class);
231
- $gdExposer->tryConverting($image);
232
-
233
- //$gdExposer->tryToMakeTrueColorIfNot($image);
234
-
235
- //$pretend['functionsNotExisting'] = ['imagewebp'];
236
-
237
- }
238
- /*
239
- public function testMakeTrueColorUsingWorkaround()
240
- {
241
- $gd = $this->createGd('test.png');
242
- self::resetPretending();
243
-
244
- $gdExposer = new GdExposer($gd);
245
-
246
- if (!$gdExposer->isOperating()) {
247
- return;
248
- }
249
-
250
- }*/
251
-
252
- }
253
-
254
- require_once('pretend.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/GmagickTest.php DELETED
@@ -1,29 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters;
4
-
5
- use PHPUnit\Framework\TestCase;
6
-
7
- class GmagickTest extends TestCase
8
- {
9
-
10
- public function testConvert()
11
- {
12
- ConverterTestHelper::runAllConvertTests($this, 'Gmagick');
13
- }
14
-
15
- /*
16
- public function testSupported()
17
- {
18
- if (!extension_loaded('Gmagick')) {
19
- return;
20
- }
21
- if (!class_exists('Gmagick')) {
22
- return;
23
- }
24
- $im = new \Gmagick();
25
- $this->assertSame([], $im->queryformats());
26
-
27
- }
28
- */
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/GraphicsMagickTest.php DELETED
@@ -1,67 +0,0 @@
1
- <?php
2
- /**
3
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
4
- *
5
- * @link https://github.com/rosell-dk/webp-convert
6
- * @license MIT
7
- */
8
-
9
- namespace WebPConvert\Tests\Convert\Converters;
10
-
11
- use WebPConvert\Convert\Converters\GraphicsMagick;
12
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
13
- use WebPConvert\Loggers\BufferLogger;
14
-
15
- use PHPUnit\Framework\TestCase;
16
-
17
- /**
18
- * @coversDefaultClass WebPConvert\Convert\Converters\GraphicsMagick
19
- * @covers WebPConvert\Convert\Converters\GraphicsMagick
20
- */
21
- class GraphicsMagickTest extends TestCase
22
- {
23
-
24
- public static function getImageFolder()
25
- {
26
- return realpath(__DIR__ . '/../../images');
27
- }
28
-
29
- public static function getImagePath($image)
30
- {
31
- return self::getImageFolder() . '/' . $image;
32
- }
33
-
34
- public function testConvert()
35
- {
36
- ConverterTestHelper::runAllConvertTests($this, 'GraphicsMagick');
37
- }
38
-
39
- private static function tryThis($test, $source, $options)
40
- {
41
- $bufferLogger = new BufferLogger();
42
-
43
- try {
44
- GraphicsMagick::convert($source, $source . '.webp', $options, $bufferLogger);
45
-
46
- $test->addToAssertionCount(1);
47
- } catch (ConversionFailedException $e) {
48
-
49
- //$bufferLogger->getText()
50
- throw $e;
51
- } catch (ConverterNotOperationalException $e) {
52
- // (SystemRequirementsNotMetException is also a ConverterNotOperationalException)
53
- // this is ok.
54
- return;
55
- }
56
- }
57
-
58
- public function testWithNice() {
59
- $source = self::getImagePath('test.png');
60
- $options = [
61
- 'use-nice' => true,
62
- 'lossless' => true,
63
- ];
64
- self::tryThis($this, $source, $options);
65
- }
66
-
67
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImageMagickTest.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
- /**
3
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
4
- *
5
- * @link https://github.com/rosell-dk/webp-convert
6
- * @license MIT
7
- */
8
-
9
- namespace WebPConvert\Tests\Convert\Converters;
10
-
11
- use WebPConvert\Convert\Converters\ImageMagick;
12
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
13
- use WebPConvert\Loggers\BufferLogger;
14
-
15
- use PHPUnit\Framework\TestCase;
16
-
17
- class ImageMagickTest extends TestCase
18
- {
19
-
20
- public static function getImageFolder()
21
- {
22
- return realpath(__DIR__ . '/../../images');
23
- }
24
-
25
- public static function getImagePath($image)
26
- {
27
- return self::getImageFolder() . '/' . $image;
28
- }
29
-
30
- public function testConvert()
31
- {
32
- ConverterTestHelper::runAllConvertTests($this, 'ImageMagick');
33
- }
34
-
35
- private static function tryThis($test, $source, $options)
36
- {
37
- $bufferLogger = new BufferLogger();
38
-
39
- try {
40
- ImageMagick::convert($source, $source . '.webp', $options, $bufferLogger);
41
-
42
- $test->addToAssertionCount(1);
43
- } catch (ConversionFailedException $e) {
44
-
45
- //$bufferLogger->getText()
46
- throw $e;
47
- } catch (ConverterNotOperationalException $e) {
48
- // (SystemRequirementsNotMetException is also a ConverterNotOperationalException)
49
- // this is ok.
50
- return;
51
- }
52
- }
53
-
54
- public function testWithNice() {
55
- $source = self::getImagePath('test.png');
56
- $options = [
57
- 'use-nice' => true,
58
- 'encoding' => 'lossless',
59
- ];
60
- self::tryThis($this, $source, $options);
61
- }
62
-
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImagickTest.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters;
4
-
5
- use WebPConvert\Convert\Converters\Imagick;
6
-
7
- use PHPUnit\Framework\TestCase;
8
-
9
- /**
10
- * @coversDefaultClass WebPConvert\Convert\Converters\Imagick
11
- * @covers WebPConvert\Convert\Converters\Imagick
12
- */
13
- class ImagickTest extends TestCase
14
- {
15
-
16
- public static $imageDir = __DIR__ . '/../../images/';
17
-
18
- public function testConvert()
19
- {
20
- ConverterTestHelper::runAllConvertTests($this, 'Imagick');
21
- }
22
-
23
- /**
24
- * @coversNothing
25
- */
26
- public function testQueryFormats()
27
- {
28
- if (!extension_loaded('imagick')) {
29
- $this->markTestSkipped(
30
- 'The imagick extension is not available.'
31
- );
32
- return;
33
- }
34
- //if (!class_exists('\\Imagick')) {}
35
-
36
- $im = new \Imagick();
37
-
38
- $this->assertEquals(1, count($im->queryFormats('JPEG')));
39
- $this->assertGreaterThan(2, count($im->queryFormats('*')));
40
- $this->assertGreaterThan(2, count($im->queryFormats()));
41
- $this->assertEquals(count($im->queryFormats('*')), count($im->queryFormats()));
42
- }
43
-
44
- /**
45
- * @coversNothing
46
- */
47
- public function testThatImagickFunctionsUsedDoesNotThrow()
48
- {
49
- if (!extension_loaded('imagick')) {
50
- $this->markTestSkipped(
51
- 'The imagick extension is not available.'
52
- );
53
- return;
54
- }
55
- $im = new \Imagick(self::$imageDir . '/test.jpg');
56
- $im->setImageFormat('JPEG');
57
- $im->stripImage();
58
- $im->setImageCompressionQuality(100);
59
- $imageBlob = $im->getImageBlob();
60
-
61
- $this->addToAssertionCount(1);
62
- }
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/StackTest.php DELETED
@@ -1,56 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters;
4
-
5
- use WebPConvert\Convert\Converters\Stack;
6
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
7
- use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
8
-
9
- use PHPUnit\Framework\TestCase;
10
-
11
- class StackTest extends TestCase
12
- {
13
-
14
- public static function getImageFolder()
15
- {
16
- return realpath(__DIR__ . '/../../images');
17
- }
18
-
19
- public static function getImagePath($image)
20
- {
21
- return self::getImageFolder() . '/' . $image;
22
- }
23
-
24
- public function testConvert()
25
- {
26
- //ConverterTestHelper::runAllConvertTests($this, 'Stack');
27
- }
28
-
29
- public function testConverterNotFound()
30
- {
31
- $this->expectException(ConverterNotFoundException::class);
32
-
33
- Stack::convert(
34
- self::getImagePath('test.jpg'),
35
- self::getImagePath('test.webp'),
36
- [
37
- 'converters' => ['invalid-id']
38
- ]
39
- );
40
- }
41
-
42
- public function testCustomConverter()
43
- {
44
- Stack::convert(
45
- self::getImagePath('test.jpg'),
46
- self::getImagePath('test.webp'),
47
- [
48
- 'converters' => [
49
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
50
- ]
51
- ]
52
- );
53
- $this->addToAssertionCount(1);
54
- }
55
-
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/VipsTest.php DELETED
@@ -1,234 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Converters;
4
-
5
- use WebPConvert\Convert\Converters\Vips;
6
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
7
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
8
- use WebPConvert\Tests\Convert\Exposers\VipsExposer;
9
-
10
- use PHPUnit\Framework\TestCase;
11
-
12
- /**
13
- * @coversDefaultClass WebPConvert\Convert\Converters\Vips
14
- * @covers WebPConvert\Convert\Converters\Vips
15
- */
16
- class VipsTest extends TestCase
17
- {
18
-
19
- public function getImageFolder()
20
- {
21
- return realpath(__DIR__ . '/../../images');
22
- }
23
-
24
- public function getImagePath($image)
25
- {
26
- return $this->getImageFolder() . '/' . $image;
27
- }
28
-
29
- public function __construct()
30
- {
31
- //require_once('pretend.inc');
32
- }
33
-
34
- public function testConvert()
35
- {
36
- $options = [];
37
- ConverterTestHelper::runAllConvertTests($this, 'Vips', $options);
38
-
39
- $options = [
40
- 'smart-subsample' => true,
41
- 'preset' => 'text',
42
- ];
43
- ConverterTestHelper::runAllConvertTests($this, 'Vips', $options);
44
- }
45
-
46
-
47
- private function createVips($src, $options = [])
48
- {
49
- $source = $this->getImagePath($src);
50
- $this->assertTrue(file_exists($source), 'source does not exist:' . $source);
51
-
52
- return new Vips($source, $source . '.webp', $options);
53
- }
54
-
55
- private function createVipsExposer($src, $options = [])
56
- {
57
- return new VipsExposer($this->createVips($src, $options));
58
- }
59
-
60
- private function isVipsOperational()
61
- {
62
- try {
63
- $vips->checkOperationality();
64
- $vips->checkConvertability();
65
- } catch (\Exception $e) {
66
- return false;
67
- }
68
- return true;
69
- }
70
-
71
- public function testCreateParamsForVipsWebPSave1()
72
- {
73
- $options = [
74
- 'encoding' => 'lossless',
75
- 'smart-subsample' => true,
76
- 'near-lossless' => 90,
77
- 'preset' => 'picture', // In vips, this has the constant: 1
78
- ];
79
- $vipsExposer = $this->createVipsExposer('test.png', $options);
80
-
81
- $vipsParams = $vipsExposer->createParamsForVipsWebPSave();
82
-
83
- // Check some options that are straightforwardly copied
84
- $this->assertSame(true, $vipsParams['lossless']);
85
- $this->assertSame($options['smart-subsample'], $vipsParams['smart_subsample']);
86
- $this->assertSame(1, $vipsParams['preset']);
87
-
88
- // When near-lossless is set, the value should be copied to Q
89
- $this->assertSame($options['near-lossless'], $vipsParams['Q']);
90
- }
91
-
92
- public function testCreateParamsForVipsWebPSave2()
93
- {
94
- $options = [
95
- 'alpha-quality' => 100
96
- ];
97
- $vipsExposer = $this->createVipsExposer('test.png', $options);
98
-
99
- $vipsParams = $vipsExposer->createParamsForVipsWebPSave();
100
-
101
- // Some options are only set if they differ from default
102
- $this->assertFalse(isset($vipsParams['smart_subsample']));
103
- $this->assertFalse(isset($vipsParams['alpha_q']));
104
- }
105
-
106
- public function testCreateImageResource1()
107
- {
108
- // Exit if vips is not operational
109
- if (!$this->isVipsOperational()) {
110
- return;
111
- }
112
-
113
- $source = $this->getImagePath('non-existing');
114
- $vips = new Vips($source, $source . '.webp', []);
115
- $vipsExposer = new VipsExposer($vips);
116
-
117
- // It must fail because it should not be able to create resource when file does not exist
118
- $this->expectException(ConversionFailedException::class);
119
-
120
- $vipsExposer->createImageResource();
121
- }
122
-
123
- public function testNotOperational1()
124
- {
125
- global $pretend;
126
-
127
- $vips = $this->createVips('test.png');
128
- reset_pretending();
129
-
130
- // pretend vips_image_new_from_file
131
- $pretend['functionsNotExisting'] = ['vips_image_new_from_file'];
132
- $this->expectException(SystemRequirementsNotMetException::class);
133
- $vips->checkOperationality();
134
- }
135
-
136
- public function testNotOperational2()
137
- {
138
- global $pretend;
139
-
140
- $vips = $this->createVips('test.png');
141
- reset_pretending();
142
-
143
- // pretend vips_image_new_from_file
144
- $pretend['extensionsNotExisting'] = ['vips'];
145
- $this->expectException(SystemRequirementsNotMetException::class);
146
- $vips->checkOperationality();
147
- }
148
-
149
- public function testOperational1()
150
- {
151
- global $pretend;
152
-
153
- $vips = $this->createVips('test.png');
154
- reset_pretending();
155
-
156
- // pretend vips_image_new_from_file
157
- $pretend['functionsExisting'] = ['vips_image_new_from_file'];
158
- $pretend['extensionsExisting'] = ['vips'];
159
- $vips->checkOperationality();
160
-
161
- $this->addToAssertionCount(1);
162
- }
163
-
164
- /**
165
- * @covers ::webpsave
166
- */
167
- public function testWebpsave()
168
- {
169
- reset_pretending();
170
-
171
- $vips = $this->createVips('test.png', []);
172
- $vipsExposer = new VipsExposer($vips);
173
-
174
- // Exit if vips is not operational
175
- if (!$this->isVipsOperational()) {
176
- $this->markTestSkipped('vips is not operational');
177
- return;
178
- }
179
-
180
- $im = $vipsExposer->createImageResource();
181
- $options = $vipsExposer->createParamsForVipsWebPSave();
182
-
183
- // Create non-existing vips option.
184
- // - The converter must be able to ignore this without failing
185
- $options['non-existing-vips-option'] = true;
186
- $vipsExposer->webpsave($im, $options);
187
- }
188
-
189
- /**
190
- * @covers ::createImageResource
191
- */
192
- public function testCreateImageResourceWhenFileNotFound()
193
- {
194
- //
195
- reset_pretending();
196
-
197
- // Exit if vips is not operational
198
- if (!$this->isVipsOperational()) {
199
- $this->markTestSkipped('vips is not operational');
200
- return;
201
- }
202
-
203
- $source = self::$imageDir . '/i-do-not-exist.jpg';
204
- $this->assertFalse(file_exists($source));
205
-
206
- $options = [];
207
- $vips = new Vips($source, $source . '.webp', $options);
208
- $vipsExposer = new VipsExposer($vips);
209
-
210
- // this should fail!
211
- try {
212
- $im = $vipsExposer->createImageResource();
213
- $this->fail('exception was expected');
214
- } catch (ConversionFailedException $e) {
215
- $this->assertRegExp('#not found#', $e->getMessage());
216
- }
217
-
218
- }
219
- /*
220
- public function testDoActualConvert()
221
- {
222
-
223
- $options = [
224
- 'alpha-quality' => 100
225
- ];
226
- $vipsExposer = $this->createVipsExposer('test.png', $options);
227
-
228
- $vips = $this->createVips('not-existing.png');
229
-
230
- $this->addToAssertionCount(1);
231
- }*/
232
- }
233
-
234
- require_once('pretend.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/WPCTest.php DELETED
@@ -1,262 +0,0 @@
1
- <?php
2
- /**
3
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
4
- *
5
- * @link https://github.com/rosell-dk/webp-convert
6
- * @license MIT
7
- */
8
-
9
- namespace WebPConvert\Tests\Convert\Converters;
10
-
11
- use WebPConvert\Convert\Converters\Wpc;
12
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
13
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
14
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
15
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
16
- use WebPConvert\Loggers\BufferLogger;
17
-
18
- use PHPUnit\Framework\TestCase;
19
-
20
- /**
21
- * @coversDefaultClass WebPConvert\Convert\Converters\Wpc
22
- * @covers WebPConvert\Convert\Converters\Wpc
23
- */
24
- class WpcTest extends TestCase
25
- {
26
-
27
- public function getImageFolder()
28
- {
29
- return realpath(__DIR__ . '/../../images');
30
- }
31
-
32
- public function getImagePath($image)
33
- {
34
- return $this->getImageFolder() . '/' . $image;
35
- }
36
-
37
- /* public function testApi0()
38
- {
39
- if (!empty(getenv('WEBPCONVERT_WPC_API_URL_API0'))) {
40
- $source = $this->imageDir . '/test.png';
41
- Wpc::convert($source, $source . '.webp', [
42
- 'api-version' => 0,
43
- 'api-url' => getenv('WEBPCONVERT_WPC_API_URL_API0')
44
- ]);
45
- }
46
- }
47
- */
48
-
49
- private static function tryThis($test, $source, $options)
50
- {
51
- $bufferLogger = new BufferLogger();
52
-
53
- try {
54
- Wpc::convert($source, $source . '.webp', $options, $bufferLogger);
55
-
56
- $test->addToAssertionCount(1);
57
- } catch (ConversionFailedException $e) {
58
-
59
- // we accept this failure that seems to happen when WPC gets stressed:
60
- if (strpos($e->getMessage(), 'unable to open image') !== false) {
61
- return;
62
- }
63
-
64
- // we also accept this failure that also seems to happen when WPC gets stressed:
65
- if (strpos($e->getMessage(), 'We got nothing back') !== false) {
66
- return;
67
- }
68
-
69
- if ($e->getMessage() == 'Error saving file. Check file permissions') {
70
- throw new ConversionFailedException(
71
- 'Failed saving file. Here is the log:' . $bufferLogger->getText()
72
- );
73
- }
74
-
75
- throw $e;
76
- }
77
- }
78
-
79
- public function testApi0()
80
- {
81
- if (empty(getenv('WEBPCONVERT_WPC_API_URL_API0'))) {
82
- return;
83
- }
84
-
85
-
86
- $source = $this->getImagePath('test.png');
87
- $options = [
88
- 'api-version' => 0,
89
- 'api-url' => getenv('WEBPCONVERT_WPC_API_URL_API0'),
90
- 'lossless' => true,
91
- ];
92
-
93
- self::tryThis($this, $source, $options);
94
-
95
-
96
- }
97
-
98
- public function testApi1()
99
- {
100
- if (empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
101
- return;
102
- }
103
-
104
- $source = $this->getImagePath('test.png');
105
- $options = [
106
- 'api-version' => 1,
107
- 'crypt-api-key-in-transfer' => true,
108
- 'lossless' => true,
109
- ];
110
-
111
- self::tryThis($this, $source, $options);
112
- }
113
-
114
- public function testWrongSecretButRightUrl()
115
- {
116
- if (empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
117
- return;
118
- }
119
-
120
- $source = $this->getImagePath('test.png');
121
- $options = [
122
- 'api-version' => 1,
123
- 'crypt-api-key-in-transfer' => true,
124
- 'api-key' => 'wrong!',
125
- ];
126
-
127
- $this->expectException(InvalidApiKeyException::class);
128
- self::tryThis($this, $source, $options);
129
- }
130
-
131
- public function testBadURL()
132
- {
133
- $this->expectException(ConverterNotOperationalException::class);
134
-
135
- Wpc::convert(
136
- $this->getImagePath('test.png'),
137
- $this->getImagePath('test.webp'),
138
- [
139
- 'api-url' => 'badurl!',
140
- 'secret' => 'bad dog!',
141
- ]
142
- );
143
- }
144
-
145
- public function test404()
146
- {
147
- //$this->expectException(ConversionFailedException::class);
148
-
149
- try {
150
- Wpc::convert(
151
- $this->getImagePath('test.png'),
152
- $this->getImagePath('test.webp'),
153
- [
154
- 'api-url' => 'https://google.com/hello',
155
- 'secret' => 'bad dog!',
156
- ]
157
- );
158
- $this->fail('Expected an exception');
159
-
160
- } catch (ConversionFailedException $e) {
161
- // this is expected!
162
- $this->addToAssertionCount(1);
163
-
164
- $this->assertRegExp('#we got a 404 response#', $e->getMessage());
165
- }
166
-
167
- }
168
-
169
- public function testUnexpectedResponse()
170
- {
171
- //$this->expectException(ConversionFailedException::class);
172
-
173
- try {
174
- Wpc::convert(
175
- $this->getImagePath('test.png'),
176
- $this->getImagePath('test.webp'),
177
- [
178
- 'api-url' => 'https://www.google.com/',
179
- 'secret' => 'bad dog!',
180
- ]
181
- );
182
- $this->fail('Expected an exception');
183
-
184
- } catch (ConversionFailedException $e) {
185
- // this is expected!
186
- $this->addToAssertionCount(1);
187
-
188
- $this->assertRegExp('#We did not receive an image#', $e->getMessage());
189
- }
190
- }
191
-
192
-
193
- /*
194
- HMM.. Apparently wpc can't handle much stress.
195
- The runAllConvertTests often results in an error like this:
196
-
197
- 'WPC failed converting image: "unable to open image '../conversions/80c80b20834edd62456fe9e6da4d24d64be51dc1.jpg': No such file or directory @ error/blob.c/OpenBlob/3489"'
198
-
199
- public function testApi0()
200
- {
201
- if (!empty(getenv('WEBPCONVERT_WPC_API_URL_API0'))) {
202
- ConverterTestHelper::runAllConvertTests($this, 'Wpc', [
203
- 'api-version' => 0,
204
- 'api-url' => getenv('WEBPCONVERT_WPC_API_URL_API0')
205
- ]);
206
- }
207
- }
208
-
209
- public function testApi1()
210
- {
211
- if (empty(getenv('WEBPCONVERT_WPC_API_URL')) || empty(getenv('WEBPCONVERT_WPC_API_KEY'))) {
212
- return;
213
- }
214
-
215
- ConverterTestHelper::runAllConvertTests($this, 'Wpc', [
216
- 'api-version' => 1,
217
- 'crypt-api-key-in-transfer' => true
218
- ]);
219
-
220
- // TODO: Also test without crypt
221
- }
222
- */
223
-
224
- /*
225
- public function testMissingURL()
226
- {
227
- $this->expectException(ConverterNotOperationalException::class);
228
-
229
- Wpc::convert($this->imageDir . '/test.png', $this->imageDir . '/test.webp', [
230
- 'api-url' => '',
231
- 'secret' => 'bad dog!',
232
- ]);
233
- }*/
234
-
235
-
236
- /*
237
- public function testWrongSecretButRightUrl()
238
- {
239
- if (empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
240
- return;
241
- }
242
-
243
- $this->expectException(InvalidApiKeyException::class);
244
-
245
- Wpc::convert($this->imageDir . '/test.png', $this->imageDir . '/test.webp', [
246
- 'api-version' => 0,
247
- 'api-url' => getenv('WEBPCONVERT_WPC_API_URL'),
248
- 'secret' => 'purposely-wrong-secret!'
249
- ]);
250
- }
251
-
252
- public function testBadURL()
253
- {
254
- $this->expectException(ConverterNotOperationalException::class);
255
-
256
- Wpc::convert($this->imageDir . '/test.png', $this->imageDir . '/test.webp', [
257
- 'api-url' => 'badurl!',
258
- 'secret' => 'bad dog!',
259
- ]);
260
- }*/
261
-
262
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Converters/pretend.inc DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
-
3
- namespace {
4
- $pretend = [
5
- 'functionsNotExisting' => [],
6
- 'functionsExisting' => [],
7
- 'extensionsNotExisting' => [],
8
- 'extensionsExisting' => []
9
- ];
10
- $hasDeclaredMockFunctions = false;
11
-
12
- function reset_pretending()
13
- {
14
- global $pretend;
15
- $pretend = [
16
- 'functionsNotExisting' => [],
17
- 'functionsExisting' => [],
18
- 'extensionsNotExisting' => [],
19
- 'extensionsExisting' => []
20
- ];
21
-
22
- }
23
- }
24
-
25
- namespace WebPConvert\Convert\Converters {
26
-
27
-
28
- global $hasDeclaredMockFunctions;
29
-
30
- if(!$hasDeclaredMockFunctions) {
31
- $hasDeclaredMockFunctions = true;
32
- function function_exists($function) {
33
-
34
- global $pretend;
35
- if (in_array($function, $pretend['functionsNotExisting'])) {
36
- return false;
37
- }
38
- if (in_array($function, $pretend['functionsExisting'])) {
39
- return true;
40
- }
41
- return \function_exists($function);
42
- }
43
-
44
- function extension_loaded($extension) {
45
- global $pretend;
46
- if (in_array($extension, $pretend['extensionsNotExisting'])) {
47
- return false;
48
- }
49
- if (in_array($extension, $pretend['extensionsExisting'])) {
50
- return true;
51
- }
52
- return \extension_loaded($extension);
53
- }
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Exposers/AbstractConverterExposer.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Exposers;
4
-
5
- use WebPConvert\Convert\Converters\AbstractConverter;
6
- use WebPConvert\Tests\BaseExposer;
7
-
8
- /**
9
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
10
- * - so they can be tested
11
- *
12
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
13
- */
14
- class AbstractConverterExposer extends BaseExposer {
15
-
16
- //public static $extraOptions = [];
17
-
18
- public $converter;
19
- public static $currentlyCalling;
20
-
21
- public function __construct($converter)
22
- {
23
- parent::__construct($converter);
24
- }
25
-
26
- public function getSource()
27
- {
28
- return $this->getPrivateProperty('source');
29
- }
30
-
31
- public function isOperating()
32
- {
33
- $inject = function() {
34
- try {
35
- $this->checkOperationality();
36
- $this->checkConvertability();
37
- } catch (\Exception $e) {
38
- return false;
39
- }
40
- return true;
41
- };
42
- return $this->bindDynamicFunctionToObjectAndCallIt($inject);
43
- }
44
-
45
- /*
46
- public function prepareOptions()
47
- {
48
- $this->callPrivateFunction('prepareOptions', AbstractConverter::class);
49
- }*/
50
-
51
- public function getOptions()
52
- {
53
- return $this->getPrivateProperty('options', AbstractConverter::class);
54
- }
55
-
56
- /*
57
- public function getDefaultOptions()
58
- {
59
- //return $this->getPrivateStaticProperty('defaultOptions', AbstractConverter::class);
60
- return $this->callPrivateFunction('getDefaultOptions', AbstractConverter::class);
61
- }*/
62
-
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Exposers/CwebpExposer.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Exposers;
4
-
5
- /**
6
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
7
- * - so they can be tested
8
- *
9
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
10
- */
11
- class CwebpExposer extends AbstractConverterExposer {
12
-
13
- public function __construct($gd)
14
- {
15
- parent::__construct($gd);
16
- }
17
-
18
- public function createCommandLineOptions($version = '0.6.1')
19
- {
20
- return $this->callPrivateFunction('createCommandLineOptions', null, $version);
21
- }
22
-
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Exposers/GdExposer.php DELETED
@@ -1,110 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Exposers;
4
-
5
- use WebPConvert\Convert\Converters\Gd;
6
-
7
- /**
8
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
9
- * - so they can be tested
10
- *
11
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
12
- */
13
- class GdExposer extends AbstractConverterExposer {
14
-
15
- public function __construct($gd)
16
- {
17
- parent::__construct($gd);
18
- }
19
-
20
- public function createImageResource()
21
- {
22
- return $this->callPrivateFunction('createImageResource', null);
23
- }
24
-
25
-
26
- public function makeTrueColorUsingWorkaround(&$image)
27
- {
28
- return $this->callPrivateFunctionByRef('makeTrueColorUsingWorkaround', null, $image);
29
-
30
- // return $this->callPrivateFunction('makeTrueColorUsingWorkaround', null, $image);
31
- /*
32
- The following would also work:
33
-
34
- $cb = function(&$image) {
35
- echo 'callback:...' . gettype($image);
36
- return $this->makeTrueColorUsingWorkaround($image);
37
- };
38
- //$class = get_class(Gd::class);
39
- $functionNowBinded = $cb->bindTo($this->objectToExposeFrom, Gd::class);
40
-
41
- return $functionNowBinded($image);*/
42
- }
43
-
44
- public function trySettingAlphaBlending(&$image)
45
- {
46
- return $this->callPrivateFunctionByRef('trySettingAlphaBlending', null, $image);
47
- }
48
-
49
- public function tryToMakeTrueColorIfNot(&$image)
50
- {
51
- return $this->callPrivateFunctionByRef('tryToMakeTrueColorIfNot', null, $image);
52
- }
53
-
54
- public function tryConverting(&$image)
55
- {
56
- return $this->callPrivateFunctionByRef('tryConverting', null, $image);
57
- }
58
-
59
-
60
- /*
61
- public function checkOperationality()
62
- {
63
- $this->checkOperationality();
64
- }
65
-
66
- public function exposedCheckConvertability()
67
- {
68
- $this->checkConvertability();
69
- }
70
-
71
- public function exposedGetImage()
72
- {
73
- return $this->image;
74
- }
75
-
76
- public function exposedCreateImageResource()
77
- {
78
- $this->createImageResource();
79
- }
80
-
81
- */
82
- /*
83
- Other method for calling pnivate:
84
-
85
- https://stackoverflow.com/questions/2738663/call-private-methods-and-private-properties-from-outside-a-class-in-php/2738847#2738847
86
- $reflector = new \ReflectionClass(Gd::class);
87
- $reflector->getMethod('createImageResource')->setAccessible(true);
88
- $unlockedGate = $reflector->newInstance($source, $source . '.webp');
89
- $unlockedGate->createImageResource();
90
- */
91
-
92
- /*
93
- $gd = new Gd($source, $source . '.webp');
94
- $reflectedGd = new \ReflectionObject($gd);
95
- $createImageResourceMethod = $reflectedGd->getMethod('createImageResource');
96
- $createImageResourceMethod->setAccessible(true);
97
- $createImageResourceMethod->invoke();
98
-
99
- */
100
- // https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/
101
- /*
102
- $sourceThief = function($gd) {
103
- return $gd->source;
104
- };
105
-
106
- $gd = new Gd($source, $source . '.webp');
107
- $sourceThief = \Closure::bind($sourceThief, null, $gd);
108
- $this->assertEquals($source, $sourceThief($gd));
109
- */
110
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Exposers/VipsExposer.php DELETED
@@ -1,39 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Exposers;
4
-
5
- use WebPConvert\Convert\Converters\Vips;
6
-
7
- /**
8
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
9
- * - so they can be tested
10
- *
11
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
12
- */
13
- class VipsExposer extends AbstractConverterExposer {
14
-
15
- public function __construct($vips)
16
- {
17
- parent::__construct($vips);
18
- }
19
-
20
- public function createParamsForVipsWebPSave()
21
- {
22
- return $this->callPrivateFunction('createParamsForVipsWebPSave', null);
23
- }
24
-
25
- public function createImageResource()
26
- {
27
- return $this->callPrivateFunction('createImageResource', null);
28
- }
29
-
30
- public function doActualConvert()
31
- {
32
- return $this->callPrivateFunction('doActualConvert', null);
33
- }
34
-
35
- public function webpsave($im, $options)
36
- {
37
- return $this->callPrivateFunction('webpsave', null, $im, $options);
38
- }
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Helpers/JpegQualityDetectorTest.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Helpers;
4
-
5
- use WebPConvert\Convert\Helpers\JpegQualityDetector;
6
-
7
- use PHPUnit\Framework\TestCase;
8
-
9
- class JpegQualityDetectorTest extends TestCase
10
- {
11
-
12
- private static $imgDir = __DIR__ . '/../images';
13
-
14
- public function testDetectQualityOfJpg()
15
- {
16
- $result = JpegQualityDetector::detectQualityOfJpg(self::$imgDir . '/small-q61.jpg');
17
- if (is_null($result)) {
18
- $this->addToAssertionCount(1);
19
- } else {
20
- $this->assertSame(61, $result);
21
- }
22
- }
23
-
24
-
25
- public function testDetectQualityOfJpgNonExistantFile()
26
- {
27
- $result = JpegQualityDetector::detectQualityOfJpg('i dont exist');
28
-
29
- $this->assertNull($result);
30
- }
31
-
32
- // TODO: Test when PNG is supplied
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/Helpers/PhpIniSizesTest.php DELETED
@@ -1,85 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\BaseConverters;
4
-
5
- use WebPConvert\Convert\Helpers\PhpIniSizes;
6
-
7
- use PHPUnit\Framework\TestCase;
8
-
9
- class PhpIniSizesTest extends TestCase
10
- {
11
-
12
- public function testParseShortHandSize()
13
- {
14
- // Test without units
15
- $this->assertEquals(0, PhpIniSizes::parseShortHandSize('0'));
16
- $this->assertEquals(10, PhpIniSizes::parseShortHandSize('10'));
17
-
18
- // Test "k" unit
19
- $this->assertEquals(1024, PhpIniSizes::parseShortHandSize('1k'));
20
-
21
- // Test capitial "K"
22
- $this->assertEquals(1024, PhpIniSizes::parseShortHandSize('1K'));
23
-
24
- // Test "M" unit
25
- $this->assertEquals(1024 * 1024, PhpIniSizes::parseShortHandSize('1M'));
26
-
27
- // Test "G" unit
28
- $this->assertEquals(1024 * 1024 * 1024, PhpIniSizes::parseShortHandSize('1G'));
29
-
30
-
31
- // Moving to terrabytes, we have to be careful.
32
- // Terrabytes cannot be represented as integers on 32 bit systems.
33
- // (on 32 bit systems, max integer value is 107.374.182.400, which can represent up to ~107G)
34
-
35
- // Testing floating point numbers for equality is prone to errors.
36
- //$this->assertInternalType('int', PhpIniSizes::parseShortHandSize('10'));
37
- //$this->assertEquals(10.0, 10);
38
-
39
-
40
- // So, ie "200G" can not be represented by an int.
41
-
42
- // The computation goes:
43
- // floor($size) * pow(1024, stripos('bkmgtpezy', $unit[0]));
44
-
45
- // floor() always returns float, according to docs (but may also
46
- // pow() returns int unless the number is too high, in that case it returns float.
47
- // And the result? What do you get if you multiply an int and a float (which is in fact representating an integer),
48
- // and the result is more than PHP_INT_MAX?
49
- // In the docs, it states the following:
50
- // "an operation which results in a number beyond the bounds of the integer type will return a float instead."
51
- // [https://www.php.net/manual/en/language.types.integer.php]
52
- // Se it seems we are good.
53
- // But let's check!
54
-
55
- $greatComputation = floor(100) * PHP_INT_MAX;
56
- $this->assertGreaterThan(PHP_INT_MAX, $greatComputation);
57
-
58
- $greaterComputation = floatval(200) * floatval(PHP_INT_MAX);
59
- $this->assertGreaterThan($greatComputation, $greaterComputation);
60
-
61
- // Test "T" unit
62
- $this->assertGreaterThan(PhpIniSizes::parseShortHandSize('1G'), PhpIniSizes::parseShortHandSize('100T'));
63
- $this->assertGreaterThan(1024 * 1024 * 1024 * 1024, PhpIniSizes::parseShortHandSize('1T') + 1);
64
-
65
-
66
- // Test that decimals are trunked, as described here:
67
- // https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
68
- $this->assertEquals(1024, PhpIniSizes::parseShortHandSize('1.5k'));
69
- $this->assertEquals(0, PhpIniSizes::parseShortHandSize('0.5M'));
70
-
71
-
72
- // Test syntax violations, which must result in parse error.
73
- $this->assertFalse(PhpIniSizes::parseShortHandSize('0.5MM'));
74
- $this->assertFalse(PhpIniSizes::parseShortHandSize('//5'));
75
- }
76
-
77
- /* TODO...
78
- public function testTestFilesizeRequirements()
79
- {
80
- $iniValue = ini_get('upload_max_filesize');
81
-
82
- // could we call ini_set? instead of mocking ini_get ?
83
- }
84
- */
85
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExposedConverter.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\TestConverters;
4
-
5
- use WebPConvert\Convert\Converters\AbstractConverter;
6
-
7
- /**
8
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
9
- * - so they can be tested
10
- *
11
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
12
- */
13
- class ExposedConverter extends AbstractConverter {
14
-
15
- protected function getOptionDefinitionsExtra()
16
- {
17
- return [];
18
- }
19
-
20
- public function doActualConvert()
21
- {
22
- file_put_contents($this->destination, 'we-pretend-this-is-a-valid-webp!');
23
- }
24
-
25
- /*
26
- public static function exposedGetMimeType($filePath)
27
- {
28
- $instance = self::createInstance(
29
- $filePath,
30
- $filePath . '.webp'
31
- );
32
- return $instance->getMimeTypeOfSource();
33
- }*/
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExtendedConverters/EwwwExtended.php DELETED
@@ -1,14 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\TestConverters\ExtendedConverters;
4
-
5
- use WebPConvert\Convert\Converters\Ewww;
6
-
7
- class EwwwExtended extends Ewww
8
- {
9
- public function callDoActualConvert()
10
- {
11
- $this->doActualConvert();
12
- }
13
-
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/FailureGuaranteedConverter.php DELETED
@@ -1,14 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\TestConverters;
4
-
5
- use WebPConvert\Convert\Converters\AbstractConverter;
6
- use WebPConvert\Convert\Exceptions\ConversionFailedException;
7
-
8
- class FailureGuaranteedConverter extends AbstractConverter {
9
-
10
- public function doActualConvert()
11
- {
12
- throw new ConversionFailedException('Failure guaranteed!');
13
- }
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/SuccessGuaranteedConverter.php DELETED
@@ -1,13 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\TestConverters;
4
-
5
- use WebPConvert\Convert\Converters\AbstractConverter;
6
-
7
- class SuccessGuaranteedConverter extends AbstractConverter {
8
-
9
- public function doActualConvert()
10
- {
11
- file_put_contents($this->destination, 'we-pretend-this-is-a-valid-webp!');
12
- }
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Helpers/WarningsIntoExceptionsTest.php DELETED
@@ -1,64 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Helpers;
4
-
5
- use WebPConvert\Helpers\WarningsIntoExceptions;
6
- use WebPConvert\Exceptions\WarningException;
7
-
8
- use PHPUnit\Framework\TestCase;
9
-
10
- class WarningsIntoExceptionsTest extends TestCase
11
- {
12
- public function testNothing()
13
- {
14
- $this->assertTrue(true);
15
- }
16
- /*
17
- private static $imgDir = __DIR__ . '/../../images';
18
-
19
- public function testUserWarning()
20
- {
21
- WarningsIntoExceptions::activate();
22
-
23
- $this->expectException(WarningException::class);
24
-
25
- // trigger user warning
26
- trigger_error('warning test', E_USER_WARNING);
27
-
28
- WarningsIntoExceptions::deactivate();
29
- }
30
-
31
-
32
- public function testWarning()
33
- {
34
- WarningsIntoExceptions::activate();
35
-
36
- $this->expectException(WarningException::class);
37
-
38
- // trigger build-in warning (chmod expects exactly two parameters)
39
- chmod('hth');
40
- WarningsIntoExceptions::deactivate();
41
- }*/
42
-
43
-
44
- /*
45
- To suppress and capture output from exec calls, you need to redirect the stderr to stdout.
46
- Otherwise it is "echoed to screen"
47
-
48
-
49
- https://stackoverflow.com/questions/1606943/suppressing-output-from-exec-calls-in-php
50
- public function testWarning2()
51
- {
52
- WarningsIntoExceptions::activate();
53
-
54
- //$this->expectException(WarningException);
55
-
56
- //ob_start();
57
- exec('hahotehua 2>&1', $output, $returnCode);
58
- //ob_end_clean();
59
-
60
- WarningsIntoExceptions::deactivate();
61
-
62
-
63
- }*/
64
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Serve/HeaderTest.php DELETED
@@ -1,49 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Serve;
4
-
5
- use WebPConvert\Serve\Header;
6
- use WebPConvert\Serve\MockedHeader;
7
- use WebPConvert\Loggers\BufferLogger;
8
-
9
- use PHPUnit\Framework\TestCase;
10
-
11
- class HeaderTest extends TestCase
12
- {
13
-
14
- public function testAddHeader()
15
- {
16
- MockedHeader::reset();
17
-
18
- Header::addHeader('X-test: testing');
19
- $header0 = MockedHeader::getHeaders()[0];
20
- $this->assertEquals('X-test: testing', $header0[0]);
21
- $this->assertFalse($header0[1]);
22
-
23
- Header::addHeader('X-test: testing2');
24
- $header1 = MockedHeader::getHeaders()[1];
25
- $this->assertEquals('X-test: testing2', $header1[0]);
26
- $this->assertFalse($header1[1]);
27
- }
28
-
29
- public function testSetHeader()
30
- {
31
- MockedHeader::reset();
32
-
33
- Header::setHeader('X-test: testing set header');
34
- $header0 = MockedHeader::getHeaders()[0];
35
- $this->assertEquals('X-test: testing set header', $header0[0]);
36
- $this->assertTrue($header0[1]);
37
- }
38
-
39
- public function testAddLogHeader()
40
- {
41
- MockedHeader::reset();
42
- Header::addLogHeader('test', new BufferLogger());
43
- $header0 = MockedHeader::getHeaders()[0];
44
- $this->assertEquals('X-WebP-Convert-Log: test', $header0[0]);
45
- $this->assertFalse($header0[1]);
46
-
47
- }
48
- }
49
- require_once('mock-header.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPExposer.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Convert\Exposers;
4
-
5
- use WebPConvert\Serve\ServeConvertedWebP;
6
- use WebPConvert\Tests\BaseExposer;
7
-
8
- /**
9
- * Class for exposing otherwise unaccessible methods of AbstractConverter,
10
- * - so they can be tested
11
- *
12
- * TODO: expose and test more methods! (and make more methods private/protected in AbstractConverter)
13
- */
14
- class ServeConvertedWebPExposer extends BaseExposer {
15
-
16
- public function __construct($instance)
17
- {
18
- parent::__construct($instance);
19
- }
20
- /*
21
- public function serveDestination($destination, $options)
22
- {
23
- return $this->callPrivateFunction('serveDestination', null, $destination, $options);
24
- }*/
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPTest.php DELETED
@@ -1,420 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Serve;
4
-
5
- use ImageMimeTypeGuesser\ImageMimeTypeGuesser;
6
- use WebPConvert\Exceptions\InvalidInputException;
7
- use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
8
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
9
- use WebPConvert\Serve\ServeConvertedWebP;
10
- use WebPConvert\Serve\MockedHeader;
11
- use WebPConvert\Serve\Exceptions\ServeFailedException;
12
-
13
- use ServeConvertedWebPExposer;
14
-
15
- use PHPUnit\Framework\TestCase;
16
-
17
- /**
18
- * @coversDefaultClass WebPConvert\Serve\ServeConvertedWebP
19
- * @covers WebPConvert\Serve\ServeConvertedWebP
20
- */
21
- class ServeConvertedWebPTest extends TestCase
22
- {
23
-
24
- public function getImageFolder()
25
- {
26
- return realpath(__DIR__ . '/../images');
27
- }
28
-
29
- public function getImagePath($image)
30
- {
31
- return $this->getImageFolder() . '/' . $image;
32
- }
33
-
34
- /**
35
- * @covers ::serveOriginal
36
- */
37
- public function testServeOriginal()
38
- {
39
- $source = $this->getImagePath('test.png');
40
- $this->assertTrue(file_exists($source), 'source file does not exist:' . $source);
41
-
42
- $destination = $source . '.webp';
43
-
44
- ob_start();
45
- $options = [
46
- //'serve-original' => true,
47
- //'reconvert' => true,
48
- 'convert' => [
49
- 'converters' => [
50
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
51
- ]
52
- ]
53
- ];
54
- ServeConvertedWebP::serveOriginal($source, $options);
55
- $result = ob_get_clean();
56
-
57
- // Test that headers were set as expected
58
- //$this->assertTrue(MockedHeader::hasHeaderContaining('X-WebP-Convert-Action:'));
59
-
60
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/png'));
61
- $this->assertFalse(MockedHeader::hasHeader('Vary: Accept'));
62
- $this->assertTrue(MockedHeader::hasHeaderContaining('Last-Modified:'));
63
- $this->assertFalse(MockedHeader::hasHeaderContaining('Cache-Control:'));
64
- }
65
-
66
-
67
- /**
68
- * @covers ::serveOriginal
69
- */
70
- public function testServeOriginalNotAnImage()
71
- {
72
- $this->expectException(InvalidImageTypeException::class);
73
-
74
- $source =$this->getImagePath('text.txt');
75
- $this->assertTrue(file_exists($source), 'source file does not exist');
76
-
77
- $contentType = ImageMimeTypeGuesser::lenientGuess($source);
78
- $this->assertSame(false, $contentType);
79
-
80
- ob_start();
81
- $options = [
82
- //'serve-original' => true,
83
- //'reconvert' => true,
84
- 'convert' => [
85
- 'converters' => [
86
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
87
- ]
88
- ]
89
- ];
90
- ServeConvertedWebP::serveOriginal($source, []);
91
- $result = ob_get_clean();
92
- $this->assertEquals('', $result);
93
- }
94
-
95
- /**
96
- * @covers ::serveOriginal
97
- */
98
- public function testServeOriginalNotAnImage2()
99
- {
100
- $this->expectException(InvalidImageTypeException::class);
101
-
102
- $source = $this->getImagePath('text');
103
- $this->assertTrue(file_exists($source), 'source file does not exist');
104
-
105
- $contentType = ImageMimeTypeGuesser::lenientGuess($source);
106
- $this->assertSame(null, $contentType);
107
-
108
- ob_start();
109
- $options = [
110
- //'serve-original' => true,
111
- //'reconvert' => true,
112
- 'convert' => [
113
- 'converters' => [
114
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
115
- ]
116
- ]
117
- ];
118
- ServeConvertedWebP::serveOriginal($source, $options);
119
- $result = ob_get_clean();
120
- $this->assertEquals('', $result);
121
- }
122
-
123
- /**
124
- * @covers ::serve
125
- */
126
- public function testServeReconvert()
127
- {
128
- $source = $this->getImagePath('test.png');
129
- $this->assertTrue(file_exists($source));
130
-
131
- $destination = $source . '.webp';
132
-
133
- ob_start();
134
- $options = [
135
- //'serve-original' => true,
136
- 'reconvert' => true,
137
- 'convert' => [
138
- 'converters' => [
139
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
140
- ]
141
- ]
142
- ];
143
- ServeConvertedWebP::serve($source, $destination, $options);
144
- $result = ob_get_clean();
145
-
146
- // Test that headers were set as expected
147
- //$this->assertTrue(MockedHeader::hasHeaderContaining('X-WebP-Convert-Action:'));
148
-
149
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/webp'));
150
- $this->assertFalse(MockedHeader::hasHeader('Vary: Accept'));
151
- $this->assertTrue(MockedHeader::hasHeaderContaining('Last-Modified:'));
152
- $this->assertFalse(MockedHeader::hasHeaderContaining('Cache-Control:'));
153
- }
154
-
155
- /**
156
- * @covers ::serve
157
- */
158
- public function testServeServeOriginal()
159
- {
160
- $source = $this->getImagePath('test.png');
161
- $this->assertTrue(file_exists($source));
162
-
163
- $destination = $source . '.webp';
164
-
165
- ob_start();
166
- $options = [
167
- 'serve-original' => true,
168
- //'reconvert' => true,
169
- 'convert' => [
170
- 'converters' => [
171
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
172
- ]
173
- ]
174
- ];
175
- ServeConvertedWebP::serve($source, $destination, $options);
176
- $result = ob_get_clean();
177
-
178
- // Test that headers were set as expected
179
- //$this->assertTrue(MockedHeader::hasHeaderContaining('X-WebP-Convert-Action:'));
180
-
181
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/png'));
182
- $this->assertFalse(MockedHeader::hasHeader('Vary: Accept'));
183
- $this->assertTrue(MockedHeader::hasHeaderContaining('Last-Modified:'));
184
- $this->assertFalse(MockedHeader::hasHeaderContaining('Cache-Control:'));
185
- }
186
-
187
- /**
188
- * Testing when the "cached" image can be served
189
- * @covers ::serve
190
- */
191
- public function testServeDestination()
192
- {
193
- $source = $this->getImagePath('/test.png');
194
- $this->assertTrue(file_exists($source));
195
-
196
- // create fake webp at destination
197
- $destination = $source . '.webp';
198
- file_put_contents($destination, '1234');
199
- $this->assertTrue(file_exists($destination));
200
-
201
- ob_start();
202
- $options = [
203
- //'serve-original' => true,
204
- //'reconvert' => true,
205
- 'convert' => [
206
- 'converters' => [
207
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
208
- ]
209
- ]
210
- ];
211
- ServeConvertedWebP::serve($source, $destination, $options);
212
- $result = ob_get_clean();
213
-
214
- // Check that destination is output (it has the content "1234")
215
- $this->assertEquals('1234', $result);
216
-
217
- // Test that headers were set as expected
218
- //$this->assertTrue(MockedHeader::hasHeaderContaining('X-WebP-Convert-Action:'));
219
-
220
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/webp'));
221
- }
222
-
223
- /**
224
- * @covers ::serve
225
- */
226
- public function testEmptySourceArg()
227
- {
228
- $this->expectException(InvalidInputException::class);
229
-
230
- ob_start();
231
- $options = [
232
- //'serve-original' => true,
233
- //'reconvert' => true,
234
- 'convert' => [
235
- 'converters' => [
236
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
237
- ]
238
- ]
239
- ];
240
-
241
- $source = '';
242
- $this->assertEmpty($source);
243
- ServeConvertedWebP::serve($source, $this->getImagePath('test.png.webp'), $options);
244
- $result = ob_get_clean();
245
- }
246
-
247
- /**
248
- * @covers ::serve
249
- */
250
- public function testEmptyDestinationArg()
251
- {
252
- $this->expectException(InvalidInputException::class);
253
-
254
- $source = $this->getImagePath('test.png');
255
- $this->assertTrue(file_exists($source));
256
-
257
- $destination = '';
258
-
259
- ob_start();
260
- $options = [
261
- //'serve-original' => true,
262
- //'reconvert' => true,
263
- 'convert' => [
264
- 'converters' => [
265
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
266
- ]
267
- ]
268
- ];
269
- ServeConvertedWebP::serve($source, $destination, $options);
270
- $result = ob_get_clean();
271
- }
272
-
273
- /**
274
- * @covers ::serve
275
- */
276
- public function testNoFileAtSource()
277
- {
278
- $this->expectException(TargetNotFoundException::class);
279
-
280
- $source = $this->getImagePath('i-do-not-exist.png');
281
- $this->assertFalse(file_exists($source));
282
-
283
- $destination = '';
284
-
285
- ob_start();
286
- $options = [
287
- //'serve-original' => true,
288
- //'reconvert' => true,
289
- 'convert' => [
290
- 'converters' => [
291
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
292
- ]
293
- ]
294
- ];
295
- ServeConvertedWebP::serve($source, $destination, $options);
296
- $result = ob_get_clean();
297
- }
298
-
299
- /**
300
- * @covers ::serve
301
- */
302
- public function testServeReport()
303
- {
304
- $source = $this->getImagePath('test.png');
305
- $this->assertTrue(file_exists($source));
306
- $destination = $source . '.webp';
307
-
308
- ob_start();
309
- $options = [
310
- //'serve-original' => true,
311
- //'reconvert' => true,
312
- 'show-report' => true,
313
- 'convert' => [
314
- 'converters' => [
315
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
316
- ]
317
- ]
318
- ];
319
- ServeConvertedWebP::serve($source, $destination, $options);
320
- $result = ob_get_clean();
321
-
322
- // Check that output looks like a report
323
- $this->assertTrue(strpos($result, 'source:') !== false, 'The following does not contain "source:":' . $result);
324
-
325
- // Test that headers were set as expected
326
- //$this->assertTrue(MockedHeader::hasHeaderContaining('X-WebP-Convert-Action:'));
327
-
328
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/webp'));
329
- }
330
-
331
- public function testSourceIsLighter()
332
- {
333
- $source = $this->getImagePath('plaintext-with-jpg-extension.jpg');
334
-
335
- // create fake webp at destination, which is larger than the fake jpg
336
- file_put_contents($source . '.webp', 'aotehu aotnehuatnoehutaoehu atonse uaoehu');
337
-
338
- $this->assertTrue(file_exists($source));
339
- $this->assertTrue(file_exists($source . '.webp'));
340
-
341
- ob_start();
342
- $options = [
343
- 'convert' => [
344
- 'converters' => [
345
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
346
- ]
347
- ]
348
- ];
349
- ServeConvertedWebP::serve($source, $source . '.webp', $options);
350
- $result = ob_get_clean();
351
-
352
- // the source file contains "text", so the next assert asserts that source was served
353
- $this->assertRegExp('#text#', $result);
354
- }
355
-
356
- public function testExistingOutDated()
357
- {
358
- $source = $this->getImagePath('test.jpg');
359
- $this->assertTrue(file_exists($source));
360
-
361
- $destination = $source . '.webp';
362
- @unlink($destination);
363
- copy($this->getImagePath('pre-converted/test.webp'), $destination);
364
-
365
- // set modification date earlier than source
366
- touch($destination, filemtime($source) - 1000);
367
-
368
- // check that it worked
369
- $this->assertLessThan(filemtime($source), filemtime($destination));
370
-
371
- ob_start();
372
- $options = [
373
- 'convert' => [
374
- 'converters' => [
375
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
376
- ]
377
- ]
378
- ];
379
- ServeConvertedWebP::serve($source, $source . '.webp', $options);
380
- $result = ob_get_clean();
381
-
382
- unlink($destination);
383
-
384
- // Our success-converter always creates fake webps with the content:
385
- // "we-pretend-this-is-a-valid-webp!".
386
- // So testing that we got this back is the same as testing that a "conversion" was
387
- // done and the converted file was served. It is btw smaller than the source.
388
-
389
- $this->assertRegExp('#we-pretend-this-is-a-valid-webp!#', $result);
390
- }
391
-
392
- public function testNoFileAtDestination()
393
- {
394
- $source = $this->getImagePath('test.jpg');
395
- $this->assertTrue(file_exists($source));
396
-
397
- $destination = $source . '.webp';
398
- @unlink($destination);
399
-
400
- ob_start();
401
- $options = [
402
- 'convert' => [
403
- 'converters' => [
404
- '\\WebPConvert\\Tests\\Convert\\TestConverters\\SuccessGuaranteedConverter'
405
- ]
406
- ]
407
- ];
408
- ServeConvertedWebP::serve($source, $source . '.webp', $options);
409
- $result = ob_get_clean();
410
-
411
- // Our success-converter always creates fake webps with the content:
412
- // "we-pretend-this-is-a-valid-webp!".
413
- // So testing that we got this back is the same as testing that a "convert" was
414
- // done and the converted file was served. It is btw smaller than the source.
415
-
416
- $this->assertRegExp('#we-pretend-this-is-a-valid-webp!#', $result);
417
- }
418
-
419
- }
420
- require_once('mock-header.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Serve/ServeFileTest.php DELETED
@@ -1,192 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Tests\Serve;
4
-
5
- use WebPConvert\Serve\ServeFile;
6
- use WebPConvert\Serve\MockedHeader;
7
- use WebPConvert\Exceptions\InvalidInpuException;
8
- use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
9
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
10
- //use WebPConvert\Serve\Exceptions\ServeFailedException;
11
-
12
- use PHPUnit\Framework\TestCase;
13
-
14
- class ServeFileTest extends TestCase
15
- {
16
-
17
- public function getImageFolder()
18
- {
19
- return realpath(__DIR__ . '/../images');
20
- }
21
-
22
- public function getImagePath($image)
23
- {
24
- return $this->getImageFolder() . '/' . $image;
25
- }
26
-
27
- public function testServeDefaultOptions()
28
- {
29
- MockedHeader::reset();
30
-
31
- $filename = self::getImagePath('plaintext-with-jpg-extension.jpg');
32
- $this->assertTrue(file_exists($filename));
33
-
34
- ob_start();
35
- ServeFile::serve($filename, 'image/webp', []);
36
- $result = ob_get_clean();
37
-
38
- // Test that content of file was send to output
39
- $this->assertEquals("text\n", $result);
40
-
41
- $headers = MockedHeader::getHeaders();
42
- $this->assertGreaterThanOrEqual(1, MockedHeader::getNumHeaders());
43
-
44
- // Test that headers were set as expected
45
- $this->assertTrue(MockedHeader::hasHeader('Content-Type: image/webp'));
46
- $this->assertFalse(MockedHeader::hasHeader('Vary: Accept'));
47
-
48
- $this->assertTrue(MockedHeader::hasHeaderContaining('Content-Length:'));
49
-
50
- //$this->assertTrue(MockedHeader::hasHeader('Last-Modified: Mon, 29 Apr 2019 12:54:37 GMT'));
51
-
52
- // TODO:The following fails on travis. WHY???
53
- //$this->assertTrue(MockedHeader::hasHeaderContaining('Last-Modified:'));
54
-
55
- //$this->assertTrue(MockedHeader::hasHeader('Cache-Control: public, max-age=86400'));
56
- //$this->assertTrue(MockedHeader::hasHeaderContaining('Expires:'));
57
- }
58
-
59
- public function testServeVaryHeader()
60
- {
61
- MockedHeader::reset();
62
-
63
- $this->assertEquals(0, MockedHeader::getNumHeaders());
64
-
65
- $filename = self::getImagePath('plaintext-with-jpg-extension.jpg');
66
- $this->assertTrue(file_exists($filename));
67
-
68
- $options = [
69
- 'headers' => [
70
- 'vary-accept' => true
71
- ]
72
- ];
73
-
74
- ob_start();
75
- ServeFile::serve($filename, 'image/webp', $options);
76
- $result = ob_get_clean();
77
-
78
- $this->assertTrue(MockedHeader::hasHeader('Vary: Accept'));
79
-
80
- }
81
-
82
-
83
- public function testServeNoHeaders()
84
- {
85
- MockedHeader::reset();
86
-
87
- $this->assertEquals(0, MockedHeader::getNumHeaders());
88
-
89
- $filename = self::getImagePath('plaintext-with-jpg-extension.jpg');
90
- $this->assertTrue(file_exists($filename));
91
-
92
- $options = [
93
- 'headers' => [
94
- 'cache-control' => false,
95
- 'content-length' => false,
96
- 'content-type' => false,
97
- 'expires' => false,
98
- 'last-modified' => false,
99
- 'vary-accept' => false
100
- ],
101
- 'cache-control-header' => 'private, max-age=100',
102
- ];
103
-
104
- ob_start();
105
- ServeFile::serve($filename, 'image/webp', $options);
106
- $result = ob_get_clean();
107
-
108
- // Test that content of file was send to output
109
- $this->assertEquals("text\n", $result);
110
-
111
- // Test that headers were set as expected
112
- // We actually expect that none are added.
113
-
114
- $headers = MockedHeader::getHeaders();
115
- $this->assertEquals(0, MockedHeader::getNumHeaders());
116
-
117
-
118
- // TODO:The following fails on travis. WHY???
119
- //$this->assertFalse(MockedHeader::hasHeader('Content-Type: image/webp'));
120
-
121
- //$this->assertTrue(MockedHeader::hasHeader('Vary: Accept'));
122
- //$this->assertFalse(MockedHeader::hasHeader('Last-Modified: Mon, 29 Apr 2019 12:54:37 GMT'));
123
-
124
- // TODO:The following fails on travis. WHY???
125
- //$this->assertTrue(MockedHeader::hasHeaderContaining('Last-Modified:'));
126
-
127
-
128
- $this->assertFalse(MockedHeader::hasHeader('Cache-Control: public, max-age=86400'));
129
- $this->assertFalse(MockedHeader::hasHeaderContaining('Expires:'));
130
- }
131
-
132
- public function testServeCustomCacheControl()
133
- {
134
- MockedHeader::reset();
135
- $filename = self::getImagePath('plaintext-with-jpg-extension.jpg');
136
- $this->assertTrue(file_exists($filename));
137
-
138
- $options = [
139
- 'headers' => [
140
- 'cache-control' => true,
141
- 'expires' => true,
142
- ],
143
- 'cache-control-header' => 'private, max-age=100',
144
- ];
145
-
146
- ob_start();
147
- ServeFile::serve($filename, 'image/webp', $options);
148
- $result = ob_get_clean();
149
- $this->assertTrue(MockedHeader::hasHeader('Cache-Control: private, max-age=100'));
150
- $this->assertTrue(MockedHeader::hasHeaderContaining('Expires:'));
151
- }
152
-
153
- public function testServeCustomCacheControlNoMaxAge()
154
- {
155
- MockedHeader::reset();
156
- $filename = self::getImagePath('plaintext-with-jpg-extension.jpg');
157
- $this->assertTrue(file_exists($filename));
158
- $options = [
159
- 'headers' => [
160
- 'cache-control' => true,
161
- ],
162
- 'cache-control-header' => 'private',
163
- ];
164
- ob_start();
165
- ServeFile::serve($filename, 'image/webp', $options);
166
- $result = ob_get_clean();
167
- $this->assertTrue(MockedHeader::hasHeader('Cache-Control: private'));
168
-
169
- // When there is no max-age, there should neither be any Expires
170
- $this->assertFalse(MockedHeader::hasHeaderContaining('Expires:'));
171
- }
172
-
173
- public function testServeNonexistantFile()
174
- {
175
- MockedHeader::reset();
176
-
177
- $filename = __DIR__ . '/i-dont-exist-no';
178
- $this->assertFalse(file_exists($filename));
179
-
180
- $this->expectException(TargetNotFoundException::class);
181
-
182
- ob_start();
183
- ServeFile::serve($filename, 'image/webp', []);
184
- $result = ob_get_clean();
185
-
186
- $this->assertEquals("", $result);
187
-
188
- $this->assertTrue(MockedHeader::hasHeader('X-WebP-Convert-Error: Could not read file'));
189
- }
190
-
191
- }
192
- require_once('mock-header.inc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/Serve/mock-header.inc DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
-
3
- namespace WebPConvert\Serve {
4
-
5
- class MockedHeader {
6
-
7
- private static $headers = [];
8
-
9
- public static function header($header, $replace) {
10
- self::$headers[] = [$header, $replace];
11
- //echo $header;
12
- }
13
-
14
- public static function getHeaders() {
15
- return self::$headers;
16
- }
17
-
18
- public static function reset() {
19
- self::$headers = [];
20
- }
21
-
22
- public static function hasHeader($header)
23
- {
24
- $headerValues = array_column(self::$headers, 0);
25
- return in_array($header, $headerValues);
26
- }
27
-
28
- public static function hasHeaderContaining($headerText)
29
- {
30
- $headerValues = array_column(self::$headers, 0);
31
- foreach ($headerValues as $header) {
32
- if (false !== strpos($header, $headerText)) {
33
- return true;
34
- }
35
- }
36
-
37
- return false;
38
- }
39
-
40
- public static function getNumHeaders() {
41
- return count(self::$headers);
42
- }
43
-
44
- }
45
-
46
- //use WebPConvert\Tests\Serve\MockedHeader;
47
- function header($h, $replace)
48
- {
49
- MockedHeader::header($h, $replace);
50
- }
51
-
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/WebPConvertTest.php DELETED
@@ -1,238 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * WebPConvert - Convert JPEG & PNG to WebP with PHP
5
- *
6
- * @link https://github.com/rosell-dk/webp-convert
7
- * @license MIT
8
- */
9
-
10
- namespace WebPConvert\Tests;
11
-
12
- use WebPConvert\WebPConvert;
13
- use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
14
- //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
15
- use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
16
- use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
17
-
18
- use PHPUnit\Framework\TestCase;
19
-
20
- class WebPConvertTest extends TestCase
21
- {
22
- /*
23
- public function testPreferredConverters()
24
- {
25
- $this->assertEmpty(WebPConvert::$preferredConverters);
26
- }
27
-
28
- public function testExcludeDefaultBinaries()
29
- {
30
- $this->assertFalse(WebPConvert::$excludeDefaultBinaries);
31
- }
32
-
33
- public function testAllowedExtensions()
34
- {
35
- $allowed = ['jpg', 'jpeg', 'png'];
36
-
37
- foreach ($allowed as $key) {
38
- $this->assertContains($key, WebPConvert::$allowedExtensions);
39
- }
40
- }
41
-
42
- public function testSetConverters()
43
- {
44
- $preferred = ['gd', 'cwebp'];
45
- WebPConvert::setConverters($preferred);
46
-
47
- $this->assertEquals($preferred, WebPConvert::$preferredConverters);
48
- }
49
- */
50
- /**
51
- * @expectedExceptio \Exception
52
- */
53
- /*
54
- public function testIsValidTargetInvalid()
55
- {
56
- WebPConvert::isValidTarget('Invalid');
57
- }
58
-
59
- public function testIsValidTargetValid()
60
- {
61
- $this->assertTrue(WebPConvert::isValidTarget(__FILE__));
62
- }
63
- */
64
- /**
65
- * @expectedExceptio \Exception
66
- */
67
- /*
68
- public function testIsAllowedExtensionInvalid()
69
- {
70
- $allowed = ['jpg', 'jpeg', 'png'];
71
-
72
- foreach ($allowed as $key) {
73
- WebPConvert::isAllowedExtension(__FILE__);
74
- }
75
- }
76
-
77
- public function testIsAllowedExtensionValid()
78
- {
79
- $source = (__DIR__ . '/test.jpg');
80
-
81
- $this->assertTrue(WebPConvert::isAllowedExtension($source));
82
- }
83
-
84
- public function testGetConvertersDefault()
85
- {
86
- $default = ['cwebp', 'ewww', 'gd', 'imagick'];
87
-
88
- foreach ($default as $key) {
89
- $this->assertContains($key, WebPConvert::getConverters());
90
- }
91
- }
92
-
93
- public function testGetConvertersCustom()
94
- {
95
- WebPConvert::$preferredConverters = ['gd', 'cwebp'];
96
- $custom = ['gd', 'cwebp', 'ewww', 'imagick'];
97
-
98
- $this->assertEquals($custom, WebPConvert::getConverters());
99
- }
100
-
101
- public function testSetGetConverters()
102
- {
103
- $preferred = ['gd', 'cwebp'];
104
- WebPConvert::setConverters($preferred, true);
105
-
106
- $this->assertEquals($preferred, WebPConvert::getConverters());
107
- }
108
- */
109
-
110
- /*
111
- Idea: WebPConvert could throw custom exceptions, and we
112
- could test these like this:
113
- $this->expectException(InvalidArgumentException::class);
114
- https://phpunit.readthedocs.io/en/7.1/writing-tests-for-phpunit.html#testing-exceptions
115
- */
116
-
117
- /**
118
- * Test convert.
119
- * - It must either make a successful conversion, or thwrow an exception
120
- * - It must return boolean
121
- *//*
122
- public function testConvert()
123
- {
124
- $source = (__DIR__ . '/test.jpg');
125
- $destination = (__DIR__ . '/test.webp');
126
-
127
- $result = WebPConvert::convert($source, $destination);
128
-
129
- $this->assertTrue(file_exists($destination));
130
- $this->assertInternalType('boolean', $result);
131
- }*/
132
-
133
- /**
134
- * Basically test what happens when no converters are able to do a conversion,
135
- * WebPConvert::convert should in that case return false
136
- */
137
- public function testConvertWithNoConverters()
138
- {
139
- $this->expectException(ConverterNotOperationalException::class);
140
- $source = __DIR__ . '/test.jpg';
141
- $destination = __DIR__ . '/test.jpg.webp';
142
- $result = WebPConvert::convert($source, $destination, array(
143
- 'converters' => array()
144
- ));
145
- //$this->assertFalse($result);
146
- }
147
-
148
-
149
- public function testTargetNotFound()
150
- {
151
- $this->expectException(TargetNotFoundException::class);
152
-
153
- WebPConvert::convert(__DIR__ . '/i-dont-existno.jpg', __DIR__ . '/i-dont-exist.webp');
154
- //$this->assertTrue($result);
155
- }
156
-
157
- public function warningHandler($errno, $errstr, $errfile, $errline)
158
- {
159
- //echo 'warning handler here';
160
- //return false;
161
- }
162
-
163
- public function testInvalidDestinationFolder()
164
- {
165
-
166
- // Notice: mkdir emits a warning on failure.
167
- // I have reconfigured php unit to not turn warnings into exceptions (phpunit.xml.dist)
168
- // - if I did not do that, the exception would not be CreateDestinationFolderException
169
-
170
- $this->expectException(CreateDestinationFolderException::class);
171
- //$this->expectException(\Exception::class);
172
-
173
-
174
- // Set error handler in order to suppress warnings.
175
- // (we probably get a warning because mkdir() does not have permission to create the dir it is asked to)
176
- $handler = set_error_handler(
177
- array($this, "warningHandler"),
178
- E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_ERROR
179
- );
180
- //echo 'previously defined handler:' . print_r($handler, true);*/
181
-
182
- /*
183
- set_error_handler(
184
- array($this, "warningHandler"),
185
- E_ALL
186
- );
187
-
188
- chown();*/
189
-
190
- // I here assume that no system grants write access to their root folder
191
- // this is perhaps wrong to assume?
192
- $destinationFolder = '/you-can-delete-me/';
193
-
194
- WebPConvert::convert(__DIR__ . '/test.jpg', $destinationFolder . 'you-can-delete-me.webp');
195
-
196
- restore_error_handler();
197
- }
198
-
199
- /**
200
- * Test ConversionSkippedException by testing Gd.
201
- */
202
- /*
203
- public function testDeclined()
204
- {
205
- // only try Gd
206
- //WebPConvert::setConverterOrder(array('gd'), true);
207
-
208
- // configure Gd not to convert pngs
209
- //WebPConvert::setConverterOption('gd', 'convert_pngs', false);
210
-
211
- $source = __DIR__ . '/test.png';
212
- $destination = __DIR__ . '/test.png.webp';
213
- $options = array(
214
- 'converters' => array(
215
- array(
216
- 'converter' => 'gd',
217
- 'options' => array(
218
- 'skip-pngs' => true,
219
- ),
220
- ),
221
- )
222
- );
223
- try {
224
- WebPConvert::convert($source, $destination, $options);
225
- } catch (\WebPConvert\Convert\Exceptions\SystemRequirementsNotMetException $e) {
226
- // converter isn't operational, so we cannot make the unit test
227
- return;
228
- } catch (\WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException $e) {
229
- // Yeah, this is what we want to test.
230
-
231
- $this->expectException(\WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException::class);
232
- WebPConvert::convert($source, $destination, $options);
233
- }
234
- }*/
235
-
236
-
237
- // How to test CreateDestinationFileException ?
238
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/bootstrap-webp-convert-test.php DELETED
@@ -1,20 +0,0 @@
1
- <?php
2
- require __DIR__ . '/../src-build/webp-convert.inc';
3
-
4
- spl_autoload_register('webpconvert_disable_autoload', true, true);
5
- function webpconvert_disable_autoload($class) {
6
- //echo $class . "\n";
7
- if (strpos($class, 'WebPConvert\\') === 0) {
8
- if (strpos($class, 'WebPConvert\\Tests\\') !== 0) {
9
- throw new \Exception(
10
- 'Autoloader was about to autoload a WebPConvert class. ' .
11
- 'But that means it was not included in the build! "' . $class . '"');
12
- //require_once WEBPEXPRESS_PLUGIN_DIR . '/lib/classes/' . substr($class, 12) . '.php';
13
- }
14
- if (strpos($class, 'ImageMimeTypeGuesser\\') === 0) {
15
- throw new \Exception(
16
- 'Autoloader was about to autoload a ImageMimeTypeGuesser class. ' .
17
- 'But that means it was not included in the build! "' . $class . '"');
18
- }
19
- }
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/bootstrap-wod-test.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
- require __DIR__ . '/../src-build/webp-on-demand-1.inc';
3
- require __DIR__ . '/../src-build/webp-on-demand-2.inc';
4
-
5
-
6
- spl_autoload_register('webpconvert_disable_autoload', true, true);
7
- function webpconvert_disable_autoload($class) {
8
- //echo $class . "\n";
9
- //return;
10
- /*
11
- if (strpos($class, 'WebPConvert\\') === 0) {
12
- if (strpos($class, 'WebPConvert\\Tests\\') !== 0) {
13
- throw new \Exception(
14
- 'Autoloader was about to autoload a WebPConvert class. ' .
15
- 'But that means it was not included in the build! "' . $class . '"');
16
- //require_once WEBPEXPRESS_PLUGIN_DIR . '/lib/classes/' . substr($class, 12) . '.php';
17
- }
18
- }
19
- if (strpos($class, 'ImageMimeTypeGuesser\\') === 0) {
20
- throw new \Exception(
21
- 'Autoloader was about to autoload a ImageMimeTypeGuesser class. ' .
22
- 'But that means it was not included in the build! "' . $class . '"');
23
- }*/
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/rosell-dk/webp-convert/tests/images/not-true-color.png DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/plaintext-with-jpg-extension.jpg DELETED
@@ -1 +0,0 @@
1
- text
 
vendor/rosell-dk/webp-convert/tests/images/png-with-jpeg-extension.jpg DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/png-without-extension DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/pre-converted/test-bigger.webp DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/pre-converted/test.webp DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/small-q61.jpg DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/test.jpg DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/test.png DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/images/text DELETED
File without changes
vendor/rosell-dk/webp-convert/tests/images/text-with-jpg-extension.jpg DELETED
File without changes
vendor/rosell-dk/webp-convert/tests/images/text.txt DELETED
File without changes
vendor/rosell-dk/webp-convert/tests/images/with space.jpg DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/test.jpg DELETED
Binary file
vendor/rosell-dk/webp-convert/tests/test.png DELETED
Binary file
webp-express.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WebP Express
4
  * Plugin URI: https://github.com/rosell-dk/webp-express
5
  * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
- * Version: 0.14.6
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
3
  * Plugin Name: WebP Express
4
  * Plugin URI: https://github.com/rosell-dk/webp-express
5
  * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
+ * Version: 0.14.7
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
wod/webp-on-demand.php CHANGED
@@ -1,15 +1,4 @@
1
  <?php
2
- // https://www.askapache.com/htaccess/crazy-advanced-mod_rewrite-tutorial/#Decoding_Mod_Rewrite_Variables
3
-
4
- ini_set('display_errors', 1);
5
- error_reporting(E_ALL);
6
-
7
- //echo 'display errors:' . ini_get('display_errors'); exit;
8
-
9
- //require 'webp-on-demand-1.inc';
10
- //require '../vendor/autoload.php';
11
-
12
- //print_r($_GET); exit;
13
 
14
  use \WebPConvert\WebPConvert;
15
  use \WebPConvert\Serve\ServeConvertedWebP;
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  use \WebPConvert\WebPConvert;
4
  use \WebPConvert\Serve\ServeConvertedWebP;
wod/webp-realizer.php CHANGED
@@ -1,14 +1,4 @@
1
  <?php
2
- // https://www.askapache.com/htaccess/crazy-advanced-mod_rewrite-tutorial/#Decoding_Mod_Rewrite_Variables
3
-
4
- ini_set('display_errors', 1);
5
- error_reporting(E_ALL);
6
-
7
- //echo 'display errors:' . ini_get('display_errors');
8
- //exit;
9
-
10
- //require 'webp-on-demand-1.inc';
11
- //require '../vendor/autoload.php';
12
 
13
  use \WebPConvert\WebPConvert;
14
  use \WebPConvert\ServeExistingOrHandOver;
1
  <?php
 
 
 
 
 
 
 
 
 
 
2
 
3
  use \WebPConvert\WebPConvert;
4
  use \WebPConvert\ServeExistingOrHandOver;