diff --git a/.htaccess b/.htaccess index 3e742b6a1eb186c8528f49bb89aca88574e0b9c3..00830a527af6b2a3fe47f09070abd1e8fbfe1bee 100644 --- a/.htaccess +++ b/.htaccess @@ -25,8 +25,9 @@ ErrorDocument 404 /index.php DirectoryIndex index.php index.html index.htm # Override PHP settings that cannot be changed at runtime. See -# sites/default/default.settings.php and drupal_environment_initialize() in -# core/includes/bootstrap.inc for settings that can be changed at runtime. +# sites/default/default.settings.php and +# Drupal\Core\DrupalKernel::bootEnvironment() for settings that can be +# changed at runtime. # PHP 5, Apache 1 and 2. @@ -119,7 +120,7 @@ DirectoryIndex index.php index.html index.htm RewriteRule ^ %1/core/%2 [L,QSA,R=301] # Pass all requests not referring directly to files in the filesystem to - # index.php. Clean URLs are handled in drupal_environment_initialize(). + # index.php. RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !=/favicon.ico diff --git a/core/authorize.php b/core/authorize.php index f938f3903324c6ad706d1bfa61e09ab7819af82d..96fe9aeb3b691c6cbb604241f00459d7fccde558 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -20,13 +20,15 @@ * @link authorize Authorized operation helper functions @endlink */ +use Drupal\Core\DrupalKernel; +use Symfony\Component\HttpFoundation\Request; use Drupal\Core\Site\Settings; use Drupal\Core\Page\DefaultHtmlPageRenderer; // Change the directory to the Drupal root. chdir('..'); -require_once __DIR__ . '/vendor/autoload.php'; +$autoloader = require_once __DIR__ . '/vendor/autoload.php'; /** * Global flag to identify update.php and authorize.php runs. @@ -51,18 +53,9 @@ function authorize_access_allowed() { return Settings::get('allow_authorize_operations', TRUE) && user_access('administer software updates'); } -// *** Real work of the script begins here. *** - -require_once __DIR__ . '/includes/bootstrap.inc'; -require_once __DIR__ . '/includes/common.inc'; -require_once __DIR__ . '/includes/file.inc'; -require_once __DIR__ . '/includes/module.inc'; -require_once __DIR__ . '/includes/ajax.inc'; - -// Prepare a minimal bootstrap. -drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE); -$request = \Drupal::request(); -\Drupal::service('request_stack')->push($request); +$request = Request::createFromGlobals(); +$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); +$kernel->prepareLegacyRequest($request); // We have to enable the user and system modules, even to check access and // display errors via the maintenance theme. diff --git a/core/core.services.yml b/core/core.services.yml index 6a2a46825a7800c952ee506d09bbebf4d79a7d1c..41ee37343e6c55664507d13304266716ff68f7b0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -197,6 +197,10 @@ 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'] @@ -590,10 +594,6 @@ services: tags: - { name: event_subscriber } arguments: ['@path.alias_manager', '@path_processor_manager'] - legacy_request_subscriber: - class: Drupal\Core\EventSubscriber\LegacyRequestSubscriber - tags: - - { name: event_subscriber } finish_response_subscriber: class: Drupal\Core\EventSubscriber\FinishResponseSubscriber tags: diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 3d41fcb419dc37b26a00ee787b48466dd40b4640..3e4fca8f06db8c8da751e7c2cef893027b3f999b 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -6,28 +6,16 @@ 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; /** @@ -126,26 +114,36 @@ /** * 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, initalize a kernel. + * Second bootstrap phase, initialize a kernel. + * + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. */ 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; @@ -207,11 +205,6 @@ /** * 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, @@ -220,100 +213,36 @@ * @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 - * The path of the matching directory. + * @return string + * The path of the matching directory.@see default.settings.php * - * @see default.settings.php - */ -function conf_path($require_settings = TRUE, $reset = FALSE) { - static $conf_path; - - if (isset($conf_path) && !$reset) { - return $conf_path; + * @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. + */ +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(); + } } - - // Check for a simpletest override. - if ($test_prefix = drupal_valid_test_ua()) { - $conf_path = 'sites/simpletest/' . substr($test_prefix, 10); - return $conf_path; + if (\Drupal::hasService('kernel')) { + $site_path = \Drupal::service('kernel')->getSitePath(); } - - // Otherwise, use the normal $conf_path. - $script_name = $_SERVER['SCRIPT_NAME']; - if (!$script_name) { - $script_name = $_SERVER['SCRIPT_FILENAME']; + if (!isset($site_path) || empty($site_path)) { + $site_path = DrupalKernel::findSitePath($request, $require_settings); } - $http_host = $_SERVER['HTTP_HOST']; - $conf_path = find_conf_path($http_host, $script_name, $require_settings); - return $conf_path; + return $site_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. * @@ -333,207 +262,6 @@ 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. * @@ -1256,138 +984,54 @@ function drupal_anonymous_user() { } /** - * 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 + * Bootstraps DrupalKernel to a given boot level. * * @param $phase - * A constant telling which phase to bootstrap to. When you bootstrap to a - * particular phase, all earlier phases are run automatically. Possible - * values: + * A constant telling which phase to bootstrap to. 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 + * @return int * 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) { - // 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; - } + // Temporary variables used for booting later legacy phases. + /** @var \Drupal\Core\DrupalKernel $kernel */ + static $kernel; + static $boot_level = 0; + if (isset($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; - } + $request = Request::createFromGlobals(); + for ($current_phase = $boot_level; $current_phase <= $phase; $current_phase++) { switch ($current_phase) { case DRUPAL_BOOTSTRAP_CONFIGURATION: - _drupal_bootstrap_configuration(); + $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod'); break; case DRUPAL_BOOTSTRAP_KERNEL: - _drupal_bootstrap_kernel(); + $kernel->boot(); break; case DRUPAL_BOOTSTRAP_PAGE_CACHE: - _drupal_bootstrap_page_cache(); + $kernel->handlePageCache($request); break; case DRUPAL_BOOTSTRAP_CODE: - require_once __DIR__ . '/common.inc'; - _drupal_bootstrap_code(); - break; - case DRUPAL_BOOTSTRAP_FULL: - _drupal_bootstrap_full(); + $kernel->prepareLegacyRequest($request); 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; - } - - $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); + return \Drupal::getContainer() ? DRUPAL_BOOTSTRAP_CODE : DRUPAL_BOOTSTRAP_CONFIGURATION; } /** @@ -1469,111 +1113,14 @@ 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 5572e547228efdef1871f6c9986fb9db9ec399ca..62c8ed7131c71d6c00b85c86f84f75f6f4ba4a71 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2603,70 +2603,6 @@ function drupal_valid_token($token, $value = '') { return \Drupal::csrfToken()->validate($token, $value); } -/** - * Loads code for subsystems and modules, and registers stream wrappers. - */ -function _drupal_bootstrap_code() { - require_once __DIR__ . '/../../' . Settings::get('path_inc', 'core/includes/path.inc'); - require_once __DIR__ . '/module.inc'; - require_once __DIR__ . '/theme.inc'; - require_once __DIR__ . '/pager.inc'; - require_once __DIR__ . '/../../' . Settings::get('menu_inc', 'core/includes/menu.inc'); - require_once __DIR__ . '/tablesort.inc'; - require_once __DIR__ . '/file.inc'; - require_once __DIR__ . '/unicode.inc'; - require_once __DIR__ . '/form.inc'; - require_once __DIR__ . '/mail.inc'; - require_once __DIR__ . '/ajax.inc'; - require_once __DIR__ . '/errors.inc'; - require_once __DIR__ . '/schema.inc'; - require_once __DIR__ . '/entity.inc'; - - // Load all enabled modules - \Drupal::moduleHandler()->loadAll(); - - // Make sure all stream wrappers are registered. - file_get_stream_wrappers(); - // Ensure mt_rand() is reseeded to prevent random values from one page load - // being exploited to predict random values in subsequent page loads. - $seed = unpack("L", Crypt::randomBytes(4)); - mt_srand($seed[1]); - - // Set the allowed protocols once we have the config available. - $allowed_protocols = \Drupal::config('system.filter')->get('protocols'); - if (!isset($allowed_protocols)) { - // \Drupal\Component\Utility\UrlHelper::filterBadProtocol() is called by the - // installer and update.php, in which case the configuration may not exist - // (yet). Provide a minimal default set of allowed protocols for these - // cases. - $allowed_protocols = array('http', 'https'); - } - UrlHelper::setAllowedProtocols($allowed_protocols); -} - -/** - * Temporary BC function for scripts not using DrupalKernel. - * - * DrupalKernel skips this and replicates it via event listeners. - * - * @see \Drupal\Core\EventSubscriber\PathSubscriber; - * @see \Drupal\Core\EventSubscriber\LegacyRequestSubscriber; - */ -function _drupal_bootstrap_full($skip = FALSE) { - static $called = FALSE; - - if ($called || $skip) { - $called = TRUE; - return; - } - - // Let all modules take action before the menu system handles the request. - // We do not want this while running update.php. - if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { - drupal_theme_initialize(); - } -} - /** * Stores the current page in the cache. * diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 8b0b9e43ec04fa47dcab3398ea4831bccd13b84e..ebe1cd5cbda56a457648d3c8346f4ea4de537d3c 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_FULL)) { + if ($fatal && drupal_get_bootstrap_phase() < DRUPAL_BOOTSTRAP_CODE) { // The installer initializes a maintenance theme at the earliest possible // point in time already. Do not unset that. if (!$is_installer) { @@ -244,9 +244,9 @@ function _drupal_log_error($error, $fatal = FALSE) { /** * Returns the current error level. * - * This function should only be used to get the current error level pre - * DRUPAL_BOOTSTRAP_KERNEL or before Drupal is installed. In all other - * situations the following code is preferred: + * This function should only be used to get the current error level prior to + * DRUPAL_BOOTSTRAP_KERNEL or before Drupal is installed. In all other situations + * the following code is preferred: * @code * \Drupal::config('system.logging')->get('error_level'); * @endcode diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index b00d23b21d3829dbd7f88690dbb1edd88d363f2e..ed2fc975bb8f9c01a95cf2f2857b2cf53fe3d4e8 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -272,12 +272,6 @@ function install_begin_request(&$install_state) { // Allow command line scripts to override server variables used by Drupal. require_once __DIR__ . '/bootstrap.inc'; - // Initialize conf_path(). - // This primes the site path to be used during installation. By not requiring - // settings.php, a bare site folder can be prepared in the /sites directory, - // which will be used for installing Drupal. - conf_path(FALSE); - // If the hash salt leaks, it becomes possible to forge a valid testing user // agent, install a new copy of Drupal, and take over the original site. // The user agent header is used to pass a database prefix in the request when @@ -288,7 +282,8 @@ function install_begin_request(&$install_state) { exit; } - drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + $site_path = DrupalKernel::findSitePath($request, FALSE); + Settings::initialize($site_path); // Ensure that procedural dependencies are loaded as early as possible, // since the error/exception handlers depend on them. @@ -356,27 +351,24 @@ function install_begin_request(&$install_state) { $environment = 'prod'; } - $kernel = new DrupalKernel($environment, drupal_classloader(), FALSE); + $kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), $environment, FALSE); + $kernel->setSitePath($site_path); $kernel->boot(); - - // Enter the request scope and add the Request. - // @todo Remove this after converting all installer screens into controllers. $container = $kernel->getContainer(); - $container->enterScope('request'); - $container->set('request', $request, 'request'); - $container->get('request_stack')->push($request); // Register the file translation service. if (isset($GLOBALS['config']['locale.settings']['translation.path'])) { $directory = $GLOBALS['config']['locale.settings']['translation.path']; } else { - $directory = conf_path() . '/files/translations'; + $directory = $site_path . '/files/translations'; } $container->set('string_translator.file_translation', new FileTranslation($directory)); $container->get('string_translation') ->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'])); @@ -1455,7 +1447,6 @@ function install_load_profile(&$install_state) { * An array of information about the current installation state. */ function install_bootstrap_full() { - drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); \Drupal::service('session_manager')->initialize(); } diff --git a/core/includes/install.inc b/core/includes/install.inc index 67e41f66225b8d8247f9c00424109b922c5875ce..ce7db8771e0f1e672b74a7668f754e56c8f625f8 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -9,8 +9,6 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\OpCodeCache; use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Database\Database; -use Drupal\Core\DrupalKernel; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Site\Settings; @@ -634,24 +632,29 @@ function drupal_verify_profile($install_state) { * to set the default language. */ function drupal_install_system($install_state) { - // Boot a new kernel into a regular production environment. - $request = \Drupal::hasRequest() ? \Drupal::request() : FALSE; - + // Remove the service provider of the early installer. unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']); - $kernel = new DrupalKernel('prod', drupal_classloader(), FALSE); - $kernel->boot(); - if ($request) { - $kernel->getContainer()->enterScope('request'); - $kernel->getContainer()->set('request', $request, 'request'); - $kernel->getContainer()->get('request_stack')->push($request); - } + // Reboot into a full production environment to continue the installation. + $kernel = \Drupal::service('kernel'); + $kernel->shutdown(); + $kernel->prepareLegacyRequest(\Drupal::request()); // Install base system configuration. \Drupal::service('config.installer')->installDefaultConfig('core', 'core'); // Install System module. - \Drupal::moduleHandler()->install(array('system'), FALSE); + $kernel->getContainer()->get('module_handler')->install(array('system'), FALSE); + + // DrupalKernel::prepareLegacyRequest() above calls into + // DrupalKernel::bootCode(), which primes file_get_stream_wrappers()'s static + // list of custom stream wrappers that are based on the currently enabled + // list of modules (none). + // @todo Custom stream wrappers of a new module have to be registered as soon + // as the module is installed/enabled. Fix either ModuleHandler::install() + // and/or DrupalKernel::updateModules(). + // @see https://drupal.org/node/2028109 + drupal_static_reset('file_get_stream_wrappers'); // Ensure default language is saved. if (isset($install_state['parameters']['langcode'])) { diff --git a/core/includes/path.inc b/core/includes/path.inc index 60437495e6882263616be2e9ac699de14da5cbad..a10f0e73f39d830168944b5b22e086ba4bc088d4 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -74,9 +74,7 @@ function drupal_match_path($path, $patterns) { * - http://example.com/path/alias (which is a path alias for node/306) returns * "node/306" as opposed to the path alias. * - * This function is available only after DRUPAL_BOOTSTRAP_FULL. - * - * @return + * @return string * The current Drupal URL path. * * @see request_path() diff --git a/core/includes/schema.inc b/core/includes/schema.inc index a386e62d1c163e2a9387e2287d2a87764cb35518..f181c5467e3048ace5fb4e104b1c7f60aa4d30a2 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) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) { + if (!empty($schema)) { \Drupal::cache()->set('schema', $schema, Cache::PERMANENT); } } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 2b06429a15e4414342e0951af30037484bbab63e..7e92f959865aabcf9f3389295fedc412800a1656 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -24,6 +24,7 @@ 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 @@ -91,8 +92,11 @@ 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() { +function drupal_theme_initialize(Request $request = NULL) { global $theme, $theme_key; // If $theme is already set, assume the others are set, too, and do nothing @@ -105,7 +109,9 @@ function drupal_theme_initialize() { // @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. - $request = \Drupal::request(); + if (!$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 66031666ada56665427b0a51d7b5ecbe5fd74c0b..135ab2473c18858c85c8e0d81f4334ab5fff0539 100644 --- a/core/includes/utility.inc +++ b/core/includes/utility.inc @@ -8,6 +8,9 @@ use Drupal\Component\Utility\Variable; use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\Cache\Cache; +use Drupal\Core\DrupalKernel; +use Symfony\Component\HttpFoundation\Request; +use Composer\Autoload\ClassLoader; /** * Drupal-friendly var_export(). @@ -32,34 +35,39 @@ function drupal_var_export($var, $prefix = '') { * * Requires DRUPAL_BOOTSTRAP_CONFIGURATION. * + * @param \Composer\Autoload\ClassLoader $classloader + * The classloader. + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * * @see rebuild.php */ -function drupal_rebuild() { +function drupal_rebuild(ClassLoader $classloader, Request $request) { // Remove Drupal's error and exception handlers; they rely on a working // service container and other subsystems and will only cause a fatal error // that hides the actual error. restore_error_handler(); restore_exception_handler(); - // drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL) will build a new kernel. This - // comes before DRUPAL_BOOTSTRAP_PAGE_CACHE. + // Force kernel to rebuild container. PhpStorageFactory::get('service_container')->deleteAll(); PhpStorageFactory::get('twig')->deleteAll(); - // Disable the page cache. - drupal_page_is_cacheable(FALSE); - // Bootstrap up to where caches exist and clear them. - drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_CACHE); + $kernel = new DrupalKernel('prod', $classloader); + $kernel->prepareLegacyRequest($request); + foreach (Cache::getBins() as $bin) { $bin->deleteAll(); } - drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + // Disable the page cache. + drupal_page_is_cacheable(FALSE); + drupal_flush_all_caches(); // Restore Drupal's error and exception handlers. - // @see _drupal_bootstrap_configuration() + // @see \Drupal\Core\DrupalKernel::boot() set_error_handler('_drupal_error_handler'); set_exception_handler('_drupal_exception_handler'); } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index fe43d8647a4e96dcd977347953941b068ee6297d..135ab43fa8c2207c117afb151b8985ca6c4f63e0 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -7,21 +7,25 @@ namespace Drupal\Core; -use Drupal\Core\PhpStorage\PhpStorageFactory; +use Drupal\Component\Utility\Crypt; +use Drupal\Component\Utility\Timer; +use Drupal\Component\Utility\Unicode; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Config\BootstrapConfigStorageFactory; use Drupal\Core\Config\NullStorage; -use Drupal\Core\CoreServiceProvider; +use Drupal\Core\Database\Database; 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 Symfony\Component\Config\Loader\LoaderInterface; +use Drupal\Core\PhpStorage\PhpStorageFactory; +use Drupal\Core\Site\Settings; 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; @@ -61,7 +65,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { * * @var bool */ - protected $booted; + protected $booted = FALSE; /** * Holds the list of enabled modules. @@ -163,15 +167,79 @@ 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'. Used by - * Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use - * this value currently. Pass 'prod'. + * String indicating the environment, e.g. 'prod' or 'dev'. * @param \Composer\Autoload\ClassLoader $class_loader - * (optional) The classloader is only used if $storage is not given or + * (optional) The class loader 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. @@ -184,24 +252,156 @@ 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; + return $this; } + + // 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; - if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) { - watchdog('DrupalKernel', 'Container cannot be written to disk'); - } + + return $this; } /** @@ -213,15 +413,103 @@ public function shutdown() { } $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} */ @@ -246,14 +534,14 @@ public function discoverServiceProviders() { $this->classLoaderAddMultiplePsr4($this->getModuleNamespacesPsr4($module_filenames)); // Load each module's serviceProvider class. - foreach ($this->moduleList as $module => $weight) { + foreach ($module_filenames as $module => $filename) { $camelized = ContainerBuilder::camelize($module); $name = "{$camelized}ServiceProvider"; $class = "Drupal\\{$module}\\{$name}"; if (class_exists($class)) { $this->serviceProviderClasses['app'][$module] = $class; } - $filename = dirname($module_filenames[$module]) . "/$module.services.yml"; + $filename = dirname($filename) . "/$module.services.yml"; if (file_exists($filename)) { $this->serviceYamls['app'][$module] = $filename; } @@ -270,7 +558,7 @@ public function discoverServiceProviders() { if (!empty($GLOBALS['conf']['container_yamls'])) { $this->serviceYamls['site'] = $GLOBALS['conf']['container_yamls']; } - if (file_exists($site_services_yml = conf_path() . '/services.yml')) { + if (file_exists($site_services_yml = $this->getSitePath() . '/services.yml')) { $this->serviceYamls['site'][] = $site_services_yml; } } @@ -298,14 +586,27 @@ public function terminate(Request $request, Response $response) { /** * {@inheritdoc} */ - public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = TRUE) { - if (FALSE === $this->booted) { - $this->boot(); - } - + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { + $this->boot(); + $this->preHandle($request); return $this->getHttpKernel()->handle($request, $type, $catch); } + /** + * {@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; + } + /** * Returns module data on the filesystem. * @@ -469,6 +770,215 @@ protected function initializeContainer() { \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. */ @@ -634,7 +1144,7 @@ protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) /** * Gets a http kernel from the container * - * @return HttpKernel + * @return \Symfony\Component\HttpKernel\HttpKernelInterface */ 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 1e2ed070fddcd2a719580a5a53cf0c6a66000110..08d007c49135cb00a4b119c2f7a58be697a4d626 100644 --- a/core/lib/Drupal/Core/DrupalKernelInterface.php +++ b/core/lib/Drupal/Core/DrupalKernelInterface.php @@ -8,6 +8,7 @@ namespace Drupal\Core; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; /** * The interface for DrupalKernel, the core of Drupal. @@ -19,6 +20,8 @@ interface DrupalKernelInterface extends HttpKernelInterface { /** * Boots the current kernel. + * + * @return $this */ public function boot(); @@ -53,6 +56,22 @@ public function getServiceProviders($origin); */ public function getContainer(); + /** + * Set the current site path. + * + * @param $path + * The current site path. + */ + public function setSitePath($path); + + /** + * Get the site path. + * + * @return string + * The current site path. + */ + public function getSitePath(); + /** * Updates the kernel's list of modules to the new list. * @@ -65,4 +84,28 @@ public function getContainer(); * List of module filenames, keyed by module name. */ public function updateModules(array $module_list, array $module_filenames = array()); + + /** + * Attempts to serve a page from the cache. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * + * @return $this + */ + public function handlePageCache(Request $request); + + /** + * Prepare the kernel for handling a request without handling the request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * + * @return $this + * + * @deprecated 8.x + * Only used by legacy front-controller scripts. + */ + public function prepareLegacyRequest(Request $request); + } diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php deleted file mode 100644 index aa7774e90ffab7696c570ecfb70d1d176199ba48..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php +++ /dev/null @@ -1,64 +0,0 @@ -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 new file mode 100644 index 0000000000000000000000000000000000000000..37ddfbb05a7ee0c6bd0a0ee6657bf73a5012470a --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php @@ -0,0 +1,47 @@ +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 3ac0f03328cf03232b138819ef67aae85c33e481..e2d57d2b6e975ced8b01e1c0bc354faf11a64f94 100644 --- a/core/lib/Drupal/Core/Site/Settings.php +++ b/core/lib/Drupal/Core/Site/Settings.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Site; +use Drupal\Core\Database\Database; + /** * Read only settings that are initialized with the class. * @@ -80,6 +82,31 @@ public static function getAll() { return self::$instance->storage; } + /** + * Bootstraps settings.php and the Settings singleton. + * + * @param string $site_path + * The current site path. + */ + public static function initialize($site_path) { + // Export these settings.php variables to the global namespace. + global $base_url, $cookie_domain, $config_directories, $config; + $settings = array(); + $config = array(); + $databases = array(); + + // Make conf_path() available as local variable in settings.php. + if (is_readable(DRUPAL_ROOT . '/' . $site_path . '/settings.php')) { + require DRUPAL_ROOT . '/' . $site_path . '/settings.php'; + } + + // Initialize Database. + Database::setMultipleConnectionInfo($databases); + + // Initialize Settings. + new Settings($settings); + } + /** * Gets a salt useful for hardening against SQL injection. * diff --git a/core/lib/Drupal/Core/Test/TestKernel.php b/core/lib/Drupal/Core/Test/TestKernel.php index 12938ce73685c1b08a10eacaa0e5290518c28a4c..6144dbf7cc555b65c2521891c7ffdfc45055efa8 100644 --- a/core/lib/Drupal/Core/Test/TestKernel.php +++ b/core/lib/Drupal/Core/Test/TestKernel.php @@ -8,46 +8,28 @@ namespace Drupal\Core\Test; use Drupal\Core\DrupalKernel; -use Drupal\Core\Extension\Extension; -use Drupal\Core\Installer\InstallerServiceProvider; +use Symfony\Component\HttpFoundation\Request; use Composer\Autoload\ClassLoader; /** - * Kernel for run-tests.sh. + * Kernel to mock requests to test simpletest. */ class TestKernel extends DrupalKernel { - /** - * Constructs a TestKernel. - * - * @param \Composer\Autoload\ClassLoader $class_loader - * The classloader. - */ - 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 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'; + 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); } } diff --git a/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/core/lib/Drupal/Core/Test/TestRunnerKernel.php new file mode 100644 index 0000000000000000000000000000000000000000..7b04673c3fd500a69a1d897230bdc8ebd55c532e --- /dev/null +++ b/core/lib/Drupal/Core/Test/TestRunnerKernel.php @@ -0,0 +1,85 @@ +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 e4231a3e515a73c762344bcb9db21f30eb2603f9..ec00177518aa5988339ae07c249e9ee6adcabbef 100644 --- a/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php +++ b/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php @@ -122,6 +122,8 @@ function testLoading() { // 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'; @@ -129,12 +131,14 @@ function testLoading() { $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 84c05cfd7c59d0c09e1863b6ec399c26287bd20c..0cb7d3ca020db5c0ae66d17ba63e092fe28e73fb 100644 --- a/core/modules/dblog/src/Tests/DbLogTest.php +++ b/core/modules/dblog/src/Tests/DbLogTest.php @@ -138,7 +138,7 @@ private function generateLogEntries($count, $type = 'custom', $severity = WATCHD 'user' => $this->big_user, 'uid' => $this->big_user->id(), 'request_uri' => $base_root . request_uri(), - 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', + 'referer' => \Drupal::request()->server->get('HTTP_REFERER'), 'ip' => '127.0.0.1', 'timestamp' => REQUEST_TIME, ); @@ -421,7 +421,7 @@ protected function testDBLogAddAndClear() { 'user' => $this->big_user, 'uid' => $this->big_user->id(), 'request_uri' => $base_root . request_uri(), - 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', + 'referer' => \Drupal::request()->server->get('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 0f976e07c290974376b1a8fc1c575e2915c8ccc6..2af6b923b680526996d40884d90df4434bf9da94 100644 --- a/core/modules/field/src/Tests/FieldHelpTest.php +++ b/core/modules/field/src/Tests/FieldHelpTest.php @@ -56,6 +56,7 @@ public function testFieldHelp() { // 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 e735ce3fe2795e607b1d8c0257811c3286a54b8b..6fa42f9fcc57c01937588f002d76892fb74f35b3 100644 --- a/core/modules/simpletest/src/InstallerTestBase.php +++ b/core/modules/simpletest/src/InstallerTestBase.php @@ -7,7 +7,10 @@ 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. @@ -109,7 +112,8 @@ protected function setUp() { $this->setUpSite(); // Import new settings.php written by the installer. - drupal_settings_initialize(); + $request = Request::createFromGlobals(); + Settings::initialize(DrupalKernel::findSitePath($request)); foreach ($GLOBALS['config_directories'] as $type => $path) { $this->configDirectories[$type] = $path; } @@ -121,12 +125,14 @@ protected function setUp() { // WebTestBase::tearDown() will delete the entire test site directory. // Not using File API; a potential error must trigger a PHP warning. chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777); - - $this->rebuildContainer(); + $this->kernel = DrupalKernel::createFromRequest($request, drupal_classloader(), 'prod', FALSE); + $this->kernel->prepareLegacyRequest($request); + $this->container = $this->kernel->getContainer(); + $config = $this->container->get('config.factory'); // Manually configure the test mail collector implementation to prevent // tests from sending out e-mails and collect them in state instead. - \Drupal::config('system.mail') + $config->get('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 409c1b95a8ea60a1cc451800f5b0a726848f84c5..7a76b5be8713a433d659fea96f0b194ce7a0be1f 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -13,6 +13,7 @@ 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; @@ -131,23 +132,31 @@ protected function setUp() { // Create and set new configuration directories. $this->prepareConfigDirectories(); - // Build a minimal, partially mocked environment for unit tests. - $this->containerBuild(\Drupal::getContainer()); - // Make sure it survives kernel rebuilds. + // Add this test class as a service provider. + // @todo Remove the indirection; implement ServiceProviderInterface instead. $GLOBALS['conf']['container_service_providers']['TestServiceProvider'] = 'Drupal\simpletest\TestServiceProvider'; - \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); + // 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)); $this->kernel->boot(); - $request = Request::create('/'); + // 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(); $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. @@ -404,8 +413,7 @@ protected function enableModules(array $modules) { // 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. - $module_handler = $this->container->get('module_handler'); - $module_handler->reload(); + $this->container->get('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 023cf8e53bcc8f531227ef6a20beb1adba4b01e5..b133447f77d398a7c5ecaa07bf12ac16635bdba1 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -15,7 +15,6 @@ 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; @@ -653,7 +652,7 @@ protected function assertIdenticalObject($object1, $object2, $message = '', $gro * TRUE if the assertion succeeded, FALSE otherwise. * * @see TestBase::prepareEnvironment() - * @see _drupal_bootstrap_configuration() + * @see \Drupal\Core\DrupalKernel::bootConfiguration() */ 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 ec50d4dd70f367bc02f8a2cfd79c62c5dff85e34..e75411d62bbb6ea24d34126ef7542c07d9ce811c 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -20,6 +20,7 @@ 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; @@ -173,6 +174,8 @@ abstract class WebTestBase extends TestBase { /** * The kernel used in this test. + * + * @var \Drupal\Core\DrupalKernel */ protected $kernel; @@ -844,17 +847,11 @@ protected function setUp() { ); $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; } @@ -867,7 +864,11 @@ protected function setUp() { // Not using File API; a potential error must trigger a PHP warning. chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777); - $this->rebuildContainer(); + $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'); // Manually create and configure private and temporary files directories. // While these could be preset/enforced in settings.php like the public @@ -875,7 +876,7 @@ protected function setUp() { // 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); - \Drupal::config('system.file') + $config->get('system.file') ->set('path.private', $this->private_files_directory) ->set('path.temporary', $this->temp_files_directory) ->save(); @@ -884,7 +885,7 @@ protected function setUp() { // 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. - \Drupal::config('system.mail') + $config->get('system.mail') ->set('interface.default', 'test_mail_collector') ->save(); @@ -892,10 +893,10 @@ protected function setUp() { // environment optimizations for all tests to avoid needless overhead and // ensure a sane default experience for test authors. // @see https://drupal.org/node/2259167 - \Drupal::config('system.logging') + $config->get('system.logging') ->set('error_level', 'verbose') ->save(); - \Drupal::config('system.performance') + $config->get('system.performance') ->set('css.preprocess', FALSE) ->set('js.preprocess', FALSE) ->save(); @@ -915,22 +916,20 @@ protected function setUp() { } if ($modules) { $modules = array_unique($modules); - $success = \Drupal::moduleHandler()->install($modules, TRUE); + $success = $container->get('module_handler')->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_bootstrap_code() - // @see _drupal_bootstrap_full() + // @see \Drupal\Core\DrupalKernel::bootCode() // @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. @@ -1085,29 +1084,18 @@ protected function writeCustomTranslations() { * tests can invoke this workaround when requiring services from newly * enabled modules to be immediately available in the same request. */ - protected function rebuildContainer($environment = 'prod') { - // Preserve the request object after the container rebuild. + protected function rebuildContainer() { + // Maintain the current global request object. $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'); - } - $this->kernel = new DrupalKernel($environment, drupal_classloader(), TRUE, FALSE); - $this->kernel->boot(); + // Rebuild the kernel and bring it back to a fully bootstrapped state. + $this->kernel->shutdown(); + $this->kernel->prepareLegacyRequest($request); + // DrupalKernel replaces the container in \Drupal::getContainer() with a // different object, so we need to replace the instance on this test class. - $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 = $this->kernel->getContainer(); + $this->container->get('current_user')->setAccount(\Drupal::currentUser()); // The request context is normally set by the router_listener from within @@ -1429,7 +1417,7 @@ protected function curlClose() { * TRUE if this test was instantiated in a request within the test site, * FALSE otherwise. * - * @see _drupal_bootstrap_configuration() + * @see \Drupal\Core\DrupalKernel::bootConfiguration() */ protected function isInChildSite() { return DRUPAL_TEST_IN_CHILD_SITE; diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php index 85864e10b58749e5cdeaa9f43ad58c6a728af15d..4d2004646ae677a4afb40f9c17e29baaae234daf 100644 --- a/core/modules/statistics/statistics.php +++ b/core/modules/statistics/statistics.php @@ -5,15 +5,22 @@ * Handles counts of node views via AJAX with minimal bootstrap. */ -// Change the directory to the Drupal root. +use Drupal\Core\DrupalKernel; +use Symfony\Component\HttpFoundation\Request; + chdir('../../..'); -// 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); +$autoloader = require_once dirname(dirname(__DIR__)) . '/vendor/autoload.php'; + +$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod'); +$kernel->boot(); + +$views = $kernel->getContainer() + ->get('config.factory') + ->get('statistics.settings') + ->get('count_content_views'); -if (\Drupal::config('statistics.settings')->get('count_content_views')) { +if ($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 17e4b4b9f030ec77e7d6176802edaa9771e6a46c..9ec9df936dcc495990c408bdc93fea6f592743d3 100644 --- a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php +++ b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php @@ -10,12 +10,18 @@ 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', @@ -38,25 +44,60 @@ function setUp() { '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. - $module_enabled = array( + $modules_enabled = array( 'system' => 'system', 'user' => 'user', ); - $kernel = new DrupalKernel('testing', $classloader); - $kernel->updateModules($module_enabled); - $kernel->boot(); + + $request = Request::createFromGlobals(); + $this->getTestKernel($request, $modules_enabled) + // Trigger Kernel dump. + ->getContainer(); + // Instantiate it a second time and we should get the compiled Container // class. - $kernel = new DrupalKernel('testing', $classloader); - $kernel->boot(); + $kernel = $this->getTestKernel($request); $container = $kernel->getContainer(); $refClass = new \ReflectionClass($container); $is_compiled_container = @@ -66,25 +107,24 @@ function testCompileDIC() { // 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($module_enabled), $module_list); + $this->assertEqual(array_values($modules_enabled), $module_list); // Now use the read-only storage implementation, simulating a "production" // environment. - $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(); + $container = $this->getTestKernel($request, NULL, TRUE) + ->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($module_enabled), $module_list); + $this->assertEqual(array_values($modules_enabled), $module_list); + // Test that our synthetic services are there. $classloader = $container->get('class_loader'); $refClass = new \ReflectionClass($classloader); @@ -98,25 +138,26 @@ function testCompileDIC() { // Add another module so that we can test that the new module's bundle is // registered to the new container. - $module_enabled['service_provider_test'] = 'service_provider_test'; - $kernel = new DrupalKernel('testing', $classloader); - $kernel->updateModules($module_enabled); - $kernel->boot(); + $modules_enabled['service_provider_test'] = 'service_provider_test'; + $this->getTestKernel($request, $modules_enabled, TRUE); + // Instantiate it a second time and we should still get a ContainerBuilder // class because we are using the read-only PHP storage. - $kernel = new DrupalKernel('testing', $classloader); - $kernel->updateModules($module_enabled); - $kernel->boot(); + $kernel = $this->getTestKernel($request, $modules_enabled, TRUE); $container = $kernel->getContainer(); + $refClass = new \ReflectionClass($container); $is_container_builder = $refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder'); - $this->assertTrue($is_container_builder); + $this->assertTrue($is_container_builder, 'Container is a builder'); + // Assert that the new module's bundle was registered to the new container. - $this->assertTrue($container->has('service_provider_test_class')); + $this->assertTrue($container->has('service_provider_test_class'), 'Container has test service'); + // 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 4faec44b5673d1b360a545800493dac14008fd31..58e62bc2d272f3b325959158785d31325d717d81 100644 --- a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php +++ b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php @@ -102,6 +102,8 @@ public function testFieldItemAttributes() { // 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 4383658ba25018e5854fa3ec298d72b0529c9a0e..58258955b86a0459c66d06456a268d2f474f38f0 100644 --- a/core/modules/system/src/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/src/Tests/Menu/MenuRouterTest.php @@ -312,6 +312,7 @@ protected function doTestMenuOptionalPlaceholders() { 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 9ba2e5c9f0c485796e2daf38d864d8a8cd2d0ade..773cfa0b3f21edd31d8020801b1e092e1dab8059 100644 --- a/core/modules/system/src/Tests/System/ScriptTest.php +++ b/core/modules/system/src/Tests/System/ScriptTest.php @@ -45,6 +45,14 @@ public function testPasswordHashSh() { * 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 202ddd2a44bf45d42b6ffda14f58728d7166fea7..e5c33a174e8532bff8416b512d65d6aade1fadb1 100644 --- a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php +++ b/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php @@ -69,6 +69,7 @@ function testGeneralSuggestionsAlter() { // 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().'); } @@ -90,6 +91,7 @@ function testTemplateSuggestionsAlter() { // 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().'); } @@ -113,6 +115,7 @@ function testSpecificSuggestionsAlter() { // 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.'); @@ -135,6 +138,7 @@ function testThemeFunctionSuggestionsAlter() { // 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.'); } @@ -151,6 +155,7 @@ public function testSuggestionsAlterInclude() { // 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'); @@ -169,6 +174,7 @@ function testExecutionOrder() { ->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 662031f79b456b4dc7117a09c3c69beb630e2e05..33869799e316b1e398610774723187c985be3702 100644 --- a/core/modules/system/tests/http.php +++ b/core/modules/system/tests/http.php @@ -5,19 +5,29 @@ * 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 $key => $value) { - $_SERVER[$key] = str_replace('core/modules/system/tests/http.php', 'index.php', $value); - $_SERVER[$key] = str_replace('https://', 'http://', $_SERVER[$key]); +foreach ($_SERVER as &$value) { + $value = str_replace('core/modules/system/tests/http.php', 'index.php', $value); + $value = str_replace('https://', 'http://', $value); } -// 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); +$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); diff --git a/core/modules/system/tests/https.php b/core/modules/system/tests/https.php index 247e6e515e0a86ae73fca91ba09377bc48a42af2..00a6b13d6155ee7bb55e0f4fe841913ded4329bd 100644 --- a/core/modules/system/tests/https.php +++ b/core/modules/system/tests/https.php @@ -8,18 +8,28 @@ * 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 $key => $value) { - $_SERVER[$key] = str_replace('core/modules/system/tests/https.php', 'index.php', $value); - $_SERVER[$key] = str_replace('http://', 'https://', $_SERVER[$key]); +foreach ($_SERVER as &$value) { + $value = str_replace('core/modules/system/tests/https.php', 'index.php', $value); + $value = str_replace('http://', 'https://', $value); } -// 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); +$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); diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 3599c5944b1a472204b8ab683918118601291ba7..f46770abe1f9ef2246766dd6571acd42cad330b5 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_bootstrap_page_cache(). + * done by \Drupal\Core\DrupalKernel::handlePageCache(). * * @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_bootstrap_page_cache() + // @see \Drupal\Core\DrupalKernel::handlePageCache() $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 8016987f38df4aa3610befac5a798e0de692d625..c6fefcf965367c2e5b4da49802a2306becceac7e 100644 --- a/core/modules/user/src/Tests/UserLoginTest.php +++ b/core/modules/user/src/Tests/UserLoginTest.php @@ -135,6 +135,7 @@ function testPasswordRehashOnLogin() { // 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 01feff62eda24643d3403afc7dc3cb3adc5b4c6b..315ef98d620845847d9e87f7713992a4cc3b67f4 100644 --- a/core/modules/views_ui/src/Tests/PreviewTest.php +++ b/core/modules/views_ui/src/Tests/PreviewTest.php @@ -34,6 +34,8 @@ public static function getInfo() { */ 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 3e8ed1282c152a9ef231eca9041a4231bb689487..f7acbcea8a7c145d49c8172cd17cd85bb3ca848d 100644 --- a/core/rebuild.php +++ b/core/rebuild.php @@ -11,25 +11,30 @@ */ 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('..'); -require_once __DIR__ . '/vendor/autoload.php'; -require_once __DIR__ . '/includes/bootstrap.inc'; +$autoloader = require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/includes/utility.inc'; -drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); +$request = Request::createFromGlobals(); +// Manually resemble early bootstrap of DrupalKernel::boot(). +require_once __DIR__ . '/includes/bootstrap.inc'; +DrupalKernel::bootEnvironment(); +Settings::initialize(DrupalKernel::findSitePath($request)); if (Settings::get('rebuild_access', FALSE) || - (isset($_GET['token'], $_GET['timestamp']) && - ((REQUEST_TIME - $_GET['timestamp']) < 300) && - ($_GET['token'] === Crypt::hmacBase64($_GET['timestamp'], Settings::get('hash_salt'))) + ($request->get('token') && $request->get('timestamp') && + ((REQUEST_TIME - $request->get('timestamp')) < 300) && + ($request->get('token') === Crypt::hmacBase64($request->get('timestamp'), Settings::get('hash_salt'))) )) { - drupal_rebuild(); + drupal_rebuild($autoloader, $request); drupal_set_message('Cache rebuild complete.'); } - -header('Location: ' . $GLOBALS['base_url']); +$base_path = dirname(dirname($request->getBaseUrl())); +header('Location: ' . $base_path); diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh index f80d75c6ca8a51c491b36393baf24068e246703b..ee3c10246dc295e8fc34c1547346bda62c4e88f6 100755 --- a/core/scripts/password-hash.sh +++ b/core/scripts/password-hash.sh @@ -9,6 +9,7 @@ */ 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. @@ -56,14 +57,10 @@ // Password list to be processed. $passwords = $_SERVER['argv']; -$core = dirname(__DIR__); -require_once $core . '/vendor/autoload.php'; -require_once $core . '/includes/bootstrap.inc'; +$autoloader = require __DIR__ . '/../vendor/autoload.php'; -// Bootstrap the code so we have the container. -drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); - -$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE); +$request = Request::createFromGlobals(); +$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', 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 9e7dc03ddb0ff4563e266195413803163d406730..bcfd2b6fc16add9b80089f219e18d0957aaebcb0 100755 --- a/core/scripts/rebuild_token_calculator.sh +++ b/core/scripts/rebuild_token_calculator.sh @@ -7,7 +7,9 @@ */ 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. @@ -16,11 +18,11 @@ return; } -$core = dirname(__DIR__); -require_once $core . '/vendor/autoload.php'; -require_once $core . '/includes/bootstrap.inc'; +require __DIR__ . '/../vendor/autoload.php'; +require_once __DIR__ . '/../includes/bootstrap.inc'; -drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); +$request = Request::createFromGlobals(); +Settings::initialize(DrupalKernel::findSitePath($request)); $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 8eff2a29805ee2852bc41daa3e936463f27113df..4679325dcf38239c6a76efc9f47b2c22ae992946 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\TestKernel; +use Drupal\Core\Test\TestRunnerKernel; use Symfony\Component\HttpFoundation\Request; -require_once __DIR__ . '/../vendor/autoload.php'; +$autoloader = require_once __DIR__ . '/../vendor/autoload.php'; const SIMPLETEST_SCRIPT_COLOR_PASS = 32; // Green. const SIMPLETEST_SCRIPT_COLOR_FAIL = 31; // Red. @@ -26,7 +26,10 @@ } simpletest_script_init(); -simpletest_script_bootstrap(); + +$request = Request::createFromGlobals(); +$kernel = TestRunnerKernel::createFromRequest($request, $autoloader); +$kernel->prepareLegacyRequest($request); if ($args['execute-test']) { simpletest_script_setup_database(); @@ -353,50 +356,6 @@ 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 72c5d5bc930047ca11d5bb6f9bb995134fd542d2..99fe34d05198ddaf71314addbbdb2c2686c388cd 100644 --- a/core/tests/bootstrap.php +++ b/core/tests/bootstrap.php @@ -87,7 +87,7 @@ function drupal_phpunit_register_extension_dirs(Composer\Autoload\ClassLoader $l // Set sane locale settings, to ensure consistent string, dates, times and // numbers handling. -// @see drupal_environment_initialize() +// @see \Drupal\Core\DrupalKernel::bootEnvironment() 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 b6accf3a5f42d97d5682de51bc3bebd58e816167..9147e9ef85f22c6ccd915b7f5f4aea19e07446a0 100644 --- a/core/update.php +++ b/core/update.php @@ -25,7 +25,7 @@ // Change the directory to the Drupal root. chdir('..'); -require_once __DIR__ . '/vendor/autoload.php'; +$autoloader = 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,18 +297,17 @@ function update_task_list($active = NULL) { // 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); -// Bootstrap the database. -require_once __DIR__ . '/includes/database.inc'; +$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(); // 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 @@ -321,27 +320,9 @@ function update_task_list($active = NULL) { } } -// 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); +$kernel->prepareLegacyRequest($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' @@ -378,7 +359,6 @@ function update_task_list($active = NULL) { 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 a453ecc1f910c0b56de4a2a020f6290bbef4b25f..6bff08359f7db56e2973166a0a93e8c15dc2356e 100644 --- a/index.php +++ b/index.php @@ -8,13 +8,22 @@ * 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; -require_once __DIR__ . '/core/vendor/autoload.php'; -require_once __DIR__ . '/core/includes/bootstrap.inc'; +$autoloader = require_once __DIR__ . '/core/vendor/autoload.php'; try { - drupal_handle_request(); + + $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); } 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 587a9cc1a3c1db79e08d39ffc3ebad9db644551a..7781c0c14d21958924b9c860b074fb5bdc835a3e 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -245,7 +245,7 @@ * directory and reverse proxy address, and temporary configuration, such as * turning on Twig debugging and security overrides. * - * @see \Drupal\Component\Utility\Settings::get() + * @see \Drupal\Core\Site\Settings::get() */ /** @@ -490,8 +490,8 @@ * 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_environment_initialize() in core/includes/bootstrap.inc for - * required runtime settings and the .htaccess file for non-runtime settings. + * See \Drupal\Core\DrupalKernel::bootEnvironment() 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 3336222b476f6ce8268007bb80344c3b39bf8b0b..b71c37cdcb7ac5f48551cca1bf40c2a3f26ac764 100644 --- a/web.config +++ b/web.config @@ -61,8 +61,7 @@ --> + to index.php. -->