WebP Express - Version 0.15.0

Version Description

(released: 17 sep 2019)

  • Provided test-buttons for checking if the redirects works.
  • You can now choose which folders WebP Express is active in. Ie "Uploads and Themes".
  • You can now choose an alternative file structure for the webps which does not rely on DOCUMENT_ROOT being available.
  • WebP Express can now handle when wp-content is symlinked.
  • The .htaccess rules are now divided across folders. Some rules are needed where the source files are located, some where the webp files are located.
  • Added option to convert only PNG files
  • And a couple of bugfixes.

For more info, see the closed issues on the 0.15.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/22?closed=1

Download this release

Release Info

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

Code changes from version 0.14.22 to 0.15.0

Files changed (86) hide show
  1. BACKERS.md +1 -0
  2. README.md +95 -106
  3. README.txt +109 -98
  4. changelog.txt +161 -0
  5. composer.lock +5 -5
  6. docs/development.md +2 -15
  7. lib/classes/AdminInit.php +3 -2
  8. lib/classes/AlterHtmlHelper.php +37 -32
  9. lib/classes/BulkConvert.php +52 -27
  10. lib/classes/CacheMover.php +57 -5
  11. lib/classes/CachePurge.php +4 -0
  12. lib/classes/CapabilityTest.php +6 -0
  13. lib/classes/Config.php +75 -10
  14. lib/classes/Convert.php +40 -8
  15. lib/classes/ConvertHelperIndependent.php +306 -71
  16. lib/classes/FileHelper.php +31 -0
  17. lib/classes/HTAccess.php +120 -565
  18. lib/classes/HTAccessRules.php +855 -0
  19. lib/classes/HandleUploadHooks.php +1 -1
  20. lib/classes/ImageRoot.php +50 -0
  21. lib/classes/ImageRoots.php +49 -0
  22. lib/classes/Mime.php +15 -8
  23. lib/classes/PathHelper.php +344 -0
  24. lib/classes/Paths.php +351 -95
  25. lib/classes/PlatformInfo.php +64 -15
  26. lib/classes/PluginActivate.php +3 -2
  27. lib/classes/PluginDeactivate.php +9 -6
  28. lib/classes/SanityCheck.php +123 -43
  29. lib/classes/SelfTest.php +118 -0
  30. lib/classes/SelfTestHelper.php +535 -0
  31. lib/classes/SelfTestRedirectAbstract.php +115 -0
  32. lib/classes/SelfTestRedirectToConverter.php +208 -0
  33. lib/classes/SelfTestRedirectToExisting.php +253 -0
  34. lib/classes/SelfTestRedirectToWebPRealizer.php +228 -0
  35. lib/classes/WebPOnDemand.php +229 -0
  36. lib/classes/WebPRealizer.php +253 -0
  37. lib/classes/WodConfigLoader.php +180 -0
  38. lib/dismissable-messages/0.15.0/new-scope-setting-content.php +12 -0
  39. lib/dismissable-messages/0.15.0/new-scope-setting-index.php +13 -0
  40. lib/dismissable-messages/0.15.0/new-scope-setting-no-uploads.php +12 -0
  41. lib/migrate/migrate11.php +77 -0
  42. lib/migrate/migrate7.php +1 -1
  43. lib/options/css/webp-express-options-page.css +56 -2
  44. lib/options/enqueue_scripts.php +29 -11
  45. lib/options/js/0.14.9/escapeHTML.js +0 -16
  46. lib/options/js/{0.14.9 → 0.15.0}/authorized_sites_bak.js +0 -0
  47. lib/options/js/{0.14.9 → 0.15.0}/bulk-convert.js +18 -13
  48. lib/options/js/{0.14.9 → 0.15.0}/converters.js +0 -45
  49. lib/options/js/{0.14.9 → 0.15.0}/das-popup.js +0 -0
  50. lib/options/js/0.15.0/escapeHTML.js +34 -0
  51. lib/options/js/{0.14.9 → 0.15.0}/image-comparison-slider.js +0 -0
  52. lib/options/js/{0.14.9 → 0.15.0}/page.js +20 -7
  53. lib/options/js/{0.14.9 → 0.15.0}/purge-cache.js +1 -1
  54. lib/options/js/0.15.0/self-test.js +156 -0
  55. lib/options/js/{0.14.9 → 0.15.0}/sortable.min.js +0 -0
  56. lib/options/js/{0.14.9 → 0.15.0}/test-convert.js +8 -2
  57. lib/options/js/{0.14.9 → 0.15.0}/whitelist.js +0 -0
  58. lib/options/options/conversion-options/jpeg.inc +1 -1
  59. lib/options/options/general/destination-extension.inc +2 -1
  60. lib/options/options/general/destination-structure.inc +38 -0
  61. lib/options/options/general/general.inc +11 -0
  62. lib/options/options/general/image-types.inc +1 -0
  63. lib/options/options/general/scope.inc +59 -0
  64. lib/options/options/redirection-rules/enable-redirection-to-converter.inc +3 -0
  65. lib/options/options/redirection-rules/enable-redirection-to-webp-realizer.inc +4 -1
  66. lib/options/options/redirection-rules/redirect-to-existing.inc +4 -0
  67. lib/options/options/redirection-rules/redirection-rules.inc +9 -3
  68. lib/options/page-messages.php +2 -2
  69. lib/options/page.php +5 -1
  70. lib/options/submit.php +111 -18
  71. vendor/composer/autoload_classmap.php +0 -83
  72. vendor/composer/autoload_static.php +0 -87
  73. vendor/composer/installed.json +6 -6
  74. vendor/rosell-dk/webp-convert/.php_cs.dist +0 -19
  75. vendor/rosell-dk/webp-convert/BACKERS.md +1 -0
  76. vendor/rosell-dk/webp-convert/README.md +12 -7
  77. vendor/rosell-dk/webp-convert/composer.json +1 -18
  78. vendor/rosell-dk/webp-convert/phpdox.xml +0 -9
  79. vendor/rosell-dk/webp-convert/phpstan.neon +0 -28
  80. vendor/rosell-dk/webp-convert/phpunit.xml.dist +0 -41
  81. vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php +6 -0
  82. vendor/rosell-dk/webp-convert/src/Helpers/Sanitize.php +30 -0
  83. vendor/rosell-dk/webp-convert/src/Options/Options.php +11 -2
  84. webp-express.php +1 -1
  85. wod/webp-on-demand.php +11 -219
  86. wod/webp-realizer.php +12 -207
