diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index da9f11b..09e9536 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -23,6 +23,7 @@
 use Drupal\Core\StackMiddleware\ReverseProxyMiddleware;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Url;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -91,7 +92,7 @@
  *
  * @see install_state_defaults()
  */
-function install_drupal($class_loader, $settings = array()) {
+function install_drupal($class_loader, $settings = array(), $sapi_adapter = NULL) {
   // Support the old way of calling this function with just a settings array.
   // @todo Remove this when Drush is updated in the Drupal testing
   //   infrastructure in https://www.drupal.org/node/2389243
@@ -110,7 +111,7 @@ function install_drupal($class_loader, $settings = array()) {
   try {
     // Begin the page request. This adds information about the current state of
     // the Drupal installation to the passed-in array.
-    install_begin_request($class_loader, $install_state);
+    install_begin_request($class_loader, $install_state, $sapi_adapter);
     // Based on the installation state, run the remaining tasks for this page
     // request, and collect any output.
     $output = install_run_tasks($install_state);
@@ -282,8 +283,11 @@ function install_state_defaults() {
  *   An array of information about the current installation state. This is
  *   modified with information gleaned from the beginning of the page request.
  */
-function install_begin_request($class_loader, &$install_state) {
+function install_begin_request($class_loader, &$install_state, $sapi_adapter = NULL) {
   $request = Request::createFromGlobals();
+  if (!isset($sapi_adapter)) {
+    $sapi_adapter = SAPIAdapter::creatFromGlobals();
+  }
 
   // Add any installation parameters passed in via the URL.
   if ($install_state['interactive']) {
@@ -400,7 +404,7 @@ function install_begin_request($class_loader, &$install_state) {
   }
 
   // Only allow dumping the container once the hash salt has been created.
-  $kernel = InstallerKernel::createFromRequest($request, $class_loader, $environment, (bool) Settings::get('hash_salt', FALSE));
+  $kernel = InstallerKernel::createFromRequest($request, $class_loader, $environment, (bool) Settings::get('hash_salt', FALSE), $sapi_adapter);
   $kernel->setSitePath($site_path);
   $kernel->boot();
   $container = $kernel->getContainer();
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index d42c550..756b7a7 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -21,6 +21,7 @@
 use Drupal\Core\File\MimeType\MimeTypeGuesser;
 use Drupal\Core\Http\TrustedHostsRequestFactory;
 use Drupal\Core\Language\Language;
+use Drupal\Core\SAPI\SAPIAdapter;
 use Drupal\Core\Site\Settings;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -213,15 +214,11 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
   protected $serviceProviders;
 
   /**
-   * Whether the PHP environment has been initialized.
+   * The SAPI adapter.
    *
-   * This legacy phase can only be booted once because it sets session INI
-   * settings. If a session has already been started, re-generating these
-   * settings would break the session.
-   *
-   * @var bool
+   * @var \Drupal\Core\SAPI\SAPIAdapterInterface.
    */
-  protected static $isEnvironmentInitialized = FALSE;
+  protected $sapiAdapter;
 
   /**
    * The site directory.
@@ -251,15 +248,22 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
    * @param bool $allow_dumping
    *   (optional) FALSE to stop the container from being written to or read
    *   from disk. Defaults to TRUE.
+   * @param \Drupal\Core\SAPI\SAPIAdapterInterface $sapi_adapter
+   *   (optional) A SAPI adapter to use in order to prepare the execution
+   *   environment. A suitable adapter is selected automatically if omitted.
    *
    * @return static
    *
    * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
    *   In case the host name in the request is not trusted.
    */
-  public static function createFromRequest(Request $request, $class_loader, $environment, $allow_dumping = TRUE) {
-    $kernel = new static($environment, $class_loader, $allow_dumping);
-    static::bootEnvironment();
+  public static function createFromRequest(Request $request, $class_loader, $environment, $allow_dumping = TRUE, $sapi_adapter = NULL) {
+    if (!isset($sapi_adapter)) {
+      $sapi_adapter = SAPIAdapter::createFromGlobals();
+    }
+    $kernel = new static($environment, $class_loader, $allow_dumping, $sapi_adapter);
+    $core_root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
+    $sapi_adapter->prepareEnvironment($core_root, $environment);
     $kernel->initializeSettings($request);
     return $kernel;
   }
@@ -276,11 +280,20 @@ public static function createFromRequest(Request $request, $class_loader, $envir
    * @param bool $allow_dumping
    *   (optional) FALSE to stop the container from being written to or read
    *   from disk. Defaults to TRUE.
+   * @param \Drupal\Core\SAPI\SAPIAdapterInterface $sapi_adapter
+   *   (optional) A SAPI adapter to use in order to prepare the execution
+   *   environment. A suitable adapter is selected automatically if omitted.
    */
-  public function __construct($environment, $class_loader, $allow_dumping = TRUE) {
+  public function __construct($environment, $class_loader, $allow_dumping = TRUE, $sapi_adapter = NULL) {
     $this->environment = $environment;
     $this->classLoader = $class_loader;
     $this->allowDumping = $allow_dumping;
+    if (isset($sapi_adapter)) {
+      $this->sapiAdapter = $sapi_adapter;
+    }
+    else {
+      $this->sapiAdapter = SAPIAdapter::createFromGlobals();
+    }
     $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
   }
 
@@ -615,7 +628,7 @@ public function terminate(Request $request, Response $response) {
    */
   public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
     // Ensure sane PHP environment variables.
-    static::bootEnvironment();
+    $this->sapiAdapter->prepareEnvironment($this->root, $this->environment);
 
     try {
       $this->initializeSettings($request);
@@ -867,78 +880,6 @@ protected function initializeContainer() {
   }
 
   /**
-   * Setup a consistent PHP environment.
-   *
-   * This method sets PHP environment options we want to be sure are set
-   * correctly for security or just saneness.
-   */
-  public static function bootEnvironment() {
-    if (static::$isEnvironmentInitialized) {
-      return;
-    }
-
-    // Include our bootstrap file.
-    $core_root = dirname(dirname(dirname(__DIR__)));
-    require_once $core_root . '/includes/bootstrap.inc';
-
-    // Enforce E_STRICT, but allow users to set levels not part of E_STRICT.
-    error_reporting(E_STRICT | E_ALL);
-
-    // Override PHP settings required for Drupal to work properly.
-    // sites/default/default.settings.php contains more runtime settings.
-    // The .htaccess file contains settings that cannot be changed at runtime.
-
-    // Use session cookies, not transparent sessions that puts the session id in
-    // the query string.
-    ini_set('session.use_cookies', '1');
-    ini_set('session.use_only_cookies', '1');
-    ini_set('session.use_trans_sid', '0');
-    // Don't send HTTP headers using PHP's session handler.
-    // Send an empty string to disable the cache limiter.
-    ini_set('session.cache_limiter', '');
-    // Use httponly session cookies.
-    ini_set('session.cookie_httponly', '1');
-
-    // Set sane locale settings, to ensure consistent string, dates, times and
-    // numbers handling.
-    setlocale(LC_ALL, 'C');
-
-    // Detect string handling method.
-    Unicode::check();
-
-    // Indicate that code is operating in a test child site.
-    if (!defined('DRUPAL_TEST_IN_CHILD_SITE')) {
-      if ($test_prefix = drupal_valid_test_ua()) {
-        // Only code that interfaces directly with tests should rely on this
-        // constant; e.g., the error/exception handler conditionally adds further
-        // error information into HTTP response headers that are consumed by
-        // Simpletest's internal browser.
-        define('DRUPAL_TEST_IN_CHILD_SITE', TRUE);
-
-        // Web tests are to be conducted with runtime assertions active.
-        assert_options(ASSERT_ACTIVE, TRUE);
-        // Now synchronize PHP 5 and 7's handling of assertions as much as
-        // possible.
-        \Drupal\Component\Assertion\Handle::register();
-
-        // Log fatal errors to the test site directory.
-        ini_set('log_errors', 1);
-        ini_set('error_log', DRUPAL_ROOT . '/sites/simpletest/' . substr($test_prefix, 10) . '/error.log');
-      }
-      else {
-        // Ensure that no other code defines this.
-        define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
-      }
-    }
-
-    // Set the Drupal custom error handler.
-    set_error_handler('_drupal_error_handler');
-    set_exception_handler('_drupal_exception_handler');
-
-    static::$isEnvironmentInitialized = TRUE;
-  }
-
-  /**
    * Locate site path and initialize settings singleton.
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
diff --git a/core/lib/Drupal/Core/SAPI/CliSAPIAdapter.php b/core/lib/Drupal/Core/SAPI/CliSAPIAdapter.php
new file mode 100644
index 0000000..8b6a3a8
--- /dev/null
+++ b/core/lib/Drupal/Core/SAPI/CliSAPIAdapter.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\SAPI\CliSAPIAdapter.
+ */
+
+namespace Drupal\Core\SAPI;
+
+/**
+ * A SAPI adapter used when run from the command line.
+ */
+class CliSAPIAdapter extends SAPIAdapter {
+
+  /**
+   * {@inheritdocs}
+   */
+  static function createFromGlobals() {
+    return new static();
+  }
+
+  /**
+   * {@inheritdocs}
+   */
+  public function prepareEnvironment($root, $environment) {
+    parent::prepareEnvironment($root, $environment);
+
+    // Simpletest child site is never run in CLI.
+    define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
+
+    // Set the Drupal custom error handler.
+    set_error_handler('_drupal_error_handler');
+    set_exception_handler('_drupal_exception_handler');
+  }
+}
diff --git a/core/lib/Drupal/Core/SAPI/HttpSAPIAdapter.php b/core/lib/Drupal/Core/SAPI/HttpSAPIAdapter.php
new file mode 100644
index 0000000..a6d6200
--- /dev/null
+++ b/core/lib/Drupal/Core/SAPI/HttpSAPIAdapter.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\SAPI\HttpSAPIAdapter.
+ */
+
+namespace Drupal\Core\SAPI;
+
+/**
+ * A SAPI adapter used when application is served over HTTP.
+ */
+class HttpSAPIAdapter extends SAPIAdapter {
+
+  /**
+   * {@inheritdocs}
+   */
+  static function createFromGlobals() {
+    return new static();
+  }
+
+  /**
+   * {@inheritdocs}
+   */
+  public function prepareEnvironment($root, $environment) {
+    parent::prepareEnvironment($root, $environment);
+
+    // Enforce E_STRICT, but allow users to set levels not part of E_STRICT.
+    error_reporting(E_STRICT | E_ALL);
+
+    // Override PHP settings required for Drupal to work properly.
+    // sites/default/default.settings.php contains more runtime settings.
+    // The .htaccess file contains settings that cannot be changed at runtime.
+
+    // Use session cookies, not transparent sessions that puts the session id in
+    // the query string.
+    ini_set('session.use_cookies', '1');
+    ini_set('session.use_only_cookies', '1');
+    ini_set('session.use_trans_sid', '0');
+    // Don't send HTTP headers using PHP's session handler.
+    // Send an empty string to disable the cache limiter.
+    ini_set('session.cache_limiter', '');
+    // Use httponly session cookies.
+    ini_set('session.cookie_httponly', '1');
+
+    // Indicate that code is operating in a test child site.
+      if ($test_prefix = drupal_valid_test_ua()) {
+        // Only code that interfaces directly with tests should rely on this
+        // constant; e.g., the error/exception handler conditionally adds further
+        // error information into HTTP response headers that are consumed by
+        // Simpletest's internal browser.
+        define('DRUPAL_TEST_IN_CHILD_SITE', TRUE);
+
+        // Web tests are to be conducted with runtime assertions active.
+        assert_options(ASSERT_ACTIVE, TRUE);
+        // Now synchronize PHP 5 and 7's handling of assertions as much as
+        // possible.
+        \Drupal\Component\Assertion\Handle::register();
+
+        // Log fatal errors to the test site directory.
+        ini_set('log_errors', 1);
+        ini_set('error_log', $root . '/sites/simpletest/' . substr($test_prefix, 10) . '/error.log');
+      }
+      else {
+        // Ensure that no other code defines this.
+        define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
+      }
+
+    // Set the Drupal custom error handler.
+    set_error_handler('_drupal_error_handler');
+    set_exception_handler('_drupal_exception_handler');
+  }
+}
diff --git a/core/lib/Drupal/Core/SAPI/NullSAPIAdapter.php b/core/lib/Drupal/Core/SAPI/NullSAPIAdapter.php
new file mode 100644
index 0000000..670a99f
--- /dev/null
+++ b/core/lib/Drupal/Core/SAPI/NullSAPIAdapter.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\NullSAPIAdapter.
+ */
+
+namespace Drupal\Core\SAPI;
+
+/**
+ * A SAPI adapter for testing purposes.
+ *
+ * Use this adapter only when a new kernel is instantiated insid an environment
+ * which has been prepared already by a production SAPI adapter.
+ */
+class NullSAPIAdapter implements SAPIAdapterInterface {
+
+  /**
+   * {@inheritdocs}
+   */
+  static function createFromGlobals() {
+    return new static();
+  }
+
+  /**
+   * {@inheritdocs}
+   */
+  public function prepareEnvironment($core_root, $environment) {
+  }
+
+}
diff --git a/core/lib/Drupal/Core/SAPI/SAPIAdapter.php b/core/lib/Drupal/Core/SAPI/SAPIAdapter.php
new file mode 100644
index 0000000..22ea440
--- /dev/null
+++ b/core/lib/Drupal/Core/SAPI/SAPIAdapter.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\SAPI\SAPIAdapter.
+ */
+
+namespace Drupal\Core\SAPI;
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Common base class for all production level SAPI adapters.
+ */
+class SAPIAdapter implements SAPIAdapterInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createFromGlobals() {
+    if (PHP_SAPI === 'cli') {
+      return CliSAPIAdapter::createFromGlobals();
+    }
+    else {
+      return HttpSAPIAdapter::createFromGlobals();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareEnvironment($root, $environment) {
+    // Include our bootstrap file.
+    require_once $root . '/core/includes/bootstrap.inc';
+
+    // Set sane locale settings, to ensure consistent string, dates, times and
+    // numbers handling.
+    setlocale(LC_ALL, 'C');
+
+    // Detect string handling method.
+    Unicode::check();
+
+    // Enforce E_STRICT, but allow users to set levels not part of E_STRICT.
+    error_reporting(E_STRICT | E_ALL);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/SAPI/SAPIAdapterInterface.php b/core/lib/Drupal/Core/SAPI/SAPIAdapterInterface.php
new file mode 100644
index 0000000..801995d
--- /dev/null
+++ b/core/lib/Drupal/Core/SAPI/SAPIAdapterInterface.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\SAPI\SAPIAdapterInterface.
+ */
+
+namespace Drupal\Core\SAPI;
+
+/**
+ * Interface for SAPI adapters.
+ *
+ * A SAPI adapter prepares the global scope as needed by the application
+ * depending on the current server API. E.g., it is responsible for setting up
+ * important ini-settings and for installing error/exception handlers.
+ */
+interface SAPIAdapterInterface {
+  /**
+   * Creates a new SAPI adapter suitable for the current execution environment.
+   *
+   * @return static
+   */
+  public static function createFromGlobals();
+
+  /**
+   * Sets up the global scope as needed by the application.
+   *
+   * @param string $root
+   *   The app root.
+   * @param string $environment
+   *   String indicating the environment, e.g., 'prod' or 'dev'.
+   */
+  public function prepareEnvironment($root, $environment);
+
+}
diff --git a/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/core/lib/Drupal/Core/Test/TestRunnerKernel.php
index 9a19d84..e78cc5f 100644
--- a/core/lib/Drupal/Core/Test/TestRunnerKernel.php
+++ b/core/lib/Drupal/Core/Test/TestRunnerKernel.php
@@ -20,15 +20,17 @@ class TestRunnerKernel extends DrupalKernel {
   /**
    * {@inheritdoc}
    */
-  public static function createFromRequest(Request $request, $class_loader, $environment = 'test_runner', $allow_dumping = TRUE) {
-    return parent::createFromRequest($request, $class_loader, $environment);
+  public static function createFromRequest(Request $request, $class_loader, $environment = 'test_runner', $allow_dumping = TRUE, $sapi_adapter = NULL) {
+    return parent::createFromRequest($request, $class_loader, $environment, $allow_dumping, $sapi_adapter);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function __construct($environment, $class_loader) {
-    parent::__construct($environment, $class_loader, FALSE);
+  public function __construct($environment, $class_loader, $allow_dumping = TRUE, $sapi_adapter = NULL) {
+    // Forcibly disable container dumping.
+    $allow_dumping = FALSE;
+    parent::__construct($environment, $class_loader, $allow_dumping, $sapi_adapter);
 
     // Prime the module list and corresponding Extension objects.
     // @todo Remove System module. Needed because
diff --git a/core/lib/Drupal/Core/Update/UpdateKernel.php b/core/lib/Drupal/Core/Update/UpdateKernel.php
index 80f243c..871017f 100644
--- a/core/lib/Drupal/Core/Update/UpdateKernel.php
+++ b/core/lib/Drupal/Core/Update/UpdateKernel.php
@@ -59,9 +59,9 @@ protected function cacheDrupalContainer(array $container_definition) {
    * {@inheritdoc}
    */
   public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
-    try {
-      static::bootEnvironment();
+    $this->sapiAdapter->prepareEnvironment($this->root, $this->environment);
 
+    try {
       // First boot up basic things, like loading the include files.
       $this->initializeSettings($request);
       $this->boot();
diff --git a/core/modules/simpletest/src/BrowserTestBase.php b/core/modules/simpletest/src/BrowserTestBase.php
index dc933ac..65bd7d7 100644
--- a/core/modules/simpletest/src/BrowserTestBase.php
+++ b/core/modules/simpletest/src/BrowserTestBase.php
@@ -17,6 +17,7 @@
 use Drupal\Core\Database\ConnectionNotDefinedException;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Session\AnonymousUserSession;
 use Drupal\Core\Session\UserSession;
@@ -59,6 +60,13 @@
   protected $classLoader;
 
   /**
+   * The SAPI adapter.
+   *
+   * @var \Drupal\Core\SAPI\SAPIAdapterInterface.
+   */
+  protected $sapiAdapter;
+
+  /**
    * The site directory of this test run.
    *
    * @var string
@@ -871,7 +879,7 @@ public function installDrupal() {
 
     // Execute the non-interactive installer.
     require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
-    install_drupal($parameters);
+    install_drupal($this->classLoader, $parameters, $this->sapiAdapter);
 
     // Import new settings.php written by the installer.
     Settings::initialize(DRUPAL_ROOT, $directory, $this->classLoader);
@@ -888,7 +896,7 @@ public function installDrupal() {
     chmod($directory, 0777);
 
     $request = \Drupal::request();
-    $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE);
+    $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE, $this->sapiAdapter);
     $this->kernel->prepareLegacyRequest($request);
     // Force the container to be built from scratch instead of loaded from the
     // disk. This forces us to not accidentally load the parent site.
@@ -1080,8 +1088,9 @@ private function changeDatabasePrefix() {
   protected function prepareEnvironment() {
     // Bootstrap Drupal so we can use Drupal's built in functions.
     $this->classLoader = require __DIR__ . '/../../../../autoload.php';
+    $this->sapiAdapter = new NullSAPIAdapter();
     $request = Request::createFromGlobals();
-    $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader);
+    $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader, 'test_runner', FALSE, $this->sapiAdapter);
     // TestRunnerKernel expects the working directory to be DRUPAL_ROOT.
     chdir(DRUPAL_ROOT);
     $kernel->prepareLegacyRequest($request);
diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php
index adc5463..b4fef22 100644
--- a/core/modules/simpletest/src/InstallerTestBase.php
+++ b/core/modules/simpletest/src/InstallerTestBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Language\Language;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Session\UserSession;
 use Drupal\Core\Site\Settings;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -16,6 +17,7 @@
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
 
+
 /**
  * Base class for testing the interactive installer.
  */
@@ -151,7 +153,8 @@ protected function setUp() {
       // WebTestBase::tearDown() will delete the entire test site directory.
       // Not using File API; a potential error must trigger a PHP warning.
       chmod($this->container->get('app.root') . '/' . $this->siteDirectory, 0777);
-      $this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE);
+      $sapi_adapter = new NullSAPIAdapter();
+      $this->kernel = DrupalKernel::createFromRequest($request, $class_loader, 'prod', FALSE, $sapi_adapter);
       $this->kernel->prepareLegacyRequest($request);
       $this->container = $this->kernel->getContainer();
 
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 681e75c..16145b7 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -17,9 +17,10 @@
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
 use Drupal\Core\Language\Language;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Site\Settings;
-use Symfony\Component\DependencyInjection\Parameter;
 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -181,7 +182,8 @@ protected function setUp() {
 
     // Bootstrap a new kernel.
     $class_loader = require DRUPAL_ROOT . '/autoload.php';
-    $this->kernel = new DrupalKernel('testing', $class_loader, FALSE);
+    $sapi_adapter = new NullSAPIAdapter();
+    $this->kernel = new DrupalKernel('testing', $class_loader, FALSE, $sapi_adapter);
     $request = Request::create('/');
     $site_path = DrupalKernel::findSitePath($request);
     $this->kernel->setSitePath($site_path);
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index e9b6376..1912644 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -7,15 +7,14 @@
 
 namespace Drupal\simpletest;
 
-use Drupal\block\Entity\Block;
 use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Serialization\Yaml;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Cache\Cache;
-use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Entity\EntityInterface;
@@ -23,6 +22,7 @@
 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
 use Drupal\Core\Extension\MissingDependencyException;
 use Drupal\Core\Render\Element;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Session\AnonymousUserSession;
 use Drupal\Core\Session\UserSession;
@@ -30,6 +30,7 @@
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Test\AssertMailTrait;
 use Drupal\Core\Url;
+use Drupal\block\Entity\Block;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Zend\Diactoros\Uri;
@@ -234,12 +235,20 @@
   protected $classLoader;
 
   /**
+   * The SAPI adapter.
+   *
+   * @var \Drupal\Core\SAPI\SAPIAdapterInterface.
+   */
+  protected $sapiAdapter;
+
+  /**
    * Constructor for \Drupal\simpletest\WebTestBase.
    */
   function __construct($test_id = NULL) {
     parent::__construct($test_id);
     $this->skipClasses[__CLASS__] = TRUE;
     $this->classLoader = require DRUPAL_ROOT . '/autoload.php';
+    $this->sapiAdapter = new NullSAPIAdapter();
   }
 
   /**
@@ -578,7 +587,7 @@ protected function setUp() {
    */
   protected function doInstall() {
     require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
-    install_drupal($this->classLoader, $this->installParameters());
+    install_drupal($this->classLoader, $this->installParameters(), $this->sapiAdapter);
   }
 
   /**
@@ -867,7 +876,7 @@ protected function initUserSession() {
    *   The container.
    */
   protected function initKernel(Request $request) {
-    $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE);
+    $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE, $this->sapiAdapter);
     $this->kernel->prepareLegacyRequest($request);
     // Force the container to be built from scratch instead of loaded from the
     // disk. This forces us to not accidentally load the parent site.
diff --git a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
index e8e35a6..5596972 100644
--- a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\DrupalKernel;
 
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Site\Settings;
 use Drupal\simpletest\KernelTestBase;
 use Symfony\Component\HttpFoundation\Request;
@@ -62,7 +63,8 @@ protected function prepareConfigDirectories() {
   protected function getTestKernel(Request $request, array $modules_enabled = NULL) {
     // Manually create kernel to avoid replacing settings.
     $class_loader = require DRUPAL_ROOT . '/autoload.php';
-    $kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing');
+    $sapi_adapter = new NullSAPIAdapter();
+    $kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing', TRUE, $sapi_adapter);
     $this->settingsSet('container_yamls', []);
     $this->settingsSet('hash_salt', $this->databasePrefix);
     if (isset($modules_enabled)) {
@@ -159,6 +161,7 @@ public function testCompileDIC() {
   public function testRepeatedBootWithDifferentEnvironment() {
     $request = Request::createFromGlobals();
     $class_loader = require DRUPAL_ROOT . '/autoload.php';
+    $sapi_adapter = new NullSAPIAdapter();
 
     $environments = [
       'testing1',
@@ -168,7 +171,7 @@ public function testRepeatedBootWithDifferentEnvironment() {
     ];
 
     foreach ($environments as $environment) {
-      $kernel = DrupalKernel::createFromRequest($request, $class_loader, $environment);
+      $kernel = DrupalKernel::createFromRequest($request, $class_loader, $environment, TRUE, $sapi_adapter);
       $this->settingsSet('container_yamls', []);
       $this->settingsSet('hash_salt', $this->databasePrefix);
       $kernel->boot();
diff --git a/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php b/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
index 0ba18d6..6a43e0a 100644
--- a/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
+++ b/core/modules/system/src/Tests/System/IgnoreReplicaSubscriberTest.php
@@ -8,12 +8,13 @@
 namespace Drupal\system\Tests\System;
 
 use Drupal\Core\Database\Database;
-use Drupal\Core\EventSubscriber\ReplicaDatabaseIgnoreSubscriber;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\EventSubscriber\ReplicaDatabaseIgnoreSubscriber;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\simpletest\KernelTestBase;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 
 /**
  * Tests that ReplicaDatabaseIgnoreSubscriber functions correctly.
@@ -34,7 +35,8 @@ function testSystemInitIgnoresSecondaries() {
 
     db_ignore_replica();
     $class_loader = require \Drupal::root() . '/autoload.php';
-    $kernel = new DrupalKernel('testing', $class_loader, FALSE);
+    $sapi_adapter = new NullSAPIAdapter();
+    $kernel = new DrupalKernel('testing', $class_loader, FALSE, $sapi_adapter);
     $event = new GetResponseEvent($kernel, Request::create('http://example.com'), HttpKernelInterface::MASTER_REQUEST);
     $subscriber = new ReplicaDatabaseIgnoreSubscriber();
     $subscriber->checkReplicaServer($event);
diff --git a/core/rebuild.php b/core/rebuild.php
index ccd4976..b3aa8ba 100644
--- a/core/rebuild.php
+++ b/core/rebuild.php
@@ -12,10 +12,11 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\SAPI\SAPIAdapter;
 use Drupal\Core\Site\Settings;
-use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 
 // Change the directory to the Drupal root.
 chdir('..');
@@ -25,8 +26,8 @@
 
 $request = Request::createFromGlobals();
 // Manually resemble early bootstrap of DrupalKernel::boot().
-require_once __DIR__ . '/includes/bootstrap.inc';
-DrupalKernel::bootEnvironment();
+$sapi_adapter = SAPIAdapter::createFromGlobals();
+$sapi_adapter->prepareEnvironment(__DIR__ . '/../', 'prod');
 
 try {
   Settings::initialize(dirname(__DIR__), DrupalKernel::findSitePath($request), $autoloader);
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index 160c84d..4c0f178 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -21,6 +21,7 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Site\Settings;
 use Drupal\simpletest\AssertContentTrait;
 use Drupal\simpletest\AssertHelperTrait;
@@ -119,6 +120,13 @@
   protected $classLoader;
 
   /**
+   * The SAPI adapter.
+   *
+   * @var \Drupal\Core\SAPI\SAPIAdapterInterface.
+   */
+  protected $sapiAdapter;
+
+  /**
    * @var string
    */
   protected $siteDirectory;
@@ -237,6 +245,7 @@ protected function bootEnvironment() {
     \Drupal::unsetContainer();
 
     $this->classLoader = require $this->root . '/autoload.php';
+    $this->sapiAdapter = new NullSAPIAdapter();
 
     require_once $this->root . '/core/includes/bootstrap.inc';
 
@@ -326,7 +335,7 @@ private function bootKernel() {
     }
 
     // Bootstrap the kernel. Do not use createFromRequest() to retain Settings.
-    $kernel = new DrupalKernel('testing', $this->classLoader, FALSE);
+    $kernel = new DrupalKernel('testing', $this->classLoader, FALSE, $this->sapiAdapter);
     $kernel->setSitePath($this->siteDirectory);
     // Boot the precompiled container. The kernel will enhance it with synthetic
     // services.
@@ -474,7 +483,7 @@ protected function getDatabaseConnectionInfo() {
    */
   private function getCompiledContainerBuilder(array $modules) {
     if (!isset(self::$initialContainerBuilder)) {
-      $kernel = new DrupalKernel('testing', $this->classLoader, FALSE);
+      $kernel = new DrupalKernel('testing', $this->classLoader, FALSE, $this->sapiAdapter);
       $kernel->setSitePath($this->siteDirectory);
       if ($modules && $extensions = $this->getExtensionsForModules($modules)) {
         $kernel->updateModules($extensions, $extensions);
diff --git a/core/tests/Drupal/Tests/Core/DrupalKernel/DiscoverServiceProvidersTest.php b/core/tests/Drupal/Tests/Core/DrupalKernel/DiscoverServiceProvidersTest.php
index c955b58..edbb734 100644
--- a/core/tests/Drupal/Tests/Core/DrupalKernel/DiscoverServiceProvidersTest.php
+++ b/core/tests/Drupal/Tests/Core/DrupalKernel/DiscoverServiceProvidersTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\DrupalKernel;
 
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\SAPI\NullSAPIAdapter;
 use Drupal\Core\Site\Settings;
 use Drupal\Tests\UnitTestCase;
 
@@ -29,7 +30,8 @@ public function testDiscoverServiceCustom() {
       ),
     ));
 
-    $kernel = new DrupalKernel('prod', new \Composer\Autoload\ClassLoader());
+    $sapi_handler = new NullSAPIAdapter();
+    $kernel = new DrupalKernel('prod', new \Composer\Autoload\ClassLoader(), TRUE, $sapi_handler);
     $kernel->discoverServiceProviders();
 
     $expect = array(
@@ -49,7 +51,8 @@ public function testDiscoverServiceCustom() {
    */
   public function testDiscoverServiceNoContainerYamls() {
     new Settings([]);
-    $kernel = new DrupalKernel('prod', new \Composer\Autoload\ClassLoader());
+    $sapi_handler = new NullSAPIAdapter();
+    $kernel = new DrupalKernel('prod', new \Composer\Autoload\ClassLoader(), TRUE, $sapi_handler);
     $kernel->discoverServiceProviders();
 
     $expect = [
