diff --git a/CFastTrackCache.php b/CFastTrackCache.php new file mode 100644 index 0000000..39ff7b5 --- /dev/null +++ b/CFastTrackCache.php @@ -0,0 +1,231 @@ +enabled = $enabled; + return $this; + } + + + + /** + * Set the path to the cache dir which must exist. + * + * @param string $path to the cache dir. + * + * @throws Exception when $path is not a directory. + * + * @return $this + */ + public function setCacheDir($path) + { + if (!is_dir($path)) { + throw new Exception("Cachedir is not a directory."); + } + + $this->path = rtrim($path, "/"); + + return $this; + } + + + + /** + * Set the filename to store in cache, use the querystring to create that + * filename. + * + * @param array $clear items to clear in $_GET when creating the filename. + * + * @return string as filename created. + */ + public function setFilename($clear) + { + $query = $_GET; + + // Remove parts from querystring that should not be part of filename + foreach ($clear as $value) { + unset($query[$value]); + } + + arsort($query); + $queryAsString = http_build_query($query); + + $this->filename = md5($queryAsString); + $this->container["query-string"] = $queryAsString; + + return $this->filename; + } + + + + /** + * Add header items. + * + * @param string $header add this as header. + * + * @return $this + */ + public function addHeader($header) + { + $this->container["header"][] = $header; + return $this; + } + + + + /** + * Add header items on output, these are not output when 304. + * + * @param string $header add this as header. + * + * @return $this + */ + public function addHeaderOnOutput($header) + { + $this->container["header-output"][] = $header; + return $this; + } + + + + /** + * Set path to source image to. + * + * @param string $source path to source image file. + * + * @return $this + */ + public function setSource($source) + { + $this->container["source"] = $source; + return $this; + } + + + + /** + * Set last modified of source image, use to check for 304. + * + * @param string $lastModified + * + * @return $this + */ + public function setLastModified($lastModified) + { + $this->container["last-modified"] = $lastModified; + return $this; + } + + + + /** + * Get filename of cached item. + * + * @return string as filename. + */ + public function getFilename() + { + return $this->path . "/" . $this->filename; + } + + + + /** + * Write current item to cache. + * + * @return boolean if cache file was written. + */ + public function writeToCache() + { + if (!$this->enabled) { + return false; + } + + if (is_dir($this->path) && is_writable($this->path)) { + $filename = $this->getFilename(); + return file_put_contents($filename, json_encode($this->container)) !== false; + } + + return false; + } + + + + /** + * Output current item from cache, if available. + * + * @return void + */ + public function output() + { + $filename = $this->getFilename(); + if (!is_readable($filename)) { + return; + } + + $item = json_decode(file_get_contents($filename), true); + + if (!is_readable($item["source"])) { + return; + } + + foreach ($item["header"] as $value) { + header($value); + } + + if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) + && strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]) == $item["last-modified"]) { + header("HTTP/1.0 304 Not Modified"); + debug("fast track 304"); + exit; + } + + foreach ($item["header-output"] as $value) { + header($value); + } + + readfile($item["source"]); + debug("fast track 200"); + exit; + } +} diff --git a/CImage.php b/CImage.php index d30b33e..d0db1d2 100644 --- a/CImage.php +++ b/CImage.php @@ -379,6 +379,12 @@ class CImage private $useCache = true; + /** + * Disable the fasttrackCacke to start with, inject an object to enable it. + */ + private $fastTrackCache = null; + + /* * Set whitelist for valid hostnames from where remote source can be @@ -446,6 +452,25 @@ class CImage + /** + * Inject object and use it, must be available as member. + * + * @param string $property to set as object. + * @param object $object to set to property. + * + * @return $this + */ + public function injectDependency($property, $object) + { + if (!property_exists($this, $property)) { + $this->raiseError("Injecting unknown property."); + } + $this->$property = $object; + return $this; + } + + + /** * Set verbose mode. * @@ -2520,7 +2545,7 @@ class CImage /** - * Add HTTP header for putputting together with image. + * Add HTTP header for output together with image. * * @param string $type the header type such as "Cache-Control" * @param string $value the value to use @@ -2571,14 +2596,20 @@ class CImage // Get image modification time clearstatcache(); $lastModified = filemtime($file); - $gmdate = gmdate("D, d M Y H:i:s", $lastModified); + $lastModifiedFormat = "D, d M Y H:i:s"; + $gmdate = gmdate($lastModifiedFormat, $lastModified); if (!$this->verbose) { - header('Last-Modified: ' . $gmdate . " GMT"); + $header = "Last-Modified: $gmdate GMT"; + header($header); + $this->fastTrackCache->addHeader($header); + $this->fastTrackCache->setLastModified($lastModified); } - foreach($this->HTTPHeader as $key => $val) { - header("$key: $val"); + foreach ($this->HTTPHeader as $key => $val) { + $header = "$key: $val"; + header($header); + $this->fastTrackCache->addHeader($header); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) { @@ -2590,6 +2621,7 @@ class CImage } header("HTTP/1.0 304 Not Modified"); + debug("standard 304"); } else { @@ -2610,9 +2642,18 @@ class CImage } } - header("Content-type: $mime"); - header("Content-length: $size"); + $header = "Content-type: $mime"; + header($header); + $this->fastTrackCache->addHeaderOnOutput($header); + + $header = "Content-length: $size"; + header($header); + $this->fastTrackCache->addHeaderOnOutput($header); + + $this->fastTrackCache->setSource($file); + $this->fastTrackCache->writeToCache(); readfile($file); + debug("standard 200"); } exit; diff --git a/cache/README.md b/cache/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/webroot/img.php b/webroot/img.php index b9edcd2..e416808 100644 --- a/webroot/img.php +++ b/webroot/img.php @@ -14,6 +14,17 @@ $version = "v0.7.12 (2016-06-01)"; define("CIMAGE_USER_AGENT", "CImage/$version"); +// Include debug functions +function debug($msg) +{ + $file = "/tmp/cimage"; + $msg .= ":" . count(get_included_files()); + $msg .= ":" . round(memory_get_peak_usage()/1024/1024, 3) . "MB"; + $msg .= ":" . (string) round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']), 6) . "ms"; + file_put_contents($file, "$msg\n", FILE_APPEND); + +} + /** * Display error message. @@ -348,7 +359,8 @@ if ($autoloader) { /** * Create the class for the image. */ -$img = new CImage(); +$CImage = getConfig('CImage', 'CImage'); +$img = new $CImage(); $img->setVerbose($verbose || $verboseFile); @@ -356,12 +368,46 @@ $img->setVerbose($verbose || $verboseFile); /** * Get the cachepath from config. */ +$CCache = getConfig('CCache', 'CCache'); $cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); -$cache = new CCache(); +$cache = new $CCache(); $cache->setDir($cachePath); +/** + * no-cache, nc - skip the cached version and process and create a new version in cache. + */ +$useCache = getDefined(array('no-cache', 'nc'), false, true); + +verbose("use cache = $useCache"); + + + +/** + * Prepare fast track cache for swriting cache items. + */ +$fastTrackCache = "fasttrack"; +$allowFastTrackCache = getConfig('fast_track_allow', false); + +$CFastTrackCache = getConfig('CFastTrackCache', 'CFastTrackCache'); +$ftc = new $CFastTrackCache(); +$ftc->setCacheDir($cache->getPathToSubdir($fastTrackCache)) + ->enable($allowFastTrackCache) + ->setFilename(array('no-cache', 'nc')); +$img->injectDependency("fastTrackCache", $ftc); + + + +/** + * Load and output images from fast track cache, if items are available + * in cache. + */ +if ($useCache && $allowFastTrackCache) { + $ftc->output(); +} + + /** * Allow or disallow remote download of images from other servers. @@ -707,15 +753,6 @@ verbose("use original = $useOriginal"); -/** - * no-cache, nc - skip the cached version and process and create a new version in cache. - */ -$useCache = getDefined(array('no-cache', 'nc'), false, true); - -verbose("use cache = $useCache"); - - - /** * quality, q - set level of quality for jpeg images */ @@ -1082,6 +1119,9 @@ if ($status) { $res = $cache->getStatusOfSubdir("srgb"); $text .= "Cache srgb $res\n"; + $res = $cache->getStatusOfSubdir($fasttrackCache); + $text .= "Cache fasttrack $res\n"; + $text .= "Alias path writable = " . is_writable($aliasPath) . "\n"; $no = extension_loaded('exif') ? null : 'NOT'; diff --git a/webroot/img_config.php b/webroot/img_config.php index 5eb59a0..1f3ca27 100644 --- a/webroot/img_config.php +++ b/webroot/img_config.php @@ -23,7 +23,7 @@ return array( * mode: 'production' */ //'mode' => 'production', - //'mode' => 'development', + 'mode' => 'development', //'mode' => 'strict', @@ -45,16 +45,42 @@ return array( * End all paths with a slash. * * Default values: - * image_path: __DIR__ . '/img/' - * cache_path: __DIR__ . '/../cache/' - * alias_path: null + * image_path: __DIR__ . '/img/' + * cache_path: __DIR__ . '/../cache/' + * alias_path: null */ - 'image_path' => __DIR__ . '/img/', - 'cache_path' => __DIR__ . '/../cache/', + 'image_path' => __DIR__ . '/img/', + 'cache_path' => __DIR__ . '/../cache/', //'alias_path' => __DIR__ . '/img/alias/', + /** + * Fast track cache. Save a json representation of the image as a + * fast track to the cached version of the image. This avoids some + * processing and allows for quicker load times of cached images. + * + * Default values: + * fast_track_allow: false + */ + 'fast_track_allow' => true, + + + + /** + * Class names to use, to ease dependency injection. + * + * Default values: + * CImage: CImage + * CCache: CCache + * CFastTrackCache: CFastTrackCache + */ + //'CImage' => 'CImage', + //'CCache' => 'CCache', + //'CFastTrackCache' => 'CFastTrackCache', + + + /** * Use password to protect from missusage, send &pwd=... or &password=.. * with the request to match the password or set to false to disable.