Add X13 WebP module for image conversion to next-generation formats

- Implemented the main module class with essential properties and methods.
- Added translation support for various user interface strings.
- Created XML configuration file for module versioning.
- Ensured compatibility with different PHP versions and PrestaShop versions.
This commit is contained in:
2025-10-16 21:30:24 +02:00
parent d220e35c31
commit 3b29a79921
235 changed files with 40220 additions and 189 deletions

View File

@@ -0,0 +1,181 @@
# Tweaks
## Store converted images in separate folder
In most cases, you probably want the cache of converted images to be stored in their own folder rather than have them mingled with the source files.
To have have the cache folder contain a file structure mirroring the structure of the original files, you can do this:
```php
$applicationRoot = $_SERVER["DOCUMENT_ROOT"]; // If your application is not in document root, you can change accordingly.
$imageRoot = $applicationRoot . '/webp-images'; // Change to where you want the webp images to be saved
$sourceRel = substr($source, strlen($applicationRoot));
$destination = $imageRoot . $sourceRel . '.webp';
```
If your images are stored outside document root (a rare case), you can simply use the complete absolute path:
```php
$destination = $imageRoot . $source . '.webp'; // pst: $source is an absolute path, and starts with '/'
```
This will ie store a converted image in */var/www/example.com/public_html/app/webp-images/var/www/example.com/images/logo.jpg.webp*
If your application can be configured to store outside document root, but rarely is, you can go for this structure:
```php
$docRoot = $_SERVER["DOCUMENT_ROOT"];
$imageRoot = $contentDirAbs . '/webp-images';
if (substr($source, 0, strlen($docRoot)) === $docRoot) {
// Source file is residing inside document root.
// We can store relative to that.
$sourceRel = substr($source, strlen($docRoot));
$destination = $imageRoot . '/doc-root' . $sourceRel . '.webp';
} else {
// Source file is residing outside document root.
// we must add complete path to structure
$destination = $imageRoot . '/abs' . $source . '.webp';
}
```
If you do not know the application root beforehand, and thus do not know the appropriate root for the converted images, see next tweak.
## Get the application root automatically
When you want destination files to be put in their own folder, you need to know the root of the application (the folder in which the .htaccess rules resides). In most applications, you know the root. In many cases, it is simply the document root. However, if you are writing an extension, plugin or module to a framework that can be installed in a subfolder, you may have trouble finding it. Many applications have a *index.php* in the root, which can get it with `__DIR__`. However, you do not want to run an entire bootstrap each time you serve an image. Obviously, to get around this, you can place *webp-on-demand.php* in the webroot. However, some frameworks, such as Wordpress, will not allow a plugin to put a file in the root. Now, how could we determine the application root from a file inside some subdir? Here are three suggestions:
1. You could traverse parent folders until you find a file you expect to be in application root (ie a .htaccess containing the string "webp-on-demand.php"). This should work.
2. If the rules in the *.htaccess* file are generated by your application, you probably have access to the path at generation time. You can then simply put the path in the *.htaccess*, as an extra parameter to the script (or better: the relative path from document root to the application).
3. You can use the following hack:
### The hack
The idea is to grab the URL path of the image in the *.htaccess* and pass it to the script. Assuming that the URL paths always matches the file paths, we can get the application root by subtracting that relative path to source from the absolute path to source.
In *.htaccess*, we grab the url-path by appending "&url-path=$1.$2" to the rewrite rule:
```
RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php?source=%{SCRIPT_FILENAME}&url-path=$1.$2 [NC,L]
```
In the script, we can then calculate the application root like this:
```php
$applicationRoot = substr($_GET['source'], 0, -strlen($_GET['url-path']));
```
## CDN
To work properly with a CDN, a "Vary Accept" header should be added when serving images. This is a declaration that the response varies with the *Accept* header (recall that we inspect *Accept* header in the .htaccess to determine if the browsers supports webp images). If this header is missing, the CDN will see no reason to cache separate images depending on the Accept header.
Add this snippet to the *.htaccess* to make webp-on-demand work with CDN's:
```
<IfModule mod_headers.c>
SetEnvIf Request_URI "\.(jpe?g|png)" ADDVARY
# Declare that the response varies depending on the accept header.
# The purpose is to make CDN cache both original images and converted images.
Header append "Vary" "Accept" env=ADDVARY
</IfModule>
```
***Note:*** When configuring the CDN, you must make sure to set it up to forward the the "Accept" header to your origin server.
## Make .htaccess route directly to existing images
There may be a performance benefit of using the *.htaccess* file to route to already converted images, instead of letting the PHP script serve it. Note however:
- If you do the routing in .htaccess, the solution will not be able to discard converted images when original images are updated.
- Performance benefit may be insignificant (*WebPConvertAndServe* class is not autoloaded when serving existing images)
Add the following to the *.htaccess* to make it route to existing converted images. Place it above the # Redirect images to webp-on-demand.php" comment. Take care of replacing [[your-base-path]] with the directory your *.htaccess* lives in (relative to document root, and [[your-destination-root]] with the directory the converted images resides.
```
# Redirect to existing converted image (under appropriate circumstances)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{DOCUMENT_ROOT}/[[your-base-path]]/[[your-destination-root]]/$1.$2.webp -f
RewriteRule ^\/?(.*)\.(jpe?g|png)$ /[[your-base-path]]/[[your-destination-root]]/$1.$2.webp [NC,T=image/webp,L]
```
*edit:* Removed the QSD flag from the RewriteRule because it is not supported in Apache < 2.4 (and it [triggers error](https://github.com/rosell-dk/webp-express/issues/155))
Note however that DOCUMENT_ROOT can be unreliable.
If you store the webp images in the same folder as the originals and append ".webp" (rather than replace the file extension), you can do this instead:
```
# Redirect to existing converted image (under appropriate circumstances)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME}.webp -f
RewriteRule ^/?(.+)\.(jpe?g|png)$ $1.$2.webp [T=image/webp,L]
```
RewriteCond %{REQUEST_FILENAME}.webp -f
### Redirect with CDN support
If you are using a CDN, and want to redirect to existing images with the .htaccess, it is a good idea to add a "Vary Accept" header. This instructs the CDN that the response varies with the *Accept* header (we do not need to do that when routing to webp-on-demand.php, because the script takes care of adding this header, when appropriate.)
You can achieve redirect with CDN support with the following rules:
```
<IfModule mod_rewrite.c>
RewriteEngine On
# Redirect to existing converted image (under appropriate circumstances)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{DOCUMENT_ROOT}/[[your-base-path]]/[[your-destination-root]]/$1.$2.webp -f
RewriteRule ^\/?(.*)\.(jpe?g|png)$ /[[your-base-path]]/[[your-destination-root]]/$1.$2.webp [NC,T=image/webp,QSD,E=WEBPACCEPT:1,L]
# Redirect images to webp-on-demand.php (if browser supports webp)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php?source=%{SCRIPT_FILENAME}&url-path=$1.$2 [NC,L]
</IfModule>
<IfModule mod_headers.c>
# Apache appends "REDIRECT_" in front of the environment variables, but LiteSpeed does not.
# These next line is for Apache, in order to set environment variables without "REDIRECT_"
SetEnvIf REDIRECT_WEBPACCEPT 1 WEBPACCEPT=1
# Make CDN caching possible.
# The effect is that the CDN will cache both the webp image and the jpeg/png image and return the proper
# image to the proper clients (for this to work, make sure to set up CDN to forward the "Accept" header)
Header append Vary Accept env=WEBPACCEPT
</IfModule>
AddType image/webp .webp
```
## Forward the querystring
By forwarding the query string, you can allow control directly from the URL. You could for example make it possible to add "?debug" to an image URL, and thereby getting a conversion report. Or make "?reconvert" force reconversion.
In order to forward the query string, you need to add this condition before the RewriteRule that redirects to *webp-on-demand.php*:
```
RewriteCond %{QUERY_STRING} (.*)
```
That condition will always be met. The side effect is that it stores the match (the complete querystring). That match will be available as %1 in the RewriteRule. So, in the RewriteRule, we will have to add "&%1" after the last argument. Here is a complete solution:
```
<IfModule mod_rewrite.c>
RewriteEngine On
# Redirect images to webp-on-demand.php (if browser supports webp)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{QUERY_STRING} (.*)
RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php?source=%{SCRIPT_FILENAME}&%1 [NC,L]
</IfModule>
AddType image/webp .webp
```
Of course, in order to *do* something with that querystring, you must use them in your *webp-on-demand.php* script. You could for example use them directly in the options array sent to the *convertAndServe()* method. To achieve the mentioned "debug" and "reconvert" features, do this:
```php
$options = [
'show-report' => isset($_GET['debug']),
'reconvert' => isset($_GET['reconvert']),
'serve-original' => isset($_GET['original']),
];
```
*EDIT:*
I have just discovered a simpler way to achieve the querystring forward: The [QSA flag](https://httpd.apache.org/docs/trunk/rewrite/flags.html).
So, simply set the QSA flag in the RewriteRule, and nothing more:
```
RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php?source=%{SCRIPT_FILENAME} [NC,QSA,L]
```

View File

@@ -0,0 +1,145 @@
# WebP on demand
This is a solution for automatically serving WebP images instead of jpeg/pngs [for browsers that supports WebP](https://caniuse.com/#feat=webp) (At the time of writing, 78% of all mobile users and 72% of all desktop users uses browsers supporting webp)
Once set up, it will automatically convert images, no matter how they are referenced. It for example also works on images referenced in CSS. As the solution does not require any change in the HTML, it can easily be integrated into any website / framework
## Overview
A setup consists of a PHP script that serves converted images and some *redirect rules* that redirects JPG/PNG images to the script.
## Requirements
* *Apache* or *LiteSpeed* web server. Can be made to work with *NGINX* as well. Documentation is on the roadmap.
* *mod_rewrite* module for Apache
* PHP >= 5.6 (we are only testing down to 5.6. It should however work in 5.5 as well)
* That one of the *webp-convert* converters are working (these have different requirements)
## Installation
Here we assume you are using Composer. [Not using composer? - Follow me!](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/without-composer.md)
### 1. Require the webp-convert library with composer
```
composer require rosell-dk/webp-convert
```
### 2. Create the script
Create a file *webp-on-demand.php*, and place it in webroot, or where-ever you like in you web-application.
Here is a minimal example to get started with:
```php
<?php
// To start with, lets display any errors.
// - this will reveal if you entered wrong paths
error_reporting(E_ALL);
ini_set("display_errors", 1);
// Once you got it working, make sure that PHP warnings are not send to the output
// - this will corrupt the image
// For example, you can do it by commenting out the lines below:
// error_reporting(0);
// ini_set("display_errors", 0);
require 'vendor/autoload.php'; // Make sure to point this correctly
use WebPConvert\WebPConvert;
$source = $_GET['source']; // Absolute file path to source file. Comes from the .htaccess
$destination = $source . '.webp'; // Store the converted images besides the original images (other options are available!)
$options = [
// UNCOMMENT NEXT LINE, WHEN YOU ARE UP AND RUNNING!
'show-report' => true // Show a conversion report instead of serving the converted image.
// More options available!
// https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/introduction-for-converting.md
// https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/serving/introduction-for-serving.md
];
WebPConvert::serveConverted($source, $destination, $options);
```
### 3. Add redirect rules
Place the following rewrite rules in a *.htaccess* file in the directory where you want the solution to take effect:
```
<IfModule mod_rewrite.c>
RewriteEngine On
# Redirect images to webp-on-demand.php (if browser supports webp)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php?source=%{SCRIPT_FILENAME} [NC,L]
</IfModule>
AddType image/webp .webp
```
If you have placed *webp-on-demand.php* in a subfolder, you will need to change the rewrite rule accordingly.
The `RewriteCond %{REQUEST_FILENAME} -f` is not strictly necessary, but there to be sure that we got an existing file, and it could perhaps also prevent some undiscovered way of misuse.
### 4. Validate that it works
Browse to a JPEG image. Instead of an image, you should see a conversion report. Hopefully, you get a success. Otherwise, you need to hook up to a cloud converter or try to meet the requirements for cwebp, gd or imagick.
Once you get a successful conversion, you can uncomment the "show-report" option in the script.
It should work now, but to be absolute sure:
- Visit a page on your site with an image on it, using *Google Chrome*.
- Right-click the page and choose "Inspect"
- Click the "Network" tab
- Reload the page
- Find a jpeg or png image in the list. In the "type" column, it should say "webp". There should also be a *X-WebP-Convert-Status* header on the image that provides some insights on how things went.
### 5. Try this improvement and see if it works
It seems that it is not necessary to pass the filename in the query string.
Try replacing `$source = $_GET['source'];` in the script with the following:
```php
$docRoot = rtrim($_SERVER["DOCUMENT_ROOT"], '/');
$requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
$source = $docRoot . urldecode($requestUriNoQS);
```
And you can then remove `?source=%{SCRIPT_FILENAME}` from the `.htaccess` file.
There are some benefits of not passing in query string:
1. Passing a path in the query string may be blocked by a firewall, as it looks suspicious.
2. The script called to convert arbitrary files
3. One person experienced problems with spaces in filenames passed in the query string. See [this issue](https://github.com/rosell-dk/webp-convert/issues/95)
### 6. Customizing and tweaking
Basic customizing is done by setting options in the `$options` array. Check out the [docs on convert()](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/convert.md) and the [docs on convertAndServe()](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/serving/convert-and-serve.md)
Other tweaking is described in *docs/webp-on-demand/tweaks.md*:
- [Store converted images in separate folder](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/tweaks.md#store-converted-images-in-separate-folder)
- [CDN](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/tweaks.md#cdn)
- [Make .htaccess route directly to existing images](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/tweaks.md#make-htaccess-route-directly-to-existing-images)
- [Forward the query string](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/tweaks.md#forward-the-querystring)
## Troubleshooting
### The redirect rule doesn't seem to be working
If images are neither routed to the converter or a 404, it means that the redirect rule isn't taking effect. Common reasons for this includes:
- Perhaps there are other rules in your *.htaccess* that interfere with the rules?
- Perhaps your site is on *Apache*, but it has been configured to use *Nginx* to serve image files. To find out which server that is handling the images, browse to an image and eximine the "Server" response header. In case *NGINX* are serving images, see if you can reconfigure your server setup. Alternatively, you can create *NGINX* rewrite rules. There are some [here](https://github.com/S1SYPHOS/kirby-webp#nginx) and [there](https://github.com/uhop/grunt-tight-sprite/wiki/Recipe:-serve-WebP-with-nginx-conditionally).
- Perhaps the server isn't configured to allow *.htaccess* files? Try inserting rubbish in the top of the *.htaccess* file and refresh. You should now see an *Internal Server Error* error page. If you don't, your *.htaccess* file is ignored. Probably you will need to set *AllowOverride All* in your Virtual Host. [Look here for more help](
https://docs.bolt.cm/3.4/howto/making-sure-htaccess-works#test-if-htaccess-is-working)
- Perhaps the Apache *mod_rewrite* extension isn't enabled? Try removing both `<IfModule mod_rewrite.c>` and `</IfModule>` lines: if you get an *Internal Server Error* error page after this change, it's probably that it's indeed not enabled.
## Related
* https://www.maxcdn.com/blog/how-to-reduce-image-size-with-webp-automagically/
* https://www.digitalocean.com/community/tutorials/how-to-create-and-serve-webp-images-to-speed-up-your-website

View File

@@ -0,0 +1,58 @@
# WebP On Demand without composer
For your convenience, the library has been cooked down to two files: *webp-on-demand-1.inc* and *webp-on-demand-2.inc*. The second one is loaded when the first one decides it needs to do a conversion (and not simply serve existing image).
## Installing
### 1. Copy the latest build files into your website
The build files are distributed [here](https://github.com/rosell-dk/webp-convert-concat/tree/master/build). Open the "latest" folder and copy *webp-on-demand-1.inc* and *webp-on-demand-2.inc* into your website. They can be located wherever you like.
### 2. Create a *webp-on-demand.php*
Create a file *webp-on-demand.php*, and place it in webroot, or where-ever you like in you web-application.
Here is a minimal example to get started with:
```php
<?php
// To start with, lets display any errors.
// - this will reveal if you entered wrong paths
error_reporting(E_ALL);
ini_set("display_errors", 1);
// Once you got it working, make sure that PHP warnings are not send to the output
// - this will corrupt the image
// For example, you can do it by commenting out the lines below:
// error_reporting(0);
// ini_set("display_errors", 0);
use WebPConvert\WebPConvert;
require 'webp-on-demand-1.inc';
function webpconvert_autoloader($class) {
if (strpos($class, 'WebPConvert\\') === 0) {
require_once __DIR__ . '/webp-on-demand-2.inc';
}
}
spl_autoload_register('webpconvert_autoloader', true, true);
$source = $_GET['source']; // Absolute file path to source file. Comes from the .htaccess
$destination = $source . '.webp'; // Store the converted images besides the original images (other options are available!)
$options = [
// UNCOMMENT NEXT LINE, WHEN YOU ARE UP AND RUNNING!
'show-report' => true // Show a conversion report instead of serving the converted image.
// More options available!
// https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/introduction-for-converting.md
// https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/serving/introduction-for-serving.md
];
WebPConvert::serveConverted($source, $destination, $options);
```
Note that the procedure has changed in 2.0. In 1.x, the library supported a `require-for-conversion` option, but this option has been removed in 2.0. It was not really needed, as the example above illustrates.
### 3. Continue the regular install instructions from step 3
[Click here to continue...](https://github.com/rosell-dk/webp-on-demand#3-add-redirect-rules)