Last active
January 26, 2021 22:10
-
-
Save cantoute/3292ae4af270edf84cb5cda16e188caa to your computer and use it in GitHub Desktop.
Real simple Webp on demand based on cwebp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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