Skip to content

Instantly share code, notes, and snippets.

@sminnee
Created June 1, 2018 03:51
Show Gist options
  • Select an option

  • Save sminnee/83f607a09c27b1c0dc59d71b819fa401 to your computer and use it in GitHub Desktop.

Select an option

Save sminnee/83f607a09c27b1c0dc59d71b819fa401 to your computer and use it in GitHub Desktop.
diff --git a/control/Controller.php b/control/Controller.php
index 274b567..484c06f 100644
--- a/control/Controller.php
+++ b/control/Controller.php
@@ -502,7 +502,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
*/
public function redirectBack() {
// Don't cache the redirect back ever
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
$url = null;
diff --git a/control/Director.php b/control/Director.php
index b98731e..095e700 100644
--- a/control/Director.php
+++ b/control/Director.php
@@ -384,7 +384,7 @@ class Director implements TemplateGlobalProvider {
try {
$result = $controllerObj->handleRequest($request, $model);
} catch(SS_HTTPResponse_Exception $responseException) {
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
$result = $responseException->getResponse();
}
if(!is_object($result) || $result instanceof SS_HTTPResponse) return $result;
diff --git a/control/FlushRequestFilter.php b/control/FlushRequestFilter.php
index 64d8de6..5a6e295 100644
--- a/control/FlushRequestFilter.php
+++ b/control/FlushRequestFilter.php
@@ -19,7 +19,7 @@ class FlushRequestFilter implements RequestFilter {
public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) {
if(array_key_exists('flush', $request->getVars())) {
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
}
return true;
}
diff --git a/control/HTTP.php b/control/HTTP.php
index b93df19..5df1481 100644
--- a/control/HTTP.php
+++ b/control/HTTP.php
@@ -341,9 +341,9 @@ class HTTP {
$config = Config::inst()->forClass(__CLASS__);
// if http caching is disabled by config, disable it - used on dev environments due to frequently changing
- // templates and other data
+ // templates and other data. will be overridden by forced publicCache() or privateCache() calls
if ($config->get('disable_http_cache')) {
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache();
}
// Populate $responseHeaders with all the headers that we want to build
@@ -356,7 +356,7 @@ class HTTP {
$requestHeaders = array_change_key_case(apache_request_headers(), CASE_LOWER);
if (array_key_exists('x-requested-with', $requestHeaders) && strtolower($requestHeaders['x-requested-with']) == 'xmlhttprequest') {
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
}
}
@@ -400,11 +400,12 @@ class HTTP {
// (http://support.microsoft.com/kb/323308)
// Note: this is also fixable by ticking "Do not save encrypted pages to disk" in advanced options.
HTTPCacheControl::singleton()
- ->privateCache()
+ ->privateCache(true)
->removeDirective('no-cache')
->removeDirective('no-store');
}
+ // TODO: These risk overriding nocache / privatecache calls. Perhaps this should only be applied if caching is unspecified or specified as public
if (!empty($cacheControlHeaders)) {
HTTPCacheControl::singleton()->setDirectivesFromArray($cacheControlHeaders);
}
diff --git a/control/HTTPCacheControl.php b/control/HTTPCacheControl.php
index c538a50..dbfd373 100644
--- a/control/HTTPCacheControl.php
+++ b/control/HTTPCacheControl.php
@@ -20,11 +20,33 @@ class HTTPCacheControl extends SS_Object {
private $state = array();
/**
- * Whether the cache-control object is locked to further changes
- *
- * @var bool
+ * Forcing level of previous setting; higher number wins
+ * Combination of consts belo
+ *w
+ * @var int
*/
- protected $locked = false;
+ protected $forcingLevel = 0;
+
+ /**
+ * Forcing level forced, optionally combined with one of the below.
+ */
+ private const LEVEL_FORCED = 10;
+
+ /**
+ * Forcing level caching disabled. Overrides public/private.
+ */
+ private const LEVEL_DISABLED = 3;
+
+ /**
+ * Forcing level private-cached. Overrides public.
+ */
+ private const LEVEL_PRIVATE = 2;
+
+ /**
+ * Forcing level public cached. Lowest priority.
+ */
+ private const LEVEL_PUBLIC = 1;
+
/**
* A list of allowed cache directives for HTTPResponses
@@ -48,17 +70,6 @@ class HTTPCacheControl extends SS_Object {
);
/**
- * Lock the current state of the cache control to prevent further modifications
- *
- * @return $this
- */
- public function lock()
- {
- $this->locked = true;
- return $this;
- }
-
- /**
* Low level method for setting directives include any experimental or custom ones added via config
*
* @param string $directive
@@ -203,35 +214,6 @@ class HTTPCacheControl extends SS_Object {
}
/**
- * Indicates that the response may be cached by any cache. (eg: CDNs, Proxies, Web browsers)
- *
- * Also removes `public` as this is a contradictory directive
- *
- * @return $this
- */
- public function setPublic()
- {
- $this->setDirective('public');
- $this->removeDirective('private');
- return $this;
- }
-
- /**
- * Indicates that the response is intended for a single user and must not be stored by a shared cache.
- * A private cache may store the response.
- *
- * Also removes `private` as this is a contradictory directive
- *
- * @return $this
- */
- public function setPrivate()
- {
- $this->setDirective('private');
- $this->removeDirective('public');
- return $this;
- }
-
- /**
* Specifies the maximum amount of time (seconds) a resource will be considered fresh.
* This directive is relative to the time of the request.
*
@@ -282,44 +264,68 @@ class HTTPCacheControl extends SS_Object {
* Removes all state and replaces it with `no-cache, no-store, must-revalidate`. Although `no-store` is sufficient
* the others are added under recommendation from Mozilla (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Examples)
*
+ * This will take precendence over unforced privateCache / publicCache calls
+ *
+ * @param bool $force Force the cache to private even if it's forced private or public
* @return $this
*/
- public function disableCaching()
+ public function disableCache($force = false)
{
+ // Only exeucute this if its forcing level is high enough
+ $forcingLevel = self::LEVEL_DISABLED + ($force ? self::LEVEL_FORCED : 0);
+ $this->forcingLevel = $forcingLevel;
+
$this->state = array(
'no-cache' => null,
'no-store' => null,
'must-revalidate' => null,
);
- $this->lock();
return $this;
}
/**
- * Helper function to set the current cache to private
+ * Indicates that the response is intended for a single user and must not be stored by a shared cache.
+ * A private cache may store the response.
+ *
+ * Also removes `private` as this is a contradictory directive
*
+ * @param bool $force Force the cache to private even if it's forced public
* @return $this
*/
- public function privateCache()
+ public function privateCache($force = false)
{
- $this->setPrivate();
+ // Only exeucute this if its forcing level is high enough
+ $forcingLevel = self::LEVEL_PRIVATE + ($force ? self::LEVEL_FORCED : 0);
+ if ($forcingLevel < $this->forcingLevel) {
+ return;
+ }
+ $this->forcingLevel = $forcingLevel;
+
+ // Update the directives
+ $this->setDirective('private');
+ $this->removeDirective('public');
return $this;
}
/**
- * Helper function to set the current cache to public
+ * Indicates that the response may be cached by any cache. (eg: CDNs, Proxies, Web browsers)
*
- * @param bool $force Force the cache to public even if it's private
+ * Also removes `public` as this is a contradictory directive
*
+ * @param bool $force Force the cache to public even if it's private, unless it's been forced private
* @return $this
*/
public function publicCache($force = false)
{
- if ($force || !$this->hasDirective('private')) {
- $this->setPublic();
- } else {
- user_error('Cannot change a private cache to public', E_USER_WARNING);
+ // Only exeucute this if its forcing level is high enough
+ $forcingLevel = self::LEVEL_PUBLIC + ($force ? self::LEVEL_FORCED : 0);
+ if ($forcingLevel < $this->forcingLevel) {
+ return;
}
+ $this->forcingLevel = $forcingLevel;
+
+ $this->setDirective('public');
+ $this->removeDirective('private');
return $this;
}
diff --git a/control/VersionedRequestFilter.php b/control/VersionedRequestFilter.php
index aae5951..7c71de1 100644
--- a/control/VersionedRequestFilter.php
+++ b/control/VersionedRequestFilter.php
@@ -46,7 +46,7 @@ class VersionedRequestFilter implements RequestFilter {
public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) {
if (Versioned::current_stage() !== Versioned::LIVE && !HTTPCacheControl::singleton()->hasDirective('no-store')) {
- HTTPCacheControl::singleton()->privateCache();
+ HTTPCacheControl::singleton()->privateCache(true);
}
return true;
}
diff --git a/forms/Form.php b/forms/Form.php
index b07a029..d2f7c00 100644
--- a/forms/Form.php
+++ b/forms/Form.php
@@ -855,7 +855,7 @@ class Form extends RequestHandler {
// If we need to disable cache, do it
if ($needsCacheDisabled) {
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
}
$attrs = $this->getAttributes();
diff --git a/security/Security.php b/security/Security.php
index 5fd36c7..15a9058 100644
--- a/security/Security.php
+++ b/security/Security.php
@@ -240,7 +240,7 @@ class Security extends Controller implements TemplateGlobalProvider {
if(!$controller) $controller = Controller::curr();
- HTTPCacheControl::singleton()->disableCaching();
+ HTTPCacheControl::singleton()->disableCache(true);
if(Director::is_ajax()) {
$response = ($controller) ? $controller->getResponse() : new SS_HTTPResponse();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment