Version 3 end of life
This version of Silverstripe CMS will not recieve any additional bug fixes or documentation updates. Go to documentation for the most recent stable version.


SilverStripe 3.7 and PHP 7.2 and Object subclasses

For Project Code

SilverStripe 3.7 now supports PHP 7.2, which is exciting, but PHP 7.2 introduces an object keyword. To use it, you can replace any uses of Object with SS_Object in your own project code.

-class MyClass extends Object
+class MyClass extends SS_Object
	public function myFunction()
-		$foo = Object::has_extension('MyExtension');
+		$foo = SS_Object::has_extension('MyExtension');

You are also reliant on any SilverStripe modules directly using Object to upgrade their codebase. Matches for SS_Object in the module codebase will tell you it's been upgraded. A search for extends Object or Object:: isn't fool proof, but will give you an indication that the module still needs to be upgraded. If in doubt, check the module README.

For Module Authors

If you are publishing a 3.x-compatible module that directly references the Object class, we recommend that you replace all references to Object with SS_Object, and add the following line to your module's _config.php in order to support both current SilverStripe 3.x and SilverStripe 3.7 releases running on PHP 7.2:

// Ensure compatibility with PHP 7.2 ("object" is a reserved word),
// with SilverStripe 3.6 (using Object) and SilverStripe 3.7 (using SS_Object)
if (!class_exists('SS_Object')) class_alias('Object', 'SS_Object');

Don't forget to mention explicit PHP 7.2 and SilverStripe 3.7 compatibility in your module README.

