diff --git a/core/lib/Drupal/Component/PhpStorage/BootstrapStorageFactory.php b/core/lib/Drupal/Component/PhpStorage/BootstrapStorageFactory.php
new file mode 100644
index 0000000..81fbb93
--- /dev/null
+++ b/core/lib/Drupal/Component/PhpStorage/BootstrapStorageFactory.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\PhpStorage\BootstrapStorageFactory.
+ */
+
+
+namespace Drupal\Component\PhpStorage;
+
+
+use Drupal\Core\Cache\DatabaseBackend;
+use Drupal\Core\Cache\DatabaseCacheTagsChecksum;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Site\Settings;
+
+class BootstrapStorageFactory {
+
+  /**
+   * Returns a PHP storage implementation.
+   *
+   * @param $class_loader
+   *   The class loader. Normally Composer's ClassLoader, as included by the
+   *   front controller, but may also be decorated; e.g.,
+   *   \Symfony\Component\ClassLoader\ApcClassLoader.
+   *
+   * @return \Drupal\Component\PhpStorage\PhpStorageInterface
+   *   A PHP storage implementation.
+   */
+  public static function get($class_loader = NULL) {
+    $bootstrap_php_storage = Settings::get('bootstrap_php_storage');
+    $storage = FALSE;
+    if (!empty($bootstrap_php_storage) && is_callable($bootstrap_php_storage)) {
+      $storage = call_user_func($bootstrap_php_storage, $class_loader);
+    }
+    // Fallback to the DatabaseStorage.
+    return $storage ?: self::getDatabaseCacheStorage();
+  }
+
+  /**
+   * Returns a Database Cache based PHP storage implementation.
+   *
+   * @return \Drupal\Component\PhpStorage\CacheStorage
+   */
+  public static function getDatabaseCacheStorage() {
+    $connection = Database::getConnection();
+    $backend = new DatabaseBackend($connection, new DatabaseCacheTagsChecksum($connection), 'php_storage');
+    return new CacheStorage($backend, Settings::getHashSalt());
+  }
+
+}
diff --git a/core/lib/Drupal/Component/PhpStorage/CacheStorage.php b/core/lib/Drupal/Component/PhpStorage/CacheStorage.php
new file mode 100644
index 0000000..e8a60bc
--- /dev/null
+++ b/core/lib/Drupal/Component/PhpStorage/CacheStorage.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\PhpStorage\CacheStorage.
+ */
+
+namespace Drupal\Component\PhpStorage;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\StreamWrapper\PharCacheStream;
+
+/**
+ * This class stores PHP classes in a cache storage keeping it opcacheable.
+ */
+class CacheStorage implements PhpStorageInterface {
+
+  public function __construct(CacheBackendInterface $backend, $secret) {
+    $this->backend = $backend;
+    $this->secret = $secret;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exists($name) {
+    PharCacheStream::init($this->backend, $this->secret);
+    return file_exists(PharCacheStream::MAGIC_NAME . $name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($name) {
+    PharCacheStream::init($this->backend, $this->secret);
+    return (@include_once PharCacheStream::MAGIC_NAME . $name) !== FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save($name, $code) {
+    PharCacheStream::init($this->backend, $this->secret);
+    return file_put_contents(PharCacheStream::MAGIC_NAME . $name, $code);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function writeable() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($name) {
+    PharCacheStream::init($this->backend, $this->secret);
+    return unlink(PharCacheStream::MAGIC_NAME . $name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAll() {
+    // @TODO
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFullPath($name) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listAll() {
+    // @TODO
+  }
+}
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 82afbaa..5507da0 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core;
 
 use Drupal\Component\FileCache\FileCacheFactory;
+use Drupal\Component\PhpStorage\BootstrapStorageFactory;
 use Drupal\Component\ProxyBuilder\ProxyDumper;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Unicode;
@@ -1208,7 +1209,7 @@ protected function getHttpKernel() {
    */
   protected function storage() {
     if (!isset($this->storage)) {
-      $this->storage = PhpStorageFactory::get('service_container');
+      $this->storage = BootstrapStorageFactory::get($this->classLoader);
     }
     return $this->storage;
   }
diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
index 7720655..5bf4bcb 100644
--- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
@@ -50,6 +50,46 @@ public static function getType() {
   }
 
   /**
+   * @param $target
+   * @param $option
+   * @param $value
+   * @return bool
+   */
+  public static function metadata($target, $option, $value) {
+    $return = FALSE;
+    switch ($option) {
+      case STREAM_META_TOUCH:
+        if (!empty($value)) {
+          $return = touch($target, $value[0], $value[1]);
+        }
+        else {
+          $return = touch($target);
+        }
+        break;
+
+      case STREAM_META_OWNER_NAME:
+      case STREAM_META_OWNER:
+        $return = chown($target, $value);
+        break;
+
+      case STREAM_META_GROUP_NAME:
+      case STREAM_META_GROUP:
+        $return = chgrp($target, $value);
+        break;
+
+      case STREAM_META_ACCESS:
+        $return = chmod($target, $value);
+        break;
+    }
+    if ($return) {
+      // For convenience clear the file status cache of the underlying file,
+      // since metadata operations are often followed by file status checks.
+      clearstatcache(TRUE, $target);
+    }
+    return $return;
+  }
+
+  /**
    * Gets the path that the wrapper is responsible for.
    *
    * @todo Review this method name in D8 per https://www.drupal.org/node/701358.
@@ -304,37 +344,7 @@ public function stream_cast($cast_as) {
    */
   public function stream_metadata($uri, $option, $value) {
     $target = $this->getLocalPath($uri);
-    $return = FALSE;
-    switch ($option) {
-      case STREAM_META_TOUCH:
-        if (!empty($value)) {
-          $return = touch($target, $value[0], $value[1]);
-        }
-        else {
-          $return = touch($target);
-        }
-        break;
-
-      case STREAM_META_OWNER_NAME:
-      case STREAM_META_OWNER:
-        $return = chown($target, $value);
-        break;
-
-      case STREAM_META_GROUP_NAME:
-      case STREAM_META_GROUP:
-        $return = chgrp($target, $value);
-        break;
-
-      case STREAM_META_ACCESS:
-        $return = chmod($target, $value);
-        break;
-    }
-    if ($return) {
-      // For convenience clear the file status cache of the underlying file,
-      // since metadata operations are often followed by file status checks.
-      clearstatcache(TRUE, $target);
-    }
-    return $return;
+    return static::metadata($target, $option, $value);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StreamWrapper/PharCacheStream.php b/core/lib/Drupal/Core/StreamWrapper/PharCacheStream.php
new file mode 100644
index 0000000..607f536
--- /dev/null
+++ b/core/lib/Drupal/Core/StreamWrapper/PharCacheStream.php
@@ -0,0 +1,319 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\StreamWrapper\PharCacheStream.
+ */
+
+namespace Drupal\Core\StreamWrapper;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+
+class PharCacheStream {
+
+  // This file doesn't exist. When you try reading
+  // phar://./drupal-phpstorage.phar/something.php the value will be read from
+  // a K-V store. The key will be a hash of a secret and "something.php".
+  const MAGIC_NAME = 'phar://./drupal-phpstorage.phar/';
+
+  /**
+   * Stream context resource.
+   *
+   * @var resource
+   */
+  public $context = NULL;
+
+  /**
+   * A generic resource handle.
+   *
+   * @var resource
+   */
+  public $handle = NULL;
+
+  /**
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected static $backend;
+
+  /**
+   * @var string
+   */
+  protected static $secret;
+
+  /**
+   * The key in the storage when the contents need to be written back.
+   *
+   * @var string
+   */
+  protected $key;
+
+  public static function init(CacheBackendInterface $backend, $secret) {
+    if (empty(static::$backend)) {
+      static::$backend = $backend;
+      static::$secret = $secret;
+      static::useOurWrapper();
+    }
+  }
+
+  public function dir_closedir() {
+    closedir($this->handle);
+    return TRUE;
+  }
+
+  public function dir_opendir($path) {
+    static::usePhpWrapper();
+    $this->key = '';
+    $this->handle = opendir($path, $this->context);
+    static::useOurWrapper();
+    return (bool) $this->handle;
+  }
+
+  public function dir_readdir() {
+    return readdir($this->handle);
+  }
+
+  public function dir_rewinddir() {
+    rewinddir($this->handle);
+  }
+
+  public function mkdir($path, $mode, $options) {
+    static::usePhpWrapper();
+    $return = mkdir($path, $mode, $options);
+    static::useOurWrapper();
+    return $return;
+  }
+
+  public function rename($path_from, $path_to) {
+    static::usePhpWrapper();
+    $return = rename($path_from, $path_to);
+    static::useOurWrapper();
+    return $return;
+  }
+
+  public function rmdir($path, $options) {
+    static::usePhpWrapper();
+    $return = rmdir($path, $options);
+    static::useOurWrapper();
+    return $return;
+  }
+
+  public function stream_cast() {
+    return $this->handle ? $this->handle : FALSE;
+  }
+
+  public function stream_close () {
+    if ($this->key) {
+      fseek($this->handle, 0);
+      $buf = '';
+      while (!feof($this->handle)) {
+        $buf .= fread($this->handle, 1048576);
+      }
+      static::$backend->setMultiple([
+        $this->key => ['data' => $buf],
+        "$this->key:mtime" => ['data' => time()],
+        "$this->key:size" => ['data' => strlen($buf)],
+      ]);
+    }
+    return fclose($this->handle);
+  }
+
+  public function stream_eof() {
+    return feof($this->handle);
+  }
+
+  public function stream_flush() {
+    return fflush($this->handle);
+  }
+
+  public function stream_lock($operation) {
+    return flock($this->handle, $operation);
+  }
+
+  public function stream_metadata($path, $option, $value) {
+    static::usePhpWrapper();
+    $return = LocalStream::metadata($path, $option, $value);
+    static::useOurWrapper();
+    return $return;
+  }
+
+  public function stream_open($path, $mode, $options) {
+    $this->key = '';
+    if ($filename = static::getFilename($path)) {
+      $this->handle = $this->openFromStorage($filename, $mode);
+    }
+    else {
+      static::usePhpWrapper();
+      $this->handle = fopen($path, $mode, $options & STREAM_USE_PATH, $this->context);
+      static::useOurWrapper();
+    }
+    return (bool) $this->handle;
+  }
+
+  public function stream_read($count) {
+    return fread($this->handle, $count);
+  }
+
+  public function stream_seek($offset, $whence = SEEK_SET) {
+    return fseek($this->handle, $offset, $whence);
+  }
+
+  public function stream_set_option($option, $arg1, $arg2) {
+    switch ($option) {
+      case STREAM_OPTION_BLOCKING:
+        return stream_set_blocking($this->handle, $arg1);
+      case STREAM_OPTION_READ_TIMEOUT:
+        return stream_set_timeout($this->handle, $arg1, $arg2);
+      case STREAM_OPTION_WRITE_BUFFER:
+        return stream_set_write_buffer($this->handle, $arg1);
+      case STREAM_OPTION_READ_BUFFER:
+        return stream_set_read_buffer($this->handle, $arg1);
+      default:
+        throw new \BadMethodCallException(sprintf('Option %d not implemented', $option));
+    }
+  }
+
+  public function stream_stat() {
+    return fstat($this->handle);
+  }
+
+  public function stream_tell() {
+    return ftell($this->handle);
+  }
+
+  public function stream_truncate($new_size) {
+    return ftruncate($this->handle, $new_size);
+  }
+
+  public function stream_write ($data) {
+    return fwrite($this->handle, $data);
+  }
+
+  public function unlink($path) {
+    if ($filename = static::getFilename($path)) {
+      $return = static::$backend->delete(static::getKeyFromFilename($filename));
+    }
+    else {
+      static::usePhpWrapper();
+      $return = unlink($path, $this->context);
+      static::useOurWrapper();
+    }
+    return $return;
+  }
+
+  public function url_stat($path, $flags) {
+    $quiet = $flags & STREAM_URL_STAT_QUIET;
+    if ($quiet) {
+      set_error_handler(function() {});
+    }
+    if ($filename = static::getFilename($path)) {
+      try {
+        $return = $this->statFromStorage($filename);
+      }
+      catch (\Exception $e) {
+        if (!$quiet) {
+          throw $e;
+        }
+        $return = FALSE;
+      }
+    }
+    else {
+      static::usePhpWrapper();
+      $return = stat($path);
+      static::useOurWrapper();
+    }
+    if ($quiet) {
+      restore_error_handler();
+    }
+    return $return;
+  }
+
+  protected static function usePhpWrapper() {
+    stream_wrapper_restore('phar');
+  }
+
+  protected static function useOurWrapper() {
+    stream_wrapper_unregister('phar');
+    stream_wrapper_register('phar', get_called_class());
+  }
+
+  protected static function getFilename($path) {
+    $n = strlen(static::MAGIC_NAME);
+    if (substr($path, 0, $n) === static::MAGIC_NAME) {
+      return substr($path, $n);
+    }
+    return FALSE;
+  }
+
+  /**
+   * @param $filename
+   * @param $mode
+   *
+   * @return resource|FALSE
+   */
+  protected function openFromStorage($filename, $mode) {
+    $handle = fopen('php://memory', 'rwb');
+    $key = static::getKeyFromFilename($filename);
+    if (strpos($mode, 'r') !== FALSE) {
+      $cached = static::$backend->get($key);
+      if ($mode === 'r') {
+        if (!$cached) {
+          fclose($handle);
+          $handle = FALSE;
+        }
+      }
+    }
+    else {
+      $this->key = $key;
+    }
+    if (!empty($cached)) {
+      if (fwrite($handle, $cached->data) !== FALSE) {
+        fseek($handle, 0);
+      }
+      else {
+        fclose($handle);
+        $handle = FALSE;
+      }
+    }
+    return $handle;
+  }
+
+  /**
+   * @param $filename
+   *
+   * @return array
+   */
+  protected function statFromStorage($filename) {
+    $key = static::getKeyFromFilename($filename);
+    $return = [
+      'dev' => 0,
+      'ino' => 0,
+      'mode' => 0,
+      'nlink' => 0,
+      'uid' => 0,
+      'gid' => 0,
+      'rdev' => 0,
+      'size' => 0,
+      'atime' => 0,
+      'mtime' => 0,
+      'ctime' => 0,
+      'blksize' => -1,
+      'blocks' => -1,
+    ];
+    $cids = ["$key:size", "$key:mtime"];
+    $cached = static::$backend->getMultiple($cids);
+    if (count($cached) == 2) {
+      $return['size'] = $cached["$key:size"]->data;
+      $return['mtime'] = $cached["$key:mtime"]->data;
+    }
+    return $return + array_values($return);
+  }
+
+  /**
+   * @param $filename
+   * @return string
+   */
+  protected static function getKeyFromFilename($filename) {
+    return hash_hmac('sha256', $filename, static::$secret);
+  }
+
+}
