<?php
// WEBPSHIM 22-07 version
/*\

add these rewrite lines to your .htaccess file:
##WEBPSHIM-BEGIN - DO NOT EDIT!
RewriteCond %{REQUEST_FILENAME} (?i).+\.(jpe?g|png)$
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond "%{DOCUMENT_ROOT}/webpshim_cache/%{REQUEST_URI}.webp" -f
RewriteCond %{QUERY_STRING} !^nowebp
RewriteRule (?i).+\.(jpe?g|png)$ /webpshim_cache/%{REQUEST_URI}.webp [T=image/webp,E=accept:1,L]

RewriteCond %{REQUEST_FILENAME} (?i).+\.(jpe?g|png)$
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond "%{DOCUMENT_ROOT}/webpshim_cache/%{REQUEST_URI}.webp" !-f
RewriteCond %{REQUEST_URI} !admin
RewriteCond %{QUERY_STRING} !^nowebp
RewriteRule (?i).+\.(jpe?g|png)$ /webpshim.php [T=image/webp,E=accept:1,L]
#RewriteRule (?i).+\.(jpe?g|png)$ /webpshim.php [T=image/webp,H=application/x-httpd-alt-php74,E=accept:1,L]

<IfModule mod_headers.c>
Header append Vary Accept env=REDIRECT_accept
</IfModule>
AddType image/webp .webp
##WEBPSHIM-END

optional: add a cron job to clear the webp cache regularly

\*/

function mb_stringbeforelast($haystack, $needle='.') {
	if (($pos = mb_strrpos($haystack, $needle)) === false) {
		return false;
	}

	return mb_substr($haystack, 0, $pos);
}

function mb_stringafterlast($haystack, $needle='.') {
	if (($pos = mb_strrpos($haystack, $needle)) === false) {
		return false;
	}

	return mb_substr($haystack, $pos + 1);
}

$debug = false;
function debuglog($s) {
	global $debug; if (!$debug) return;

	file_put_contents(__DIR__ . '/webpshim.debug.log', date('Y-m-d H:i:s') . ' - ' . print_r($s, true) . "\n", FILE_APPEND);
}

$DIR_ROOT = str_replace('//' ,'/', rtrim($_SERVER['DOCUMENT_ROOT'], '/').'/');

// TODO put these in an ini file
$WEBP_QUALITY = 75;
$logging = true;

////////////////////////////////////////////////////////////////////////////////

//// get and validate file path
$source_path = rawurldecode($_SERVER['REQUEST_URI']);

// remove query string
if (strpos($source_path,'?') !== false) {
	$source_path = mb_stringbeforelast($source_path,'?');
}

// remove leading slash
if (mb_substr($source_path, 0, 1) === '/') {
	$source_path = mb_substr($source_path, 1);
}
$source_fullpath = $DIR_ROOT . $source_path;

if (!file_exists($source_fullpath)) {
	http_response_code(404);
	exit;
}

//// get and validate file extension
$source_ext = mb_strtolower(mb_stringafterlast($source_path, '.'));

if (!in_array($source_ext, ['jpg', 'jpeg', 'png'])) {
	// not a supported format, read file contents directly and exit

	header('Accept-Ranges: bytes');
	header('Content-Length: ' . filesize($source_fullpath));

	$mime = mime_content_type($source_fullpath);

	if ($mime) {
		header('Content-Type: ' . $mime);	
	}

	header_remove('Server');
	header_remove('x-powered-by');

	if (function_exists('apache_setenv')) {
		apache_setenv('no-gzip', 1);
	} else {
		header('Content-Encoding: none');
		header_remove('Content-Encoding');
	}

	readfile($source_fullpath);

	exit;
}

//// get source image details, set up initial dest image details
$source_size = filesize($source_fullpath);
$source_mime = getimagesize($source_fullpath)['mime'];

$dest_path = $source_path . '.webp';
$dest_fullpath = $DIR_ROOT . 'webpshim_cache/' . $dest_path;
$dest_size = 0; // filesize of destination webp (once generated)

$created = false; // whether the webp was created

if(mb_strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false) {
	// client supports WebP

	if (!is_dir(dirname($dest_fullpath))) {
		mkdir(dirname($dest_fullpath), 0755, true);
	}

	//// try using imagemagick library first, fallback to gd library

	if ( // try using imagemagick library first
		extension_loaded('imagick')
		&& Imagick::queryFormats('WEBP')
		&& $image = new Imagick($source_fullpath)
	) {
		$image->stripImage();

		$image->setImageFormat('webp');
		$image->setImageCompressionQuality($WEBP_QUALITY); // HARDCODED (for now)
		// TODO: support for lossless WebP when converting from png

		if ($source_ext == 'png') {
			$image->setImageAlphaChannel(imagick::ALPHACHANNEL_ACTIVATE);
			$image->setBackgroundColor(new ImagickPixel('transparent'));
		}

		if ($image->writeImage($dest_fullpath)) {
			$created = true;
			$dest_size = filesize($dest_fullpath);
		} 

		$image->clear();
	} elseif ( // fallback to gd library
		gd_info()['WebP Support']
		&& function_exists('imagewebp')
		&& function_exists($imagecreate_func = "imagecreatefrom".strtolower(substr($source_mime, strpos($source_mime, '/') + 1)))
		&& ($image = $imagecreate_func($source_fullpath)) !== false
	) {
		if ($source_ext === 'png') {
			imagepalettetotruecolor($image);
			//imagealphablending($image, true);
			//imagesavealpha($image, true);
		}

		if (imagewebp($image, $dest_fullpath, $WEBP_QUALITY)) {
			$created = true;
			$dest_size = filesize($dest_fullpath);
		}

		imagedestroy($image);
	}

	if ($dest_size) {
		// webp successfully created
		$dest_mime = getimagesize($dest_fullpath)['mime'];
	} elseif ($created && file_exists($dest_fullpath)) {
		// reported webp was created but filesize is 0 - likely webp creation failed, remove empty webp and return original file
		unlink($dest_fullpath);
		$created = false;
	}
}

if ($dest_size) {
	$return_size = $dest_size;
	$return_mime = $dest_mime;
	$return_fullpath = $dest_fullpath;
} else {
	// either webp failed to generate or client does not support webp - return original image
	$return_size = $source_size;
	$return_mime = $source_mime;
	$return_fullpath = $source_fullpath;
}

//// now return the image
if (ob_get_level()) {
	ob_end_clean(); // just in case...
}

http_response_code(200);

header('Accept-Ranges: bytes');
header('Content-Length: ' . $return_size);
header('Content-Type: ' . $return_mime);

header_remove('Server');
header_remove('x-powered-by');

if ($created) {
	header('X-Gen: webpshim');
}

if (function_exists('apache_setenv')) {
	apache_setenv('no-gzip', 1);
} else {
	header('Content-Encoding: none');
	header_remove('Content-Encoding');
}

readfile($return_fullpath);

exit;