Note that in SilverStripe 4.x, the Object class was deleted so there isn’t an SS_Object class there either (see

Versioned cache segmentation

SS_Cache now maintains separate cache pools for each versioned stage. This prevents developers from caching draft data and then accidentally exposing it on the live stage without potentially required authorisation checks. Unless you rely on caching across stages, you don't need to change your own code for this change to take effect. Note that cache keys will be internally rewritten, causing any existing cache items to become invalid when this change is deployed.

// Before:
$cache = SS_Cache::factory('myapp');
$cache->save('Some draft content. Not for public viewing yet.', 'my_key');
$cache->load('my_key'); // 'Some draft content. Not for public viewing yet'

// After:
$cache = SS_Cache::factory('myapp');
$cache->save('Some draft content. Not for public viewing yet.', 'my_key');
$cache->load('my_key'); // null

Data that is not content sensitive can be cached across stages by simply opting out of the segmented cache with the disable-segmentation argument.

$cache = SS_Cache::factory('myapp', 'Output', array('disable-segmentation' => true));

HTTP Cache Header changes


In order to support developers in making safe choices around HTTP caching, we've introduced a HTTPCacheControl class to control if a response should be considered public or private. This is an abstraction on existing lowlevel APIs like HTTP::add_cache_headers() and SS_HTTPResponse->addHeader().

This change introduces smaller but necessary changes to HTTP caching headers sent by SilverStripe. If you are relying on HTTP caching in your implementation, or use modules such as silverstripe/controllerpolicy, please review the implications of these changes below.

In short, these APIs make it easier to express your caching preferences without running the risk of overriding essential core safety measures. Most commonly, these APIs will prevent HTTP caching of draft content.

It will also prevent caching of content generated with an active session, since the system can't tell whether session data was used to vary the output. In this case, it's up to the developer to opt-in to caching, after ensuring that certain execution paths are safe despite of using sessions.

The system behaviour does not guard against accidentally caching "private" content, since there are too many variations under which output could be considered private (e.g. a custom "approval" flag on a comment object). It is up to the developer to ensure caching is used appropriately there.

By default, SilverStripe sends headers which signal to HTTP caches that the response should be considered not cacheable.

See Developer Guide: Performance > HTTP Cache Headers for details on the new API.

Example Usage

Global opt-in for page content

Enable caching for all page content (through Page_Controller).

class Page_Controller extends ContentController
    public function init()
-        HTTP::set_cache_age(60);
+        HTTPCacheControl::singleton()
+           ->enableCache()
+           ->setMaxAge(60); // 1 minute


Note: SilverStripe will still override this preference when a session is active, a CSRF token token is present, or draft content has been requested.

Opt-out for a particular controller action

If a controller output relies on session data, cookies, permission checks or other triggers for conditional output, you can disable caching either on a controller level (through init()) or for a particular action.

class MyPage_Controller extends Page_Controller
    public function myprivateaction($request)
        $response = $this->myPrivateResponse();
-        HTTP::set_cache_age(0);
+        HTTPCacheControl::singleton()
+           ->disableCache();

        return $response;

Note: SilverStripe will still override this preference when a session is active, a CSRF token token is present, or draft content has been requested.

Global opt-in, ignoring session (advanced)

This can be helpful in situations where forms are embedded on the website. SilverStripe will still override this preference when draft content has been requested. CAUTION: This mode relies on a developer examining each execution path to ensure that no session data is used to vary output.

Use case: By default, forms include a CSRF token which starts a session with a value that's unique to the visitor, which makes the output uncacheable. But any subsequent requests by this visitor will also carry a session, leading to uncacheable output for this visitor. This is the case even if the output does not contain any forms, and does not vary for this particular visitor.

class Page_Controller extends ContentController
    public function init()
-        HTTP::set_cache_age(60);
+        HTTPCacheControl::singleton()
+           ->enableCache($force=true) // DANGER ZONE
+           ->setMaxAge(60); // 1 minute


Detailed Changes

  • Added Cache-Control: no-store header to default responses, to prevent intermediary HTTP proxies (e.g. CDNs) from caching unless developers opt-in
  • Removed Cache-Control: no-transform header from default responses
  • Removed Vary: Cookie as an unreliable cache buster, rely on the existing Cache-Control: no-store defaults instead
  • Removed Vary: Accept, since it's very uncommon to vary content on the Content-Type headers submitted through the request, and it can significantly decrease the likelyhood of a cache hit. Note this is different from Vary: Accept-Encoding, which is important for compression (e.g. gzip), and usually added by other layers such as Apache's mod_gzip.

Disable session-based stage setting

When viewing a versioned record (usually pages) in "draft" mode, SilverStripe records this mode in the session for further requests. This has the advantage of transparently working on XHR and API requests, as well as authenticated users navigating through other views.

These subsequent requests no longer carried an explicit stage query parameter, which meant the same URL might show draft or live content depending on your session state. While most HTTP caching layers deal gracefully with this variation by disabling any caching when a session cookie is present, there is a small chance that draft content is exposed to unauthenticated users for the lifetime of the cache.

Due to this potential risk for information leakage, we have decided to only rely on the stage query parameter starting with SilverStripe 4.2. In SilverStripe 3.x, you can opt-in to this behaviour as well:

  use_session: false

If you are consistently using the built-in SiteTree->Link() and Controller->Link() methods to get URLs, this change likely won't affect you.

If you are manually concatenating URLs to SilverStripe controllers rather than through their Link() methods (in custom PHP or JavaScript), or have implemented your own Link() methods on controllers exposing versioned objects, you'll need to check your business logic.

Check our versioning docs for more details.

Change Log


  • 2018-04-24 e4c0f271b Ensure passwords do not get added to session on submission failure (Aaron Carlino) - See ss-2018-013

API Changes

  • 2017-06-10 413b4936a Add extension hook to FormField::extraClass() (Damian Mooyman)
  • 2016-11-28 f16d7e183 Deprecate unused / undesirable create_new_password implementation (Damian Mooyman)

Features and Enhancements

  • 2018-06-07 2b4954035 Add better HTTP cache-control manipulation (#8086) (Daniel Hensby)
  • 2018-06-06 c639ffa9c isPopulated method to allow StringField subclasses to check existence without RAW (Aaron Carlino)
  • 2018-05-07 dfdaac48 Backport versioned querystring fix (#2153) (Damian Mooyman)
  • 2018-05-07 47a9cdfd4 Backport of querystring work to 3.x (#8026) (Damian Mooyman)
  • 2017-11-30 910381633 Add php 7.2 support (Daniel Hensby)
  • 2017-11-06 2e43780a8 Add sort columns to DB index automatically (Daniel Hensby)
  • 2017-09-28 2f0a0cb63 Add (alt text) to title field for clarity (Robbie Averill)
  • 2017-08-28 0b34066f0 incorrect scalar types in doc blocks, add chainable returns in setters (Robbie Averill)
  • 2017-08-03 8577ad128 Added SSL support for MySQLi Connector (fixes #7242) (John Dalangin - PRAXXYS)
  • 2017-08-02 2f9bfae1f Added MySQL SSL PDO Support (John Dalangin - PRAXXYS)
  • 2017-07-04 b347ab86 Add version provider configuration (Robbie Averill)
  • 2017-07-04 ee4d8b4d4 Add new SilverStripeVersionProvider to provider module versions (Robbie Averill)
  • 2017-06-15 a990c99d6 suffix subfolder in silverstripe-cache with php-version (#6810) (Lukas)


  • 2018-06-07 4a0e5b636 Fix crash on fixed_fields in default_sort (Damian Mooyman)
  • 2018-06-04 85a712e1c postgres test (Damian Mooyman)
  • 2018-06-04 41e601a03 Regression from #8009 (Daniel Hensby)
  • 2018-06-04 a20b0a4aa Remove use of deprecated each method (Daniel Hensby)
  • 2018-06-01 05a519ecc code style / php 5.3 compat (Damian Mooyman)
  • 2018-06-01 c5205ecc Ensure errorpage is built in live mode (Damian Mooyman)
  • 2018-06-01 2756d60da Prevent stage querystring args during dev/build (Damian Mooyman)
  • 2018-05-29 1cbf27e0f PHP 5.3 compat for referencing $this in closure, and make method public for same reason (Robbie Averill)
  • 2018-05-23 b6dbae8b Make RedirectorPage::Link compatible with SiteTree::Link (Daniel Hensby)
  • 2018-04-17 af3a9f3ec Duplicating many_many relationships looses the extra fields (fixes #7973) (UndefinedOffset)
  • 2018-03-20 ebd3fb652 Don't auto-generate indexes for Text field types (fixes #7900) (Loz Calver)
  • 2018-03-15 61ce4771f ing HTMLEditorField API documentation (3Dgoo)
  • 2018-03-15 d17d93f7 Remove SearchForm results() function from allowed_actions (Steve Dixon)
  • 2018-03-14 97f22cbaa ing FormAction API documentation (3Dgoo)
  • 2018-03-01 6523d7a6e ing HTMLEditorField API documentation (3Dgoo)
  • 2018-02-27 c755f7728 indentation (Aaron Carlino)
  • 2018-02-16 86addea1d Split HTML manipulation to onadd, so elements are not accidentally duplicated (Christopher Joe)
  • 2018-02-13 c767e472d DataObject singleton creation (Jonathon Menz)
  • 2017-12-01 74a3ba54a count size of $relations (Daniel Hensby)
  • 2017-11-29 2717f0134 link to wiki (JorisDebonnet)
  • 2017-08-08 1a4a006d0 PDOConnector ssl_cipher bug fixes #7258 (John)
  • 2017-04-12 8999f70ac ing broken search in SecurityAdmin Groups field (Sean Harvey)