diff --git a/core/core.services.yml b/core/core.services.yml
index f36eb44..5a8d3b7 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -25,6 +25,8 @@ services:
   cache.backend.database:
     class: Drupal\Core\Cache\DatabaseBackendFactory
     arguments: ['@database']
+  cache.backend.php:
+    class: Drupal\Core\Cache\PhpBackendFactory
   cache.bootstrap:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
diff --git a/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php
index bfb7a2a..92924a1 100644
--- a/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php
@@ -83,4 +83,30 @@ function writeable() {
   public function deleteAll() {
     return FALSE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPath($name) {
+    return $this->getFullPath($name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listAll() {
+    $names = array();
+    if (file_exists($this->directory)) {
+      foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
+        if (!$fileinfo->isDot()) {
+          $name = $fileinfo->getFilename();
+          if ($name != '.htaccess') {
+            $names[] = $name;
+          }
+        }
+      }
+    }
+    return $names;
+  }
+
 }
diff --git a/core/lib/Drupal/Component/PhpStorage/FileStorage.php b/core/lib/Drupal/Component/PhpStorage/FileStorage.php
index 33604f1..0eb2502 100644
--- a/core/lib/Drupal/Component/PhpStorage/FileStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/FileStorage.php
@@ -202,6 +202,13 @@ protected function getFullPath($name) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getPath($name) {
+    return $this->getFullPath($name);
+  }
+
+  /**
    * Implements Drupal\Component\PhpStorage\PhpStorageInterface::writeable().
    */
   public function writeable() {
@@ -248,4 +255,23 @@ protected function unlink($path) {
     // If there's nothing to delete return TRUE anyway.
     return TRUE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listAll() {
+    $names = array();
+    if (file_exists($this->directory)) {
+      foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
+        if (!$fileinfo->isDot()) {
+          $name = $fileinfo->getFilename();
+          if ($name != '.htaccess') {
+            $names[] = $name;
+          }
+        }
+      }
+    }
+    return $names;
+  }
+
 }
diff --git a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
index 3e660a0..e9dfe18 100644
--- a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFastFileStorage.php
@@ -149,6 +149,17 @@ protected function getFullPath($name, &$directory = NULL, &$directory_mtime = NU
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function delete($name) {
+    $path = $this->getContainingDirectoryFullPath($name);
+    if (file_exists($path)) {
+      return $this->unlink($path);
+    }
+    return FALSE;
+  }
+
+  /**
    * Returns the full path of the containing directory where the file is or should be stored.
    */
   protected function getContainingDirectoryFullPath($name) {
diff --git a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php
index b9dd5b0..573e23c 100644
--- a/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php
+++ b/core/lib/Drupal/Component/PhpStorage/MTimeProtectedFileStorage.php
@@ -65,4 +65,12 @@ protected function checkFile($name) {
     $filename = $this->getFullPath($name, $directory, $directory_mtime);
     return file_exists($filename) && filemtime($filename) <= $directory_mtime ? $filename : FALSE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPath($name) {
+    return $this->checkFile($name);
+  }
+
 }
diff --git a/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php
index 03ca812..e1b0649 100644
--- a/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php
+++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php
@@ -78,4 +78,25 @@ public function delete($name);
    * Removes all files in this bin.
    */
   public function deleteAll();
+
+  /**
+   * Gets the full file path.
+   *
+   * @param string $name
+   *   The virtual file name. Can be a relative path.
+   *
+   * @return string|FALSE
+   *   The full file path for the provided name. Return FALSE if the
+   *   implementation needs to prevent access to the file.
+   */
+  public function getPath($name);
+
+  /**
+   * Lists all the files in the storage.
+   *
+   * @return array
+   *   Array of filenames.
+   */
+  public function listAll();
+
 }
diff --git a/core/lib/Drupal/Core/Cache/CacheChainBackend.php b/core/lib/Drupal/Core/Cache/CacheChainBackend.php
new file mode 100644
index 0000000..47cea49
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheChainBackend.php
@@ -0,0 +1,584 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Cache\CacheChainBackend.
+ */
+
+namespace Drupal\Core\Cache;
+
+
+/**
+ * Defines a default cache implementation.
+ *
+ * This is Drupal's default cache implementation. It uses the database to store
+ * cached data. Each cache bin corresponds to a database table by the same name.
+ *
+ * @ingroup cache
+ */
+class CacheChainBackend implements CacheBackendInterface {
+
+  /**
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * The remote backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $remoteBackend;
+
+  /**
+   * Constructs a CacheChainBackend object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   * @param string $bin
+   *   The cache bin for which the object is created.
+   */
+  public function __construct(Connection $connection, $bin) {
+    // All cache tables should be prefixed with 'cache_'.
+    $bin = 'cache_' . $bin;
+
+    $this->bin = $bin;
+    $this->connection = $connection;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::get().
+   */
+  public function get($cid, $allow_invalid = FALSE) {
+    $cids = array($cid);
+    $cache = $this->getMultiple($cids, $allow_invalid);
+    return reset($cache);
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
+   */
+  public function getMultiple(&$cids, $allow_invalid = FALSE) {
+    // When serving cached pages, the overhead of using ::select() was found
+    // to add around 30% overhead to the request. Since $this->bin is a
+    // variable, this means the call to ::query() here uses a concatenated
+    // string. This is highly discouraged under any other circumstances, and
+    // is used here only due to the performance overhead we would incur
+    // otherwise. When serving an uncached page, the overhead of using
+    // ::select() is a much smaller proportion of the request.
+    $result = array();
+    try {
+      $result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
+    }
+    catch (\Exception $e) {
+      // Nothing to do.
+    }
+    $cache = array();
+    foreach ($result as $item) {
+      $item = $this->prepareItem($item, $allow_invalid);
+      if ($item) {
+        $cache[$item->cid] = $item;
+      }
+    }
+    $cids = array_diff($cids, array_keys($cache));
+    return $cache;
+  }
+
+  /**
+   * Prepares a cached item.
+   *
+   * Checks that items are either permanent or did not expire, and unserializes
+   * data as appropriate.
+   *
+   * @param object $cache
+   *   An item loaded from cache_get() or cache_get_multiple().
+   * @param bool $allow_invalid
+   *   If FALSE, the method returns FALSE if the cache item is not valid.
+   *
+   * @return mixed|false
+   *   The item with data unserialized as appropriate and a property indicating
+   *   whether the item is valid, or FALSE if there is no valid item to load.
+   */
+  protected function prepareItem($cache, $allow_invalid) {
+    if (!isset($cache->data)) {
+      return FALSE;
+    }
+
+    $cache->tags = $cache->tags ? explode(' ', $cache->tags) : array();
+
+    $checksum = $this->checksumTags($cache->tags);
+
+    // Check if deleteTags() has been called with any of the entry's tags.
+    if ($cache->checksum_deletions != $checksum['deletions']) {
+      return FALSE;
+    }
+
+    // Check expire time.
+    $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
+
+    // Check if invalidateTags() has been called with any of the entry's tags.
+    if ($cache->checksum_invalidations != $checksum['invalidations']) {
+      $cache->valid = FALSE;
+    }
+
+    if (!$allow_invalid && !$cache->valid) {
+      return FALSE;
+    }
+
+    // Unserialize and return the cached data.
+    if ($cache->serialized) {
+      $cache->data = unserialize($cache->data);
+    }
+
+    return $cache;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::set().
+   */
+  public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
+    $try_again = FALSE;
+    try {
+      // The bin might not yet exist.
+      $this->doSet($cid, $data, $expire, $tags);
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the bins.
+      if (!$try_again = $this->ensureBinExists()) {
+        // If the exception happened for other reason than the missing bin
+        // table, propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the bin has been created, try again if necessary.
+    if ($try_again) {
+      $this->doSet($cid, $data, $expire, $tags);
+    }
+  }
+
+  /**
+   * Actually set the cache.
+   */
+  protected function doSet($cid, $data, $expire, $tags) {
+    $flat_tags = $this->flattenTags($tags);
+    $deleted_tags = &drupal_static('Drupal\Core\Cache\CacheChainBackend::deletedTags', array());
+    $invalidated_tags = &drupal_static('Drupal\Core\Cache\CacheChainBackend::invalidatedTags', array());
+    // Remove tags that were already deleted or invalidated during this request
+    // from the static caches so that another deletion or invalidation can
+    // occur.
+    foreach ($flat_tags as $tag) {
+      if (isset($deleted_tags[$tag])) {
+        unset($deleted_tags[$tag]);
+      }
+      if (isset($invalidated_tags[$tag])) {
+        unset($invalidated_tags[$tag]);
+      }
+    }
+    $checksum = $this->checksumTags($flat_tags);
+    $fields = array(
+      'serialized' => 0,
+      'created' => REQUEST_TIME,
+      'expire' => $expire,
+      'tags' => implode(' ', $flat_tags),
+      'checksum_invalidations' => $checksum['invalidations'],
+      'checksum_deletions' => $checksum['deletions'],
+    );
+    if (!is_string($data)) {
+      $fields['data'] = serialize($data);
+      $fields['serialized'] = 1;
+    }
+    else {
+      $fields['data'] = $data;
+      $fields['serialized'] = 0;
+    }
+
+    $this->connection->merge($this->bin)
+      ->key('cid', $cid)
+      ->fields($fields)
+      ->execute();
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::delete().
+   */
+  public function delete($cid) {
+    $this->deleteMultiple(array($cid));
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
+   */
+  public function deleteMultiple(array $cids) {
+    try {
+      // Delete in chunks when a large array is passed.
+      do {
+        $this->connection->delete($this->bin)
+          ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+          ->execute();
+      }
+      while (count($cids));
+    }
+    catch (\Exception $e) {
+      // Create the cache table, which will be empty. This fixes cases during
+      // core install where a cache table is cleared before it is set
+      // with {cache_render} and {cache_data}.
+      if (!$this->ensureBinExists()) {
+        $this->catchException($e);
+      }
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
+   */
+  public function deleteTags(array $tags) {
+    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
+    $deleted_tags = &drupal_static('Drupal\Core\Cache\CacheChainBackend::deletedTags', array());
+    foreach ($this->flattenTags($tags) as $tag) {
+      // Only delete tags once per request unless they are written again.
+      if (isset($deleted_tags[$tag])) {
+        continue;
+      }
+      $deleted_tags[$tag] = TRUE;
+      unset($tag_cache[$tag]);
+      try {
+        $this->connection->merge('cache_tags')
+          ->insertFields(array('deletions' => 1))
+          ->expression('deletions', 'deletions + 1')
+          ->key('tag', $tag)
+          ->execute();
+      }
+      catch (\Exception $e) {
+        $this->catchException($e, 'cache_tags');
+      }
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
+   */
+  public function deleteAll() {
+    try {
+      $this->connection->truncate($this->bin)->execute();
+    }
+    catch (\Exception $e) {
+      // Create the cache table, which will be empty. This fixes cases during
+      // core install where a cache table is cleared before it is set
+      // with {cache_render} and {cache_data}.
+      if (!$this->ensureBinExists()) {
+        $this->catchException($e);
+      }
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
+   */
+  public function invalidate($cid) {
+    $this->invalidateMultiple(array($cid));
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::invalideMultiple().
+   */
+  public function invalidateMultiple(array $cids) {
+    try {
+      // Update in chunks when a large array is passed.
+      do {
+        $this->connection->update($this->bin)
+          ->fields(array('expire' => REQUEST_TIME - 1))
+          ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+          ->execute();
+      }
+      while (count($cids));
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
+   */
+  public function invalidateTags(array $tags) {
+    try {
+      $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
+      $invalidated_tags = &drupal_static('Drupal\Core\Cache\CacheChainBackend::invalidatedTags', array());
+      foreach ($this->flattenTags($tags) as $tag) {
+        // Only invalidate tags once per request unless they are written again.
+        if (isset($invalidated_tags[$tag])) {
+          continue;
+        }
+        $invalidated_tags[$tag] = TRUE;
+        unset($tag_cache[$tag]);
+        $this->connection->merge('cache_tags')
+          ->insertFields(array('invalidations' => 1))
+          ->expression('invalidations', 'invalidations + 1')
+          ->key('tag', $tag)
+          ->execute();
+      }
+    }
+    catch (\Exception $e) {
+      $this->catchException($e, 'cache_tags');
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
+   */
+  public function invalidateAll() {
+    try {
+      $this->connection->update($this->bin)
+        ->fields(array('expire' => REQUEST_TIME - 1))
+        ->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
+   */
+  public function garbageCollection() {
+    try {
+      $this->connection->delete($this->bin)
+        ->condition('expire', Cache::PERMANENT, '<>')
+        ->condition('expire', REQUEST_TIME, '<')
+        ->execute();
+    }
+    catch (\Exception $e) {
+      // If the table does not exist, it surely does not have garbage in it.
+      // If the table exists, the next garbage collection will clean up.
+      // There is nothing to do.
+    }
+  }
+
+  /**
+   * 'Flattens' a tags array into an array of strings.
+   *
+   * @param array $tags
+   *   Associative array of tags to flatten.
+   *
+   * @return array
+   *   An indexed array of flattened tag identifiers.
+   */
+  protected function flattenTags(array $tags) {
+    if (isset($tags[0])) {
+      return $tags;
+    }
+
+    $flat_tags = array();
+    foreach ($tags as $namespace => $values) {
+      if (is_array($values)) {
+        foreach ($values as $value) {
+          $flat_tags[] = "$namespace:$value";
+        }
+      }
+      else {
+        $flat_tags[] = "$namespace:$values";
+      }
+    }
+    return $flat_tags;
+  }
+
+  /**
+   * Returns the sum total of validations for a given set of tags.
+   *
+   * @param array $tags
+   *   Array of flat tags.
+   *
+   * @return int
+   *   Sum of all invalidations.
+   *
+   * @see \Drupal\Core\Cache\CacheChainBackend::flattenTags()
+   */
+  protected function checksumTags($flat_tags) {
+    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
+
+    $checksum = array(
+      'invalidations' => 0,
+      'deletions' => 0,
+    );
+
+    $query_tags = array_diff($flat_tags, array_keys($tag_cache));
+    if ($query_tags) {
+      $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC);
+      $tag_cache += $db_tags;
+
+      // Fill static cache with empty objects for tags not found in the database.
+      $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum);
+    }
+
+    foreach ($flat_tags as $tag) {
+      $checksum['invalidations'] += $tag_cache[$tag]['invalidations'];
+      $checksum['deletions'] += $tag_cache[$tag]['deletions'];
+    }
+
+    return $checksum;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty().
+   */
+  public function isEmpty() {
+    $this->garbageCollection();
+    $query = $this->connection->select($this->bin);
+    $query->addExpression('1');
+    try {
+      $result = $query->range(0, 1)
+        ->execute()
+        ->fetchField();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+    return empty($result);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeBin() {
+    try {
+      $this->connection->schema()->dropTable($this->bin);
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+  /**
+   * Check if the cache bin exists and create it if not.
+   */
+  protected function ensureBinExists() {
+    try {
+      $database_schema = $this->connection->schema();
+      if (!$database_schema->tableExists($this->bin)) {
+        $schema_definition = $this->schemaDefinition();
+        $database_schema->createTable($this->bin, $schema_definition['bin']);
+        // If the bin doesn't exist, the cache tags table may also not exist.
+        if (!$database_schema->tableExists('cache_tags')) {
+          $database_schema->createTable('cache_tags', $schema_definition['cache_tags']);
+        }
+        return TRUE;
+      }
+    }
+    // If another process has already created the cache table, attempting to
+    // recreate it will throw an exception. In this case just catch the
+    // exception and do nothing.
+    catch (SchemaObjectExistsException $e) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Act on an exception when cache might be stale.
+   *
+   * If the cache_tags table does not yet exist, that's fine but if the table
+   * exists and yet the query failed, then the cache is stale and the
+   * exception needs to propagate.
+   *
+   * @param $e
+   *   The exception.
+   * @param string|null $table_name
+   *   The table name, defaults to $this->bin. Can be cache_tags.
+   */
+  protected function catchException(\Exception $e, $table_name = NULL) {
+    if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) {
+      throw $e;
+    }
+  }
+
+  /**
+   * Defines the schema for the cache bin and cache_tags table.
+   */
+  public function schemaDefinition() {
+    $schema['bin'] = array(
+      'description' => 'Storage for the cache API.',
+      'fields' => array(
+        'cid' => array(
+          'description' => 'Primary Key: Unique cache ID.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'data' => array(
+          'description' => 'A collection of data to cache.',
+          'type' => 'blob',
+          'not null' => FALSE,
+          'size' => 'big',
+        ),
+        'expire' => array(
+          'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'created' => array(
+          'description' => 'A Unix timestamp indicating when the cache entry was created.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'serialized' => array(
+          'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+          'type' => 'int',
+          'size' => 'small',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'tags' => array(
+          'description' => 'Space-separated list of cache tags for this entry.',
+          'type' => 'text',
+          'size' => 'big',
+          'not null' => FALSE,
+        ),
+        'checksum_invalidations' => array(
+          'description' => 'The tag invalidation sum when this entry was saved.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'checksum_deletions' => array(
+          'description' => 'The tag deletion sum when this entry was saved.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+      ),
+      'indexes' => array(
+        'expire' => array('expire'),
+      ),
+      'primary key' => array('cid'),
+    );
+    $schema['cache_tags'] = array(
+      'description' => 'Cache table for tracking cache tags related to the cache bin.',
+      'fields' => array(
+        'tag' => array(
+          'description' => 'Namespace-prefixed tag string.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'invalidations' => array(
+          'description' => 'Number incremented when the tag is invalidated.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'deletions' => array(
+          'description' => 'Number incremented when the tag is deleted.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+      ),
+      'primary key' => array('tag'),
+    );
+    return $schema;
+  }
+}
diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php
index 7f061e5..ed9df26 100644
--- a/core/lib/Drupal/Core/Cache/CacheFactory.php
+++ b/core/lib/Drupal/Core/Cache/CacheFactory.php
@@ -57,7 +57,13 @@ public function get($bin) {
       $service_name = $cache_settings['default'];
     }
     else {
-      $service_name = 'cache.backend.database';
+      // @todo do something more sensible.
+      if ($bin == 'config') {
+        $service_name = 'cache.backend.php';
+      }
+      else {
+        $service_name = 'cache.backend.database';
+      }
     }
     return $this->container->get($service_name)->get($bin);
   }
diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php
new file mode 100644
index 0000000..0be649a
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/PhpBackend.php
@@ -0,0 +1,275 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Cache\PhpBackend.
+ */
+
+namespace Drupal\Core\Cache;
+
+use Drupal\Component\PhpStorage\PhpStorageFactory;
+use Drupal\Component\Utility\Variable;
+
+/**
+ * Defines a PHP cache implementation.
+ *
+ * Stores cache items in a PHP file using a storage that implements
+ * Drupal\Component\PhpStorage\PhpStorageInterface.
+ *
+ * @ingroup cache
+ */
+class PhpBackend implements CacheBackendInterface {
+
+  /**
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * Array to store cache objects.
+   */
+  protected $cache = array();
+
+  /**
+   * Constructs a MemoryBackend object.
+   *
+   * @param string $bin
+   *   The cache bin for which the object is created.
+   */
+  public function __construct($bin) {
+    $this->bin = $bin;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($cid, $allow_invalid = FALSE) {
+    if ($file = $this->storage()->getPath($cid)) {
+      $cache = @include $file;
+    }
+    if (isset($cache)) {
+      return $this->prepareItem((object) $cache, $allow_invalid);
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMultiple(&$cids, $allow_invalid = FALSE) {
+    $ret = array();
+
+    foreach ($cids as $cid) {
+      if ($item = $this->get($cid, $allow_invalid)) {
+        $ret[$item->cid] = $item;
+      }
+    }
+
+    $cids = array_diff($cids, array_keys($ret));
+
+    return $ret;
+  }
+
+  /**
+   * Prepares a cached item.
+   *
+   * Checks that items are either permanent or did not expire, and returns data
+   * as appropriate.
+   *
+   * @param object $cache
+   *   An item loaded from cache_get() or cache_get_multiple().
+   *
+   * @return mixed
+   *   The item with data as appropriate or FALSE if there is no
+   *   valid item to load.
+   */
+  protected function prepareItem($cache, $allow_invalid) {
+    if (!isset($cache->data)) {
+      return FALSE;
+    }
+
+    // Check expire time.
+    $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
+
+    if (!$allow_invalid && !$cache->valid) {
+      return FALSE;
+    }
+
+    return $cache;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
+    $item = array(
+      'cid' => $cid,
+      'data' => $data,
+      'created' => REQUEST_TIME,
+      'expire' => $expire,
+      'tags' => $this->flattenTags($tags),
+    );
+    $this->writeItem($cid, $item);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($cid) {
+    $this->storage()->delete($cid);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteMultiple(array $cids) {
+    foreach ($cids as $cid) {
+      $this->delete($cid);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteTags(array $tags) {
+    $flat_tags = $this->flattenTags($tags);
+    foreach ($this->storage()->listAll() as $cid) {
+      $item = $this->get($cid);
+      if (array_intersect($flat_tags, $item->tags)) {
+        $this->delete($cid);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAll() {
+    $this->storage()->deleteAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidate($cid) {
+    if ($item = $this->get($cid)) {
+      $item->expire = REQUEST_TIME - 1;
+      $this->writeItem($cid, (array) $item);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateMultiple(array $cids) {
+    foreach ($cids as $cid) {
+      $this->invalidate($cid);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateTags(array $tags) {
+    $flat_tags = $this->flattenTags($tags);
+    foreach ($this->storage()->listAll() as $cid) {
+      $item = $this->get($cid);
+      if ($item && array_intersect($flat_tags, $item->tags)) {
+        $this->invalidate($cid);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateAll() {
+    $this->invalidateMultiple($this->storage()->listAll());
+  }
+
+  /**
+   * 'Flattens' a tags array into an array of strings.
+   *
+   * @param array $tags
+   *   Associative array of tags to flatten.
+   *
+   * @return array
+   *   An indexed array of strings.
+   */
+  protected function flattenTags(array $tags) {
+    if (isset($tags[0])) {
+      return $tags;
+    }
+
+    $flat_tags = array();
+    foreach ($tags as $namespace => $values) {
+      if (is_array($values)) {
+        foreach ($values as $value) {
+          $flat_tags["$namespace:$value"] = "$namespace:$value";
+        }
+      }
+      else {
+        $flat_tags["$namespace:$values"] = "$namespace:$values";
+      }
+    }
+    return $flat_tags;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $names = $this->storage()->listAll();
+    return empty($names);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function garbageCollection() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeBin() {
+    $this->cache = array();
+    $this->storage()->delete($this->bin);
+  }
+
+  /**
+   * Writes a cache item to PhpStorage.
+   *
+   * @param string $cid
+   *   The cache ID of the data to store.
+   * @param array $item
+   *   The cache item to store.
+   */
+  protected function writeItem($cid, array $item) {
+    $data = str_replace('\\', '\\\\', serialize($item));
+    $content = "<?php return unserialize(<<<EOF
+$data
+EOF
+);";
+    $this->storage()->save($cid, $content);
+  }
+
+  /**
+   * Gets the PHP code storage object to use.
+   *
+   * @return \Drupal\Component\PhpStorage\PhpStorageInterface
+   */
+  protected function storage() {
+    if (!isset($this->storage)) {
+      if ($this->bin != 'cache') {
+        $store = 'cache_' . $this->bin;
+      }
+      else {
+        $store = 'cache';
+      }
+      $this->storage = PhpStorageFactory::get($store);
+    }
+    return $this->storage;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/PhpBackendFactory.php b/core/lib/Drupal/Core/Cache/PhpBackendFactory.php
new file mode 100644
index 0000000..0801b72
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/PhpBackendFactory.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\PhpBackendFactory.
+ */
+
+namespace Drupal\Core\Cache;
+
+class PhpBackendFactory implements CacheFactoryInterface {
+
+  /**
+   * Gets PhpBackend for the specified cache bin.
+   *
+   * @param $bin
+   *   The cache bin for which the object is created.
+   *
+   * @return \Drupal\Core\Cache\PhpBackend
+   *   The cache backend object for the specified cache bin.
+   */
+  function get($bin) {
+    return new PhpBackend($bin);
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php
index e558816..4b20ea0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php
@@ -135,10 +135,11 @@ public function testSetGet() {
     $backend = $this->getCacheBackend();
 
     $this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
-    $backend->set('test1', 7);
+    $with_backslash = array('foo' => '\Drupal\foo\Bar');
+    $backend->set('test1', $with_backslash);
     $cached = $backend->get('test1');
     $this->assert(is_object($cached), "Backend returned an object for cache id test1.");
-    $this->assertIdentical(7, $cached->data);
+    $this->assertIdentical($with_backslash, $cached->data);
     $this->assertTrue($cached->valid, 'Item is marked as valid.');
     $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.');
     $this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/PhpBackendUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/PhpBackendUnitTest.php
new file mode 100644
index 0000000..6258550
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/PhpBackendUnitTest.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Cache\PhpBackendUnitTest.
+ */
+
+namespace Drupal\system\Tests\Cache;
+
+use Drupal\Core\Cache\PhpBackend;
+
+/**
+ * Tests PhpBackendUnitTest using GenericCacheBackendUnitTestBase.
+ */
+class PhpBackendUnitTest extends GenericCacheBackendUnitTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Php cache backend',
+      'description' => 'Unit test of the PHP cache backend using the generic cache unit test base.',
+      'group' => 'Cache',
+    );
+  }
+
+  /**
+   * Creates a new instance of MemoryBackend.
+   *
+   * @return
+   *   A new MemoryBackend object.
+   */
+  protected function createCacheBackend($bin) {
+    return new PhpBackend($bin);
+  }
+
+}
