Commit 5e58da00 authored by catch's avatar catch

Issue #2016629 by larowlan, neclimdul, sun, alexpott, jibran, ParisLiakos,...

Issue #2016629 by larowlan, neclimdul, sun, alexpott, jibran, ParisLiakos, donquixote, effulgentsia, msonnabaum: Refactor bootstrap to better utilize the kernel.
parent 18bbe3a7
......@@ -25,8 +25,9 @@ ErrorDocument 404 /index.php
DirectoryIndex index.php index.html index.htm
# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_environment_initialize() in
# core/includes/bootstrap.inc for settings that can be changed at runtime.
# sites/default/default.settings.php and
# Drupal\Core\DrupalKernel::bootEnvironment() for settings that can be
# changed at runtime.
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
......@@ -119,7 +120,7 @@ DirectoryIndex index.php index.html index.htm
RewriteRule ^ %1/core/%2 [L,QSA,R=301]
# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
# index.php.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
......
......@@ -20,13 +20,15 @@
* @link authorize Authorized operation helper functions @endlink
*/
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Site\Settings;
use Drupal\Core\Page\DefaultHtmlPageRenderer;
// Change the directory to the Drupal root.
chdir('..');
require_once __DIR__ . '/vendor/autoload.php';
$autoloader = require_once __DIR__ . '/vendor/autoload.php';
/**
* Global flag to identify update.php and authorize.php runs.
......@@ -51,18 +53,9 @@ function authorize_access_allowed() {
return Settings::get('allow_authorize_operations', TRUE) && user_access('administer software updates');
}
// *** Real work of the script begins here. ***
require_once __DIR__ . '/includes/bootstrap.inc';
require_once __DIR__ . '/includes/common.inc';
require_once __DIR__ . '/includes/file.inc';
require_once __DIR__ . '/includes/module.inc';
require_once __DIR__ . '/includes/ajax.inc';
// Prepare a minimal bootstrap.
drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE);
$request = \Drupal::request();
\Drupal::service('request_stack')->push($request);
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->prepareLegacyRequest($request);
// We have to enable the user and system modules, even to check access and
// display errors via the maintenance theme.
......
......@@ -204,6 +204,10 @@ services:
arguments: ['@access_check.theme']
tags:
- { name: service_collector, tag: theme_negotiator, call: addNegotiator }
theme.negotiator.request_subscriber:
class: Drupal\Core\EventSubscriber\ThemeNegotiatorRequestSubscriber
tags:
- { name: event_subscriber }
theme.negotiator.default:
class: Drupal\Core\Theme\DefaultNegotiator
arguments: ['@config.factory']
......@@ -608,10 +612,6 @@ services:
tags:
- { name: event_subscriber }
arguments: ['@path.alias_manager', '@path_processor_manager']
legacy_request_subscriber:
class: Drupal\Core\EventSubscriber\LegacyRequestSubscriber
tags:
- { name: event_subscriber }
finish_response_subscriber:
class: Drupal\Core\EventSubscriber\FinishResponseSubscriber
tags:
......
This diff is collapsed.
......@@ -2603,70 +2603,6 @@ function drupal_valid_token($token, $value = '') {
return \Drupal::csrfToken()->validate($token, $value);
}
/**
* Loads code for subsystems and modules, and registers stream wrappers.
*/
function _drupal_bootstrap_code() {
require_once __DIR__ . '/../../' . Settings::get('path_inc', 'core/includes/path.inc');
require_once __DIR__ . '/module.inc';
require_once __DIR__ . '/theme.inc';
require_once __DIR__ . '/pager.inc';
require_once __DIR__ . '/../../' . Settings::get('menu_inc', 'core/includes/menu.inc');
require_once __DIR__ . '/tablesort.inc';
require_once __DIR__ . '/file.inc';
require_once __DIR__ . '/unicode.inc';
require_once __DIR__ . '/form.inc';
require_once __DIR__ . '/mail.inc';
require_once __DIR__ . '/ajax.inc';
require_once __DIR__ . '/errors.inc';
require_once __DIR__ . '/schema.inc';
require_once __DIR__ . '/entity.inc';
// Load all enabled modules
\Drupal::moduleHandler()->loadAll();
// Make sure all stream wrappers are registered.
file_get_stream_wrappers();
// Ensure mt_rand() is reseeded to prevent random values from one page load
// being exploited to predict random values in subsequent page loads.
$seed = unpack("L", Crypt::randomBytes(4));
mt_srand($seed[1]);
// Set the allowed protocols once we have the config available.
$allowed_protocols = \Drupal::config('system.filter')->get('protocols');
if (!isset($allowed_protocols)) {
// \Drupal\Component\Utility\UrlHelper::filterBadProtocol() is called by the
// installer and update.php, in which case the configuration may not exist
// (yet). Provide a minimal default set of allowed protocols for these
// cases.
$allowed_protocols = array('http', 'https');
}
UrlHelper::setAllowedProtocols($allowed_protocols);
}
/**
* Temporary BC function for scripts not using DrupalKernel.
*
* DrupalKernel skips this and replicates it via event listeners.
*
* @see \Drupal\Core\EventSubscriber\PathSubscriber;
* @see \Drupal\Core\EventSubscriber\LegacyRequestSubscriber;
*/
function _drupal_bootstrap_full($skip = FALSE) {
static $called = FALSE;
if ($called || $skip) {
$called = TRUE;
return;
}
// Let all modules take action before the menu system handles the request.
// We do not want this while running update.php.
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
drupal_theme_initialize();
}
}
/**
* Stores the current page in the cache.
*
......
......@@ -120,7 +120,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
$is_installer = drupal_installation_attempted();
// Initialize a maintenance theme if the bootstrap was not complete.
// Do it early because drupal_set_message() triggers a drupal_theme_initialize().
if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
if ($fatal && drupal_get_bootstrap_phase() < DRUPAL_BOOTSTRAP_CODE) {
// The installer initializes a maintenance theme at the earliest possible
// point in time already. Do not unset that.
if (!$is_installer) {
......@@ -244,9 +244,9 @@ function _drupal_log_error($error, $fatal = FALSE) {
/**
* Returns the current error level.
*
* This function should only be used to get the current error level pre
* DRUPAL_BOOTSTRAP_KERNEL or before Drupal is installed. In all other
* situations the following code is preferred:
* This function should only be used to get the current error level prior to
* DRUPAL_BOOTSTRAP_KERNEL or before Drupal is installed. In all other situations
* the following code is preferred:
* @code
* \Drupal::config('system.logging')->get('error_level');
* @endcode
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Installer\Exception\AlreadyInstalledException;
use Drupal\Core\Installer\Exception\InstallerException;
use Drupal\Core\Installer\Exception\NoProfilesException;
use Drupal\Core\Installer\InstallerKernel;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Page\DefaultHtmlPageRenderer;
......@@ -272,12 +273,6 @@ function install_begin_request(&$install_state) {
// Allow command line scripts to override server variables used by Drupal.
require_once __DIR__ . '/bootstrap.inc';
// Initialize conf_path().
// This primes the site path to be used during installation. By not requiring
// settings.php, a bare site folder can be prepared in the /sites directory,
// which will be used for installing Drupal.
conf_path(FALSE);
// If the hash salt leaks, it becomes possible to forge a valid testing user
// agent, install a new copy of Drupal, and take over the original site.
// The user agent header is used to pass a database prefix in the request when
......@@ -288,7 +283,8 @@ function install_begin_request(&$install_state) {
exit;
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
$site_path = DrupalKernel::findSitePath($request, FALSE);
Settings::initialize($site_path);
// Ensure that procedural dependencies are loaded as early as possible,
// since the error/exception handlers depend on them.
......@@ -359,22 +355,17 @@ function install_begin_request(&$install_state) {
$environment = 'prod';
}
$kernel = new DrupalKernel($environment, drupal_classloader(), FALSE);
$kernel = InstallerKernel::createFromRequest($request, drupal_classloader(), $environment);
$kernel->setSitePath($site_path);
$kernel->boot();
// Enter the request scope and add the Request.
// @todo Remove this after converting all installer screens into controllers.
$container = $kernel->getContainer();
$container->enterScope('request');
$container->set('request', $request, 'request');
$container->get('request_stack')->push($request);
// Register the file translation service.
if (isset($GLOBALS['config']['locale.settings']['translation.path'])) {
$directory = $GLOBALS['config']['locale.settings']['translation.path'];
}
else {
$directory = conf_path() . '/files/translations';
$directory = $site_path . '/files/translations';
}
$container->set('string_translator.file_translation', new FileTranslation($directory));
$container->get('string_translation')
......@@ -419,6 +410,8 @@ function install_begin_request(&$install_state) {
// checked by other subsystems (e.g., the theme system).
$module_handler->loadAll();
$kernel->prepareLegacyRequest($request);
// Prepare for themed output. We need to run this at the beginning of the
// page request to avoid a different theme accidentally getting set. (We also
// need to run it even in the case of command-line installations, to prevent
......@@ -1458,7 +1451,6 @@ function install_load_profile(&$install_state) {
* An array of information about the current installation state.
*/
function install_bootstrap_full() {
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
\Drupal::service('session_manager')->initialize();
}
......
......@@ -9,8 +9,6 @@
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\OpCodeCache;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Database\Database;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Site\Settings;
......@@ -634,24 +632,33 @@ function drupal_verify_profile($install_state) {
* to set the default language.
*/
function drupal_install_system($install_state) {
// Boot a new kernel into a regular production environment.
$request = \Drupal::hasRequest() ? \Drupal::request() : FALSE;
// Remove the service provider of the early installer.
unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']);
$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE);
$kernel->boot();
if ($request) {
$kernel->getContainer()->enterScope('request');
$kernel->getContainer()->set('request', $request, 'request');
$kernel->getContainer()->get('request_stack')->push($request);
}
$request = \Drupal::request();
// Reboot into a full production environment to continue the installation.
/** @var \Drupal\Core\Installer\InstallerKernel $kernel */
$kernel = \Drupal::service('kernel');
$kernel->shutdown();
// Have installer rebuild from the disk, rather then building from scratch.
$kernel->rebuildContainer(FALSE);
$kernel->prepareLegacyRequest($request);
// Install base system configuration.
\Drupal::service('config.installer')->installDefaultConfig('core', 'core');
// Install System module.
\Drupal::moduleHandler()->install(array('system'), FALSE);
$kernel->getContainer()->get('module_handler')->install(array('system'), FALSE);
// DrupalKernel::prepareLegacyRequest() above calls into
// DrupalKernel::bootCode(), which primes file_get_stream_wrappers()'s static
// list of custom stream wrappers that are based on the currently enabled
// list of modules (none).
// @todo Custom stream wrappers of a new module have to be registered as soon
// as the module is installed/enabled. Fix either ModuleHandler::install()
// and/or DrupalKernel::updateModules().
// @see https://drupal.org/node/2028109
drupal_static_reset('file_get_stream_wrappers');
// Ensure default language is saved.
if (isset($install_state['parameters']['langcode'])) {
......
......@@ -59,9 +59,7 @@ function drupal_match_path($path, $patterns) {
* - http://example.com/path/alias (which is a path alias for node/306) returns
* "node/306" as opposed to the path alias.
*
* This function is available only after DRUPAL_BOOTSTRAP_FULL.
*
* @return
* @return string
* The current Drupal URL path.
*
* @see request_path()
......
......@@ -86,7 +86,7 @@ function drupal_get_complete_schema($rebuild = FALSE) {
// If the schema is empty, avoid saving it: some database engines require
// the schema to perform queries, and this could lead to infinite loops.
if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
if (!empty($schema)) {
\Drupal::cache()->set('schema', $schema, Cache::PERMANENT);
}
}
......
......@@ -18,11 +18,13 @@
use Drupal\Core\Page\FeedLinkElement;
use Drupal\Core\Page\LinkElement;
use Drupal\Core\Page\MetaElement;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Template\RenderWrapper;
use Drupal\Core\Theme\ThemeSettings;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Render\Element;
use Symfony\Component\HttpFoundation\Request;
/**
* @defgroup content_flags Content markers
......@@ -90,8 +92,12 @@ function drupal_theme_access($theme) {
/**
* Initializes the theme system by loading the theme.
*
* @param RouteMatch $route_match
* The route match to use for theme initialization.
// @todo Force calling methods to provide as RouteMatch.
*/
function drupal_theme_initialize() {
function drupal_theme_initialize(RouteMatch $route_match = NULL) {
global $theme, $theme_key;
// If $theme is already set, assume the others are set, too, and do nothing
......@@ -101,10 +107,12 @@ function drupal_theme_initialize() {
$themes = list_themes();
// @todo Let the theme.negotiator listen to the kernel request event.
// Determine the active theme for the theme negotiator service. This includes
// the default theme as well as really specific ones like the ajax base theme.
$theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch());
if (!$route_match) {
$route_match = \Drupal::routeMatch();
}
$theme = \Drupal::service('theme.negotiator')->determineActiveTheme($route_match);
// If no theme could be negotiated, or if the negotiated theme is not within
// the list of enabled themes, fall back to the default theme output of core
......
......@@ -8,6 +8,9 @@
use Drupal\Component\Utility\Variable;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Cache\Cache;
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
use Composer\Autoload\ClassLoader;
/**
* Drupal-friendly var_export().
......@@ -32,34 +35,39 @@ function drupal_var_export($var, $prefix = '') {
*
* Requires DRUPAL_BOOTSTRAP_CONFIGURATION.
*
* @param \Composer\Autoload\ClassLoader $classloader
* The classloader.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @see rebuild.php
*/
function drupal_rebuild() {
function drupal_rebuild(ClassLoader $classloader, Request $request) {
// Remove Drupal's error and exception handlers; they rely on a working
// service container and other subsystems and will only cause a fatal error
// that hides the actual error.
restore_error_handler();
restore_exception_handler();
// drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL) will build a new kernel. This
// comes before DRUPAL_BOOTSTRAP_PAGE_CACHE.
// Force kernel to rebuild container.
PhpStorageFactory::get('service_container')->deleteAll();
PhpStorageFactory::get('twig')->deleteAll();
// Disable the page cache.
drupal_page_is_cacheable(FALSE);
// Bootstrap up to where caches exist and clear them.
drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE);
$kernel = new DrupalKernel('prod', $classloader);
$kernel->prepareLegacyRequest($request);
foreach (Cache::getBins() as $bin) {
$bin->deleteAll();
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Disable the page cache.
drupal_page_is_cacheable(FALSE);
drupal_flush_all_caches();
// Restore Drupal's error and exception handlers.
// @see _drupal_bootstrap_configuration()
// @see \Drupal\Core\DrupalKernel::boot()
set_error_handler('_drupal_error_handler');
set_exception_handler('_drupal_exception_handler');
}
This diff is collapsed.
......@@ -8,6 +8,7 @@
namespace Drupal\Core;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* The interface for DrupalKernel, the core of Drupal.
......@@ -19,6 +20,8 @@ interface DrupalKernelInterface extends HttpKernelInterface {
/**
* Boots the current kernel.
*
* @return $this
*/
public function boot();
......@@ -53,6 +56,22 @@ public function getServiceProviders($origin);
*/
public function getContainer();
/**
* Set the current site path.
*
* @param $path
* The current site path.
*/
public function setSitePath($path);
/**
* Get the site path.
*
* @return string
* The current site path.
*/
public function getSitePath();
/**
* Updates the kernel's list of modules to the new list.
*
......@@ -65,4 +84,28 @@ public function getContainer();
* List of module filenames, keyed by module name.
*/
public function updateModules(array $module_list, array $module_filenames = array());
/**
* Attempts to serve a page from the cache.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return $this
*/
public function handlePageCache(Request $request);
/**
* Prepare the kernel for handling a request without handling the request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return $this
*
* @deprecated 8.x
* Only used by legacy front-controller scripts.
*/
public function prepareLegacyRequest(Request $request);
}
......@@ -2,39 +2,21 @@
/**
* @file
* Definition of Drupal\Core\EventSubscriber\LegacyRequestSubscriber.
* Contains \Drupal\Core\EventSubscriber\ThemeNegotiatorRequestSubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Routing\RouteMatch;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* KernelEvents::REQUEST event subscriber to initialize theme and modules.
*
* @todo Remove this subscriber when all of the code in it has been refactored.
*/
class LegacyRequestSubscriber implements EventSubscriberInterface {
/**
* Initializes the rest of the legacy Drupal subsystems.
*
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The Event to process.
* Initializes the theme for the current request.
*/
public function onKernelRequestLegacy(GetResponseEvent $event) {
if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
// Tell Drupal it is now fully bootstrapped (for the benefit of code that
// calls drupal_get_bootstrap_phase()), but without having
// _drupal_bootstrap_full() do anything, since we've already done the
// equivalent above and in earlier listeners.
_drupal_bootstrap_full(TRUE);
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
}
}
class ThemeNegotiatorRequestSubscriber implements EventSubscriberInterface {
/**
* Initializes the theme system after the routing system.
......@@ -42,9 +24,13 @@ public function onKernelRequestLegacy(GetResponseEvent $event) {
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The Event to process.
*/
public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) {
public function onKernelRequestThemeNegotiator(GetResponseEvent $event) {
if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
drupal_theme_initialize();
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
// @todo Refactor drupal_theme_initialize() into a request subscriber.
// @see https://drupal.org/node/2228093
drupal_theme_initialize(RouteMatch::createFromRequest($event->getRequest()));
}
}
}
......@@ -54,11 +40,9 @@ public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) {
* @return array
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequestLegacy', 90);
// Initialize the theme system after the routing system.
$events[KernelEvents::REQUEST][] = array('onKernelRequestLegacyAfterRouting', 30);
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequestThemeNegotiator', 29);
return $events;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Installer\InstallerKernel.
*/
namespace Drupal\Core\Installer;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* Extend DrupalKernel to handle force some kernel behaviors.
*/
class InstallerKernel extends DrupalKernel {
/**
* {@inheritdoc}
*/
protected function initializeContainer($rebuild = TRUE) {
$container = parent::initializeContainer($rebuild);
return $container;
}
/**
* {@inheritdoc}
*/
protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) {
if (Settings::get('hash_salt')) {
return parent::dumpDrupalContainer($container, $baseClass);
}
}
}
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Site;
use Drupal\Core\Database\Database;
/**
* Read only settings that are initialized with the class.
*
......@@ -80,6 +82,31 @@ public static function getAll() {
return self::$instance->storage;
}
/**
* Bootstraps settings.php and the Settings singleton.
*
* @param string $site_path
* The current site path.
*/
public static function initialize($site_path) {
// Export these settings.php variables to the global namespace.
global $base_url, $cookie_domain, $config_directories, $config;
$settings = array();
$config = array();
$databases = array();
// Make conf_path() available as local variable in settings.php.
if (is_readable(DRUPAL_ROOT . '/' . $site_path . '/settings.php')) {
require DRUPAL_ROOT . '/' . $site_path . '/settings.php';
}
// Initialize Database.
Database::setMultipleConnectionInfo($databases);
// Initialize Settings.
new Settings($settings);
}
/**
* Gets a salt useful for hardening against SQL injection.
*
......
......@@ -8,46 +8,28 @@
namespace Drupal\Core\Test;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Installer\InstallerServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Composer\Autoload\ClassLoader;
/**
* Kernel for run-tests.sh.
* Kernel to mock requests to test simpletest.
*/
class TestKernel extends DrupalKernel {
/**
* Constructs a TestKernel.
*
* @param \Composer\Autoload\ClassLoader $class_loader
* The classloader.
* {@inheritdoc}
*/
public function __construct(ClassLoader $class_loader) {
parent::__construct('test_runner', $class_loader, FALSE);
// Prime the module list and corresponding Extension objects.
// @todo Remove System module. Needed because \Drupal\Core\Datetime\Date
// has a (needless) dependency on the 'date_format' entity, so calls to
// format_date()/format_interval() cause a plugin not found exception.
$this->moduleList = array(
'system' => 0,
'simpletest' => 0,
);
$this->moduleData = array(
'system' => new Extension('module', 'core/modules/system/system.info.yml', 'system.module'),
'simpletest' => new Extension('module', 'core/modules/simpletest/simpletest.info.yml', 'simpletest.module'),
);
public static function createFromRequest(Request $request, ClassLoader $class_loader, $environment, $allow_dumping = TRUE) {
// Include our bootstrap file.
require_once __DIR__ . '/../../../../includes/bootstrap.inc';
// Exit if we should be in a test environment but aren't.
if (!drupal_valid_test_ua()) {
header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden');
exit;
}
/**
* {@inheritdoc}
*/
public function discoverServiceProviders() {
parent::discoverServiceProviders();
// The test runner does not require an installed Drupal site to exist.
// Therefore, its environment is identical to that of the early installer.
$this->serviceProviderClasses['app']['Test'] = 'Drupal\Core\Installer\InstallerServiceProvider';
return parent::createFromRequest($request, $class_loader, $environment, $allow_dumping);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Test\TestRunnerKernel.
*/
namespace Drupal\Core\Test;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Installer\InstallerServiceProvider;
use Composer\Autoload\ClassLoader;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
/**