BACKERS.md CHANGED
@@ -13,6 +13,7 @@ To become a backer, yourself, [go to my page at patreon.com](https://www.patreon
13
| Name | Date | Message (max 70 chars, plain text only) |
14
| --------------------- | ---------- | ----------------------------------------------------------------------- |
15
| Tammy Valgardson | 2018-12-27 | |
16
17
<sub>I reserve the right to disallow inappropriate messages. No Trump hooting or bashing here, please. And don't be aggressive, obscene or anything unpleasant. But I don't have to point that out, do I?</sub>
18
13
| Name | Date | Message (max 70 chars, plain text only) |
14
| --------------------- | ---------- | ----------------------------------------------------------------------- |
15
| Tammy Valgardson | 2018-12-27 | |
16
+ | Max Kreminsky | 2019-08-02 | |
17
18
<sub>I reserve the right to disallow inappropriate messages. No Trump hooting or bashing here, please. And don't be aggressive, obscene or anything unpleasant. But I don't have to point that out, do I?</sub>
19
README.md CHANGED
@@ -12,12 +12,8 @@ But well, it is developed ([here on github](https://github.com/rosell-dk/webp-ex
12
## Description
13
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
14
15
- **Security notice**
16
- Security issues has been found and fixed. I urge you to upgrade to the latest release (at least 0.14.11, but go with 0.14.15, as there are important bug fixes)
17
-
18
-
19
### The image converter
20
- The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `cwebp`, `gd`, `imagick`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
21
22
### The "Serving webp to browsers that supports it" part.
23
@@ -27,16 +23,21 @@ The plugin supports different ways of delivering webps to browsers that supports
27
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
28
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
29
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
30
- 5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. You currently have to add the javascript yourself, but I expect to add an option for it in the next release.
31
32
- The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) and its "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md)
33
34
### Benefits
35
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
36
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
37
- Better ranking in Google searches (performance is taken into account by Google)
38
- Less bandwidth consumption - makes a huge difference in the parts of the world where the internet is slow and costly (you know, ~80% of the world population lives under these circumstances).
39
- - Currently ~73% of all traffic, and ~79% of mobile browsing traffic are done with browsers supporting webp. With Mozilla and Microsoft [finally on board](https://medium.com/@richard_90141/webp-image-support-an-8-year-saga-7aa2bedb8d02), these numbers are bound to increase. Check current numbers on [caniuse.com](https://caniuse.com/webp)).
40
41
### Recent news
42
Feb 2019: Multisite is now supported (0.12.0)
@@ -68,20 +69,23 @@ Here you have all options available.
68
69
70
#### Conversion methods
71
- WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, ewww cloud converter and remote WebP express. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
72
73
In case no conversion methods are working out of the box, you have several options:
74
- You can install this plugin on another website, which supports a local conversion method and connect to that using the "Remote WebP Express" conversion method
75
- You can [purchase a key](https://ewww.io/plans/) for the ewww cloud converter. They do not charge credits for webp conversions, so all you ever have to pay is the one dollar start-up fee :)
76
- You can set up [webp-convert-cloud-service](https://github.com/rosell-dk/webp-convert-cloud-service) on another server and connect to that. Its open source.
77
- - You can try to meet the server requirements of cwebp, gd, imagick or gmagick. Check out [this wiki page](https://github.com/rosell-dk/webp-convert/wiki/Meeting-the-requirements-of-the-converters) on how to do that
78
79
- ### The auto quality
80
- If your server has imagick og gmagick installed, the plugin will be able to detect the quality of a jpeg, and use the same quality for the converted webp. You can tell if it does, by looking at the quality option. If it allows you to select "auto" quality, it is available, otherwise it is not, and you will only have the option to set a specific quality for all conversions. *Auto* should be chosen, if available, as this ensures that each conversion are converted with an appropriate quality. Say you have a jpeg with low quality (say 30). The best result, is achieved by converting it to the same quality. Converting it with high quality (say 80), will not get you better quality, only a larger file.
81
82
- If you do not the "auto" option available:
83
- - Install imagick or gmagick, if you can
84
- - Use "Remote WebP Express" converter to connect to a site, that *does* have the auto option available
85
- If you have cwebp converter available, you can configure it to aim for a certain reduction, rather than using the quality parameter. Set this to for example 50%, or even 45%.
86
87
### Verifying that it works.
@@ -146,72 +150,36 @@ Easy enough. Browsers looks at the *content type* header rather than the URL to
146
147
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
148
149
- ### I am on NGINX / OpenResty
150
The easy solution is simply to use the plugin in "CDN friendly" mode, do a bulk conversion (takes care of converting existing images) and activate the "Convert on upload" option (takes care of converting new images in the media library).
151
152
- This however does not cover images in external CSS and images being dynamically added with javascript. And it does not handle new theme images.
153
154
- To get this working, requires manually inserting redirection rules in the NGINX configuration file (nginx.conf or the configuration file for the site, found in `/etc/nginx/sites-available`).
155
156
- There are two different approaches to achieve the redirections. One based on *rewrite* and one based on *try_files*. As *try_files* performs best, I shall recommend that.
157
158
For multisite on NGINX, read [here](https://github.com/rosell-dk/webp-express/issues/8)
159
160
- #### method 1 (try_files)
161
-
162
- Lets take this step by step.
163
- First step is to redirect images to the script. Second step is redirecting directly to existing webp images. Finally, as an optional third step, you can add extra rules for enabling the *Convert non-existing webp-files upon request* functionality
164
165
- **Step 1**: *Redirecting images to the script*
166
-
167
- The following will redirect all images under wp-content to the script, but only for webp-enabled browsers.
168
169
Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
170
171
- ```nginx
172
- location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
173
- add_header Vary Accept;
174
- if ($http_accept !~* "webp"){
175
- break;
176
- }
177
- try_files
178
- /nonexisting-because-try-files-needs-fallback
179
- /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
180
- ;
181
- }
182
- ```
183
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
184
-
185
- If you have moved wp-content to a non-standard place, you must change accordingly. Especially note that you must also change the wp-content parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
186
-
187
- The `xsource` parameter helps the script finding the source file. It is only needed on some setups. You can try deleting it and see if it still works.
188
-
189
- Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g", so the first line becomes:
190
- ```nginx
191
- location ~* ^/?wp-content/.*\.jpe?g$ {
192
- ```
193
-
194
- If you cannot get this to work then perhaps you need to add the following to your `mime.types` configuration file:
195
- `image/webp webp;`
196
-
197
- If you still cannot get it to work, you can instead try *method 2*
198
-
199
- Note: There is no longer any reason to add "&$args" to the line begining with "/wp-content". It was there to enable debugging a single image by appending "?debug" to the url. I however removed that functionality from `webp-on-demand.php`.
200
-
201
- **Step 2**: *Redirecting directly to existing webp images.*
202
-
203
- Once you got this working, lets improve performance by redirecting directly to existing webp images. This step isn't necessary, as the script also does that - but invoking the php script takes more resources that the direct redirect. Also, a direct redirect will produce *ETag* response header, which is increases caching performance.
204
-
205
- The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that.
206
-
207
- The following rules works both when WebP Express are configured to store images in the same folder as the originals ("mingled") and when or in a separate folder. Nginx will first see if there is a corresponding webp in the separate folder and then if there is a corresponding webp in the same folder and finally fall back to calling the conversion script.
208
-
209
```nginx
210
# WebP Express rules
211
# --------------------
212
location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
213
add_header Vary Accept;
214
- # expires 365d;
215
if ($http_accept !~* "webp"){
216
break;
217
}
@@ -221,53 +189,51 @@ location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
221
/wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
222
;
223
}
224
# ------------------- (WebP Express rules ends here)
225
```
226
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
227
228
- If you have configured WebP Express to store images in separate folder, you do not need the "$uri.webp" line. But it doesn't hurt to have it. But the reverse is not true. If configured to store images in the same folder ("mingled"), you still need the line that looks for a webp in the separate folder. The reason for this is that the "mingled" only applies to the images in the upload folder - other images - such as theme images are always stored in a separate folder.
229
230
- Again, beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
231
232
- Credits: This second step builds on [Eugene Lazutkins solution](http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/).
233
234
- **Step 3:**: *Caching*
235
- In most cases you can and should allow images to be cached for a long period. To do that, simply uncomment the "expires 365d;" line, by removing the "#" in front of it.
236
237
- **Step 4:**: *Routing requests for non-existing webps to the converter*
238
239
- Simply add the following rules below the ones you added in step 2:
240
241
- ```nginx
242
- location ~* ^/?wp-content/.*\.(png|jpe?g)\.webp$ {
243
- try_files
244
- $uri
245
- /wp-content/plugins/webp-express/wod/webp-realizer.php?wp-content=wp-content&$args
246
- ;
247
- }
248
- ```
249
250
- Again, beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
251
252
253
- #### method 2 (rewrite)
254
255
- **Step 1**: *Redirecting images to the script*
256
257
- Insert the following in the `server` context:
258
259
- ```nginx
260
- # WebP Express rules
261
- # --------------------
262
- if ($http_accept ~* "webp"){
263
- rewrite ^/(.*).(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
264
- }
265
- # ------------------- (WebP Express rules ends here)
266
- ```
267
268
- **Step 2**: *Redirecting directly to existing webp images.*
269
270
- Make sure that WebP Express is configured with "Destination" set to "Mingled". And then insert the following in the `server` context (replacing the rules you inserted in step 1)
271
```nginx
272
# WebP Express rules
273
# --------------------
@@ -295,17 +261,22 @@ if ($whattodo = A) {
295
}
296
# ------------------- (WebP Express rules ends here)
297
```
298
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
299
300
- Again, `wp-content` argument must point to the wp-content folder (relative to document root). In most installations, it is 'wp-content'.
301
302
- And again, the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that:
303
- 1. Set *Destination folder* to *mingled*
304
- 2. Set *File extension* to *Append ".webp"*
305
306
- The "expires 365d;" lines set caching to one year. You can remove these lines if you wish.
307
308
- I have not set any expire on the webp-on-demand.php request. This is not needed, as the script sets this according to what you set up in WebP Express settings. Also, trying to do it would require a new location block matching webp-on-demand.php, but that would override the location block handling php files, and thus break the functionality.
309
310
It is possible to put this stuff inside a `location` directive. However, having `if` directives inside `location` directives [is considered evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/). But it seems that in our case, it works. If you wish to do that, use the following rules instead:
311
@@ -338,12 +309,22 @@ location ~* ^/wp-content/.*\.webp$ {
338
}
339
# ------------------- (WebP Express rules ends here)
340
```
341
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
342
343
Discussion on this topic [here](https://wordpress.org/support/topic/nginx-rewrite-rules-4/)
344
And here: https://github.com/rosell-dk/webp-express/issues/166
345
346
- Here are rules if you need to replace the file extension with ".webp" rather than appending ".webp" to it: https://www.keycdn.com/support/optimus/configuration-to-deliver-webp
347
348
### I am on a Windows server
349
Good news! It should work now, thanks to a guy that calls himself lwxbr. At least on XAMPP 7.3.1, Windows 10. https://github.com/rosell-dk/webp-express/pull/213.
@@ -526,7 +507,7 @@ If you do not want to use serve varied images:
526
- Open WebP Express options
527
- Switch to *CDN friendly* mode.
528
- Set *File extension* to "Set to .webp"
529
- - Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely neccessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
530
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
531
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
532
@@ -610,6 +591,15 @@ Here are my current plans ahead: 0.15 will probably be a file manager-like inter
610
611
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
612
613
## Changes in 0.14.12 - 0.14.15
614
- Fixed errors with "redirect to conversion script" on systems with symlinked folders
615
- Fixed errors with "redirect to conversion script" on systems where the filename cannot be passed through an environment variable
@@ -624,7 +614,6 @@ It is urged that you upgrade all of you WebP Express installations!
624
- Security fix: Added checks for file paths and directories.
625
- Security fix: Nonces and capability checks for AJAX calls.
626
627
-
628
## Changes in 0.14.4
629
- Now bundles with multiple cwebp binaries for linux for systems where 1.0.2 fails.
630
12
## Description
13
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
14
15
### The image converter
16
+ The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `imagick`, `cwebp`, `vips`, `gd`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
17
18
### The "Serving webp to browsers that supports it" part.
19
23
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
24
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
25
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
26
+ 5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. However, beware that the javascript doesn't support srcset attributes, which is why I haven't added that method to the plugin (yet).
27
28
+ The plugin implements the "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/webp-on-demand.md) and builds on a bunch of open source libraries (all maintained by me):
29
+ - [WebPConvert](https://github.com/rosell-dk/webp-convert): For converting images to webp
30
+ - [WebP Convert Cloud Service](https://github.com/rosell-dk/webp-convert-cloud-service): For the Web Service functionality
31
+ - [DOM Util for WebP](https://github.com/rosell-dk/dom-util-for-webp): For the Alter HTML functionality
32
+ - [Image MimeType Guesser](https://github.com/rosell-dk/image-mime-type-guesser): For detecting mime types of images.
33
34
### Benefits
35
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
36
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
37
- Better ranking in Google searches (performance is taken into account by Google)
38
- Less bandwidth consumption - makes a huge difference in the parts of the world where the internet is slow and costly (you know, ~80% of the world population lives under these circumstances).
39
+ - Currently ~83% of all traffic, and ~80% of mobile browsing traffic are done with browsers supporting webp. With Mozilla and Microsoft [finally on board](https://medium.com/@richard_90141/webp-image-support-an-8-year-saga-7aa2bedb8d02), these numbers are bound to increase. Check current numbers on [caniuse.com](https://caniuse.com/webp)).
40
+ - It's great for the environment too! Reducing network traffic reduces electricity consumption which reduces CO2 emissions.
41
42
### Recent news
43
Feb 2019: Multisite is now supported (0.12.0)
69
70
71
#### Conversion methods
72
+ WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, Vips extension, ewww cloud converter and remote WebP express etc. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
73
74
In case no conversion methods are working out of the box, you have several options:
75
- You can install this plugin on another website, which supports a local conversion method and connect to that using the "Remote WebP Express" conversion method
76
- You can [purchase a key](https://ewww.io/plans/) for the ewww cloud converter. They do not charge credits for webp conversions, so all you ever have to pay is the one dollar start-up fee :)
77
- You can set up [webp-convert-cloud-service](https://github.com/rosell-dk/webp-convert-cloud-service) on another server and connect to that. Its open source.
78
+ - You can try to meet the server requirements of cwebp, Gd, Imagick, Gmagick or Vips. Check out [this wiki page](https://github.com/rosell-dk/webp-convert/wiki/Meeting-the-requirements-of-the-converters) on how to do that
79
+
80
+ ### Quality detection of jpegs
81
+ If your server has Imagick extension or is able to execute imagemagick binary, the plugin will be able to detect the quality of a jpeg, and use that quality for the converted webp. You can tell if the quality detection is available by hovering the help icon in Conversion > Jpeg options > Quality for lossy. The last line in that help text tells you.
82
83
+ This auto quality has benefits over fixed quality as it ensures that each conversion are converted with an appropriate quality. Encoding low quality jpegs to high quality webps does not magically increase the visual quality so that your webp looks better than the original. But it does result in a much larger filesize than if the jpeg where converting to a webp with the same quality setting as the original.
84
85
+ If you do not have quality detection working, you can try one of the following:
86
+ - Install Imagick on the server (for this purpose, it is not required that it is compiled with WebP support)
87
+ - Install imagemagick on the server and grant permission for PHP to use the "exec" function.
88
+ - Use "Remote WebP Express" converter to connect to a site, that *does* have quality detection working
89
- If you have cwebp converter available, you can configure it to aim for a certain reduction, rather than using the quality parameter. Set this to for example 50%, or even 45%.
90
91
### Verifying that it works.
150
151
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
152
153
+ ### I am on NGINX or OpenResty
154
+
155
+ #### The simple way (no redirecting rules)
156
The easy solution is simply to use the plugin in "CDN friendly" mode, do a bulk conversion (takes care of converting existing images) and activate the "Convert on upload" option (takes care of converting new images in the media library).
157
158
+ *PRO*: Very easy to set up.
159
+ *CON*: Images in external CSS and images being dynamically added with javascript will not be served as webp.
160
+ *CON*: New new theme images will not be converted until you run a new Bulk conversion
161
162
+ #### The advanced way (creating NGINX redirecting rules)
163
+ Creating NGINX rules requires manually inserting redirection rules in the NGINX configuration file (nginx.conf or the configuration file for the site, found in `/etc/nginx/sites-available`). If you do not have access to do that, you will have to settle with the "simple way" described above.
164
165
+ There are two different approaches to achieve the redirections. The one that I recommend is based on a *try_files* directive. If that doesn't work for you, you can try the alternative rules that are based on the *rewrite* directive. The rules are described in the next couple of sections.
166
167
For multisite on NGINX, read [here](https://github.com/rosell-dk/webp-express/issues/8)
168
169
+ #### Recommended rules (using "try_files")
170
171
+ __Preparational step:__
172
+ The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that by setting *General > File extension* to *Append ".webp"*
173
174
+ __The rules:__
175
Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
176
177
```nginx
178
# WebP Express rules
179
# --------------------
180
location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
181
add_header Vary Accept;
182
+ expires 365d;
183
if ($http_accept !~* "webp"){
184
break;
185
}
189
/wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
190
;
191
}
192
+
193
+ # Route requests for non-existing webps to the converter
194
+ location ~* ^/?wp-content/.*\.(png|jpe?g)\.webp$ {
195
+ try_files
196
+ $uri
197
+ /wp-content/plugins/webp-express/wod/webp-realizer.php?wp-content=wp-content
198
+ ;
199
+ }
200
# ------------------- (WebP Express rules ends here)
201
```
202
203
+ __BEWARE:__
204
+ - Beware that when copy/pasting you might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded (in the last line in the try_files block)
205
206
+ - Beware that the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you __must__ configure *WebP Express* to store the converted files like that.
207
208
+ - Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
209
210
+ - Beware that if you have moved wp-content to a non-standard place, you must change accordingly. Note that you must then also change the "wp-content" parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
211
212
+ - I have put in an expires statement for caching. You might want to modify or disable that.
213
214
+ - The rules contains all redirections (as if you enabled all three redirection options in settings). If you do not wish to redirect to converter, remove the last line in the try_files block. If you do not wish to create webp files upon request, remove the last location block.
215
216
+ - If you have configured WebP Express to store images in separate folder, you do not need the "$uri.webp" line in the first "try_files" block. But it doesn't hurt to have it. And beware that the reverse is not true. If configured to store images in the same folder ("mingled"), you still need the line that looks for a webp in the separate folder. The reason for this is that the "mingled" only applies to the images in the upload folder - other images - such as theme images are always stored in a separate folder.
217
218
+ If you cannot get this to work then perhaps you need to add the following to your *mime.types* configuration file:
219
+ `image/webp webp;`
220
221
+ If you still cannot get it to work, you can instead try the alternative rules below.
222
223
+ Credits: These rules are builds upon [Eugene Lazutkins solution](http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/).
224
225
+ #### Alternative rules (using "rewrite")
226
227
+ In case the recommended rules does not work for you, you can try these alternative rules.
228
229
+ The reason I recommend the *try_files* approach above over these alternative rules is that it is a bit simpler and it is supposed to perform marginally better. These alternative rules are in no way inferior to the other. Choose whatever works!
230
+
231
+ __Preparational step:__
232
+ The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that by setting *General > File extension* to *Append ".webp"*. Also make sure that WebP Express is configured with "Destination" set to "Mingled".
233
234
+ __The rules:__
235
+ Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
236
237
```nginx
238
# WebP Express rules
239
# --------------------
261
}
262
# ------------------- (WebP Express rules ends here)
263
```
264
265
+ __BEWARE:__
266
+
267
+ - Beware that when copy/pasting you might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded (in the last line in the try_files block)
268
+
269
+ - Beware that the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you __must__ configure *WebP Express* to store the converted files like that.
270
+
271
+ - Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
272
+
273
+ - Beware that if you have moved wp-content to a non-standard place, you must change accordingly. Note that you must then also change the "wp-content" parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
274
275
+ - I have put in an expires statement for caching. You might want to modify or disable that.
276
277
+ - I have not set any expire on the webp-on-demand.php request. This is not needed, as the script sets this according to what you set up in WebP Express settings. Also, trying to do it would require a new location block matching webp-on-demand.php, but that would override the location block handling php files, and thus break the functionality.
278
279
+ - There is no longer any reason to add "&$args" to the line begining with "/wp-content". It was there to enable debugging a single image by appending "?debug" to the url. I however removed that functionality from `webp-on-demand.php`.
280
281
It is possible to put this stuff inside a `location` directive. However, having `if` directives inside `location` directives [is considered evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/). But it seems that in our case, it works. If you wish to do that, use the following rules instead:
282
309
}
310
# ------------------- (WebP Express rules ends here)
311
```
312
+
313
+ PS: In case you only want to redirect images to the script (and not to existing), the rules becomes much simpler:
314
+
315
+ ```nginx
316
+ # WebP Express rules
317
+ # --------------------
318
+ if ($http_accept ~* "webp"){
319
+ rewrite ^/(.*).(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
320
+ }
321
+ # ------------------- (WebP Express rules ends here)
322
+ ```
323
324
Discussion on this topic [here](https://wordpress.org/support/topic/nginx-rewrite-rules-4/)
325
And here: https://github.com/rosell-dk/webp-express/issues/166
326
327
+ Here are rules if you need to *replace* the file extension with ".webp" rather than appending ".webp" to it: https://www.keycdn.com/support/optimus/configuration-to-deliver-webp
328
329
### I am on a Windows server
330
Good news! It should work now, thanks to a guy that calls himself lwxbr. At least on XAMPP 7.3.1, Windows 10. https://github.com/rosell-dk/webp-express/pull/213.
507
- Open WebP Express options
508
- Switch to *CDN friendly* mode.
509
- Set *File extension* to "Set to .webp"
510
+ - Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely necessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
511
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
512
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
513
591
592
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
593
594
+ ## Changes in 0.15.0
595
+ - Provided test-buttons for checking if the redirects works.
596
+ - You can now choose which folders WebP Express is active in. Ie "Uploads and Themes".
597
+ - You can now choose an alternative file structure for the webps which does not rely on DOCUMENT_ROOT being available.
598
+ - WebP Express can now handle when wp-content is symlinked.
599
+ - The .htaccess rules are now divided across folders. Some rules are needed where the source files are located, some where the webp files are located.
600
+ - Added option to convert only PNG files
601
+ - And a couple of bugfixes.
602
+
603
## Changes in 0.14.12 - 0.14.15
604
- Fixed errors with "redirect to conversion script" on systems with symlinked folders
605
- Fixed errors with "redirect to conversion script" on systems where the filename cannot be passed through an environment variable
614
- Security fix: Added checks for file paths and directories.
615
- Security fix: Nonces and capability checks for AJAX calls.
616
617
## Changes in 0.14.4
618
- Now bundles with multiple cwebp binaries for linux for systems where 1.0.2 fails.
619
README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://ko-fi.com/rosell
4
Tags: webp, images, performance
5
Requires at least: 4.0
6
Tested up to: 5.2
7
- Stable tag: 0.14.22
8
Requires PHP: 5.6
9
License: GPLv3
10
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@@ -15,12 +15,8 @@ Serve autogenerated WebP images instead of jpeg/png to browsers that supports We
15
16
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
17
18
- !! **SECURITY NOTICE** !!
19
- Security issues has recently been found and fixed. I urge you to upgrade to the latest release (at least 0.14.11, but go with 0.14.22, as there are important bug fixes)
20
-
21
-
22
### The image converter
23
- The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `cwebp`, `gd`, `imagick`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
24
25
### The "Serving webp to browsers that supports it" part.
26
@@ -30,9 +26,13 @@ The plugin supports different ways of delivering webps to browsers that supports
30
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
31
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
32
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
33
- 5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. You currently have to add the javascript yourself, but I expect to add an option for it in the next release.
34
35
- The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) and its "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md)
36
37
### Benefits
38
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
@@ -153,67 +153,33 @@ Easy enough. Browsers looks at the *content type* header rather than the URL to
153
154
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
155
156
- = I am on NGINX / OpenResty =
157
- The easy solution is simply to use the plugin in "CDN friendly" mode, do a bulk conversion (takes care of converting existing images) and activate the "Convert on upload" option (takes care of converting new images in the media library).
158
159
- This however does not cover images in external CSS and images being dynamically added with javascript. And it does not handle new theme images.
160
161
- There are two different approaches to achieve the redirections. One based on *rewrite* and one based on *try_files*. As *try_files* performs best, I shall recommend that.
162
163
- For multisite on NGINX, read [here](https://github.com/rosell-dk/webp-express/issues/8)
164
165
- **method 1 (try_files)**
166
167
- Lets take this step by step.
168
- First step is to redirect images to the script. Second step is redirecting directly to existing webp images. Finally, as an optional third step, you can add extra rules for enabling the *Convert non-existing webp-files upon request* functionality
169
170
- **Step 1**: *Redirecting images to the script*
171
172
- The following will redirect all images under wp-content to the script, but only for webp-enabled browsers.
173
174
Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
175
176
`
177
- location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
178
- add_header Vary Accept;
179
- if ($http_accept !~* "webp"){
180
- break;
181
- }
182
- try_files
183
- /nonexisting-because-try-files-needs-fallback
184
- /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
185
- ;
186
- }
187
- `
188
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
189
-
190
- If you have moved wp-content to a non-standard place, you must change accordingly. Especially note that you must also change the wp-content parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
191
-
192
- The `xsource` parameter helps the script finding the source file. It is only needed on some setups. You can try deleting it and see if it still works.
193
-
194
- Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g", so the first line becomes:
195
- `
196
- location ~* ^/?wp-content/.*\.jpe?g$ {
197
- `
198
-
199
- If you cannot get this to work then perhaps you need to add the following to your `mime.types` configuration file:
200
- `
201
- image/webp webp;
202
- `
203
-
204
- If you still cannot get it to work, you can instead try *method 2*
205
-
206
- Note: There is no longer any reason to add "&$args" to the line begining with "/wp-content". It was there to enable debugging a single image by appending "?debug" to the url. I however removed that functionality from `webp-on-demand.php`.
207
-
208
- **Step 2**: *Redirecting directly to existing webp images.*
209
-
210
- Once you got this working, lets improve performance by redirecting directly to existing webp images. This step isn't necessary, as the script also does that - but invoking the php script takes more resources that the direct redirect. Also, a direct redirect will produce *ETag* response header, which is increases caching performance.
211
-
212
- The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that.
213
-
214
- The following rules works both when WebP Express are configured to store images in the same folder as the originals ("mingled") and when or in a separate folder. Nginx will first see if there is a corresponding webp in the separate folder and then if there is a corresponding webp in the same folder and finally fall back to calling the conversion script.
215
-
216
- `
217
location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
218
add_header Vary Accept;
219
expires 365d;
@@ -226,51 +192,54 @@ location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
226
/wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
227
;
228
}
229
- `
230
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
231
-
232
- If you have configured WebP Express to store images in separate folder, you do not need the "$uri.webp" line. But it doesn't hurt to have it. But the reverse is not true. If configured to store images in the same folder ("mingled"), you still need the line that looks for a webp in the separate folder. The reason for this is that the "mingled" only applies to the images in the upload folder - other images - such as theme images are always stored in a separate folder.
233
-
234
- Again, beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
235
-
236
- Credits: This second step builds on [Eugene Lazutkins solution](http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/).
237
-
238
- **Step 3:**: *Caching*
239
- In most cases you can and should allow images to be cached for a long period. If you do not want to do that, simply remove the "expires 365d;" line.
240
241
- **Step 4:**: *Routing requests for non-existing webps to the converter*
242
-
243
- Simply add the following rules below the ones you added in step 2:
244
-
245
- `
246
location ~* ^/?wp-content/.*\.(png|jpe?g)\.webp$ {
247
try_files
248
$uri
249
/wp-content/plugins/webp-express/wod/webp-realizer.php?wp-content=wp-content
250
;
251
}
252
`
253
254
- Again, beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
255
256
257
- **method 2 (rewrite)**
258
259
- **Step 1**: *Redirecting images to the script*
260
261
- Insert the following in the `server` context:
262
263
- `
264
- if ($http_accept ~* "webp"){
265
- rewrite ^/(.*).(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
266
- }
267
- }
268
- `
269
270
- **Step 2**: *Redirecting directly to existing webp images.*
271
272
- Make sure that WebP Express is configured with "Destination" set to "Mingled". And then insert the following in the `server` context (replacing the rules you inserted in step 1)
273
`
274
location ~* ^/wp-content/.*\.(png|jpe?g)$ {
275
add_header Vary Accept;
276
expires 365d;
@@ -293,22 +262,30 @@ if ($whattodo = AB) {
293
if ($whattodo = A) {
294
rewrite ^/wp-content/.*\.(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
295
}
296
- `
297
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
298
299
- Again, `wp-content` argument must point to the wp-content folder (relative to document root). In most installations, it is 'wp-content'.
300
301
- And again, the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that:
302
- 1. Set *Destination folder* to *mingled*
303
- 2. Set *File extension* to *Append ".webp"*
304
305
- The "expires 365d;" lines set caching to one year. You can remove these lines if you wish.
306
307
- I have not set any expire on the webp-on-demand.php request. This is not needed, as the script sets this according to what you set up in WebP Express settings. Also, trying to do it would require a new location block matching webp-on-demand.php, but that would override the location block handling php files, and thus break the functionality.
308
309
It is possible to put this stuff inside a `location` directive. However, having `if` directives inside `location` directives [is considered evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/). But it seems that in our case, it works. If you wish to do that, use the following rules instead:
310
311
`
312
location ~* ^/wp-content/.*\.(png|jpe?g)$ {
313
add_header Vary Accept;
314
expires 365d;
@@ -333,13 +310,24 @@ location ~* ^/wp-content/.*\.webp$ {
333
add_header Vary Accept;
334
}
335
}
336
`
337
- *Beware when copy/pasting: You might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded*
338
339
Discussion on this topic [here](https://wordpress.org/support/topic/nginx-rewrite-rules-4/)
340
And here: https://github.com/rosell-dk/webp-express/issues/166
341
342
- Here are rules if you need to replace the file extension with ".webp" rather than appending ".webp" to it: https://www.keycdn.com/support/optimus/configuration-to-deliver-webp
343
344
= I am on a Windows server =
345
Good news! It should work now, thanks to a guy that calls himself lwxbr. At least on XAMPP 7.3.1, Windows 10. https://github.com/rosell-dk/webp-express/pull/213.
@@ -520,7 +508,7 @@ If you do not want to use serve varied images:
520
- Open WebP Express options
521
- Switch to *CDN friendly* mode.
522
- Set *File extension* to "Set to .webp"
523
- - Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely neccessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
524
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
525
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
526
@@ -612,6 +600,19 @@ Easy enough! - [Go here!](https://ko-fi.com/rosell). Or [here](https://buymeacof
612
613
== Changelog ==
614
615
= 0.14.22 =
616
*(released: 4 aug 2019)*
617
@@ -636,7 +637,14 @@ Easy enough! - [Go here!](https://ko-fi.com/rosell). Or [here](https://buymeacof
636
* Fixed bug: Ewww api-key was forgot upon saving options
637
638
= 0.14.19 =
639
- *(released: 28 jun 2019)*
640
641
* Removed a line that might course Sanity Check to fail ("path not within document root")
642
@@ -937,6 +945,9 @@ For older releases, check out changelog.txt
937
938
== Upgrade Notice ==
939
940
= 0.14.22 =
941
* A bundle of bug fixes and a security fix for Windows
942
4
Tags: webp, images, performance
5
Requires at least: 4.0
6
Tested up to: 5.2
7
+ Stable tag: 0.15.0
8
Requires PHP: 5.6
9
License: GPLv3
10
License URI: https://www.gnu.org/licenses/gpl-3.0.html
15
16
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
17
18
### The image converter
19
+ The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `imagick`, `cwebp`, `vips`, `gd`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
20
21
### The "Serving webp to browsers that supports it" part.
22
26
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
27
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
28
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
29
+ 5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. However, beware that the javascript doesn't support srcset attributes, which is why I haven't added that method to the plugin (yet).
30
31
+ The plugin implements the "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/webp-on-demand/webp-on-demand.md) and builds on a bunch of open source libraries (all maintained by me):
32
+ - [WebPConvert](https://github.com/rosell-dk/webp-convert): For converting images to webp
33
+ - [WebP Convert Cloud Service](https://github.com/rosell-dk/webp-convert-cloud-service): For the Web Service functionality
34
+ - [DOM Util for WebP](https://github.com/rosell-dk/dom-util-for-webp): For the Alter HTML functionality
35
+ - [Image MimeType Guesser](https://github.com/rosell-dk/image-mime-type-guesser): For detecting mime types of images.
36
37
### Benefits
38
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
153
154
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
155
156
+ = I am on NGINX or OpenResty =
157
158
+ **The simple way (no redirecting rules)**
159
+ The easy solution is simply to use the plugin in "CDN friendly" mode, do a bulk conversion (takes care of converting existing images) and activate the "Convert on upload" option (takes care of converting new images in the media library).
160
161
+ *PRO*: Very easy to set up.
162
+ *CON*: Images in external CSS and images being dynamically added with javascript will not be served as webp.
163
+ *CON*: New new theme images will not be converted until you run a new Bulk conversion
164
165
+ **The advanced way (creating NGINX redirecting rules)**
166
+ Creating NGINX rules requires manually inserting redirection rules in the NGINX configuration file (nginx.conf or the configuration file for the site, found in `/etc/nginx/sites-available`). If you do not have access to do that, you will have to settle with the "simple way" described above.
167
168
+ There are two different approaches to achieve the redirections. The one that I recommend is based on a *try_files* directive. If that doesn't work for you, you can try the alternative rules that are based on the *rewrite* directive. The rules are described in the next couple of sections.
169
170
+ For multisite on NGINX, read [here](https://github.com/rosell-dk/webp-express/issues/8)
171
172
+ **Recommended rules (using "try_files")**
173
174
+ __Preparational step:__
175
+ The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that by setting *General > File extension* to *Append ".webp"*
176
177
+ __The rules:__
178
Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
179
180
`
181
+ &#35; WebP Express rules
182
+ &#35; --------------------
183
location ~* ^/?wp-content/.*\.(png|jpe?g)$ {
184
add_header Vary Accept;
185
expires 365d;
192
/wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content
193
;
194
}
195
196
+ &#35; Route requests for non-existing webps to the converter
197
location ~* ^/?wp-content/.*\.(png|jpe?g)\.webp$ {
198
try_files
199
$uri
200
/wp-content/plugins/webp-express/wod/webp-realizer.php?wp-content=wp-content
201
;
202
}
203
+ &#35; ------------------- (WebP Express rules ends here)
204
`
205
206
+ __BEWARE:__
207
+ - Beware that when copy/pasting you might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded (in the last line in the try_files block)
208
209
+ - Beware that the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you __must__ configure *WebP Express* to store the converted files like that.
210
211
+ - Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
212
213
+ - Beware that if you have moved wp-content to a non-standard place, you must change accordingly. Note that you must then also change the "wp-content" parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
214
215
+ - I have put in an expires statement for caching. You might want to modify or disable that.
216
217
+ - The rules contains all redirections (as if you enabled all three redirection options in settings). If you do not wish to redirect to converter, remove the last line in the try_files block. If you do not wish to create webp files upon request, remove the last location block.
218
+
219
+ - If you have configured WebP Express to store images in separate folder, you do not need the "$uri.webp" line in the first "try_files" block. But it doesn't hurt to have it. And beware that the reverse is not true. If configured to store images in the same folder ("mingled"), you still need the line that looks for a webp in the separate folder. The reason for this is that the "mingled" only applies to the images in the upload folder - other images - such as theme images are always stored in a separate folder.
220
221
+ If you cannot get this to work then perhaps you need to add the following to your *mime.types* configuration file:
222
+ `image/webp webp;`
223
+
224
+ If you still cannot get it to work, you can instead try the alternative rules below.
225
+
226
+ Credits: These rules are builds upon [Eugene Lazutkins solution](http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/).
227
+
228
+ **Alternative rules (using "rewrite")**
229
+
230
+ In case the recommended rules does not work for you, you can try these alternative rules.
231
+
232
+ The reason I recommend the *try_files* approach above over these alternative rules is that it is a bit simpler and it is supposed to perform marginally better. These alternative rules are in no way inferior to the other. Choose whatever works!
233
+
234
+ __Preparational step:__
235
+ The rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you must configure *WebP Express* to store the converted files like that by setting *General > File extension* to *Append ".webp"*. Also make sure that WebP Express is configured with "Destination" set to "Mingled".
236
+
237
+ __The rules:__
238
+ Insert the following in the `server` context of your configuration file (usually found in `/etc/nginx/sites-available`). "The `server` context" refers to the part of the configuration that starts with "server {" and ends with the matching "}".
239
240
`
241
+ &#35; WebP Express rules
242
+ &#35; --------------------
243
location ~* ^/wp-content/.*\.(png|jpe?g)$ {
244
add_header Vary Accept;
245
expires 365d;
262
if ($whattodo = A) {
263
rewrite ^/wp-content/.*\.(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
264
}
265
+ &#35; ------------------- (WebP Express rules ends here)
266
+ ```
267
+
268
+ __BEWARE:__
269
+
270
+ - Beware that when copy/pasting you might get html-encoded characters. Verify that the ampersand before "wp-content" isn't encoded (in the last line in the try_files block)
271
+
272
+ - Beware that the rules looks for existing webp files by appending ".webp" to the URL. So for this to work, you __must__ configure *WebP Express* to store the converted files like that.
273
+
274
+ - Beware that if you haven't enabled *png* conversion, you should replace "(png|jpe?g)" with "jpe?g".
275
276
+ - Beware that if you have moved wp-content to a non-standard place, you must change accordingly. Note that you must then also change the "wp-content" parameter to the script. It expects a relative path to wp-content (from document root) and is needed so the script can find the configuration file.
277
278
+ - I have put in an expires statement for caching. You might want to modify or disable that.
279
280
+ - I have not set any expire on the webp-on-demand.php request. This is not needed, as the script sets this according to what you set up in WebP Express settings. Also, trying to do it would require a new location block matching webp-on-demand.php, but that would override the location block handling php files, and thus break the functionality.
281
282
+ - There is no longer any reason to add "&$args" to the line begining with "/wp-content". It was there to enable debugging a single image by appending "?debug" to the url. I however removed that functionality from `webp-on-demand.php`.
283
284
It is possible to put this stuff inside a `location` directive. However, having `if` directives inside `location` directives [is considered evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/). But it seems that in our case, it works. If you wish to do that, use the following rules instead:
285
286
`
287
+ &#35; WebP Express rules
288
+ &#35; --------------------
289
location ~* ^/wp-content/.*\.(png|jpe?g)$ {
290
add_header Vary Accept;
291
expires 365d;
310
add_header Vary Accept;
311
}
312
}
313
+ &#35; ------------------- (WebP Express rules ends here)
314
+ `
315
+
316
+ PS: In case you only want to redirect images to the script (and not to existing), the rules becomes much simpler:
317
+
318
+ `
319
+ &#35; WebP Express rules
320
+ &#35; --------------------
321
+ if ($http_accept ~* "webp"){
322
+ rewrite ^/(.*).(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?xsource=x$request_filename&wp-content=wp-content break;
323
+ }
324
+ &#35; ------------------- (WebP Express rules ends here)
325
`
326
327
Discussion on this topic [here](https://wordpress.org/support/topic/nginx-rewrite-rules-4/)
328
And here: https://github.com/rosell-dk/webp-express/issues/166
329
330
+ Here are rules if you need to *replace* the file extension with ".webp" rather than appending ".webp" to it: https://www.keycdn.com/support/optimus/configuration-to-deliver-webp
331
332
= I am on a Windows server =
333
Good news! It should work now, thanks to a guy that calls himself lwxbr. At least on XAMPP 7.3.1, Windows 10. https://github.com/rosell-dk/webp-express/pull/213.
508
- Open WebP Express options
509
- Switch to *CDN friendly* mode.
510
- Set *File extension* to "Set to .webp"
511
+ - Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely necessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
512
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
513
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
514
600
601
== Changelog ==
602
603
+ = 0.15.0 =
604
+ *(released: 17 sep 2019)*
605
+
606
+ * Provided test-buttons for checking if the redirects works.
607
+ * You can now choose which folders WebP Express is active in. Ie "Uploads and Themes".
608
+ * You can now choose an alternative file structure for the webps which does not rely on DOCUMENT_ROOT being available.
609
+ * WebP Express can now handle when wp-content is symlinked.
610
+ * The .htaccess rules are now divided across folders. Some rules are needed where the source files are located, some where the webp files are located.
611
+ * Added option to convert only PNG files
612
+ * And a couple of bugfixes.
613
+
614
+ For more info, see the closed issues on the 0.15.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/22?closed=1
615
+
616
= 0.14.22 =
617
*(released: 4 aug 2019)*
618
637
* Fixed bug: Ewww api-key was forgot upon saving options
638
639
= 0.14.19 =
640
+ *(released: 28 jun 2019)** Provided test-buttons for checking if the redirects works.
641
+ * You can now choose which folders WebP Express is active in. Ie "Uploads and Themes".
642
+ * You can now choose an alternative file structure for the webps which does not rely on DOCUMENT_ROOT being available.
643
+ * WebP Express can now handle when wp-content is symlinked.
644
+ * The .htaccess rules are now divided across folders. Some rules are needed where the source files are located, some where the webp files are located.
645
+ * And a couple of bugfixes.
646
+
647
+ For more info, see the closed issues on the 0.15.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/22?closed=1
648
649
* Removed a line that might course Sanity Check to fail ("path not within document root")
650
945
946
== Upgrade Notice ==
947
948
+ = 0.15.0 =
949
+ * New "Scope" and "destination structure" options, nice test buttons and a lot of work behind the surface
950
+
951
= 0.14.22 =
952
* A bundle of bug fixes and a security fix for Windows
953
changelog.txt CHANGED
@@ -1,3 +1,164 @@
1
= 0.13.0 =
2
*(released: 21 mar 2019)*
3
* Bulk Conversion
1
+ = 0.15.0 =
2
+ *(released: 17 sep 2019)*
3
+
4
+ * Provided test-buttons for checking if the redirects works.
5
+ * You can now choose which folders WebP Express is active in. Ie "Uploads and Themes".
6
+ * You can now choose an alternative file structure for the webps which does not rely on DOCUMENT_ROOT being available.
7
+ * WebP Express can now handle when wp-content is symlinked.
8
+ * The .htaccess rules are now divided across folders. Some rules are needed where the source files are located, some where the webp files are located.
9
+ * Added option to convert only PNG files
10
+ * And a couple of bugfixes.
11
+
12
+ For more info, see the closed issues on the 0.15.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/22?closed=1
13
+
14
+ = 0.14.22 =
15
+ *(released: 4 aug 2019)*
16
+
17
+ * Fixed bug in Nginx rules in the FAQ (they did not take into account that the webp images outside upload folder are never stored "mingled")
18
+ * Fixed bug: The extension setting was not respected - it was always appending ".webp", never setting. Thanks to Florian from Germany and Derrick Hammer from USA for reporting.
19
+ * Fixed bug: It turns out that Imagick gives up quality detection for some images and returns 0. This resulted in very poor quality webps when the quality was set to same as jpeg. The 0 is now treated as a failure and the max-quality will be used for such images. Thanks to @sanjayojha303 from India for reporting that there were quality problems on some images.
20
+ * Fixed bug-like behavior: The conversion scripts no longer requires that the respective setting is on for Nginx. Thanks to Mike from Russia for reporting this.
21
+ * Fixed bug: The error handler in webp-convert for handling warnings could in some cases result in endless recursion. For some the result was that they could no longer upload images. Thanks to Tobias Keller from Germany for reporting this bug.
22
+ * Fixed minor bug: Attempt to call private method in a rare scenario (when accessing one of the php scripts in the "wod" folder directly - which is not allowed). Thanks to Giacomo Lawrance from the U.K. for providing input that led to this discovery.
23
+ * Fixed minor bug: It was not tested whether a corresponding webp existed before trying to deleting it when an image was deleted. This produced warnings in debug.log.
24
+ * Security related: Added sanitizing of paths to avoid false positives on coderisk.com (there where no risk because already test the paths for sanity - but this is not detected by coderisk, as the variable is not modified). This was simply done in order get rid of the warnings at coderisk.
25
+ * Security fix: Paths were not sanitized on Windows.
26
+
27
+ = 0.14.21 =
28
+ *(released: 30 jun 2019)*
29
+
30
+ * Hopefully fixed WebP Express Error: "png" option is Object
31
+
32
+ = 0.14.20 =
33
+ *(released: 29 jun 2019)*
34
+
35
+ * Fixed bug: Ewww api-key was forgot upon saving options
36
+
37
+ = 0.14.19 =
38
+ *(released: 28 jun 2019)*
39
+
40
+ * Removed a line that might course Sanity Check to fail ("path not within document root")
41
+
42
+ = 0.14.18 =
43
+ *(released: 28 jun 2019)*
44
+
45
+ * Fixed Sanity Error: Path is outside allowed path on systems using symlinked folders
46
+ * Updated cache breaking token for javascript in order for the last fix for changing password with Remote WebP Express to take effect
47
+ * Fixed undefined variable error in image_make_intermediate_size hook, which prevented webps thumbnails to be generated upon upload
48
+ * Minor bug fix in cwebp converter (updated to webp-convert v.2.1.4)
49
+
50
+ = 0.14.17 =
51
+ *(released: 28 jun 2019)*
52
+
53
+ * Relaxed abspath sanity check on Windows
54
+ * Fixed updating password for Remote WebP Express
55
+
56
+ = 0.14.16 =
57
+ *(released: 26 jun 2019)*
58
+
59
+ * Fixed conversion errors using Bulk convert or Test convert on systems with symlinked folders
60
+
61
+ = 0.14.15 =
62
+ *(released: 26 jun 2019)*
63
+
64
+ * Fixed errors with "redirect to conversion script" on systems with symlinked folders
65
+ * Fixed errors with "redirect to conversion script" on systems where the filename cannot be passed through an environment variable
66
+
67
+ = 0.14.14 =
68
+ *(released: 26 jun 2019)*
69
+
70
+ * Fixed errors on systems with symlinked folders
71
+
72
+ = 0.14.13 =
73
+ *(released: 26 jun 2019)*
74
+
75
+ * Fixed errors in conversion scripts
76
+
77
+ = 0.14.12 =
78
+ *(released: 26 jun 2019)*
79
+
80
+ * Fixed critical bug
81
+
82
+ = 0.14.11 =
83
+ *(released: 24 jun 2019)*
84
+
85
+ The following security fixes has been applied in 0.14.0 - 0.14.11:
86
+ It is urged that you upgrade all of you WebP Express installations!
87
+
88
+ – Security fix: Closed a security hole that could be used to view the content of any file on the server (provided that the full path is known or guessed). This is a very serious flaw, which unfortunately has been around for quite a while.
89
+ – Security fix: Added capability checks to options page.
90
+ – Security fix: Sanitized user input.
91
+ – Security fix: Added checks for file paths and directories.
92
+ – Security fix: Nonces and capability checks for AJAX calls.
93
+
94
+ = 0.14.10 =
95
+ *(released: 24 jun 2019)*
96
+
97
+ * Security related
98
+
99
+ = 0.14.9 =
100
+ *(released: 22 jun 2019)*
101
+
102
+ * Security related
103
+
104
+ = 0.14.8 =
105
+ *(released: 21 jun 2019)*
106
+
107
+ * Security related
108
+
109
+ = 0.14.7 =
110
+ *(released: 20 jun 2019)*
111
+
112
+ * Security related: Removed unneccesary files from webp-convert library
113
+
114
+ = 0.14.6 =
115
+ *(released: 20 jun 2019)*
116
+
117
+ * Security related
118
+
119
+ = 0.14.5 =
120
+ *(released: 20 jun 2019)*
121
+
122
+ * Security related
123
+
124
+ = 0.14.4 =
125
+ *(released: 18 jun 2019)*
126
+
127
+ * Now bundles with multiple cwebp binaries for linux for systems where 1.0.2 fails.
128
+
129
+ = 0.14.3 =
130
+ *(released: 18 jun 2019)*
131
+
132
+ * Fixed filename of supplied cwebp for linux (bug was introduced in 0.14.2)
133
+
134
+ = 0.14.2 =
135
+ *(released: 17 jun 2019)*
136
+
137
+ * Fixed problem with older versions of cwebp
138
+ * Fixed that images was not deleted
139
+ * Fixed cache problem on options page on systems that disables cache busting (it resulted in "SyntaxError: JSON.parse")
140
+
141
+ = 0.14.1 =
142
+ *(released: 15 jun 2019)*
143
+
144
+ * Security related
145
+
146
+ = 0.14.0 =
147
+ *(released: 15 jun 2019)*
148
+
149
+ * Security fix: Closed a security hole that could be used to view the content of any file on the server (provided that the full path is known or guessed). This is a very serious flaw, which has been around for quite a while. I urge you to upgrade to 0.14.0.
150
+ * Added new "encoding" option, which can be set to auto. This can in some cases dramatically reduce the size of the webp. It is supported by all converters except ewww and gd.
151
+ * Added new "near-lossless" option (only for cwebp and vips). Using this is a good idea for reducing size of lossless webps with an acceptable loss of quality
152
+ * Added new "alpha-quality" option (all converters, except ewww and gd). Using this is a good idea when images with transparency are converted to lossy webp - it has the potential to reduce the size up to 50% (depending on the source material) while keeping an acceptable level of quality
153
+ * Added new conversion methods: Vips and GraphicsMagick
154
+ * Imagick conversion method now supports webp options (finally cracked it!)
155
+ * Using MimeType detection instead of relying on file extensions
156
+ * In "test" converter you now change options and also test PNG files
157
+ * Added conversion logs
158
+ * PNGs are now enabled by default (with the new conversion features especially PNGs are compressed much better)
159
+
160
+ For more info, see the closed issues on the 0.14.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/9?closed=1
161
+
162
= 0.13.0 =
163
*(released: 21 mar 2019)*
164
* Bulk Conversion
composer.lock CHANGED
@@ -118,16 +118,16 @@
118
},
119
{
120
"name": "rosell-dk/webp-convert",
121
- "version": "2.1.5",
122
"source": {
123
"type": "git",
124
"url": "https://github.com/rosell-dk/webp-convert.git",
125
- "reference": "b28ce7fe71ce4f930bd352f12b4f1591ac21b9f0"
126
},
127
"dist": {
128
"type": "zip",
129
- "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/b28ce7fe71ce4f930bd352f12b4f1591ac21b9f0",
130
- "reference": "b28ce7fe71ce4f930bd352f12b4f1591ac21b9f0",
131
"shasum": ""
132
},
133
"require": {
@@ -190,7 +190,7 @@
190
"png",
191
"png2webp"
192
],
193
- "time": "2019-08-02T14:13:24+00:00"
194
},
195
{
196
"name": "rosell-dk/webp-convert-cloud-service",
118
},
119
{
120
"name": "rosell-dk/webp-convert",
121
+ "version": "2.1.6",
122
"source": {
123
"type": "git",
124
"url": "https://github.com/rosell-dk/webp-convert.git",
125
+ "reference": "5cb744d2786468fb51883d1f3a90562166df3320"
126
},
127
"dist": {
128
"type": "zip",
129
+ "url": "https://api.github.com/repos/rosell-dk/webp-convert/zipball/5cb744d2786468fb51883d1f3a90562166df3320",
130
+ "reference": "5cb744d2786468fb51883d1f3a90562166df3320",
131
"shasum": ""
132
},
133
"require": {
190
"png",
191
"png2webp"
192
],
193
+ "time": "2019-08-10T22:04:08+00:00"
194
},
195
{
196
"name": "rosell-dk/webp-convert-cloud-service",
docs/development.md CHANGED
@@ -1,6 +1,6 @@
1
## Updating the vendor dir:
2
3
- 1. Run `composer update` in the root.
4
2. Run `composer dump-autoload -o`
5
(for some reason, `vendor/composer/autoload_classmap.php` looses all its mappings on composer update). It also looses them on a `composer dump-autoload` (without the -o option).
6
It actually seems that the mappings are not needed. It seems to work fine when I alter autoload_real to not use the static loader. But well, I'm reluctant to change anything that works.
@@ -11,21 +11,8 @@ It actually seems that the mappings are not needed. It seems to work fine when I
11
- cd into the folder
12
13
```
14
- rm -r tests
15
-
16
- rm -r vendor/rosell-dk/webp-convert/build-scripts
17
- rm -r vendor/rosell-dk/webp-convert/tests
18
- rm -r vendor/rosell-dk/webp-convert/build-tests-webp-convert
19
- rm -r vendor/rosell-dk/webp-convert/build-tests-wod
20
- rm -r vendor/rosell-dk/webp-convert/src-build
21
rm -r vendor/rosell-dk/webp-convert/docs
22
- rm vendor/rosell-dk/webp-convert/*.sh
23
- rm -r vendor/rosell-dk/webp-convert/src//Helpers/*.txt
24
- rm vendor/rosell-dk/webp-convert/.gitignore
25
-
26
- rm -r vendor/rosell-dk/webp-convert-cloud-service/tests
27
- rm -r vendor/rosell-dk/webp-convert-cloud-service/docs
28
-
29
rm vendor/rosell-dk/dom-util-for-webp/phpstan.neon
30
31
```
1
## Updating the vendor dir:
2
3
+ 1. Run `composer update` in the root (plugin root).
4
2. Run `composer dump-autoload -o`
5
(for some reason, `vendor/composer/autoload_classmap.php` looses all its mappings on composer update). It also looses them on a `composer dump-autoload` (without the -o option).
6
It actually seems that the mappings are not needed. It seems to work fine when I alter autoload_real to not use the static loader. But well, I'm reluctant to change anything that works.
11
- cd into the folder
12
13
```
14
rm -r vendor/rosell-dk/webp-convert/docs
15
+ rm -r vendor/rosell-dk/webp-convert/src/Helpers/*.txt
16
rm vendor/rosell-dk/dom-util-for-webp/phpstan.neon
17
18
```
lib/classes/AdminInit.php CHANGED
@@ -30,7 +30,7 @@ class AdminInit
30
public static function runMigrationIfNeeded()
31
{
32
// When an update requires a migration, the number should be increased
33
- define('WEBPEXPRESS_MIGRATION_VERSION', '10');
34
35
if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
36
// run migration logic
@@ -38,7 +38,7 @@ class AdminInit
38
}
39
40
// uncomment next line to test-run a migration
41
- //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate10.php';
42
}
43
44
public static function adminInitHandler()
@@ -62,6 +62,7 @@ class AdminInit
62
add_action('wp_ajax_webpexpress_view_log', array('\WebPExpress\ConvertLog', 'processAjaxViewLog'));
63
add_action('wp_ajax_webpexpress_purge_cache', array('\WebPExpress\CachePurge', 'processAjaxPurgeCache'));
64
add_action('wp_ajax_webpexpress_dismiss_message', array('\WebPExpress\DismissableMessages', 'processAjaxDismissMessage'));
65
66
67
// Add settings link on the plugins page
30
public static function runMigrationIfNeeded()
31
{
32
// When an update requires a migration, the number should be increased
33
+ define('WEBPEXPRESS_MIGRATION_VERSION', '11');
34
35
if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
36
// run migration logic
38
}
39
40
// uncomment next line to test-run a migration
41
+ //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate11.php';
42
}
43
44
public static function adminInitHandler()
62
add_action('wp_ajax_webpexpress_view_log', array('\WebPExpress\ConvertLog', 'processAjaxViewLog'));
63
add_action('wp_ajax_webpexpress_purge_cache', array('\WebPExpress\CachePurge', 'processAjaxPurgeCache'));
64
add_action('wp_ajax_webpexpress_dismiss_message', array('\WebPExpress\DismissableMessages', 'processAjaxDismissMessage'));
65
+ add_action('wp_ajax_webpexpress_self_test', array('\WebPExpress\SelfTest', 'processAjax'));
66
67
68
// Add settings link on the plugins page
lib/classes/AlterHtmlHelper.php CHANGED
@@ -81,7 +81,7 @@ class AlterHtmlHelper
81
82
/**
83
* Looks if $imageUrl is rooted in $baseUrl and if the file is there
84
- *
85
*
86
* @param $imageUrl (ie http://example.com/wp-content/image.jpg)
87
* @param $baseUrl (ie http://example.com/wp-content)
@@ -109,7 +109,7 @@ class AlterHtmlHelper
109
110
}
111
112
-
113
public static function isSourceInUpload($src)
114
{
115
/* $src is ie http://we0/wp-content-moved/themes/twentyseventeen/assets/images/header.jpg */
@@ -135,13 +135,14 @@ class AlterHtmlHelper
135
*
136
* returns false
137
* - if no source file found in that base
138
- * - or webp file isn't there and the `only-for-webps-that-exists` option is set
139
*
140
- * @param $imageUrl (ie http://example.com/wp-content/image.jpg)
141
- * @param $baseUrl (ie http://example.com/wp-content)
142
- * @param $baseDir (ie /var/www/example.com/wp-content)
143
*/
144
- private static function getWebPUrlInBase($sourceUrl, $baseUrl, $baseDir)
145
{
146
//error_log('getWebPUrlInBase:' . $sourceUrl . ':' . $baseUrl . ':' . $baseDir);
147
@@ -151,40 +152,44 @@ class AlterHtmlHelper
151
return false;
152
}
153
154
- // Calculate file path to src
155
$srcPathAbs = $baseDir . $srcPathRel;
156
157
- // Check that src file exists
158
if (!@file_exists($srcPathAbs)) {
159
return false;
160
}
161
162
163
- // Calculate $destPathAbs and $destUrl
164
- // -------------------------------------
165
- $inUpload = self::isSourceInUpload($sourceUrl);
166
167
- if ((self::$options['destination-folder'] == 'mingled') && $inUpload) {
168
- // mingled
169
- if (self::$options['destination-extension'] == 'append') {
170
- $destPathAbs = $srcPathAbs . '.webp';
171
- $destUrl = $sourceUrl . '.webp';
172
- } else {
173
- $destPathAbs = preg_replace('/\\.(png|jpe?g)#x2F;', '', $srcPathAbs) . '.webp';
174
- $destUrl = preg_replace('/\\.(png|jpe?g)#x2F;', '', $sourceUrl) . '.webp';
175
- }
176
- } else {
177
- // separate (images that are not in upload are always put in separate)
178
-
179
- $relPathFromDocRoot = '/webp-express/webp-images/doc-root/';
180
- $relPathFromDocRoot .= PathHelper::getRelDir(realpath($_SERVER['DOCUMENT_ROOT']), $baseDir) . $srcPathRel;
181
-
182
- list ($contentDirAbs, $contentUrl) = self::$options['bases']['content'];
183
-
184
- $destPathAbs = $contentDirAbs . $relPathFromDocRoot . '.webp';
185
- $destUrl = $contentUrl . $relPathFromDocRoot . '.webp';
186
}
187
188
$webpMustExist = self::$options['only-for-webps-that-exists'];
189
if ($webpMustExist && (!@file_exists($destPathAbs))) {
190
return false;
@@ -243,7 +248,7 @@ class AlterHtmlHelper
243
$baseDir = Paths::getUploadDirAbs();
244
}
245
246
- $result = self::getWebPUrlInBase($sourceUrl, $baseUrl, $baseDir);
247
if ($result !== false) {
248
return $result;
249
}
81
82
/**
83
* Looks if $imageUrl is rooted in $baseUrl and if the file is there
84
+ * PS: NOT USED ANYMORE!
85
*
86
* @param $imageUrl (ie http://example.com/wp-content/image.jpg)
87
* @param $baseUrl (ie http://example.com/wp-content)
109
110
}
111
112
+ // NOT USED ANYMORE
113
public static function isSourceInUpload($src)
114
{
115
/* $src is ie http://we0/wp-content-moved/themes/twentyseventeen/assets/images/header.jpg */
135
*
136
* returns false
137
* - if no source file found in that base
138
+ * - or source file is found but webp file isn't there and the `only-for-webps-that-exists` option is set
139
*
140
+ * @param string $sourceUrl Url of source image (ie http://example.com/wp-content/image.jpg)
141
+ * @param string $rootId Id (created in Config::updateAutoloadedOptions). Ie "uploads", "content" or any image root id
142
+ * @param string $baseUrl Base url of source image (ie http://example.com/wp-content)
143
+ * @param string $baseDir Base dir of source image (ie /var/www/example.com/wp-content)
144
*/
145
+ public static function getWebPUrlInBase($sourceUrl, $rootId, $baseUrl, $baseDir)
146
{
147
//error_log('getWebPUrlInBase:' . $sourceUrl . ':' . $baseUrl . ':' . $baseDir);
148
152
return false;
153
}
154
155
+ // Calculate file path to source
156
$srcPathAbs = $baseDir . $srcPathRel;
157
158
+ // Check that source file exists
159
if (!@file_exists($srcPathAbs)) {
160
return false;
161
}
162
163
+ // Calculate destination of webp (both path and url)
164
+ // ----------------------------------------
165
166
+ // We are calculating: $destPathAbs and $destUrl.
167
168
+ if (!isset(self::$options['bases'][$rootId])) {
169
+ return false;
170
}
171
172
+ $destinationRoot = Paths::destinationRoot(
173
+ $rootId,
174
+ self::$options['destination-folder'],
175
+ self::$options['destination-structure']
176
+ );
177
+
178
+ $relPathFromImageRootToSource = PathHelper::getRelDir(
179
+ realpath(Paths::getAbsDirById($rootId)),
180
+ realpath($srcPathAbs)
181
+ );
182
+ $relPathFromImageRootToDest = ConvertHelperIndependent::appendOrSetExtension(
183
+ $relPathFromImageRootToSource,
184
+ self::$options['destination-folder'],
185
+ self::$options['destination-extension'],
186
+ ($rootId == 'uploads')
187
+ );
188
+ $result['destination-url'] = $destinationRoot['url'] . '/' . $relPathFromImageRootToDest;
189
+
190
+ $destPathAbs = $destinationRoot['abs-path'] . '/' . $relPathFromImageRootToDest;
191
+ $destUrl = $destinationRoot['url'] . '/' . $relPathFromImageRootToDest;
192
+
193
$webpMustExist = self::$options['only-for-webps-that-exists'];
194
if ($webpMustExist && (!@file_exists($destPathAbs))) {
195
return false;
248
$baseDir = Paths::getUploadDirAbs();
249
}
250
251
+ $result = self::getWebPUrlInBase($sourceUrl, $id, $baseUrl, $baseDir);
252
if ($result !== false) {
253
return $result;
254
}
lib/classes/BulkConvert.php CHANGED
@@ -2,26 +2,11 @@
2
3
namespace WebPExpress;
4
5
- use \WebPExpress\ConvertHelperIndependent;
6
- use \WebPExpress\Paths;
7
- use \WebPExpress\PathHelper;
8
-
9
class BulkConvert
10
{
11
12
- public static function getUploadFolder($destinationFolder)
13
- {
14
- switch ($destinationFolder) {
15
- case 'mingled':
16
- return Paths::getUploadDirAbs();
17
- case 'separate':
18
- return Paths::getCacheDirAbs() . '/doc-root/' . Paths::getUploadDirRel();
19
- }
20
- }
21
-
22
public static function getList($config)
23
{
24
- //$cacheDir = self::getUploadFolder($config['destination-folder']);
25
26
27
/*
@@ -34,11 +19,12 @@ class BulkConvert
34
35
$listOptions = [
36
//'root' => Paths::getUploadDirAbs(),
37
- //'cache-root' => self::getUploadFolder($config['destination-folder']),
38
'ext' => $config['destination-extension'],
39
'destination-folder' => $config['destination-folder'], /* hm, "destination-folder" is a bad name... */
40
'webExpressContentDirAbs' => Paths::getWebPExpressContentDirAbs(),
41
'uploadDirAbs' => Paths::getUploadDirAbs(),
42
'filter' => [
43
'only-converted' => false,
44
'only-unconverted' => true,
@@ -46,13 +32,42 @@ class BulkConvert
46
]
47
];
48
49
$groups = [];
50
51
- $groups[] = [
52
- 'groupName' => 'wp-content',
53
- 'root' => Paths::getContentDirAbs(),
54
- ];
55
56
if (Paths::isUploadDirMovedOutOfWPContentDir()) {
57
$groups[] = [
58
'groupName' => 'uploads',
@@ -60,18 +75,28 @@ class BulkConvert
60
];
61
}
62
63
if (Paths::isPluginDirMovedOutOfWpContent()) {
64
$groups[] = [
65
'groupName' => 'plugins',
66
'root' => Paths::getPluginDirAbs(),
67
];
68
- }
69
70
foreach ($groups as $i => &$group) {
71
$listOptions['root'] = $group['root'];
72
/*
73
No use, because if uploads is in wp-content, the cache root will be different for the files in uploads (if mingled)
74
- $group['cache-root'] = ConvertHelperIndependent::getDestinationFolder(
75
$group['root'],
76
$listOptions['destination-folder'],
77
$listOptions['ext'],
@@ -79,11 +104,9 @@ class BulkConvert
79
$listOptions['uploadDirAbs']
80
);*/
81
$group['files'] = self::getListRecursively('.', $listOptions);
82
- //'cache-root' => ConvertHelperIndependent::getDestinationFolder()
83
}
84
85
-
86
-
87
return $groups;
88
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
89
}
@@ -133,13 +156,15 @@ class BulkConvert
133
$addThis = true;
134
135
if (($filter['only-converted']) || ($filter['only-unconverted'])) {
136
- //$cacheDir = $listOptions['cache-root'] . '/' . $relDir;
137
$destination = ConvertHelperIndependent::getDestination(
138
$dir . "/" . $filename,
139
$listOptions['destination-folder'],
140
$listOptions['ext'],
141
$listOptions['webExpressContentDirAbs'],
142
- $listOptions['uploadDirAbs']
143
);
144
$webpExists = @file_exists($destination);
145
2
3
namespace WebPExpress;
4
5
class BulkConvert
6
{
7
8
public static function getList($config)
9
{
10
11
12
/*
19
20
$listOptions = [
21
//'root' => Paths::getUploadDirAbs(),
22
'ext' => $config['destination-extension'],
23
'destination-folder' => $config['destination-folder'], /* hm, "destination-folder" is a bad name... */
24
'webExpressContentDirAbs' => Paths::getWebPExpressContentDirAbs(),
25
'uploadDirAbs' => Paths::getUploadDirAbs(),
26
+ 'useDocRootForStructuringCacheDir' => (($config['destination-structure'] == 'doc-root') && (Paths::canUseDocRootForStructuringCacheDir())),
27
+ 'imageRoots' => new ImageRoots(Paths::getImageRootsDefForSelectedIds($config['scope'])), // (Paths::getImageRootsDef()
28
'filter' => [
29
'only-converted' => false,
30
'only-unconverted' => true,
32
]
33
];
34
35
+ //$dirs = $config['scope'];
36
+
37
+ $rootIds = Paths::filterOutSubRoots($config['scope']);
38
+
39
$groups = [];
40
+ foreach ($rootIds as $rootId) {
41
+ $groups[] = [
42
+ 'groupName' => $rootId,
43
+ 'root' => Paths::getAbsDirById($rootId)
44
+ ];
45
+ }
46
47
+ /*
48
+ if (in_array('index', $config['scope'])) {
49
+ if (Paths::isWPContentDirMovedOutOfAbsPath()) {
50
+
51
+ }
52
+ } elseif (in_array('wp-content', $config['scope'])) {
53
+ $dirs[] = 'wp-content';
54
+ if (in_array('uploads', $config['scope']) && Paths::isUploadDirMovedOutOfWPContentDir()) {
55
+ $dirs[] = 'uploads';
56
+ }
57
+ if (in_array('plugins', $config['scope']) && Paths::isPluginDirMovedOutOfWpContent()) {
58
+ $dirs[] = 'plugins';
59
+ }
60
+ // ps: themes is always below wp-content
61
+ } else {
62
+ if (in_array('uploads', $config['scope'])) {
63
+ $dirs[] = 'uploads';
64
+ }
65
+ if (in_array('plugins', $config['scope'])) {
66
+ $dirs[] = 'plugins';
67
+ }
68
+ }
69
70
+ /*
71
if (Paths::isUploadDirMovedOutOfWPContentDir()) {
72
$groups[] = [
73
'groupName' => 'uploads',
75
];
76
}
77
78
+ $groups[] = [
79
+ 'groupName' => 'themes',
80
+ 'root' => Paths::getThemesDirAbs(),
81
+ ];
82
+
83
+ $groups[] = [
84
+ 'groupName' => 'wp-content',
85
+ 'root' => Paths::getContentDirAbs(),
86
+ ];
87
+
88
if (Paths::isPluginDirMovedOutOfWpContent()) {
89
$groups[] = [
90
'groupName' => 'plugins',
91
'root' => Paths::getPluginDirAbs(),
92
];
93
+ }*/
94
95
foreach ($groups as $i => &$group) {
96
$listOptions['root'] = $group['root'];
97
/*
98
No use, because if uploads is in wp-content, the cache root will be different for the files in uploads (if mingled)
99
+ $group['image-root'] = ConvertHelperIndependent::getDestinationFolder(
100
$group['root'],
101
$listOptions['destination-folder'],
102
$listOptions['ext'],
104
$listOptions['uploadDirAbs']
105
);*/
106
$group['files'] = self::getListRecursively('.', $listOptions);
107
+ //'image-root' => ConvertHelperIndependent::getDestinationFolder()
108
}
109
110
return $groups;
111
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
112
}
156
$addThis = true;
157
158
if (($filter['only-converted']) || ($filter['only-unconverted'])) {
159
+ //$cacheDir = $listOptions['image-root'] . '/' . $relDir;
160
$destination = ConvertHelperIndependent::getDestination(
161
$dir . "/" . $filename,
162
$listOptions['destination-folder'],
163
$listOptions['ext'],
164
$listOptions['webExpressContentDirAbs'],
165
+ $listOptions['uploadDirAbs'],
166
+ $listOptions['useDocRootForStructuringCacheDir'],
167
+ $listOptions['imageRoots']
168
);
169
$webpExists = @file_exists($destination);
170
lib/classes/CacheMover.php CHANGED
@@ -3,6 +3,7 @@
3
namespace WebPExpress;
4
5
use \WebPExpress\FileHelper;
6
use \WebPExpress\Paths;
7
8
class CacheMover
@@ -45,15 +46,64 @@ class CacheMover
45
FileHelper::chmod_r($dir, $dirPerm, $filePerm, $uid, $gid, '#\.webp$#', ($alsoSetOnDirs ? null : '#^$#'));
46
}
47
48
/**
49
* Move cache because of change in options.
50
- * Only move the upload folder
51
* Only move those that has an original
52
* Only move those that can be moved.
53
* @return [$numFilesMoved, $numFilesFailedMoving]
54
*/
55
public static function move($newConfig, $oldConfig)
56
{
57
$fromDir = self::getUploadFolder($oldConfig['destination-folder']);
58
$fromExt = $oldConfig['destination-extension'];
59
@@ -62,6 +112,12 @@ class CacheMover
62
63
$srcDir = self::getUploadFolder('mingled');
64
65
// for testing!
66
/*
67
$fromDir = self::getUploadFolder('mingled'); // separate | mingled
@@ -77,10 +133,6 @@ class CacheMover
77
78
//error_log('move to:' . $toDir . ' ( ' . (file_exists($toDir) ? 'exists' : 'does not exist ') . ')');
79
80
- $result = self::moveRecursively($fromDir, $toDir, $srcDir, $fromExt, $toExt);
81
- self::chmodFixSubDirs($toDir, ($newConfig['destination-folder'] == 'separate'));
82
-
83
- return $result;
84
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
85
}
86
3
namespace WebPExpress;
4
5
use \WebPExpress\FileHelper;
6
+ use \WebPExpress\PathHelper;
7
use \WebPExpress\Paths;
8
9
class CacheMover
46
FileHelper::chmod_r($dir, $dirPerm, $filePerm, $uid, $gid, '#\.webp$#', ($alsoSetOnDirs ? null : '#^$#'));
47
}
48
49
+ public static function getDestinationFolderForImageRoot($config, $imageRootId)
50
+ {
51
+ return Paths::getCacheDirForImageRoot($config['destination-folder'], $config['destination-structure'], $imageRootId);
52
+ }
53
+
54
/**
55
* Move cache because of change in options.
56
+ * If structure is unchanged, only move the upload folder
57
* Only move those that has an original
58
* Only move those that can be moved.
59
* @return [$numFilesMoved, $numFilesFailedMoving]
60
*/
61
public static function move($newConfig, $oldConfig)
62
{
63
+ if (!Paths::canUseDocRootForStructuringCacheDir()) {
64
+ if (($oldConfig['destination-structure'] == 'doc-root') || ($newConfig['destination-structure'] == 'doc-root')) {
65
+ // oh, well. Seems document root is not available.
66
+ // so we cannot move from or to that kind of structure
67
+ // This could happen if document root once was available but now is unavailable
68
+ return [0, 0];
69
+ }
70
+ }
71
+
72
+ $changeStructure = ($newConfig['destination-structure'] != $oldConfig['destination-structure']);
73
+
74
+ if ($changeStructure) {
75
+ $rootIds = Paths::getImageRootIds();
76
+ } else {
77
+ $rootIds = ['uploads'];
78
+ }
79
+
80
+ $numFilesMovedTotal = 0;
81
+ $numFilesFailedMovingTotal = 0;
82
+ foreach ($rootIds as $rootId) {
83
+
84
+ $isUploadsMingled = (($newConfig['destination-folder'] == 'mingled') && ($rootId == 'uploads'));
85
+
86
+ $fromDir = self::getDestinationFolderForImageRoot($oldConfig, $rootId);
87
+ $fromExt = $oldConfig['destination-extension'];
88
+
89
+ $toDir = self::getDestinationFolderForImageRoot($newConfig, $rootId);
90
+ $toExt = $newConfig['destination-extension'];
91
+
92
+ $srcDir = Paths::getAbsDirById($rootId);
93
+
94
+ list($numFilesMoved, $numFilesFailedMoving) = self::moveRecursively($fromDir, $toDir, $srcDir, $fromExt, $toExt);
95
+ if (!$isUploadsMingled) {
96
+ FileHelper::removeEmptySubFolders($fromDir);
97
+ }
98
+
99
+ $numFilesMovedTotal += $numFilesMoved;
100
+ $numFilesFailedMovingTotal += $numFilesFailedMoving;
101
+
102
+ $chmodFixFoldersToo = !$isUploadsMingled;
103
+ self::chmodFixSubDirs($toDir, $chmodFixFoldersToo);
104
+ }
105
+ return [$numFilesMovedTotal, $numFilesFailedMovingTotal];
106
+ /*
107
$fromDir = self::getUploadFolder($oldConfig['destination-folder']);
108
$fromExt = $oldConfig['destination-extension'];
109
112
113
$srcDir = self::getUploadFolder('mingled');
114
115
+ $result = self::moveRecursively($fromDir, $toDir, $srcDir, $fromExt, $toExt);
116
+ self::chmodFixSubDirs($toDir, ($newConfig['destination-folder'] == 'separate'));
117
+ */
118
+
119
+ //return $result;
120
+
121
// for testing!
122
/*
123
$fromDir = self::getUploadFolder('mingled'); // separate | mingled
133
134
//error_log('move to:' . $toDir . ' ( ' . (file_exists($toDir) ? 'exists' : 'does not exist ') . ')');
135
136
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
137
}
138
lib/classes/CachePurge.php CHANGED
@@ -7,6 +7,8 @@ use \WebPExpress\FileHelper;
7
use \WebPExpress\DismissableMessages;
8
use \WebPExpress\Paths;
9
10
class CachePurge
11
{
12
@@ -27,6 +29,7 @@ class CachePurge
27
$numFailed = 0;
28
29
list($numDeleted, $numFailed) = self::purgeWebPFilesInDir(Paths::getCacheDirAbs(), $filter, $config);
30
31
if ($config['destination-folder'] == 'mingled') {
32
list($d, $f) = self::purgeWebPFilesInDir(Paths::getUploadDirAbs(), $filter, $config);
@@ -35,6 +38,7 @@ class CachePurge
35
$numFailed += $f;
36
}
37
38
return [
39
'delete-count' => $numDeleted,
40
'fail-count' => $numFailed
7
use \WebPExpress\DismissableMessages;
8
use \WebPExpress\Paths;
9
10
+ // TODO! Needs to be updated to work with the new "destination-structure" setting
11
+
12
class CachePurge
13
{
14
29
$numFailed = 0;
30
31
list($numDeleted, $numFailed) = self::purgeWebPFilesInDir(Paths::getCacheDirAbs(), $filter, $config);
32
+ FileHelper::removeEmptySubFolders(Paths::getCacheDirAbs());
33
34
if ($config['destination-folder'] == 'mingled') {
35
list($d, $f) = self::purgeWebPFilesInDir(Paths::getUploadDirAbs(), $filter, $config);
38
$numFailed += $f;
39
}
40
41
+
42
return [
43
'delete-count' => $numDeleted,
44
'fail-count' => $numFailed
lib/classes/CapabilityTest.php CHANGED
@@ -1,5 +1,11 @@
1
<?php
2
3
namespace WebPExpress;
4
5
use \WebPExpress\FileHelper;
1
<?php
2
+ /*
3
+ This functionality will be moved to a separate project.
4
5
+ Btw:
6
+ Seems someone else got similar idea:
7
+ http://christian.roy.name/blog/detecting-modrewrite-using-php
8
+ */
9
namespace WebPExpress;
10
11
use \WebPExpress\FileHelper;
lib/classes/Config.php CHANGED
@@ -15,7 +15,7 @@ class Config
15
{
16
17
/**
18
- * Return object or false, if config file does not exist, or read error
19
*/
20
public static function loadJSONOptions($filename)
21
{
@@ -33,7 +33,10 @@ class Config
33
34
public static function saveJSONOptions($filename, $obj)
35
{
36
- $result = @file_put_contents($filename, json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT));
37
/*if ($result === false) {
38
echo 'COULD NOT' . $filename;
39
}*/
@@ -41,6 +44,9 @@ class Config
41
}
42
43
44
public static function loadConfig()
45
{
46
return self::loadJSONOptions(Paths::getConfigFileName());
@@ -61,10 +67,12 @@ class Config
61
'image-types' => 3,
62
'destination-folder' => 'separate',
63
'destination-extension' => 'append',
64
'cache-control' => 'no-header', /* can be "no-header", "set" or "custom" */
65
'cache-control-custom' => 'public, max-age=31536000, stale-while-revalidate=604800, stale-if-error=604800',
66
'cache-control-max-age' => 'one-week',
67
'cache-control-public' => false,
68
69
// redirection rules
70
'enable-redirection-to-converter' => true,
@@ -121,7 +129,13 @@ class Config
121
]
122
*/
123
]
124
125
]
126
];
127
}
@@ -170,6 +184,7 @@ class Config
170
]);
171
$config['alter-html']['only-for-webps-that-exists'] = true;
172
$config['web-service']['enabled'] = false;
173
174
}
175
@@ -192,15 +207,41 @@ class Config
192
$defaultConfig = self::getDefaultConfig(true);
193
$config = array_merge($defaultConfig, $config);
194
195
$config['alter-html'] = array_replace_recursive($defaultConfig['alter-html'], $config['alter-html']);
196
}
197
198
if (!isset($config['base-htaccess-on-these-capability-tests'])) {
199
self::runAndStoreCapabilityTests($config);
200
}
201
202
$config = self::applyOperationMode($config);
203
204
if (!isset($config['web-service'])) {
205
$config['web-service'] = [
206
'enabled' => false
@@ -425,16 +466,44 @@ class Config
425
unset($obj['enabled']);
426
$obj['destination-folder'] = $config['destination-folder'];
427
$obj['destination-extension'] = $config['destination-extension'];
428
$obj['bases'] = [
429
'uploads' => [
430
Paths::getUploadDirAbs(),
431
Paths::getUploadUrl()
432
],
433
- 'content' => [
434
Paths::getContentDirAbs(),
435
Paths::getContentUrl()
436
- ],
437
- ];
438
$obj['image-types'] = $config['image-types']; // 0=none,1=jpg, 2=png, 3=both
439
440
Option::updateOption(
@@ -447,9 +516,7 @@ class Config
447
public static function saveConfigurationFile($config)
448
{
449
$config['paths-used-in-htaccess'] = [
450
- 'existing' => Paths::getPathToExisting(),
451
'wod-url-path' => Paths::getWodUrlPath(),
452
- 'config-dir-rel' => Paths::getConfigDirRel()
453
];
454
455
if (Paths::createConfigDirIfMissing()) {
@@ -609,9 +676,7 @@ class Config
609
'destination-folder' => $config['destination-folder'],
610
'forward-query-string' => $config['forward-query-string'],
611
//'method-for-passing-source' => $config['method-for-passing-source'],
612
- 'paths' => [
613
- 'uploadDirRel' => Paths::getUploadDirRel()
614
- ],
615
'success-response' => $config['success-response'],
616
];
617
15
{
16
17
/**
18
+ * @return object|false Returns parsed file the file exists and can be read. Otherwise it returns false
19
*/
20
public static function loadJSONOptions($filename)
21
{
33
34
public static function saveJSONOptions($filename, $obj)
35
{
36
+ $result = @file_put_contents(
37
+ $filename,
38
+ json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT)
39
+ );
40
/*if ($result === false) {
41
echo 'COULD NOT' . $filename;
42
}*/
44
}
45
46
47
+ /**
48
+ * @return object|false Returns config object if config file exists and can be read. Otherwise it returns false
49
+ */
50
public static function loadConfig()
51
{
52
return self::loadJSONOptions(Paths::getConfigFileName());
67
'image-types' => 3,
68
'destination-folder' => 'separate',
69
'destination-extension' => 'append',
70
+ 'destination-structure' => 'doc-root', /* can be "doc-root" or "image-roots" */
71
'cache-control' => 'no-header', /* can be "no-header", "set" or "custom" */
72
'cache-control-custom' => 'public, max-age=31536000, stale-while-revalidate=604800, stale-if-error=604800',
73
'cache-control-max-age' => 'one-week',
74
'cache-control-public' => false,
75
+ 'scope' => ['themes', 'uploads'],
76
77
// redirection rules
78
'enable-redirection-to-converter' => true,
129
]
130
*/
131
]
132
+ ],
133
134
+ 'environment-when-config-was-saved' => [
135
+ 'doc-root-available' => null, // null means unavailable
136
+ 'doc-root-resolvable' => null,
137
+ 'doc-root-usable-for-structuring' => null,
138
+ 'image-roots' => null,
139
]
140
];
141
}
184
]);
185
$config['alter-html']['only-for-webps-that-exists'] = true;
186
$config['web-service']['enabled'] = false;
187
+ $config['scope'] = ['uploads'];
188
189
}
190
207
$defaultConfig = self::getDefaultConfig(true);
208
$config = array_merge($defaultConfig, $config);
209
210
+ // Make sure new defaults below "alter-html" are added into the existing array
211
+ // (note that this will not remove old unused properties, if some key should become obsolete)
212
$config['alter-html'] = array_replace_recursive($defaultConfig['alter-html'], $config['alter-html']);
213
+
214
+ // Make sure new defaults below "environment-when-config-was-saved" are added into the existing array
215
+ $config['environment-when-config-was-saved'] = array_replace_recursive($defaultConfig['environment-when-config-was-saved'], $config['environment-when-config-was-saved']);
216
}
217
218
if (!isset($config['base-htaccess-on-these-capability-tests'])) {
219
self::runAndStoreCapabilityTests($config);
220
}
221
222
+ // Apparently, migrate7 did not fix old "operation-mode" values for all.
223
+ // So fix here
224
+ if ($config['operation-mode'] == 'just-redirect') {
225
+ $config['operation-mode'] = 'no-conversion';
226
+ }
227
+ if ($config['operation-mode'] == 'no-varied-responses') {
228
+ $config['operation-mode'] = 'cdn-friendly';
229
+ }
230
+ if ($config['operation-mode'] == 'varied-responses') {
231
+ $config['operation-mode'] = 'varied-image-responses';
232
+ }
233
+
234
$config = self::applyOperationMode($config);
235
236
+ // Fix scope: Remove invalid and put in correct order
237
+ $fixedScope = [];
238
+ foreach (Paths::getImageRootIds() as $rootId) {
239
+ if (in_array($rootId, $config['scope'])) {
240
+ $fixedScope[] = $rootId;
241
+ }
242
+ }
243
+ $config['scope'] = $fixedScope;
244
+
245
if (!isset($config['web-service'])) {
246
$config['web-service'] = [
247
'enabled' => false
466
unset($obj['enabled']);
467
$obj['destination-folder'] = $config['destination-folder'];
468
$obj['destination-extension'] = $config['destination-extension'];
469
+ $obj['destination-structure'] = $config['destination-structure'];
470
+
471
+
472
+ $obj['bases'] = [];
473
+ foreach ($config['scope'] as $rootId) {
474
+ $obj['bases'][$rootId] = [
475
+ Paths::getAbsDirById($rootId),
476
+ Paths::getUrlById($rootId)
477
+ ];
478
+ }
479
+
480
+ /*
481
+ // TODO!
482
+ // Instead of "bases", use image root ids.
483
+ // Its a numeric array and there is no id called "content"
484
+
485
$obj['bases'] = [
486
'uploads' => [
487
Paths::getUploadDirAbs(),
488
Paths::getUploadUrl()
489
],
490
+ ];
491
+
492
+ if ($obj['destination-structure'] == 'doc-root') {
493
+ $obj['bases']['content'] = [
494
Paths::getContentDirAbs(),
495
Paths::getContentUrl()
496
+ ];
497
+ } else {
498
+ foreach (Paths::getImageRootIds() as $rootId) {
499
+ $obj['bases'][$rootId] = [
500
+ Paths::getAbsDirById($rootId),
501
+ Paths::getUrlById($rootId)
502
+ ];
503
+ }
504
+ }*/
505
+
506
+
507
$obj['image-types'] = $config['image-types']; // 0=none,1=jpg, 2=png, 3=both
508
509
Option::updateOption(
516
public static function saveConfigurationFile($config)
517
{
518
$config['paths-used-in-htaccess'] = [
519
'wod-url-path' => Paths::getWodUrlPath(),
520
];
521
522
if (Paths::createConfigDirIfMissing()) {
676
'destination-folder' => $config['destination-folder'],
677
'forward-query-string' => $config['forward-query-string'],
678
//'method-for-passing-source' => $config['method-for-passing-source'],
679
+ 'image-roots' => Paths::getImageRootsDef(),
680
'success-response' => $config['success-response'],
681
];
682
lib/classes/Convert.php CHANGED
@@ -5,6 +5,7 @@ namespace WebPExpress;
5
use \WebPExpress\ConvertHelperIndependent;
6
use \WebPExpress\Config;
7
use \WebPExpress\ConvertersHelper;
8
use \WebPExpress\SanityCheck;
9
use \WebPExpress\SanityException;
10
use \WebPExpress\Validate;
@@ -23,7 +24,9 @@ class Convert
23
$config['destination-folder'],
24
$config['destination-extension'],
25
Paths::getWebPExpressContentDirAbs(),
26
- Paths::getUploadDirAbs()
27
);
28
}
29
@@ -32,11 +35,17 @@ class Convert
32
try {
33
// Check source
34
// ---------------
35
- $checking = 'filename!';
36
- $filename = $source;
37
//$filename = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
38
// PS: No need to check mime type as the WebPConvert library does that (it only accepts image/jpeg and image/png)
39
40
41
// Check config
42
// --------------
@@ -76,22 +85,38 @@ class Convert
76
$logDir = SanityCheck::absPathIsInDocRoot(Paths::getWebPExpressContentDirAbs() . '/log');
77
78
79
- } catch (SanityException $e) {
80
return [
81
'success' => false,
82
- 'msg' => 'Sanitation check failed for ' . $checking . ': '. $e->getMessage(),
83
'log' => '',
84
];
85
}
86
87
// Done with sanitizing, lets get to work!
88
// ---------------------------------------
89
-
90
$result = ConvertHelperIndependent::convert($source, $destination, $convertOptions, $logDir, $converter);
91
92
if ($result['success'] === true) {
93
$result['filesize-original'] = @filesize($source);
94
$result['filesize-webp'] = @filesize($destination);
95
}
96
return $result;
97
}
@@ -127,7 +152,9 @@ class Convert
127
$destination,
128
$config['destination-folder'],
129
$config['destination-extension'],
130
- Paths::getWebPExpressContentDirAbs()
131
);
132
}
133
@@ -145,7 +172,12 @@ class Convert
145
// Check "filename"
146
$checking = '"filename" argument';
147
Validate::postHasKey('filename');
148
- $filename = sanitize_text_field($_POST['filename']);
149
//$filename = SanityCheck::absPathExistsAndIsFileInDocRoot($filename);
150
// PS: No need to check mime version as webp-convert does that.
151
5
use \WebPExpress\ConvertHelperIndependent;
6
use \WebPExpress\Config;
7
use \WebPExpress\ConvertersHelper;
8
+ use \WebPExpress\ImageRoots;
9
use \WebPExpress\SanityCheck;
10
use \WebPExpress\SanityException;
11
use \WebPExpress\Validate;
24
$config['destination-folder'],
25
$config['destination-extension'],
26
Paths::getWebPExpressContentDirAbs(),
27
+ Paths::getUploadDirAbs(),
28
+ (($config['destination-structure'] == 'doc-root') && (Paths::canUseDocRootForStructuringCacheDir())),
29
+ new ImageRoots(Paths::getImageRootsDef())
30
);
31
}
32
35
try {
36
// Check source
37
// ---------------
38
+ $checking = 'source path';
39
+ $source = SanityCheck::absPathExistsAndIsFile($source);
40
//$filename = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
41
// PS: No need to check mime type as the WebPConvert library does that (it only accepts image/jpeg and image/png)
42
43
+ // Check that source is within a valid image root
44
+ $activeRootIds = Paths::getImageRootIds(); // Currently, root ids cannot be selected, so all root ids are active.
45
+ $rootId = Paths::findImageRootOfPath($source, $activeRootIds);
46
+ if ($rootId === false) {
47
+ throw new Exception('Path of source is not within a valid image root');
48
+ }
49
50
// Check config
51
// --------------
85
$logDir = SanityCheck::absPathIsInDocRoot(Paths::getWebPExpressContentDirAbs() . '/log');
86
87
88
+ } catch (\Exception $e) {
89
return [
90
'success' => false,
91
+ 'msg' => 'Check failed for ' . $checking . ': '. $e->getMessage(),
92
'log' => '',
93
];
94
}
95
96
// Done with sanitizing, lets get to work!
97
// ---------------------------------------
98
+ //return false;
99
$result = ConvertHelperIndependent::convert($source, $destination, $convertOptions, $logDir, $converter);
100
101
if ($result['success'] === true) {
102
$result['filesize-original'] = @filesize($source);
103
$result['filesize-webp'] = @filesize($destination);
104
+ $result['destination-path'] = $destination;
105
+
106
+ $rootOfDestination = Paths::destinationRoot($rootId, $config['destination-folder'], $config['destination-structure']);
107
+
108
+ $relPathFromImageRootToSource = PathHelper::getRelDir(
109
+ realpath(Paths::getAbsDirById($rootId)),
110
+ realpath($source)
111
+ );
112
+ $relPathFromImageRootToDest = ConvertHelperIndependent::appendOrSetExtension(
113
+ $relPathFromImageRootToSource,
114
+ $config['destination-folder'],
115
+ $config['destination-extension'],
116
+ ($rootId == 'uploads')
117
+ );
118
+
119
+ $result['destination-url'] = $rootOfDestination['url'] . '/' . $relPathFromImageRootToDest;
120
}
121
return $result;
122
}
152
$destination,
153
$config['destination-folder'],
154
$config['destination-extension'],
155
+ $config['destination-structure'],
156
+ Paths::getWebPExpressContentDirAbs(),
157
+ new ImageRoots(Paths::getImageRootsDef())
158
);
159
}
160
172
// Check "filename"
173
$checking = '"filename" argument';
174
Validate::postHasKey('filename');
175
+
176
+ $filename = sanitize_text_field(stripslashes($_POST['filename']));
177
+
178
+ // holy moly! Wordpress automatically adds slashes to the global POST vars - https://stackoverflow.com/questions/2496455/why-are-post-variables-getting-escaped-in-php
179
+ $filename = wp_unslash($_POST['filename']);
180
+
181
//$filename = SanityCheck::absPathExistsAndIsFileInDocRoot($filename);
182
// PS: No need to check mime version as webp-convert does that.
183
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -47,6 +47,31 @@ class ConvertHelperIndependent
47
return strpos($normalizedSource, $normalizedDocRoot) === 0;
48
}
49
50
51
/**
52
* Get destination path corresponding to the source path given (and some configurations)
@@ -54,15 +79,24 @@ class ConvertHelperIndependent
54
* If for example Operation mode is set to "mingled" and extension is set to "Append .webp",
55
* the result of finding the destination path that corresponds to "/path/to/logo.jpg" will be "/path/to/logo.jpg.webp".
56
*
57
- * @param string $source Path to source file
58
- * @param string $destinationFolder 'mingled' or 'separate'
59
- * @param string $destinationExt Extension ('append' or 'set')
60
- * @param string $webExpressContentDirAbs
61
- * @param string $uploadDirAbs
62
*
63
* @return string|false Returns path to destination corresponding to source, or false on failure
64
*/
65
- public static function getDestination($source, $destinationFolder, $destinationExt, $webExpressContentDirAbs, $uploadDirAbs)
66
{
67
// At this point, everything has already been checked for sanity. But for good meassure, lets
68
// check the most important parts again. This is after all a public method.
@@ -77,25 +111,113 @@ class ConvertHelperIndependent
77
// Calculate destination and check that the result is sane
78
// -------------------------------------------------------
79
if (self::storeMingledOrNot($source, $destinationFolder, $uploadDirAbs)) {
80
- if ($destinationExt == 'append') {
81
- // TODO: make this check work with symlinks
82
- //$destination = SanityCheck::absPathIsInDocRoot($source . '.webp');
83
- $destination = $source . '.webp';
84
- } else {
85
- $destination = preg_replace('/\\.(jpe?g|png)#x2F;', '', $source) . '.webp';
86
- }
87
} else {
88
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
89
- $imageRoot = $webExpressContentDirAbs . '/webp-images';
90
91
- // TODO: make this check work with symlinks
92
- //SanityCheck::absPathIsInDocRoot($imageRoot);
93
94
- $sourceRel = substr($source, strlen($docRoot) + 1);
95
- $destination = $imageRoot . '/doc-root/' . $sourceRel . '.webp';
96
97
- // TODO: make this check work with symlinks
98
- //$destination = SanityCheck::absPathIsInDocRoot($destination);
99
}
100
101
} catch (SanityException $e) {
@@ -113,47 +235,140 @@ class ConvertHelperIndependent
113
* Returns false if source file is not found or if a path is not sane. Otherwise returns path to source
114
* destination does not have to exist.
115
*
116
- * @param string $destination Path to destination file (does not have to exist)
117
- * @param string $webExpressContentDirAbs
118
*
119
* @return string|false Returns path to source, if found. If not - or a path is not sane, false is returned
120
*/
121
- private static function findSourceSeparate($destination, $webExpressContentDirAbs)
122
{
123
try {
124
125
- // Check that destination path is sane and inside document root
126
- // --------------------------
127
- $destination = SanityCheck::absPathIsInDocRoot($destination);
128
129
130
- // Check that calculated image root is sane and inside document root
131
- // --------------------------
132
- $imageRoot = SanityCheck::absPathIsInDocRoot($webExpressContentDirAbs . '/webp-images/doc-root');
133
134
135
- // Calculate source and check that it is sane and exists
136
- // -----------------------------------------------------
137
138
- // TODO: This does not work on Windows yet.
139
- // NOTE: WE CANNOT DO AS WITH sourceIsInsideDocRoot, because it relies on realpath, which only translates EXISTING paths.
140
- // $destination does not exist yet, when this method is called from webp-realizer.php
141
- if (strpos($destination, $imageRoot . '/') === 0) {
142
143
- // "Eat" the left part off the $destination parameter. $destination is for example:
144
- // "/var/www/webp-express-tests/we0/wp-content-moved/webp-express/webp-images/doc-root/wordpress/uploads-moved/2018/12/tegning5-300x265.jpg.webp"
145
- // We also eat the slash (+1)
146
- $sourceRel = substr($destination, strlen($imageRoot) + 1);
147
148
- $docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
149
- $source = $docRoot . '/' . $sourceRel;
150
- $source = preg_replace('/\\.(webp)#x2F;', '', $source);
151
} else {
152
return false;
153
}
154
-
155
- $source = SanityCheck::absPathExistsAndIsFileInDocRoot($source);
156
-
157
} catch (SanityException $e) {
158