Skip to content

Instantly share code, notes, and snippets.

@cantoute
Last active January 26, 2021 22:10
Show Gist options
  • Save cantoute/3292ae4af270edf84cb5cda16e188caa to your computer and use it in GitHub Desktop.
Save cantoute/3292ae4af270edf84cb5cda16e188caa to your computer and use it in GitHub Desktop.
Real simple Webp on demand based on cwebp
<?php
/******************
* .htaccess exemple
******************
<IfModule mod_rewrite.c>
RewriteEngine On
# Redirect to existing converted image in cache-dir (if browser supports webp)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME}.webp -f
RewriteRule ^/?(.+)\.(jpe?g|png)$ /$1.$2.webp [NC,T=image/webp,E=EXISTING:1,E=ADDVARY:1,L]
# Redirect images to webp-on-demand.php (if browser supports webp)
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^/?(.+)\.(jpe?g|png)$ /images/webp-on-demand.php?path=/$1.$2 [NC,L,E=REQFN:%{REQUEST_FILENAME}]
# Make sure that browsers which does not support webp also gets the Vary:Accept header
# when requesting images that would be redirected to webp on browsers that does.
<IfModule mod_headers.c>
<FilesMatch "(?i)\.(jpe?g|png)$">
Header append "Vary" "Accept"
</FilesMatch>
</IfModule>
</IfModule>
*/
ignore_user_abort(true);
set_time_limit(90);
$vroot = realpath(dirname(__FILE__));
function command_exist($cmd) {
$return = shell_exec(sprintf("which %s", escapeshellarg($cmd)));
if (empty($return)) {
$return = false;
}
return trim($return);
}
$nice = command_exist('nice');
$imgopt = command_exist('imgopt');
$cwebp = command_exist('cwebp');
if ($nice === false) {
$nice = '';
} else {
// using full path to nice bugs cwebp
$nice = $nice . ' ';
}
if ($cwebp === false) {
http_response_code(500);
die("Command cwebp could not be found in PATH: ".getenv('PATH'));
} else {
$cwebp = $nice . $cwebp;
}
if ($imgopt !== false) {
$imgopt = $nice . $imgopt;
}
if ($_GET['path']) {
$path = realpath(dirname(__FILE__).$_GET["path"]);
} else {
$path = realpath(dirname(__FILE__).$_SERVER["PATH_INFO"]);
}
if (
substr($path, 0, strlen($vroot)) !== $vroot // path outside of vroot
) {
http_response_code(404);
die('Error 404');
}
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (
!in_array($ext, Array('jpg', 'jpeg', 'png'))
) {
http_response_code(400);
die('Can only be used with jpg/jpeg or png!');
}
if( strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) === false ) {
// webp not supported, lets send original
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($path)).' GMT', true, 200);
if($ext === 'png') {
header('Content-Type: image/png');
} else {
header('Content-Type: image/jpeg');
}
header('Content-Length: ' . filesize($path));
print file_get_contents($path);
} else {
$webp_path = $path . '.webp';
$lock = $webp_path . '.lock';
// credit https://stackoverflow.com/questions/5449395/file-locking-in-php
// atomic, simpler and faster than flock!
// https://docstore.mik.ua/orelly/webprog/pcook/ch18_25.htm
// loop until we can successfully make the lock directory
$locked = 0;
while (! $locked) {
if (
@mkdir($lock, 0777)
|| time() - filemtime($lock.'/.') > 30 // if lock is older than 30s bypass it
) {
$locked = 1;
} else {
sleep(1);
}
}
// if during lock other process yet had converted the file
if(
file_exists($webp_path)
&& filesize($webp_path) > 0
) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($webp_path)).' GMT', true, 200);
header('Content-Type: image/webp');
header('Content-Length: ' . filesize($webp_path));
print file_get_contents($webp_path);
} else {
touch($lock); // in case of dead lock, try limit other waiting running in here
if ($imgopt !== false) {
// run imgopt on original file - strip out exif and aggressively recompress png
$cmd = $imgopt . ' ' . $path;
$output = null;
$retval = null;
exec($cmd, $output, $retval);
}
if($ext == 'jpg' || $ext === 'jpeg') {
$cmd = $cwebp . ' -quiet -z 6 -q 80 -o "'. $webp_path .'" -- "'. $path .'"';
} else {
// png
$cmd = $cwebp . ' -quiet -z 6 -alpha_q 80 -near_lossless 60 -lossless -o "' . $webp_path .'" -- "'. $path .'"';
}
$output = null;
$retval = null;
exec($cmd, $output, $retval);
if(
$retval === 0
&& file_exists($webp_path)
&& filesize($webp_path) > 0
) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($webp_path)).' GMT', true, 200);
header('Content-Type: image/webp');
header('Content-Length: ' . filesize($webp_path));
print file_get_contents($webp_path);
} else {
http_response_code(500);
print $cmd;
print '<pre>';
print_r($output);
print '</pre>';
}
}
// release lock
@rmdir($lock);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment