/** * Send javascript file content with as much caching as possible * @param string $jspath * @param string $etag * @param string $filename */ function js_send_cached($jspath, $etag, $filename = 'javascript.php') { require(__DIR__ . '/xsendfilelib.php'); $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often header('Etag: "'.$etag.'"'); header('Content-Disposition: inline; filename="'.$filename.'"'); header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($jspath)) .' GMT'); header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); header('Pragma: '); header('Cache-Control: public, max-age='.$lifetime); header('Accept-Ranges: none'); header('Content-Type: application/javascript; charset=utf-8'); if (xsendfile($jspath)) { die; } if (!min_enable_zlib_compression()) { header('Content-Length: '.filesize($jspath)); } readfile($jspath); die; }
/** * Enhanced readfile() with optional acceleration. * @param string|stored_file $file * @param string $mimetype * @param bool $accelerate * @return void */ function readfile_accel($file, $mimetype, $accelerate) { global $CFG; if ($mimetype === 'text/plain') { // there is no encoding specified in text files, we need something consistent header('Content-Type: text/plain; charset=utf-8'); } else { header('Content-Type: ' . $mimetype); } $lastmodified = is_object($file) ? $file->get_timemodified() : filemtime($file); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastmodified) . ' GMT'); if (is_object($file)) { header('Etag: "' . $file->get_contenthash() . '"'); if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $file->get_contenthash()) { header('HTTP/1.1 304 Not Modified'); return; } } // if etag present for stored file rely on it exclusively if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) and (empty($_SERVER['HTTP_IF_NONE_MATCH']) or !is_object($file))) { // get unixtime of request header; clip extra junk off first $since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"])); if ($since && $since >= $lastmodified) { header('HTTP/1.1 304 Not Modified'); return; } } if ($accelerate and !empty($CFG->xsendfile)) { if (empty($CFG->disablebyteserving) and $mimetype !== 'text/plain') { header('Accept-Ranges: bytes'); } else { header('Accept-Ranges: none'); } if (is_object($file)) { $fs = get_file_storage(); if ($fs->xsendfile($file->get_contenthash())) { return; } } else { require_once "{$CFG->libdir}/xsendfilelib.php"; if (xsendfile($file)) { return; } } } $filesize = is_object($file) ? $file->get_filesize() : filesize($file); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastmodified) . ' GMT'); if ($accelerate and empty($CFG->disablebyteserving) and $mimetype !== 'text/plain') { header('Accept-Ranges: bytes'); if (!empty($_SERVER['HTTP_RANGE']) and strpos($_SERVER['HTTP_RANGE'], 'bytes=') !== FALSE) { // byteserving stuff - for acrobat reader and download accelerators // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 // inspired by: http://www.coneural.org/florian/papers/04_byteserving.php $ranges = false; if (preg_match_all('/(\\d*)-(\\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) { foreach ($ranges as $key => $value) { if ($ranges[$key][1] == '') { //suffix case $ranges[$key][1] = $filesize - $ranges[$key][2]; $ranges[$key][2] = $filesize - 1; } else { if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) { //fix range length $ranges[$key][2] = $filesize - 1; } } if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) { //invalid byte-range ==> ignore header $ranges = false; break; } //prepare multipart header $ranges[$key][0] = "\r\n--" . BYTESERVING_BOUNDARY . "\r\nContent-Type: {$mimetype}\r\n"; $ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/{$filesize}\r\n\r\n"; } } else { $ranges = false; } if ($ranges) { if (is_object($file)) { $handle = $file->get_content_file_handle(); } else { $handle = fopen($file, 'rb'); } byteserving_send_file($handle, $mimetype, $ranges, $filesize); } } } else { // Do not byteserve header('Accept-Ranges: none'); } header('Content-Length: ' . $filesize); if ($filesize > 10000000) { // for large files try to flush and close all buffers to conserve memory while (@ob_get_level()) { if (!@ob_end_flush()) { break; } } } // send the whole file content if (is_object($file)) { $file->readfile(); } else { readfile_allow_large($file, $filesize); } }
function yui_image_cached($imagepath, $imagename, $mimetype, $etag) { global $CFG; require("$CFG->dirroot/lib/xsendfilelib.php"); $lifetime = 60*60*24*360; // 1 year, we do not change YUI versions often, there are a few custom yui modules header('Content-Disposition: inline; filename="'.$imagename.'"'); header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($imagepath)) .' GMT'); header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); header('Pragma: '); header('Cache-Control: public, max-age=315360000, no-transform'); header('Accept-Ranges: none'); header('Content-Type: '.$mimetype); header('Content-Length: '.filesize($imagepath)); header('Etag: "'.$etag.'"'); if (xsendfile($imagepath)) { die; } // no need to gzip already compressed images ;-) readfile($imagepath); die; }
/** * Serve file content using X-Sendfile header. * Please make sure that all headers are already sent * and the all access control checks passed. * * @param string $contenthash sah1 hash of the file content to be served * @return bool success */ public function xsendfile($contenthash) { global $CFG; require_once "{$CFG->libdir}/xsendfilelib.php"; $hashpath = $this->path_from_hash($contenthash); return xsendfile("{$hashpath}/{$contenthash}"); }
header('Etag: '.$etag); die; } require_once("$CFG->dirroot/lib/xsendfilelib.php"); header('Etag: '.$etag); header('Content-Disposition: inline; filename="'.$filename.'"'); header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($file)) .' GMT'); header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); header('Pragma: '); header('Cache-Control: public, max-age='.$lifetime); header('Accept-Ranges: none'); header('Content-Type: '.$mimetype); if (xsendfile($file)) { die; } if ($mimetype === 'text/css' or $mimetype === 'application/javascript') { if (!min_enable_zlib_compression()) { header('Content-Length: '.filesize($file)); } } else { // No need to compress images. header('Content-Length: '.filesize($file)); } readfile($file); die;
function send_cached_font($fontpath, $etag, $font, $mimetype) { global $CFG; require "{$CFG->dirroot}/lib/xsendfilelib.php"; $lifetime = 60 * 60 * 24 * 60; // 60 days only - the revision may get incremented quite often. header('Etag: "' . $etag . '"'); header('Content-Disposition: inline; filename="' . $font . '"'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($fontpath)) . ' GMT'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); header('Pragma: '); header('Cache-Control: public, max-age=' . $lifetime); header('Accept-Ranges: none'); header('Content-Type: ' . $mimetype); header('Content-Length: ' . filesize($fontpath)); if (xsendfile($fontpath)) { die; } // No need to gzip already compressed fonts. readfile($fontpath); die; }
function send_cached_image($imagepath, $etag) { global $CFG; require "{$CFG->dirroot}/lib/xsendfilelib.php"; $lifetime = 60 * 60 * 24 * 60; // 60 days only - the revision may get incremented quite often $pathinfo = pathinfo($imagepath); $imagename = $pathinfo['filename'] . '.' . $pathinfo['extension']; $mimetype = get_contenttype_from_ext($pathinfo['extension']); header('Etag: "' . $etag . '"'); header('Content-Disposition: inline; filename="' . $imagename . '"'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($imagepath)) . ' GMT'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); header('Pragma: '); header('Cache-Control: public, max-age=' . $lifetime); header('Accept-Ranges: none'); header('Content-Type: ' . $mimetype); header('Content-Length: ' . filesize($imagepath)); if (xsendfile($imagepath)) { die; } // no need to gzip already compressed images ;-) readfile($imagepath); die; }
/** * Serve file content using X-Sendfile header. * Please make sure that all headers are already sent * and the all access control checks passed. * * @param string $contenthash sah1 hash of the file content to be served * @return bool success */ public function xsendfile($contenthash) { global $CFG; require_once("$CFG->libdir/xsendfilelib.php"); $hashpath = $this->path_from_hash($contenthash); return xsendfile("$hashpath/$contenthash"); }