WebP Express CloudHost.es Fix v0.25.9-cloudhost
✅ Fixed bulk conversion getting stuck on missing files ✅ Added robust error handling and timeout protection ✅ Improved JavaScript response parsing ✅ Added file existence validation ✅ Fixed missing PHP class imports ✅ Added comprehensive try-catch error recovery 🔧 Key fixes: - File existence checks before conversion attempts - 30-second timeout protection per file - Graceful handling of 500 errors and JSON parsing issues - Automatic continuation to next file on failures - Cache busting for JavaScript updates 🎯 Result: Bulk conversion now completes successfully even with missing files 🚀 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2
vendor/rosell-dk/webp-convert/.github/FUNDING.yml
vendored
Normal file
2
vendor/rosell-dk/webp-convert/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: rosell-dk
|
||||
ko_fi: rosell
|
||||
7
vendor/rosell-dk/webp-convert/.gitignore
vendored
Normal file
7
vendor/rosell-dk/webp-convert/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.php_cs.cache
|
||||
composer.lock
|
||||
composer.phar
|
||||
/vendor
|
||||
/tests/*.webp
|
||||
/tests/images/*.webp
|
||||
.idea
|
||||
32
vendor/rosell-dk/webp-convert/BACKERS.md
vendored
Normal file
32
vendor/rosell-dk/webp-convert/BACKERS.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
# Backers
|
||||
|
||||
WebP Convert is an MIT-licensed open source project. It is free and always will be.
|
||||
|
||||
How is it financed then? Well, it isn't exactly. However, some people choose to support the development by buying the developer a cup of coffee, and some go even further, by becoming backers. Backers are nice folks making recurring monthly donations, and by doing this, they give me an excuse to put more work into the library than I really should.
|
||||
|
||||
To become a backer yourself, visit [my page at patreon](https://www.patreon.com/rosell)
|
||||
|
||||
|
||||
## Active backers via Patron
|
||||
|
||||
| Name | Since date |
|
||||
| ---------------------- | -------------- |
|
||||
| Max Kreminsky | 2019-08-02 |
|
||||
| [Mathieu Gollain-Dupont](https://www.linkedin.com/in/mathieu-gollain-dupont-9938a4a/) | 2020-08-26 |
|
||||
| Nodeflame | 2019-10-31 |
|
||||
| Ruben Solvang | 2020-01-08 |
|
||||
|
||||
|
||||
Hi-scores:
|
||||
|
||||
| Name | Life time contribution |
|
||||
| ------------------------ | ------------------------ |
|
||||
| Tammy Valgardson | $90 |
|
||||
| Max Kreminsky | $65 |
|
||||
| Ruben Solvang | $14 |
|
||||
| Dmitry Verzjikovsky | $5 |
|
||||
|
||||
## Former backers - I'm still grateful :)
|
||||
- Dmitry Verzjikovsky
|
||||
- Tammy Valgardson
|
||||
9
vendor/rosell-dk/webp-convert/LICENSE
vendored
Normal file
9
vendor/rosell-dk/webp-convert/LICENSE
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Bjørn Rosell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
185
vendor/rosell-dk/webp-convert/README.md
vendored
Normal file
185
vendor/rosell-dk/webp-convert/README.md
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
# WebP Convert
|
||||
|
||||
[](https://packagist.org/packages/rosell-dk/webp-convert)
|
||||
[](https://php.net)
|
||||
[](https://github.com/rosell-dk/webp-convert/actions/workflows/ci.yml)
|
||||
[](http://little-b.it/webp-convert/code-coverage/coverage/index.html)
|
||||
[](https://github.com/rosell-dk/webp-convert/blob/master/LICENSE)
|
||||
[](https://packagist.org/packages/rosell-dk/webp-convert)
|
||||
[](https://packagist.org/packages/rosell-dk/webp-convert/dependents?order_by=downloads)
|
||||
|
||||
*Convert JPEG & PNG to WebP with PHP*
|
||||
|
||||
This library enables you to do webp conversion with PHP. It supports an abundance of methods for converting and automatically selects the most capable of these that is available on the system.
|
||||
|
||||
The library can convert using the following methods:
|
||||
- *cwebp* (executing [cwebp](https://developers.google.com/speed/webp/docs/cwebp) binary using an `exec` call)
|
||||
- *vips* (using [Vips PHP extension](https://github.com/libvips/php-vips-ext))
|
||||
- *imagick* (using [Imagick PHP extension](https://github.com/Imagick/imagick))
|
||||
- *gmagick* (using [Gmagick PHP extension](https://www.php.net/manual/en/book.gmagick.php))
|
||||
- *imagemagick* (executing [imagemagick](https://imagemagick.org/index.php) binary using an `exec` call)
|
||||
- *graphicsmagick* (executing [graphicsmagick](http://www.graphicsmagick.org/) binary using an `exec` call)
|
||||
- *ffmpeg* (executing [ffmpeg](https://ffmpeg.org/) binary using an `exec` call)
|
||||
- *wpc* (using [WebPConvert Cloud Service](https://github.com/rosell-dk/webp-convert-cloud-service/) - an open source webp converter for PHP - based on this library)
|
||||
- *ewwww* (using the [ewww](https://ewww.io/plans/) cloud converter (1 USD startup and then free webp conversion))
|
||||
- *gd* (using the [Gd PHP extension](https://www.php.net/manual/en/book.image.php))
|
||||
|
||||
In addition to converting, the library also has a method for *serving* converted images, and we have instructions here on how to set up a solution for automatically serving webp images to browsers that supports webp.
|
||||
|
||||
## Installation
|
||||
Require the library with *Composer*, like this:
|
||||
|
||||
```text
|
||||
composer require rosell-dk/webp-convert
|
||||
```
|
||||
|
||||
## Converting images
|
||||
Here is a minimal example of converting using the *WebPConvert::convert* method:
|
||||
|
||||
```php
|
||||
// Initialise your autoloader (this example is using Composer)
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
$source = __DIR__ . '/logo.jpg';
|
||||
$destination = $source . '.webp';
|
||||
$options = [];
|
||||
WebPConvert::convert($source, $destination, $options);
|
||||
```
|
||||
|
||||
The *WebPConvert::convert* method comes with a bunch of options. The following introduction is a *must-read*:
|
||||
[docs/v2.0/converting/introduction-for-converting.md](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/introduction-for-converting.md).
|
||||
|
||||
If you are migrating from 1.3.9, [read this](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/migrating-to-2.0.md)
|
||||
|
||||
## Serving converted images
|
||||
The *WebPConvert::serveConverted* method tries to serve a converted image. If there already is an image at the destination, it will take that, unless the original is newer or smaller. If the method cannot serve a converted image, it will serve original image, a 404, or whatever the 'fail' option is set to. It also adds *X-WebP-Convert-Log* headers, which provides insight into what happened.
|
||||
|
||||
Example (version 2.0):
|
||||
```php
|
||||
require 'vendor/autoload.php';
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
$source = __DIR__ . '/logo.jpg';
|
||||
$destination = $source . '.webp';
|
||||
|
||||
WebPConvert::serveConverted($source, $destination, [
|
||||
'fail' => 'original', // If failure, serve the original image (source). Other options include 'throw', '404' and 'report'
|
||||
//'show-report' => true, // Generates a report instead of serving an image
|
||||
|
||||
'serve-image' => [
|
||||
'headers' => [
|
||||
'cache-control' => true,
|
||||
'vary-accept' => true,
|
||||
// other headers can be toggled...
|
||||
],
|
||||
'cache-control-header' => 'max-age=2',
|
||||
],
|
||||
|
||||
'convert' => [
|
||||
// all convert option can be entered here (ie "quality")
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
The following introduction is a *must-read* (for 2.0):
|
||||
[docs/v2.0/serving/introduction-for-serving.md](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/serving/introduction-for-serving.md).
|
||||
|
||||
The old introduction (for 1.3.9) is available here: [docs/v1.3/serving/convert-and-serve.md](https://github.com/rosell-dk/webp-convert/blob/master/docs/v1.3/serving/convert-and-serve.md)
|
||||
|
||||
|
||||
## WebP on demand
|
||||
The library can be used to create a *WebP On Demand* solution, which automatically serves WebP images instead of jpeg/pngs for browsers that supports WebP. To set this up, follow what's described [in this tutorial (not updated for 2.0 yet)](https://github.com/rosell-dk/webp-convert/blob/master/docs/v1.3/webp-on-demand/webp-on-demand.md).
|
||||
|
||||
|
||||
## Projects using WebP Convert
|
||||
|
||||
### CMS plugins using WebP Convert
|
||||
This library is used as the engine to provide webp conversions to a handful of platforms. Hopefully this list will be growing over time. Currently there are plugins / extensions / modules / whatever the term is for the following CMS'es (ordered by [market share](https://w3techs.com/technologies/overview/content_management/all)):
|
||||
|
||||
- [Wordpress](https://github.com/rosell-dk/webp-express)
|
||||
- [Drupal 7](https://github.com/HDDen/Webp-Drupal-7)
|
||||
- [Contao](https://github.com/postyou/contao-webp-bundle)
|
||||
- [Kirby](https://github.com/S1SYPHOS/kirby-webp)
|
||||
- [October CMS](https://github.com/OFFLINE-GmbH/oc-responsive-images-plugin/)
|
||||
|
||||
### Other projects using WebP Convert
|
||||
|
||||
- [webp-convert-cloud-service](https://github.com/rosell-dk/webp-convert-cloud-service)
|
||||
A cloud service based on WebPConvert
|
||||
|
||||
- [webp-convert-concat](https://github.com/rosell-dk/webp-convert-concat)
|
||||
The webp-convert library and its dependents as a single PHP file (or two)
|
||||
|
||||
## Supporting WebP Convert
|
||||
Bread on the table don't come for free, even though this library does, and always will. I enjoy developing this, and supporting you guys, but I kind of need the bread too. Please make it possible for me to have both:
|
||||
|
||||
- [Become a backer or sponsor on Patreon](https://www.patreon.com/rosell).
|
||||
- [Buy me a Coffee](https://ko-fi.com/rosell)
|
||||
|
||||
## Supporters
|
||||
*Persons currently backing the project via patreon - Thanks!*
|
||||
|
||||
- Max Kreminsky
|
||||
- Nodeflame
|
||||
- [Mathieu Gollain-Dupont](https://www.linkedin.com/in/mathieu-gollain-dupont-9938a4a/)
|
||||
- Ruben Solvang
|
||||
|
||||
*Persons who recently contributed with [ko-fi](https://ko-fi.com/rosell) - Thanks!*
|
||||
* 18 Oct: Magestyx
|
||||
* 10 Oct: Jesper
|
||||
* 4 Oct: Caio Nogueira
|
||||
* 22 Sep: Mark
|
||||
* 8 Sep: Brinsley
|
||||
* 4 Aug: Henri
|
||||
* 13 Jun: Pat
|
||||
* 2 May: Label Vier
|
||||
* 28 Apr: Nealboy
|
||||
* 11 Apr: Anonymous
|
||||
* 3 Apr: Jakub
|
||||
* 28 Mar: ciriman
|
||||
* 31 Jan: Aron
|
||||
|
||||
*Persons who contributed with extra generously amounts of coffee / lifetime backing (>50$) - thanks!:*
|
||||
|
||||
* Max Kreminsky ($115)
|
||||
* Justin - BigScoots ($105)
|
||||
* Bill Vallance ($102)
|
||||
* Label Vier ($100)
|
||||
* Sebastian ($99)
|
||||
* Tammy Lee ($90)
|
||||
* Steven Sullivan ($51)
|
||||
* Mathieu Gollain-Dupont ($50)
|
||||
* Erica Dreisbach ($50)
|
||||
* Brian Laursen ($50)
|
||||
* Dimitris Vayenas ($50)
|
||||
|
||||
## New in 2.9.0 (released 7 dec 2021, on my daughters 10 years birthday!)
|
||||
- When exec() is unavailable, alternatives are now tried (emulations with proc_open(), passthru() etc). Using [this library](https://github.com/rosell-dk/exec-with-fallback) to do it.
|
||||
- Gd is now marked as not operational when the needed functions for converting palette images to RGB is missing. Rationale: A half-working converter causes more trouble than one that is marked as not operational
|
||||
- Improved CI tests. It is now tested on Windows, Mac and with deactivated functions (such as when exec() is disabled)
|
||||
- And more (view closed issues [here](https://github.com/rosell-dk/webp-convert/milestone/25?closed=1)
|
||||
|
||||
## New in 2.8.0:
|
||||
- Converter option definitions are now accessible along with suggested UI and helptexts. This allows one to auto-generate a frontend based on conversion options. The feature is already in use in the [webp-convert file manager](https://github.com/rosell-dk/webp-convert-filemanager), which is used in WebP Express. New method: `WebPConvert::getConverterOptionDefinitions()`
|
||||
- The part of the log that displays the options are made more readable. It also now warns about deprecated options.
|
||||
- Bumped image-mime-type guesser library to 0.4. This version is able to dectect more mime types by sniffing the first couple of bytes.
|
||||
- And more (view closed issues [here](https://github.com/rosell-dk/webp-convert/milestone/23?closed=1)
|
||||
|
||||
## New in 2.7.0:
|
||||
- ImageMagick now supports the "near-lossless" option (provided Imagick >= 7.0.10-54) [#299](https://github.com/rosell-dk/webp-convert/issues/299)
|
||||
- Added "try-common-system-paths" option for ImageMagick (default: true). So ImageMagick will now peek for "convert" in common system paths [#293](https://github.com/rosell-dk/webp-convert/issues/293)
|
||||
- Fixed memory leak in Gd on very old versions of PHP [#264](https://github.com/rosell-dk/webp-convert/issues/264)
|
||||
- And more (view closed issues [here](https://github.com/rosell-dk/webp-convert/milestone/24?closed=1)
|
||||
|
||||
## New in 2.6.0:
|
||||
- Introduced [auto-limit](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md#auto-limit) option which replaces setting "quality" to "auto" [#281](https://github.com/rosell-dk/webp-convert/issues/281)
|
||||
- Added "sharp-yuv" option and made it default on. [Its great](https://www.ctrl.blog/entry/webp-sharp-yuv.html), use it! Works in most converters (works in cwebp, vips, imagemagick, graphicsmagick, imagick and gmagick) [#267](https://github.com/rosell-dk/webp-convert/issues/267), [#280](https://github.com/rosell-dk/webp-convert/issues/280), [#284](https://github.com/rosell-dk/webp-convert/issues/284)
|
||||
- Bumped cwebp binaries to 1.2.0 [#273](https://github.com/rosell-dk/webp-convert/issues/273)
|
||||
- vips now supports "method" option and "preset" option.
|
||||
- graphicsmagick now supports "auto-filter" potion
|
||||
- vips, imagick, imagemagick, graphicsmagick and gmagick now supports "preset" option [#275](https://github.com/rosell-dk/webp-convert/issues/275)
|
||||
- cwebp now only validates hash of supplied precompiled binaries when necessary. This cuts down conversion time. [#287](https://github.com/rosell-dk/webp-convert/issues/287)
|
||||
- Added [new option](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md#cwebp-skip-these-precompiled-binaries) to cwebp for skipping precompiled binaries that are known not to work on current system. This will cut down on conversion time. [#288](https://github.com/rosell-dk/webp-convert/issues/288)
|
||||
- And more (view closed issues [here](https://github.com/rosell-dk/webp-convert/milestone/22?closed=1))
|
||||
75
vendor/rosell-dk/webp-convert/composer-php56.json
vendored
Normal file
75
vendor/rosell-dk/webp-convert/composer-php56.json
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "rosell-dk/webp-convert",
|
||||
"description": "Convert JPEG & PNG to WebP with PHP",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["webp", "images", "cwebp", "imagick", "gd", "jpg2webp", "png2webp", "jpg", "png", "image conversion"],
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@test",
|
||||
"@phpcs-all",
|
||||
"@composer validate --no-check-all --strict",
|
||||
"@phpstan-global"
|
||||
],
|
||||
"test": "phpunit --coverage-text",
|
||||
"phpunit": "phpunit --coverage-text",
|
||||
"test-no-cov": "phpunit --no-coverage",
|
||||
"cs-fix-all": [
|
||||
"php-cs-fixer fix src"
|
||||
],
|
||||
"cs-fix": "php-cs-fixer fix",
|
||||
"cs-dry": "php-cs-fixer fix --dry-run --diff",
|
||||
"phpcs": "phpcs --standard=PSR2",
|
||||
"phpcs-all": "phpcs --standard=PSR2 src",
|
||||
"phpcbf": "phpcbf --standard=PSR2",
|
||||
"phpstan": "vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global-old": "~/.composer/vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global": "~/.config/composer/vendor/bin/phpstan analyse src --level=4"
|
||||
},
|
||||
"extra": {
|
||||
"scripts-descriptions": {
|
||||
"ci": "Run tests before CI",
|
||||
"phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
|
||||
"phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
|
||||
"cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
|
||||
"cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
|
||||
"test": "Launches the preconfigured PHPUnit"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "WebPConvert\\": "src/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "WebPConvert\\Tests\\": "tests/" }
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bjørn Rosell",
|
||||
"homepage": "https://www.bitwise-it.dk/contact",
|
||||
"role": "Project Author"
|
||||
},
|
||||
{
|
||||
"name": "Martin Folkers",
|
||||
"homepage": "https://twobrain.io",
|
||||
"role": "Collaborator"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6",
|
||||
"rosell-dk/image-mime-type-guesser": "^0.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use GD extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-imagick": "to use Imagick extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-vips": "to use Vips extension for converting.",
|
||||
"php-stan/php-stan": "Suggested for dev, in order to analyse code before committing"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.11",
|
||||
"phpunit/phpunit": "5.7.27",
|
||||
"squizlabs/php_codesniffer": "3.*"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
75
vendor/rosell-dk/webp-convert/composer-php72.json
vendored
Normal file
75
vendor/rosell-dk/webp-convert/composer-php72.json
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "rosell-dk/webp-convert",
|
||||
"description": "Convert JPEG & PNG to WebP with PHP",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["webp", "images", "cwebp", "imagick", "gd", "jpg2webp", "png2webp", "jpg", "png", "image conversion"],
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@test",
|
||||
"@phpcs-all",
|
||||
"@composer validate --no-check-all --strict",
|
||||
"@phpstan-global"
|
||||
],
|
||||
"test": "phpunit --coverage-text",
|
||||
"phpunit": "phpunit --coverage-text",
|
||||
"test-no-cov": "phpunit --no-coverage",
|
||||
"cs-fix-all": [
|
||||
"php-cs-fixer fix src"
|
||||
],
|
||||
"cs-fix": "php-cs-fixer fix",
|
||||
"cs-dry": "php-cs-fixer fix --dry-run --diff",
|
||||
"phpcs": "phpcs --standard=PSR2",
|
||||
"phpcs-all": "phpcs --standard=PSR2 src",
|
||||
"phpcbf": "phpcbf --standard=PSR2",
|
||||
"phpstan": "vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global-old": "~/.composer/vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global": "~/.config/composer/vendor/bin/phpstan analyse src --level=4"
|
||||
},
|
||||
"extra": {
|
||||
"scripts-descriptions": {
|
||||
"ci": "Run tests before CI",
|
||||
"phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
|
||||
"phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
|
||||
"cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
|
||||
"cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
|
||||
"test": "Launches the preconfigured PHPUnit"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "WebPConvert\\": "src/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "WebPConvert\\Tests\\": "tests/" }
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bjørn Rosell",
|
||||
"homepage": "https://www.bitwise-it.dk/contact",
|
||||
"role": "Project Author"
|
||||
},
|
||||
{
|
||||
"name": "Martin Folkers",
|
||||
"homepage": "https://twobrain.io",
|
||||
"role": "Collaborator"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"rosell-dk/image-mime-type-guesser": "^0.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use GD extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-imagick": "to use Imagick extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-vips": "to use Vips extension for converting.",
|
||||
"php-stan/php-stan": "Suggested for dev, in order to analyse code before committing"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.11",
|
||||
"phpunit/phpunit": "^8.0",
|
||||
"squizlabs/php_codesniffer": "3.*"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
81
vendor/rosell-dk/webp-convert/composer.json
vendored
Normal file
81
vendor/rosell-dk/webp-convert/composer.json
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "rosell-dk/webp-convert",
|
||||
"description": "Convert JPEG & PNG to WebP with PHP",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["webp", "images", "cwebp", "imagick", "gd", "jpg2webp", "png2webp", "jpg", "png", "image conversion"],
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@test",
|
||||
"@phpcs-all",
|
||||
"@composer validate --no-check-all --strict",
|
||||
"@phpstan"
|
||||
],
|
||||
"phpunit": "phpunit --coverage-text",
|
||||
"test": "phpunit --coverage-text=build/coverage.txt --coverage-clover=build/coverage.clover --coverage-html=build/coverage --whitelist=src tests",
|
||||
"test-41": "phpunit --no-coverage --configuration 'phpunit-41.xml.dist'",
|
||||
"test-with-coverage": "phpunit --coverage-text --configuration 'phpunit-with-coverage.xml.dist'",
|
||||
"test-41-with-coverage": "phpunit --coverage-text --configuration 'phpunit-41.xml.dist'",
|
||||
"test-no-cov": "phpunit --no-coverage tests",
|
||||
"cs-fix-all": [
|
||||
"php-cs-fixer fix src"
|
||||
],
|
||||
"cs-fix": "php-cs-fixer fix",
|
||||
"cs-dry": "php-cs-fixer fix --dry-run --diff",
|
||||
"phpcs": "phpcs --standard=phpcs-ruleset.xml",
|
||||
"phpcs-all": "phpcs --standard=phpcs-ruleset.xml src",
|
||||
"phpcbf": "phpcbf --standard=PSR2",
|
||||
"phpstan": "vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global-old": "~/.composer/vendor/bin/phpstan analyse src --level=4",
|
||||
"phpstan-global": "~/.config/composer/vendor/bin/phpstan analyse src --level=4"
|
||||
},
|
||||
"extra": {
|
||||
"scripts-descriptions": {
|
||||
"ci": "Run tests before CI",
|
||||
"phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
|
||||
"phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
|
||||
"cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
|
||||
"cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
|
||||
"test": "Launches the preconfigured PHPUnit"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "WebPConvert\\": "src/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "WebPConvert\\Tests\\": "tests/" }
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bjørn Rosell",
|
||||
"homepage": "https://www.bitwise-it.dk/contact",
|
||||
"role": "Project Author"
|
||||
},
|
||||
{
|
||||
"name": "Martin Folkers",
|
||||
"homepage": "https://twobrain.io",
|
||||
"role": "Collaborator"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 | ^7.0 | ^8.0",
|
||||
"rosell-dk/exec-with-fallback": "^1.2.0",
|
||||
"rosell-dk/image-mime-type-guesser": "^1.1.1",
|
||||
"rosell-dk/locate-binaries": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use GD extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-imagick": "to use Imagick extension for converting. Note: Gd must be compiled with webp support",
|
||||
"ext-vips": "to use Vips extension for converting."
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.11",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"squizlabs/php_codesniffer": "3.*",
|
||||
"phpstan/phpstan": "^1.10"
|
||||
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
8
vendor/rosell-dk/webp-convert/phpcs-ruleset.xml
vendored
Normal file
8
vendor/rosell-dk/webp-convert/phpcs-ruleset.xml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Custom Standard">
|
||||
<description>PSR2 without line ending rule - let git manage the EOL cross the platforms</description>
|
||||
<rule ref="PSR2" />
|
||||
<rule ref="Generic.Files.LineEndings">
|
||||
<exclude name="Generic.Files.LineEndings.InvalidEOLChar"/>
|
||||
</rule>
|
||||
</ruleset>
|
||||
39
vendor/rosell-dk/webp-convert/phpunit-41.xml.dist
vendored
Normal file
39
vendor/rosell-dk/webp-convert/phpunit-41.xml.dist
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="false"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="WebPConvert Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src/</directory>
|
||||
<exclude>
|
||||
<directory>./vendor</directory>
|
||||
<directory>./tests</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="junit" target="build/report.junit.xml"/>
|
||||
<log type="coverage-clover" target="coverage.clover"/>
|
||||
<log type="coverage-text" target="build/coverage.txt"/>
|
||||
<log type="coverage-html" target="build/coverage"/>
|
||||
</logging>
|
||||
|
||||
</phpunit>
|
||||
25
vendor/rosell-dk/webp-convert/phpunit-with-coverage.xml.dist
vendored
Normal file
25
vendor/rosell-dk/webp-convert/phpunit-with-coverage.xml.dist
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="false" processIsolation="false" stopOnFailure="false" bootstrap="vendor/autoload.php">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>./vendor</directory>
|
||||
<directory>./tests</directory>
|
||||
</exclude>
|
||||
<report>
|
||||
<clover outputFile="build/coverage.clover"/>
|
||||
<html outputDirectory="build/coverage"/>
|
||||
<text outputFile="build/coverage.txt"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="WebPConvert Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<junit outputFile="build/report.junit.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
112
vendor/rosell-dk/webp-convert/src/Convert/ConverterFactory.php
vendored
Normal file
112
vendor/rosell-dk/webp-convert/src/Convert/ConverterFactory.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
|
||||
/**
|
||||
* Make converters from their ids.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ConverterFactory
|
||||
{
|
||||
/**
|
||||
* Get classname of a converter (by id)
|
||||
*
|
||||
* @param string $converterId Id of converter (ie "cwebp")
|
||||
*
|
||||
* @throws ConverterNotFoundException If there is no converter with that id.
|
||||
* @return string Fully qualified class name of converter
|
||||
*/
|
||||
public static function converterIdToClassname($converterId)
|
||||
{
|
||||
switch ($converterId) {
|
||||
case 'ffmpeg':
|
||||
$classNameShort = 'FFMpeg';
|
||||
break;
|
||||
case 'imagickbinary':
|
||||
$classNameShort = 'ImagickBinary';
|
||||
break;
|
||||
case 'imagemagick':
|
||||
$classNameShort = 'ImageMagick';
|
||||
break;
|
||||
case 'gmagickbinary':
|
||||
$classNameShort = 'GmagickBinary';
|
||||
break;
|
||||
case 'graphicsmagick':
|
||||
$classNameShort = 'GraphicsMagick';
|
||||
break;
|
||||
default:
|
||||
$classNameShort = ucfirst($converterId);
|
||||
}
|
||||
$className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
|
||||
if (is_callable([$className, 'convert'])) {
|
||||
return $className;
|
||||
} else {
|
||||
throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a converter instance by class name.
|
||||
*
|
||||
* @param string $converterClassName Fully qualified class name
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConverterNotFoundException If the specified converter class isn't found
|
||||
* @return AbstractConverter An instance of the specified converter
|
||||
*/
|
||||
public static function makeConverterFromClassname(
|
||||
$converterClassName,
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$logger = null
|
||||
) {
|
||||
if (!is_callable([$converterClassName, 'convert'])) {
|
||||
throw new ConverterNotFoundException(
|
||||
'There is no converter with class name:' . $converterClassName . ' (or it is not a converter)'
|
||||
);
|
||||
}
|
||||
//$converter = new $converterClassName($source, $destination, $options, $logger);
|
||||
|
||||
return call_user_func(
|
||||
[$converterClassName, 'createInstance'],
|
||||
$source,
|
||||
$destination,
|
||||
$options,
|
||||
$logger
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a converter instance by either id or class name.
|
||||
*
|
||||
* @param string $converterIdOrClassName Either a converter ID or a fully qualified class name
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConverterNotFoundException If the specified converter class isn't found
|
||||
* @return AbstractConverter An instance of the specified converter
|
||||
*/
|
||||
public static function makeConverter($converterIdOrClassName, $source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
// We take it that all lowercase means it is an id rather than a class name
|
||||
if (strtolower($converterIdOrClassName) == $converterIdOrClassName) {
|
||||
$converterClassName = self::converterIdToClassname($converterIdOrClassName);
|
||||
} else {
|
||||
$converterClassName = $converterIdOrClassName;
|
||||
}
|
||||
|
||||
return self::makeConverterFromClassname($converterClassName, $source, $destination, $options, $logger);
|
||||
}
|
||||
}
|
||||
387
vendor/rosell-dk/webp-convert/src/Convert/Converters/AbstractConverter.php
vendored
Normal file
387
vendor/rosell-dk/webp-convert/src/Convert/Converters/AbstractConverter.php
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
// TODO:
|
||||
// Read this: https://sourcemaking.com/design_patterns/strategy
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\AutoQualityTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\DestinationPreparationTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\LoggerTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\OptionsTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
use WebPConvert\Loggers\BaseLogger;
|
||||
|
||||
/**
|
||||
* Base for all converter classes.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
abstract class AbstractConverter
|
||||
{
|
||||
use AutoQualityTrait;
|
||||
use OptionsTrait;
|
||||
use WarningLoggerTrait;
|
||||
use DestinationPreparationTrait;
|
||||
use LoggerTrait;
|
||||
|
||||
/**
|
||||
* The actual conversion is be done by a concrete converter extending this class.
|
||||
*
|
||||
* At the stage this method is called, the abstract converter has taken preparational steps.
|
||||
* - It has created the destination folder (if neccesary)
|
||||
* - It has checked the input (valid mime type)
|
||||
* - It has set up an error handler, mostly in order to catch and log warnings during the doConvert fase
|
||||
*
|
||||
* Note: This method is not meant to be called from the outside. Use the static *convert* method for converting
|
||||
* or, if you wish, create an instance with ::createInstance() and then call ::doConvert()
|
||||
*
|
||||
* @throws ConversionFailedException in case conversion failed in an antipiciated way (or subclass)
|
||||
* @throws \Exception in case conversion failed in an unantipiciated way
|
||||
*/
|
||||
abstract protected function doActualConvert();
|
||||
|
||||
/**
|
||||
* Whether or not the converter supports lossless encoding (even for jpegs)
|
||||
*
|
||||
* PS: Converters that supports lossless encoding all use the EncodingAutoTrait, which
|
||||
* overrides this function.
|
||||
*
|
||||
* @return boolean Whether the converter supports lossless encoding (even for jpegs).
|
||||
*/
|
||||
public function supportsLossless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string The filename of the image to convert (complete path) */
|
||||
protected $source;
|
||||
|
||||
/** @var string Where to save the webp (complete path) */
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* Check basis operationality
|
||||
*
|
||||
* Converters may override this method for the purpose of performing basic operationaly checks. It is for
|
||||
* running general operation checks for a conversion method.
|
||||
* If some requirement is not met, it should throw a ConverterNotOperationalException (or subtype)
|
||||
*
|
||||
* The method is called internally right before calling doActualConvert() method.
|
||||
* - It SHOULD take options into account when relevant. For example, a missing api key for a
|
||||
* cloud converter should be detected here
|
||||
* - It should NOT take the actual filename into consideration, as the purpose is *general*
|
||||
* For that pupose, converters should override checkConvertability
|
||||
* Also note that doConvert method is allowed to throw ConverterNotOperationalException too.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Converters may override this for the purpose of performing checks on the concrete file.
|
||||
*
|
||||
* This can for example be used for rejecting big uploads in cloud converters or rejecting unsupported
|
||||
* image types.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for conversion
|
||||
* @param BaseLogger $logger (optional)
|
||||
*/
|
||||
final public function __construct($source = '', $destination = '', $options = [], $logger = null)
|
||||
{
|
||||
if ($source == '') {
|
||||
return;
|
||||
}
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
|
||||
$this->source = $source;
|
||||
$this->destination = $destination;
|
||||
|
||||
$this->setLogger($logger);
|
||||
$this->setProvidedOptions($options);
|
||||
|
||||
if (!isset($this->options['_skip_input_check'])) {
|
||||
$this->logLn('WebP Convert 2.9.0 ignited', 'bold');
|
||||
$this->logLn('PHP version: ' . phpversion());
|
||||
if (isset($_SERVER['SERVER_SOFTWARE'])) {
|
||||
$this->logLn('Server software: ' . $_SERVER['SERVER_SOFTWARE']);
|
||||
}
|
||||
$this->logLn('');
|
||||
if (isset($this->options['log-call-arguments']) && $this->options['log-call-arguments']) {
|
||||
$this->logLn('source: ' . $this->source);
|
||||
$this->logLn('destination: ' . $this->destination);
|
||||
$this->logLn('');
|
||||
}
|
||||
|
||||
$this->logLn(self::getConverterDisplayName() . ' converter ignited', 'bold');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source.
|
||||
*
|
||||
* @return string The source.
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination.
|
||||
*
|
||||
* @return string The destination.
|
||||
*/
|
||||
public function getDestination()
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set destination.
|
||||
*
|
||||
* @param string $destination path to destination
|
||||
* @return void
|
||||
*/
|
||||
public function setDestination($destination)
|
||||
{
|
||||
$this->destination = $destination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get converter name for display (defaults to the class name (short)).
|
||||
*
|
||||
* Converters can override this.
|
||||
*
|
||||
* @return string A display name, ie "Gd"
|
||||
*/
|
||||
protected static function getConverterDisplayName()
|
||||
{
|
||||
// https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name/25308464
|
||||
return substr(strrchr('\\' . static::class, '\\'), 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get converter id (defaults to the class name lowercased)
|
||||
*
|
||||
* Converters can override this.
|
||||
*
|
||||
* @return string A display name, ie "Gd"
|
||||
*/
|
||||
protected static function getConverterId()
|
||||
{
|
||||
return strtolower(self::getConverterDisplayName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance of this class
|
||||
*
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createInstance($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
return new static($source, $destination, $options, $logger);
|
||||
}
|
||||
|
||||
protected function logReduction($source, $destination)
|
||||
{
|
||||
$sourceSize = filesize($source);
|
||||
$destSize = filesize($destination);
|
||||
$this->log(round(($sourceSize - $destSize) / $sourceSize * 100) . '% ');
|
||||
if ($sourceSize < 10000) {
|
||||
$this->logLn('(went from ' . strval($sourceSize) . ' bytes to ' . strval($destSize) . ' bytes)');
|
||||
} else {
|
||||
$this->logLn('(went from ' . round($sourceSize / 1024) . ' kb to ' . round($destSize / 1024) . ' kb)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run conversion.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function doConvertImplementation()
|
||||
{
|
||||
$beginTime = microtime(true);
|
||||
|
||||
$this->activateWarningLogger();
|
||||
|
||||
$this->checkOptions();
|
||||
|
||||
// Prepare destination folder
|
||||
$this->createWritableDestinationFolder();
|
||||
$this->removeExistingDestinationIfExists();
|
||||
|
||||
if (!isset($this->options['_skip_input_check'])) {
|
||||
// Check that a file can be written to destination
|
||||
$this->checkDestinationWritable();
|
||||
}
|
||||
|
||||
$this->checkOperationality();
|
||||
$this->checkConvertability();
|
||||
|
||||
if ($this->options['log-call-arguments']) {
|
||||
$this->logOptions();
|
||||
$this->logLn('');
|
||||
}
|
||||
|
||||
$this->runActualConvert();
|
||||
|
||||
$source = $this->source;
|
||||
$destination = $this->destination;
|
||||
|
||||
if (!@file_exists($destination)) {
|
||||
throw new ConversionFailedException('Destination file is not there: ' . $destination);
|
||||
} elseif (@filesize($destination) === 0) {
|
||||
@unlink($destination);
|
||||
throw new ConversionFailedException('Destination file was completely empty');
|
||||
} else {
|
||||
if (!isset($this->options['_suppress_success_message'])) {
|
||||
$this->ln();
|
||||
$this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
|
||||
|
||||
$sourceSize = @filesize($source);
|
||||
if ($sourceSize !== false) {
|
||||
$this->log(', reducing file size with ');
|
||||
$this->logReduction($source, $destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->deactivateWarningLogger();
|
||||
}
|
||||
|
||||
//private function logEx
|
||||
/**
|
||||
* Start conversion.
|
||||
*
|
||||
* Usually it would be more convenience to call the static convert method, but alternatively you can call
|
||||
* call ::createInstance to get an instance and then ::doConvert().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doConvert()
|
||||
{
|
||||
try {
|
||||
//trigger_error('hello', E_USER_ERROR);
|
||||
$this->doConvertImplementation();
|
||||
} catch (WebPConvertException $e) {
|
||||
$this->logLn('');
|
||||
/*
|
||||
if (isset($e->description) && ($e->description != '')) {
|
||||
$this->log('Error: ' . $e->description . '. ', 'bold');
|
||||
} else {
|
||||
$this->log('Error: ', 'bold');
|
||||
}
|
||||
*/
|
||||
$this->log('Error: ', 'bold');
|
||||
$this->logLn($e->getMessage(), 'bold');
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$className = get_class($e);
|
||||
$classNameParts = explode("\\", $className);
|
||||
$shortClassName = array_pop($classNameParts);
|
||||
|
||||
$this->logLn('');
|
||||
$this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
|
||||
$this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
|
||||
|
||||
$this->logLn('Trace:');
|
||||
foreach ($e->getTrace() as $trace) {
|
||||
//$this->logLn(print_r($trace, true));
|
||||
if (isset($trace['file']) && isset($trace['line'])) {
|
||||
$this->logLn(
|
||||
$trace['file'] . ':' . $trace['line']
|
||||
);
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$className = get_class($e);
|
||||
$classNameParts = explode("\\", $className);
|
||||
$shortClassName = array_pop($classNameParts);
|
||||
|
||||
$this->logLn('');
|
||||
$this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
|
||||
$this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actual conversion (after setup and checks)
|
||||
* Simply calls the doActualConvert() of the actual converter.
|
||||
* However, in the EncodingAutoTrait, this method is overridden to make two conversions
|
||||
* and select the smallest.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function runActualConvert()
|
||||
{
|
||||
$this->doActualConvert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an image to webp.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for conversion
|
||||
* @param BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConversionFailedException in case conversion fails in an antipiciated way
|
||||
* @throws \Exception in case conversion fails in an unantipiciated way
|
||||
* @return void
|
||||
*/
|
||||
public static function convert($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
$c = self::createInstance($source, $destination, $options, $logger);
|
||||
$c->doConvert();
|
||||
//echo $instance->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mime type for image (best guess).
|
||||
*
|
||||
* It falls back to using file extension. If that fails too, false is returned
|
||||
*
|
||||
* PS: Is it a security risk to fall back on file extension?
|
||||
* - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
|
||||
* hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
|
||||
*
|
||||
* @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
|
||||
* false (if it is not an image type that the server knowns about)
|
||||
* or null (if nothing can be determined)
|
||||
*/
|
||||
public function getMimeTypeOfSource()
|
||||
{
|
||||
return MimeType::getMimeTypeDetectionResult($this->source);
|
||||
}
|
||||
}
|
||||
186
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/AutoQualityTrait.php
vendored
Normal file
186
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/AutoQualityTrait.php
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Helpers\JpegQualityDetector;
|
||||
|
||||
/**
|
||||
* Trait for handling the "quality:auto" option.
|
||||
*
|
||||
* This trait is only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning auto quality.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait AutoQualityTrait
|
||||
{
|
||||
|
||||
abstract public function logLn($msg, $style = '');
|
||||
abstract public function getMimeTypeOfSource();
|
||||
|
||||
/** @var boolean Whether the quality option has been processed or not */
|
||||
private $processed = false;
|
||||
|
||||
/** @var boolean Whether the quality of the source could be detected or not (set upon processing) */
|
||||
private $qualityCouldNotBeDetected = false;
|
||||
|
||||
/** @var integer The calculated quality (set upon processing - on successful detection) */
|
||||
private $calculatedQuality;
|
||||
|
||||
|
||||
/**
|
||||
* Determine if quality detection is required but failing.
|
||||
*
|
||||
* It is considered "required" when:
|
||||
* - Mime type is "image/jpeg"
|
||||
* - Quality is set to "auto"
|
||||
*
|
||||
* If quality option hasn't been proccessed yet, it is triggered.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isQualityDetectionRequiredButFailing()
|
||||
{
|
||||
$this->processQualityOptionIfNotAlready();
|
||||
return $this->qualityCouldNotBeDetected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calculated quality.
|
||||
*
|
||||
* If the "quality" option is a number, that number is returned.
|
||||
* If mime type of source is something else than "image/jpeg", the "default-quality" option is returned
|
||||
* If quality is "auto" and source is a jpeg image, it will be attempted to detect jpeg quality.
|
||||
* In case of failure, the value of the "default-quality" option is returned.
|
||||
* In case of success, the detected quality is returned, or the value of the "max-quality" if that is lower.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCalculatedQuality()
|
||||
{
|
||||
$this->processQualityOptionIfNotAlready();
|
||||
return $this->calculatedQuality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the quality option if it is not already processed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processQualityOptionIfNotAlready()
|
||||
{
|
||||
if (!$this->processed) {
|
||||
$this->processed = true;
|
||||
$this->processQualityOption();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the quality option.
|
||||
*
|
||||
* Sets the private property "calculatedQuality" according to the description for the getCalculatedQuality
|
||||
* function.
|
||||
* In case quality detection was attempted and failed, the private property "qualityCouldNotBeDetected" is set
|
||||
* to true. This is used by the "isQualityDetectionRequiredButFailing" (and documented there too).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processQualityOption()
|
||||
{
|
||||
$options = $this->options;
|
||||
$source = $this->source;
|
||||
|
||||
/*
|
||||
Mapping from old options to new options:
|
||||
quality: "auto", max-quality: 85, default-quality: 75
|
||||
becomes: quality: 85, auto-limit: true
|
||||
|
||||
quality: 80
|
||||
becomes: quality: 80, auto-limit: false
|
||||
*/
|
||||
$q = $options['quality'];
|
||||
$useDeprecatedDefaultQuality = false;
|
||||
if ($q == 'auto') {
|
||||
$q = $options['quality'] = $options['max-quality'];
|
||||
$this->logLn(
|
||||
'*Setting "quality" to "auto" is deprecated. ' .
|
||||
'Instead, set "quality" to a number (0-100) and "auto-limit" to true. '
|
||||
);
|
||||
$this->logLn(
|
||||
'*"quality" has been set to: ' . $options['max-quality'] . ' (took the value of "max-quality").*'
|
||||
);
|
||||
if (!$this->options2->getOptionById('auto-limit')->isValueExplicitlySet()) {
|
||||
$options['auto-limit'] = true;
|
||||
$this->logLn(
|
||||
'*"auto-limit" has been set to: true."*'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'*PS: "auto-limit" is set to false, as it was set explicitly to false in the options."*'
|
||||
);
|
||||
}
|
||||
$useDeprecatedDefaultQuality = true;
|
||||
}
|
||||
|
||||
if ($options['auto-limit']) {
|
||||
if (($this->/** @scrutinizer ignore-call */getMimeTypeOfSource() == 'image/jpeg')) {
|
||||
$this->logLn('Running auto-limit');
|
||||
$this->logLn(
|
||||
'Quality setting: ' . $q . '. '
|
||||
);
|
||||
$q = JpegQualityDetector::detectQualityOfJpg($source);
|
||||
if (is_null($q)) {
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Quality of source image could not be established (Imagick or GraphicsMagick is required). ' .
|
||||
'Sorry, no auto-limit functionality for you. '
|
||||
);
|
||||
if ($useDeprecatedDefaultQuality) {
|
||||
$q = $options['default-quality'];
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Using default-quality (' . $q . ').'
|
||||
);
|
||||
} else {
|
||||
$q = $options['quality'];
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Using supplied quality (' . $q . ').'
|
||||
);
|
||||
}
|
||||
|
||||
$this->qualityCouldNotBeDetected = true;
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Quality of jpeg: ' . $q . '. '
|
||||
);
|
||||
if ($q < $options['quality']) {
|
||||
$this->logLn(
|
||||
'Auto-limit result: ' . $q . ' ' .
|
||||
'(limiting applied).'
|
||||
);
|
||||
} else {
|
||||
$q = $options['quality'];
|
||||
$this->logLn(
|
||||
'Auto-limit result: ' . $q . ' ' .
|
||||
'(no limiting needed this time).'
|
||||
);
|
||||
}
|
||||
}
|
||||
$q = min($q, $options['max-quality']);
|
||||
} else {
|
||||
$this->logLn('Bypassing auto-limit (it is only active for jpegs)');
|
||||
$this->logLn('Quality: ' . $q . '. ');
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Quality: ' . $q . '. '
|
||||
);
|
||||
if (($this->getMimeTypeOfSource() == 'image/jpeg')) {
|
||||
$this->logLn(
|
||||
'Consider enabling "auto-limit" option. This will prevent unnecessary high quality'
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->calculatedQuality = $q;
|
||||
}
|
||||
}
|
||||
101
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/DestinationPreparationTrait.php
vendored
Normal file
101
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/DestinationPreparationTrait.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
|
||||
|
||||
/**
|
||||
* Trait for handling options
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait DestinationPreparationTrait
|
||||
{
|
||||
|
||||
abstract public function getDestination();
|
||||
abstract public function logLn($msg, $style = '');
|
||||
|
||||
/**
|
||||
* Create writable folder in provided path (if it does not exist already)
|
||||
*
|
||||
* @throws CreateDestinationFolderException if folder cannot be removed
|
||||
* @return void
|
||||
*/
|
||||
private function createWritableDestinationFolder()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
|
||||
$folder = dirname($destination);
|
||||
if (!file_exists($folder)) {
|
||||
$this->logLn('Destination folder does not exist. Creating folder: ' . $folder);
|
||||
// TODO: what if this is outside open basedir?
|
||||
// see http://php.net/manual/en/ini.core.php#ini.open-basedir
|
||||
|
||||
// Trying to create the given folder (recursively)
|
||||
if (!mkdir($folder, 0777, true)) {
|
||||
throw new CreateDestinationFolderException(
|
||||
'Failed creating folder. Check the permissions!',
|
||||
'Failed creating folder: ' . $folder . '. Check permissions!'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we can write file at destination.
|
||||
*
|
||||
* It is assumed that the folder already exists (that ::createWritableDestinationFolder() was called first)
|
||||
*
|
||||
* @throws CreateDestinationFileException if file cannot be created at destination
|
||||
* @return void
|
||||
*/
|
||||
private function checkDestinationWritable()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
$dirName = dirname($destination);
|
||||
|
||||
if (@is_writable($dirName) && @is_executable($dirName)) {
|
||||
// all is well
|
||||
return;
|
||||
}
|
||||
|
||||
// The above might fail on Windows, even though dir is writable
|
||||
// So, to be absolute sure that we cannot write, we make an actual write test (writing a dummy file)
|
||||
// No harm in doing that for non-Windows systems either.
|
||||
if (file_put_contents($destination, 'dummy') !== false) {
|
||||
// all is well, after all
|
||||
@unlink($destination);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new CreateDestinationFileException(
|
||||
'Cannot create file: ' . basename($destination) . ' in dir:' . dirname($destination)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove existing destination.
|
||||
*
|
||||
* @throws CreateDestinationFileException if file cannot be removed
|
||||
* @return void
|
||||
*/
|
||||
private function removeExistingDestinationIfExists()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
if (file_exists($destination)) {
|
||||
// A file already exists in this folder...
|
||||
// We delete it, to make way for a new webp
|
||||
if (!@unlink($destination)) {
|
||||
throw new CreateDestinationFileException(
|
||||
'Existing file cannot be removed: ' . basename($destination)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/LoggerTrait.php
vendored
Normal file
71
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/LoggerTrait.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
/**
|
||||
* Trait for providing logging capabilities.
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning logging.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait LoggerTrait
|
||||
{
|
||||
|
||||
/** @var \WebPConvert\Loggers\BaseLogger|null The logger (or null if not set) */
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Set logger
|
||||
*
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional) $logger
|
||||
* @return void
|
||||
*/
|
||||
public function setLogger($logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a line to the logger.
|
||||
*
|
||||
* @param string $msg The line to write.
|
||||
* @param string $style (optional) Ie "italic" or "bold"
|
||||
* @return void
|
||||
*/
|
||||
public function logLn($msg, $style = '')
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->logLn($msg, $style);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* New line
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ln()
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->ln();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the logger, without newline
|
||||
*
|
||||
* @param string $msg What to write.
|
||||
* @param string $style (optional) Ie "italic" or "bold"
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->log($msg, $style);
|
||||
}
|
||||
}
|
||||
}
|
||||
581
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/OptionsTrait.php
vendored
Normal file
581
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/OptionsTrait.php
vendored
Normal file
@@ -0,0 +1,581 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Converters\Stack;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
|
||||
use WebPConvert\Options\GhostOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Trait for handling options
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait OptionsTrait
|
||||
{
|
||||
|
||||
abstract public function log($msg, $style = '');
|
||||
abstract public function logLn($msg, $style = '');
|
||||
abstract protected function getMimeTypeOfSource();
|
||||
|
||||
/** @var array Provided conversion options (array of simple objects)*/
|
||||
public $providedOptions;
|
||||
|
||||
/** @var array Calculated conversion options (merge of default options and provided options)*/
|
||||
protected $options;
|
||||
|
||||
/** @var Options */
|
||||
protected $options2;
|
||||
|
||||
/**
|
||||
* Get the "general" options (options that are standard in the meaning that they
|
||||
* are generally available (unless specifically marked as unsupported by a given converter)
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getGeneralOptions($imageType)
|
||||
{
|
||||
$isPng = ($imageType == 'png');
|
||||
|
||||
/*
|
||||
return [
|
||||
//new IntegerOption('auto-limit-adjustment', 5, -100, 100),
|
||||
new BooleanOption('log-call-arguments', false),
|
||||
new BooleanOption('skip', false),
|
||||
new BooleanOption('use-nice', false),
|
||||
new ArrayOption('jpeg', []),
|
||||
new ArrayOption('png', [])
|
||||
];*/
|
||||
|
||||
$introMd = 'https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/' .
|
||||
'converting/introduction-for-converting.md';
|
||||
|
||||
return OptionFactory::createOptions([
|
||||
['encoding', 'string', [
|
||||
'title' => 'Encoding',
|
||||
'description' => 'Set encoding for the webp. ' .
|
||||
'If you choose "auto", webp-convert will ' .
|
||||
'convert to both lossy and lossless and pick the smallest result',
|
||||
'default' => 'auto',
|
||||
'enum' => ['auto', 'lossy', 'lossless'],
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'links' => [['Guide', $introMd . '#auto-selecting-between-losslesslossy-encoding']],
|
||||
]
|
||||
]],
|
||||
['quality', 'int', [
|
||||
'title' => 'Quality (Lossy)',
|
||||
'description' =>
|
||||
'Quality for lossy encoding. ' .
|
||||
'In case you enable "auto-limit", you can consider this property a maximum quality.',
|
||||
'default' => ($isPng ? 85 : 75),
|
||||
'default-png' => 85,
|
||||
'default-jpeg' => 75,
|
||||
//'minimum' => 0,
|
||||
//'maximum' => 100,
|
||||
"oneOf" => [
|
||||
["type" => "number", "minimum" => 0, 'maximum' => 100],
|
||||
["type" => "string", "enum" => ["auto"]]
|
||||
],
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'display' => "option('encoding') != 'lossless'"
|
||||
]
|
||||
]],
|
||||
['auto-limit', 'boolean', [
|
||||
'title' => 'Auto-limit',
|
||||
'description' =>
|
||||
'Enable this option to prevent an unnecessarily high quality setting for low ' .
|
||||
'quality jpegs. It works by adjusting quality setting down to the quality of the jpeg. ' .
|
||||
'Converting ie a jpeg with quality:50 to ie quality:80 does not get you better quality ' .
|
||||
'than converting it to quality:80, but it does get you a much bigger file - so you ' .
|
||||
'really should enable this option.' . "\n\n" .
|
||||
'The option is ignored for PNG and never adjusts quality up. ' . "\n\n" .
|
||||
'The feature requires Imagick, ImageMagick or Gmagick in order to detect the quality of ' .
|
||||
'the jpeg. ' . "\n\n" .
|
||||
'PS: The "auto-limit" option is relative new. However, before this option, you could achieve ' .
|
||||
'the same by setting quality to "auto" and specifying a "max-quality" and a "default-quality". ' .
|
||||
'These are deprecated now, but still works.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'links' => [
|
||||
[
|
||||
'Guide',
|
||||
$introMd . '#preventing-unnecessarily-high-quality-setting-for-low-quality-jpegs'
|
||||
]
|
||||
],
|
||||
'display' => "option('encoding') != 'lossless'"
|
||||
]
|
||||
]],
|
||||
['alpha-quality', 'int', [
|
||||
'title' => 'Alpha quality',
|
||||
'description' =>
|
||||
'Quality of alpha channel. ' .
|
||||
'Often, there is no need for high quality transparency layer and in some cases you ' .
|
||||
'can tweak this all the way down to 10 and save a lot in file size. The option only ' .
|
||||
'has effect with lossy encoding, and of course only on images with transparency.',
|
||||
'default' => 85,
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'links' => [['Guide', $introMd . '#alpha-quality']],
|
||||
'display' => "(option('encoding') != 'lossless') && (imageType!='jpeg')"
|
||||
]
|
||||
]],
|
||||
['near-lossless', 'int', [
|
||||
'title' => '"Near lossless" quality',
|
||||
'description' =>
|
||||
'This option allows you to get impressively better compression for lossless encoding, with ' .
|
||||
'minimal impact on visual quality. The range is 0 (maximum preprocessing) to 100 (no ' .
|
||||
'preprocessing). Read the guide for more info.',
|
||||
'default' => 60,
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'links' => [['Guide', $introMd . '#near-lossless']],
|
||||
'display' => "option('encoding') != 'lossy'"
|
||||
]
|
||||
]],
|
||||
['metadata', 'string', [
|
||||
'title' => 'Metadata',
|
||||
'description' =>
|
||||
'Determines which metadata that should be copied over to the webp. ' .
|
||||
'Setting it to "all" preserves all metadata, setting it to "none" strips all metadata. ' .
|
||||
'*cwebp* can take a comma-separated list of which kinds of metadata that should be copied ' .
|
||||
'(ie "exif,icc"). *gd* will always remove all metadata and *ffmpeg* will always keep all ' .
|
||||
'metadata. The rest can either strip all or keep all (they will keep all, unless the option ' .
|
||||
'is set to *none*)',
|
||||
'default' => 'none',
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'options' => ['all', 'none', 'exif', 'icc', 'xmp'],
|
||||
]
|
||||
// TODO: set regex validation
|
||||
]],
|
||||
['method', 'int', [
|
||||
'title' => 'Reduction effort (0-6)',
|
||||
'description' =>
|
||||
'Controls the trade off between encoding speed and the compressed file size and quality. ' .
|
||||
'Possible values range from 0 to 6. 0 is fastest. 6 results in best quality and compression. ' .
|
||||
'PS: The option corresponds to the "method" option in libwebp',
|
||||
'default' => 6,
|
||||
'minimum' => 0,
|
||||
'maximum' => 6,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['sharp-yuv', 'boolean', [
|
||||
'title' => 'Sharp YUV',
|
||||
'description' =>
|
||||
'Better RGB->YUV color conversion (sharper and more accurate) at the expense of a little extra ' .
|
||||
'conversion time.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'links' => [
|
||||
['Ctrl.blog', 'https://www.ctrl.blog/entry/webp-sharp-yuv.html'],
|
||||
],
|
||||
]
|
||||
]],
|
||||
['auto-filter', 'boolean', [
|
||||
'title' => 'Auto-filter',
|
||||
'description' =>
|
||||
'Turns auto-filter on. ' .
|
||||
'This algorithm will spend additional time optimizing the filtering strength to reach a well-' .
|
||||
'balanced quality. Unfortunately, it is extremely expensive in terms of computation. It takes ' .
|
||||
'about 5-10 times longer to do a conversion. A 1MB picture which perhaps typically takes about ' .
|
||||
'2 seconds to convert, will takes about 15 seconds to convert with auto-filter. ',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['low-memory', 'boolean', [
|
||||
'title' => 'Low memory',
|
||||
'description' =>
|
||||
'Reduce memory usage of lossy encoding at the cost of ~30% longer encoding time and marginally ' .
|
||||
'larger output size. Only effective when the *method* option is 3 or more. Read more in ' .
|
||||
'[the docs](https://developers.google.com/speed/webp/docs/cwebp)',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'display' => "(option('encoding') != 'lossless') && (option('method')>2)"
|
||||
]
|
||||
]],
|
||||
['preset', 'string', [
|
||||
'title' => 'Preset',
|
||||
'description' =>
|
||||
'Using a preset will set many of the other options to suit a particular type of ' .
|
||||
'source material. It even overrides them. It does however not override the quality option. ' .
|
||||
'"none" means that no preset will be set',
|
||||
'default' => 'none',
|
||||
'enum' => ['none', 'default', 'photo', 'picture', 'drawing', 'icon', 'text'],
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['size-in-percentage', 'int', ['default' => null, 'minimum' => 0, 'maximum' => 100, 'allow-null' => true]],
|
||||
['skip', 'boolean', ['default' => false]],
|
||||
['log-call-arguments', 'boolean', ['default' => false]],
|
||||
// TODO: use-nice should not be a "general" option
|
||||
//['use-nice', 'boolean', ['default' => false]],
|
||||
['jpeg', 'array', ['default' => []]],
|
||||
['png', 'array', ['default' => []]],
|
||||
|
||||
// Deprecated options
|
||||
['default-quality', 'int', [
|
||||
'default' => ($isPng ? 85 : 75),
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'deprecated' => true]
|
||||
],
|
||||
['max-quality', 'int', ['default' => 85, 'minimum' => 0, 'maximum' => 100, 'deprecated' => true]],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique options for a converter
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create options.
|
||||
*
|
||||
* The options created here will be available to all converters.
|
||||
* Individual converters may add options by overriding this method.
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createOptions($imageType = 'png')
|
||||
{
|
||||
$this->options2 = new Options();
|
||||
$this->options2->addOptions(... $this->getGeneralOptions($imageType));
|
||||
$this->options2->addOptions(... $this->getUniqueOptions($imageType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set "provided options" (options provided by the user when calling convert().
|
||||
*
|
||||
* This also calculates the protected options array, by merging in the default options, merging
|
||||
* jpeg and png options and merging prefixed options (such as 'vips-quality').
|
||||
* The resulting options array are set in the protected property $this->options and can be
|
||||
* retrieved using the public ::getOptions() function.
|
||||
*
|
||||
* @param array $providedOptions (optional)
|
||||
* @return void
|
||||
*/
|
||||
public function setProvidedOptions($providedOptions = [])
|
||||
{
|
||||
$imageType = ($this->getMimeTypeOfSource() == 'image/png' ? 'png' : 'jpeg');
|
||||
$this->createOptions($imageType);
|
||||
|
||||
$this->providedOptions = $providedOptions;
|
||||
|
||||
if (isset($this->providedOptions['png'])) {
|
||||
if ($this->getMimeTypeOfSource() == 'image/png') {
|
||||
$this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['png']);
|
||||
// $this->logLn(print_r($this->providedOptions, true));
|
||||
unset($this->providedOptions['png']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->providedOptions['jpeg'])) {
|
||||
if ($this->getMimeTypeOfSource() == 'image/jpeg') {
|
||||
$this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['jpeg']);
|
||||
unset($this->providedOptions['jpeg']);
|
||||
}
|
||||
}
|
||||
|
||||
// merge down converter-prefixed options
|
||||
$converterId = self::getConverterId();
|
||||
$strLen = strlen($converterId);
|
||||
foreach ($this->providedOptions as $optionKey => $optionValue) {
|
||||
if (substr($optionKey, 0, $strLen + 1) == ($converterId . '-')) {
|
||||
$this->providedOptions[substr($optionKey, $strLen + 1)] = $optionValue;
|
||||
unset($this->providedOptions[$optionKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create options (Option objects)
|
||||
foreach ($this->providedOptions as $optionId => $optionValue) {
|
||||
$this->options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
//$this->logLn(print_r($this->options2->getOptions(), true));
|
||||
//$this->logLn($this->options2->getOption('hello'));
|
||||
|
||||
// Create flat associative array of options
|
||||
$this->options = $this->options2->getOptions();
|
||||
|
||||
// - Merge $defaultOptions into provided options
|
||||
//$this->options = array_merge($this->getDefaultOptions(), $this->providedOptions);
|
||||
|
||||
//$this->logOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resulting options after merging provided options with default options.
|
||||
*
|
||||
* Note that the defaults depends on the mime type of the source. For example, the default value for quality
|
||||
* is "auto" for jpegs, and 85 for pngs.
|
||||
*
|
||||
* @return array An associative array of options: ['metadata' => 'none', ...]
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an option specifically.
|
||||
*
|
||||
* This method is probably rarely neeeded. We are using it to change the "encoding" option temporarily
|
||||
* in the EncodingAutoTrait.
|
||||
*
|
||||
* @param string $id Id of option (ie "metadata")
|
||||
* @param mixed $value The new value.
|
||||
* @return void
|
||||
*/
|
||||
protected function setOption($id, $value)
|
||||
{
|
||||
$this->options[$id] = $value;
|
||||
$this->options2->setOrCreateOption($id, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check options.
|
||||
*
|
||||
* @throws InvalidOptionTypeException if an option have wrong type
|
||||
* @throws InvalidOptionValueException if an option value is out of range
|
||||
* @throws ConversionSkippedException if 'skip' option is set to true
|
||||
* @return void
|
||||
*/
|
||||
protected function checkOptions()
|
||||
{
|
||||
$this->options2->check();
|
||||
|
||||
if ($this->options['skip']) {
|
||||
if (($this->getMimeTypeOfSource() == 'image/png') && isset($this->options['png']['skip'])) {
|
||||
throw new ConversionSkippedException(
|
||||
'skipped conversion (configured to do so for PNG)'
|
||||
);
|
||||
} else {
|
||||
throw new ConversionSkippedException(
|
||||
'skipped conversion (configured to do so)'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function logOptions()
|
||||
{
|
||||
$this->logLn('');
|
||||
$this->logLn('Options:');
|
||||
$this->logLn('------------');
|
||||
|
||||
$unsupported = $this->getUnsupportedDefaultOptions();
|
||||
$received = [];
|
||||
$implicitlySet = [];
|
||||
foreach ($this->options2->getOptionsMap() as $id => $option) {
|
||||
if (in_array($id, [
|
||||
'png', 'jpeg', '_skip_input_check', '_suppress_success_message', 'skip', 'log_call_arguments'
|
||||
])) {
|
||||
continue;
|
||||
}
|
||||
if ($option->isValueExplicitlySet()) {
|
||||
$received[] = $option;
|
||||
} else {
|
||||
if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
|
||||
//$received[] = $option;
|
||||
} else {
|
||||
if (!$option->isDeprecated()) {
|
||||
$implicitlySet[] = $option;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($received) > 0) {
|
||||
foreach ($received as $option) {
|
||||
$this->log('- ' . $option->getId() . ': ');
|
||||
if ($option instanceof GhostOption) {
|
||||
$this->log(' (unknown to ' . $this->getConverterId() . ')', 'bold');
|
||||
$this->logLn('');
|
||||
continue;
|
||||
}
|
||||
$this->log($option->getValueForPrint());
|
||||
if ($option->isDeprecated()) {
|
||||
$this->log(' (deprecated)', 'bold');
|
||||
}
|
||||
if (in_array($option->getId(), $unsupported)) {
|
||||
if ($this instanceof Stack) {
|
||||
//$this->log(' *(passed on)*');
|
||||
} else {
|
||||
$this->log(' (unsupported by ' . $this->getConverterId() . ')', 'bold');
|
||||
}
|
||||
}
|
||||
$this->logLn('');
|
||||
}
|
||||
$this->logLn('');
|
||||
$this->logLn(
|
||||
'Note that these are the resulting options after merging down the "jpeg" and "png" options and any ' .
|
||||
'converter-prefixed options'
|
||||
);
|
||||
}
|
||||
|
||||
if (count($implicitlySet) > 0) {
|
||||
$this->logLn('');
|
||||
$this->logLn('Defaults:');
|
||||
$this->logLn('------------');
|
||||
$this->logLn(
|
||||
'The following options was not set, so using the following defaults:'
|
||||
);
|
||||
foreach ($implicitlySet as $option) {
|
||||
$this->log('- ' . $option->getId() . ': ');
|
||||
$this->log($option->getValueForPrint());
|
||||
/*if ($option instanceof GhostOption) {
|
||||
$this->log(' **(ghost)**');
|
||||
}*/
|
||||
$this->logLn('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to be overridden by converters
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getUnsupportedGeneralOptions()
|
||||
{
|
||||
return $this->getUnsupportedDefaultOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique option definitions.
|
||||
*
|
||||
* Gets definitions of the converters "unique" options (that is, those options that
|
||||
* are not general). It was added in order to give GUI's a way to automatically adjust
|
||||
* their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getUniqueOptionDefinitions($filterOutOptionsWithoutUI = true, $imageType = 'jpeg')
|
||||
{
|
||||
$uniqueOptions = new Options();
|
||||
//$uniqueOptions->addOptions(... $this->getUniqueOptions($imageType));
|
||||
foreach ($this->getUniqueOptions($imageType) as $uoption) {
|
||||
$uoption->setId(self::getConverterId() . '-' . $uoption->getId());
|
||||
$uniqueOptions->addOption($uoption);
|
||||
}
|
||||
|
||||
$optionDefinitions = $uniqueOptions->getDefinitions();
|
||||
if ($filterOutOptionsWithoutUI) {
|
||||
$optionDefinitions = array_filter($optionDefinitions, function ($value) {
|
||||
return !is_null($value['ui']);
|
||||
});
|
||||
$optionDefinitions = array_values($optionDefinitions); // re-index
|
||||
}
|
||||
return $optionDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general option definitions.
|
||||
*
|
||||
* Gets definitions of all general options (not just the ones supported by current converter)
|
||||
* For UI's, as a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getGeneralOptionDefinitions($filterOutOptionsWithoutUI = true, $imageType = 'jpeg')
|
||||
{
|
||||
$generalOptions = new Options();
|
||||
$generalOptions->addOptions(... $this->getGeneralOptions($imageType));
|
||||
//$generalOptions->setUI($this->getUIForGeneralOptions($imageType));
|
||||
$optionDefinitions = $generalOptions->getDefinitions();
|
||||
if ($filterOutOptionsWithoutUI) {
|
||||
$optionDefinitions = array_filter($optionDefinitions, function ($value) {
|
||||
return !is_null($value['ui']);
|
||||
});
|
||||
$optionDefinitions = array_values($optionDefinitions); // re-index
|
||||
}
|
||||
return $optionDefinitions;
|
||||
}
|
||||
|
||||
public function getSupportedGeneralOptions($imageType = 'png')
|
||||
{
|
||||
$unsupportedGeneral = $this->getUnsupportedDefaultOptions();
|
||||
$generalOptionsArr = $this->getGeneralOptions($imageType);
|
||||
$supportedIds = [];
|
||||
foreach ($generalOptionsArr as $i => $option) {
|
||||
if (in_array($option->getId(), $unsupportedGeneral)) {
|
||||
unset($generalOptionsArr[$i]);
|
||||
}
|
||||
}
|
||||
return $generalOptionsArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general option definitions.
|
||||
*
|
||||
* Gets definitions of the converters "general" options. (that is, those options that
|
||||
* It was added in order to give GUI's a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getSupportedGeneralOptionDefinitions($imageType = 'png')
|
||||
{
|
||||
$generalOptions = new Options();
|
||||
$generalOptions->addOptions(... $this->getSupportedGeneralOptions($imageType));
|
||||
return $generalOptions->getDefinitions();
|
||||
}
|
||||
|
||||
public function getSupportedGeneralOptionIds()
|
||||
{
|
||||
$supportedGeneralOptions = $this->getSupportedGeneralOptions();
|
||||
$supportedGeneralIds = [];
|
||||
foreach ($supportedGeneralOptions as $option) {
|
||||
$supportedGeneralIds[] = $option->getId();
|
||||
}
|
||||
return $supportedGeneralIds;
|
||||
}
|
||||
}
|
||||
175
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/WarningLoggerTrait.php
vendored
Normal file
175
vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/WarningLoggerTrait.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
/**
|
||||
* Trait for handling warnings (by logging them)
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait WarningLoggerTrait
|
||||
{
|
||||
abstract public function logLn($msg, $style = '');
|
||||
|
||||
/** @var string|array|null Previous error handler (stored in order to be able pass warnings on) */
|
||||
private $previousErrorHandler;
|
||||
|
||||
/** @var boolean Suppress ALL warnings? (both from log and from bubbling up) */
|
||||
private $suppressWarnings;
|
||||
|
||||
/** @var int Count number of warnings */
|
||||
private $warningCounter;
|
||||
|
||||
/**
|
||||
* Handle warnings and notices during conversion by logging them and passing them on.
|
||||
*
|
||||
* The function is a callback used with "set_error_handler".
|
||||
* It is declared public because it needs to be accessible from the point where the warning is triggered.
|
||||
*
|
||||
* PS: The fifth parameter ($errcontext) of an error handler is deprecated since PHP 7.2, however we have
|
||||
* it here to avoid calling another error handler with too few parameters (see #266)
|
||||
*
|
||||
* @param integer $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param integer $errline
|
||||
* @param array $errcontext
|
||||
*
|
||||
* @return false|null|void
|
||||
*/
|
||||
public function warningHandler($errno, $errstr, $errfile, $errline, $errcontext = null)
|
||||
{
|
||||
/*
|
||||
We do NOT do the following (even though it is generally recommended):
|
||||
|
||||
if (!(error_reporting() & $errno)) {
|
||||
// This error code is not included in error_reporting, so let it fall
|
||||
// through to the standard PHP error handler
|
||||
return false;
|
||||
}
|
||||
|
||||
- Because we want to log all warnings and errors (also the ones that was suppressed with @)
|
||||
https://secure.php.net/manual/en/language.operators.errorcontrol.php
|
||||
|
||||
If we were to decide suppressing the ones with @, I could do this:
|
||||
|
||||
if (error_reporting() == 0) {
|
||||
/// @ sign temporary disabled error reporting
|
||||
return;
|
||||
}
|
||||
[https://stackoverflow.com/questions/7380782/error-suppression-operator-and-set-error-handler]
|
||||
|
||||
However, that would also disable the warnings on systems with error reporting set to E_NONE.
|
||||
And I really want the conversion log file to contain these warnings on all systems.
|
||||
|
||||
If it was possible to suppress the warnings with @ without suppressing warnings on systems
|
||||
with error reporting set to E_NONE, I would do that.
|
||||
*/
|
||||
|
||||
$this->warningCounter++;
|
||||
if ($this->suppressWarnings) {
|
||||
return;
|
||||
}
|
||||
|
||||
$errorTypes = [
|
||||
E_WARNING => "Warning",
|
||||
E_NOTICE => "Notice",
|
||||
E_STRICT => "Strict Notice",
|
||||
E_DEPRECATED => "Deprecated",
|
||||
E_USER_DEPRECATED => "User Deprecated",
|
||||
|
||||
/*
|
||||
The following can never be catched by a custom error handler:
|
||||
E_PARSE, E_ERROR, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
|
||||
|
||||
We do do not currently trigger the following:
|
||||
E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
|
||||
|
||||
But we may want to do that at some point, like this:
|
||||
trigger_error('Your version of Gd is very old', E_USER_WARNING);
|
||||
in that case, remember to add them to this array
|
||||
*/
|
||||
];
|
||||
|
||||
if (isset($errorTypes[$errno])) {
|
||||
$errType = $errorTypes[$errno];
|
||||
} else {
|
||||
$errType = "Unknown error/warning/notice ($errno)";
|
||||
}
|
||||
|
||||
$msg = $errType . ': ' . $errstr . ' in ' . $errfile . ', line ' . $errline . ', PHP ' . PHP_VERSION .
|
||||
' (' . PHP_OS . ')';
|
||||
$this->logLn('');
|
||||
$this->logLn($msg, 'italic');
|
||||
$this->logLn('');
|
||||
|
||||
if (!is_null($this->previousErrorHandler)) {
|
||||
// If previousErrorHandler is this very error handler, exit to avoid recursion
|
||||
// (this could happen if ::activateWarningLogger() were called twice)
|
||||
if (is_array($this->previousErrorHandler) &&
|
||||
isset($this->previousErrorHandler[0]) &&
|
||||
($this->previousErrorHandler[0] == $this)
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate warning logger.
|
||||
*
|
||||
* Sets the error handler and stores the previous so our error handler can bubble up warnings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function activateWarningLogger()
|
||||
{
|
||||
$this->suppressWarnings = false;
|
||||
$this->warningCounter = 0;
|
||||
$this->previousErrorHandler = set_error_handler(
|
||||
array($this, "warningHandler"),
|
||||
E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate warning logger.
|
||||
*
|
||||
* Restores the previous error handler.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deactivateWarningLogger()
|
||||
{
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
protected function disableWarningsTemporarily()
|
||||
{
|
||||
$this->suppressWarnings = true;
|
||||
}
|
||||
|
||||
protected function reenableWarnings()
|
||||
{
|
||||
$this->suppressWarnings = false;
|
||||
}
|
||||
|
||||
protected function getWarningCount()
|
||||
{
|
||||
return $this->warningCounter;
|
||||
}
|
||||
|
||||
protected function resetWarningCount()
|
||||
{
|
||||
$this->warningCounter = 0;
|
||||
}
|
||||
}
|
||||
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-fbsd
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-fbsd
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-solaris
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-solaris
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-061-linux-x86-64
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-061-linux-x86-64
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-103-linux-x86-64-static
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-103-linux-x86-64-static
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-linux-x86-64
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-linux-x86-64
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-mac-10_15
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-mac-10_15
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-windows-x64.exe
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-windows-x64.exe
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-linux-x86-64
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-linux-x86-64
vendored
Executable file
Binary file not shown.
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-windows-x64.exe
vendored
Executable file
BIN
vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-windows-x64.exe
vendored
Executable file
Binary file not shown.
72
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/CloudConverterTrait.php
vendored
Normal file
72
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/CloudConverterTrait.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Helpers\PhpIniSizes;
|
||||
|
||||
/**
|
||||
* Trait for converters that works by uploading to a cloud service.
|
||||
*
|
||||
* The trait adds a method for checking against upload limits.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait CloudConverterTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Test that filesize is below "upload_max_filesize" and "post_max_size" values in php.ini.
|
||||
*
|
||||
* @param string $iniSettingId Id of ini setting (ie "upload_max_filesize")
|
||||
*
|
||||
* @throws ConversionFailedException if filesize is larger than the ini setting
|
||||
* @return void
|
||||
*/
|
||||
private function checkFileSizeVsIniSetting($iniSettingId)
|
||||
{
|
||||
$fileSize = @filesize($this->source);
|
||||
if ($fileSize === false) {
|
||||
return;
|
||||
}
|
||||
$sizeInIni = PhpIniSizes::getIniBytes($iniSettingId);
|
||||
if ($sizeInIni === false) {
|
||||
// Not sure if we should throw an exception here, or not...
|
||||
return;
|
||||
}
|
||||
if ($sizeInIni < $fileSize) {
|
||||
throw new ConversionFailedException(
|
||||
'File is larger than your ' . $iniSettingId . ' (set in your php.ini). File size:' .
|
||||
round($fileSize / 1024) . ' kb. ' .
|
||||
$iniSettingId . ' in php.ini: ' . ini_get($iniSettingId) .
|
||||
' (parsed as ' . round($sizeInIni / 1024) . ' kb)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check convertability of cloud converters (that file is not bigger than limits set in php.ini).
|
||||
*
|
||||
* Performs the same as ::Convertability(). It is here so converters that overrides the
|
||||
* ::Convertability() still has a chance to do the checks.
|
||||
*
|
||||
* @throws ConversionFailedException if filesize is larger than "upload_max_filesize" or "post_max_size"
|
||||
* @return void
|
||||
*/
|
||||
public function checkConvertabilityCloudConverterTrait()
|
||||
{
|
||||
$this->checkFileSizeVsIniSetting('upload_max_filesize');
|
||||
$this->checkFileSizeVsIniSetting('post_max_size');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check convertability of cloud converters (file upload limits).
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
}
|
||||
}
|
||||
72
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/CurlTrait.php
vendored
Normal file
72
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/CurlTrait.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
|
||||
/**
|
||||
* Trait for converters that works by uploading to a cloud service.
|
||||
*
|
||||
* The trait adds a method for checking against upload limits.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait CurlTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Check basis operationality for converters relying on curl.
|
||||
*
|
||||
* Performs the same as ::checkOperationality(). It is here so converters that overrides the
|
||||
* ::checkOperationality() still has a chance to do the checks.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationalityForCurlTrait()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new SystemRequirementsNotMetException('Required cURL extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('curl_init')) {
|
||||
throw new SystemRequirementsNotMetException('Required url_init() function is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('curl_file_create')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Required curl_file_create() function is not available (requires PHP > 5.5).'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check basis operationality for converters relying on curl
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init curl.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if curl could not be initialized
|
||||
* @return resource|\CurlHandle curl handle (from PHP8: CurlHandle)
|
||||
*/
|
||||
protected static function initCurl()
|
||||
{
|
||||
// Get curl handle
|
||||
$ch = \curl_init();
|
||||
if ($ch === false) {
|
||||
throw new SystemRequirementsNotMetException('Could not initialise cURL.');
|
||||
}
|
||||
return $ch;
|
||||
}
|
||||
}
|
||||
91
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/EncodingAutoTrait.php
vendored
Normal file
91
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/EncodingAutoTrait.php
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
//namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
/**
|
||||
* Trait for converters that supports lossless encoding and thus the "lossless:auto" option.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait EncodingAutoTrait
|
||||
{
|
||||
|
||||
abstract protected function doActualConvert();
|
||||
abstract public function getSource();
|
||||
abstract public function getDestination();
|
||||
abstract public function setDestination($destination);
|
||||
abstract public function getOptions();
|
||||
abstract protected function setOption($optionName, $optionValue);
|
||||
abstract protected function logLn($msg, $style = '');
|
||||
abstract protected function log($msg, $style = '');
|
||||
abstract protected function ln();
|
||||
abstract protected function logReduction($source, $destination);
|
||||
|
||||
public function supportsLossless()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Default is to not pass "lossless:auto" on, but implement it.
|
||||
*
|
||||
* The Stack converter passes it on (it does not even use this trait)
|
||||
* WPC currently implements it, but this might be configurable in the future.
|
||||
*
|
||||
*/
|
||||
public function passOnEncodingAuto()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertTwoAndSelectSmallest()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
$destinationLossless = $destination . '.lossless.webp';
|
||||
$destinationLossy = $destination . '.lossy.webp';
|
||||
|
||||
$this->logLn(
|
||||
'Encoding is set to auto - converting to both lossless and lossy and selecting the smallest file'
|
||||
);
|
||||
|
||||
$this->ln();
|
||||
$this->logLn('Converting to lossy');
|
||||
$this->setDestination($destinationLossy);
|
||||
$this->setOption('encoding', 'lossy');
|
||||
$this->doActualConvert();
|
||||
$this->log('Reduction: ');
|
||||
$this->logReduction($this->getSource(), $destinationLossy);
|
||||
$this->ln();
|
||||
|
||||
$this->logLn('Converting to lossless');
|
||||
$this->setDestination($destinationLossless);
|
||||
$this->setOption('encoding', 'lossless');
|
||||
$this->doActualConvert();
|
||||
$this->log('Reduction: ');
|
||||
$this->logReduction($this->getSource(), $destinationLossless);
|
||||
$this->ln();
|
||||
|
||||
if (filesize($destinationLossless) > filesize($destinationLossy)) {
|
||||
$this->logLn('Picking lossy');
|
||||
@unlink($destinationLossless);
|
||||
@rename($destinationLossy, $destination);
|
||||
} else {
|
||||
$this->logLn('Picking lossless');
|
||||
@unlink($destinationLossy);
|
||||
@rename($destinationLossless, $destination);
|
||||
}
|
||||
$this->setDestination($destination);
|
||||
$this->setOption('encoding', 'auto');
|
||||
}
|
||||
|
||||
protected function runActualConvert()
|
||||
{
|
||||
if (!$this->passOnEncodingAuto() && ($this->getOptions()['encoding'] == 'auto') && $this->supportsLossless()) {
|
||||
$this->convertTwoAndSelectSmallest();
|
||||
} else {
|
||||
$this->doActualConvert();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/ExecTrait.php
vendored
Normal file
107
vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/ExecTrait.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
/**
|
||||
* Trait for converters that uses exec() or similar
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait ExecTrait
|
||||
{
|
||||
|
||||
abstract protected function logLn($msg, $style = '');
|
||||
|
||||
|
||||
/**
|
||||
* Helper function for examining if "nice" command is available
|
||||
*
|
||||
* @return boolean true if nice is available
|
||||
*/
|
||||
protected static function hasNiceSupport()
|
||||
{
|
||||
ExecWithFallback::exec("nice 2>&1", $niceOutput);
|
||||
|
||||
if (is_array($niceOutput) && isset($niceOutput[0])) {
|
||||
if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
|
||||
/*
|
||||
* Nice is available - default niceness (+10)
|
||||
* https://www.lifewire.com/uses-of-commands-nice-renice-2201087
|
||||
* https://www.computerhope.com/unix/unice.htm
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false; // to satisfy phpstan
|
||||
}
|
||||
|
||||
protected function checkNiceSupport()
|
||||
{
|
||||
$ok = self::hasNiceSupport();
|
||||
if ($ok) {
|
||||
$this->logLn('Tested "nice" command - it works :)');
|
||||
} else {
|
||||
$this->logLn(
|
||||
'**No "nice" support. To save a few ms, you can disable the "use-nice" option.**'
|
||||
);
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
protected static function niceOption()
|
||||
{
|
||||
return ['use-nice', 'boolean', [
|
||||
'title' => 'Use nice',
|
||||
'description' =>
|
||||
'If *use-nice* is set, it will be examined if the *nice* command is available. ' .
|
||||
'If it is, the binary is executed using *nice*. This assigns low priority to the process and ' .
|
||||
'will save system resources - but result in slower conversion.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs output from the exec call.
|
||||
*
|
||||
* @param array $output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function logExecOutput($output)
|
||||
{
|
||||
if (is_array($output) && count($output) > 0) {
|
||||
$this->logLn('');
|
||||
$this->logLn('Output:', 'italic');
|
||||
foreach ($output as $line) {
|
||||
$this->logLn(print_r($line, true));
|
||||
}
|
||||
$this->logLn('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check basic operationality of exec converters (that the "exec" or similar function is available)
|
||||
*
|
||||
* @throws WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationalityExecTrait()
|
||||
{
|
||||
if (!ExecWithFallback::anyAvailable()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'exec() is not enabled (nor is alternative methods, such as proc_open())'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
980
vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php
vendored
Normal file
980
vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php
vendored
Normal file
@@ -0,0 +1,980 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
use LocateBinaries\LocateBinaries;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling cwebp binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Cwebp extends AbstractConverter
|
||||
{
|
||||
|
||||
use EncodingAutoTrait;
|
||||
use ExecTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
$binariesForOS = [];
|
||||
if (isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
||||
foreach (self::$suppliedBinariesInfo[PHP_OS] as $i => list($file, $hash, $version)) {
|
||||
$binariesForOS[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption(),
|
||||
['try-cwebp', 'boolean', [
|
||||
'title' => 'Try plain cwebp command',
|
||||
'description' =>
|
||||
'If set, the converter will try executing "cwebp -version". In case it succeeds, ' .
|
||||
'and the version is higher than those working cwebps found using other methods, ' .
|
||||
'the conversion will be done by executing this cwebp.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-discovering-cwebp', 'boolean', [
|
||||
'title' => 'Try discovering cwebp binary',
|
||||
'description' =>
|
||||
'If set, the converter will try to discover installed cwebp binaries using a "which -a cwebp" ' .
|
||||
'command, or in case that fails, a "whereis -b cwebp" command. These commands will find ' .
|
||||
'cwebp binaries residing in PATH',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-common-system-paths', 'boolean', [
|
||||
'title' => 'Try locating cwebp in common system paths',
|
||||
'description' =>
|
||||
'If set, the converter will look for a cwebp binaries residing in common system locations ' .
|
||||
'such as "/usr/bin/cwebp". If such exist, it is assumed that they are valid cwebp binaries. ' .
|
||||
'A version check will be run on the binaries found (they are executed with the "-version" flag. ' .
|
||||
'The cwebp with the highest version found using this method and the other enabled methods will ' .
|
||||
'be used for the actual conversion.' .
|
||||
'Note: All methods for discovering cwebp binaries are per default enabled. You can save a few ' .
|
||||
'microseconds by disabling some, but it is probably not worth it, as your ' .
|
||||
'setup will then become less resilient to system changes.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-supplied-binary-for-os', 'boolean', [
|
||||
'title' => 'Try precompiled cwebp binaries',
|
||||
'description' =>
|
||||
'If set, the converter will try use a precompiled cwebp binary that comes with webp-convert. ' .
|
||||
'But only if it has a higher version that those found by other methods. As the library knows ' .
|
||||
'the versions of its bundled binaries, no additional time is spent executing them with the ' .
|
||||
'"-version" parameter. The binaries are hash-checked before executed. ' .
|
||||
'The library btw. comes with several versions of precompiled cwebps because they have different ' .
|
||||
'dependencies - some works on some systems and others on others.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['skip-these-precompiled-binaries', 'string', [
|
||||
'title' => 'Skip these precompiled binaries',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => '',
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'advanced' => true,
|
||||
'options' => $binariesForOS,
|
||||
'display' => "option('cwebp-try-supplied-binary-for-os') == true"
|
||||
]
|
||||
|
||||
]],
|
||||
['rel-path-to-precompiled-binaries', 'string', [
|
||||
'title' => 'Rel path to precompiled binaries',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => './Binaries',
|
||||
'ui' => [
|
||||
'component' => '',
|
||||
'advanced' => true,
|
||||
'display' => "option('cwebp-try-supplied-binary-for-os') == true"
|
||||
],
|
||||
'sensitive' => true
|
||||
]],
|
||||
['command-line-options', 'string', [
|
||||
'title' => 'Command line options',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => '',
|
||||
'ui' => [
|
||||
'component' => 'input',
|
||||
'advanced' => true,
|
||||
]
|
||||
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// OS-specific binaries included in this library, along with hashes
|
||||
// If other binaries are going to be added, notice that the first argument is what PHP_OS returns.
|
||||
// (possible values, see here: https://stackoverflow.com/questions/738823/possible-values-for-php-os)
|
||||
// Got the precompiled binaries here: https://developers.google.com/speed/webp/docs/precompiled
|
||||
// Note when changing binaries:
|
||||
// 1: Do NOT use "." in filename. It causes unzipping to fail on some hosts
|
||||
// 2: Set permission to 775. 755 causes unzipping to fail on some hosts
|
||||
private static $suppliedBinariesInfo = [
|
||||
'WINNT' => [
|
||||
['cwebp-120-windows-x64.exe', '2849fd06012a9eb311b02a4f8918ae4b16775693bc21e95f4cc6a382eac299f9', '1.2.0'],
|
||||
|
||||
// Keep the 1.1.0 version a while, in case some may have problems with the 1.2.0 version
|
||||
['cwebp-110-windows-x64.exe', '442682869402f92ad2c8b3186c02b0ea6d6da68d2f908df38bf905b3411eb9fb', '1.1.0'],
|
||||
],
|
||||
'Darwin' => [
|
||||
['cwebp-110-mac-10_15', 'bfce742da09b959f9f2929ba808fed9ade25c8025530434b6a47d217a6d2ceb5', '1.1.0'],
|
||||
],
|
||||
'SunOS' => [
|
||||
// Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions
|
||||
// Can you help me get a 1.0.3 version?
|
||||
['cwebp-060-solaris', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f', '0.6.0']
|
||||
],
|
||||
'FreeBSD' => [
|
||||
// Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions
|
||||
// Can you help me get a 1.0.3 version?
|
||||
['cwebp-060-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573', '0.6.0']
|
||||
],
|
||||
'Linux' => [
|
||||
|
||||
// PS: Some experience the following error with 1.20:
|
||||
// /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found
|
||||
// (see #278)
|
||||
|
||||
['cwebp-120-linux-x86-64', 'f1b7dc03e95535a6b65852de07c0404be4dba078af48369f434ee39b2abf8f4e', '1.2.0'],
|
||||
|
||||
// As some experience the an error with 1.20 (see #278), we keep the 1.10
|
||||
['cwebp-110-linux-x86-64', '1603b07b592876dd9fdaa62b44aead800234c9474ff26dc7dd01bc0f4785c9c6', '1.1.0'],
|
||||
|
||||
// Statically linked executable
|
||||
// It may be that it on some systems works, where the dynamically linked does not (see #196)
|
||||
[
|
||||
'cwebp-103-linux-x86-64-static',
|
||||
'ab96f01b49336da8b976c498528080ff614112d5985da69943b48e0cb1c5228a',
|
||||
'1.0.3'
|
||||
],
|
||||
|
||||
// Old executable for systems in case all of the above fails
|
||||
['cwebp-061-linux-x86-64', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568', '0.6.1'],
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Check all hashes of the precompiled binaries.
|
||||
*
|
||||
* This isn't used when converting, but can be used as a startup check.
|
||||
*/
|
||||
public static function checkAllHashes()
|
||||
{
|
||||
foreach (self::$suppliedBinariesInfo as $os => $arr) {
|
||||
foreach ($arr as $i => list($filename, $expectedHash)) {
|
||||
$actualHash = hash_file("sha256", __DIR__ . '/Binaries/' . $filename);
|
||||
if ($expectedHash != $actualHash) {
|
||||
throw new \Exception(
|
||||
'Hash for ' . $filename . ' is incorrect! ' .
|
||||
'Checksum is: ' . $actualHash . ', ' .
|
||||
', but expected: ' . $expectedHash .
|
||||
'. Did you transfer with FTP, but not in binary mode? '
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
$options = $this->options;
|
||||
if (!$options['try-supplied-binary-for-os'] &&
|
||||
!$options['try-common-system-paths'] &&
|
||||
!$options['try-cwebp'] &&
|
||||
!$options['try-discovering-cwebp']
|
||||
) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to neither try pure cwebp command, ' .
|
||||
'nor look for cweb binaries in common system locations and ' .
|
||||
'nor to use one of the supplied precompiled binaries. ' .
|
||||
'But these are the only ways this converter can convert images. No conversion can be made!'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function executeBinary($binary, $commandOptions, $useNice)
|
||||
{
|
||||
//$version = $this->detectVersion($binary);
|
||||
|
||||
// Redirect stderr to same place as stdout with "2>&1"
|
||||
// https://www.brianstorti.com/understanding-shell-script-idiom-redirect/
|
||||
|
||||
$command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions . ' 2>&1';
|
||||
|
||||
//$logger->logLn('command options:' . $commandOptions);
|
||||
$this->logLn('Trying to convert by executing the following command:');
|
||||
$startExecuteBinaryTime = self::startTimer();
|
||||
;
|
||||
$this->logLn($command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
$this->logExecOutput($output);
|
||||
$this->logTimeSpent($startExecuteBinaryTime, 'Executing cwebp binary took: ');
|
||||
$this->logLn('');
|
||||
/*
|
||||
if ($returnCode == 255) {
|
||||
if (isset($output[0])) {
|
||||
// Could be an error like 'Error! Cannot open output file' or 'Error! ...preset... '
|
||||
$this->logLn(print_r($output[0], true));
|
||||
}
|
||||
}*/
|
||||
//$logger->logLn(self::msgForExitCode($returnCode));
|
||||
return intval($returnCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use "escapeshellarg()" on all arguments in a commandline string of options
|
||||
*
|
||||
* For example, passing '-sharpness 5 -crop 10 10 40 40 -low_memory' will result in:
|
||||
* [
|
||||
* "-sharpness '5'"
|
||||
* "-crop '10' '10' '40' '40'"
|
||||
* "-low_memory"
|
||||
* ]
|
||||
* @param string $commandLineOptions string which can contain multiple commandline options
|
||||
* @return array Array of command options
|
||||
*/
|
||||
private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
|
||||
{
|
||||
if (!ctype_print($commandLineOptions)) {
|
||||
throw new ConversionFailedException(
|
||||
'Non-printable characters are not allowed in the extra command line options'
|
||||
);
|
||||
}
|
||||
|
||||
if (preg_match('#[^a-zA-Z0-9_\s\-]#', $commandLineOptions)) {
|
||||
throw new ConversionFailedException('The extra command line options contains inacceptable characters');
|
||||
}
|
||||
|
||||
$cmdOptions = [];
|
||||
$arr = explode(' -', ' ' . $commandLineOptions);
|
||||
foreach ($arr as $cmdOption) {
|
||||
$pos = strpos($cmdOption, ' ');
|
||||
$cName = '';
|
||||
if (!$pos) {
|
||||
$cName = $cmdOption;
|
||||
if ($cName == '') {
|
||||
continue;
|
||||
}
|
||||
$cmdOptions[] = '-' . $cName;
|
||||
} else {
|
||||
$cName = substr($cmdOption, 0, $pos);
|
||||
$cValues = substr($cmdOption, $pos + 1);
|
||||
$cValuesArr = explode(' ', $cValues);
|
||||
foreach ($cValuesArr as &$cArg) {
|
||||
$cArg = escapeshellarg($cArg);
|
||||
}
|
||||
$cValues = implode(' ', $cValuesArr);
|
||||
$cmdOptions[] = '-' . $cName . ' ' . $cValues;
|
||||
}
|
||||
}
|
||||
return $cmdOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options for a given version of cwebp.
|
||||
*
|
||||
* The "-near_lossless" param is not supported on older versions of cwebp, so skip on those.
|
||||
*
|
||||
* @param string $version Version of cwebp (ie "1.0.3")
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions($version)
|
||||
{
|
||||
|
||||
$this->logLn('Creating command line options for version: ' . $version);
|
||||
|
||||
// we only need two decimal places for version.
|
||||
// convert to number to make it easier to compare
|
||||
$version = preg_match('#^\d+\.\d+#', $version, $matches);
|
||||
$versionNum = 0;
|
||||
if (isset($matches[0])) {
|
||||
$versionNum = floatval($matches[0]);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Could not extract version number from the following version string: ' . $version,
|
||||
'bold'
|
||||
);
|
||||
}
|
||||
|
||||
//$this->logLn('version:' . strval($versionNum));
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$cmdOptions = [];
|
||||
|
||||
// Metadata (all, exif, icc, xmp or none (default))
|
||||
// Comma-separated list of existing metadata to copy from input to output
|
||||
if ($versionNum >= 0.3) {
|
||||
$cmdOptions[] = '-metadata ' . $options['metadata'];
|
||||
} else {
|
||||
$this->logLn('Ignoring metadata option (requires cwebp 0.3)', 'italic');
|
||||
}
|
||||
|
||||
// preset. Appears first in the list as recommended in the docs
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$cmdOptions[] = '-preset ' . escapeshellarg($options['preset']);
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
$addedSizeOption = false;
|
||||
if (!is_null($options['size-in-percentage'])) {
|
||||
$sizeSource = filesize($this->source);
|
||||
if ($sizeSource !== false) {
|
||||
$targetSize = floor($sizeSource * $options['size-in-percentage'] / 100);
|
||||
$cmdOptions[] = '-size ' . $targetSize;
|
||||
$addedSizeOption = true;
|
||||
}
|
||||
}
|
||||
|
||||
// quality
|
||||
if (!$addedSizeOption) {
|
||||
$cmdOptions[] = '-q ' . $this->getCalculatedQuality();
|
||||
}
|
||||
|
||||
// alpha-quality
|
||||
if ($this->options['alpha-quality'] !== 100) {
|
||||
$cmdOptions[] = '-alpha_q ' . escapeshellarg($this->options['alpha-quality']);
|
||||
}
|
||||
|
||||
// Losless PNG conversion
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// No need to add -lossless when near-lossless is used (on version >= 0.5)
|
||||
if (($options['near-lossless'] === 100) || ($versionNum < 0.5)) {
|
||||
$cmdOptions[] = '-lossless';
|
||||
}
|
||||
}
|
||||
|
||||
// Near-lossles
|
||||
if ($options['near-lossless'] !== 100) {
|
||||
if ($versionNum < 0.5) {
|
||||
$this->logLn('Ignoring near-lossless option (requires cwebp 0.5)', 'italic');
|
||||
} else {
|
||||
// The "-near_lossless" flag triggers lossless encoding. We don't want that to happen,
|
||||
// we want the "encoding" option to be respected, and we need it to be in order for
|
||||
// encoding=auto to work.
|
||||
// So: Only set when "encoding" is set to "lossless"
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
$cmdOptions[] = '-near_lossless ' . $options['near-lossless'];
|
||||
} else {
|
||||
$this->logLn(
|
||||
'The near-lossless option ignored for lossy'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Autofilter
|
||||
if ($options['auto-filter'] === true) {
|
||||
$cmdOptions[] = '-af';
|
||||
}
|
||||
|
||||
// SharpYUV
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if ($versionNum >= 0.6) { // #284
|
||||
$cmdOptions[] = '-sharp_yuv';
|
||||
} else {
|
||||
$this->logLn('Ignoring sharp-yuv option (requires cwebp 0.6)', 'italic');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built-in method option
|
||||
$cmdOptions[] = '-m ' . strval($options['method']);
|
||||
|
||||
// Built-in low memory option
|
||||
if ($options['low-memory']) {
|
||||
$cmdOptions[] = '-low_memory';
|
||||
}
|
||||
|
||||
// command-line-options
|
||||
if ($options['command-line-options']) {
|
||||
/*
|
||||
In some years, we can use the splat instead (requires PHP 5.6)
|
||||
array_push(
|
||||
$cmdOptions,
|
||||
...self::escapeShellArgOnCommandLineOptions($options['command-line-options'])
|
||||
);
|
||||
*/
|
||||
foreach (self::escapeShellArgOnCommandLineOptions($options['command-line-options']) as $cmdLineOption) {
|
||||
array_push($cmdOptions, $cmdLineOption);
|
||||
}
|
||||
}
|
||||
|
||||
// Source file
|
||||
$cmdOptions[] = escapeshellarg($this->source);
|
||||
|
||||
// Output
|
||||
$cmdOptions[] = '-o ' . escapeshellarg($this->destination);
|
||||
|
||||
$commandOptions = implode(' ', $cmdOptions);
|
||||
//$this->logLn('command line options:' . $commandOptions);
|
||||
|
||||
return $commandOptions;
|
||||
}
|
||||
|
||||
private function checkHashForSuppliedBinary($binaryFile, $hash)
|
||||
{
|
||||
// File exists, now generate its hash
|
||||
// hash_file() is normally available, but it is not always
|
||||
// - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash
|
||||
// If available, validate that hash is correct.
|
||||
|
||||
if (function_exists('hash_file')) {
|
||||
$this->logLn(
|
||||
'Checking checksum for supplied binary: ' . $binaryFile
|
||||
);
|
||||
$startHashCheckTime = self::startTimer();
|
||||
|
||||
$binaryHash = hash_file('sha256', $binaryFile);
|
||||
|
||||
if ($binaryHash != $hash) {
|
||||
$this->logLn(
|
||||
'Binary checksum of supplied binary is invalid! ' .
|
||||
'Did you transfer with FTP, but not in binary mode? ' .
|
||||
'File:' . $binaryFile . '. ' .
|
||||
'Expected checksum: ' . $hash . '. ' .
|
||||
'Actual checksum:' . $binaryHash . '.',
|
||||
'bold'
|
||||
);
|
||||
return false;
|
||||
;
|
||||
}
|
||||
|
||||
$this->logTimeSpent($startHashCheckTime, 'Checksum test took: ');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supplied binary info for current OS.
|
||||
* paths are made absolute and checked. Missing are removed
|
||||
*
|
||||
* @return array Two arrays.
|
||||
* First array: array of files (absolute paths)
|
||||
* Second array: array of info objects (absolute path, hash and version)
|
||||
*/
|
||||
private function getSuppliedBinaryInfoForCurrentOS()
|
||||
{
|
||||
$this->log('Checking if we have a supplied precompiled binary for your OS (' . PHP_OS . ')... ');
|
||||
|
||||
// Try supplied binary (if available for OS, and hash is correct)
|
||||
$options = $this->options;
|
||||
if (!isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
||||
$this->logLn('No we dont - not for that OS');
|
||||
return [];
|
||||
}
|
||||
|
||||
$filesFound = [];
|
||||
$info = [];
|
||||
$files = self::$suppliedBinariesInfo[PHP_OS];
|
||||
if (count($files) == 1) {
|
||||
$this->logLn('We do.');
|
||||
} else {
|
||||
$this->logLn('We do. We in fact have ' . count($files));
|
||||
}
|
||||
|
||||
$skipThese = explode(',', $this->options['skip-these-precompiled-binaries']);
|
||||
|
||||
//$this->logLn('However, skipping' . print_r($skipThese, true));
|
||||
|
||||
foreach ($files as $i => list($file, $hash, $version)) {
|
||||
//$file = $info[0];
|
||||
//$hash = $info[1];
|
||||
|
||||
$binaryFile = __DIR__ . '/' . $options['rel-path-to-precompiled-binaries'] . '/' . $file;
|
||||
|
||||
// Replace "/./" with "/" in path (we could alternatively use realpath)
|
||||
//$binaryFile = preg_replace('#\/\.\/#', '/', $binaryFile);
|
||||
// The file should exist, but may have been removed manually.
|
||||
/*
|
||||
if (!file_exists($binaryFile)) {
|
||||
$this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
|
||||
return false;
|
||||
}*/
|
||||
if (in_array($file, $skipThese)) {
|
||||
$this->logLn('Skipped: ' . $file . ' (was told to in the "skip-these-precompiled-binaries" option)');
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$realPathResult = realpath($binaryFile);
|
||||
if ($realPathResult === false) {
|
||||
$this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
|
||||
continue;
|
||||
}
|
||||
$binaryFile = $realPathResult;
|
||||
$filesFound[] = $realPathResult;
|
||||
$info[] = [$realPathResult, $hash, $version, $file];
|
||||
}
|
||||
return [$filesFound, $info];
|
||||
}
|
||||
|
||||
private function who()
|
||||
{
|
||||
ExecWithFallback::exec('whoami 2>&1', $whoOutput, $whoReturnCode);
|
||||
if (($whoReturnCode == 0) && (isset($whoOutput[0]))) {
|
||||
return 'user: "' . $whoOutput[0] . '"';
|
||||
} else {
|
||||
return 'the user that the command was run with';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the version of a cwebp binary.
|
||||
*
|
||||
* @param string $binary The binary to detect version for (path to cwebp or simply "cwebp")
|
||||
*
|
||||
* @return string|int Version string (ie "1.0.2") OR return code, in case of failure
|
||||
*/
|
||||
private function detectVersion($binary)
|
||||
{
|
||||
$command = $binary . ' -version 2>&1';
|
||||
$this->log('- Executing: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
if ($returnCode == 0) {
|
||||
if (isset($output[0])) {
|
||||
$this->logLn('. Result: version: *' . $output[0] . '*');
|
||||
return $output[0];
|
||||
}
|
||||
} else {
|
||||
$this->log('. Result: ');
|
||||
if ($returnCode == 127) {
|
||||
$this->logLn(
|
||||
'*Exec failed* (the cwebp binary was not found at path: ' . $binary .
|
||||
', or it had missing library dependencies)'
|
||||
);
|
||||
} else {
|
||||
if ($returnCode == 126) {
|
||||
$this->logLn(
|
||||
'*Exec failed*. ' .
|
||||
'Permission denied (' . $this->who() . ' does not have permission to execute that binary)'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'*Exec failed* (return code: ' . $returnCode . ')'
|
||||
);
|
||||
$this->logExecOutput($output);
|
||||
}
|
||||
}
|
||||
return $returnCode;
|
||||
}
|
||||
return ''; // Will not happen. Just so phpstan doesn't complain
|
||||
}
|
||||
|
||||
/**
|
||||
* Check versions for an array of binaries.
|
||||
*
|
||||
* @param array $binaries array of binaries to detect the version of
|
||||
*
|
||||
* @return array the "detected" key holds working binaries and their version numbers, the
|
||||
* the "failed" key holds failed binaries and their error codes.
|
||||
*/
|
||||
private function detectVersions($binaries)
|
||||
{
|
||||
$binariesWithVersions = [];
|
||||
$binariesWithFailCodes = [];
|
||||
|
||||
foreach ($binaries as $binary) {
|
||||
$versionStringOrFailCode = $this->detectVersion($binary);
|
||||
// $this->logLn($binary . ': ' . $versionString);
|
||||
if (gettype($versionStringOrFailCode) == 'string') {
|
||||
$binariesWithVersions[$binary] = $versionStringOrFailCode;
|
||||
} else {
|
||||
$binariesWithFailCodes[$binary] = $versionStringOrFailCode;
|
||||
}
|
||||
}
|
||||
return ['detected' => $binariesWithVersions, 'failed' => $binariesWithFailCodes];
|
||||
}
|
||||
|
||||
private function logBinariesFound($binaries, $startTime)
|
||||
{
|
||||
if (count($binaries) == 0) {
|
||||
$this->logLn('Found 0 binaries' . self::getTimeStr($startTime));
|
||||
} else {
|
||||
$this->logLn('Found ' . count($binaries) . ' binaries' . self::getTimeStr($startTime));
|
||||
foreach ($binaries as $binary) {
|
||||
$this->logLn('- ' . $binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function logDiscoverAction($optionName, $description)
|
||||
{
|
||||
if ($this->options[$optionName]) {
|
||||
$this->logLn(
|
||||
'Discovering binaries ' . $description . ' ' .
|
||||
'(to skip this step, disable the "' . $optionName . '" option)'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Skipped discovering binaries ' . $description . ' ' .
|
||||
'(enable "' . $optionName . '" if you do not want to skip that step)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function startTimer()
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
return microtime(true);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function readTimer($startTime)
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$endTime = microtime(true);
|
||||
$seconds = ($endTime - $startTime);
|
||||
return round(($seconds * 1000));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getTimeStr($startTime, $pre = ' (spent ', $post = ')')
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$ms = self::readTimer($startTime);
|
||||
return $pre . $ms . ' ms' . $post;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function logTimeSpent($startTime, $pre = 'Spent: ')
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$ms = self::readTimer($startTime);
|
||||
$this->logLn($pre . $ms . ' ms');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Two arrays (in an array).
|
||||
* First array: binaries found,
|
||||
* Second array: supplied binaries info for current OS
|
||||
*/
|
||||
private function discoverCwebpBinaries()
|
||||
{
|
||||
$this->logLn(
|
||||
'Looking for cwebp binaries.'
|
||||
);
|
||||
|
||||
$startDiscoveryTime = self::startTimer();
|
||||
|
||||
$binaries = [];
|
||||
|
||||
if (defined('WEBPCONVERT_CWEBP_PATH')) {
|
||||
$this->logLn('WEBPCONVERT_CWEBP_PATH was defined, so using that path and ignoring any other');
|
||||
return [[constant('WEBPCONVERT_CWEBP_PATH')],[[], []]];
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_CWEBP_PATH'))) {
|
||||
$this->logLn(
|
||||
'WEBPCONVERT_CWEBP_PATH environment variable was set, so using that path and ignoring any other'
|
||||
);
|
||||
return [[getenv('WEBPCONVERT_CWEBP_PATH')],[[], []]];
|
||||
}
|
||||
|
||||
if ($this->options['try-cwebp']) {
|
||||
$startTime = self::startTimer();
|
||||
$this->logLn(
|
||||
'Discovering if a plain cwebp call works (to skip this step, disable the "try-cwebp" option)'
|
||||
);
|
||||
$result = $this->detectVersion('cwebp');
|
||||
if (gettype($result) == 'string') {
|
||||
$this->logLn('We could get the version, so yes, a plain cwebp call works ' .
|
||||
'(spent ' . self::readTimer($startTime) . ' ms)');
|
||||
$binaries[] = 'cwebp';
|
||||
} else {
|
||||
$this->logLn('Nope a plain cwebp call does not work' . self::getTimeStr($startTime));
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Skipped discovering if a plain cwebp call works' .
|
||||
' (enable the "try-cwebp" option if you do not want to skip that step)'
|
||||
);
|
||||
}
|
||||
|
||||
// try-discovering-cwebp
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-discovering-cwebp', 'using "which -a cwebp" command.');
|
||||
if ($this->options['try-discovering-cwebp']) {
|
||||
$moreBinaries = LocateBinaries::locateInstalledBinaries('cwebp');
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
// 'try-common-system-paths'
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-common-system-paths', 'by peeking in common system paths');
|
||||
if ($this->options['try-common-system-paths']) {
|
||||
$moreBinaries = LocateBinaries::locateInCommonSystemPaths('cwebp');
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
// try-supplied-binary-for-os
|
||||
$suppliedBinariesInfo = [[], []];
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-supplied-binary-for-os', 'which are distributed with the webp-convert library');
|
||||
if ($this->options['try-supplied-binary-for-os']) {
|
||||
$suppliedBinariesInfo = $this->getSuppliedBinaryInfoForCurrentOS();
|
||||
$moreBinaries = $suppliedBinariesInfo[0];
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
//$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
$this->logTimeSpent($startDiscoveryTime, 'Discovering cwebp binaries took: ');
|
||||
$this->logLn('');
|
||||
|
||||
return [array_values(array_unique($binaries)), $suppliedBinariesInfo];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try executing a cwebp binary (or command, like: "cwebp")
|
||||
*
|
||||
* @param string $binary
|
||||
* @param string $version Version of cwebp (ie "1.0.3")
|
||||
* @param boolean $useNice Whether to use "nice" command or not
|
||||
*
|
||||
* @return boolean success or not.
|
||||
*/
|
||||
private function tryCwebpBinary($binary, $version, $useNice)
|
||||
{
|
||||
|
||||
//$this->logLn('Trying binary: ' . $binary);
|
||||
$commandOptions = $this->createCommandLineOptions($version);
|
||||
|
||||
$returnCode = $this->executeBinary($binary, $commandOptions, $useNice);
|
||||
if ($returnCode == 0) {
|
||||
// It has happened that even with return code 0, there was no file at destination.
|
||||
if (!file_exists($this->destination)) {
|
||||
$this->logLn('executing cweb returned success code - but no file was found at destination!');
|
||||
return false;
|
||||
} else {
|
||||
$this->logLn('Success');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Exec failed (return code: ' . $returnCode . ')'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for composing an error message when no converters are working.
|
||||
*
|
||||
* @param array $versions The array which we get from calling ::detectVersions($binaries)
|
||||
* @return string An informative and to the point error message.
|
||||
*/
|
||||
private function composeMeaningfullErrorMessageNoVersionsWorking($versions)
|
||||
{
|
||||
// TODO: Take "supplied" into account
|
||||
|
||||
// PS: array_values() is used to reindex
|
||||
$uniqueFailCodes = array_values(array_unique(array_values($versions['failed'])));
|
||||
$justOne = (count($versions['failed']) == 1);
|
||||
|
||||
if (count($uniqueFailCodes) == 1) {
|
||||
if ($uniqueFailCodes[0] == 127) {
|
||||
return 'No cwebp binaries located. Check the conversion log for details.';
|
||||
}
|
||||
}
|
||||
// If there are more failures than 127, the 127 failures are unintesting.
|
||||
// It is to be expected that some of the common system paths does not contain a cwebp.
|
||||
$uniqueFailCodesBesides127 = array_values(array_diff($uniqueFailCodes, [127]));
|
||||
|
||||
if (count($uniqueFailCodesBesides127) == 1) {
|
||||
if ($uniqueFailCodesBesides127[0] == 126) {
|
||||
return 'No cwebp binaries could be executed (permission denied for ' . $this->who() . ').';
|
||||
}
|
||||
}
|
||||
|
||||
$errorMsg = '';
|
||||
if ($justOne) {
|
||||
$errorMsg .= 'The cwebp file found cannot be can be executed ';
|
||||
} else {
|
||||
$errorMsg .= 'None of the cwebp files can be executed ';
|
||||
}
|
||||
if (count($uniqueFailCodesBesides127) == 1) {
|
||||
$errorMsg .= '(failure code: ' . $uniqueFailCodesBesides127[0] . ')';
|
||||
} else {
|
||||
$errorMsg .= '(failure codes: ' . implode(', ', $uniqueFailCodesBesides127) . ')';
|
||||
}
|
||||
return $errorMsg;
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
list($foundBinaries, $suppliedBinariesInfo) = $this->discoverCwebpBinaries();
|
||||
$suppliedBinaries = $suppliedBinariesInfo[0];
|
||||
$allBinaries = array_merge($foundBinaries, $suppliedBinaries);
|
||||
|
||||
//$binaries = $this->discoverCwebpBinaries();
|
||||
if (count($allBinaries) == 0) {
|
||||
$this->logLn('No cwebp binaries found!');
|
||||
|
||||
$discoverOptions = [
|
||||
'try-supplied-binary-for-os',
|
||||
'try-common-system-paths',
|
||||
'try-cwebp',
|
||||
'try-discovering-cwebp'
|
||||
];
|
||||
$disabledDiscoverOptions = [];
|
||||
foreach ($discoverOptions as $discoverOption) {
|
||||
if (!$this->options[$discoverOption]) {
|
||||
$disabledDiscoverOptions[] = $discoverOption;
|
||||
}
|
||||
}
|
||||
if (count($disabledDiscoverOptions) == 0) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'No cwebp binaries found.'
|
||||
);
|
||||
} else {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'No cwebp binaries found. Try enabling the "' .
|
||||
implode('" option or the "', $disabledDiscoverOptions) . '" option.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$detectedVersions = [];
|
||||
if (count($foundBinaries) > 0) {
|
||||
$this->logLn(
|
||||
'Detecting versions of the cwebp binaries found' .
|
||||
(count($suppliedBinaries) > 0 ? ' (except supplied binaries)' : '.')
|
||||
);
|
||||
$startDetectionTime = self::startTimer();
|
||||
$versions = $this->detectVersions($foundBinaries);
|
||||
$detectedVersions = $versions['detected'];
|
||||
|
||||
$this->logTimeSpent($startDetectionTime, 'Detecting versions took: ');
|
||||
}
|
||||
|
||||
//$suppliedVersions = [];
|
||||
$suppliedBinariesHash = [];
|
||||
$suppliedBinariesFilename = [];
|
||||
|
||||
$binaryVersions = $detectedVersions;
|
||||
foreach ($suppliedBinariesInfo[1] as list($path, $hash, $version, $filename)) {
|
||||
$binaryVersions[$path] = $version;
|
||||
$suppliedBinariesHash[$path] = $hash;
|
||||
$suppliedBinariesFilename[$path] = $filename;
|
||||
}
|
||||
|
||||
//$binaryVersions = array_merge($detectedVersions, $suppliedBinariesInfo);
|
||||
|
||||
// TODO: reimplement
|
||||
/*
|
||||
$versions['supplied'] = $suppliedBinariesInfo;
|
||||
|
||||
$binaryVersions = $versions['detected'];
|
||||
if ((count($binaryVersions) == 0) && (count($suppliedBinaries) == 0)) {
|
||||
// No working cwebp binaries found, no supplied binaries found
|
||||
|
||||
throw new SystemRequirementsNotMetException(
|
||||
$this->composeMeaningfullErrorMessageNoVersionsWorking($versions)
|
||||
);
|
||||
}*/
|
||||
|
||||
// Sort binaries so those with highest numbers comes first
|
||||
arsort($binaryVersions);
|
||||
$this->logLn(
|
||||
'Binaries ordered by version number.'
|
||||
);
|
||||
foreach ($binaryVersions as $binary => $version) {
|
||||
$this->logLn('- ' . $binary . ': (version: ' . $version . ')');
|
||||
}
|
||||
|
||||
// Execute!
|
||||
$this->logLn(
|
||||
'Starting conversion, using the first of these. If that should fail, ' .
|
||||
'the next will be tried and so on.'
|
||||
);
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
|
||||
$success = false;
|
||||
foreach ($binaryVersions as $binary => $version) {
|
||||
if (isset($suppliedBinariesHash[$binary])) {
|
||||
if (!$this->checkHashForSuppliedBinary($binary, $suppliedBinariesHash[$binary])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($this->tryCwebpBinary($binary, $version, $useNice)) {
|
||||
$success = true;
|
||||
break;
|
||||
} else {
|
||||
if (isset($suppliedBinariesFilename[$binary])) {
|
||||
$this->logLn(
|
||||
'Note: You can prevent trying this precompiled binary, by setting the ' .
|
||||
'"skip-these-precompiled-binaries" option to "' . $suppliedBinariesFilename[$binary] . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cwebp sets file permissions to 664 but instead ..
|
||||
// .. $this->source file permissions should be used
|
||||
|
||||
if ($success) {
|
||||
$fileStatistics = stat($this->source);
|
||||
if ($fileStatistics !== false) {
|
||||
// Apply same permissions as source file, but strip off the executable bits
|
||||
$permissions = $fileStatistics['mode'] & 0000666;
|
||||
chmod($this->destination, $permissions);
|
||||
}
|
||||
} else {
|
||||
throw new SystemRequirementsNotMetException('Failed converting. Check the conversion log for details.');
|
||||
}
|
||||
}
|
||||
}
|
||||
397
vendor/rosell-dk/webp-convert/src/Convert/Converters/Ewww.php
vendored
Normal file
397
vendor/rosell-dk/webp-convert/src/Convert/Converters/Ewww.php
vendored
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp using ewww cloud service.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Ewww extends AbstractConverter
|
||||
{
|
||||
use CloudConverterTrait;
|
||||
use CurlTrait;
|
||||
|
||||
/** @var array|null Array of invalid or exceeded api keys discovered during conversions (during the request) */
|
||||
public static $nonFunctionalApiKeysDiscoveredDuringConversion;
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['api-key', 'string', [
|
||||
'title' => 'Ewww API key',
|
||||
'description' => 'ewww API key. ' .
|
||||
'If you choose "auto", webp-convert will ' .
|
||||
'convert to both lossy and lossless and pick the smallest result',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
]
|
||||
]],
|
||||
['check-key-status-before-converting', 'boolean', [
|
||||
'title' => 'Check key status before converting',
|
||||
'description' =>
|
||||
'If enabled, the api key will be validated (relative inexpensive) before trying ' .
|
||||
'to convert. For automatic conversions, you should enable it. Otherwise you run the ' .
|
||||
'risk that the same files will be uploaded to ewww cloud service over and over again, ' .
|
||||
'in case the key has expired. For manually triggered conversions, you can safely disable ' .
|
||||
'the option.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
]
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get api key from options or environment variable
|
||||
*
|
||||
* @return string|false api key or false if none is set
|
||||
*/
|
||||
private function getKey()
|
||||
{
|
||||
if (!empty($this->options['api-key'])) {
|
||||
return $this->options['api-key'];
|
||||
}
|
||||
if (defined('WEBPCONVERT_EWWW_API_KEY')) {
|
||||
return constant('WEBPCONVERT_EWWW_API_KEY');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_EWWW_API_KEY'))) {
|
||||
return getenv('WEBPCONVERT_EWWW_API_KEY');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check operationality of Ewww converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met (curl)
|
||||
* @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
|
||||
$apiKey = $this->getKey();
|
||||
|
||||
if ($apiKey === false) {
|
||||
if (isset($this->options['key'])) {
|
||||
throw new InvalidApiKeyException(
|
||||
'The "key" option has been renamed to "api-key" in webp-convert 2.0. ' .
|
||||
'You must change the configuration accordingly.'
|
||||
);
|
||||
}
|
||||
|
||||
throw new InvalidApiKeyException('Missing API key.');
|
||||
}
|
||||
|
||||
if (strlen($apiKey) < 20) {
|
||||
throw new InvalidApiKeyException(
|
||||
'Api key is invalid. Api keys are supposed to be 32 characters long - ' .
|
||||
'the provided api key is much shorter'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for curl requirements
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
|
||||
if ($this->options['check-key-status-before-converting']) {
|
||||
$keyStatus = self::getKeyStatus($apiKey);
|
||||
switch ($keyStatus) {
|
||||
case 'great':
|
||||
break;
|
||||
case 'exceeded':
|
||||
throw new ConverterNotOperationalException('Quota has exceeded');
|
||||
//break;
|
||||
case 'invalid':
|
||||
throw new InvalidApiKeyException('Api key is invalid');
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public function checkConvertability()
|
||||
{
|
||||
// check upload limits
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
}
|
||||
*/
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$ch = self::initCurl();
|
||||
|
||||
//$this->logLn('api key:' . $this->getKey());
|
||||
|
||||
$postData = [
|
||||
'api_key' => $this->getKey(),
|
||||
'webp' => '1',
|
||||
'file' => curl_file_create($this->source),
|
||||
'quality' => $this->getCalculatedQuality(),
|
||||
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
||||
];
|
||||
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'User-Agent: WebPConvert',
|
||||
'Accept: image/*'
|
||||
],
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]
|
||||
);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
throw new ConversionFailedException(curl_error($ch));
|
||||
}
|
||||
|
||||
// The API does not always return images.
|
||||
// For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
|
||||
// Messages has a http content type of ie 'text/html; charset=UTF-8
|
||||
// Images has application/octet-stream.
|
||||
// So verify that we got an image back.
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
if (($contentType != 'application/octet-stream') && ($contentType != 'image/webp')) {
|
||||
//echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
curl_close($ch);
|
||||
|
||||
/*
|
||||
For bogus or expired key it returns: {"error":"invalid","t":"exceeded"}
|
||||
For exceeded key it returns: {"error":"exceeded"}
|
||||
*/
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
$this->logLn('We received the following error response: ' . $responseObj->error);
|
||||
$this->logLn('Complete response: ' . json_encode($responseObj));
|
||||
|
||||
// Store the invalid key in array so it can be received once the Stack is completed
|
||||
// (even when stack succeeds)
|
||||
if (!isset(self::$nonFunctionalApiKeysDiscoveredDuringConversion)) {
|
||||
self::$nonFunctionalApiKeysDiscoveredDuringConversion = [];
|
||||
}
|
||||
if (!in_array($options['api-key'], self::$nonFunctionalApiKeysDiscoveredDuringConversion)) {
|
||||
self::$nonFunctionalApiKeysDiscoveredDuringConversion[] = $options['api-key'];
|
||||
}
|
||||
if ($responseObj->error == "invalid") {
|
||||
throw new InvalidApiKeyException('The api key is invalid (or expired)');
|
||||
} else {
|
||||
throw new InvalidApiKeyException('The quota is exceeded for the api-key');
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionFailedException(
|
||||
'ewww api did not return an image. It could be that the key is invalid. Response: '
|
||||
. $response
|
||||
. ". Content type: "
|
||||
. curl_getinfo($ch, CURLINFO_CONTENT_TYPE)
|
||||
);
|
||||
}
|
||||
|
||||
// Not sure this can happen. So just in case
|
||||
if ($response == '') {
|
||||
throw new ConversionFailedException('ewww api did not return anything');
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $response);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Error saving file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep subscription alive by optimizing a jpeg
|
||||
* (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
|
||||
*/
|
||||
public static function keepSubscriptionAlive($source, $key)
|
||||
{
|
||||
try {
|
||||
$ch = curl_init();
|
||||
} catch (\Exception $e) {
|
||||
return 'curl is not installed';
|
||||
}
|
||||
if ($ch === false) {
|
||||
return 'curl could not be initialized';
|
||||
}
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'User-Agent: WebPConvert',
|
||||
'Accept: image/*'
|
||||
],
|
||||
CURLOPT_POSTFIELDS => [
|
||||
'api_key' => $key,
|
||||
'webp' => '0',
|
||||
'file' => curl_file_create($source),
|
||||
'domain' => $_SERVER['HTTP_HOST'],
|
||||
'quality' => 60,
|
||||
'metadata' => 0
|
||||
],
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]
|
||||
);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
return 'curl error' . curl_error($ch);
|
||||
}
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
if (($contentType != 'application/octet-stream') && ($contentType != 'image/webp')) {
|
||||
curl_close($ch);
|
||||
|
||||
/* May return this: {"error":"invalid","t":"exceeded"} */
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
return 'The key is invalid';
|
||||
}
|
||||
|
||||
return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
|
||||
}
|
||||
|
||||
// Not sure this can happen. So just in case
|
||||
if ($response == '') {
|
||||
return 'ewww api did not return anything';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
public static function blacklistKey($key)
|
||||
{
|
||||
}
|
||||
|
||||
public static function isKeyBlacklisted($key)
|
||||
{
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Return "great", "exceeded" or "invalid"
|
||||
*/
|
||||
public static function getKeyStatus($key)
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||||
'api_key' => $key
|
||||
]);
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
|
||||
|
||||
$response = curl_exec($ch);
|
||||
// echo $response;
|
||||
if (curl_errno($ch)) {
|
||||
throw new \Exception(curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
// Possible responses:
|
||||
// “great” = verification successful
|
||||
// “exceeded” = indicates a valid key with no remaining image credits.
|
||||
// an empty response indicates that the key is not valid
|
||||
|
||||
if ($response == '') {
|
||||
return 'invalid';
|
||||
}
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
if (($responseObj->error == 'invalid') || ($responseObj->error == 'bye invalid')) {
|
||||
return 'invalid';
|
||||
} else {
|
||||
if ($responseObj->error == 'bye invalid') {
|
||||
return 'invalid';
|
||||
} else {
|
||||
throw new \Exception('Ewww returned unexpected error: ' . $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($responseObj->status)) {
|
||||
throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
|
||||
}
|
||||
switch ($responseObj->status) {
|
||||
case 'great':
|
||||
case 'exceeded':
|
||||
return $responseObj->status;
|
||||
}
|
||||
throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
|
||||
}
|
||||
|
||||
public static function isWorkingKey($key)
|
||||
{
|
||||
return (self::getKeyStatus($key) == 'great');
|
||||
}
|
||||
|
||||
public static function isValidKey($key)
|
||||
{
|
||||
return (self::getKeyStatus($key) != 'invalid');
|
||||
}
|
||||
|
||||
public static function getQuota($key)
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||||
'api_key' => $key
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
|
||||
|
||||
$response = curl_exec($ch);
|
||||
return $response; // ie -830 23. Seems to return empty for invalid keys
|
||||
// or empty
|
||||
//echo $response;
|
||||
}
|
||||
}
|
||||
178
vendor/rosell-dk/webp-convert/src/Convert/Converters/FFMpeg.php
vendored
Normal file
178
vendor/rosell-dk/webp-convert/src/Convert/Converters/FFMpeg.php
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling imagemagick binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class FFMpeg extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'near-lossless',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption()
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_FFMPEG_PATH')) {
|
||||
return constant('WEBPCONVERT_FFMPEG_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_FFMPEG_PATH'))) {
|
||||
return getenv('WEBPCONVERT_FFMPEG_PATH');
|
||||
}
|
||||
return 'ffmpeg';
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('# --enable-libwebp#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'ffmpeg is not installed (cannot execute: "' . $this->getPath() . '")'
|
||||
);
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('ffmpeg was compiled without libwebp');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions()
|
||||
{
|
||||
// PS: Available webp options for ffmpeg are documented here:
|
||||
// https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
|
||||
|
||||
$commandArguments = [];
|
||||
|
||||
$commandArguments[] = '-i';
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
|
||||
// preset. Appears first in the list as recommended in the cwebp docs
|
||||
if (!is_null($this->options['preset'])) {
|
||||
if ($this->options['preset'] != 'none') {
|
||||
$commandArguments[] = '-preset ' . $this->options['preset'];
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite existing files?, yes!
|
||||
$commandArguments[] = '-y';
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality:auto was specified, but could not be determined.
|
||||
// we cannot apply the max-quality logic, but we can provide auto quality
|
||||
// simply by not specifying the quality option.
|
||||
} else {
|
||||
$commandArguments[] = '-qscale ' . escapeshellarg($this->getCalculatedQuality());
|
||||
}
|
||||
if ($this->options['encoding'] == 'lossless') {
|
||||
$commandArguments[] = '-lossless 1';
|
||||
} else {
|
||||
$commandArguments[] = '-lossless 0';
|
||||
}
|
||||
|
||||
if ($this->options['metadata'] == 'none') {
|
||||
// Unfortunately there seems to be no easy solution available for removing all metadata.
|
||||
}
|
||||
|
||||
// compression_level maps to method, according to https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
|
||||
$commandArguments[] = '-compression_level ' . $this->options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->destination);
|
||||
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
//$this->logLn($this->getVersion());
|
||||
|
||||
$command = $this->getPath() . ' ' . $this->createCommandLineOptions() . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('ffmpeg is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
throw new SystemRequirementsNotMetException('The exec() call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
536
vendor/rosell-dk/webp-convert/src/Convert/Converters/Gd.php
vendored
Normal file
536
vendor/rosell-dk/webp-convert/src/Convert/Converters/Gd.php
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using gd extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Gd extends AbstractConverter
|
||||
{
|
||||
public function supportsLossless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
private $errorMessageWhileCreating = '';
|
||||
private $errorNumberWhileCreating;
|
||||
|
||||
/**
|
||||
* Check (general) operationality of Gd converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new SystemRequirementsNotMetException('Required Gd extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('imagewebp')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without webp support.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('imagepalettetotruecolor')) {
|
||||
if (!self::functionsExist([
|
||||
'imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
|
||||
'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'
|
||||
])) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd cannot convert palette color images to RGB. ' .
|
||||
'Even though it would be possible to convert RGB images to webp with Gd, ' .
|
||||
'we refuse to do it. A partial working converter causes more trouble than ' .
|
||||
'a non-working. To make this converter work, you need the imagepalettetotruecolor() ' .
|
||||
'function to be enabled on your system.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Gd has been compiled without support for image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!function_exists('imagecreatefrompng')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
if (!function_exists('imagecreatefromjpeg')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without Jpeg support and can therefore not convert this jpeg image.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if all functions exists.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private static function functionsExist($functionNamesArr)
|
||||
{
|
||||
foreach ($functionNamesArr as $functionName) {
|
||||
if (!function_exists($functionName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert image pallette to true color on older systems that does not have imagepalettetotruecolor().
|
||||
*
|
||||
* The aim is to function as imagepalettetotruecolor, but for older systems.
|
||||
* So, if the image is already rgb, nothing will be done, and true will be returned
|
||||
* PS: Got the workaround here: https://secure.php.net/manual/en/function.imagepalettetotruecolor.php
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
|
||||
* otherwise FALSE is returned.
|
||||
*/
|
||||
private function makeTrueColorUsingWorkaround(&$image)
|
||||
{
|
||||
//return $this->makeTrueColor($image);
|
||||
/*
|
||||
if (function_exists('imageistruecolor') && imageistruecolor($image)) {
|
||||
return true;
|
||||
}*/
|
||||
if (self::functionsExist(['imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
|
||||
'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'])) {
|
||||
$dst = imagecreatetruecolor(imagesx($image), imagesy($image));
|
||||
|
||||
if ($dst === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
|
||||
//prevent blending with default black
|
||||
if (imagealphablending($dst, false) !== false) {
|
||||
//change the RGB values if you need, but leave alpha at 127
|
||||
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
|
||||
|
||||
if ($transparent !== false) {
|
||||
//simpler than flood fill
|
||||
if (imagefilledrectangle($dst, 0, 0, imagesx($image), imagesy($image), $transparent) !== false) {
|
||||
//restore default blending
|
||||
if (imagealphablending($dst, true) !== false) {
|
||||
if (imagecopy($dst, $image, 0, 0, 0, 0, imagesx($image), imagesy($image)) !== false) {
|
||||
$success = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success) {
|
||||
imagedestroy($image);
|
||||
$image = $dst;
|
||||
} else {
|
||||
imagedestroy($dst);
|
||||
}
|
||||
return $success;
|
||||
} else {
|
||||
// The necessary methods for converting color palette are not avalaible
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert image pallette to true color.
|
||||
*
|
||||
* Try to convert image pallette to true color. If imagepalettetotruecolor() exists, that is used (available from
|
||||
* PHP >= 5.5.0). Otherwise using workaround found on the net.
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
|
||||
* otherwise FALSE is returned.
|
||||
*/
|
||||
private function makeTrueColor(&$image)
|
||||
{
|
||||
if (function_exists('imagepalettetotruecolor')) {
|
||||
return imagepalettetotruecolor($image);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'imagepalettetotruecolor() is not available on this system - using custom implementation instead'
|
||||
);
|
||||
return $this->makeTrueColorUsingWorkaround($image);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Gd image resource from source
|
||||
*
|
||||
* @throws InvalidInputException if mime type is unsupported or could not be detected
|
||||
* @throws ConversionFailedException if imagecreatefrompng or imagecreatefromjpeg fails
|
||||
* @return resource|\GdImage $image The created image
|
||||
*/
|
||||
private function createImageResource()
|
||||
{
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
$image = imagecreatefrompng($this->source);
|
||||
if ($image === false) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to load/create image (imagecreatefrompng() failed)'
|
||||
);
|
||||
}
|
||||
return $image;
|
||||
|
||||
case 'image/jpeg':
|
||||
$image = imagecreatefromjpeg($this->source);
|
||||
if ($image === false) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to load/create image (imagecreatefromjpeg() failed)'
|
||||
);
|
||||
}
|
||||
return $image;
|
||||
}
|
||||
|
||||
throw new InvalidInputException('Unsupported mime type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to make image resource true color if it is not already.
|
||||
*
|
||||
* @param resource|\GdImage $image The image to work on
|
||||
* @return boolean|null True if it is now true color. False if it is NOT true color. null, if we cannot tell
|
||||
*/
|
||||
protected function tryToMakeTrueColorIfNot(&$image)
|
||||
{
|
||||
$whatIsItNow = null;
|
||||
$mustMakeTrueColor = false;
|
||||
if (function_exists('imageistruecolor')) {
|
||||
if (imageistruecolor($image)) {
|
||||
$this->logLn('image is true color');
|
||||
$whatIsItNow = true;
|
||||
} else {
|
||||
$this->logLn('image is not true color');
|
||||
$mustMakeTrueColor = true;
|
||||
$whatIsItNow = false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn('It can not be determined if image is true color');
|
||||
$mustMakeTrueColor = true;
|
||||
}
|
||||
|
||||
if ($mustMakeTrueColor) {
|
||||
$this->logLn('converting color palette to true color');
|
||||
$success = $this->makeTrueColor($image);
|
||||
if ($success) {
|
||||
return true;
|
||||
} else {
|
||||
$this->logLn(
|
||||
'FAILED converting color palette to true color. '
|
||||
);
|
||||
}
|
||||
}
|
||||
return $whatIsItNow;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean true if alpha blending was set successfully, false otherwise
|
||||
*/
|
||||
protected function trySettingAlphaBlending($image)
|
||||
{
|
||||
if (function_exists('imagealphablending')) {
|
||||
// TODO: Should we set second parameter to false instead?
|
||||
// As here: https://www.texelate.co.uk/blog/retaining-png-transparency-with-php-gd
|
||||
// (PS: I have backed up some local changes - to Gd.php, which includes changing that param
|
||||
// to false. But I didn't finish up back then and now I forgot, so need to retest before
|
||||
// changing anything...
|
||||
if (!imagealphablending($image, true)) {
|
||||
$this->logLn('Warning: imagealphablending() failed');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Warning: imagealphablending() is not available on your system.' .
|
||||
' Converting PNGs with transparency might fail on some systems'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (function_exists('imagesavealpha')) {
|
||||
if (!imagesavealpha($image, true)) {
|
||||
$this->logLn('Warning: imagesavealpha() failed');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Warning: imagesavealpha() is not available on your system. ' .
|
||||
'Converting PNGs with transparency might fail on some systems'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function errorHandlerWhileCreatingWebP($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
$this->errorNumberWhileCreating = $errno;
|
||||
$this->errorMessageWhileCreating = $errstr . ' in ' . $errfile . ', line ' . $errline .
|
||||
', PHP ' . PHP_VERSION . ' (' . PHP_OS . ')';
|
||||
//return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return void
|
||||
*/
|
||||
protected function destroyAndRemove($image)
|
||||
{
|
||||
imagedestroy($image);
|
||||
if (file_exists($this->destination)) {
|
||||
@unlink($this->destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return void
|
||||
*/
|
||||
protected function tryConverting($image)
|
||||
{
|
||||
|
||||
// Danger zone!
|
||||
// Using output buffering to generate image.
|
||||
// In this zone, Do NOT do anything that might produce unwanted output
|
||||
// Do NOT call $this->logLn
|
||||
// --------------------------------- (start of danger zone)
|
||||
|
||||
$addedZeroPadding = false;
|
||||
set_error_handler(array($this, "errorHandlerWhileCreatingWebP"));
|
||||
|
||||
// This line may trigger log, so we need to do it BEFORE ob_start() !
|
||||
$q = $this->getCalculatedQuality();
|
||||
|
||||
ob_start();
|
||||
|
||||
// Adding this try/catch is perhaps not neccessary.
|
||||
// I'm not certain that the error handler takes care of Throwable errors.
|
||||
// and - sorry - was to lazy to find out right now. So for now: better safe than sorry. #320
|
||||
$error = null;
|
||||
$success = false;
|
||||
|
||||
try {
|
||||
// Beware: This call can throw FATAL on windows (cannot be catched)
|
||||
// This for example happens on palette images
|
||||
$success = imagewebp($image, null, $q);
|
||||
} catch (\Exception $e) {
|
||||
$error = $e;
|
||||
} catch (\Throwable $e) {
|
||||
$error = $e;
|
||||
}
|
||||
if (!is_null($error)) {
|
||||
restore_error_handler();
|
||||
ob_end_clean();
|
||||
$this->destroyAndRemove($image);
|
||||
throw $error;
|
||||
}
|
||||
if (!$success) {
|
||||
$this->destroyAndRemove($image);
|
||||
ob_end_clean();
|
||||
restore_error_handler();
|
||||
throw new ConversionFailedException(
|
||||
'Failed creating image. Call to imagewebp() failed.',
|
||||
$this->errorMessageWhileCreating
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// The following hack solves an `imagewebp` bug
|
||||
// See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
||||
if (ob_get_length() % 2 == 1) {
|
||||
echo "\0";
|
||||
$addedZeroPadding = true;
|
||||
}
|
||||
$output = ob_get_clean();
|
||||
restore_error_handler();
|
||||
|
||||
if ($output == '') {
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed: imagewebp() returned empty string'
|
||||
);
|
||||
}
|
||||
|
||||
// --------------------------------- (end of danger zone).
|
||||
|
||||
|
||||
if ($this->errorMessageWhileCreating != '') {
|
||||
switch ($this->errorNumberWhileCreating) {
|
||||
case E_WARNING:
|
||||
$this->logLn('An warning was produced during conversion: ' . $this->errorMessageWhileCreating);
|
||||
break;
|
||||
case E_NOTICE:
|
||||
$this->logLn('An notice was produced during conversion: ' . $this->errorMessageWhileCreating);
|
||||
break;
|
||||
default:
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'An error was produced during conversion',
|
||||
$this->errorMessageWhileCreating
|
||||
);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($addedZeroPadding) {
|
||||
$this->logLn(
|
||||
'Fixing corrupt webp by adding a zero byte ' .
|
||||
'(older versions of Gd had a bug, but this hack fixes it)'
|
||||
);
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $output);
|
||||
|
||||
if (!$success) {
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to save the image. Check file permissions!'
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Previous code was much simpler, but on a system, the hack was not activated (a file with uneven number of bytes
|
||||
was created). This is puzzeling. And the old code did not provide any insights.
|
||||
Also, perhaps having two subsequent writes to the same file could perhaps cause a problem.
|
||||
In the new code, there is only one write.
|
||||
However, a bad thing about the new code is that the entire webp file is read into memory. This might cause
|
||||
memory overflow with big files.
|
||||
Perhaps we should check the filesize of the original and only use the new code when it is smaller than
|
||||
memory limit set in PHP by a certain factor.
|
||||
Or perhaps only use the new code on older versions of Gd
|
||||
https://wordpress.org/support/topic/images-not-seen-on-chrome/#post-11390284
|
||||
|
||||
Here is the old code:
|
||||
|
||||
$success = imagewebp($image, $this->destination, $this->getCalculatedQuality());
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to save the image as webp (call to imagewebp() failed). ' .
|
||||
'It probably failed writing file. Check file permissions!'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// This hack solves an `imagewebp` bug
|
||||
// See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
||||
if (filesize($this->destination) % 2 == 1) {
|
||||
file_put_contents($this->destination, "\0", FILE_APPEND);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$versionString = gd_info()["GD Version"];
|
||||
$this->logLn('GD Version: ' . $versionString);
|
||||
|
||||
// Btw: Check out processWebp here:
|
||||
// https://github.com/Intervention/image/blob/master/src/Intervention/Image/Gd/Encoder.php
|
||||
|
||||
// Create image resource
|
||||
$image = $this->createImageResource();
|
||||
|
||||
// Try to convert color palette if it is not true color
|
||||
$isItTrueColorNow = $this->tryToMakeTrueColorIfNot($image);
|
||||
if ($isItTrueColorNow === false) {
|
||||
// our tests shows that converting palette fails on all systems,
|
||||
throw new ConversionFailedException(
|
||||
'Cannot convert image because it is a palette image and the palette image cannot ' .
|
||||
'be converted to RGB (which is required). To convert to RGB, we would need ' .
|
||||
'imagepalettetotruecolor(), which is not available on your system. ' .
|
||||
'Our workaround does not have the neccasary functions for converting to RGB either.'
|
||||
);
|
||||
}
|
||||
if (is_null($isItTrueColorNow)) {
|
||||
$isWindows = preg_match('/^win/i', PHP_OS);
|
||||
$isMacDarwin = preg_match('/^darwin/i', PHP_OS); // actually no problem in PHP 7.4 and 8.0
|
||||
if ($isWindows || $isMacDarwin) {
|
||||
throw new ConversionFailedException(
|
||||
'Cannot convert image because it appears to be a palette image and the palette image ' .
|
||||
'cannot be converted to RGB, as you do not have imagepalettetotruecolor() enabled. ' .
|
||||
'Converting palette on ' .
|
||||
($isWindows ? 'Windows causes FATAL error' : 'Mac causes halt') .
|
||||
'So we abort now'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getMimeTypeOfSource() == 'image/png') {
|
||||
if (function_exists('version_compare')) {
|
||||
if (version_compare($versionString, "2.1.1", "<=")) {
|
||||
$this->logLn(
|
||||
'BEWARE: Your version of Gd looses the alpha chanel when converting to webp.' .
|
||||
'You should upgrade Gd, use another converter or stop converting PNGs. ' .
|
||||
'See: https://github.com/rosell-dk/webp-convert/issues/238'
|
||||
);
|
||||
} elseif (version_compare($versionString, "2.2.4", "<=")) {
|
||||
$this->logLn(
|
||||
'BEWARE: Older versions of Gd looses the alpha chanel when converting to webp.' .
|
||||
'We have not tested if transparency fails on your (rather old) version of Gd. ' .
|
||||
'Please let us know. ' .
|
||||
'See: https://github.com/rosell-dk/webp-convert/issues/238'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to set alpha blending
|
||||
$this->trySettingAlphaBlending($image);
|
||||
}
|
||||
|
||||
// Try to convert it to webp
|
||||
$this->tryConverting($image);
|
||||
|
||||
// End of story
|
||||
imagedestroy($image);
|
||||
}
|
||||
}
|
||||
173
vendor/rosell-dk/webp-convert/src/Convert/Converters/Gmagick.php
vendored
Normal file
173
vendor/rosell-dk/webp-convert/src/Convert/Converters/Gmagick.php
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Gmagick extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Gmagick extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'near-lossless',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of Gmagick converter.
|
||||
*
|
||||
* Note:
|
||||
* It may be that Gd has been compiled without jpeg support or png support.
|
||||
* We do not check for this here, as the converter could still be used for the other.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('Gmagick')) {
|
||||
throw new SystemRequirementsNotMetException('Required Gmagick extension is not available.');
|
||||
}
|
||||
|
||||
if (!class_exists('Gmagick')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick is installed, but not correctly. The class Gmagick is not available'
|
||||
);
|
||||
}
|
||||
|
||||
$im = new \Gmagick($this->source);
|
||||
|
||||
if (!in_array('WEBP', $im->queryformats())) {
|
||||
throw new SystemRequirementsNotMetException('Gmagick was compiled without WebP support.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Gmagick does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$im = new \Gmagick();
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!in_array('PNG', $im->queryFormats())) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'image/jpeg':
|
||||
if (!in_array('JPEG', $im->queryFormats())) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
|
||||
// PS: graphicsmagick options are documented here: (search for "webp:")
|
||||
// http://www.graphicsmagick.org/GraphicsMagick.html
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
try {
|
||||
$im = new \Gmagick($this->source);
|
||||
} catch (\Exception $e) {
|
||||
throw new ConversionFailedException(
|
||||
'Failed creating Gmagick object of file',
|
||||
'Failed creating Gmagick object of file: "' . $this->source . '" - Gmagick threw an exception.',
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
$im->setimageformat('WEBP');
|
||||
|
||||
// setimageoption() has not always been there, so check first. #169
|
||||
if (method_exists($im, 'setimageoption')) {
|
||||
// Finally cracked setting webp options.
|
||||
// See #167
|
||||
// - and https://stackoverflow.com/questions/47294962/how-to-write-lossless-webp-files-with-perlmagick
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because gmagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$im->setimageoption('webp', 'image-hint', $imageHint);
|
||||
}
|
||||
}
|
||||
$im->setimageoption('webp', 'method', $options['method']);
|
||||
$im->setimageoption('webp', 'lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
|
||||
$im->setimageoption('webp', 'alpha-quality', $options['alpha-quality']);
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$im->setimageoption('webp', 'auto-filter', 'true');
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
$im->setimageoption('webp', 'use-sharp-yuv', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
low-memory seems not to be supported:
|
||||
$im->setimageoption('webp', 'low-memory', $options['low-memory'] ? true : false);
|
||||
*/
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
// Strip metadata and profiles
|
||||
$im->stripImage();
|
||||
}
|
||||
|
||||
// Ps: Imagick automatically uses same quality as source, when no quality is set
|
||||
// This feature is however not present in Gmagick
|
||||
// TODO: However, it might be possible after all - see #91
|
||||
$im->setcompressionquality($this->getCalculatedQuality());
|
||||
|
||||
// We call getImageBlob().
|
||||
// That method is undocumented, but it is there!
|
||||
// - just like it is in imagick, as pointed out here:
|
||||
// https://www.php.net/manual/ru/gmagick.readimageblob.php
|
||||
|
||||
/** @scrutinizer ignore-call */
|
||||
$imageBlob = $im->getImageBlob();
|
||||
|
||||
$success = @file_put_contents($this->destination, $imageBlob);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Failed writing file');
|
||||
}
|
||||
}
|
||||
}
|
||||
28
vendor/rosell-dk/webp-convert/src/Convert/Converters/GmagickBinary.php
vendored
Normal file
28
vendor/rosell-dk/webp-convert/src/Convert/Converters/GmagickBinary.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Non-functional converter, just here to tell you that it has been renamed.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GmagickBinary extends AbstractConverter
|
||||
{
|
||||
public function checkOperationality()
|
||||
{
|
||||
throw new ConversionFailedException(
|
||||
'This converter has changed ID from "gmagickbinary" to "graphicsmagick". You need to change!'
|
||||
);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$this->checkOperationality();
|
||||
}
|
||||
}
|
||||
220
vendor/rosell-dk/webp-convert/src/Convert/Converters/GraphicsMagick.php
vendored
Normal file
220
vendor/rosell-dk/webp-convert/src/Convert/Converters/GraphicsMagick.php
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling gmagick binary (gm).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GraphicsMagick extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'near-lossless',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption()
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_GRAPHICSMAGICK_PATH')) {
|
||||
return constant('WEBPCONVERT_GRAPHICSMAGICK_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH'))) {
|
||||
return getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH');
|
||||
}
|
||||
return 'gm';
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
if (($returnCode == 0) && isset($output[0])) {
|
||||
return preg_replace('#http.*#', '', $output[0]);
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#WebP.*yes#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('gmagick is not installed');
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('webp delegate missing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions()
|
||||
{
|
||||
// For available webp options, check out:
|
||||
// https://github.com/kstep/graphicsmagick/blob/master/coders/webp.c
|
||||
|
||||
$commandArguments = [];
|
||||
|
||||
/*
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// Unlike imagick binary, it seems gmagick binary uses a fixed
|
||||
// quality (75) when quality is omitted
|
||||
// So we cannot simply omit in order to get same quality as source.
|
||||
// But perhaps there is another way?
|
||||
// Check out #91 - it is perhaps as easy as this: "-define jpeg:preserve-settings"
|
||||
}
|
||||
*/
|
||||
$commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
// preset
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'Note: the preset was mapped to "graph" because graphicsmagick does not support ' .
|
||||
'"drawing", "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$commandArguments[] = '-define webp:image-hint=' . escapeshellarg($imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
// encoding
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// Btw:
|
||||
// I am not sure if we should set "quality" for lossless.
|
||||
// Quality should not apply to lossless, but my tests shows that it does in some way for gmagick
|
||||
// setting it low, you get bad visual quality and small filesize. Setting it high, you get the opposite
|
||||
// Some claim it is a bad idea to set quality, but I'm not so sure.
|
||||
// https://stackoverflow.com/questions/4228027/
|
||||
// First, I do not just get bigger images when setting quality, as toc777 does.
|
||||
// Secondly, the answer is very old and that bad behaviour is probably fixed by now.
|
||||
$commandArguments[] = '-define webp:lossless=true';
|
||||
} else {
|
||||
$commandArguments[] = '-define webp:lossless=false';
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$commandArguments[] = '-define webp:auto-filter=true';
|
||||
}
|
||||
|
||||
if ($options['alpha-quality'] !== 100) {
|
||||
$commandArguments[] = '-define webp:alpha-quality=' . strval($options['alpha-quality']);
|
||||
}
|
||||
|
||||
if ($options['low-memory']) {
|
||||
$commandArguments[] = '-define webp:low-memory=true';
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
$commandArguments[] = '-define webp:use-sharp-yuv=true';
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
$commandArguments[] = '-strip';
|
||||
}
|
||||
|
||||
$commandArguments[] = '-define webp:method=' . $options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
$commandArguments[] = escapeshellarg('webp:' . $this->destination);
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
//$this->logLn('Using quality:' . $this->getCalculatedQuality());
|
||||
|
||||
$this->logLn('Version: ' . $this->getVersion());
|
||||
|
||||
$command = $this->getPath() . ' convert ' . $this->createCommandLineOptions() . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('gmagick is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
$this->logLn('command:' . $command);
|
||||
$this->logLn('return code:' . $returnCode);
|
||||
$this->logLn('output:' . print_r(implode("\n", $output), true));
|
||||
throw new SystemRequirementsNotMetException('The exec() call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
275
vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php
vendored
Normal file
275
vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
use LocateBinaries\LocateBinaries;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling imagemagick binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ImageMagick extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption(),
|
||||
['try-common-system-paths', 'boolean', [
|
||||
'title' => 'Try locating ImageMagick in common system paths',
|
||||
'description' =>
|
||||
'If set, the converter will look for a ImageMagick binaries residing in common system locations ' .
|
||||
'such as "/usr/bin/convert". ' .
|
||||
'If such exist, it is assumed that they are valid ImageMagick binaries. ',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
// To futher improve this converter, I could check out:
|
||||
// https://github.com/Orbitale/ImageMagickPHP
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_IMAGEMAGICK_PATH')) {
|
||||
return constant('WEBPCONVERT_IMAGEMAGICK_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_IMAGEMAGICK_PATH'))) {
|
||||
return getenv('WEBPCONVERT_IMAGEMAGICK_PATH');
|
||||
}
|
||||
|
||||
if ($this->options['try-common-system-paths']) {
|
||||
$binaries = LocateBinaries::locateInCommonSystemPaths('convert');
|
||||
if (!empty($binaries)) {
|
||||
return $binaries[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 'convert';
|
||||
}
|
||||
|
||||
private function getVersion()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
if (($returnCode == 0) && isset($output[0])) {
|
||||
return $output[0];
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -list delegate 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#webp\\s*=#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// try other command
|
||||
ExecWithFallback::exec($this->getPath() . ' -list configure 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#DELEGATE.*webp#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
// PS, convert -version does not output delegates on travis, so it is not reliable
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'imagemagick is not installed (cannot execute: "' . $this->getPath() . '")'
|
||||
);
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('webp delegate missing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @param string $versionNumber. Ie "6.9.10-23"
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions($versionNumber = 'unknown')
|
||||
{
|
||||
// Available webp options for imagemagick are documented here:
|
||||
// - https://imagemagick.org/script/webp.php
|
||||
// - https://github.com/ImageMagick/ImageMagick/blob/main/coders/webp.c
|
||||
|
||||
// We should perhaps implement low-memory. Its already in cwebp, it
|
||||
// could perhaps be promoted to a general option
|
||||
|
||||
$commandArguments = [];
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality:auto was specified, but could not be determined.
|
||||
// we cannot apply the max-quality logic, but we can provide auto quality
|
||||
// simply by not specifying the quality option.
|
||||
} else {
|
||||
$commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
// "image-hint" is at least available from 6.9.4-0 (I can't see further back)
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because imagemagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$commandArguments[] = '-define webp:image-hint=' . escapeshellarg($imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// lossless is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:lossless=true';
|
||||
}
|
||||
|
||||
if ($options['low-memory']) {
|
||||
// low-memory is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:low-memory=true';
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
// auto-filter is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:auto-filter=true';
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
$commandArguments[] = '-strip';
|
||||
}
|
||||
|
||||
if ($options['alpha-quality'] !== 100) {
|
||||
// alpha-quality is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:alpha-quality=' . strval($options['alpha-quality']);
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if (version_compare($versionNumber, '7.0.8-26', '>=')) {
|
||||
$commandArguments[] = '-define webp:use-sharp-yuv=true';
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "sharp-yuv" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.8-26 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['near-lossless'] != 100) {
|
||||
if (version_compare($versionNumber, '7.0.10-54', '>=')) { // #299
|
||||
$commandArguments[] = '-define webp:near-lossless=' . escapeshellarg($options['near-lossless']);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "near-lossless" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.10-54 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// "method" is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:method=' . $options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
$commandArguments[] = escapeshellarg('webp:' . $this->destination);
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$version = $this->getVersion();
|
||||
|
||||
$this->logLn($version);
|
||||
|
||||
preg_match('#\d+\.\d+\.\d+[\d\.\-]+#', $version, $matches);
|
||||
$versionNumber = (isset($matches[0]) ? $matches[0] : 'unknown');
|
||||
|
||||
$this->logLn('Extracted version number: ' . $versionNumber);
|
||||
|
||||
$command = $this->getPath() . ' ' . $this->createCommandLineOptions($versionNumber) . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('imagemagick is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
throw new SystemRequirementsNotMetException('The exec call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
229
vendor/rosell-dk/webp-convert/src/Convert/Converters/Imagick.php
vendored
Normal file
229
vendor/rosell-dk/webp-convert/src/Convert/Converters/Imagick.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Imagick extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Imagick extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check operationality of Imagick converter.
|
||||
*
|
||||
* Note:
|
||||
* It may be that Gd has been compiled without jpeg support or png support.
|
||||
* We do not check for this here, as the converter could still be used for the other.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('imagick')) {
|
||||
throw new SystemRequirementsNotMetException('Required iMagick extension is not available.');
|
||||
}
|
||||
|
||||
if (!class_exists('\\Imagick')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'iMagick is installed, but not correctly. The class Imagick is not available'
|
||||
);
|
||||
}
|
||||
|
||||
$im = new \Imagick();
|
||||
if (!in_array('WEBP', $im->queryFormats('WEBP'))) {
|
||||
throw new SystemRequirementsNotMetException('iMagick was compiled without WebP support.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Imagick does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$im = new \Imagick();
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!in_array('PNG', $im->queryFormats('PNG'))) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Imagick has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'image/jpeg':
|
||||
if (!in_array('JPEG', $im->queryFormats('JPEG'))) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Imagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* It may also throw an ImagickException if imagick throws an exception
|
||||
* @throws CreateDestinationFileException if imageblob could not be saved to file
|
||||
*/
|
||||
protected function doActualConvert()
|
||||
{
|
||||
/*
|
||||
* More about iMagick's WebP options:
|
||||
* - Inspect source code: https://github.com/ImageMagick/ImageMagick/blob/master/coders/webp.c#L559
|
||||
* (search for "webp:")
|
||||
* - http://www.imagemagick.org/script/webp.php
|
||||
* - https://developers.google.com/speed/webp/docs/cwebp
|
||||
* - https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
|
||||
*/
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
// This might throw - we let it!
|
||||
$im = new \Imagick($this->source);
|
||||
//$im = new \Imagick();
|
||||
//$im->pingImage($this->source);
|
||||
//$im->readImage($this->source);
|
||||
|
||||
$version = \Imagick::getVersion();
|
||||
$this->logLn('ImageMagic API version (full): ' . $version['versionString']);
|
||||
|
||||
preg_match('#\d+\.\d+\.\d+[\d\.\-]+#', $version['versionString'], $matches);
|
||||
$versionNumber = (isset($matches[0]) ? $matches[0] : 'unknown');
|
||||
$this->logLn('ImageMagic API version (just the number): ' . $versionNumber);
|
||||
|
||||
// Note: good enough for info, but not entirely reliable - see #304
|
||||
$extVersion = (defined('\Imagick::IMAGICK_EXTVER') ? \Imagick::IMAGICK_EXTVER : phpversion('imagick'));
|
||||
$this->logLn('Imagic extension version: ' . $extVersion);
|
||||
|
||||
$im->setImageFormat('WEBP');
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because imagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$im->setOption('webp:image-hint', $imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
$im->setOption('webp:method', $options['method']);
|
||||
$im->setOption('webp:lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
|
||||
$im->setOption('webp:low-memory', $options['low-memory'] ? 'true' : 'false');
|
||||
$im->setOption('webp:alpha-quality', $options['alpha-quality']);
|
||||
|
||||
if ($options['near-lossless'] != 100) {
|
||||
if (version_compare($versionNumber, '7.0.10-54', '>=')) {
|
||||
$im->setOption('webp:near-lossless', $options['near-lossless']);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: near-lossless is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.10-54 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$im->setOption('webp:auto-filter', 'true');
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if (version_compare($versionNumber, '7.0.8-26', '>=')) {
|
||||
$im->setOption('webp:use-sharp-yuv', 'true');
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "sharp-yuv" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.8-26 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
// To strip metadata, we need to use the stripImage() method. However, that method does not only remove
|
||||
// metadata, but color profiles as well. We want to keep the color profiles, so we grab it now to be able
|
||||
// to restore it. (Thanks, Max Eremin: https://www.php.net/manual/en/imagick.stripimage.php#120380)
|
||||
|
||||
// Grab color profile (to be able to restore them)
|
||||
$profiles = $im->getImageProfiles("icc", true);
|
||||
|
||||
// Strip metadata (and color profiles)
|
||||
$im->stripImage();
|
||||
|
||||
// Restore color profiles
|
||||
if (!empty($profiles)) {
|
||||
$im->profileImage("icc", $profiles['icc']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// Luckily imagick is a big boy, and automatically converts with same quality as
|
||||
// source, when the quality isn't set.
|
||||
// So we simply do not set quality.
|
||||
// This actually kills the max-quality functionality. But I deem that this is more important
|
||||
// because setting image quality to something higher than source generates bigger files,
|
||||
// but gets you no extra quality. When failing to limit quality, you at least get something
|
||||
// out of it
|
||||
$this->logLn('Converting without setting quality in order to achieve auto quality');
|
||||
} else {
|
||||
$im->setImageCompressionQuality($this->getCalculatedQuality());
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/29171248/php-imagick-jpeg-optimization
|
||||
// setImageFormat
|
||||
|
||||
// TODO: Read up on
|
||||
// https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
|
||||
// https://github.com/nwtn/php-respimg
|
||||
|
||||
// TODO:
|
||||
// Should we set alpha channel for PNG's like suggested here:
|
||||
// https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
|
||||
// It seems that alpha channel works without... (at least I see completely transparerent pixels)
|
||||
|
||||
// We used to use writeImageFile() method. But we now use getImageBlob(). See issue #43
|
||||
|
||||
// This might throw - we let it!
|
||||
$imageBlob = $im->getImageBlob();
|
||||
|
||||
$success = file_put_contents($this->destination, $imageBlob);
|
||||
|
||||
if (!$success) {
|
||||
throw new CreateDestinationFileException('Failed writing file');
|
||||
}
|
||||
|
||||
// Btw: check out processWebp() method here:
|
||||
// https://github.com/Intervention/image/blob/master/src/Intervention/Image/Imagick/Encoder.php
|
||||
}
|
||||
}
|
||||
28
vendor/rosell-dk/webp-convert/src/Convert/Converters/ImagickBinary.php
vendored
Normal file
28
vendor/rosell-dk/webp-convert/src/Convert/Converters/ImagickBinary.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Non-functional converter, just here to tell you that it has been renamed.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ImagickBinary extends AbstractConverter
|
||||
{
|
||||
public function checkOperationality()
|
||||
{
|
||||
throw new ConversionFailedException(
|
||||
'This converter has changed ID from "imagickbinary" to "imagemagick". You need to change!'
|
||||
);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$this->checkOperationality();
|
||||
}
|
||||
}
|
||||
283
vendor/rosell-dk/webp-convert/src/Convert/Converters/Stack.php
vendored
Normal file
283
vendor/rosell-dk/webp-convert/src/Convert/Converters/Stack.php
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\ConverterFactory;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by trying a stack of converters until success.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Stack extends AbstractConverter
|
||||
{
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
'skip',
|
||||
'default-quality',
|
||||
'quality',
|
||||
'max-quality',
|
||||
];
|
||||
}
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['converters', 'array', [
|
||||
'title' => 'Converters',
|
||||
'description' => 'Converters to try, ordered by priority.',
|
||||
'default' => self::getAvailableConverters(),
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'options' => self::getAvailableConverters(),
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['converter-options', 'array', [
|
||||
'title' => 'Converter options',
|
||||
'description' =>
|
||||
'Extra options for specific converters.',
|
||||
'default' => [],
|
||||
'sensitive' => true,
|
||||
'ui' => null
|
||||
]],
|
||||
['preferred-converters', 'array', [
|
||||
'title' => 'Preferred converters',
|
||||
'description' =>
|
||||
'With this option you can move specified converters to the top of the stack. ' .
|
||||
'The converters are specified by id.',
|
||||
'default' => [],
|
||||
'ui' => null
|
||||
]],
|
||||
['extra-converters', 'array', [
|
||||
'title' => 'Extra converters',
|
||||
'description' =>
|
||||
'Add extra converters to the bottom of the stack',
|
||||
'default' => [],
|
||||
'sensitive' => true,
|
||||
'ui' => null
|
||||
]],
|
||||
['shuffle', 'boolean', [
|
||||
'title' => 'Shuffle',
|
||||
'description' =>
|
||||
'Shuffles the converter order on each conversion. ' .
|
||||
'Can for example be used to spread out requests on multiple cloud converters',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
]);
|
||||
|
||||
|
||||
/*
|
||||
return [
|
||||
new SensitiveArrayOption('converters', self::getAvailableConverters()),
|
||||
new SensitiveArrayOption('converter-options', []),
|
||||
new BooleanOption('shuffle', false),
|
||||
new ArrayOption('preferred-converters', []),
|
||||
new SensitiveArrayOption('extra-converters', [])
|
||||
];*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available converters (ids) - ordered by awesomeness.
|
||||
*
|
||||
* @return array An array of ids of converters that comes with this library
|
||||
*/
|
||||
public static function getAvailableConverters()
|
||||
{
|
||||
return [
|
||||
'cwebp', 'vips', 'imagick', 'gmagick', 'imagemagick', 'graphicsmagick', 'wpc', 'ffmpeg', 'ewww', 'gd'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (count($this->options['converters']) == 0) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Converter stack is empty! - no converters to try, no conversion can be made!'
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: We should test if all converters are found in order to detect problems early
|
||||
|
||||
//$this->logLn('Stack converter ignited');
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$options = $this->options;
|
||||
|
||||
$beginTimeStack = microtime(true);
|
||||
|
||||
$anyRuntimeErrors = false;
|
||||
|
||||
$converters = $options['converters'];
|
||||
if (count($options['extra-converters']) > 0) {
|
||||
$converters = array_merge($converters, $options['extra-converters']);
|
||||
/*foreach ($options['extra-converters'] as $extra) {
|
||||
$converters[] = $extra;
|
||||
}*/
|
||||
}
|
||||
|
||||
// preferred-converters
|
||||
if (count($options['preferred-converters']) > 0) {
|
||||
foreach (array_reverse($options['preferred-converters']) as $prioritizedConverter) {
|
||||
foreach ($converters as $i => $converter) {
|
||||
if (is_array($converter)) {
|
||||
$converterId = $converter['converter'];
|
||||
} else {
|
||||
$converterId = $converter;
|
||||
}
|
||||
if ($converterId == $prioritizedConverter) {
|
||||
unset($converters[$i]);
|
||||
array_unshift($converters, $converter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// perhaps write the order to the log? (without options) - but this requires some effort
|
||||
}
|
||||
|
||||
// shuffle
|
||||
if ($options['shuffle']) {
|
||||
shuffle($converters);
|
||||
}
|
||||
|
||||
//$this->logLn(print_r($converters));
|
||||
//$options['converters'] = $converters;
|
||||
//$defaultConverterOptions = $options;
|
||||
$defaultConverterOptions = [];
|
||||
|
||||
foreach ($this->options2->getOptionsMap() as $id => $option) {
|
||||
// Right here, there used to be a check that ensured that unknown options was not passed down to the
|
||||
// converters (" && !($option instanceof GhostOption)"). But well, as the Stack doesn't know about
|
||||
// converter specific options, such as "try-cwebp", these was not passed down (see #259)
|
||||
// I'm not sure why the check was made in the first place, but it does not seem neccessary, as the
|
||||
// converters simply ignore unknown options. So the check has now been removed.
|
||||
if ($option->isValueExplicitlySet()) {
|
||||
$defaultConverterOptions[$id] = $option->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
//unset($defaultConverterOptions['converters']);
|
||||
//unset($defaultConverterOptions['converter-options']);
|
||||
$defaultConverterOptions['_skip_input_check'] = true;
|
||||
$defaultConverterOptions['_suppress_success_message'] = true;
|
||||
unset($defaultConverterOptions['converters']);
|
||||
unset($defaultConverterOptions['extra-converters']);
|
||||
unset($defaultConverterOptions['converter-options']);
|
||||
unset($defaultConverterOptions['preferred-converters']);
|
||||
unset($defaultConverterOptions['shuffle']);
|
||||
|
||||
// $this->logLn('converters: ' . print_r($converters, true));
|
||||
|
||||
//return;
|
||||
foreach ($converters as $converter) {
|
||||
if (is_array($converter)) {
|
||||
$converterId = $converter['converter'];
|
||||
$converterOptions = isset($converter['options']) ? $converter['options'] : [];
|
||||
} else {
|
||||
$converterId = $converter;
|
||||
$converterOptions = [];
|
||||
if (isset($options['converter-options'][$converterId])) {
|
||||
// Note: right now, converter-options are not meant to be used,
|
||||
// when you have several converters of the same type
|
||||
$converterOptions = $options['converter-options'][$converterId];
|
||||
}
|
||||
}
|
||||
$converterOptions = array_merge($defaultConverterOptions, $converterOptions);
|
||||
/*
|
||||
if ($converterId != 'stack') {
|
||||
//unset($converterOptions['converters']);
|
||||
//unset($converterOptions['converter-options']);
|
||||
} else {
|
||||
//$converterOptions['converter-options'] =
|
||||
$this->logLn('STACK');
|
||||
$this->logLn('converterOptions: ' . print_r($converterOptions, true));
|
||||
}*/
|
||||
|
||||
$beginTime = microtime(true);
|
||||
|
||||
$this->ln();
|
||||
$this->logLn($converterId . ' converter ignited', 'bold');
|
||||
|
||||
$converter = ConverterFactory::makeConverter(
|
||||
$converterId,
|
||||
$this->source,
|
||||
$this->destination,
|
||||
$converterOptions,
|
||||
$this->logger
|
||||
);
|
||||
|
||||
try {
|
||||
$converter->doConvert();
|
||||
|
||||
//self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
|
||||
|
||||
$this->logLn($converterId . ' succeeded :)');
|
||||
//throw new ConverterNotOperationalException('...');
|
||||
return;
|
||||
} catch (ConverterNotOperationalException $e) {
|
||||
$this->logLn($e->getMessage());
|
||||
} catch (ConversionSkippedException $e) {
|
||||
$this->logLn($e->getMessage());
|
||||
} catch (ConversionFailedException $e) {
|
||||
$this->logLn($e->getMessage(), 'italic');
|
||||
$prev = $e->getPrevious();
|
||||
if (!is_null($prev)) {
|
||||
$this->logLn($prev->getMessage(), 'italic');
|
||||
$this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
|
||||
$this->ln();
|
||||
}
|
||||
//$this->logLn($e->getTraceAsString());
|
||||
$anyRuntimeErrors = true;
|
||||
}
|
||||
$this->logLn($converterId . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
|
||||
}
|
||||
|
||||
$this->ln();
|
||||
$this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
|
||||
|
||||
// Hm, Scrutinizer complains that $anyRuntimeErrors is always false. But that is not true!
|
||||
if ($anyRuntimeErrors) {
|
||||
// At least one converter failed
|
||||
throw new ConversionFailedException(
|
||||
'None of the converters in the stack could convert the image.'
|
||||
);
|
||||
} else {
|
||||
// All converters threw a SystemRequirementsNotMetException
|
||||
throw new ConverterNotOperationalException('None of the converters in the stack are operational');
|
||||
}
|
||||
}
|
||||
}
|
||||
306
vendor/rosell-dk/webp-convert/src/Convert/Converters/Vips.php
vendored
Normal file
306
vendor/rosell-dk/webp-convert/src/Convert/Converters/Vips.php
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
|
||||
//require '/home/rosell/.composer/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* Convert images to webp using Vips extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Vips extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'auto-filter',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
$ssOption = new BooleanOption('smart-subsample', false);
|
||||
$ssOption->markDeprecated();
|
||||
return [
|
||||
$ssOption
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check operationality of Vips converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('vips')) {
|
||||
throw new SystemRequirementsNotMetException('Required Vips extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('vips_image_new_from_file')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_image_new_from_file" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('vips_call')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_call" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('vips_error_buffer')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_error_buffer" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
vips_error_buffer(); // clear error buffer
|
||||
$result = vips_call('webpsave', null);
|
||||
if ($result === -1) {
|
||||
$message = vips_error_buffer();
|
||||
if (strpos($message, 'VipsOperation: class "webpsave" not found') === 0) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips has not been compiled with webp support.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Vips does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
// It seems that png and jpeg are always supported by Vips
|
||||
// - so nothing needs to be done here
|
||||
|
||||
if (function_exists('vips_version')) {
|
||||
$this->logLn('vipslib version: ' . vips_version());
|
||||
}
|
||||
$this->logLn('vips extension version: ' . phpversion('vips'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create vips image resource from source file
|
||||
*
|
||||
* @throws ConversionFailedException if image resource cannot be created
|
||||
* @return resource vips image resource
|
||||
*/
|
||||
private function createImageResource()
|
||||
{
|
||||
// We are currently using vips_image_new_from_file(), but we could consider
|
||||
// calling vips_jpegload / vips_pngload instead
|
||||
$result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []);
|
||||
if ($result === -1) {
|
||||
/*throw new ConversionFailedException(
|
||||
'Failed creating new vips image from file: ' . $this->source
|
||||
);*/
|
||||
$message = /** @scrutinizer ignore-call */ vips_error_buffer();
|
||||
throw new ConversionFailedException($message);
|
||||
}
|
||||
|
||||
if (!is_array($result)) {
|
||||
throw new ConversionFailedException(
|
||||
'vips_image_new_from_file did not return an array, which we expected'
|
||||
);
|
||||
}
|
||||
|
||||
if (count($result) != 1) {
|
||||
throw new ConversionFailedException(
|
||||
'vips_image_new_from_file did not return an array of length 1 as we expected ' .
|
||||
'- length was: ' . count($result)
|
||||
);
|
||||
}
|
||||
|
||||
$im = array_shift($result);
|
||||
return $im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create parameters for webpsave
|
||||
*
|
||||
* @return array the parameters as an array
|
||||
*/
|
||||
private function createParamsForVipsWebPSave()
|
||||
{
|
||||
// webpsave options are described here:
|
||||
// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
|
||||
// near_lossless option is described here: https://github.com/libvips/libvips/pull/430
|
||||
|
||||
// NOTE: When a new option becomes available, we MUST remember to add
|
||||
// it to the array of possibly unsupported options in webpsave() !
|
||||
$options = [
|
||||
"Q" => $this->getCalculatedQuality(),
|
||||
'lossless' => ($this->options['encoding'] == 'lossless'),
|
||||
'strip' => $this->options['metadata'] == 'none',
|
||||
];
|
||||
|
||||
// Only set the following options if they differ from the default of vipslib
|
||||
// This ensures we do not get warning if that property isn't supported
|
||||
if ($this->options['smart-subsample'] !== false) {
|
||||
// PS: The smart-subsample option is now deprecated, as it turned out
|
||||
// it was corresponding to the "sharp-yuv" option (see #280)
|
||||
$options['smart_subsample'] = $this->options['smart-subsample'];
|
||||
$this->logLn(
|
||||
'*Note: the "smart-subsample" option is now deprecated. It turned out it corresponded to ' .
|
||||
'the general option "sharp-yuv". You should use "sharp-yuv" instead.*'
|
||||
);
|
||||
}
|
||||
if ($this->options['sharp-yuv'] !== false) {
|
||||
$options['smart_subsample'] = $this->options['sharp-yuv'];
|
||||
}
|
||||
|
||||
if ($this->options['alpha-quality'] !== 100) {
|
||||
$options['alpha_q'] = $this->options['alpha-quality'];
|
||||
}
|
||||
|
||||
if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) {
|
||||
// preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last
|
||||
$options['preset'] = array_search(
|
||||
$this->options['preset'],
|
||||
['default', 'picture', 'photo', 'drawing', 'icon', 'text']
|
||||
);
|
||||
}
|
||||
if ($this->options['near-lossless'] !== 100) {
|
||||
if ($this->options['encoding'] == 'lossless') {
|
||||
// We only let near_lossless have effect when encoding is set to lossless
|
||||
// otherwise encoding=auto would not work as expected
|
||||
// Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016
|
||||
// seems it corresponds to release 8.4.2
|
||||
$options['near_lossless'] = true;
|
||||
|
||||
// In Vips, the near-lossless value is controlled by Q.
|
||||
// this differs from how it is done in cwebp, where it is an integer.
|
||||
// We have chosen same option syntax as cwebp
|
||||
$options['Q'] = $this->options['near-lossless'];
|
||||
}
|
||||
}
|
||||
if ($this->options['method'] !== 4) {
|
||||
$options['reduction_effort'] = $this->options['method'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save as webp, using vips extension.
|
||||
*
|
||||
* Tries to save image resource as webp, using the supplied options.
|
||||
* Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
|
||||
* (recursively call itself until there is no more of these kind of errors).
|
||||
*
|
||||
* @param resource $im A vips image resource to save
|
||||
* @throws ConversionFailedException if conversion fails.
|
||||
*/
|
||||
private function webpsave($im, $options)
|
||||
{
|
||||
/** @scrutinizer ignore-call */ vips_error_buffer(); // clear error buffer
|
||||
$result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options);
|
||||
|
||||
//trigger_error('test-warning', E_USER_WARNING);
|
||||
if ($result === -1) {
|
||||
$message = /** @scrutinizer ignore-call */ vips_error_buffer();
|
||||
|
||||
$nameOfPropertyNotFound = '';
|
||||
if (preg_match("#no property named .(.*).#", $message, $matches)) {
|
||||
$nameOfPropertyNotFound = $matches[1];
|
||||
} elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) {
|
||||
// Actually, I am not quite sure if this ever happens.
|
||||
// I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning
|
||||
if (in_array($matches[1], [
|
||||
'lossless',
|
||||
'alpha_q',
|
||||
'near_lossless',
|
||||
'smart_subsample',
|
||||
'reduction_effort',
|
||||
'preset'
|
||||
])) {
|
||||
$nameOfPropertyNotFound = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($nameOfPropertyNotFound != '') {
|
||||
$msg = 'Note: Your version of vipslib does not support the "' .
|
||||
$nameOfPropertyNotFound . '" property';
|
||||
|
||||
switch ($nameOfPropertyNotFound) {
|
||||
case 'alpha_q':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'near_lossless':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'smart_subsample':
|
||||
$msg .= ' (its the vips equalent to the "sharp-yuv" option. It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'reduction_effort':
|
||||
$msg .= ' (its the vips equalent to the "method" option. It was introduced in vips 8.8.0)';
|
||||
break;
|
||||
case 'preset':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
}
|
||||
$msg .= '. The option is ignored.';
|
||||
|
||||
|
||||
$this->logLn($msg, 'bold');
|
||||
|
||||
unset($options[$nameOfPropertyNotFound]);
|
||||
$this->webpsave($im, $options);
|
||||
} else {
|
||||
throw new ConversionFailedException($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert, using vips extension.
|
||||
*
|
||||
* Tries to create image resource and save it as webp using the calculated options.
|
||||
* PS: The Vips "webpsave" call fails when a parameter is not supported, but our webpsave() method
|
||||
* detect this and unset that parameter and try again (repeat until success).
|
||||
*
|
||||
* @throws ConversionFailedException if conversion fails.
|
||||
*/
|
||||
protected function doActualConvert()
|
||||
{
|
||||
/*
|
||||
$im = \Jcupitt\Vips\Image::newFromFile($this->source);
|
||||
//$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]);
|
||||
|
||||
$im->webpsave($this->destination, [
|
||||
"Q" => 80,
|
||||
//'near_lossless' => true
|
||||
]);
|
||||
return;*/
|
||||
|
||||
$im = $this->createImageResource();
|
||||
$options = $this->createParamsForVipsWebPSave();
|
||||
$this->webpsave($im, $options);
|
||||
}
|
||||
}
|
||||
415
vendor/rosell-dk/webp-convert/src/Convert/Converters/Wpc.php
vendored
Normal file
415
vendor/rosell-dk/webp-convert/src/Convert/Converters/Wpc.php
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Wpc (a cloud converter based on WebP Convert).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Wpc extends AbstractConverter
|
||||
{
|
||||
use CloudConverterTrait;
|
||||
use CurlTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['api-key', 'string', [
|
||||
'title' => 'API key',
|
||||
'description' => 'The API key is set up on the remote. Copy that.',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
'display' => "option('wpc-api-version') != 0"
|
||||
]
|
||||
]],
|
||||
['secret', 'string', [
|
||||
'title' => 'Secret',
|
||||
'description' => '',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
'display' => "option('wpc-api-version') == 0"
|
||||
]
|
||||
]],
|
||||
['api-url', 'string', [
|
||||
'title' => 'API url',
|
||||
'description' => 'The endpoint of the web service. Copy it from the remote setup',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
]
|
||||
]],
|
||||
['api-version', 'int', [
|
||||
'title' => 'API version',
|
||||
'description' =>
|
||||
'Refers to the major version of Wpc. ' .
|
||||
'It is probably 2, as it is a long time since 2.0 was released',
|
||||
'default' => 2,
|
||||
'minimum' => 0,
|
||||
'maximum' => 2,
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'advanced' => false,
|
||||
'options' => [0, 1, 2],
|
||||
]
|
||||
]],
|
||||
['crypt-api-key-in-transfer', 'boolean', [
|
||||
'title' => 'Crypt API key in transfer',
|
||||
'description' =>
|
||||
'If checked, the api key will be crypted in requests. ' .
|
||||
'Crypting the api-key protects it from being stolen during transfer',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'display' => "option('wpc-api-version') >= 1"
|
||||
]
|
||||
]],
|
||||
]);
|
||||
|
||||
/*return [
|
||||
new SensitiveStringOption('api-key', ''),
|
||||
new SensitiveStringOption('secret', ''),
|
||||
new SensitiveStringOption('api-url', ''),
|
||||
new SensitiveStringOption('url', ''), // DO NOT USE. Only here to keep the protection
|
||||
new IntegerOption('api-version', 2, 0, 2),
|
||||
new BooleanOption('crypt-api-key-in-transfer', false) // new in api v.1
|
||||
];*/
|
||||
}
|
||||
|
||||
public function supportsLossless()
|
||||
{
|
||||
return ($this->options['api-version'] >= 2);
|
||||
}
|
||||
|
||||
public function passOnEncodingAuto()
|
||||
{
|
||||
// We could make this configurable. But I guess passing it on is always to be preferred
|
||||
// for api >= 2.
|
||||
return ($this->options['api-version'] >= 2);
|
||||
}
|
||||
|
||||
private static function createRandomSaltForBlowfish()
|
||||
{
|
||||
$salt = '';
|
||||
$validCharsForSalt = array_merge(
|
||||
range('A', 'Z'),
|
||||
range('a', 'z'),
|
||||
range('0', '9'),
|
||||
['.', '/']
|
||||
);
|
||||
|
||||
for ($i = 0; $i < 22; $i++) {
|
||||
$salt .= $validCharsForSalt[array_rand($validCharsForSalt)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get api key from options or environment variable
|
||||
*
|
||||
* @return string api key or empty string if none is set
|
||||
*/
|
||||
private function getApiKey()
|
||||
{
|
||||
if ($this->options['api-version'] == 0) {
|
||||
if (!empty($this->options['secret'])) {
|
||||
return $this->options['secret'];
|
||||
}
|
||||
} elseif ($this->options['api-version'] >= 1) {
|
||||
if (!empty($this->options['api-key'])) {
|
||||
return $this->options['api-key'];
|
||||
}
|
||||
}
|
||||
if (defined('WEBPCONVERT_WPC_API_KEY')) {
|
||||
return constant('WEBPCONVERT_WPC_API_KEY');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_WPC_API_KEY'))) {
|
||||
return getenv('WEBPCONVERT_WPC_API_KEY');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url from options or environment variable
|
||||
*
|
||||
* @return string URL to WPC or empty string if none is set
|
||||
*/
|
||||
private function getApiUrl()
|
||||
{
|
||||
if (!empty($this->options['api-url'])) {
|
||||
return $this->options['api-url'];
|
||||
}
|
||||
if (defined('WEBPCONVERT_WPC_API_URL')) {
|
||||
return constant('WEBPCONVERT_WPC_API_URL');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
|
||||
return getenv('WEBPCONVERT_WPC_API_URL');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check operationality of Wpc converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met (curl)
|
||||
* @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$apiVersion = $options['api-version'];
|
||||
|
||||
if ($this->getApiUrl() == '') {
|
||||
if (isset($this->options['url']) && ($this->options['url'] != '')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'The "url" option has been renamed to "api-url" in webp-convert 2.0. ' .
|
||||
'You must change the configuration accordingly.'
|
||||
);
|
||||
}
|
||||
throw new ConverterNotOperationalException(
|
||||
'Missing URL. You must install Webp Convert Cloud Service on a server, ' .
|
||||
'or the WebP Express plugin for Wordpress - and supply the url.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($apiVersion == 0) {
|
||||
if (!empty($this->getApiKey())) {
|
||||
// if secret is set, we need md5() and md5_file() functions
|
||||
if (!function_exists('md5')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
|
||||
'contents. ' .
|
||||
'But the required md5() PHP function is not available.'
|
||||
);
|
||||
}
|
||||
if (!function_exists('md5_file')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
|
||||
'contents. But the required md5_file() PHP function is not available.'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($options['crypt-api-key-in-transfer']) {
|
||||
if (!function_exists('crypt')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to crypt the api-key, but crypt() function is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!defined('CRYPT_BLOWFISH')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to crypt the api-key. ' .
|
||||
'That requires Blowfish encryption, which is not available on your current setup.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for curl requirements
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
}
|
||||
|
||||
/*
|
||||
public function checkConvertability()
|
||||
{
|
||||
// check upload limits
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
|
||||
// TODO: some from below can be moved up here
|
||||
}
|
||||
*/
|
||||
|
||||
private function createOptionsToSend()
|
||||
{
|
||||
$optionsToSend = $this->options;
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality was set to "auto", but we could not meassure the quality of the jpeg locally
|
||||
// Ask the cloud service to do it, rather than using what we came up with.
|
||||
$optionsToSend['quality'] = 'auto';
|
||||
} else {
|
||||
$optionsToSend['quality'] = $this->getCalculatedQuality();
|
||||
}
|
||||
|
||||
// The following are unset for security reasons.
|
||||
unset($optionsToSend['converters']);
|
||||
unset($optionsToSend['secret']);
|
||||
unset($optionsToSend['api-key']);
|
||||
unset($optionsToSend['api-url']);
|
||||
|
||||
$apiVersion = $optionsToSend['api-version'];
|
||||
|
||||
if ($apiVersion == 1) {
|
||||
// Lossless can be "auto" in api 2, but in api 1 "auto" is not supported
|
||||
//unset($optionsToSend['lossless']);
|
||||
} elseif ($apiVersion == 2) {
|
||||
//unset($optionsToSend['png']);
|
||||
//unset($optionsToSend['jpeg']);
|
||||
|
||||
// The following are unset for security reasons.
|
||||
unset($optionsToSend['cwebp-command-line-options']);
|
||||
unset($optionsToSend['command-line-options']);
|
||||
}
|
||||
|
||||
return $optionsToSend;
|
||||
}
|
||||
|
||||
private function createPostData()
|
||||
{
|
||||
$options = $this->options;
|
||||
|
||||
$postData = [
|
||||
'file' => curl_file_create($this->source),
|
||||
'options' => json_encode($this->createOptionsToSend()),
|
||||
'servername' => (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')
|
||||
];
|
||||
|
||||
$apiVersion = $options['api-version'];
|
||||
|
||||
$apiKey = $this->getApiKey();
|
||||
|
||||
if ($apiVersion == 0) {
|
||||
$postData['hash'] = md5(md5_file($this->source) . $apiKey);
|
||||
} else {
|
||||
//$this->logLn('api key: ' . $apiKey);
|
||||
|
||||
if ($options['crypt-api-key-in-transfer']) {
|
||||
$salt = self::createRandomSaltForBlowfish();
|
||||
$postData['salt'] = $salt;
|
||||
|
||||
// Strip off the first 28 characters (the first 6 are always "$2y$10$". The next 22 is the salt)
|
||||
$postData['api-key-crypted'] = substr(crypt($apiKey, '$2y$10$' . $salt . '$'), 28);
|
||||
} else {
|
||||
$postData['api-key'] = $apiKey;
|
||||
}
|
||||
}
|
||||
return $postData;
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
//$this->logLn('api url: ' . $this->getApiUrl());
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $this->getApiUrl(),
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $this->createPostData(),
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$this->logLn('Curl error: ', 'bold');
|
||||
$this->logLn(curl_error($ch));
|
||||
throw new ConverterNotOperationalException('Curl error:');
|
||||
}
|
||||
|
||||
// Check if we got a 404
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($httpCode == 404) {
|
||||
curl_close($ch);
|
||||
throw new ConversionFailedException(
|
||||
'WPC was not found at the specified URL - we got a 404 response.'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for empty response
|
||||
if (empty($response)) {
|
||||
throw new ConversionFailedException(
|
||||
'Error: Unexpected result. We got nothing back. ' .
|
||||
'HTTP CODE: ' . $httpCode . '. ' .
|
||||
'Content type:' . curl_getinfo($ch, CURLINFO_CONTENT_TYPE)
|
||||
);
|
||||
};
|
||||
|
||||
// The WPC cloud service either returns an image or an error message
|
||||
// Images has application/octet-stream.
|
||||
// Verify that we got an image back.
|
||||
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
||||
curl_close($ch);
|
||||
|
||||
if (substr($response, 0, 1) == '{') {
|
||||
$responseObj = json_decode($response, true);
|
||||
if (isset($responseObj['errorCode'])) {
|
||||
switch ($responseObj['errorCode']) {
|
||||
case 0:
|
||||
throw new ConverterNotOperationalException(
|
||||
'There are problems with the server setup: "' .
|
||||
$responseObj['errorMessage'] . '"'
|
||||
);
|
||||
case 1:
|
||||
throw new InvalidApiKeyException(
|
||||
'Access denied. ' . $responseObj['errorMessage']
|
||||
);
|
||||
default:
|
||||
throw new ConversionFailedException(
|
||||
'Conversion failed: "' . $responseObj['errorMessage'] . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
|
||||
if (substr($response, 0, 7) == 'failed!') {
|
||||
throw new ConversionFailedException(
|
||||
'WPC failed converting image: "' . substr($response, 7) . '"'
|
||||
);
|
||||
}
|
||||
|
||||
$this->logLn('Bummer, we did not receive an image');
|
||||
$this->log('What we received starts with: "');
|
||||
$this->logLn(
|
||||
str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400)))) . '..."'
|
||||
);
|
||||
|
||||
throw new ConversionFailedException('Unexpected result. We did not receive an image but something else.');
|
||||
//throw new ConverterNotOperationalException($response);
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $response);
|
||||
curl_close($ch);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Error saving file. Check file permissions');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class ConversionSkippedException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'The converter declined converting';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
|
||||
class InvalidApiKeyException extends ConverterNotOperationalException
|
||||
{
|
||||
public $description = 'The converter is not operational (access denied)';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
|
||||
class SystemRequirementsNotMetException extends ConverterNotOperationalException
|
||||
{
|
||||
public $description = 'The converter is not operational (system requirements not met)';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class ConverterNotOperationalException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'The converter is not operational';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
|
||||
|
||||
class CreateDestinationFileException extends FileSystemProblemsException
|
||||
{
|
||||
public $description = 'The converter could not create destination file. Check file permisions!';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
|
||||
|
||||
class CreateDestinationFolderException extends FileSystemProblemsException
|
||||
{
|
||||
public $description = 'The converter could not create destination folder. Check file permisions!';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class FileSystemProblemsException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'Filesystem problems';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class ConverterNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not exist.';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class InvalidImageTypeException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not handle the supplied image type';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class TargetNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter could not locate source file';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Convert/Exceptions/ConversionFailed/InvalidInputException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Convert/Exceptions/ConversionFailed/InvalidInputException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class InvalidInputException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'Invalid input';
|
||||
}
|
||||
31
vendor/rosell-dk/webp-convert/src/Convert/Exceptions/ConversionFailedException.php
vendored
Normal file
31
vendor/rosell-dk/webp-convert/src/Convert/Exceptions/ConversionFailedException.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
/**
|
||||
* ConversionFailedException is the base exception in the hierarchy for conversion errors.
|
||||
*
|
||||
* Exception hierarchy from here:
|
||||
*
|
||||
* WebpConvertException
|
||||
* ConversionFailedException
|
||||
* ConversionSkippedException
|
||||
* ConverterNotOperationalException
|
||||
* InvalidApiKeyException
|
||||
* SystemRequirementsNotMetException
|
||||
* FileSystemProblemsException
|
||||
* CreateDestinationFileException
|
||||
* CreateDestinationFolderException
|
||||
* InvalidInputException
|
||||
* ConverterNotFoundException
|
||||
* InvalidImageTypeException
|
||||
* InvalidOptionValueException
|
||||
* TargetNotFoundException
|
||||
*/
|
||||
class ConversionFailedException extends WebPConvertException
|
||||
{
|
||||
//public $description = 'Conversion failed';
|
||||
public $description = '';
|
||||
}
|
||||
169
vendor/rosell-dk/webp-convert/src/Convert/Helpers/JpegQualityDetector.php
vendored
Normal file
169
vendor/rosell-dk/webp-convert/src/Convert/Helpers/JpegQualityDetector.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Helpers;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
/**
|
||||
* Try to detect quality of a jpeg image using various tools.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class JpegQualityDetector
|
||||
{
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using imagick extension.
|
||||
*
|
||||
* Note that the detection might fail for two different reasons:
|
||||
* 1) Imagick is not installed
|
||||
* 2) Imagick for some reason fails to detect quality for some images
|
||||
*
|
||||
* In both cases, null is returned.
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingImagick($filename)
|
||||
{
|
||||
if (extension_loaded('imagick') && class_exists('\\Imagick')) {
|
||||
try {
|
||||
$img = new \Imagick($filename);
|
||||
|
||||
// The required function is available as from PECL imagick v2.2.2
|
||||
if (method_exists($img, 'getImageCompressionQuality')) {
|
||||
$quality = $img->getImageCompressionQuality();
|
||||
if ($quality === 0) {
|
||||
// We have experienced that this Imagick method returns 0 for some images,
|
||||
// (even though the imagemagick binary is able to detect the quality)
|
||||
// ie "/test/images/quality-undetectable-with-imagick.jpg". See #208
|
||||
$quality = null;
|
||||
}
|
||||
return $quality;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Well well, it just didn't work out.
|
||||
// - But perhaps next method will work...
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using imagick binary.
|
||||
*
|
||||
* Note that the detection might fail for three different reasons:
|
||||
* 1) exec function is not available
|
||||
* 2) the 'identify' command is not available on the system
|
||||
* 3) imagemagick for some reason fails to detect quality for some images
|
||||
*
|
||||
* In the first two cases, null is returned.
|
||||
* In the third case, 92 is returned. This is what imagemagick returns when it cannot detect the quality.
|
||||
* and unfortunately we cannot distinguish between the situation where the quality is undetectable
|
||||
* and the situation where the quality is actually 92 (at least, I have not found a way to do so)
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingImageMagick($filename)
|
||||
{
|
||||
if (ExecWithFallback::anyAvailable()) {
|
||||
// Try Imagick using exec, and routing stderr to stdout (the "2>$1" magic)
|
||||
|
||||
try {
|
||||
ExecWithFallback::exec(
|
||||
"identify -format '%Q' " . escapeshellarg($filename) . " 2>&1",
|
||||
$output,
|
||||
$returnCode
|
||||
);
|
||||
//echo 'out:' . print_r($output, true);
|
||||
if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
|
||||
return intval($output[0]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// its ok, there are other fish in the sea
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using graphicsmagick binary.
|
||||
*
|
||||
* It seems that graphicsmagick is never able to detect the quality! - and always returns
|
||||
* the default quality, which is 75.
|
||||
* However, as this might be solved in future versions, the method might be useful one day.
|
||||
* But we treat "75" as a failure to detect and shall return null in that case.
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingGraphicsMagick($filename)
|
||||
{
|
||||
if (ExecWithFallback::anyAvailable()) {
|
||||
// Try GraphicsMagick
|
||||
try {
|
||||
ExecWithFallback::exec(
|
||||
"gm identify -format '%Q' " . escapeshellarg($filename) . " 2>&1",
|
||||
$output,
|
||||
$returnCode
|
||||
);
|
||||
if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
|
||||
$quality = intval($output[0]);
|
||||
|
||||
// It seems that graphicsmagick is (currently) never able to detect the quality!
|
||||
// - and always returns 75 as a fallback
|
||||
// We shall therefore treat 75 as a failure to detect. (#209)
|
||||
if ($quality == 75) {
|
||||
return null;
|
||||
}
|
||||
return $quality;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg.
|
||||
*
|
||||
* Note: This method does not throw errors, but might dispatch warnings.
|
||||
* You can use the WarningsIntoExceptions class if it is critical to you that nothing gets "printed"
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
public static function detectQualityOfJpg($filename)
|
||||
{
|
||||
|
||||
//trigger_error('warning test', E_USER_WARNING);
|
||||
|
||||
// Test that file exists in order not to break things.
|
||||
if (!file_exists($filename)) {
|
||||
// One could argue that it would be better to throw an Exception...?
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try Imagick extension, if available
|
||||
$quality = self::detectQualityOfJpgUsingImagick($filename);
|
||||
|
||||
if (is_null($quality)) {
|
||||
$quality = self::detectQualityOfJpgUsingImageMagick($filename);
|
||||
}
|
||||
|
||||
if (is_null($quality)) {
|
||||
$quality = self::detectQualityOfJpgUsingGraphicsMagick($filename);
|
||||
}
|
||||
|
||||
return $quality;
|
||||
}
|
||||
}
|
||||
70
vendor/rosell-dk/webp-convert/src/Convert/Helpers/PhpIniSizes.php
vendored
Normal file
70
vendor/rosell-dk/webp-convert/src/Convert/Helpers/PhpIniSizes.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Helpers;
|
||||
|
||||
/**
|
||||
* Get/parse shorthandsize strings from php.ini as bytes.
|
||||
*
|
||||
* Parse strings like "1k" into bytes (1024).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class PhpIniSizes
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse a shordhandsize string as the ones returned by ini_get()
|
||||
*
|
||||
* Parse a shorthandsize string having the syntax allowed in php.ini and returned by ini_get().
|
||||
* Ie "1K" => 1024.
|
||||
* Strings without units are also accepted.
|
||||
* The shorthandbytes syntax is described here: https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
|
||||
*
|
||||
* @param string $shortHandSize A size string of the type returned by ini_get()
|
||||
* @return float|false The parsed size (beware: it is float, do not check high numbers for equality),
|
||||
* or false if parse error
|
||||
*/
|
||||
public static function parseShortHandSize($shortHandSize)
|
||||
{
|
||||
|
||||
$result = preg_match("#^\\s*(\\d+(?:\\.\\d+)?)([bkmgtpezy]?)\\s*$#i", $shortHandSize, $matches);
|
||||
if ($result !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Truncate, because that is what php does.
|
||||
$digitsValue = floor($matches[1]);
|
||||
|
||||
if ((count($matches) >= 3) && ($matches[2] != '')) {
|
||||
$unit = $matches[2];
|
||||
|
||||
// Find the position of the unit in the ordered string which is the power
|
||||
// of magnitude to multiply a kilobyte by.
|
||||
$position = stripos('bkmgtpezy', $unit);
|
||||
|
||||
return floatval($digitsValue * pow(1024, $position));
|
||||
} else {
|
||||
return $digitsValue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the size of an php.ini option.
|
||||
*
|
||||
* Calls ini_get() and parses the size to a number.
|
||||
* If the configuration option is null, does not exist, or cannot be parsed as a shorthandsize, false is returned
|
||||
*
|
||||
* @param string $varname The configuration option name.
|
||||
* @return float|false The parsed size or false if the configuration option does not exist
|
||||
*/
|
||||
public static function getIniBytes($iniVarName)
|
||||
{
|
||||
$iniVarValue = ini_get($iniVarName);
|
||||
if (($iniVarValue == '') || $iniVarValue === false) {
|
||||
return false;
|
||||
}
|
||||
return self::parseShortHandSize($iniVarValue);
|
||||
}
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInput/InvalidImageTypeException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInput/InvalidImageTypeException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions\InvalidInput;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
|
||||
class InvalidImageTypeException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not handle the supplied image type';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInput/TargetNotFoundException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInput/TargetNotFoundException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions\InvalidInput;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
|
||||
class TargetNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter could not locate source file';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInputException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInputException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidInputException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid input';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Exceptions/SanityException.txt
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Exceptions/SanityException.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class SanityException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Sanity check failed';
|
||||
}
|
||||
44
vendor/rosell-dk/webp-convert/src/Exceptions/WebPConvertException.php
vendored
Normal file
44
vendor/rosell-dk/webp-convert/src/Exceptions/WebPConvertException.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
/**
|
||||
* WebPConvertException is the base exception for all exceptions in this library.
|
||||
*
|
||||
* Note that the parameters for the constructor differs from that of the Exception class.
|
||||
* We do not use exception code here, but are instead allowing two version of the error message:
|
||||
* a short version and a long version.
|
||||
* The short version may not contain special characters or dynamic content.
|
||||
* The detailed version may.
|
||||
* If the detailed version isn't provided, getDetailedMessage will return the short version.
|
||||
*
|
||||
*/
|
||||
class WebPConvertException extends \Exception
|
||||
{
|
||||
public $description = '';
|
||||
protected $detailedMessage;
|
||||
protected $shortMessage;
|
||||
|
||||
public function getDetailedMessage()
|
||||
{
|
||||
return $this->detailedMessage;
|
||||
}
|
||||
|
||||
public function getShortMessage()
|
||||
{
|
||||
return $this->shortMessage;
|
||||
}
|
||||
|
||||
public function __construct($shortMessage = "", $detailedMessage = "", $previous = null)
|
||||
{
|
||||
$detailedMessage = ($detailedMessage != '') ? $detailedMessage : $shortMessage;
|
||||
$this->detailedMessage = $detailedMessage;
|
||||
$this->shortMessage = $shortMessage;
|
||||
|
||||
parent::__construct(
|
||||
$detailedMessage,
|
||||
0,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
||||
61
vendor/rosell-dk/webp-convert/src/Helpers/InputValidator.php
vendored
Normal file
61
vendor/rosell-dk/webp-convert/src/Helpers/InputValidator.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Helpers\PathChecker;
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
|
||||
|
||||
/**
|
||||
* Functions for sanitizing.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class InputValidator
|
||||
{
|
||||
|
||||
private static $allowedMimeTypes = [
|
||||
'image/jpeg',
|
||||
'image/png'
|
||||
];
|
||||
|
||||
/**
|
||||
* Check mimetype and if file path is ok and exists
|
||||
*/
|
||||
public static function checkMimeType($filePath, $allowedMimeTypes = null)
|
||||
{
|
||||
if (is_null($allowedMimeTypes)) {
|
||||
$allowedMimeTypes = self::$allowedMimeTypes;
|
||||
}
|
||||
// the following also tests that file path is ok and file exists
|
||||
$fileMimeType = MimeType::getMimeTypeDetectionResult($filePath);
|
||||
|
||||
if (is_null($fileMimeType)) {
|
||||
throw new InvalidImageTypeException('Image type could not be detected');
|
||||
} elseif ($fileMimeType === false) {
|
||||
throw new InvalidImageTypeException('File seems not to be an image.');
|
||||
} elseif (!in_array($fileMimeType, $allowedMimeTypes)) {
|
||||
throw new InvalidImageTypeException('Unsupported mime type: ' . $fileMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkSource($source)
|
||||
{
|
||||
PathChecker::checkSourcePath($source);
|
||||
self::checkMimeType($source);
|
||||
}
|
||||
|
||||
public static function checkDestination($destination)
|
||||
{
|
||||
PathChecker::checkDestinationPath($destination);
|
||||
}
|
||||
|
||||
public static function checkSourceAndDestination($source, $destination)
|
||||
{
|
||||
self::checkSource($source);
|
||||
self::checkDestination($destination);
|
||||
}
|
||||
}
|
||||
40
vendor/rosell-dk/webp-convert/src/Helpers/MimeType.php
vendored
Normal file
40
vendor/rosell-dk/webp-convert/src/Helpers/MimeType.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use ImageMimeTypeGuesser\ImageMimeTypeGuesser;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Get MimeType, results cached by path.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class MimeType
|
||||
{
|
||||
private static $cachedDetections = [];
|
||||
|
||||
/**
|
||||
* Get mime type for image (best guess).
|
||||
*
|
||||
* It falls back to using file extension. If that fails too, false is returned
|
||||
*
|
||||
* @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
|
||||
* false (if it is not an image type that the server knowns about)
|
||||
* or null (if nothing can be determined)
|
||||
*/
|
||||
public static function getMimeTypeDetectionResult($absFilePath)
|
||||
{
|
||||
PathChecker::checkAbsolutePathAndExists($absFilePath);
|
||||
|
||||
if (isset(self::$cachedDetections[$absFilePath])) {
|
||||
return self::$cachedDetections[$absFilePath];
|
||||
}
|
||||
self::$cachedDetections[$absFilePath] = ImageMimeTypeGuesser::lenientGuess($absFilePath);
|
||||
return self::$cachedDetections[$absFilePath];
|
||||
}
|
||||
}
|
||||
115
vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php
vendored
Normal file
115
vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Functions for sanitizing.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class PathChecker
|
||||
{
|
||||
|
||||
/**
|
||||
* Check absolute file path to prevent attacks.
|
||||
*
|
||||
* - Prevents non printable characters
|
||||
* - Prevents stream wrappers
|
||||
* - Prevents directory traversal
|
||||
*
|
||||
* Preventing non printable characters is especially done to prevent the NUL character, which can be used
|
||||
* to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
|
||||
*
|
||||
* Preventeng stream wrappers is especially done to protect against Phar Deserialization.
|
||||
* See https://blog.ripstech.com/2018/new-php-exploitation-technique/
|
||||
*
|
||||
* @param string $absFilePath
|
||||
* @return void
|
||||
*/
|
||||
public static function checkAbsolutePath($absFilePath, $text = 'file')
|
||||
{
|
||||
if (empty($absFilePath)) {
|
||||
throw new InvalidInputException('Empty filepath for ' . $text);
|
||||
}
|
||||
|
||||
// Prevent non printable characters
|
||||
/*
|
||||
if (!ctype_print($absFilePath)) {
|
||||
throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
|
||||
}*/
|
||||
|
||||
// Prevent control characters (at least the first 32 (#0 - #1f)
|
||||
if (preg_match('#[\x{0}-\x{1f}]#', $absFilePath)) {
|
||||
throw new InvalidInputException('Non-printable characters are not allowed');
|
||||
}
|
||||
|
||||
// Prevent directory traversal
|
||||
/* Disabled. We DO allow it again (#203)
|
||||
if (preg_match('#\.\.\/#', $absFilePath)) {
|
||||
throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path');
|
||||
}*/
|
||||
|
||||
// Prevent stream wrappers ("phar://", "php://" and the like)
|
||||
// https://www.php.net/manual/en/wrappers.phar.php
|
||||
if (preg_match('#^\\w+://#', $absFilePath)) {
|
||||
throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkAbsolutePathAndExists($absFilePath, $text = 'file')
|
||||
{
|
||||
if (empty($absFilePath)) {
|
||||
throw new TargetNotFoundException($text . ' argument missing');
|
||||
}
|
||||
self::checkAbsolutePath($absFilePath, $text);
|
||||
if (@!file_exists($absFilePath)) {
|
||||
throw new TargetNotFoundException($text . ' file was not found');
|
||||
}
|
||||
if (@is_dir($absFilePath)) {
|
||||
throw new InvalidInputException($text . ' is a directory');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that source path is secure, file exists and it is not a dir.
|
||||
*
|
||||
* To also check mime type, use InputValidator::checkSource
|
||||
*/
|
||||
public static function checkSourcePath($source)
|
||||
{
|
||||
self::checkAbsolutePathAndExists($source, 'source');
|
||||
}
|
||||
|
||||
public static function checkDestinationPath($destination)
|
||||
{
|
||||
if (empty($destination)) {
|
||||
throw new InvalidInputException('Destination argument missing');
|
||||
}
|
||||
self::checkAbsolutePath($destination, 'destination');
|
||||
|
||||
if (!preg_match('#\.webp$#i', $destination)) {
|
||||
// Prevent overriding important files.
|
||||
// Overriding an .htaccess file would lay down the website.
|
||||
throw new InvalidInputException(
|
||||
'Destination file must end with ".webp". ' .
|
||||
'If you deliberately want to store the webp files with another extension, you must rename ' .
|
||||
'the file after successful conversion'
|
||||
);
|
||||
}
|
||||
|
||||
if (@is_dir($destination)) {
|
||||
throw new InvalidInputException('Destination is a directory');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkSourceAndDestinationPaths($source, $destination)
|
||||
{
|
||||
self::checkSourcePath($source);
|
||||
self::checkDestinationPath($destination);
|
||||
}
|
||||
}
|
||||
30
vendor/rosell-dk/webp-convert/src/Helpers/Sanitize.php
vendored
Normal file
30
vendor/rosell-dk/webp-convert/src/Helpers/Sanitize.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
class Sanitize
|
||||
{
|
||||
|
||||
/**
|
||||
* The NUL character is a demon, because it can be used to bypass other tests
|
||||
* See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
|
||||
*
|
||||
* @param string $string string remove NUL characters in
|
||||
*/
|
||||
public static function removeNUL($string)
|
||||
{
|
||||
return str_replace(chr(0), '', $string);
|
||||
}
|
||||
|
||||
public static function removeStreamWrappers($string)
|
||||
{
|
||||
return preg_replace('#^\\w+://#', '', $string);
|
||||
}
|
||||
|
||||
public static function path($string)
|
||||
{
|
||||
$string = self::removeNUL($string);
|
||||
$string = self::removeStreamWrappers($string);
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
44
vendor/rosell-dk/webp-convert/src/Loggers/BaseLogger.php
vendored
Normal file
44
vendor/rosell-dk/webp-convert/src/Loggers/BaseLogger.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
/**
|
||||
* Base for all logger classes.
|
||||
*
|
||||
* WebPConvert can provide insights into the conversion process by means of accepting a logger which
|
||||
* extends this class.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
abstract class BaseLogger
|
||||
{
|
||||
/**
|
||||
* Write a message to the log
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
abstract public function log($msg, $style = '');
|
||||
|
||||
/**
|
||||
* Add new line to the log
|
||||
* @return void
|
||||
*/
|
||||
abstract public function ln();
|
||||
|
||||
/**
|
||||
* Write a line to the log
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function logLn($msg, $style = '')
|
||||
{
|
||||
$this->log($msg, $style);
|
||||
$this->ln();
|
||||
}
|
||||
}
|
||||
113
vendor/rosell-dk/webp-convert/src/Loggers/BufferLogger.php
vendored
Normal file
113
vendor/rosell-dk/webp-convert/src/Loggers/BufferLogger.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
use WebPConvert\Loggers\BaseLogger;
|
||||
|
||||
/**
|
||||
* Collect the logging and retrieve it later in HTML or plain text format.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class BufferLogger extends BaseLogger
|
||||
{
|
||||
public $entries = array();
|
||||
|
||||
/**
|
||||
* Write a message to the buffer - all entries can later be retrieved with getText() or getHtlm().
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
$this->entries[] = [$msg, $style];
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new line to the buffer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ln()
|
||||
{
|
||||
$this->entries[] = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as HTML.
|
||||
*
|
||||
* @return string The log, formatted as HTML.
|
||||
*/
|
||||
public function getHtml()
|
||||
{
|
||||
$html = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') {
|
||||
$html .= '<br>';
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
$msg = htmlspecialchars($msg);
|
||||
if ($style == 'bold') {
|
||||
$html .= '<b>' . $msg . '</b>';
|
||||
} elseif ($style == 'italic') {
|
||||
$html .= '<i>' . $msg . '</i>';
|
||||
} else {
|
||||
$html .= $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as markdown.
|
||||
*
|
||||
* @return string The log, formatted as MarkDown.
|
||||
*/
|
||||
public function getMarkDown($newLineChar = "\n\r")
|
||||
{
|
||||
$md = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') {
|
||||
$md .= $newLineChar;
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
if ($style == 'bold') {
|
||||
$md .= '**' . $msg . '** ';
|
||||
} elseif ($style == 'italic') {
|
||||
$md .= '*' . $msg . '* ';
|
||||
} else {
|
||||
$md .= $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $md;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as plain text.
|
||||
*
|
||||
* @param string $newLineChar. The character used for new lines.
|
||||
* @return string The log, formatted as plain text.
|
||||
*/
|
||||
public function getText($newLineChar = ' ')
|
||||
{
|
||||
$text = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') { // empty string means new line
|
||||
if (substr($text, -2) != '.' . $newLineChar) {
|
||||
$text .= '.' . $newLineChar;
|
||||
}
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
$text .= $msg;
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
43
vendor/rosell-dk/webp-convert/src/Loggers/EchoLogger.php
vendored
Normal file
43
vendor/rosell-dk/webp-convert/src/Loggers/EchoLogger.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
/**
|
||||
* Echo the logs immediately (in HTML)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class EchoLogger extends BaseLogger
|
||||
{
|
||||
|
||||
/**
|
||||
* Handle log() by echoing the message.
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
$msg = htmlspecialchars($msg);
|
||||
if ($style == 'bold') {
|
||||
echo '<b>' . $msg . '</b>';
|
||||
} elseif ($style == 'italic') {
|
||||
echo '<i>' . $msg . '</i>';
|
||||
} else {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ln by echoing a <br> tag.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ln()
|
||||
{
|
||||
echo '<br>';
|
||||
}
|
||||
}
|
||||
41
vendor/rosell-dk/webp-convert/src/Options/ArrayOption.php
vendored
Normal file
41
vendor/rosell-dk/webp-convert/src/Options/ArrayOption.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ArrayOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'array';
|
||||
protected $schemaType = ['array'];
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('array');
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (count($this->getValue()) == 0) {
|
||||
return '(empty array)';
|
||||
} else {
|
||||
return parent::getValueForPrint();
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = false;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
30
vendor/rosell-dk/webp-convert/src/Options/BooleanOption.php
vendored
Normal file
30
vendor/rosell-dk/webp-convert/src/Options/BooleanOption.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Boolean option
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class BooleanOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'boolean';
|
||||
protected $schemaType = ['boolean'];
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('boolean');
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return ($this->getValue() === true ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/InvalidOptionTypeException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/InvalidOptionTypeException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidOptionTypeException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid option type';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/InvalidOptionValueException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/InvalidOptionValueException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidOptionValueException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid option value';
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/OptionNotFoundException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Options/Exceptions/OptionNotFoundException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class OptionNotFoundException extends WebPConvertException
|
||||
{
|
||||
public $description = '';
|
||||
}
|
||||
24
vendor/rosell-dk/webp-convert/src/Options/GhostOption.php
vendored
Normal file
24
vendor/rosell-dk/webp-convert/src/Options/GhostOption.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Ghost option
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GhostOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'ghost';
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return '(not defined for this converter)';
|
||||
}
|
||||
}
|
||||
76
vendor/rosell-dk/webp-convert/src/Options/IntegerOption.php
vendored
Normal file
76
vendor/rosell-dk/webp-convert/src/Options/IntegerOption.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class IntegerOption extends Option
|
||||
{
|
||||
protected $typeId = 'int';
|
||||
protected $schemaType = ['integer'];
|
||||
protected $minValue;
|
||||
protected $maxValue;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $id id of the option
|
||||
* @param integer $defaultValue default value for the option
|
||||
* @throws InvalidOptionValueException if the default value cannot pass the check
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
|
||||
{
|
||||
$this->minValue = $minValue;
|
||||
$this->maxValue = $maxValue;
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
protected function checkMin()
|
||||
{
|
||||
if (!is_null($this->minValue) && $this->getValue() < $this->minValue) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be set to minimum ' . $this->minValue . '. ' .
|
||||
'It was however set to: ' . $this->getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkMax()
|
||||
{
|
||||
if (!is_null($this->maxValue) && $this->getValue() > $this->maxValue) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be set to max ' . $this->maxValue . '. ' .
|
||||
'It was however set to: ' . $this->getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkMinMax()
|
||||
{
|
||||
$this->checkMin();
|
||||
$this->checkMax();
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('integer');
|
||||
$this->checkMinMax();
|
||||
}
|
||||
|
||||
public function getSchema()
|
||||
{
|
||||
$obj = parent::getSchema();
|
||||
$obj['minimum'] = $this->minValue;
|
||||
$obj['maximum'] = $this->maxValue;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
50
vendor/rosell-dk/webp-convert/src/Options/IntegerOrNullOption.php
vendored
Normal file
50
vendor/rosell-dk/webp-convert/src/Options/IntegerOrNullOption.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class IntegerOrNullOption extends IntegerOption
|
||||
{
|
||||
protected $schemaType = ['integer', 'null'];
|
||||
|
||||
public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
|
||||
{
|
||||
parent::__construct($id, $defaultValue, $minValue, $maxValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkMinMax();
|
||||
|
||||
$valueType = gettype($this->getValue());
|
||||
if (!in_array($valueType, ['integer', 'NULL'])) {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "' . $this->id . '" option must be either integer or NULL. ' .
|
||||
'You however provided a value of type: ' . $valueType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (gettype($this->getValue() == 'NULL')) {
|
||||
return 'null (not set)';
|
||||
}
|
||||
return parent::getValueForPrint();
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
47
vendor/rosell-dk/webp-convert/src/Options/MetadataOption.php
vendored
Normal file
47
vendor/rosell-dk/webp-convert/src/Options/MetadataOption.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Metadata option. A Comma-separated list ('all', 'none', 'exif', 'icc', 'xmp')
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class MetadataOption extends StringOption
|
||||
{
|
||||
|
||||
protected $typeId = 'metadata';
|
||||
protected $schemaType = ['string'];
|
||||
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
|
||||
$value = $this->getValue();
|
||||
|
||||
if (($value == 'all') || ($value == 'none')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (explode(',', $value) as $item) {
|
||||
if (!in_array($value, ['exif', 'icc', 'xmp'])) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"metadata" option must be "all", "none" or a comma-separated list of "exif", "icc" or "xmp". ' .
|
||||
'It was however set to: "' . $value . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//$this->checkType('string');
|
||||
}
|
||||
}
|
||||
254
vendor/rosell-dk/webp-convert/src/Options/Option.php
vendored
Normal file
254
vendor/rosell-dk/webp-convert/src/Options/Option.php
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* (base) option class.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Option
|
||||
{
|
||||
/** @var string The id of the option */
|
||||
protected $id;
|
||||
|
||||
/** @var mixed The default value of the option */
|
||||
protected $defaultValue;
|
||||
|
||||
/** @var mixed The value of the option */
|
||||
protected $value;
|
||||
|
||||
/** @var boolean Whether the value has been set (if not, getValue() will return the default value) */
|
||||
protected $isExplicitlySet = false;
|
||||
|
||||
/** @var string An option must supply a type id */
|
||||
protected $typeId;
|
||||
|
||||
/** @var array Type constraints for the value (JSON schema syntax) */
|
||||
protected $schemaType = [];
|
||||
|
||||
/** @var array|null Array of allowed values (JSON schema syntax) */
|
||||
protected $enum = null; //https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values
|
||||
|
||||
/** @var boolean Whether the option has been deprecated */
|
||||
protected $deprecated = false;
|
||||
|
||||
/** @var string Help text */
|
||||
protected $helpText = '';
|
||||
|
||||
/** @var array UI Def */
|
||||
protected $ui;
|
||||
|
||||
/** @var array|null Extra Schema Def (ie holding 'title', 'description' or other)*/
|
||||
protected $extraSchemaDefs;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $id id of the option
|
||||
* @param mixed $defaultValue default value for the option
|
||||
* @throws InvalidOptionValueException if the default value cannot pass the check
|
||||
* @throws InvalidOptionTypeException if the default value is wrong type
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->defaultValue = $defaultValue;
|
||||
|
||||
// Check that default value is ok
|
||||
$this->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id.
|
||||
*
|
||||
* @return string The id of the option
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id.
|
||||
*
|
||||
* @param string $id The id of the option
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default value.
|
||||
*
|
||||
* @return mixed The default value for the option
|
||||
*/
|
||||
public function getDefaultValue()
|
||||
{
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get value, or default value if value has not been explicitly set.
|
||||
*
|
||||
* @return mixed The value/default value
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
if (!$this->isExplicitlySet) {
|
||||
return $this->defaultValue;
|
||||
} else {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get to know if value has been set.
|
||||
*
|
||||
* @return boolean Whether or not the value has been set explicitly
|
||||
*/
|
||||
public function isValueExplicitlySet()
|
||||
{
|
||||
return $this->isExplicitlySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->isExplicitlySet = true;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is valid.
|
||||
*
|
||||
* This base class does no checking, but this method is overridden by most other options.
|
||||
* @return void
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpful function for checking type - used by subclasses.
|
||||
*
|
||||
* @param string $expectedType The expected type, ie 'string'
|
||||
* @throws InvalidOptionTypeException If the type is invalid
|
||||
* @return void
|
||||
*/
|
||||
protected function checkType($expectedType)
|
||||
{
|
||||
if (gettype($this->getValue()) != $expectedType) {
|
||||
throw new InvalidOptionTypeException(
|
||||
'The "' . $this->id . '" option must be a ' . $expectedType .
|
||||
' (you provided a ' . gettype($this->getValue()) . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function markDeprecated()
|
||||
{
|
||||
$this->deprecated = true;
|
||||
}
|
||||
|
||||
public function isDeprecated()
|
||||
{
|
||||
return $this->deprecated;
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return print_r($this->getValue(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set help text for the option
|
||||
*
|
||||
* @param string $helpText The help text
|
||||
* @return void
|
||||
*/
|
||||
public function setHelpText($helpText)
|
||||
{
|
||||
$this->helpText = $helpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get help text for the option
|
||||
*
|
||||
* @return string $helpText The help text
|
||||
*/
|
||||
public function getHelpText()
|
||||
{
|
||||
return $this->helpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ui definition for the option
|
||||
*
|
||||
* @param array $ui The UI def
|
||||
* @return void
|
||||
*/
|
||||
public function setUI($ui)
|
||||
{
|
||||
$this->ui = $ui;
|
||||
}
|
||||
|
||||
public function setExtraSchemaDefs($def)
|
||||
{
|
||||
$this->extraSchemaDefs = $def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get ui definition for the option
|
||||
*
|
||||
* @return array $ui The UI def
|
||||
*/
|
||||
public function getUI()
|
||||
{
|
||||
return $this->ui;
|
||||
}
|
||||
|
||||
public function getSchema()
|
||||
{
|
||||
if (isset($this->extraSchemaDefs)) {
|
||||
$schema = $this->extraSchemaDefs;
|
||||
} else {
|
||||
$schema = [];
|
||||
}
|
||||
$schema['type'] = $this->schemaType;
|
||||
$schema['default'] = $this->defaultValue;
|
||||
if (!is_null($this->enum)) {
|
||||
$schema['enum'] = $this->enum;
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = [
|
||||
'id' => $this->id,
|
||||
'schema' => $this->getSchema(),
|
||||
'ui' => $this->ui,
|
||||
];
|
||||
if ($this->deprecated) {
|
||||
$obj['deprecated'] = true;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
96
vendor/rosell-dk/webp-convert/src/Options/OptionFactory.php
vendored
Normal file
96
vendor/rosell-dk/webp-convert/src/Options/OptionFactory.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\IntegerOrNullOption;
|
||||
use WebPConvert\Options\MetadataOption;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\QualityOption;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.7.0
|
||||
*/
|
||||
class OptionFactory
|
||||
{
|
||||
|
||||
public static function createOption($optionName, $optionType, $def)
|
||||
{
|
||||
$option = null;
|
||||
switch ($optionType) {
|
||||
case 'int':
|
||||
$minValue = (isset($def['minimum']) ? $def['minimum'] : null);
|
||||
$maxValue = (isset($def['maximum']) ? $def['maximum'] : null);
|
||||
unset($def['minimum']);
|
||||
unset($def['maximum']);
|
||||
if (isset($def['allow-null']) && $def['allow-null']) {
|
||||
$option = new IntegerOrNullOption($optionName, $def['default'], $minValue, $maxValue);
|
||||
} else {
|
||||
if ($optionName == 'quality') {
|
||||
$option = new QualityOption($optionName, $def['default']);
|
||||
} else {
|
||||
$option = new IntegerOption($optionName, $def['default'], $minValue, $maxValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
if ($optionName == 'metadata') {
|
||||
$option = new MetadataOption($optionName, $def['default']);
|
||||
} else {
|
||||
$enum = (isset($def['enum']) ? $def['enum'] : null);
|
||||
if (isset($def['sensitive']) && ($def['sensitive'] == true)) {
|
||||
unset($def['sensitive']);
|
||||
$option = new SensitiveStringOption($optionName, $def['default'], $enum);
|
||||
} else {
|
||||
$option = new StringOption($optionName, $def['default'], $enum);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
$option = new BooleanOption($optionName, $def['default']);
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
if (isset($def['sensitive']) && ($def['sensitive'] == true)) {
|
||||
$option = new SensitiveArrayOption($optionName, $def['default']);
|
||||
} else {
|
||||
$option = new ArrayOption($optionName, $def['default']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
unset($def['default']);
|
||||
|
||||
if (!is_null($option)) {
|
||||
if (isset($def['deprecated'])) {
|
||||
$option->markDeprecated();
|
||||
}
|
||||
if (isset($def['ui'])) {
|
||||
$option->setUI($def['ui']);
|
||||
unset($def['ui']);
|
||||
}
|
||||
}
|
||||
$option->setExtraSchemaDefs($def);
|
||||
return $option;
|
||||
}
|
||||
|
||||
public static function createOptions($def)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($def as $i => list($optionName, $optionType, $optionDef)) {
|
||||
$option = self::createOption($optionName, $optionType, $optionDef);
|
||||
if (!is_null($option)) {
|
||||
$result[] = $option;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
209
vendor/rosell-dk/webp-convert/src/Options/Options.php
vendored
Normal file
209
vendor/rosell-dk/webp-convert/src/Options/Options.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\OptionNotFoundException;
|
||||
|
||||
/**
|
||||
* Handles a collection of options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Options
|
||||
{
|
||||
|
||||
/** @var array A map of options, keyed by their id */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Add option.
|
||||
*
|
||||
* @param Option $option The option object to add to collection.
|
||||
* @return void
|
||||
*/
|
||||
public function addOption($option)
|
||||
{
|
||||
$this->options[$option->getId()] = $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add options.
|
||||
*
|
||||
* Conveniently add several options in one call.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addOptions()
|
||||
{
|
||||
$options = func_get_args();
|
||||
foreach ($options as $option) {
|
||||
$this->addOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
In some years, we can use the splat instead (requires PHP 5.6):
|
||||
@param Option[] ...$options Array of options objects to add
|
||||
public function addOptions(...$options)
|
||||
{
|
||||
foreach ($options as $option) {
|
||||
$this->addOption($option);
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Set the value of an option.
|
||||
*
|
||||
* @param string $id Id of the option
|
||||
* @param mixed $value Value of the option
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($id, $value)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
throw new OptionNotFoundException(
|
||||
'Could not set option. There is no option called "' . $id . '" in the collection.'
|
||||
);
|
||||
}
|
||||
$option = $this->options[$id];
|
||||
$option->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set option, or create a new, if no such option exists.
|
||||
*
|
||||
* @param string $id Id of option to set/create
|
||||
* @param mixed $value Value of option
|
||||
* @return void
|
||||
*/
|
||||
public function setOrCreateOption($id, $value)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
$newOption = new GhostOption($id, null);
|
||||
$newOption->setValue($value);
|
||||
//$newOption = new Option($id, $value);
|
||||
$this->addOption($newOption);
|
||||
} else {
|
||||
$this->setOption($id, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of an option in the collection - by id.
|
||||
*
|
||||
* @deprecated Use getOptionValue() instead
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOption($id)
|
||||
{
|
||||
return $this->getOptionValue($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Option in the collection by id.
|
||||
*
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOptionById($id)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
throw new OptionNotFoundException(
|
||||
'There is no option called "' . $id . '" in the collection.'
|
||||
);
|
||||
}
|
||||
return $this->options[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of an option in the collection - by id.
|
||||
*
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOptionValue($id)
|
||||
{
|
||||
$option = $this->getOptionById($id);
|
||||
return $option->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map of Option objects.
|
||||
*
|
||||
* @return array map of option objects
|
||||
*/
|
||||
public function getOptionsMap()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return flat associative array of options (simple objects).
|
||||
*
|
||||
* @return array associative array of options
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->options as $id => $option) {
|
||||
$values[$id] = $option->getValue();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all options in the collection.
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
foreach ($this->options as $id => $option) {
|
||||
$option->check();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set help texts on multiple options
|
||||
*
|
||||
* @param array $helpTexts Hash of helptexts indexed by option id
|
||||
*/
|
||||
public function setHelpTexts($helpTexts)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if (array_key_exists($option->getId(), $helpTexts)) {
|
||||
$option->setHelpText($helpTexts[$option->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ui definitions on multiple options
|
||||
*
|
||||
* @param array $uis Hash of ui definitions indexed by option id
|
||||
*/
|
||||
public function setUI($uis)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if (array_key_exists($option->getId(), $uis)) {
|
||||
$option->setUI($uis[$option->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefinitions($deprecatedToo = false)
|
||||
{
|
||||
$defs = [];
|
||||
foreach ($this->options as $option) {
|
||||
if ($deprecatedToo || !($option->isDeprecated())) {
|
||||
$defs[] = $option->getDefinition();
|
||||
}
|
||||
}
|
||||
return $defs;
|
||||
}
|
||||
}
|
||||
59
vendor/rosell-dk/webp-convert/src/Options/QualityOption.php
vendored
Normal file
59
vendor/rosell-dk/webp-convert/src/Options/QualityOption.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Quality option.
|
||||
*
|
||||
* Quality can be a number between 0-100 or "auto"
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class QualityOption extends Option
|
||||
{
|
||||
protected $typeId = 'int';
|
||||
protected $schemaType = ['integer', 'string'];
|
||||
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$value = $this->getValue();
|
||||
if (gettype($value) == 'string') {
|
||||
if ($value != 'auto') {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or a number between 0-100. ' .
|
||||
'A string, different from "auto" was given'
|
||||
);
|
||||
}
|
||||
} elseif (gettype($value) == 'integer') {
|
||||
if (($value < 0) || ($value > 100)) {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or a number between 0-100. ' .
|
||||
'The number you provided (' . strval($value) . ') is out of range.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or an integer. ' .
|
||||
'You however provided a value of type: ' . gettype($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (gettype($this->getValue()) == 'string') {
|
||||
return '"' . $this->getValue() . '"';
|
||||
}
|
||||
return $this->getValue();
|
||||
}
|
||||
}
|
||||
39
vendor/rosell-dk/webp-convert/src/Options/SensitiveArrayOption.php
vendored
Normal file
39
vendor/rosell-dk/webp-convert/src/Options/SensitiveArrayOption.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class SensitiveArrayOption extends ArrayOption
|
||||
{
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (count($this->getValue()) == 0) {
|
||||
return '(empty array)';
|
||||
} else {
|
||||
return '(array of ' . count($this->getValue()) . ' items)';
|
||||
}
|
||||
//return '*****';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = true;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
42
vendor/rosell-dk/webp-convert/src/Options/SensitiveStringOption.php
vendored
Normal file
42
vendor/rosell-dk/webp-convert/src/Options/SensitiveStringOption.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class SensitiveStringOption extends StringOption
|
||||
{
|
||||
|
||||
public function __construct($id, $defaultValue, $enum = null)
|
||||
{
|
||||
parent::__construct($id, $defaultValue, $enum);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (strlen($this->getValue()) == 0) {
|
||||
return '""';
|
||||
}
|
||||
return '*****';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = true;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
55
vendor/rosell-dk/webp-convert/src/Options/StringOption.php
vendored
Normal file
55
vendor/rosell-dk/webp-convert/src/Options/StringOption.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class StringOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'string';
|
||||
protected $enum;
|
||||
protected $schemaType = ['string'];
|
||||
|
||||
public function __construct($id, $defaultValue, $enum = null)
|
||||
{
|
||||
$this->enum = $enum;
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('string');
|
||||
|
||||
if (!is_null($this->enum) && (!in_array($this->getValue(), $this->enum))) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be on of these values: ' .
|
||||
'[' . implode(', ', $this->enum) . ']. ' .
|
||||
'It was however set to: "' . $this->getValue() . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return '"' . $this->getValue() . '"';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = false;
|
||||
if (!is_null($this->enum)) {
|
||||
$obj['options'] = $this->enum;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
10
vendor/rosell-dk/webp-convert/src/Serve/Exceptions/ServeFailedException.php
vendored
Normal file
10
vendor/rosell-dk/webp-convert/src/Serve/Exceptions/ServeFailedException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Serve\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class ServeFailedException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Failed serving';
|
||||
}
|
||||
51
vendor/rosell-dk/webp-convert/src/Serve/Header.php
vendored
Normal file
51
vendor/rosell-dk/webp-convert/src/Serve/Header.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
/**
|
||||
* Add / Set HTTP header.
|
||||
*
|
||||
* This class does nothing more than adding two convenience functions for calling the "header" function.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Header
|
||||
{
|
||||
/**
|
||||
* Convenience function for adding header (append).
|
||||
*
|
||||
* @param string $header The header to add.
|
||||
* @return void
|
||||
*/
|
||||
public static function addHeader($header)
|
||||
{
|
||||
header($header, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for replacing header.
|
||||
*
|
||||
* @param string $header The header to set.
|
||||
* @return void
|
||||
*/
|
||||
public static function setHeader($header)
|
||||
{
|
||||
header($header, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add log header and optionally send it to a logger as well.
|
||||
*
|
||||
* @param string $msg Message to add to "X-WebP-Convert-Log" header
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
* @return void
|
||||
*/
|
||||
public static function addLogHeader($msg, $logger = null)
|
||||
{
|
||||
self::addHeader('X-WebP-Convert-Log: ' . $msg);
|
||||
if (!is_null($logger)) {
|
||||
$logger->logLn($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/rosell-dk/webp-convert/src/Serve/Report.php
vendored
Normal file
54
vendor/rosell-dk/webp-convert/src/Serve/Report.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Loggers\EchoLogger;
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
/**
|
||||
* Class for generating a HTML report of converting an image.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Report
|
||||
{
|
||||
public static function convertAndReport($source, $destination, $options)
|
||||
{
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<style>td {vertical-align: top} table {color: #666}</style>
|
||||
<script>
|
||||
function showOptions(elToHide) {
|
||||
document.getElementById('options').style.display='block';
|
||||
elToHide.style.display='none';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr><td><i>source:</i></td><td><?php echo htmlentities($source) ?></td></tr>
|
||||
<tr><td><i>destination:</i></td><td><?php echo htmlentities($destination) ?><td></tr>
|
||||
</table>
|
||||
<br>
|
||||
<?php
|
||||
try {
|
||||
$echoLogger = new EchoLogger();
|
||||
$options['log-call-arguments'] = true;
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $echoLogger);
|
||||
} catch (\Exception $e) {
|
||||
$msg = $e->getMessage();
|
||||
echo '<b>' . $msg . '</b>';
|
||||
|
||||
//echo '<p>Rethrowing exception for your convenience</p>';
|
||||
//throw ($e);
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
216
vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebP.php
vendored
Normal file
216
vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebP.php
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Helpers\PathChecker;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Serve\ServeFile;
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\SensitiveArrayOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
/**
|
||||
* Serve a converted webp image.
|
||||
*
|
||||
* The webp that is served might end up being one of these:
|
||||
* - a fresh convertion
|
||||
* - the destionation
|
||||
* - the original
|
||||
*
|
||||
* Exactly which is a decision based upon options, file sizes and file modification dates
|
||||
* (see the serve method of this class for details)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeConvertedWebP
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new BooleanOption('reconvert', false),
|
||||
new BooleanOption('serve-original', false),
|
||||
new BooleanOption('show-report', false),
|
||||
new BooleanOption('suppress-warnings', true),
|
||||
new BooleanOption('redirect-to-self-instead-of-serving', false),
|
||||
new ArrayOption('serve-image', []),
|
||||
new SensitiveArrayOption('convert', [])
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
return $options2->getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve original file (source).
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param array $serveImageOptions (optional) options for serving an image
|
||||
* Supported options:
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @throws ServeFailedException if source is not an image or mime type cannot be determined
|
||||
* @return void
|
||||
*/
|
||||
public static function serveOriginal($source, $serveImageOptions = [])
|
||||
{
|
||||
// PS: We do not use InputValidator::checkSource($source) because we want to be
|
||||
// a bit more lenient here and allow any image to be served (even though ie webp does not
|
||||
// qualify for being used as a source when converting)
|
||||
|
||||
// Check that the filename is ok (no control chars, streamwrappers), and that the file exists
|
||||
// and is not a dir
|
||||
PathChecker::checkSourcePath($source);
|
||||
|
||||
$contentType = MimeType::getMimeTypeDetectionResult($source);
|
||||
if (is_null($contentType)) {
|
||||
throw new ServeFailedException('Rejecting to serve original (mime type cannot be determined)');
|
||||
} elseif ($contentType === false) {
|
||||
throw new ServeFailedException('Rejecting to serve original (it is not an image)');
|
||||
} else {
|
||||
ServeFile::serve($source, $contentType, $serveImageOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve destination file.
|
||||
*
|
||||
* TODO: SHould this really be public?
|
||||
*
|
||||
* @param string $destination path to destination file
|
||||
* @param array $serveImageOptions (optional) options for serving (such as which headers to add)
|
||||
* Supported options:
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @return void
|
||||
*/
|
||||
public static function serveDestination($destination, $serveImageOptions = [])
|
||||
{
|
||||
InputValidator::checkDestination($destination);
|
||||
ServeFile::serve($destination, 'image/webp', $serveImageOptions);
|
||||
}
|
||||
|
||||
|
||||
public static function warningHandler()
|
||||
{
|
||||
// do nothing! - as we do not return anything, the warning is suppressed
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve converted webp.
|
||||
*
|
||||
* Serve a converted webp. If a file already exists at the destination, that is served (unless it is
|
||||
* older than the source - in that case a fresh conversion will be made, or the file at the destination
|
||||
* is larger than the source - in that case the source is served). Some options may alter this logic.
|
||||
* In case no file exists at the destination, a fresh conversion is made and served.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* Supported options:
|
||||
* 'show-report' => (boolean) If true, the decision will always be 'report'
|
||||
* 'serve-original' => (boolean) If true, the decision will be 'source' (unless above option is set)
|
||||
* 'reconvert ' => (boolean) If true, the decision will be 'fresh-conversion' (unless one of the
|
||||
* above options is set)
|
||||
* - All options supported by WebPConvert::convert()
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
*
|
||||
* @throws \WebPConvert\Exceptions\WebPConvertException If something went wrong.
|
||||
* @return void
|
||||
*/
|
||||
public static function serve($source, $destination, $options = [], $serveLogger = null, $convertLogger = null)
|
||||
{
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
|
||||
$options = self::processOptions($options);
|
||||
|
||||
if ($options['suppress-warnings']) {
|
||||
set_error_handler(
|
||||
array('\\WebPConvert\\Serve\\ServeConvertedWebP', "warningHandler"),
|
||||
E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//$options = array_merge(self::$defaultOptions, $options);
|
||||
|
||||
// Step 1: Is there a file at the destination? If not, trigger conversion
|
||||
// However 1: if "show-report" option is set, serve the report instead
|
||||
// However 2: "reconvert" option should also trigger conversion
|
||||
if ($options['show-report']) {
|
||||
Header::addLogHeader('Showing report', $serveLogger);
|
||||
Report::convertAndReport($source, $destination, $options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!@file_exists($destination)) {
|
||||
Header::addLogHeader('Converting (there were no file at destination)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
} elseif ($options['reconvert']) {
|
||||
Header::addLogHeader('Converting (told to reconvert)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
} else {
|
||||
// Step 2: Is the destination older than the source?
|
||||
// If yes, trigger conversion (deleting destination is implicit)
|
||||
$timestampSource = @filemtime($source);
|
||||
$timestampDestination = @filemtime($destination);
|
||||
if (($timestampSource !== false) &&
|
||||
($timestampDestination !== false) &&
|
||||
($timestampSource > $timestampDestination)) {
|
||||
Header::addLogHeader('Converting (destination was older than the source)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Serve the smallest file (destination or source)
|
||||
// However, first check if 'serve-original' is set
|
||||
if ($options['serve-original']) {
|
||||
Header::addLogHeader('Serving original (told to)', $serveLogger);
|
||||
self::serveOriginal($source, $options['serve-image']);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($options['redirect-to-self-instead-of-serving']) {
|
||||
Header::addLogHeader(
|
||||
'Redirecting to self! ' .
|
||||
'(hope you got redirection to existing webps set up, otherwise you will get a loop!)',
|
||||
$serveLogger
|
||||
);
|
||||
header('Location: ?fresh', 302);
|
||||
return;
|
||||
}
|
||||
|
||||
$filesizeDestination = @filesize($destination);
|
||||
$filesizeSource = @filesize($source);
|
||||
if (($filesizeSource !== false) &&
|
||||
($filesizeDestination !== false) &&
|
||||
($filesizeDestination > $filesizeSource)) {
|
||||
Header::addLogHeader('Serving original (it is smaller)', $serveLogger);
|
||||
self::serveOriginal($source, $options['serve-image']);
|
||||
return;
|
||||
}
|
||||
|
||||
Header::addLogHeader('Serving converted file', $serveLogger);
|
||||
self::serveDestination($destination, $options['serve-image']);
|
||||
}
|
||||
}
|
||||
160
vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebPWithErrorHandling.php
vendored
Normal file
160
vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebPWithErrorHandling.php
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Serve\ServeConvertedWeb;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
/**
|
||||
* Serve a converted webp image and handle errors.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeConvertedWebPWithErrorHandling
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new StringOption('fail', 'original', ['original', '404', 'throw', 'report']),
|
||||
new StringOption('fail-when-fail-fails', 'throw', ['original', '404', 'throw', 'report'])
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
return $options2->getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add headers for preventing caching.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function addHeadersPreventingCaching()
|
||||
{
|
||||
Header::setHeader("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
||||
Header::addHeader("Cache-Control: post-check=0, pre-check=0");
|
||||
Header::setHeader("Pragma: no-cache");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform fail action.
|
||||
*
|
||||
* @param string $fail Action to perform (original | 404 | report)
|
||||
* @param string $failIfFailFails Action to perform if $fail action fails
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* @param \Exception $e exception that was thrown when trying to serve
|
||||
* @param string $serveClass (optional) Full class name to a class that has a serveOriginal() method
|
||||
* @return void
|
||||
*/
|
||||
public static function performFailAction($fail, $failIfFailFails, $source, $destination, $options, $e, $serveClass)
|
||||
{
|
||||
self::addHeadersPreventingCaching();
|
||||
|
||||
Header::addLogHeader('Performing fail action: ' . $fail);
|
||||
|
||||
switch ($fail) {
|
||||
case 'original':
|
||||
try {
|
||||
//ServeConvertedWebP::serveOriginal($source, $options);
|
||||
call_user_func($serveClass . '::serveOriginal', $source, $options);
|
||||
} catch (\Exception $e) {
|
||||
self::performFailAction($failIfFailFails, '404', $source, $destination, $options, $e, $serveClass);
|
||||
}
|
||||
break;
|
||||
|
||||
case '404':
|
||||
$protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
|
||||
Header::setHeader($protocol . " 404 Not Found");
|
||||
break;
|
||||
|
||||
case 'report':
|
||||
$options['show-report'] = true;
|
||||
Report::convertAndReport($source, $destination, $options);
|
||||
break;
|
||||
|
||||
case 'throw':
|
||||
throw $e;
|
||||
//break; commented out as phpstan complains. But do something else complain now?
|
||||
|
||||
case 'report-as-image':
|
||||
// TODO: Implement or discard ?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve webp image and handle errors as specified in the 'fail' option.
|
||||
*
|
||||
* This method basically wraps ServeConvertedWebP:serve in order to provide exception handling.
|
||||
* The error handling is set with the 'fail' option and can be either '404', 'original' or 'report'.
|
||||
* If set to '404', errors results in 404 Not Found headers being issued. If set to 'original', an
|
||||
* error results in the original being served.
|
||||
* Look up the ServeConvertedWebP:serve method to learn more.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* Supported options:
|
||||
* - 'fail' => (string) Action to take on failure (404 | original | report | throw).
|
||||
* "404" or "throw" is recommended for development and "original" is recommended for production.
|
||||
* Default: 'original'.
|
||||
* - 'fail-when-fail-fails' => (string) Action to take if fail action also fails. Default: '404'.
|
||||
* - All options supported by WebPConvert::convert()
|
||||
* - All options supported by ServeFile::serve()
|
||||
* - All options supported by DecideWhatToServe::decide)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
* @param string $serveClass (optional) Full class name to a class that has a serve() method and a
|
||||
* serveOriginal() method
|
||||
* @return void
|
||||
*/
|
||||
public static function serve(
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$serveLogger = null,
|
||||
$convertLogger = null,
|
||||
$serveClass = '\\WebPConvert\\Serve\\ServeConvertedWebP'
|
||||
) {
|
||||
|
||||
$options = self::processOptions($options);
|
||||
try {
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
//ServeConvertedWebP::serve($source, $destination, $options, $serveLogger);
|
||||
call_user_func($serveClass . '::serve', $source, $destination, $options, $serveLogger, $convertLogger);
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof \WebPConvert\Exceptions\WebPConvertException) {
|
||||
Header::addLogHeader($e->getShortMessage(), $serveLogger);
|
||||
}
|
||||
|
||||
self::performFailAction(
|
||||
$options['fail'],
|
||||
$options['fail-when-fail-fails'],
|
||||
$source,
|
||||
$destination,
|
||||
$options,
|
||||
$e,
|
||||
$serveClass
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
vendor/rosell-dk/webp-convert/src/Serve/ServeFile.php
vendored
Normal file
133
vendor/rosell-dk/webp-convert/src/Serve/ServeFile.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
//use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
|
||||
/**
|
||||
* Serve a file (send to standard output)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeFile
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new ArrayOption('headers', []),
|
||||
new StringOption('cache-control-header', 'public, max-age=31536000')
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
$options = $options2->getOptions();
|
||||
|
||||
// headers option
|
||||
// --------------
|
||||
|
||||
$headerOptions = new Options();
|
||||
$headerOptions->addOptions(
|
||||
new BooleanOption('cache-control', false),
|
||||
new BooleanOption('content-length', true),
|
||||
new BooleanOption('content-type', true),
|
||||
new BooleanOption('expires', false),
|
||||
new BooleanOption('last-modified', true),
|
||||
new BooleanOption('vary-accept', false)
|
||||
);
|
||||
foreach ($options['headers'] as $optionId => $optionValue) {
|
||||
$headerOptions->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options['headers'] = $headerOptions->getOptions();
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve existing file.
|
||||
*
|
||||
* @param string $filename File to serve (absolute path)
|
||||
* @param string $contentType Content-type (used to set header).
|
||||
* Only used when the "set-content-type-header" option is set.
|
||||
* Set to ie "image/jpeg" for serving jpeg file.
|
||||
* @param array $options Array of named options (optional).
|
||||
* Supported options:
|
||||
* 'add-vary-accept-header' => (boolean) Whether to add *Vary: Accept* header or not. Default: true.
|
||||
* 'set-content-type-header' => (boolean) Whether to set *Content-Type* header or not. Default: true.
|
||||
* 'set-last-modified-header' => (boolean) Whether to set *Last-Modified* header or not. Default: true.
|
||||
* 'set-cache-control-header' => (boolean) Whether to set *Cache-Control* header or not. Default: true.
|
||||
* 'cache-control-header' => string Cache control header. Default: "public, max-age=86400"
|
||||
*
|
||||
* @throws ServeFailedException if serving failed
|
||||
* @return void
|
||||
*/
|
||||
public static function serve($filename, $contentType, $options = [])
|
||||
{
|
||||
// Check mimetype - this also checks that path is secure and file exists
|
||||
InputValidator::checkMimeType($filename, [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/gif'
|
||||
]);
|
||||
|
||||
/*
|
||||
if (!file_exists($filename)) {
|
||||
Header::addHeader('X-WebP-Convert-Error: Could not read file');
|
||||
throw new ServeFailedException('Could not read file');
|
||||
}*/
|
||||
|
||||
$options = self::processOptions($options);
|
||||
|
||||
if ($options['headers']['last-modified']) {
|
||||
Header::setHeader("Last-Modified: " . gmdate("D, d M Y H:i:s", @filemtime($filename)) . " GMT");
|
||||
}
|
||||
|
||||
if ($options['headers']['content-type']) {
|
||||
Header::setHeader('Content-Type: ' . $contentType);
|
||||
}
|
||||
|
||||
if ($options['headers']['vary-accept']) {
|
||||
Header::addHeader('Vary: Accept');
|
||||
}
|
||||
|
||||
if (!empty($options['cache-control-header'])) {
|
||||
if ($options['headers']['cache-control']) {
|
||||
Header::setHeader('Cache-Control: ' . $options['cache-control-header']);
|
||||
}
|
||||
if ($options['headers']['expires']) {
|
||||
// Add exprires header too (#126)
|
||||
// Check string for something like this: max-age:86400
|
||||
if (preg_match('#max-age\\s*=\\s*(\\d*)#', $options['cache-control-header'], $matches)) {
|
||||
$seconds = $matches[1];
|
||||
Header::setHeader('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + intval($seconds)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['headers']['content-length']) {
|
||||
Header::setHeader('Content-Length: ' . filesize($filename));
|
||||
}
|
||||
|
||||
if (@readfile($filename) === false) {
|
||||
Header::addHeader('X-WebP-Convert-Error: Could not read file');
|
||||
throw new ServeFailedException('Could not read file');
|
||||
}
|
||||
}
|
||||
}
|
||||
159
vendor/rosell-dk/webp-convert/src/WebPConvert.php
vendored
Normal file
159
vendor/rosell-dk/webp-convert/src/WebPConvert.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert;
|
||||
|
||||
//use WebPConvert\Convert\Converters\ConverterHelper;
|
||||
use WebPConvert\Convert\Converters\Stack;
|
||||
//use WebPConvert\Serve\ServeExistingOrHandOver;
|
||||
use WebPConvert\Convert\ConverterFactory;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use WebPConvert\Serve\ServeConvertedWebP;
|
||||
use WebPConvert\Serve\ServeConvertedWebPWithErrorHandling;
|
||||
|
||||
/**
|
||||
* Convert images to webp and/or serve them.
|
||||
*
|
||||
* This class is just a couple of convenience methods for doing conversion and/or
|
||||
* serving.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class WebPConvert
|
||||
{
|
||||
|
||||
/**
|
||||
* Convert jpeg or png into webp
|
||||
*
|
||||
* Convenience method for calling Stack::convert.
|
||||
*
|
||||
* @param string $source The image to convert (absolute,no backslashes)
|
||||
* Image must be jpeg or png.
|
||||
* @param string $destination Where to store the converted file (absolute path, no backslashes).
|
||||
* @param array $options (optional) Array of named options
|
||||
* The options are documented here:
|
||||
* https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws \WebPConvert\Convert\Exceptions\ConversionFailedException in case conversion fails
|
||||
* @return void
|
||||
*/
|
||||
public static function convert($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
if (isset($options['converter'])) {
|
||||
$converter = $options['converter'];
|
||||
unset($options['converter']);
|
||||
$c = ConverterFactory::makeConverter($converter, $source, $destination, $options, $logger);
|
||||
$c->doConvert();
|
||||
} else {
|
||||
Stack::convert($source, $destination, $options, $logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve webp image, converting first if neccessary.
|
||||
*
|
||||
* If an image already exists, it will be served, unless it is older or larger than the source. (If it is larger,
|
||||
* the original is served, if it is older, the existing webp will be deleted and a fresh conversion will be made
|
||||
* and served). In case of error, the action indicated in the 'fail' option will be triggered (default is to serve
|
||||
* the original). Look up the ServeConvertedWebP:serve() and the ServeConvertedWebPWithErrorHandling::serve()
|
||||
* methods to learn more.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting. The options are documented in the
|
||||
* ServeConvertedWebPWithErrorHandling::serve() method
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
* @return void
|
||||
*/
|
||||
public static function serveConverted(
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$serveLogger = null,
|
||||
$convertLogger = null
|
||||
) {
|
||||
//return ServeExistingOrHandOver::serveConverted($source, $destination, $options);
|
||||
//if (isset($options['handle-errors']) && $options['handle-errors'] === true) {
|
||||
if (isset($options['fail']) && ($options['fail'] != 'throw')) {
|
||||
ServeConvertedWebPWithErrorHandling::serve($source, $destination, $options, $serveLogger, $convertLogger);
|
||||
} else {
|
||||
ServeConvertedWebP::serve($source, $destination, $options, $serveLogger, $convertLogger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all converters available in webp-convert.
|
||||
*
|
||||
* @return array Array of ids.
|
||||
*/
|
||||
public static function getConverterIds()
|
||||
{
|
||||
$all = Stack::getAvailableConverters();
|
||||
$all[] = 'stack';
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get option definitions for all converters
|
||||
*
|
||||
* Added in order to give GUI's a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public static function getConverterOptionDefinitions($filterOutOptionsWithoutUI = true)
|
||||
{
|
||||
$converterIds = self::getConverterIds();
|
||||
$result = [];
|
||||
|
||||
$ewww = ConverterFactory::makeConverter('ewww', '', '');
|
||||
$result['general'] = $ewww->getGeneralOptionDefinitions($filterOutOptionsWithoutUI);
|
||||
|
||||
$generalOptionHash = [];
|
||||
$generalOptionIds = [];
|
||||
foreach ($result['general'] as &$option) {
|
||||
$generalOptionIds[] = $option['id'];
|
||||
$option['unsupportedBy'] = [];
|
||||
$generalOptionHash[$option['id']] = &$option;
|
||||
}
|
||||
//$result['general'] = $generalOptionIds;
|
||||
array_unshift($result['general'], OptionFactory::createOption('converter', 'string', [
|
||||
'title' => 'Converter',
|
||||
'description' => 'Conversion method. ' .
|
||||
"Cwebp and vips are best. " .
|
||||
'the *magick are nearly as good, but only recent versions supports near-lossless. ' .
|
||||
'gd is poor, as it does not support any webp options. ' .
|
||||
'For full discussion, check the guide',
|
||||
'default' => 'stack',
|
||||
'enum' => $converterIds,
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'links' => [
|
||||
[
|
||||
'Guide',
|
||||
'https://github.com/rosell-dk/webp-convert/blob/master/docs/v1.3/converting/converters.md'
|
||||
]
|
||||
],
|
||||
]
|
||||
])->getDefinition());
|
||||
|
||||
$supportedBy = [];
|
||||
$uniqueOptions = [];
|
||||
|
||||
foreach ($converterIds as $converterId) {
|
||||
$c = ConverterFactory::makeConverter($converterId, '', '');
|
||||
foreach ($c->getUnsupportedGeneralOptions() as $optionId) {
|
||||
$generalOptionHash[$optionId]['unsupportedBy'][] = $converterId;
|
||||
}
|
||||
$optionDefinitions = $c->getUniqueOptionDefinitions($filterOutOptionsWithoutUI);
|
||||
$uniqueOptions[$converterId] = $optionDefinitions;
|
||||
}
|
||||
$result['unique'] = $uniqueOptions;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user