setSitePath($site_path); Settings::initialize(dirname($core_root), $site_path, $class_loader); // Initialize our list of trusted HTTP Host headers to protect against // header attacks. $host_patterns = Settings::get('trusted_host_patterns', array()); if (PHP_SAPI !== 'cli' && !empty($host_patterns)) { if (static::setupTrustedHosts($request, $host_patterns) === FALSE) { throw new BadRequestHttpException('The provided host name is not valid for this server.'); } } // 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() && PHP_SAPI !== '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'. * @param $class_loader * The class loader. Normally \Composer\Autoload\ClassLoader, as included by * the front controller, but may also be decorated; e.g., * \Symfony\Component\ClassLoader\ApcClassLoader. * @param bool $allow_dumping * (optional) FALSE to stop the container from being written to or read * from disk. Defaults to TRUE. */ public function __construct($environment, $class_loader, $allow_dumping = TRUE) { $this->environment = $environment; $this->classLoader = $class_loader; $this->allowDumping = $allow_dumping; $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)))); } /** * 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) { $this->sitePath = $path; } /** * {@inheritdoc} */ public function getSitePath() { return $this->sitePath; } /** * {@inheritdoc} */ public function getAppRoot() { return $this->root; } /** * {@inheritdoc} */ public function boot() { if ($this->booted) { return $this; } // Start a page timer: Timer::start('page'); // Ensure that findSitePath is set. if (!$this->sitePath) { throw new \Exception('Kernel does not have site path set before calling boot()'); } // Initialize the container. $this->initializeContainer(); // Ensure mt_rand() is reseeded to prevent random values from one page load // being exploited to predict random values in subsequent page loads. $seed = unpack("L", Crypt::randomBytes(4)); mt_srand($seed[1]); $this->booted = TRUE; return $this; } /** * {@inheritdoc} */ public function shutdown() { if (FALSE === $this->booted) { return; } $this->container->get('stream_wrapper_manager')->unregister(); $this->booted = FALSE; $this->container = NULL; $this->moduleList = NULL; $this->moduleData = array(); } /** * {@inheritdoc} */ public function getContainer() { return $this->container; } /** * {@inheritdoc} */ public function loadLegacyIncludes() { require_once $this->root . '/core/includes/common.inc'; require_once $this->root . '/core/includes/database.inc'; require_once $this->root . '/core/includes/module.inc'; require_once $this->root . '/core/includes/theme.inc'; require_once $this->root . '/core/includes/pager.inc'; require_once $this->root . '/core/includes/menu.inc'; require_once $this->root . '/core/includes/tablesort.inc'; require_once $this->root . '/core/includes/file.inc'; require_once $this->root . '/core/includes/unicode.inc'; require_once $this->root . '/core/includes/form.inc'; require_once $this->root . '/core/includes/errors.inc'; require_once $this->root . '/core/includes/schema.inc'; require_once $this->root . '/core/includes/entity.inc'; } /** * {@inheritdoc} */ public function preHandle(Request $request) { $this->loadLegacyIncludes(); // Load all enabled modules. $this->container->get('module_handler')->loadAll(); // Register stream wrappers. $this->container->get('stream_wrapper_manager')->register(); // Initialize legacy request globals. $this->initializeRequestGlobals($request); // Put the request on the stack. $this->container->get('request_stack')->push($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); // Override of Symfony's mime type guesser singleton. MimeTypeGuesser::registerWithSymfonyGuesser($this->container); $this->prepared = TRUE; } /** * {@inheritdoc} */ public function discoverServiceProviders() { $this->serviceYamls = array( 'app' => array(), 'site' => array(), ); $this->serviceProviderClasses = array( 'app' => array(), 'site' => array(), ); $this->serviceYamls['app']['core'] = 'core/core.services.yml'; $this->serviceProviderClasses['app']['core'] = 'Drupal\Core\CoreServiceProvider'; // Retrieve enabled modules and register their namespaces. if (!isset($this->moduleList)) { $extensions = $this->getConfigStorage()->read('core.extension'); $this->moduleList = isset($extensions['module']) ? $extensions['module'] : array(); } $module_filenames = $this->getModuleFileNames(); $this->classLoaderAddMultiplePsr4($this->getModuleNamespacesPsr4($module_filenames)); // Load each module's serviceProvider class. 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($filename) . "/$module.services.yml"; if (file_exists($filename)) { $this->serviceYamls['app'][$module] = $filename; } } // Add site-specific service providers. if (!empty($GLOBALS['conf']['container_service_providers'])) { foreach ($GLOBALS['conf']['container_service_providers'] as $class) { if (class_exists($class)) { $this->serviceProviderClasses['site'][] = $class; } } } if (!$this->addServiceFiles(Settings::get('container_yamls'))) { throw new \Exception('The container_yamls setting is missing from settings.php'); } } /** * {@inheritdoc} */ public function getServiceProviders($origin) { return $this->serviceProviders[$origin]; } /** * {@inheritdoc} */ public function terminate(Request $request, Response $response) { // Only run terminate() when essential services have been set up properly // by preHandle() before. if (FALSE === $this->prepared) { return; } if ($this->getHttpKernel() instanceof TerminableInterface) { $this->getHttpKernel()->terminate($request, $response); } } /** * {@inheritdoc} */ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { $this->boot(); return $this->getHttpKernel()->handle($request, $type, $catch); } /** * {@inheritdoc} */ public function prepareLegacyRequest(Request $request) { $this->boot(); $this->preHandle($request); // Setup services which are normally initialized from within stack // middleware or during the request kernel event. if (PHP_SAPI !== 'cli') { $request->setSession($this->container->get('session')); } $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('')); $request->attributes->set(RouteObjectInterface::ROUTE_NAME, ''); $this->container->get('request_stack')->push($request); $this->container->get('router.request_context')->fromRequest($request); return $this; } /** * Returns module data on the filesystem. * * @param $module * The name of the module. * * @return \Drupal\Core\Extension\Extension|bool * Returns an Extension object if the module is found, FALSE otherwise. */ protected function moduleData($module) { if (!$this->moduleData) { // First, find profiles. $listing = new ExtensionDiscovery($this->root); $listing->setProfileDirectories(array()); $all_profiles = $listing->scan('profile'); $profiles = array_intersect_key($all_profiles, $this->moduleList); // If a module is within a profile directory but specifies another // profile for testing, it needs to be found in the parent profile. $settings = $this->getConfigStorage()->read('simpletest.settings'); $parent_profile = !empty($settings['parent_profile']) ? $settings['parent_profile'] : NULL; if ($parent_profile && !isset($profiles[$parent_profile])) { // In case both profile directories contain the same extension, the // actual profile always has precedence. $profiles = array($parent_profile => $all_profiles[$parent_profile]) + $profiles; } $profile_directories = array_map(function ($profile) { return $profile->getPath(); }, $profiles); $listing->setProfileDirectories($profile_directories); // Now find modules. $this->moduleData = $profiles + $listing->scan('module'); } return isset($this->moduleData[$module]) ? $this->moduleData[$module] : FALSE; } /** * Implements Drupal\Core\DrupalKernelInterface::updateModules(). * * @todo Remove obsolete $module_list parameter. Only $module_filenames is * needed. */ public function updateModules(array $module_list, array $module_filenames = array()) { $this->moduleList = $module_list; foreach ($module_filenames as $name => $extension) { $this->moduleData[$name] = $extension; } // If we haven't yet booted, we don't need to do anything: the new module // list will take effect when boot() is called. If we have already booted, // then rebuild the container in order to refresh the serviceProvider list // and container. if ($this->booted) { $this->initializeContainer(TRUE); } } /** * Returns the classname based on environment. * * @return string * The class name. */ protected function getClassName() { $parts = array('service_container', $this->environment); return implode('_', $parts); } /** * Returns the container class namespace based on the environment. * * @return string * The class name. */ protected function getClassNamespace() { return 'Drupal\\Core\\DependencyInjection\\Container\\' . $this->environment; } /** * Returns the kernel parameters. * * @return array An array of kernel parameters */ protected function getKernelParameters() { return array( 'kernel.environment' => $this->environment, ); } /** * Initializes the service container. * * @param bool $rebuild * Force a container rebuild. * @return \Symfony\Component\DependencyInjection\ContainerInterface */ protected function initializeContainer($rebuild = FALSE) { $this->containerNeedsDumping = FALSE; $session_manager_started = FALSE; if (isset($this->container)) { // Save the id of the currently logged in user. if ($this->container->initialized('current_user')) { $current_user_id = $this->container->get('current_user')->id(); } // If there is a session manager, close and save the session. if ($this->container->initialized('session_manager')) { $session_manager = $this->container->get('session_manager'); if ($session_manager->isStarted()) { $session_manager_started = TRUE; $session_manager->save(); } unset($session_manager); } } // If the module list hasn't already been set in updateModules and we are // not forcing a rebuild, then try and load the container from the disk. if (empty($this->moduleList) && !$rebuild) { $fully_qualified_class_name = '\\' . $this->getClassNamespace() . '\\' . $this->getClassName(); // First, try to load from storage. if (!class_exists($fully_qualified_class_name, FALSE)) { $this->storage()->load($this->getClassName() . '.php'); } // If the load succeeded or the class already existed, use it. if (class_exists($fully_qualified_class_name, FALSE)) { $container = new $fully_qualified_class_name; } } if (!isset($container)) { $container = $this->compileContainer(); } $this->attachSynthetic($container); $this->container = $container; if ($session_manager_started) { $this->container->get('session_manager')->start(); } // The request stack is preserved across container rebuilds. Reinject the // new session into the master request if one was present before. if (($request_stack = $this->container->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE))) { if ($request = $request_stack->getMasterRequest()) { if ($request->hasSession()) { $request->setSession($this->container->get('session')); } } } if (!empty($current_user_id)) { $this->container->get('current_user')->setInitialAccountId($current_user_id); } \Drupal::setContainer($this->container); // If needs dumping flag was set, dump the container. if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, static::CONTAINER_BASE_CLASS)) { $this->container->get('logger.factory')->get('DrupalKernel')->notice('Container cannot be written to disk'); } return $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; global $base_secure_url, $base_insecure_url; // @todo Refactor with the Symfony Request object. 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. $base_root = $request->getSchemeAndHttpHost(); $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, // authorize.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); } /** * Returns service instances to persist from an old container to a new one. */ protected function getServicesToPersist(ContainerInterface $container) { $persist = array(); foreach ($container->getParameter('persistIds') as $id) { // It's pointless to persist services not yet initialized. if ($container->initialized($id)) { $persist[$id] = $container->get($id); } } return $persist; } /** * Moves persistent service instances into a new container. */ protected function persistServices(ContainerInterface $container, array $persist) { foreach ($persist as $id => $object) { // Do not override services already set() on the new container, for // example 'service_container'. if (!$container->initialized($id)) { $container->set($id, $object); } } } /** * Force a container rebuild. * * @return \Symfony\Component\DependencyInjection\ContainerInterface */ public function rebuildContainer() { // Empty module properties and for them to be reloaded from scratch. $this->moduleList = NULL; $this->moduleData = array(); return $this->initializeContainer(TRUE); } /** * Attach synthetic values on to kernel. * * @param ContainerInterface $container * Container object * * @return ContainerInterface */ protected function attachSynthetic(ContainerInterface $container) { $persist = array(); if (isset($this->container)) { $persist = $this->getServicesToPersist($this->container); } $this->persistServices($container, $persist); // All namespaces must be registered before we attempt to use any service // from the container. $this->classLoaderAddMultiplePsr4($container->getParameter('container.namespaces')); $container->set('kernel', $this); // Set the class loader which was registered as a synthetic service. $container->set('class_loader', $this->classLoader); return $container; } /** * Compiles a new service container. * * @return ContainerBuilder The compiled service container */ protected function compileContainer() { // We are forcing a container build so it is reasonable to assume that the // calling method knows something about the system has changed requiring the // container to be dumped to the filesystem. if ($this->allowDumping) { $this->containerNeedsDumping = TRUE; } $this->initializeServiceProviders(); $container = $this->getContainerBuilder(); $container->set('kernel', $this); $container->setParameter('container.modules', $this->getModulesParameter()); // Get a list of namespaces and put it onto the container. $namespaces = $this->getModuleNamespacesPsr4($this->getModuleFileNames()); // Add all components in \Drupal\Core and \Drupal\Component that have one of // the following directories: // - Element // - Entity // - Plugin foreach (array('Core', 'Component') as $parent_directory) { $path = 'core/lib/Drupal/' . $parent_directory; $parent_namespace = 'Drupal\\' . $parent_directory; foreach (new \DirectoryIterator($this->root . '/' . $path) as $component) { /** @var $component \DirectoryIterator */ $pathname = $component->getPathname(); if (!$component->isDot() && $component->isDir() && ( is_dir($pathname . '/Plugin') || is_dir($pathname . '/Entity') || is_dir($pathname . '/Element') )) { $namespaces[$parent_namespace . '\\' . $component->getFilename()] = $path . '/' . $component->getFilename(); } } } $container->setParameter('container.namespaces', $namespaces); // Store the default language values on the container. This is so that the // default language can be configured using the configuration factory. This // avoids the circular dependencies that would created by // \Drupal\language\LanguageServiceProvider::alter() and allows the default // language to not be English in the installer. $default_language_values = Language::$defaultValues; if ($system = $this->getConfigStorage()->read('system.site')) { if ($default_language_values['id'] != $system['langcode']) { $default_language_values = array('id' => $system['langcode']); } } $container->setParameter('language.default_values', $default_language_values); // Register synthetic services. $container->register('class_loader')->setSynthetic(TRUE); $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE); $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE); // Register application services. $yaml_loader = new YamlFileLoader($container); foreach ($this->serviceYamls['app'] as $filename) { $yaml_loader->load($filename); } foreach ($this->serviceProviders['app'] as $provider) { if ($provider instanceof ServiceProviderInterface) { $provider->register($container); } } // Register site-specific service overrides. foreach ($this->serviceYamls['site'] as $filename) { $yaml_loader->load($filename); } foreach ($this->serviceProviders['site'] as $provider) { if ($provider instanceof ServiceProviderInterface) { $provider->register($container); } } // Identify all services whose instances should be persisted when rebuilding // the container during the lifetime of the kernel (e.g., during a kernel // reboot). Include synthetic services, because by definition, they cannot // be automatically reinstantiated. Also include services tagged to persist. $persist_ids = array(); foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isSynthetic() || $definition->getTag('persist')) { $persist_ids[] = $id; } } $container->setParameter('persistIds', $persist_ids); $container->compile(); return $container; } /** * Registers all service providers to the kernel. * * @throws \LogicException */ protected function initializeServiceProviders() { $this->discoverServiceProviders(); $this->serviceProviders = array( 'app' => array(), 'site' => array(), ); foreach ($this->serviceProviderClasses as $origin => $classes) { foreach ($classes as $name => $class) { $this->serviceProviders[$origin][$name] = new $class; } } } /** * Gets a new ContainerBuilder instance used to build the service container. * * @return ContainerBuilder */ protected function getContainerBuilder() { return new ContainerBuilder(new ParameterBag($this->getKernelParameters())); } /** * Dumps the service container to PHP code in the config directory. * * This method is based on the dumpContainer method in the parent class, but * that method is reliant on the Config component which we do not use here. * * @param ContainerBuilder $container * The service container. * @param string $baseClass * The name of the container's base class * * @return bool * TRUE if the container was successfully dumped to disk. */ protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) { if (!$this->storage()->writeable()) { return FALSE; } // Cache the container. $dumper = new PhpDumper($container); $dumper->setProxyDumper(new ProxyDumper(new ProxyBuilder())); $class = $this->getClassName(); $namespace = $this->getClassNamespace(); $content = $dumper->dump([ 'class' => $class, 'base_class' => $baseClass, 'namespace' => $namespace, ]); return $this->storage()->save($class . '.php', $content); } /** * Gets a http kernel from the container * * @return \Symfony\Component\HttpKernel\HttpKernelInterface */ protected function getHttpKernel() { return $this->container->get('http_kernel'); } /** * Gets the PHP code storage object to use for the compiled container. * * @return \Drupal\Component\PhpStorage\PhpStorageInterface */ protected function storage() { if (!isset($this->storage)) { $this->storage = PhpStorageFactory::get('service_container'); } return $this->storage; } /** * Returns the active configuration storage to use during building the container. * * @return \Drupal\Core\Config\StorageInterface */ protected function getConfigStorage() { if (!isset($this->configStorage)) { // The active configuration storage may not exist yet; e.g., in the early // installer. Catch the exception thrown by config_get_config_directory(). try { $this->configStorage = BootstrapConfigStorageFactory::get($this->classLoader); } catch (\Exception $e) { $this->configStorage = new NullStorage(); } } return $this->configStorage; } /** * Returns an array of Extension class parameters for all enabled modules. * * @return array */ protected function getModulesParameter() { $extensions = array(); foreach ($this->moduleList as $name => $weight) { if ($data = $this->moduleData($name)) { $extensions[$name] = array( 'type' => $data->getType(), 'pathname' => $data->getPathname(), 'filename' => $data->getExtensionFilename(), ); } } return $extensions; } /** * Gets the file name for each enabled module. * * @return array * Array where each key is a module name, and each value is a path to the * respective *.module or *.profile file. */ protected function getModuleFileNames() { $filenames = array(); foreach ($this->moduleList as $module => $weight) { if ($data = $this->moduleData($module)) { $filenames[$module] = $data->getPathname(); } } return $filenames; } /** * Gets the PSR-4 base directories for module namespaces. * * @param string[] $module_file_names * Array where each key is a module name, and each value is a path to the * respective *.module or *.profile file. * * @return string[] * Array where each key is a module namespace like 'Drupal\system', and each * value is the PSR-4 base directory associated with the module namespace. */ protected function getModuleNamespacesPsr4($module_file_names) { $namespaces = array(); foreach ($module_file_names as $module => $filename) { $namespaces["Drupal\\$module"] = dirname($filename) . '/src'; } return $namespaces; } /** * Registers a list of namespaces with PSR-4 directories for class loading. * * @param array $namespaces * Array where each key is a namespace like 'Drupal\system', and each value * is either a PSR-4 base directory, or an array of PSR-4 base directories * associated with this namespace. */ protected function classLoaderAddMultiplePsr4(array $namespaces = array()) { foreach ($namespaces as $prefix => $paths) { if (is_array($paths)) { foreach ($paths as $key => $value) { $paths[$key] = $this->root . '/' . $value; } } elseif (is_string($paths)) { $paths = $this->root . '/' . $paths; } $this->classLoader->addPsr4($prefix . '\\', $paths); } } /** * Validates a hostname length. * * @param string $host * A hostname. * * @return bool * TRUE if the length is appropriate, or FALSE otherwise. */ protected static function validateHostnameLength($host) { // Limit the length of the host name to 1000 bytes to prevent DoS attacks // with long host names. return strlen($host) <= 1000 // Limit the number of subdomains and port separators to prevent DoS attacks // in findSitePath(). && substr_count($host, '.') <= 100 && substr_count($host, ':') <= 100; } /** * Validates the hostname supplied from the HTTP request. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object * * @return bool * TRUE if the hostmame is valid, or FALSE otherwise. * * @todo Adjust per resolution to https://github.com/symfony/symfony/issues/12349 */ public static function validateHostname(Request $request) { // $request->getHost() can throw an UnexpectedValueException if it // detects a bad hostname, but it does not validate the length. try { $http_host = $request->getHost(); } catch (\UnexpectedValueException $e) { return FALSE; } if (static::validateHostnameLength($http_host) === FALSE) { return FALSE; } return TRUE; } /** * Sets up the lists of trusted HTTP Host headers. * * Since the HTTP Host header can be set by the user making the request, it * is possible to create an attack vectors against a site by overriding this. * Symfony provides a mechanism for creating a list of trusted Host values. * * Host patterns (as regular expressions) can be configured throught * settings.php for multisite installations, sites using ServerAlias without * canonical redirection, or configurations where the site responds to default * requests. For example, * * @code * $settings['trusted_host_patterns'] = array( * '^example\.com$', * '^*.example\.com$', * ); * @endcode * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param array $host_patterns * The array of trusted host patterns. * * @return boolean * TRUE if the Host header is trusted, FALSE otherwise. * * @see https://www.drupal.org/node/1992030 * @see \Drupal\Core\Http\TrustedHostsRequestFactory */ protected static function setupTrustedHosts(Request $request, $host_patterns) { $request->setTrustedHosts($host_patterns); // Get the host, which will validate the current request. try { $host = $request->getHost(); // Fake requests created through Request::create() without passing in the // server variables from the main request have a default host of // 'localhost'. If 'localhost' does not match any of the trusted host // patterns these fake requests would fail the host verification. Instead, // TrustedHostsRequestFactory makes sure to pass in the server variables // from the main request. $request_factory = new TrustedHostsRequestFactory($host); Request::setFactory([$request_factory, 'createRequest']); } catch (\UnexpectedValueException $e) { return FALSE; } return TRUE; } /** * Add service files. * * @param $service_yamls * A list of service files. * * @return bool * TRUE if the list was an array, FALSE otherwise. */ protected function addServiceFiles($service_yamls) { if (is_array($service_yamls)) { $this->serviceYamls['site'] = array_filter($service_yamls, 'file_exists'); return TRUE; } return FALSE; } }