MDL-55445 files: Helper functions related to serving and caching
authorFrederic Massart <fred@moodle.com>
Thu, 11 Aug 2016 03:32:50 +0000 (11:32 +0800)
committerFrederic Massart <fred@moodle.com>
Wed, 17 Aug 2016 02:26:10 +0000 (10:26 +0800)
The function send_content_uncached() is meant to be used when serving
content which should not be cached by browsers/proxies. You will
use this in development mode, or when the request is malformed
but you need to return some fallback content which shouldn't be stored
under the URL it was requested from.

The function file_safe_save_content() should only be used for content
not stored in the file API. Good candidates for this are resource
which fit well in the localcache. The function tries to write the
content in the most atomic way as possible to prevent incomplete
writes or concurrent writes.

lib/filelib.php

index faf4ae6..e2f19c9 100644 (file)
@@ -2070,6 +2070,70 @@ function send_temp_file_finished($path) {
     }
 }
 
+/**
+ * Serve content which is not meant to be cached.
+ *
+ * This is only intended to be used for volatile public files, for instance
+ * when development is enabled, or when caching is not required on a public resource.
+ *
+ * @param string $content Raw content.
+ * @param string $filename The file name.
+ * @return void
+ */
+function send_content_uncached($content, $filename) {
+    $mimetype = mimeinfo('type', $filename);
+    $charset = strpos($mimetype, 'text/') === 0 ? '; charset=utf-8' : '';
+
+    header('Content-Disposition: inline; filename="' . $filename . '"');
+    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
+    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 2) . ' GMT');
+    header('Pragma: ');
+    header('Accept-Ranges: none');
+    header('Content-Type: ' . $mimetype . $charset);
+    header('Content-Length: ' . strlen($content));
+
+    echo $content;
+    die();
+}
+
+/**
+ * Safely save content to a certain path.
+ *
+ * This function tries hard to be atomic by first copying the content
+ * to a separate file, and then moving the file across. It also prevents
+ * the user to abort a request to prevent half-safed files.
+ *
+ * This function is intended to be used when saving some content to cache like
+ * $CFG->localcachedir. If you're not caching a file you should use the File API.
+ *
+ * @param string $content The file content.
+ * @param string $destination The absolute path of the final file.
+ * @return void
+ */
+function file_safe_save_content($content, $destination) {
+    global $CFG;
+
+    clearstatcache();
+    if (!file_exists(dirname($destination))) {
+        @mkdir(dirname($destination), $CFG->directorypermissions, true);
+    }
+
+    // Prevent serving of incomplete file from concurrent request,
+    // the rename() should be more atomic than fwrite().
+    ignore_user_abort(true);
+    if ($fp = fopen($destination . '.tmp', 'xb')) {
+        fwrite($fp, $content);
+        fclose($fp);
+        rename($destination . '.tmp', $destination);
+        @chmod($destination, $CFG->filepermissions);
+        @unlink($destination . '.tmp'); // Just in case anything fails.
+    }
+    ignore_user_abort(false);
+    if (connection_aborted()) {
+        die();
+    }
+}
+
 /**
  * Handles the sending of file data to the user's browser, including support for
  * byteranges etc.