diff --git a/.htaccess b/.htaccess index 00830a527af..3e742b6a1eb 100644 --- a/.htaccess +++ b/.htaccess @@ -25,9 +25,8 @@ 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\Core\DrupalKernel::bootEnvironment() for settings that can be -# changed at runtime. +# sites/default/default.settings.php and drupal_environment_initialize() in +# core/includes/bootstrap.inc for settings that can be changed at runtime. # PHP 5, Apache 1 and 2. @@ -120,7 +119,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. + # index.php. Clean URLs are handled in drupal_environment_initialize(). RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !=/favicon.ico diff --git a/core/authorize.php b/core/authorize.php index 96fe9aeb3b6..f938f390332 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -20,15 +20,13 @@ * @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('..'); -$autoloader = require_once __DIR__ . '/vendor/autoload.php'; +require_once __DIR__ . '/vendor/autoload.php'; /** * Global flag to identify update.php and authorize.php runs. @@ -53,9 +51,18 @@ function authorize_access_allowed() { return Settings::get('allow_authorize_operations', TRUE) && user_access('administer software updates'); } -$request = Request::createFromGlobals(); -$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); -$kernel->prepareLegacyRequest($request); +// *** 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); // We have to enable the user and system modules, even to check access and // display errors via the maintenance theme. diff --git a/core/core.services.yml b/core/core.services.yml index 479b76c3e9a..b6decf34edd 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -201,10 +201,6 @@ services: arguments: ['@access_check.theme', '@request_stack'] 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'] @@ -598,6 +594,10 @@ 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: diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 3e4fca8f06d..3d41fcb419d 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -6,16 +6,28 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Environment; +use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\String; +use Drupal\Component\Utility\Timer; use Drupal\Component\Utility\Unicode; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\DrupalKernel; +use Drupal\Core\Database\Database; +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Site\Settings; +use Drupal\Core\Utility\Title; use Drupal\Core\Utility\Error; use Symfony\Component\ClassLoader\ApcClassLoader; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Exception\RuntimeException as DependencyInjectionRuntimeException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Language\Language; +use Drupal\Core\Lock\DatabaseLockBackend; +use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Session\AnonymousUserSession; /** @@ -114,36 +126,26 @@ const WATCHDOG_DEBUG = 7; /** * First bootstrap phase: initialize configuration. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ const DRUPAL_BOOTSTRAP_CONFIGURATION = 0; /** - * Second bootstrap phase, initialize a kernel. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Second bootstrap phase, initalize a kernel. */ const DRUPAL_BOOTSTRAP_KERNEL = 1; /** * Third bootstrap phase: try to serve a cached page. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ const DRUPAL_BOOTSTRAP_PAGE_CACHE = 2; /** * Fourth bootstrap phase: load code for subsystems and modules. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ const DRUPAL_BOOTSTRAP_CODE = 3; /** * Final bootstrap phase: initialize language, path, theme, and modules. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ const DRUPAL_BOOTSTRAP_FULL = 4; @@ -205,6 +207,11 @@ define('DRUPAL_ROOT', dirname(dirname(__DIR__))); /** * Returns the appropriate configuration directory. * + * Returns the configuration path based on the site's hostname, port, and + * pathname. Uses find_conf_path() to find the current configuration directory. + * See default.settings.php for examples on how the URL is converted to a + * directory. + * * @param bool $require_settings * Only configuration directories with an existing settings.php file * will be recognized. Defaults to TRUE. During initial installation, @@ -213,36 +220,100 @@ define('DRUPAL_ROOT', dirname(dirname(__DIR__))); * @param bool $reset * Force a full search for matching directories even if one had been * found previously. Defaults to FALSE. - * @param \Symfony\Component\HttpFoundation\Request $request - * (optional) The current request. Defaults to \Drupal::request() or a new - * request created from globals. * - * @return string - * The path of the matching directory.@see default.settings.php + * @return + * The path of the matching directory. * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal\Core\DrupalKernel::getSitePath() instead. If the kernel is - * unavailable or the site path needs to be recalculated then - * Drupal\Core\DrupalKernel::findSitePath() can be used. + * @see default.settings.php */ -function conf_path($require_settings = TRUE, $reset = FALSE, Request $request = NULL) { - if (!isset($request)) { - if (\Drupal::hasRequest()) { - $request = \Drupal::request(); - } - // @todo Remove once external CLI scripts (Drush) are updated. - else { - $request = Request::createFromGlobals(); - } +function conf_path($require_settings = TRUE, $reset = FALSE) { + static $conf_path; + + if (isset($conf_path) && !$reset) { + return $conf_path; } - if (\Drupal::hasService('kernel')) { - $site_path = \Drupal::service('kernel')->getSitePath(); + + // Check for a simpletest override. + if ($test_prefix = drupal_valid_test_ua()) { + $conf_path = 'sites/simpletest/' . substr($test_prefix, 10); + return $conf_path; } - if (!isset($site_path) || empty($site_path)) { - $site_path = DrupalKernel::findSitePath($request, $require_settings); + + // Otherwise, use the normal $conf_path. + $script_name = $_SERVER['SCRIPT_NAME']; + if (!$script_name) { + $script_name = $_SERVER['SCRIPT_FILENAME']; } - return $site_path; + $http_host = $_SERVER['HTTP_HOST']; + $conf_path = find_conf_path($http_host, $script_name, $require_settings); + return $conf_path; } + +/** + * Finds the appropriate configuration directory for a given host and path. + * + * Finds a matching configuration directory file by stripping the website's + * hostname from left to right and pathname from right to left. By default, + * the directory must contain a 'settings.php' file for it to match. If the + * parameter $require_settings is set to FALSE, then a directory without a + * 'settings.php' file will match as well. The first configuration + * file found will be used and the remaining ones will be ignored. If no + * configuration file is found, returns a default value '$confdir/default'. See + * default.settings.php for examples on how the URL is converted to a directory. + * + * If a file named sites.php is present in the $confdir, it will be loaded + * prior to scanning for directories. That file can define aliases in an + * associative array named $sites. The array is written in the format + * '..' => 'directory'. As an example, to create a + * directory alias for http://www.drupal.org:8080/mysite/test whose configuration + * file is in sites/example.com, the array should be defined as: + * @code + * $sites = array( + * '8080.www.drupal.org.mysite.test' => 'example.com', + * ); + * @endcode + * + * @param $http_host + * The hostname and optional port number, e.g. "www.example.com" or + * "www.example.com:8080". + * @param $script_name + * The part of the URL following the hostname, including the leading slash. + * @param $require_settings + * Defaults to TRUE. If TRUE, then only match directories with a + * 'settings.php' file. Otherwise match any directory. + * + * @return + * The path of the matching configuration directory. + * + * @see default.settings.php + * @see example.sites.php + * @see conf_path() + */ +function find_conf_path($http_host, $script_name, $require_settings = TRUE) { + // Determine whether multi-site functionality is enabled. + if (!file_exists(DRUPAL_ROOT . '/sites/sites.php')) { + return 'sites/default'; + } + + $sites = array(); + include DRUPAL_ROOT . '/sites/sites.php'; + + $uri = explode('/', $script_name); + $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.'))))); + for ($i = count($uri) - 1; $i > 0; $i--) { + for ($j = count($server); $j > 0; $j--) { + $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); + if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/sites/' . $sites[$dir])) { + $dir = $sites[$dir]; + } + if (file_exists(DRUPAL_ROOT . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/sites/' . $dir))) { + return "sites/$dir"; + } + } + } + return 'sites/default'; +} + /** * Returns the path of a configuration directory. * @@ -262,6 +333,207 @@ function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) { throw new \Exception(format_string('The configuration directory type %type does not exist.', array('%type' => $type))); } +/** + * Initializes the PHP environment. + */ +function drupal_environment_initialize() { + if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) { + $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0'; + } + + if (isset($_SERVER['HTTP_HOST'])) { + // As HTTP_HOST is user input, ensure it only contains characters allowed + // in hostnames. See RFC 952 (and RFC 2181). + // $_SERVER['HTTP_HOST'] is lowercased here per specifications. + $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']); + if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) { + // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack. + header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); + exit; + } + } + else { + // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is + // defined for E_ALL compliance. + $_SERVER['HTTP_HOST'] = ''; + } + + // @todo Refactor with the Symfony Request object. + _current_path(request_path()); + + // Enforce E_STRICT, but allow users to set levels not part of E_STRICT. + error_reporting(E_STRICT | E_ALL | error_reporting()); + + // 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'); +} + +/** + * Validates that a hostname (for example $_SERVER['HTTP_HOST']) is safe. + * + * @return + * TRUE if only containing valid characters, or FALSE otherwise. + */ +function drupal_valid_http_host($host) { + return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host); +} + +/** + * Sets the base URL, cookie domain, and session name from configuration. + */ +function drupal_settings_initialize() { + // Export these settings.php variables to the global namespace. + global $base_url, $cookie_domain, $config_directories, $config; + $databases = array(); + $settings = array(); + $config = array(); + + // Make conf_path() available as local variable in settings.php. + $conf_path = conf_path(); + if (is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) { + require DRUPAL_ROOT . '/' . $conf_path . '/settings.php'; + } + // Initialize Database. + Database::setMultipleConnectionInfo($databases); + // Initialize Settings. + new Settings($settings); +} + +/** + * Initializes global request variables. + * + * @todo D8: Eliminate this entirely in favor of Request object. + */ +function _drupal_request_initialize() { + // Provided by settings.php. + // @see drupal_settings_initialize() + global $base_url, $cookie_domain; + // Set and derived from $base_url by this function. + global $base_path, $base_root, $script_path; + global $base_secure_url, $base_insecure_url; + + $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; + + if (isset($base_url)) { + // Parse fixed base URL from settings.php. + $parts = parse_url($base_url); + if (!isset($parts['path'])) { + $parts['path'] = ''; + } + $base_path = $parts['path'] . '/'; + // Build $base_root (everything until first slash after "scheme://"). + $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path'])); + } + else { + // Create base URL + $http_protocol = $is_https ? 'https' : 'http'; + $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST']; + + $base_url = $base_root; + + // For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is + // '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'. + if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) { + // Remove "core" directory if present, allowing install.php, update.php, + // and others to auto-detect a base path. + $core_position = strrpos($dir, '/core'); + if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) { + $base_path = substr($dir, 0, $core_position); + } + else { + $base_path = $dir; + } + $base_url .= $base_path; + $base_path .= '/'; + } + else { + $base_path = '/'; + } + } + $base_secure_url = str_replace('http://', 'https://', $base_url); + $base_insecure_url = str_replace('https://', 'http://', $base_url); + + // Determine the path of the script relative to the base path, and add a + // trailing slash. This is needed for creating URLs to Drupal pages. + if (!isset($script_path)) { + $script_path = ''; + // We don't expect scripts outside of the base path, but sanity check + // anyway. + if (strpos($_SERVER['SCRIPT_NAME'], $base_path) === 0) { + $script_path = substr($_SERVER['SCRIPT_NAME'], strlen($base_path)) . '/'; + // If the request URI does not contain the script name, then clean URLs + // are in effect and the script path can be similarly dropped from URL + // generation. For servers that don't provide $_SERVER['REQUEST_URI'], we + // do not know the actual URI requested by the client, and request_uri() + // returns a URI with the script name, resulting in non-clean URLs unless + // there's other code that intervenes. + if (strpos(request_uri(TRUE) . '/', $base_path . $script_path) !== 0) { + $script_path = ''; + } + // @todo Temporary BC for install.php, update.php, and other scripts. + // - http://drupal.org/node/1547184 + // - http://drupal.org/node/1546082 + if ($script_path !== 'index.php/') { + $script_path = ''; + } + } + } + + if ($cookie_domain) { + // If the user specifies the cookie domain, also use it for session name. + $session_name = $cookie_domain; + } + else { + // Otherwise use $base_url as session name, without the protocol + // to use the same session identifiers across HTTP and HTTPS. + list( , $session_name) = explode('://', $base_url, 2); + // HTTP_HOST can be modified by a visitor, but we already sanitized it + // in drupal_settings_initialize(). + if (!empty($_SERVER['HTTP_HOST'])) { + $cookie_domain = $_SERVER['HTTP_HOST']; + // Strip leading periods, www., and port numbers from cookie domain. + $cookie_domain = ltrim($cookie_domain, '.'); + if (strpos($cookie_domain, 'www.') === 0) { + $cookie_domain = substr($cookie_domain, 4); + } + $cookie_domain = explode(':', $cookie_domain); + $cookie_domain = '.' . $cookie_domain[0]; + } + } + // Per RFC 2109, cookie domains must contain at least one dot other than the + // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain. + if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) { + ini_set('session.cookie_domain', $cookie_domain); + } + // To prevent session cookies from being hijacked, a user can configure the + // SSL version of their website to only transfer session cookies via SSL by + // using PHP's session.cookie_secure setting. The browser will then use two + // separate session cookies for the HTTPS and HTTP versions of the site. So we + // must use different session identifiers for HTTPS and HTTP to prevent a + // cookie collision. + if ($is_https) { + ini_set('session.cookie_secure', TRUE); + } + $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS'; + session_name($prefix . substr(hash('sha256', $session_name), 0, 32)); +} + /** * Returns and optionally sets the filename for a system resource. * @@ -984,54 +1256,138 @@ function drupal_anonymous_user() { } /** - * Bootstraps DrupalKernel to a given boot level. + * Ensures Drupal is bootstrapped to the specified phase. + * + * In order to bootstrap Drupal from another PHP script, you can use this code: + * @code + * require_once '/path/to/drupal/core/vendor/autoload.php'; + * require_once '/path/to/drupal/core/includes/bootstrap.inc'; + * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + * @endcode * * @param $phase - * A constant telling which phase to bootstrap to. Possible values: + * A constant telling which phase to bootstrap to. When you bootstrap to a + * particular phase, all earlier phases are run automatically. Possible + * values: * - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration. * - DRUPAL_BOOTSTRAP_KERNEL: Initalizes a kernel. + * - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page. + * - DRUPAL_BOOTSTRAP_CODE: Loads code for subsystems and modules. + * - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input + * data. * - * @return int + * @return * The most recently completed phase. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Interact directly with the kernel. - * - * @todo Obsolete. Remove once Drush has been updated. */ function drupal_bootstrap($phase = NULL) { - // Temporary variables used for booting later legacy phases. - /** @var \Drupal\Core\DrupalKernel $kernel */ - static $kernel; - static $boot_level = 0; + // Not drupal_static(), because does not depend on any run-time information. + static $phases = array( + DRUPAL_BOOTSTRAP_CONFIGURATION, + DRUPAL_BOOTSTRAP_KERNEL, + DRUPAL_BOOTSTRAP_PAGE_CACHE, + DRUPAL_BOOTSTRAP_CODE, + DRUPAL_BOOTSTRAP_FULL, + ); + // Not drupal_static(), because the only legitimate API to control this is to + // call drupal_bootstrap() with a new phase parameter. + static $final_phase = -1; + // Not drupal_static(), because it's impossible to roll back to an earlier + // bootstrap state. + static $stored_phase = -1; + // Store the phase name so it's not forgotten during recursion. Additionally, + // ensure that $final_phase is never rolled back to an earlier bootstrap + // state. + if ($phase > $final_phase) { + $final_phase = $phase; + } if (isset($phase)) { - $request = Request::createFromGlobals(); - for ($current_phase = $boot_level; $current_phase <= $phase; $current_phase++) { + // Call a phase if it has not been called before and is below the requested + // phase. + while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) { + $current_phase = array_shift($phases); + + // This function is re-entrant. Only update the completed phase when the + // current call actually resulted in a progress in the bootstrap process. + if ($current_phase > $stored_phase) { + $stored_phase = $current_phase; + } switch ($current_phase) { case DRUPAL_BOOTSTRAP_CONFIGURATION: - $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod'); + _drupal_bootstrap_configuration(); break; case DRUPAL_BOOTSTRAP_KERNEL: - $kernel->boot(); + _drupal_bootstrap_kernel(); break; case DRUPAL_BOOTSTRAP_PAGE_CACHE: - $kernel->handlePageCache($request); + _drupal_bootstrap_page_cache(); break; case DRUPAL_BOOTSTRAP_CODE: + require_once __DIR__ . '/common.inc'; + _drupal_bootstrap_code(); + break; + case DRUPAL_BOOTSTRAP_FULL: - $kernel->prepareLegacyRequest($request); + _drupal_bootstrap_full(); break; } } - $boot_level = $phase; + } + return $stored_phase; +} + +/** + * Handles an entire PHP request. + * + * This function may be called by PHP scripts (e.g., Drupal's index.php) that + * want Drupal to take over the entire PHP processing of the request. The only + * expectation is that PHP's superglobals are initialized as desired (PHP does + * this automatically, but some scripts might want to alter them) and that the + * DRUPAL_ROOT constant is defined and set to the absolute server directory of + * Drupal's codebase. + * + * Scripts and applications that want to invoke multiple Drupal requests within + * a single PHP request, or Drupal request handling within some larger workflow, + * should not call this function, but instead instantiate and use + * \Drupal\Core\DrupalKernel as needed. + * + * @param boolean $test_only + * Whether to restrict handling to only requests invoked by SimpleTest. + * + * @see index.php + */ +function drupal_handle_request($test_only = FALSE) { + // Initialize the environment, load settings.php, and activate a PSR-0 class + // autoloader with required namespaces registered. + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + + // Exit if we should be in a test environment but aren't. + if ($test_only && !drupal_valid_test_ua()) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + exit; } - return \Drupal::getContainer() ? DRUPAL_BOOTSTRAP_CODE : DRUPAL_BOOTSTRAP_CONFIGURATION; + $kernel = new DrupalKernel('prod', drupal_classloader(), !$test_only); + + // @todo Remove this once everything in the bootstrap has been + // converted to services in the DIC. + $kernel->boot(); + + // Create a request object from the HttpFoundation. + $request = Request::createFromGlobals(); + $container = \Drupal::getContainer(); + $container->set('request', $request); + $container->get('request_stack')->push($request); + + drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); + + $response = $kernel->handle($request)->prepare($request)->send(); + + $kernel->terminate($request, $response); } /** @@ -1113,14 +1469,111 @@ function _drupal_exception_handler($exception) { } } +/** + * Sets up the script environment and loads settings.php. + */ +function _drupal_bootstrap_configuration() { + drupal_environment_initialize(); + + // 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); + + // 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); + } + + // Initialize the configuration, including variables from settings.php. + drupal_settings_initialize(); + _drupal_request_initialize(); + + // Activate the class loader. + drupal_classloader(); + + // Start a page timer: + Timer::start('page'); + + // Detect string handling method. + Unicode::check(); + + // Set the Drupal custom error handler. (requires \Drupal::config()) + set_error_handler('_drupal_error_handler'); + set_exception_handler('_drupal_exception_handler'); + + // Redirect the user to the installation script if Drupal has not been + // installed yet (i.e., if no $databases array has been defined in the + // settings.php file) and we are not already installing. + if (!Database::getConnectionInfo() && !drupal_installation_attempted() && !drupal_is_cli()) { + include_once __DIR__ . '/install.inc'; + install_goto('core/install.php'); + } +} + +/** + * Initialize the kernel / service container. + */ +function _drupal_bootstrap_kernel() { + // Normally, index.php puts a container in the Drupal class by creating a + // kernel. If there is no container yet, create one. + if (!\Drupal::getContainer()) { + $kernel = new DrupalKernel('prod', drupal_classloader()); + $kernel->boot(); + $request = Request::createFromGlobals(); + $container = \Drupal::getContainer(); + $container->set('request', $request); + $container->get('request_stack')->push($request); + } +} + +/** + * Attempts to serve a page from the cache. + */ +function _drupal_bootstrap_page_cache() { + require_once __DIR__ . '/database.inc'; + // Check for a cache mode force from settings.php. + if (Settings::get('page_cache_without_database')) { + $cache_enabled = TRUE; + } + else { + $config = \Drupal::config('system.performance'); + $cache_enabled = $config->get('cache.page.use_internal'); + } + + $request = \Drupal::request(); + // If there is no session cookie and cache is enabled (or forced), try + // to serve a cached page. + if (!$request->cookies->has(session_name()) && $cache_enabled && drupal_page_is_cacheable()) { + // Get the page from the cache. + $response = drupal_page_get_cache($request); + // If there is a cached page, display it. + if ($response) { + $response->headers->set('X-Drupal-Cache', 'HIT'); + + drupal_serve_page_from_cache($response, $request); + + // We are done. + $response->prepare($request); + $response->send(); + exit; + } + } +} + /** * Returns the current bootstrap phase for this Drupal process. * * The current phase is the one most recently completed by drupal_bootstrap(). * * @see drupal_bootstrap() - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ function drupal_get_bootstrap_phase() { return drupal_bootstrap(); diff --git a/core/includes/common.inc b/core/includes/common.inc index 62c8ed7131c..5572e547228 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2603,6 +2603,70 @@ 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. * diff --git a/core/includes/errors.inc b/core/includes/errors.inc index ebe1cd5cbda..8b0b9e43ec0 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -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_CODE) { + if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) { // 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 prior to - * 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 pre + * 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 diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index ed2fc975bb8..b00d23b21d3 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -272,6 +272,12 @@ 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 @@ -282,8 +288,7 @@ function install_begin_request(&$install_state) { exit; } - $site_path = DrupalKernel::findSitePath($request, FALSE); - Settings::initialize($site_path); + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); // Ensure that procedural dependencies are loaded as early as possible, // since the error/exception handlers depend on them. @@ -351,24 +356,27 @@ function install_begin_request(&$install_state) { $environment = 'prod'; } - $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), $environment, FALSE); - $kernel->setSitePath($site_path); + $kernel = new DrupalKernel($environment, drupal_classloader(), FALSE); $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 = $site_path . '/files/translations'; + $directory = conf_path() . '/files/translations'; } $container->set('string_translator.file_translation', new FileTranslation($directory)); $container->get('string_translation') ->addTranslator($container->get('string_translator.file_translation')); - $kernel->prepareLegacyRequest($request); - // Set the default language to the selected language, if any. if (isset($install_state['parameters']['langcode'])) { $default_language = new Language(array('id' => $install_state['parameters']['langcode'])); @@ -1447,6 +1455,7 @@ 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(); } diff --git a/core/includes/install.inc b/core/includes/install.inc index ce7db8771e0..67e41f66225 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -9,6 +9,8 @@ use Symfony\Component\HttpFoundation\RedirectResponse; 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; @@ -632,29 +634,24 @@ function drupal_verify_profile($install_state) { * to set the default language. */ function drupal_install_system($install_state) { - // Remove the service provider of the early installer. - unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']); + // Boot a new kernel into a regular production environment. + $request = \Drupal::hasRequest() ? \Drupal::request() : FALSE; - // Reboot into a full production environment to continue the installation. - $kernel = \Drupal::service('kernel'); - $kernel->shutdown(); - $kernel->prepareLegacyRequest(\Drupal::request()); + 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); + } // Install base system configuration. \Drupal::service('config.installer')->installDefaultConfig('core', 'core'); // Install System module. - $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'); + \Drupal::moduleHandler()->install(array('system'), FALSE); // Ensure default language is saved. if (isset($install_state['parameters']['langcode'])) { diff --git a/core/includes/path.inc b/core/includes/path.inc index a10f0e73f39..60437495e68 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -74,7 +74,9 @@ 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. * - * @return string + * This function is available only after DRUPAL_BOOTSTRAP_FULL. + * + * @return * The current Drupal URL path. * * @see request_path() diff --git a/core/includes/schema.inc b/core/includes/schema.inc index f181c5467e3..a386e62d1c1 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -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)) { + if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) { \Drupal::cache()->set('schema', $schema, Cache::PERMANENT); } } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7e92f959865..2b06429a15e 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -24,7 +24,6 @@ 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 @@ -92,11 +91,8 @@ function drupal_theme_access($theme) { /** * Initializes the theme system by loading the theme. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request for which to initialize the theme. */ -function drupal_theme_initialize(Request $request = NULL) { +function drupal_theme_initialize() { global $theme, $theme_key; // If $theme is already set, assume the others are set, too, and do nothing @@ -109,9 +105,7 @@ function drupal_theme_initialize(Request $request = NULL) { // @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. - if (!$request) { - $request = \Drupal::request(); - } + $request = \Drupal::request(); $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request); // If no theme could be negotiated, or if the negotiated theme is not within diff --git a/core/includes/utility.inc b/core/includes/utility.inc index 135ab2473c1..66031666ada 100644 --- a/core/includes/utility.inc +++ b/core/includes/utility.inc @@ -8,9 +8,6 @@ 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(). @@ -35,39 +32,34 @@ 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(ClassLoader $classloader, Request $request) { +function drupal_rebuild() { // 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(); - // Force kernel to rebuild container. + // drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL) will build a new kernel. This + // comes before DRUPAL_BOOTSTRAP_PAGE_CACHE. PhpStorageFactory::get('service_container')->deleteAll(); PhpStorageFactory::get('twig')->deleteAll(); - // Bootstrap up to where caches exist and clear them. - $kernel = new DrupalKernel('prod', $classloader); - $kernel->prepareLegacyRequest($request); - - foreach (Cache::getBins() as $bin) { - $bin->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); + foreach (Cache::getBins() as $bin) { + $bin->deleteAll(); + } + + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_flush_all_caches(); // Restore Drupal's error and exception handlers. - // @see \Drupal\Core\DrupalKernel::boot() + // @see _drupal_bootstrap_configuration() set_error_handler('_drupal_error_handler'); set_exception_handler('_drupal_exception_handler'); } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 135ab43fa8c..fe43d8647a4 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -7,25 +7,21 @@ namespace Drupal\Core; -use Drupal\Component\Utility\Crypt; -use Drupal\Component\Utility\Timer; -use Drupal\Component\Utility\Unicode; -use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\Config\BootstrapConfigStorageFactory; use Drupal\Core\Config\NullStorage; -use Drupal\Core\Database\Database; +use Drupal\Core\CoreServiceProvider; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\YamlFileLoader; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Language\Language; -use Drupal\Core\PhpStorage\PhpStorageFactory; -use Drupal\Core\Site\Settings; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\TerminableInterface; use Composer\Autoload\ClassLoader; @@ -65,7 +61,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { * * @var bool */ - protected $booted = FALSE; + protected $booted; /** * Holds the list of enabled modules. @@ -167,79 +163,15 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { */ protected $serviceProviders; - /** - * Whether the request globals have been initialized. - * - * @var bool - */ - protected static $isRequestInitialized = FALSE; - - /** - * Whether the PHP environment has been initialized. - * - * 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 - */ - protected static $isEnvironmentInitialized = FALSE; - - /** - * The site directory. - * - * @var string - */ - protected $sitePath; - - /** - * Create a DrupalKernel object from a request. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Composer\Autoload\ClassLoader $class_loader - * (optional) The classloader is only used if $storage is not given or - * the load from storage fails and a container rebuild is required. In - * this case, the loaded modules will be registered with this loader in - * order to be able to find the module serviceProviders. - * @param string $environment - * String indicating the environment, e.g. 'prod' or 'dev'. - * @param bool $allow_dumping - * (optional) FALSE to stop the container from being written to or read - * from disk. Defaults to TRUE. - * @return static - */ - public static function createFromRequest(Request $request, ClassLoader $class_loader, $environment, $allow_dumping = TRUE) { - // Include our bootstrap file. - require_once dirname(dirname(dirname(__DIR__))) . '/includes/bootstrap.inc'; - - $kernel = new static($environment, $class_loader, $allow_dumping); - - // Ensure sane php environment variables.. - static::bootEnvironment(); - - // Get our most basic settings setup. - $site_path = static::findSitePath($request); - $kernel->setSitePath($site_path); - Settings::initialize($site_path); - - // Redirect the user to the installation script if Drupal has not been - // installed yet (i.e., if no $databases array has been defined in the - // settings.php file) and we are not already installing. - if (!Database::getConnectionInfo() && !drupal_installation_attempted() && !drupal_is_cli()) { - $response = new RedirectResponse($request->getBasePath() . '/core/install.php'); - $response->prepare($request)->send(); - } - - return $kernel; - } - /** * Constructs a DrupalKernel object. * * @param string $environment - * String indicating the environment, e.g. 'prod' or 'dev'. + * String indicating the environment, e.g. 'prod' or 'dev'. Used by + * Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use + * this value currently. Pass 'prod'. * @param \Composer\Autoload\ClassLoader $class_loader - * (optional) The class loader is only used if $storage is not given or + * (optional) The classloader is only used if $storage is not given or * the load from storage fails and a container rebuild is required. In * this case, the loaded modules will be registered with this loader in * order to be able to find the module serviceProviders. @@ -252,156 +184,24 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { */ public function __construct($environment, ClassLoader $class_loader, $allow_dumping = TRUE, $allow_loading = NULL) { $this->environment = $environment; + $this->booted = FALSE; $this->classLoader = $class_loader; $this->allowDumping = $allow_dumping; $this->allowLoading = isset($allow_loading) ? $allow_loading : $allow_dumping; } - /** - * Returns the appropriate site directory for a request. - * - * Once the kernel has been created DrupalKernelInterface::getSitePath() is - * preferred since it gets the statically cached result of this method. - * - * Site directories contain all site specific code. This includes settings.php - * for bootstrap level configuration, file configuration stores, public file - * storage and site specific modules and themes. - * - * Finds a matching site directory file by stripping the website's hostname - * from left to right and pathname from right to left. By default, the - * directory must contain a 'settings.php' file for it to match. If the - * parameter $require_settings is set to FALSE, then a directory without a - * 'settings.php' file will match as well. The first configuration file found - * will be used and the remaining ones will be ignored. If no configuration - * file is found, returns a default value 'sites/default'. See - * default.settings.php for examples on how the URL is converted to a - * directory. - * - * If a file named sites.php is present in the sites directory, it will be - * loaded prior to scanning for directories. That file can define aliases in - * an associative array named $sites. The array is written in the format - * '..' => 'directory'. As an example, to create a - * directory alias for http://www.drupal.org:8080/mysite/test whose - * configuration file is in sites/example.com, the array should be defined as: - * @code - * $sites = array( - * '8080.www.drupal.org.mysite.test' => 'example.com', - * ); - * @endcode - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. - * @param bool $require_settings - * Only directories with an existing settings.php file will be recognized. - * Defaults to TRUE. During initial installation, this is set to FALSE so - * that Drupal can detect a matching directory, then create a new - * settings.php file in it. - * - * @return string - * The path of the matching directory. - * - * @see \Drupal\Core\DrupalKernelInterface::getSitePath() - * @see \Drupal\Core\DrupalKernelInterface::setSitePath() - * @see default.settings.php - * @see example.sites.php - */ - public static function findSitePath(Request $request, $require_settings = TRUE) { - // Check for a simpletest override. - if ($test_prefix = drupal_valid_test_ua()) { - return 'sites/simpletest/' . substr($test_prefix, 10); - } - - // Determine whether multi-site functionality is enabled. - if (!file_exists(DRUPAL_ROOT . '/sites/sites.php')) { - return 'sites/default'; - } - - // Otherwise, use find the site path using the request. - $script_name = $request->server->get('SCRIPT_NAME'); - if (!$script_name) { - $script_name = $request->server->get('SCRIPT_FILENAME'); - } - $http_host = $request->server->get('HTTP_HOST'); - - $sites = array(); - include DRUPAL_ROOT . '/sites/sites.php'; - - $uri = explode('/', $script_name); - $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.'))))); - for ($i = count($uri) - 1; $i > 0; $i--) { - for ($j = count($server); $j > 0; $j--) { - $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); - if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/sites/' . $sites[$dir])) { - $dir = $sites[$dir]; - } - if (file_exists(DRUPAL_ROOT . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/sites/' . $dir))) { - return "sites/$dir"; - } - } - } - return 'sites/default'; - } - - /** - * {@inheritdoc} - */ - public function setSitePath($path) { - $this->sitePath = $path; - } - - /** - * {@inheritdoc} - */ - public function getSitePath() { - return $this->sitePath; - } - /** * {@inheritdoc} */ public function boot() { if ($this->booted) { - return $this; + return; } - - // Start a page timer: - Timer::start('page'); - - drupal_classloader(); - - // Load legacy and other functional code. - require_once DRUPAL_ROOT . '/core/includes/common.inc'; - require_once DRUPAL_ROOT . '/core/includes/database.inc'; - require_once DRUPAL_ROOT . '/' . Settings::get('path_inc', 'core/includes/path.inc'); - require_once DRUPAL_ROOT . '/core/includes/module.inc'; - require_once DRUPAL_ROOT . '/core/includes/theme.inc'; - require_once DRUPAL_ROOT . '/core/includes/pager.inc'; - require_once DRUPAL_ROOT . '/' . Settings::get('menu_inc', 'core/includes/menu.inc'); - require_once DRUPAL_ROOT . '/core/includes/tablesort.inc'; - require_once DRUPAL_ROOT . '/core/includes/file.inc'; - require_once DRUPAL_ROOT . '/core/includes/unicode.inc'; - require_once DRUPAL_ROOT . '/core/includes/form.inc'; - require_once DRUPAL_ROOT . '/core/includes/mail.inc'; - require_once DRUPAL_ROOT . '/core/includes/ajax.inc'; - require_once DRUPAL_ROOT . '/core/includes/errors.inc'; - require_once DRUPAL_ROOT . '/core/includes/schema.inc'; - require_once DRUPAL_ROOT . '/core/includes/entity.inc'; - - // Ensure that findSitePath is set. - if (!$this->sitePath) { - throw new \Exception('Kernel does not have site path set before calling boot()'); - } - // Intialize the container. $this->initializeContainer(); - - // 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]); - $this->booted = TRUE; - - return $this; + if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) { + watchdog('DrupalKernel', 'Container cannot be written to disk'); + } } /** @@ -413,103 +213,15 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { } $this->booted = FALSE; $this->container = NULL; - $this->moduleList = NULL; - $this->moduleData = array(); } /** * {@inheritdoc} */ public function getContainer() { - if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) { - watchdog('DrupalKernel', 'Container cannot be written to disk'); - } return $this->container; } - /** - * Helper method that does request related initialization. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. - */ - protected function preHandle(Request $request) { - // Load all enabled modules. - $this->container->get('module_handler')->loadAll(); - - // Initialize legacy request globals. - $this->initializeRequestGlobals($request); - - // Initialize cookie globals. - $this->initializeCookieGlobals($request); - - // Ensure container has a request scope so we can load file stream wrappers. - if (!$this->container->isScopeActive('request')) { - // Enter the request scope so that current_user service is available for - // locale/translation sake. - $this->container->enterScope('request'); - $this->container->set('request', $request, 'request'); - $this->container->get('request_stack')->push($request); - } - - // Make sure all stream wrappers are registered. - file_get_stream_wrappers(); - - // Back out scope required to initialize the file stream wrappers. - if ($this->container->isScopeActive('request')) { - $this->container->leaveScope('request'); - } - - // Set the allowed protocols once we have the config available. - $allowed_protocols = $this->container->get('config.factory')->get('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); - } - - /** - * {@inheritdoc} - * - * @todo Invoke proper request/response/terminate events. - */ - public function handlePageCache(Request $request) { - $this->boot(); - $this->initializeCookieGlobals($request); - - // Check for a cache mode force from settings.php. - if (Settings::get('page_cache_without_database')) { - $cache_enabled = TRUE; - } - else { - $config = $this->getContainer()->get('config.factory')->get('system.performance'); - $cache_enabled = $config->get('cache.page.use_internal'); - } - - // If there is no session cookie and cache is enabled (or forced), try to - // serve a cached page. - if (!$request->cookies->has(session_name()) && $cache_enabled && drupal_page_is_cacheable()) { - // Get the page from the cache. - $response = drupal_page_get_cache($request); - // If there is a cached page, display it. - if ($response) { - $response->headers->set('X-Drupal-Cache', 'HIT'); - - drupal_serve_page_from_cache($response, $request); - - // We are done. - $response->prepare($request); - $response->send(); - exit; - } - } - return $this; - } - /** * {@inheritdoc} */ @@ -534,14 +246,14 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { $this->classLoaderAddMultiplePsr4($this->getModuleNamespacesPsr4($module_filenames)); // Load each module's serviceProvider class. - foreach ($module_filenames as $module => $filename) { + foreach ($this->moduleList as $module => $weight) { $camelized = ContainerBuilder::camelize($module); $name = "{$camelized}ServiceProvider"; $class = "Drupal\\{$module}\\{$name}"; if (class_exists($class)) { $this->serviceProviderClasses['app'][$module] = $class; } - $filename = dirname($filename) . "/$module.services.yml"; + $filename = dirname($module_filenames[$module]) . "/$module.services.yml"; if (file_exists($filename)) { $this->serviceYamls['app'][$module] = $filename; } @@ -558,7 +270,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { if (!empty($GLOBALS['conf']['container_yamls'])) { $this->serviceYamls['site'] = $GLOBALS['conf']['container_yamls']; } - if (file_exists($site_services_yml = $this->getSitePath() . '/services.yml')) { + if (file_exists($site_services_yml = conf_path() . '/services.yml')) { $this->serviceYamls['site'][] = $site_services_yml; } } @@ -586,25 +298,12 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { /** * {@inheritdoc} */ - public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { - $this->boot(); - $this->preHandle($request); - return $this->getHttpKernel()->handle($request, $type, $catch); - } + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = TRUE) { + if (FALSE === $this->booted) { + $this->boot(); + } - /** - * {@inheritdoc} - */ - public function prepareLegacyRequest(Request $request) { - $this->boot(); - $this->preHandle($request); - // Enter the request scope so that current_user service is available for - // locale/translation sake. - $this->container->enterScope('request'); - $this->container->set('request', $request); - $this->container->get('request_stack')->push($request); - $this->container->get('router.request_context')->fromRequest($request); - return $this; + return $this->getHttpKernel()->handle($request, $type, $catch); } /** @@ -770,215 +469,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { \Drupal::setContainer($this->container); } - /** - * 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; - } - - // 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); - - // 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; - } - - /** - * Bootstraps the legacy global request variables. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. - * - * @todo D8: Eliminate this entirely in favor of Request object. - */ - protected function initializeRequestGlobals(Request $request) { - // Provided by settings.php. - global $base_url; - // Set and derived from $base_url by this function. - global $base_path, $base_root, $script_path; - global $base_secure_url, $base_insecure_url; - - // @todo Refactor with the Symfony Request object. - _current_path(request_path()); - - if (isset($base_url)) { - // Parse fixed base URL from settings.php. - $parts = parse_url($base_url); - if (!isset($parts['path'])) { - $parts['path'] = ''; - } - $base_path = $parts['path'] . '/'; - // Build $base_root (everything until first slash after "scheme://"). - $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path'])); - } - else { - // Create base URL. - $http_protocol = $request->isSecure() ? 'https' : 'http'; - $base_root = $http_protocol . '://' . $request->server->get('HTTP_HOST'); - - $base_url = $base_root; - - // For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is - // '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'. - if ($dir = rtrim(dirname($request->server->get('SCRIPT_NAME')), '\/')) { - // Remove "core" directory if present, allowing install.php, update.php, - // and others to auto-detect a base path. - $core_position = strrpos($dir, '/core'); - if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) { - $base_path = substr($dir, 0, $core_position); - } - else { - $base_path = $dir; - } - $base_url .= $base_path; - $base_path .= '/'; - } - else { - $base_path = '/'; - } - } - $base_secure_url = str_replace('http://', 'https://', $base_url); - $base_insecure_url = str_replace('https://', 'http://', $base_url); - - // Determine the path of the script relative to the base path, and add a - // trailing slash. This is needed for creating URLs to Drupal pages. - if (!isset($script_path)) { - $script_path = ''; - // We don't expect scripts outside of the base path, but sanity check - // anyway. - if (strpos($request->server->get('SCRIPT_NAME'), $base_path) === 0) { - $script_path = substr($request->server->get('SCRIPT_NAME'), strlen($base_path)) . '/'; - // If the request URI does not contain the script name, then clean URLs - // are in effect and the script path can be similarly dropped from URL - // generation. For servers that don't provide $_SERVER['REQUEST_URI'], - // we do not know the actual URI requested by the client, and - // request_uri() returns a URI with the script name, resulting in - // non-clean URLs unless - // there's other code that intervenes. - if (strpos(request_uri(TRUE) . '/', $base_path . $script_path) !== 0) { - $script_path = ''; - } - // @todo Temporary BC for install.php, update.php, and other scripts. - // - http://drupal.org/node/1547184 - // - http://drupal.org/node/1546082 - if ($script_path !== 'index.php/') { - $script_path = ''; - } - } - } - - } - - /** - * Initialize cookie settings. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. - * - * @todo D8: Eliminate this entirely in favor of a session object. - */ - protected function initializeCookieGlobals(Request $request) { - // If we do this more then once per page request we are likely to cause - // errors. - if (static::$isRequestInitialized) { - return; - } - global $cookie_domain; - - if ($cookie_domain) { - // If the user specifies the cookie domain, also use it for session name. - $session_name = $cookie_domain; - } - else { - // Otherwise use $base_url as session name, without the protocol - // to use the same session identifiers across HTTP and HTTPS. - $session_name = $request->getHost() . $request->getBasePath(); - // Replace "core" out of session_name so core scripts redirect properly, - // specifically install.php and update.php. - $session_name = preg_replace('/\/core$/', '', $session_name); - // HTTP_HOST can be modified by a visitor, but has been sanitized already - // in DrupalKernel::bootEnvironment(). - if ($cookie_domain = $request->server->get('HTTP_HOST')) { - // Strip leading periods, www., and port numbers from cookie domain. - $cookie_domain = ltrim($cookie_domain, '.'); - if (strpos($cookie_domain, 'www.') === 0) { - $cookie_domain = substr($cookie_domain, 4); - } - $cookie_domain = explode(':', $cookie_domain); - $cookie_domain = '.' . $cookie_domain[0]; - } - } - // Per RFC 2109, cookie domains must contain at least one dot other than the - // first. For hosts such as 'localhost' or IP Addresses we don't set a - // cookie domain. - if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) { - ini_set('session.cookie_domain', $cookie_domain); - } - // To prevent session cookies from being hijacked, a user can configure the - // SSL version of their website to only transfer session cookies via SSL by - // using PHP's session.cookie_secure setting. The browser will then use two - // separate session cookies for the HTTPS and HTTP versions of the site. So - // we must use different session identifiers for HTTPS and HTTP to prevent a - // cookie collision. - if ($request->isSecure()) { - ini_set('session.cookie_secure', TRUE); - } - $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS'; - - session_name($prefix . substr(hash('sha256', $session_name), 0, 32)); - - static::$isRequestInitialized = TRUE; - } - /** * Returns service instances to persist from an old container to a new one. */ @@ -1144,7 +634,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { /** * Gets a http kernel from the container * - * @return \Symfony\Component\HttpKernel\HttpKernelInterface + * @return HttpKernel */ protected function getHttpKernel() { return $this->container->get('http_kernel'); diff --git a/core/lib/Drupal/Core/DrupalKernelInterface.php b/core/lib/Drupal/Core/DrupalKernelInterface.php index 08d007c4913..1e2ed070fdd 100644 --- a/core/lib/Drupal/Core/DrupalKernelInterface.php +++ b/core/lib/Drupal/Core/DrupalKernelInterface.php @@ -8,7 +8,6 @@ namespace Drupal\Core; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpFoundation\Request; /** * The interface for DrupalKernel, the core of Drupal. @@ -20,8 +19,6 @@ interface DrupalKernelInterface extends HttpKernelInterface { /** * Boots the current kernel. - * - * @return $this */ public function boot(); @@ -56,22 +53,6 @@ interface DrupalKernelInterface extends HttpKernelInterface { */ 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. * @@ -84,28 +65,4 @@ interface DrupalKernelInterface extends HttpKernelInterface { * 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); - } diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php new file mode 100644 index 00000000000..aa7774e90ff --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php @@ -0,0 +1,64 @@ +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); + } + } + + /** + * Initializes the theme system after the routing system. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The Event to process. + */ + public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) { + if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + drupal_theme_initialize(); + } + } + + /** + * Registers the methods in this class that should be listeners. + * + * @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); + + return $events; + } +} diff --git a/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php deleted file mode 100644 index 37ddfbb05a7..00000000000 --- a/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php +++ /dev/null @@ -1,47 +0,0 @@ -getRequestType() == HttpKernelInterface::MASTER_REQUEST) { - 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($event->getRequest()); - } - } - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - public static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequestThemeNegotiator', 29); - return $events; - } - -} diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php index e2d57d2b6e9..3ac0f03328c 100644 --- a/core/lib/Drupal/Core/Site/Settings.php +++ b/core/lib/Drupal/Core/Site/Settings.php @@ -7,8 +7,6 @@ namespace Drupal\Core\Site; -use Drupal\Core\Database\Database; - /** * Read only settings that are initialized with the class. * @@ -82,31 +80,6 @@ final class Settings { 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. * diff --git a/core/lib/Drupal/Core/Test/TestKernel.php b/core/lib/Drupal/Core/Test/TestKernel.php index 6144dbf7cc5..12938ce7368 100644 --- a/core/lib/Drupal/Core/Test/TestKernel.php +++ b/core/lib/Drupal/Core/Test/TestKernel.php @@ -8,28 +8,46 @@ namespace Drupal\Core\Test; use Drupal\Core\DrupalKernel; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Extension\Extension; +use Drupal\Core\Installer\InstallerServiceProvider; use Composer\Autoload\ClassLoader; /** - * Kernel to mock requests to test simpletest. + * Kernel for run-tests.sh. */ class TestKernel extends DrupalKernel { + /** + * Constructs a TestKernel. + * + * @param \Composer\Autoload\ClassLoader $class_loader + * The classloader. + */ + 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'), + ); + } + /** * {@inheritdoc} */ - 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; - } - - return parent::createFromRequest($request, $class_loader, $environment, $allow_dumping); + 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'; } } diff --git a/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/core/lib/Drupal/Core/Test/TestRunnerKernel.php deleted file mode 100644 index 7b04673c3fd..00000000000 --- a/core/lib/Drupal/Core/Test/TestRunnerKernel.php +++ /dev/null @@ -1,85 +0,0 @@ -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'), - ); - } - - /** - * {@inheritdoc} - */ - public function boot() { - // Ensure that required Settings exist. - if (!Settings::getAll()) { - new Settings(array( - 'hash_salt' => 'run-tests', - )); - } - - // Remove Drupal's error/exception handlers; they are designed for HTML - // and there is no storage nor a (watchdog) logger here. - restore_error_handler(); - restore_exception_handler(); - - // In addition, ensure that PHP errors are not hidden away in logs. - ini_set('display_errors', TRUE); - - parent::boot(); - - $this->getContainer()->get('module_handler')->loadAll(); - - simpletest_classloader_register(); - } - - /** - * {@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'; - } - -} diff --git a/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php b/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php index ec00177518a..e4231a3e515 100644 --- a/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php +++ b/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php @@ -122,8 +122,6 @@ class CKEditorLoadingTest extends WebTestBase { // configuration also results in modified CKEditor configuration, so we // don't test that here. \Drupal::moduleHandler()->install(array('ckeditor_test')); - // Force container rebuild so module list is correct on request. - $this->rebuildContainer(); $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions(); $editor_settings = $editor->getSettings(); $editor_settings['toolbar']['buttons'][0][] = 'Llama'; @@ -131,14 +129,12 @@ class CKEditorLoadingTest extends WebTestBase { $editor->save(); $this->drupalGet('node/add/article'); list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck(); - $expected = array( - 'formats' => array( - 'filtered_html' => array( - 'format' => 'filtered_html', - 'editor' => 'ckeditor', - 'editorSettings' => $ckeditor_plugin->getJSSettings($editor), - 'editorSupportsContentFiltering' => TRUE, - 'isXssSafe' => FALSE, + $expected = array('formats' => array('filtered_html' => array( + 'format' => 'filtered_html', + 'editor' => 'ckeditor', + 'editorSettings' => $ckeditor_plugin->getJSSettings($editor), + 'editorSupportsContentFiltering' => TRUE, + 'isXssSafe' => FALSE, ))); $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page."); $this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct."); diff --git a/core/modules/dblog/src/Tests/DbLogTest.php b/core/modules/dblog/src/Tests/DbLogTest.php index 0cb7d3ca020..84c05cfd7c5 100644 --- a/core/modules/dblog/src/Tests/DbLogTest.php +++ b/core/modules/dblog/src/Tests/DbLogTest.php @@ -138,7 +138,7 @@ class DbLogTest extends WebTestBase { 'user' => $this->big_user, 'uid' => $this->big_user->id(), 'request_uri' => $base_root . request_uri(), - 'referer' => \Drupal::request()->server->get('HTTP_REFERER'), + 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', 'ip' => '127.0.0.1', 'timestamp' => REQUEST_TIME, ); @@ -421,7 +421,7 @@ class DbLogTest extends WebTestBase { 'user' => $this->big_user, 'uid' => $this->big_user->id(), 'request_uri' => $base_root . request_uri(), - 'referer' => \Drupal::request()->server->get('HTTP_REFERER'), + 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', 'ip' => '127.0.0.1', 'timestamp' => REQUEST_TIME, ); diff --git a/core/modules/field/src/Tests/FieldHelpTest.php b/core/modules/field/src/Tests/FieldHelpTest.php index 2af6b923b68..0f976e07c29 100644 --- a/core/modules/field/src/Tests/FieldHelpTest.php +++ b/core/modules/field/src/Tests/FieldHelpTest.php @@ -56,7 +56,6 @@ class FieldHelpTest extends WebTestBase { // Enable the Options, E-mail and Field API Test modules. \Drupal::moduleHandler()->install(array('options', 'field_test')); - $this->rebuildContainer(); \Drupal::service('plugin.manager.field.widget')->clearCachedDefinitions(); \Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions(); diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php index 6fa42f9fcc5..e735ce3fe27 100644 --- a/core/modules/simpletest/src/InstallerTestBase.php +++ b/core/modules/simpletest/src/InstallerTestBase.php @@ -7,10 +7,7 @@ namespace Drupal\simpletest; -use Drupal\Core\DrupalKernel; use Drupal\Core\Session\UserSession; -use Drupal\Core\Site\Settings; -use Symfony\Component\HttpFoundation\Request; /** * Base class for testing the interactive installer. @@ -112,8 +109,7 @@ abstract class InstallerTestBase extends WebTestBase { $this->setUpSite(); // Import new settings.php written by the installer. - $request = Request::createFromGlobals(); - Settings::initialize(DrupalKernel::findSitePath($request)); + drupal_settings_initialize(); foreach ($GLOBALS['config_directories'] as $type => $path) { $this->configDirectories[$type] = $path; } @@ -125,14 +121,12 @@ abstract class InstallerTestBase extends WebTestBase { // WebTestBase::tearDown() will delete the entire test site directory. // Not using File API; a potential error must trigger a PHP warning. chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777); - $this->kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod', FALSE); - $this->kernel->prepareLegacyRequest($request); - $this->container = $this->kernel->getContainer(); - $config = $this->container->get('config.factory'); + + $this->rebuildContainer(); // Manually configure the test mail collector implementation to prevent // tests from sending out e-mails and collect them in state instead. - $config->get('system.mail') + \Drupal::config('system.mail') ->set('interface.default', 'test_mail_collector') ->save(); diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 7a76b5be871..409c1b95a8e 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -13,7 +13,6 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DrupalKernel; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Language\Language; -use Drupal\Core\Site\Settings; use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; @@ -132,31 +131,23 @@ abstract class KernelTestBase extends UnitTestBase { // Create and set new configuration directories. $this->prepareConfigDirectories(); - // Add this test class as a service provider. - // @todo Remove the indirection; implement ServiceProviderInterface instead. + // Build a minimal, partially mocked environment for unit tests. + $this->containerBuild(\Drupal::getContainer()); + // Make sure it survives kernel rebuilds. $GLOBALS['conf']['container_service_providers']['TestServiceProvider'] = 'Drupal\simpletest\TestServiceProvider'; - // Back up settings from TestBase::prepareEnvironment(). - $settings = Settings::getAll(); - // Bootstrap a new kernel. Don't use createFromRequest so we don't mess with settings. - $this->kernel = new DrupalKernel('testing', drupal_classloader(), FALSE); - $request = Request::create('/'); - $this->kernel->setSitePath(DrupalKernel::findSitePath($request)); + \Drupal::state()->set('system.module.files', $this->moduleFiles); + \Drupal::state()->set('system.theme.files', $this->themeFiles); + + // Bootstrap the kernel. + // No need to dump it; this test runs in-memory. + $this->kernel = new DrupalKernel('unit_testing', drupal_classloader(), FALSE); $this->kernel->boot(); - // Restore and merge settings. - // DrupalKernel::boot() initializes new Settings, and the containerBuild() - // method sets additional settings. - new Settings($settings + Settings::getAll()); - - // Set the request scope. - $this->container = $this->kernel->getContainer(); + $request = Request::create('/'); $this->container->set('request', $request); $this->container->get('request_stack')->push($request); - $this->container->get('state')->set('system.module.files', $this->moduleFiles); - $this->container->get('state')->set('system.theme.files', $this->themeFiles); - // Create a minimal core.extension configuration object so that the list of // enabled modules can be maintained allowing // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work. @@ -413,7 +404,8 @@ abstract class KernelTestBase extends UnitTestBase { // Ensure isLoaded() is TRUE in order to make _theme() work. // Note that the kernel has rebuilt the container; this $module_handler is // no longer the $module_handler instance from above. - $this->container->get('module_handler')->reload(); + $module_handler = $this->container->get('module_handler'); + $module_handler->reload(); $this->pass(format_string('Enabled modules: %modules.', array( '%modules' => implode(', ', $modules), ))); diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index b133447f77d..023cf8e53bc 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -15,6 +15,7 @@ use Drupal\Core\Config\StorageComparer; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Database\ConnectionNotDefinedException; use Drupal\Core\Config\StorageInterface; +use Drupal\Core\DrupalKernel; use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountProxy; use Drupal\Core\Session\AnonymousUserSession; @@ -652,7 +653,7 @@ abstract class TestBase { * TRUE if the assertion succeeded, FALSE otherwise. * * @see TestBase::prepareEnvironment() - * @see \Drupal\Core\DrupalKernel::bootConfiguration() + * @see _drupal_bootstrap_configuration() */ protected function assertNoErrorsLogged() { // Since PHP only creates the error.log file when an actual error is diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index e75411d62bb..ec50d4dd70f 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -20,7 +20,6 @@ use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AnonymousUserSession; use Drupal\Core\Session\UserSession; -use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Datetime\DrupalDateTime; use Drupal\block\Entity\Block; @@ -174,8 +173,6 @@ abstract class WebTestBase extends TestBase { /** * The kernel used in this test. - * - * @var \Drupal\Core\DrupalKernel */ protected $kernel; @@ -847,11 +844,17 @@ abstract class WebTestBase extends TestBase { ); $this->writeSettings($settings); + // Since Drupal is bootstrapped already, install_begin_request() will not + // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to + // reload the newly written custom settings.php manually. + drupal_settings_initialize(); + // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; install_drupal($parameters); // Import new settings.php written by the installer. + drupal_settings_initialize(); foreach ($GLOBALS['config_directories'] as $type => $path) { $this->configDirectories[$type] = $path; } @@ -864,11 +867,7 @@ abstract class WebTestBase extends TestBase { // Not using File API; a potential error must trigger a PHP warning. chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777); - $request = \Drupal::request(); - $this->kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod', TRUE); - $this->kernel->prepareLegacyRequest($request); - $container = $this->kernel->getContainer(); - $config = $container->get('config.factory'); + $this->rebuildContainer(); // Manually create and configure private and temporary files directories. // While these could be preset/enforced in settings.php like the public @@ -876,7 +875,7 @@ abstract class WebTestBase extends TestBase { // UI. If declared in settings.php, they would no longer be configurable. file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); - $config->get('system.file') + \Drupal::config('system.file') ->set('path.private', $this->private_files_directory) ->set('path.temporary', $this->temp_files_directory) ->save(); @@ -885,7 +884,7 @@ abstract class WebTestBase extends TestBase { // tests from sending out e-mails and collect them in state instead. // While this should be enforced via settings.php prior to installation, // some tests expect to be able to test mail system implementations. - $config->get('system.mail') + \Drupal::config('system.mail') ->set('interface.default', 'test_mail_collector') ->save(); @@ -893,10 +892,10 @@ abstract class WebTestBase extends TestBase { // environment optimizations for all tests to avoid needless overhead and // ensure a sane default experience for test authors. // @see https://drupal.org/node/2259167 - $config->get('system.logging') + \Drupal::config('system.logging') ->set('error_level', 'verbose') ->save(); - $config->get('system.performance') + \Drupal::config('system.performance') ->set('css.preprocess', FALSE) ->set('js.preprocess', FALSE) ->save(); @@ -916,20 +915,22 @@ abstract class WebTestBase extends TestBase { } if ($modules) { $modules = array_unique($modules); - $success = $container->get('module_handler')->install($modules, TRUE); + $success = \Drupal::moduleHandler()->install($modules, TRUE); $this->assertTrue($success, String::format('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); $this->rebuildContainer(); } + // Like DRUPAL_BOOTSTRAP_CONFIGURATION above, any further bootstrap phases + // are not re-executed by the installer, as Drupal is bootstrapped already. // Reset/rebuild all data structures after enabling the modules, primarily // to synchronize all data structures and caches between the test runner and // the child site. // Affects e.g. file_get_stream_wrappers(). - // @see \Drupal\Core\DrupalKernel::bootCode() + // @see _drupal_bootstrap_code() + // @see _drupal_bootstrap_full() // @todo Test-specific setUp() methods may set up further fixtures; find a // way to execute this after setUp() is done, or to eliminate it entirely. $this->resetAll(); - $this->kernel->prepareLegacyRequest($request); // Temporary fix so that when running from run-tests.sh we don't get an // empty current path which would indicate we're on the home page. @@ -1084,18 +1085,29 @@ abstract class WebTestBase extends TestBase { * tests can invoke this workaround when requiring services from newly * enabled modules to be immediately available in the same request. */ - protected function rebuildContainer() { - // Maintain the current global request object. + protected function rebuildContainer($environment = 'prod') { + // Preserve the request object after the container rebuild. $request = \Drupal::request(); + // When called from InstallerTestBase, the current container is the minimal + // container from TestBase::prepareEnvironment(), which does not contain a + // request stack. + if (\Drupal::getContainer()->initialized('request_stack')) { + $request_stack = \Drupal::service('request_stack'); + } - // Rebuild the kernel and bring it back to a fully bootstrapped state. - $this->kernel->shutdown(); - $this->kernel->prepareLegacyRequest($request); - + $this->kernel = new DrupalKernel($environment, drupal_classloader(), TRUE, FALSE); + $this->kernel->boot(); // DrupalKernel replaces the container in \Drupal::getContainer() with a // different object, so we need to replace the instance on this test class. - $this->container = $this->kernel->getContainer(); - + $this->container = \Drupal::getContainer(); + // The current user is set in TestBase::prepareEnvironment(). + $this->container->set('request', $request); + if (isset($request_stack)) { + $this->container->set('request_stack', $request_stack); + } + else { + $this->container->get('request_stack')->push($request); + } $this->container->get('current_user')->setAccount(\Drupal::currentUser()); // The request context is normally set by the router_listener from within @@ -1417,7 +1429,7 @@ abstract class WebTestBase extends TestBase { * TRUE if this test was instantiated in a request within the test site, * FALSE otherwise. * - * @see \Drupal\Core\DrupalKernel::bootConfiguration() + * @see _drupal_bootstrap_configuration() */ protected function isInChildSite() { return DRUPAL_TEST_IN_CHILD_SITE; diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php index 4d2004646ae..85864e10b58 100644 --- a/core/modules/statistics/statistics.php +++ b/core/modules/statistics/statistics.php @@ -5,22 +5,15 @@ * Handles counts of node views via AJAX with minimal bootstrap. */ -use Drupal\Core\DrupalKernel; -use Symfony\Component\HttpFoundation\Request; - +// Change the directory to the Drupal root. chdir('../../..'); -$autoloader = require_once dirname(dirname(__DIR__)) . '/vendor/autoload.php'; +// Load the Drupal bootstrap. +require_once dirname(dirname(__DIR__)) . '/vendor/autoload.php'; +require_once dirname(dirname(__DIR__)) . '/includes/bootstrap.inc'; +drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL); -$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod'); -$kernel->boot(); - -$views = $kernel->getContainer() - ->get('config.factory') - ->get('statistics.settings') - ->get('count_content_views'); - -if ($views) { +if (\Drupal::config('statistics.settings')->get('count_content_views')) { $nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT); if ($nid) { \Drupal::database()->merge('node_counter') diff --git a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php index 9ec9df936dc..17e4b4b9f03 100644 --- a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php +++ b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php @@ -10,18 +10,12 @@ namespace Drupal\system\Tests\DrupalKernel; use Drupal\Core\DrupalKernel; use Drupal\Core\Site\Settings; use Drupal\simpletest\DrupalUnitTestBase; -use Symfony\Component\HttpFoundation\Request; /** * Tests compilation of the DIC. */ class DrupalKernelTest extends DrupalUnitTestBase { - /** - * @var \Composer\Autoload\ClassLoader - */ - protected $classloader; - public static function getInfo() { return array( 'name' => 'DrupalKernel tests', @@ -44,60 +38,25 @@ class DrupalKernelTest extends DrupalUnitTestBase { 'directory' => DRUPAL_ROOT . '/' . $this->public_files_directory . '/php', 'secret' => drupal_get_hash_salt(), ))); - - $this->classloader = drupal_classloader(); - } - - /** - * Build a kernel for testings. - * - * Because the bootstrap is in DrupalKernel::boot and that involved loading - * settings from the filesystem we need to go to extra lengths to build a kernel - * for testing. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * A request object to use in booting the kernel. - * @param array $modules_enabled - * A list of modules to enable on the kernel. - * @param bool $read_only - * Build the kernel in a read only state. - * @return DrupalKernel - */ - protected function getTestKernel(Request $request, array $modules_enabled = NULL, $read_only = FALSE) { - // Manually create kernel to avoid replacing settings. - $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'testing'); - $this->settingsSet('hash_salt', $this->databasePrefix); - if (isset($modules_enabled)) { - $kernel->updateModules($modules_enabled); - } - $kernel->boot(); - - if ($read_only) { - $php_storage = Settings::get('php_storage'); - $php_storage['service_container']['class'] = 'Drupal\Component\PhpStorage\FileReadOnlyStorage'; - $this->settingsSet('php_storage', $php_storage); - } - return $kernel; } /** * Tests DIC compilation. */ function testCompileDIC() { + $classloader = drupal_classloader(); // @todo: write a memory based storage backend for testing. - $modules_enabled = array( + $module_enabled = array( 'system' => 'system', 'user' => 'user', ); - - $request = Request::createFromGlobals(); - $this->getTestKernel($request, $modules_enabled) - // Trigger Kernel dump. - ->getContainer(); - + $kernel = new DrupalKernel('testing', $classloader); + $kernel->updateModules($module_enabled); + $kernel->boot(); // Instantiate it a second time and we should get the compiled Container // class. - $kernel = $this->getTestKernel($request); + $kernel = new DrupalKernel('testing', $classloader); + $kernel->boot(); $container = $kernel->getContainer(); $refClass = new \ReflectionClass($container); $is_compiled_container = @@ -107,24 +66,25 @@ class DrupalKernelTest extends DrupalUnitTestBase { // Verify that the list of modules is the same for the initial and the // compiled container. $module_list = array_keys($container->get('module_handler')->getModuleList()); - $this->assertEqual(array_values($modules_enabled), $module_list); + $this->assertEqual(array_values($module_enabled), $module_list); // Now use the read-only storage implementation, simulating a "production" // environment. - $container = $this->getTestKernel($request, NULL, TRUE) - ->getContainer(); - + $php_storage = Settings::get('php_storage'); + $php_storage['service_container']['class'] = 'Drupal\Component\PhpStorage\FileReadOnlyStorage'; + $this->settingsSet('php_storage', $php_storage); + $kernel = new DrupalKernel('testing', $classloader); + $kernel->boot(); + $container = $kernel->getContainer(); $refClass = new \ReflectionClass($container); $is_compiled_container = $refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' && !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder'); $this->assertTrue($is_compiled_container); - // Verify that the list of modules is the same for the initial and the // compiled container. $module_list = array_keys($container->get('module_handler')->getModuleList()); - $this->assertEqual(array_values($modules_enabled), $module_list); - + $this->assertEqual(array_values($module_enabled), $module_list); // Test that our synthetic services are there. $classloader = $container->get('class_loader'); $refClass = new \ReflectionClass($classloader); @@ -138,26 +98,25 @@ class DrupalKernelTest extends DrupalUnitTestBase { // Add another module so that we can test that the new module's bundle is // registered to the new container. - $modules_enabled['service_provider_test'] = 'service_provider_test'; - $this->getTestKernel($request, $modules_enabled, TRUE); - + $module_enabled['service_provider_test'] = 'service_provider_test'; + $kernel = new DrupalKernel('testing', $classloader); + $kernel->updateModules($module_enabled); + $kernel->boot(); // Instantiate it a second time and we should still get a ContainerBuilder // class because we are using the read-only PHP storage. - $kernel = $this->getTestKernel($request, $modules_enabled, TRUE); + $kernel = new DrupalKernel('testing', $classloader); + $kernel->updateModules($module_enabled); + $kernel->boot(); $container = $kernel->getContainer(); - $refClass = new \ReflectionClass($container); $is_container_builder = $refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder'); - $this->assertTrue($is_container_builder, 'Container is a builder'); - + $this->assertTrue($is_container_builder); // Assert that the new module's bundle was registered to the new container. - $this->assertTrue($container->has('service_provider_test_class'), 'Container has test service'); - + $this->assertTrue($container->has('service_provider_test_class')); // Test that our synthetic services are there. $classloader = $container->get('class_loader'); $refClass = new \ReflectionClass($classloader); $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a classloader'); - // Check that the location of the new module is registered. $modules = $container->getParameter('container.modules'); $this->assertEqual($modules['service_provider_test'], array( diff --git a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php index 58e62bc2d27..4faec44b567 100644 --- a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php +++ b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php @@ -102,8 +102,6 @@ class EntityViewControllerTest extends WebTestBase { // Enable the RDF module to ensure that two modules can add attributes to // the same field item. \Drupal::moduleHandler()->install(array('rdf')); - $this->rebuildContainer(); - // Set an RDF mapping for the field_test_text field. This RDF mapping will // be turned into RDFa attributes in the field item output. $mapping = rdf_get_mapping('entity_test', 'entity_test'); diff --git a/core/modules/system/src/Tests/Menu/MenuRouterTest.php b/core/modules/system/src/Tests/Menu/MenuRouterTest.php index 58258955b86..4383658ba25 100644 --- a/core/modules/system/src/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/src/Tests/Menu/MenuRouterTest.php @@ -312,7 +312,6 @@ class MenuRouterTest extends WebTestBase { protected function doTestMenuOnRoute() { \Drupal::moduleHandler()->install(array('router_test')); \Drupal::service('router.builder')->rebuild(); - $this->rebuildContainer(); $this->drupalGet('router_test/test2'); $this->assertLinkByHref('menu_no_title_callback'); diff --git a/core/modules/system/src/Tests/System/ScriptTest.php b/core/modules/system/src/Tests/System/ScriptTest.php index 773cfa0b3f2..9ba2e5c9f0c 100644 --- a/core/modules/system/src/Tests/System/ScriptTest.php +++ b/core/modules/system/src/Tests/System/ScriptTest.php @@ -45,14 +45,6 @@ class ScriptTest extends DrupalUnitTestBase { * Tests rebuild_token_calculator.sh. */ public function testRebuildTokenCalculatorSh() { - // The script requires a settings.php with a hash salt setting. - $filename = $this->siteDirectory . '/settings.php'; - touch($filename); - $settings['settings']['hash_salt'] = (object) array( - 'value' => 'some_random_key', - 'required' => TRUE, - ); - drupal_rewrite_settings($settings, $filename); $_SERVER['argv'] = array( 'core/scripts/rebuild_token_calculator.sh', ); diff --git a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php b/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php index e5c33a174e8..202ddd2a44b 100644 --- a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php +++ b/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php @@ -69,7 +69,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { // Enable the theme_suggestions_test module to test modules implementing // suggestions alter hooks. \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); $this->drupalGet('theme-test/general-suggestion-alter'); $this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().'); } @@ -91,7 +90,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { // Enable the theme_suggestions_test module to test modules implementing // suggestions alter hooks. \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); $this->drupalGet('theme-test/suggestion-alter'); $this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().'); } @@ -115,7 +113,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { // Ensure that the base hook is used to determine the suggestion alter hook. \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); $this->drupalGet('theme-test/specific-suggestion-alter'); $this->assertText('Template overridden based on suggestion alter hook determined by the base hook.'); $this->assertTrue(strpos($this->drupalGetContent(), 'theme_test_specific_suggestions__variant') < strpos($this->drupalGetContent(), 'theme_test_specific_suggestions__variant__foo'), 'Specific theme call is added to the suggestions array before the suggestions alter hook.'); @@ -138,7 +135,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { // Enable the theme_suggestions_test module to test modules implementing // suggestions alter hooks. \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); $this->drupalGet('theme-test/function-suggestion-alter'); $this->assertText('Theme function overridden based on new theme suggestion provided by a module.'); } @@ -155,7 +151,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { // the include file is always loaded. The file will always be included for // the first request because the theme registry is being rebuilt. \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); $this->drupalGet('theme-test/suggestion-alter-include'); $this->assertText('Function suggested via suggestion alter hook found in include file.', 'Include file loaded for initial request.'); $this->drupalGet('theme-test/suggestion-alter-include'); @@ -174,7 +169,6 @@ class ThemeSuggestionsAlterTest extends WebTestBase { ->set('default', 'test_theme') ->save(); \Drupal::moduleHandler()->install(array('theme_suggestions_test')); - $this->rebuildContainer(); // Send two requests so that we get all the messages we've set via // drupal_set_message(). diff --git a/core/modules/system/tests/http.php b/core/modules/system/tests/http.php index 33869799e31..662031f79b4 100644 --- a/core/modules/system/tests/http.php +++ b/core/modules/system/tests/http.php @@ -5,29 +5,19 @@ * Fake an HTTP request, for use during testing. */ -use Drupal\Core\Test\TestKernel; -use Symfony\Component\HttpFoundation\Request; - -chdir('../../../..'); - -$autoloader = require_once './core/vendor/autoload.php'; - // Set a global variable to indicate a mock HTTP request. $is_http_mock = !empty($_SERVER['HTTPS']); // Change to HTTP. $_SERVER['HTTPS'] = NULL; ini_set('session.cookie_secure', FALSE); -foreach ($_SERVER as &$value) { - $value = str_replace('core/modules/system/tests/http.php', 'index.php', $value); - $value = str_replace('https://', 'http://', $value); +foreach ($_SERVER as $key => $value) { + $_SERVER[$key] = str_replace('core/modules/system/tests/http.php', 'index.php', $value); + $_SERVER[$key] = str_replace('https://', 'http://', $_SERVER[$key]); } -$request = Request::createFromGlobals(); -$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE); -$response = $kernel - ->handlePageCache($request) - ->handle($request) - // Handle the response object. - ->prepare($request)->send(); -$kernel->terminate($request, $response); +// Change current directory to the Drupal root. +chdir('../../../..'); +require_once dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; +require_once dirname(dirname(dirname(__DIR__))) . '/includes/bootstrap.inc'; +drupal_handle_request(TRUE); diff --git a/core/modules/system/tests/https.php b/core/modules/system/tests/https.php index 00a6b13d615..247e6e515e0 100644 --- a/core/modules/system/tests/https.php +++ b/core/modules/system/tests/https.php @@ -8,28 +8,18 @@ * see http.php. */ -use Drupal\Core\Test\TestKernel; -use Symfony\Component\HttpFoundation\Request; - -chdir('../../../..'); - -$autoloader = require_once './core/vendor/autoload.php'; - // Set a global variable to indicate a mock HTTPS request. $is_https_mock = empty($_SERVER['HTTPS']); // Change to HTTPS. $_SERVER['HTTPS'] = 'on'; -foreach ($_SERVER as &$value) { - $value = str_replace('core/modules/system/tests/https.php', 'index.php', $value); - $value = str_replace('http://', 'https://', $value); +foreach ($_SERVER as $key => $value) { + $_SERVER[$key] = str_replace('core/modules/system/tests/https.php', 'index.php', $value); + $_SERVER[$key] = str_replace('http://', 'https://', $_SERVER[$key]); } -$request = Request::createFromGlobals(); -$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE); -$response = $kernel - ->handlePageCache($request) - ->handle($request) - // Handle the response object. - ->prepare($request)->send(); -$kernel->terminate($request, $response); +// Change current directory to the Drupal root. +chdir('../../../..'); +require_once dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; +require_once dirname(dirname(dirname(__DIR__))) . '/includes/bootstrap.inc'; +drupal_handle_request(TRUE); diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index f46770abe1f..3599c5944b1 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -109,7 +109,7 @@ function toolbar_element_info() { * Use Drupal's page cache for toolbar/subtrees/*, even for authenticated users. * * This gets invoked after full bootstrap, so must duplicate some of what's - * done by \Drupal\Core\DrupalKernel::handlePageCache(). + * done by _drupal_bootstrap_page_cache(). * * @todo Replace this hack with something better integrated with DrupalKernel * once Drupal's page caching itself is properly integrated. @@ -119,7 +119,7 @@ function _toolbar_initialize_page_cache() { drupal_page_is_cacheable(TRUE); // If we have a cache, serve it. - // @see \Drupal\Core\DrupalKernel::handlePageCache() + // @see _drupal_bootstrap_page_cache() $request = \Drupal::request(); $response = drupal_page_get_cache($request); if ($response) { diff --git a/core/modules/user/src/Tests/UserLoginTest.php b/core/modules/user/src/Tests/UserLoginTest.php index c6fefcf9653..8016987f38d 100644 --- a/core/modules/user/src/Tests/UserLoginTest.php +++ b/core/modules/user/src/Tests/UserLoginTest.php @@ -135,7 +135,6 @@ class UserLoginTest extends WebTestBase { // users password gets rehashed during the login. $overridden_count_log2 = 19; \Drupal::moduleHandler()->install(array('user_custom_phpass_params_test')); - $this->rebuildContainer(); $account->pass_raw = $password; $this->drupalLogin($account); diff --git a/core/modules/views_ui/src/Tests/PreviewTest.php b/core/modules/views_ui/src/Tests/PreviewTest.php index 315ef98d620..01feff62eda 100644 --- a/core/modules/views_ui/src/Tests/PreviewTest.php +++ b/core/modules/views_ui/src/Tests/PreviewTest.php @@ -34,8 +34,6 @@ class PreviewTest extends UITestBase { */ protected function testPreviewContextual() { \Drupal::moduleHandler()->install(array('contextual')); - $this->rebuildContainer(); - $this->drupalGet('admin/structure/views/view/test_preview/edit'); $this->assertResponse(200); $this->drupalPostForm(NULL, $edit = array(), t('Update preview')); diff --git a/core/rebuild.php b/core/rebuild.php index f7acbcea8a7..3e8ed1282c1 100644 --- a/core/rebuild.php +++ b/core/rebuild.php @@ -11,30 +11,25 @@ */ use Drupal\Component\Utility\Crypt; -use Drupal\Core\DrupalKernel; use Drupal\Core\Site\Settings; -use Symfony\Component\HttpFoundation\Request; // Change the directory to the Drupal root. chdir('..'); -$autoloader = require_once __DIR__ . '/vendor/autoload.php'; +require_once __DIR__ . '/vendor/autoload.php'; +require_once __DIR__ . '/includes/bootstrap.inc'; require_once __DIR__ . '/includes/utility.inc'; -$request = Request::createFromGlobals(); -// Manually resemble early bootstrap of DrupalKernel::boot(). -require_once __DIR__ . '/includes/bootstrap.inc'; -DrupalKernel::bootEnvironment(); -Settings::initialize(DrupalKernel::findSitePath($request)); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); if (Settings::get('rebuild_access', FALSE) || - ($request->get('token') && $request->get('timestamp') && - ((REQUEST_TIME - $request->get('timestamp')) < 300) && - ($request->get('token') === Crypt::hmacBase64($request->get('timestamp'), Settings::get('hash_salt'))) + (isset($_GET['token'], $_GET['timestamp']) && + ((REQUEST_TIME - $_GET['timestamp']) < 300) && + ($_GET['token'] === Crypt::hmacBase64($_GET['timestamp'], Settings::get('hash_salt'))) )) { - drupal_rebuild($autoloader, $request); + drupal_rebuild(); drupal_set_message('Cache rebuild complete.'); } -$base_path = dirname(dirname($request->getBaseUrl())); -header('Location: ' . $base_path); + +header('Location: ' . $GLOBALS['base_url']); diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh index ee3c10246dc..f80d75c6ca8 100755 --- a/core/scripts/password-hash.sh +++ b/core/scripts/password-hash.sh @@ -9,7 +9,6 @@ */ use Drupal\Core\DrupalKernel; -use Symfony\Component\HttpFoundation\Request; // Check for $_SERVER['argv'] instead of PHP_SAPI === 'cli' to allow this script // to be tested with the Simpletest UI test runner. @@ -57,10 +56,14 @@ EOF; // Password list to be processed. $passwords = $_SERVER['argv']; -$autoloader = require __DIR__ . '/../vendor/autoload.php'; +$core = dirname(__DIR__); +require_once $core . '/vendor/autoload.php'; +require_once $core . '/includes/bootstrap.inc'; -$request = Request::createFromGlobals(); -$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', FALSE); +// Bootstrap the code so we have the container. +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + +$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE); $kernel->boot(); $password_hasher = $kernel->getContainer()->get('password'); diff --git a/core/scripts/rebuild_token_calculator.sh b/core/scripts/rebuild_token_calculator.sh index bcfd2b6fc16..9e7dc03ddb0 100755 --- a/core/scripts/rebuild_token_calculator.sh +++ b/core/scripts/rebuild_token_calculator.sh @@ -7,9 +7,7 @@ */ use Drupal\Component\Utility\Crypt; -use Drupal\Core\DrupalKernel; use Drupal\Core\Site\Settings; -use Symfony\Component\HttpFoundation\Request; // Check for $_SERVER['argv'] instead of PHP_SAPI === 'cli' to allow this script // to be tested with the Simpletest UI test runner. @@ -18,11 +16,11 @@ if (!isset($_SERVER['argv']) || !is_array($_SERVER['argv'])) { return; } -require __DIR__ . '/../vendor/autoload.php'; -require_once __DIR__ . '/../includes/bootstrap.inc'; +$core = dirname(__DIR__); +require_once $core . '/vendor/autoload.php'; +require_once $core . '/includes/bootstrap.inc'; -$request = Request::createFromGlobals(); -Settings::initialize(DrupalKernel::findSitePath($request)); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); $timestamp = time(); $token = Crypt::hmacBase64($timestamp, Settings::get('hash_salt')); diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 4679325dcf3..8eff2a29805 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -8,10 +8,10 @@ use Drupal\Component\Utility\Timer; use Drupal\Core\Database\Database; use Drupal\Core\Site\Settings; -use Drupal\Core\Test\TestRunnerKernel; +use Drupal\Core\Test\TestKernel; use Symfony\Component\HttpFoundation\Request; -$autoloader = require_once __DIR__ . '/../vendor/autoload.php'; +require_once __DIR__ . '/../vendor/autoload.php'; const SIMPLETEST_SCRIPT_COLOR_PASS = 32; // Green. const SIMPLETEST_SCRIPT_COLOR_FAIL = 31; // Red. @@ -26,10 +26,7 @@ if ($args['help'] || $count == 0) { } simpletest_script_init(); - -$request = Request::createFromGlobals(); -$kernel = TestRunnerKernel::createFromRequest($request, $autoloader); -$kernel->prepareLegacyRequest($request); +simpletest_script_bootstrap(); if ($args['execute-test']) { simpletest_script_setup_database(); @@ -356,6 +353,50 @@ function simpletest_script_init() { } chdir(realpath(__DIR__ . '/../..')); + require_once dirname(__DIR__) . '/includes/bootstrap.inc'; +} + +/** + * Bootstraps a minimal Drupal environment. + * + * @see install_begin_request() + */ +function simpletest_script_bootstrap() { + // Load legacy include files. + foreach (glob(DRUPAL_ROOT . '/core/includes/*.inc') as $include) { + require_once $include; + } + + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + + // Remove Drupal's error/exception handlers; they are designed for HTML + // and there is no storage nor a (watchdog) logger here. + restore_error_handler(); + restore_exception_handler(); + + // In addition, ensure that PHP errors are not hidden away in logs. + ini_set('display_errors', TRUE); + + // Ensure that required Settings exist. + if (!Settings::getAll()) { + new Settings(array( + 'hash_salt' => 'run-tests', + )); + } + + $kernel = new TestKernel(drupal_classloader()); + $kernel->boot(); + + $request = Request::createFromGlobals(); + $container = $kernel->getContainer(); + $container->enterScope('request'); + $container->set('request', $request, 'request'); + $container->get('request_stack')->push($request); + + $module_handler = $container->get('module_handler'); + $module_handler->loadAll(); + + simpletest_classloader_register(); } /** diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php index 99fe34d0519..72c5d5bc930 100644 --- a/core/tests/bootstrap.php +++ b/core/tests/bootstrap.php @@ -87,7 +87,7 @@ define('DRUPAL_ROOT', realpath(__DIR__ . '/../../')); // Set sane locale settings, to ensure consistent string, dates, times and // numbers handling. -// @see \Drupal\Core\DrupalKernel::bootEnvironment() +// @see drupal_environment_initialize() setlocale(LC_ALL, 'C'); // Set the default timezone. While this doesn't cause any tests to fail, PHP diff --git a/core/update.php b/core/update.php index 9147e9ef85f..b6accf3a5f4 100644 --- a/core/update.php +++ b/core/update.php @@ -25,7 +25,7 @@ use Symfony\Component\DependencyInjection\Reference; // Change the directory to the Drupal root. chdir('..'); -$autoloader = require_once __DIR__ . '/vendor/autoload.php'; +require_once __DIR__ . '/vendor/autoload.php'; // Exit early if an incompatible PHP version would cause fatal errors. // The minimum version is specified explicitly, as DRUPAL_MINIMUM_PHP is not @@ -297,17 +297,18 @@ ini_set('display_errors', FALSE); // We prepare a minimal bootstrap for the update requirements check to avoid // reaching the PHP memory limit. +require_once __DIR__ . '/includes/bootstrap.inc'; require_once __DIR__ . '/includes/update.inc'; +require_once __DIR__ . '/includes/common.inc'; +require_once __DIR__ . '/includes/file.inc'; +require_once __DIR__ . '/includes/unicode.inc'; require_once __DIR__ . '/includes/install.inc'; +require_once __DIR__ . '/includes/schema.inc'; +// Bootstrap to configuration. +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); -$request = Request::createFromGlobals(); -$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'update', FALSE); - -// Enable UpdateServiceProvider service overrides. -// @see update_flush_all_caches() -$GLOBALS['conf']['container_service_providers']['UpdateServiceProvider'] = 'Drupal\Core\DependencyInjection\UpdateServiceProvider'; -$GLOBALS['conf']['update_service_provider_overrides'] = TRUE; -$kernel->boot(); +// Bootstrap the database. +require_once __DIR__ . '/includes/database.inc'; // Updating from a site schema version prior to 8000 should block the update // process. Ensure that the site is not attempting to update a database @@ -320,9 +321,27 @@ if (db_table_exists('system')) { } } -$kernel->prepareLegacyRequest($request); +// Enable UpdateServiceProvider service overrides. +// @see update_flush_all_caches() +$GLOBALS['conf']['container_service_providers']['UpdateServiceProvider'] = 'Drupal\Core\DependencyInjection\UpdateServiceProvider'; +$GLOBALS['conf']['update_service_provider_overrides'] = TRUE; + +// module.inc is not yet loaded but there are calls to module_config_sort() +// below. +require_once __DIR__ . '/includes/module.inc'; + +$settings = Settings::getAll(); +new Settings($settings); +$kernel = new DrupalKernel('update', drupal_classloader(), FALSE); +$kernel->boot(); +$request = Request::createFromGlobals(); +$container = \Drupal::getContainer(); +$container->set('request', $request); +$container->get('request_stack')->push($request); // Determine if the current user has access to run update.php. +drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE); + \Drupal::service('session_manager')->initialize(); // Ensure that URLs generated for the home and admin pages don't have 'update.php' @@ -359,6 +378,7 @@ if (is_null($op) && update_access_allowed()) { install_goto('core/update.php?op=info'); } +drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_maintenance_theme(); // Turn error reporting back on. From now on, only fatal errors (which are diff --git a/index.php b/index.php index 6bff08359f7..a453ecc1f91 100644 --- a/index.php +++ b/index.php @@ -8,22 +8,13 @@ * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory. */ -use Drupal\Core\DrupalKernel; use Drupal\Core\Site\Settings; -use Symfony\Component\HttpFoundation\Request; -$autoloader = require_once __DIR__ . '/core/vendor/autoload.php'; +require_once __DIR__ . '/core/vendor/autoload.php'; +require_once __DIR__ . '/core/includes/bootstrap.inc'; try { - - $request = Request::createFromGlobals(); - $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); - $response = $kernel - ->handlePageCache($request) - ->handle($request) - // Handle the response object. - ->prepare($request)->send(); - $kernel->terminate($request, $response); + drupal_handle_request(); } catch (Exception $e) { $message = 'If you have just changed code (for example deployed a new module or moved an existing one) read http://drupal.org/documentation/rebuild'; diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 7781c0c14d2..587a9cc1a3c 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -245,7 +245,7 @@ $config_directories = array(); * directory and reverse proxy address, and temporary configuration, such as * turning on Twig debugging and security overrides. * - * @see \Drupal\Core\Site\Settings::get() + * @see \Drupal\Component\Utility\Settings::get() */ /** @@ -490,8 +490,8 @@ $settings['update_free_access'] = FALSE; * To see what PHP settings are possible, including whether they can be set at * runtime (by using ini_set()), read the PHP documentation: * http://php.net/manual/ini.list.php - * See \Drupal\Core\DrupalKernel::bootEnvironment() for required runtime - * settings and the .htaccess file for non-runtime settings. + * See drupal_environment_initialize() in core/includes/bootstrap.inc for + * required runtime settings and the .htaccess file for non-runtime settings. * Settings defined there should not be duplicated here so as to avoid conflict * issues. */ diff --git a/web.config b/web.config index b71c37cdcb7..3336222b476 100644 --- a/web.config +++ b/web.config @@ -61,7 +61,8 @@ --> + to index.php. Clean URLs are handled in + drupal_environment_initialize(). -->