diff --git a/core/core.services.yml b/core/core.services.yml index f938145..3ce5a43 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -553,12 +553,13 @@ services: site.path: class: SplString factory_service: 'site.path.factory' - factory_method: 'get' + factory_method: 'getForRequestStack' + arguments: ['@request_stack'] tags: - { name: parameter_service } site.path.factory: class: Drupal\Core\SitePathFactory - arguments: ['@kernel'] + arguments: ['@app.root'] public: false controller_resolver: class: Drupal\Core\Controller\ControllerResolver diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 8fba794..b7de5d7 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -313,7 +313,9 @@ function install_begin_request($class_loader, &$install_state) { exit; } - $site_path = DrupalKernel::findSitePath($request, FALSE); + // @todo: Come up with a better way to figure out DRUPAL_ROOT. + $site_path_factory = new SitePathFactory(DRUPAL_ROOT); + $site_path = $site_path_factory->findSitePath($request, FALSE); Settings::initialize(dirname(dirname(__DIR__)), $site_path, $class_loader); // Ensure that procedural dependencies are loaded as early as possible, diff --git a/core/includes/utility.inc b/core/includes/utility.inc index ff65dc8..4a94357 100644 --- a/core/includes/utility.inc +++ b/core/includes/utility.inc @@ -9,6 +9,7 @@ use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\Cache\Cache; use Drupal\Core\DrupalKernel; +use Drupal\Core\SitePathFactory; use Symfony\Component\HttpFoundation\Request; use Composer\Autoload\ClassLoader; @@ -35,7 +36,10 @@ function drupal_rebuild(ClassLoader $class_loader, Request $request) { // Bootstrap up to where caches exist and clear them. $kernel = new DrupalKernel('prod', $class_loader); - $kernel->setSitePath(DrupalKernel::findSitePath($request)); + // @todo: Come up with a better way to figure out DRUPAL_ROOT. + $site_path_factory = new SitePathFactory(DRUPAL_ROOT); + $site_path = $site_path_factory->getForRequest($request); + $kernel->setSitePath($site_path_factory()); $kernel->prepareLegacyRequest($request); foreach (Cache::getBins() as $bin) { diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 557dfd1..d578b24 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -224,8 +224,9 @@ public static function createFromRequest(Request $request, $class_loader, $envir static::bootEnvironment(); // Get our most basic settings setup. - $site_path = static::findSitePath($request); - $kernel->setSitePath($site_path); + // @todo: Come up with a better way to figure out DRUPAL_ROOT. + $site_path_factory = new SitePathFactory(DRUPAL_ROOT); + $site_path = $site_path_factory->getForRequest($request); Settings::initialize(dirname($core_root), $site_path, $class_loader); // Initialize our list of trusted HTTP Host headers to protect against @@ -285,98 +286,6 @@ public function __construct($environment, $class_loader, $allow_dumping = TRUE) } /** - * 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. - * - * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException - * In case the host name in the request is invalid. - * - * @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) { - if (static::validateHostname($request) === FALSE) { - throw new BadRequestHttpException(); - } - - // 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->getHost(); - - $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) { diff --git a/core/lib/Drupal/Core/SitePathFactory.php b/core/lib/Drupal/Core/SitePathFactory.php index 3adade2..d39568f 100644 --- a/core/lib/Drupal/Core/SitePathFactory.php +++ b/core/lib/Drupal/Core/SitePathFactory.php @@ -7,17 +7,20 @@ namespace Drupal\Core; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + /** * Gets the site path from the kernel. */ class SitePathFactory { /** - * The Drupal kernel. + * The app root directory. * - * @var \Drupal\Core\DrupalKernelInterface + * @var SplString */ - protected $drupalKernel; + protected $appRoot; /** * Constructs an SitePathFactory instance. @@ -25,19 +28,131 @@ class SitePathFactory { * @param \Drupal\Core\DrupalKernelInterface $drupal_kernel * The Drupal kernel. */ - public function __construct(DrupalKernelInterface $drupal_kernel) { - $this->drupalKernel = $drupal_kernel; + public function __construct($app_root) { + $this->appRoot = $app_root; } /** - * Gets the site path. + * Generate a site path based on a request object. * - * @return string + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return \SplString * The site path. */ - public function get() { - return $this->drupalKernel->getSitePath(); + public function getForRequest(Request $request) { + return new \SplString($this->findSitePath($request)); } -} + /** + * Generate a site path based on a request stack. + * + * This method exists so that we can create the site path as a service using + * existing services. Since we have @request_stack and not @request, we have + * to get the current request from the stack. + * + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request. + * + * @return \SplString + * The site path. + */ + public function getForRequestStack(RequestStack $request_stack) { + $request = $request_stack->getCurrentRequest(); + return $this->getForRequest($request); + } + + /** + * 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. + * + * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + * In case the host name in the request is invalid. + * + * @see \Drupal\Core\DrupalKernelInterface::getSitePath() + * @see \Drupal\Core\DrupalKernelInterface::setSitePath() + * @see default.settings.php + * @see example.sites.php + */ + public function findSitePath(Request $request, $require_settings = TRUE) { + if (static::validateHostname($request) === FALSE) { + throw new BadRequestHttpException(); + } + // 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($this->app_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->getHost(); + + $sites = array(); + include $this->app_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($this->app_root . '/sites/' . $sites[$dir])) { + $dir = $sites[$dir]; + } + if (file_exists($this->app_root . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists($this->app_root . '/sites/' . $dir))) { + return "sites/$dir"; + } + } + } + return 'sites/default'; + } + +} diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index bd611f4..5da7fab 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Variable; +use Drupal\Core\SitePathFactory; use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DrupalKernel; @@ -181,8 +182,9 @@ protected function setUp() { $class_loader = require DRUPAL_ROOT . '/autoload.php'; $this->kernel = new DrupalKernel('testing', $class_loader, FALSE); $request = Request::create('/'); - $site_path = DrupalKernel::findSitePath($request); - $this->kernel->setSitePath($site_path); + // @todo: Come up with a better way to figure out DRUPAL_ROOT. + $site_path_factory = new SitePathFactory(DRUPAL_ROOT); + $site_path = $site_path_factory->getForRequest($request); if (file_exists($directory . '/settings.testing.php')) { Settings::initialize(DRUPAL_ROOT, $site_path, $class_loader); }