Version Description
(released: 20 jun 2019)
- Removed unneccesary files from webp-convert library
Download this release
Release Info
Developer | rosell.dk |
Plugin | WebP Express |
Version | 0.14.7 |
Comparing to | |
See all releases |
Code changes from version 0.14.6 to 0.14.7
- README.txt +9 -1
- lib/classes/ConvertHelperIndependent.php +1 -3
- lib/options/submit.php +255 -52
- vendor/rosell-dk/webp-convert/build-scripts/PHPMerger.php +0 -93
- vendor/rosell-dk/webp-convert/build-scripts/build-all +0 -1
- vendor/rosell-dk/webp-convert/build-scripts/build.php +0 -138
- vendor/rosell-dk/webp-convert/build-tests-webp-convert/WebPConvertBuildTest.php +0 -56
- vendor/rosell-dk/webp-convert/build-tests-wod/WodBuildTest.php +0 -178
- vendor/rosell-dk/webp-convert/install-gmagick-with-webp.sh +0 -74
- vendor/rosell-dk/webp-convert/install-imagemagick-with-webp.sh +0 -40
- vendor/rosell-dk/webp-convert/install-vips.sh +0 -40
- vendor/rosell-dk/webp-convert/src-build/webp-convert.inc +0 -7167
- vendor/rosell-dk/webp-convert/src-build/webp-on-demand-1.inc +0 -1393
- vendor/rosell-dk/webp-convert/src-build/webp-on-demand-2.inc +0 -5775
- vendor/rosell-dk/webp-convert/tests/BaseExposer.php +0 -113
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/AbstractConverterTest.php +0 -107
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/BaseTraits/AutoQualityTraitTest.php +0 -172
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/ConverterTestHelper.php +0 -188
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/CwebpTest.php +0 -292
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/EwwwTest.php +0 -97
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/GdTest.php +0 -254
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/GmagickTest.php +0 -29
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/GraphicsMagickTest.php +0 -67
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImageMagickTest.php +0 -63
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/ImagickTest.php +0 -63
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/StackTest.php +0 -56
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/VipsTest.php +0 -234
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/WPCTest.php +0 -262
- vendor/rosell-dk/webp-convert/tests/Convert/Converters/pretend.inc +0 -55
- vendor/rosell-dk/webp-convert/tests/Convert/Exposers/AbstractConverterExposer.php +0 -63
- vendor/rosell-dk/webp-convert/tests/Convert/Exposers/CwebpExposer.php +0 -23
- vendor/rosell-dk/webp-convert/tests/Convert/Exposers/GdExposer.php +0 -110
- vendor/rosell-dk/webp-convert/tests/Convert/Exposers/VipsExposer.php +0 -39
- vendor/rosell-dk/webp-convert/tests/Convert/Helpers/JpegQualityDetectorTest.php +0 -33
- vendor/rosell-dk/webp-convert/tests/Convert/Helpers/PhpIniSizesTest.php +0 -85
- vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExposedConverter.php +0 -34
- vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/ExtendedConverters/EwwwExtended.php +0 -14
- vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/FailureGuaranteedConverter.php +0 -14
- vendor/rosell-dk/webp-convert/tests/Convert/TestConverters/SuccessGuaranteedConverter.php +0 -13
- vendor/rosell-dk/webp-convert/tests/Helpers/WarningsIntoExceptionsTest.php +0 -64
- vendor/rosell-dk/webp-convert/tests/Serve/HeaderTest.php +0 -49
- vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPExposer.php +0 -25
- vendor/rosell-dk/webp-convert/tests/Serve/ServeConvertedWebPTest.php +0 -420
- vendor/rosell-dk/webp-convert/tests/Serve/ServeFileTest.php +0 -192
- vendor/rosell-dk/webp-convert/tests/Serve/mock-header.inc +0 -52
- vendor/rosell-dk/webp-convert/tests/WebPConvertTest.php +0 -238
- vendor/rosell-dk/webp-convert/tests/bootstrap-webp-convert-test.php +0 -20
- vendor/rosell-dk/webp-convert/tests/bootstrap-wod-test.php +0 -24
- vendor/rosell-dk/webp-convert/tests/images/not-true-color.png +0 -0
- vendor/rosell-dk/webp-convert/tests/images/plaintext-with-jpg-extension.jpg +0 -1
- vendor/rosell-dk/webp-convert/tests/images/png-with-jpeg-extension.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/images/png-without-extension +0 -0
- vendor/rosell-dk/webp-convert/tests/images/pre-converted/test-bigger.webp +0 -0
- vendor/rosell-dk/webp-convert/tests/images/pre-converted/test.webp +0 -0
- vendor/rosell-dk/webp-convert/tests/images/small-q61.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/images/test.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/images/test.png +0 -0
- vendor/rosell-dk/webp-convert/tests/images/text +0 -0
- vendor/rosell-dk/webp-convert/tests/images/text-with-jpg-extension.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/images/text.txt +0 -0
- vendor/rosell-dk/webp-convert/tests/images/with space.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/test.jpg +0 -0
- vendor/rosell-dk/webp-convert/tests/test.png +0 -0
- webp-express.php +1 -1
- wod/webp-on-demand.php +0 -11
- 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.
|
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.
|
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 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
// Set options that are available in all operation modes
|
35 |
$config = array_merge($config, [
|
36 |
-
'operation-mode' =>
|
37 |
|
38 |
// redirection rules
|
39 |
-
'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 ($
|
49 |
-
|
50 |
-
|
51 |
-
$config['cache-control'] = $cacheControl;
|
52 |
-
|
53 |
-
switch ($cacheControl) {
|
54 |
case 'no-header':
|
55 |
break;
|
56 |
case 'set':
|
57 |
-
$config['cache-control-max-age'] =
|
58 |
-
$config['cache-control-public'] =
|
59 |
break;
|
60 |
case 'custom':
|
61 |
-
$config['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'] =
|
71 |
-
if ($
|
72 |
-
$config['alter-html']['only-for-webp-enabled-browsers'] =
|
73 |
} else {
|
74 |
$config['alter-html']['only-for-webp-enabled-browsers'] = false;
|
75 |
}
|
76 |
-
if ($
|
77 |
-
$config['alter-html']['alter-html-add-picturefill-js'] =
|
78 |
}
|
79 |
-
if ($
|
80 |
-
$config['alter-html']['only-for-webps-that-exists'] = (
|
81 |
} else {
|
82 |
$config['alter-html']['only-for-webps-that-exists'] = true;
|
83 |
}
|
84 |
|
85 |
-
$config['alter-html']['replacement'] =
|
86 |
-
$config['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'] =
|
98 |
|
99 |
// Jpeg
|
100 |
// --------
|
101 |
-
$config['jpeg-encoding'] =
|
102 |
|
103 |
-
$auto = (
|
104 |
$config['quality-auto'] = $auto;
|
105 |
if ($auto) {
|
106 |
-
$config['max-quality'] =
|
107 |
-
$config['quality-specific'] =
|
108 |
} else {
|
109 |
$config['max-quality'] = 80;
|
110 |
-
$config['quality-specific'] =
|
111 |
}
|
112 |
|
113 |
-
$
|
114 |
-
$config['jpeg-
|
115 |
-
$config['jpeg-near-lossless'] = webp_express_sanitize_quality_field($_POST['jpeg-near-lossless']);
|
116 |
|
117 |
|
118 |
// Png
|
119 |
// --------
|
120 |
-
$config['png-encoding'] =
|
121 |
-
|
122 |
-
|
123 |
-
$config['png-
|
124 |
-
$
|
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' => $
|
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.\"ext\"';
|
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.
|
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;
|