DrupalKernel.php 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  1. <?php
  2. namespace Drupal\Core;
  3. use Composer\Autoload\ClassLoader;
  4. use Drupal\Component\Assertion\Handle;
  5. use Drupal\Component\FileCache\FileCacheFactory;
  6. use Drupal\Component\Utility\UrlHelper;
  7. use Drupal\Core\Cache\DatabaseBackend;
  8. use Drupal\Core\Config\BootstrapConfigStorageFactory;
  9. use Drupal\Core\Config\NullStorage;
  10. use Drupal\Core\Database\Database;
  11. use Drupal\Core\DependencyInjection\ContainerBuilder;
  12. use Drupal\Core\DependencyInjection\ServiceModifierInterface;
  13. use Drupal\Core\DependencyInjection\ServiceProviderInterface;
  14. use Drupal\Core\DependencyInjection\YamlFileLoader;
  15. use Drupal\Core\Extension\ExtensionDiscovery;
  16. use Drupal\Core\File\MimeType\MimeTypeGuesser;
  17. use Drupal\Core\Http\TrustedHostsRequestFactory;
  18. use Drupal\Core\Installer\InstallerKernel;
  19. use Drupal\Core\Installer\InstallerRedirectTrait;
  20. use Drupal\Core\Language\Language;
  21. use Drupal\Core\Security\PharExtensionInterceptor;
  22. use Drupal\Core\Security\RequestSanitizer;
  23. use Drupal\Core\Site\Settings;
  24. use Drupal\Core\Test\TestDatabase;
  25. use Symfony\Cmf\Component\Routing\RouteObjectInterface;
  26. use Symfony\Component\ClassLoader\ApcClassLoader;
  27. use Symfony\Component\ClassLoader\WinCacheClassLoader;
  28. use Symfony\Component\ClassLoader\XcacheClassLoader;
  29. use Symfony\Component\DependencyInjection\ContainerInterface;
  30. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  31. use Symfony\Component\HttpFoundation\RedirectResponse;
  32. use Symfony\Component\HttpFoundation\Request;
  33. use Symfony\Component\HttpFoundation\Response;
  34. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  35. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  36. use Symfony\Component\HttpKernel\TerminableInterface;
  37. use Symfony\Component\Routing\Route;
  38. use TYPO3\PharStreamWrapper\Manager as PharStreamWrapperManager;
  39. use TYPO3\PharStreamWrapper\Behavior as PharStreamWrapperBehavior;
  40. use TYPO3\PharStreamWrapper\PharStreamWrapper;
  41. /**
  42. * The DrupalKernel class is the core of Drupal itself.
  43. *
  44. * This class is responsible for building the Dependency Injection Container and
  45. * also deals with the registration of service providers. It allows registered
  46. * service providers to add their services to the container. Core provides the
  47. * CoreServiceProvider, which, in addition to registering any core services that
  48. * cannot be registered in the core.services.yaml file, adds any compiler passes
  49. * needed by core, e.g. for processing tagged services. Each module can add its
  50. * own service provider, i.e. a class implementing
  51. * Drupal\Core\DependencyInjection\ServiceProvider, to register services to the
  52. * container, or modify existing services.
  53. */
  54. class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
  55. use InstallerRedirectTrait;
  56. /**
  57. * Holds the class used for dumping the container to a PHP array.
  58. *
  59. * In combination with swapping the container class this is useful to e.g.
  60. * dump to the human-readable PHP array format to debug the container
  61. * definition in an easier way.
  62. *
  63. * @var string
  64. */
  65. protected $phpArrayDumperClass = '\Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper';
  66. /**
  67. * Holds the default bootstrap container definition.
  68. *
  69. * @var array
  70. */
  71. protected $defaultBootstrapContainerDefinition = [
  72. 'parameters' => [],
  73. 'services' => [
  74. 'database' => [
  75. 'class' => 'Drupal\Core\Database\Connection',
  76. 'factory' => 'Drupal\Core\Database\Database::getConnection',
  77. 'arguments' => ['default'],
  78. ],
  79. 'cache.container' => [
  80. 'class' => 'Drupal\Core\Cache\DatabaseBackend',
  81. 'arguments' => ['@database', '@cache_tags_provider.container', 'container', DatabaseBackend::MAXIMUM_NONE],
  82. ],
  83. 'cache_tags_provider.container' => [
  84. 'class' => 'Drupal\Core\Cache\DatabaseCacheTagsChecksum',
  85. 'arguments' => ['@database'],
  86. ],
  87. ],
  88. ];
  89. /**
  90. * Holds the class used for instantiating the bootstrap container.
  91. *
  92. * @var string
  93. */
  94. protected $bootstrapContainerClass = '\Drupal\Component\DependencyInjection\PhpArrayContainer';
  95. /**
  96. * Holds the bootstrap container.
  97. *
  98. * @var \Symfony\Component\DependencyInjection\ContainerInterface
  99. */
  100. protected $bootstrapContainer;
  101. /**
  102. * Holds the container instance.
  103. *
  104. * @var \Symfony\Component\DependencyInjection\ContainerInterface
  105. */
  106. protected $container;
  107. /**
  108. * The environment, e.g. 'testing', 'install'.
  109. *
  110. * @var string
  111. */
  112. protected $environment;
  113. /**
  114. * Whether the kernel has been booted.
  115. *
  116. * @var bool
  117. */
  118. protected $booted = FALSE;
  119. /**
  120. * Whether essential services have been set up properly by preHandle().
  121. *
  122. * @var bool
  123. */
  124. protected $prepared = FALSE;
  125. /**
  126. * Holds the list of enabled modules.
  127. *
  128. * @var array
  129. * An associative array whose keys are module names and whose values are
  130. * ignored.
  131. */
  132. protected $moduleList;
  133. /**
  134. * List of available modules and installation profiles.
  135. *
  136. * @var \Drupal\Core\Extension\Extension[]
  137. */
  138. protected $moduleData = [];
  139. /**
  140. * The class loader object.
  141. *
  142. * @var \Composer\Autoload\ClassLoader
  143. */
  144. protected $classLoader;
  145. /**
  146. * Config storage object used for reading enabled modules configuration.
  147. *
  148. * @var \Drupal\Core\Config\StorageInterface
  149. */
  150. protected $configStorage;
  151. /**
  152. * Whether the container can be dumped.
  153. *
  154. * @var bool
  155. */
  156. protected $allowDumping;
  157. /**
  158. * Whether the container needs to be rebuilt the next time it is initialized.
  159. *
  160. * @var bool
  161. */
  162. protected $containerNeedsRebuild = FALSE;
  163. /**
  164. * Whether the container needs to be dumped once booting is complete.
  165. *
  166. * @var bool
  167. */
  168. protected $containerNeedsDumping;
  169. /**
  170. * List of discovered services.yml pathnames.
  171. *
  172. * This is a nested array whose top-level keys are 'app' and 'site', denoting
  173. * the origin of a service provider. Site-specific providers have to be
  174. * collected separately, because they need to be processed last, so as to be
  175. * able to override services from application service providers.
  176. *
  177. * @var array
  178. */
  179. protected $serviceYamls;
  180. /**
  181. * List of discovered service provider class names or objects.
  182. *
  183. * This is a nested array whose top-level keys are 'app' and 'site', denoting
  184. * the origin of a service provider. Site-specific providers have to be
  185. * collected separately, because they need to be processed last, so as to be
  186. * able to override services from application service providers.
  187. *
  188. * Allowing objects is for example used to allow
  189. * \Drupal\KernelTests\KernelTestBase to register itself as service provider.
  190. *
  191. * @var array
  192. */
  193. protected $serviceProviderClasses;
  194. /**
  195. * List of instantiated service provider classes.
  196. *
  197. * @see \Drupal\Core\DrupalKernel::$serviceProviderClasses
  198. *
  199. * @var array
  200. */
  201. protected $serviceProviders;
  202. /**
  203. * Whether the PHP environment has been initialized.
  204. *
  205. * This legacy phase can only be booted once because it sets session INI
  206. * settings. If a session has already been started, re-generating these
  207. * settings would break the session.
  208. *
  209. * @var bool
  210. */
  211. protected static $isEnvironmentInitialized = FALSE;
  212. /**
  213. * The site directory.
  214. *
  215. * @var string
  216. */
  217. protected $sitePath;
  218. /**
  219. * The app root.
  220. *
  221. * @var string
  222. */
  223. protected $root;
  224. /**
  225. * Create a DrupalKernel object from a request.
  226. *
  227. * @param \Symfony\Component\HttpFoundation\Request $request
  228. * The request.
  229. * @param $class_loader
  230. * The class loader. Normally Composer's ClassLoader, as included by the
  231. * front controller, but may also be decorated; e.g.,
  232. * \Symfony\Component\ClassLoader\ApcClassLoader.
  233. * @param string $environment
  234. * String indicating the environment, e.g. 'prod' or 'dev'.
  235. * @param bool $allow_dumping
  236. * (optional) FALSE to stop the container from being written to or read
  237. * from disk. Defaults to TRUE.
  238. * @param string $app_root
  239. * (optional) The path to the application root as a string. If not supplied,
  240. * the application root will be computed.
  241. *
  242. * @return static
  243. *
  244. * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
  245. * In case the host name in the request is not trusted.
  246. */
  247. public static function createFromRequest(Request $request, $class_loader, $environment, $allow_dumping = TRUE, $app_root = NULL) {
  248. $kernel = new static($environment, $class_loader, $allow_dumping, $app_root);
  249. static::bootEnvironment($app_root);
  250. $kernel->initializeSettings($request);
  251. return $kernel;
  252. }
  253. /**
  254. * Constructs a DrupalKernel object.
  255. *
  256. * @param string $environment
  257. * String indicating the environment, e.g. 'prod' or 'dev'.
  258. * @param $class_loader
  259. * The class loader. Normally \Composer\Autoload\ClassLoader, as included by
  260. * the front controller, but may also be decorated; e.g.,
  261. * \Symfony\Component\ClassLoader\ApcClassLoader.
  262. * @param bool $allow_dumping
  263. * (optional) FALSE to stop the container from being written to or read
  264. * from disk. Defaults to TRUE.
  265. * @param string $app_root
  266. * (optional) The path to the application root as a string. If not supplied,
  267. * the application root will be computed.
  268. */
  269. public function __construct($environment, $class_loader, $allow_dumping = TRUE, $app_root = NULL) {
  270. $this->environment = $environment;
  271. $this->classLoader = $class_loader;
  272. $this->allowDumping = $allow_dumping;
  273. if ($app_root === NULL) {
  274. $app_root = static::guessApplicationRoot();
  275. }
  276. $this->root = $app_root;
  277. }
  278. /**
  279. * Determine the application root directory based on this file's location.
  280. *
  281. * @return string
  282. * The application root.
  283. */
  284. protected static function guessApplicationRoot() {
  285. // Determine the application root by:
  286. // - Removing the namespace directories from the path.
  287. // - Getting the path to the directory two levels up from the path
  288. // determined in the previous step.
  289. return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
  290. }
  291. /**
  292. * Returns the appropriate site directory for a request.
  293. *
  294. * Once the kernel has been created DrupalKernelInterface::getSitePath() is
  295. * preferred since it gets the statically cached result of this method.
  296. *
  297. * Site directories contain all site specific code. This includes settings.php
  298. * for bootstrap level configuration, file configuration stores, public file
  299. * storage and site specific modules and themes.
  300. *
  301. * A file named sites.php must be present in the sites directory for
  302. * multisite. If it doesn't exist, then 'sites/default' will be used.
  303. *
  304. * Finds a matching site directory file by stripping the website's hostname
  305. * from left to right and pathname from right to left. By default, the
  306. * directory must contain a 'settings.php' file for it to match. If the
  307. * parameter $require_settings is set to FALSE, then a directory without a
  308. * 'settings.php' file will match as well. The first configuration file found
  309. * will be used and the remaining ones will be ignored. If no configuration
  310. * file is found, returns a default value 'sites/default'. See
  311. * default.settings.php for examples on how the URL is converted to a
  312. * directory.
  313. *
  314. * The sites.php file in the sites directory can define aliases in an
  315. * associative array named $sites. The array is written in the format
  316. * '<port>.<domain>.<path>' => 'directory'. As an example, to create a
  317. * directory alias for https://www.drupal.org:8080/mysite/test whose
  318. * configuration file is in sites/example.com, the array should be defined as:
  319. * @code
  320. * $sites = array(
  321. * '8080.www.drupal.org.mysite.test' => 'example.com',
  322. * );
  323. * @endcode
  324. *
  325. * @param \Symfony\Component\HttpFoundation\Request $request
  326. * The current request.
  327. * @param bool $require_settings
  328. * Only directories with an existing settings.php file will be recognized.
  329. * Defaults to TRUE. During initial installation, this is set to FALSE so
  330. * that Drupal can detect a matching directory, then create a new
  331. * settings.php file in it.
  332. * @param string $app_root
  333. * (optional) The path to the application root as a string. If not supplied,
  334. * the application root will be computed.
  335. *
  336. * @return string
  337. * The path of the matching directory.
  338. *
  339. * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
  340. * In case the host name in the request is invalid.
  341. *
  342. * @see \Drupal\Core\DrupalKernelInterface::getSitePath()
  343. * @see \Drupal\Core\DrupalKernelInterface::setSitePath()
  344. * @see default.settings.php
  345. * @see example.sites.php
  346. */
  347. public static function findSitePath(Request $request, $require_settings = TRUE, $app_root = NULL) {
  348. if (static::validateHostname($request) === FALSE) {
  349. throw new BadRequestHttpException();
  350. }
  351. if ($app_root === NULL) {
  352. $app_root = static::guessApplicationRoot();
  353. }
  354. // Check for a simpletest override.
  355. if ($test_prefix = drupal_valid_test_ua()) {
  356. $test_db = new TestDatabase($test_prefix);
  357. return $test_db->getTestSitePath();
  358. }
  359. // Determine whether multi-site functionality is enabled.
  360. if (!file_exists($app_root . '/sites/sites.php')) {
  361. return 'sites/default';
  362. }
  363. // Otherwise, use find the site path using the request.
  364. $script_name = $request->server->get('SCRIPT_NAME');
  365. if (!$script_name) {
  366. $script_name = $request->server->get('SCRIPT_FILENAME');
  367. }
  368. $http_host = $request->getHttpHost();
  369. $sites = [];
  370. include $app_root . '/sites/sites.php';
  371. $uri = explode('/', $script_name);
  372. $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.')))));
  373. for ($i = count($uri) - 1; $i > 0; $i--) {
  374. for ($j = count($server); $j > 0; $j--) {
  375. $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
  376. if (isset($sites[$dir]) && file_exists($app_root . '/sites/' . $sites[$dir])) {
  377. $dir = $sites[$dir];
  378. }
  379. if (file_exists($app_root . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists($app_root . '/sites/' . $dir))) {
  380. return "sites/$dir";
  381. }
  382. }
  383. }
  384. return 'sites/default';
  385. }
  386. /**
  387. * {@inheritdoc}
  388. */
  389. public function setSitePath($path) {
  390. if ($this->booted && $path !== $this->sitePath) {
  391. throw new \LogicException('Site path cannot be changed after calling boot()');
  392. }
  393. $this->sitePath = $path;
  394. }
  395. /**
  396. * {@inheritdoc}
  397. */
  398. public function getSitePath() {
  399. return $this->sitePath;
  400. }
  401. /**
  402. * {@inheritdoc}
  403. */
  404. public function getAppRoot() {
  405. return $this->root;
  406. }
  407. /**
  408. * {@inheritdoc}
  409. */
  410. public function boot() {
  411. if ($this->booted) {
  412. return $this;
  413. }
  414. // Ensure that findSitePath is set.
  415. if (!$this->sitePath) {
  416. throw new \Exception('Kernel does not have site path set before calling boot()');
  417. }
  418. // Initialize the FileCacheFactory component. We have to do it here instead
  419. // of in \Drupal\Component\FileCache\FileCacheFactory because we can not use
  420. // the Settings object in a component.
  421. $configuration = Settings::get('file_cache');
  422. // Provide a default configuration, if not set.
  423. if (!isset($configuration['default'])) {
  424. // @todo Use extension_loaded('apcu') for non-testbot
  425. // https://www.drupal.org/node/2447753.
  426. if (function_exists('apcu_fetch')) {
  427. $configuration['default']['cache_backend_class'] = '\Drupal\Component\FileCache\ApcuFileCacheBackend';
  428. }
  429. }
  430. FileCacheFactory::setConfiguration($configuration);
  431. FileCacheFactory::setPrefix(Settings::getApcuPrefix('file_cache', $this->root));
  432. $this->bootstrapContainer = new $this->bootstrapContainerClass(Settings::get('bootstrap_container_definition', $this->defaultBootstrapContainerDefinition));
  433. // Initialize the container.
  434. $this->initializeContainer();
  435. if (in_array('phar', stream_get_wrappers(), TRUE)) {
  436. // Set up a stream wrapper to handle insecurities due to PHP's builtin
  437. // phar stream wrapper. This is not registered as a regular stream wrapper
  438. // to prevent \Drupal\Core\File\FileSystem::validScheme() treating "phar"
  439. // as a valid scheme.
  440. try {
  441. $behavior = new PharStreamWrapperBehavior();
  442. PharStreamWrapperManager::initialize(
  443. $behavior->withAssertion(new PharExtensionInterceptor())
  444. );
  445. }
  446. catch (\LogicException $e) {
  447. // Continue if the PharStreamWrapperManager is already initialized. For
  448. // example, this occurs during a module install.
  449. // @see \Drupal\Core\Extension\ModuleInstaller::install()
  450. }
  451. stream_wrapper_unregister('phar');
  452. stream_wrapper_register('phar', PharStreamWrapper::class);
  453. }
  454. $this->booted = TRUE;
  455. return $this;
  456. }
  457. /**
  458. * {@inheritdoc}
  459. */
  460. public function shutdown() {
  461. if (FALSE === $this->booted) {
  462. return;
  463. }
  464. $this->container->get('stream_wrapper_manager')->unregister();
  465. $this->booted = FALSE;
  466. $this->container = NULL;
  467. $this->moduleList = NULL;
  468. $this->moduleData = [];
  469. }
  470. /**
  471. * {@inheritdoc}
  472. */
  473. public function getContainer() {
  474. return $this->container;
  475. }
  476. /**
  477. * {@inheritdoc}
  478. */
  479. public function setContainer(ContainerInterface $container = NULL) {
  480. if (isset($this->container)) {
  481. throw new \Exception('The container should not override an existing container.');
  482. }
  483. if ($this->booted) {
  484. throw new \Exception('The container cannot be set after a booted kernel.');
  485. }
  486. $this->container = $container;
  487. return $this;
  488. }
  489. /**
  490. * {@inheritdoc}
  491. */
  492. public function getCachedContainerDefinition() {
  493. $cache = $this->bootstrapContainer->get('cache.container')->get($this->getContainerCacheKey());
  494. if ($cache) {
  495. return $cache->data;
  496. }
  497. return NULL;
  498. }
  499. /**
  500. * {@inheritdoc}
  501. */
  502. public function loadLegacyIncludes() {
  503. require_once $this->root . '/core/includes/common.inc';
  504. require_once $this->root . '/core/includes/database.inc';
  505. require_once $this->root . '/core/includes/module.inc';
  506. require_once $this->root . '/core/includes/theme.inc';
  507. require_once $this->root . '/core/includes/pager.inc';
  508. require_once $this->root . '/core/includes/menu.inc';
  509. require_once $this->root . '/core/includes/tablesort.inc';
  510. require_once $this->root . '/core/includes/file.inc';
  511. require_once $this->root . '/core/includes/unicode.inc';
  512. require_once $this->root . '/core/includes/form.inc';
  513. require_once $this->root . '/core/includes/errors.inc';
  514. require_once $this->root . '/core/includes/schema.inc';
  515. require_once $this->root . '/core/includes/entity.inc';
  516. }
  517. /**
  518. * {@inheritdoc}
  519. */
  520. public function preHandle(Request $request) {
  521. // Sanitize the request.
  522. $request = RequestSanitizer::sanitize(
  523. $request,
  524. (array) Settings::get(RequestSanitizer::SANITIZE_WHITELIST, []),
  525. (bool) Settings::get(RequestSanitizer::SANITIZE_LOG, FALSE)
  526. );
  527. $this->loadLegacyIncludes();
  528. // Load all enabled modules.
  529. $this->container->get('module_handler')->loadAll();
  530. // Register stream wrappers.
  531. $this->container->get('stream_wrapper_manager')->register();
  532. // Initialize legacy request globals.
  533. $this->initializeRequestGlobals($request);
  534. // Put the request on the stack.
  535. $this->container->get('request_stack')->push($request);
  536. // Set the allowed protocols.
  537. UrlHelper::setAllowedProtocols($this->container->getParameter('filter_protocols'));
  538. // Override of Symfony's MIME type guesser singleton.
  539. MimeTypeGuesser::registerWithSymfonyGuesser($this->container);
  540. $this->prepared = TRUE;
  541. }
  542. /**
  543. * {@inheritdoc}
  544. */
  545. public function discoverServiceProviders() {
  546. $this->serviceYamls = [
  547. 'app' => [],
  548. 'site' => [],
  549. ];
  550. $this->serviceProviderClasses = [
  551. 'app' => [],
  552. 'site' => [],
  553. ];
  554. $this->serviceYamls['app']['core'] = 'core/core.services.yml';
  555. $this->serviceProviderClasses['app']['core'] = 'Drupal\Core\CoreServiceProvider';
  556. // Retrieve enabled modules and register their namespaces.
  557. if (!isset($this->moduleList)) {
  558. $extensions = $this->getConfigStorage()->read('core.extension');
  559. // If core.extension configuration does not exist and we're not in the
  560. // installer itself, then we need to put the kernel into a pre-installer
  561. // mode. The container should not be dumped because Drupal is yet to be
  562. // installed. The installer service provider is registered to ensure that
  563. // cache and other automatically created tables are not created if
  564. // database settings are available. None of this is required when the
  565. // installer is running because the installer has its own kernel and
  566. // manages the addition of its own service providers.
  567. // @see install_begin_request()
  568. if ($extensions === FALSE && !InstallerKernel::installationAttempted()) {
  569. $this->allowDumping = FALSE;
  570. $this->containerNeedsDumping = FALSE;
  571. $GLOBALS['conf']['container_service_providers']['InstallerServiceProvider'] = 'Drupal\Core\Installer\InstallerServiceProvider';
  572. }
  573. $this->moduleList = isset($extensions['module']) ? $extensions['module'] : [];
  574. }
  575. $module_filenames = $this->getModuleFileNames();
  576. $this->classLoaderAddMultiplePsr4($this->getModuleNamespacesPsr4($module_filenames));
  577. // Load each module's serviceProvider class.
  578. foreach ($module_filenames as $module => $filename) {
  579. $camelized = ContainerBuilder::camelize($module);
  580. $name = "{$camelized}ServiceProvider";
  581. $class = "Drupal\\{$module}\\{$name}";
  582. if (class_exists($class)) {
  583. $this->serviceProviderClasses['app'][$module] = $class;
  584. }
  585. $filename = dirname($filename) . "/$module.services.yml";
  586. if (file_exists($filename)) {
  587. $this->serviceYamls['app'][$module] = $filename;
  588. }
  589. }
  590. // Add site-specific service providers.
  591. if (!empty($GLOBALS['conf']['container_service_providers'])) {
  592. foreach ($GLOBALS['conf']['container_service_providers'] as $class) {
  593. if ((is_string($class) && class_exists($class)) || (is_object($class) && ($class instanceof ServiceProviderInterface || $class instanceof ServiceModifierInterface))) {
  594. $this->serviceProviderClasses['site'][] = $class;
  595. }
  596. }
  597. }
  598. $this->addServiceFiles(Settings::get('container_yamls', []));
  599. }
  600. /**
  601. * {@inheritdoc}
  602. */
  603. public function getServiceProviders($origin) {
  604. return $this->serviceProviders[$origin];
  605. }
  606. /**
  607. * {@inheritdoc}
  608. */
  609. public function terminate(Request $request, Response $response) {
  610. // Only run terminate() when essential services have been set up properly
  611. // by preHandle() before.
  612. if (FALSE === $this->prepared) {
  613. return;
  614. }
  615. if ($this->getHttpKernel() instanceof TerminableInterface) {
  616. $this->getHttpKernel()->terminate($request, $response);
  617. }
  618. }
  619. /**
  620. * {@inheritdoc}
  621. */
  622. public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
  623. // Ensure sane PHP environment variables.
  624. static::bootEnvironment();
  625. try {
  626. $this->initializeSettings($request);
  627. // Redirect the user to the installation script if Drupal has not been
  628. // installed yet (i.e., if no $databases array has been defined in the
  629. // settings.php file) and we are not already installing.
  630. if (!Database::getConnectionInfo() && !InstallerKernel::installationAttempted() && PHP_SAPI !== 'cli') {
  631. $response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']);
  632. }
  633. else {
  634. $this->boot();
  635. $response = $this->getHttpKernel()->handle($request, $type, $catch);
  636. }
  637. }
  638. catch (\Exception $e) {
  639. if ($catch === FALSE) {
  640. throw $e;
  641. }
  642. $response = $this->handleException($e, $request, $type);
  643. }
  644. // Adapt response headers to the current request.
  645. $response->prepare($request);
  646. return $response;
  647. }
  648. /**
  649. * Converts an exception into a response.
  650. *
  651. * @param \Exception $e
  652. * An exception
  653. * @param \Symfony\Component\HttpFoundation\Request $request
  654. * A Request instance
  655. * @param int $type
  656. * The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
  657. * HttpKernelInterface::SUB_REQUEST)
  658. *
  659. * @return \Symfony\Component\HttpFoundation\Response
  660. * A Response instance
  661. *
  662. * @throws \Exception
  663. * If the passed in exception cannot be turned into a response.
  664. */
  665. protected function handleException(\Exception $e, $request, $type) {
  666. if ($this->shouldRedirectToInstaller($e, $this->container ? $this->container->get('database') : NULL)) {
  667. return new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']);
  668. }
  669. if ($e instanceof HttpExceptionInterface) {
  670. $response = new Response($e->getMessage(), $e->getStatusCode());
  671. $response->headers->add($e->getHeaders());
  672. return $response;
  673. }
  674. throw $e;
  675. }
  676. /**
  677. * {@inheritdoc}
  678. */
  679. public function prepareLegacyRequest(Request $request) {
  680. $this->boot();
  681. $this->preHandle($request);
  682. // Setup services which are normally initialized from within stack
  683. // middleware or during the request kernel event.
  684. if (PHP_SAPI !== 'cli') {
  685. $request->setSession($this->container->get('session'));
  686. }
  687. $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
  688. $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
  689. $this->container->get('request_stack')->push($request);
  690. $this->container->get('router.request_context')->fromRequest($request);
  691. @trigger_error(__NAMESPACE__ . '\DrupalKernel::prepareLegacyRequest is deprecated drupal:8.0.0 and is removed from drupal:9.0.0. Use DrupalKernel::boot() and DrupalKernel::preHandle() instead. See https://www.drupal.org/node/3070678', E_USER_DEPRECATED);
  692. return $this;
  693. }
  694. /**
  695. * Returns module data on the filesystem.
  696. *
  697. * @param $module
  698. * The name of the module.
  699. *
  700. * @return \Drupal\Core\Extension\Extension|bool
  701. * Returns an Extension object if the module is found, FALSE otherwise.
  702. */
  703. protected function moduleData($module) {
  704. if (!$this->moduleData) {
  705. // First, find profiles.
  706. $listing = new ExtensionDiscovery($this->root);
  707. $listing->setProfileDirectories([]);
  708. $all_profiles = $listing->scan('profile');
  709. $profiles = array_intersect_key($all_profiles, $this->moduleList);
  710. $profile_directories = array_map(function ($profile) {
  711. return $profile->getPath();
  712. }, $profiles);
  713. $listing->setProfileDirectories($profile_directories);
  714. // Now find modules.
  715. $this->moduleData = $profiles + $listing->scan('module');
  716. }
  717. return isset($this->moduleData[$module]) ? $this->moduleData[$module] : FALSE;
  718. }
  719. /**
  720. * Implements Drupal\Core\DrupalKernelInterface::updateModules().
  721. *
  722. * @todo Remove obsolete $module_list parameter. Only $module_filenames is
  723. * needed.
  724. */
  725. public function updateModules(array $module_list, array $module_filenames = []) {
  726. $pre_existing_module_namespaces = [];
  727. if ($this->booted && is_array($this->moduleList)) {
  728. $pre_existing_module_namespaces = $this->getModuleNamespacesPsr4($this->getModuleFileNames());
  729. }
  730. $this->moduleList = $module_list;
  731. foreach ($module_filenames as $name => $extension) {
  732. $this->moduleData[$name] = $extension;
  733. }
  734. // If we haven't yet booted, we don't need to do anything: the new module
  735. // list will take effect when boot() is called. However we set a
  736. // flag that the container needs a rebuild, so that a potentially cached
  737. // container is not used. If we have already booted, then rebuild the
  738. // container in order to refresh the serviceProvider list and container.
  739. $this->containerNeedsRebuild = TRUE;
  740. if ($this->booted) {
  741. // We need to register any new namespaces to a new class loader because
  742. // the current class loader might have stored a negative result for a
  743. // class that is now available.
  744. // @see \Composer\Autoload\ClassLoader::findFile()
  745. $new_namespaces = array_diff_key(
  746. $this->getModuleNamespacesPsr4($this->getModuleFileNames()),
  747. $pre_existing_module_namespaces
  748. );
  749. if (!empty($new_namespaces)) {
  750. $additional_class_loader = new ClassLoader();
  751. $this->classLoaderAddMultiplePsr4($new_namespaces, $additional_class_loader);
  752. $additional_class_loader->register();
  753. }
  754. $this->initializeContainer();
  755. }
  756. }
  757. /**
  758. * Returns the container cache key based on the environment.
  759. *
  760. * The 'environment' consists of:
  761. * - The kernel environment string.
  762. * - The Drupal version constant.
  763. * - The deployment identifier from settings.php. This allows custom
  764. * deployments to force a container rebuild.
  765. * - The operating system running PHP. This allows compiler passes to optimize
  766. * services for different operating systems.
  767. * - The paths to any additional container YAMLs from settings.php.
  768. *
  769. * @return string
  770. * The cache key used for the service container.
  771. */
  772. protected function getContainerCacheKey() {
  773. $parts = ['service_container', $this->environment, \Drupal::VERSION, Settings::get('deployment_identifier'), PHP_OS, serialize(Settings::get('container_yamls'))];
  774. return implode(':', $parts);
  775. }
  776. /**
  777. * Returns the kernel parameters.
  778. *
  779. * @return array An array of kernel parameters
  780. */
  781. protected function getKernelParameters() {
  782. return [
  783. 'kernel.environment' => $this->environment,
  784. ];
  785. }
  786. /**
  787. * Initializes the service container.
  788. *
  789. * @return \Symfony\Component\DependencyInjection\ContainerInterface
  790. */
  791. protected function initializeContainer() {
  792. $this->containerNeedsDumping = FALSE;
  793. $session_started = FALSE;
  794. if (isset($this->container)) {
  795. // Save the id of the currently logged in user.
  796. if ($this->container->initialized('current_user')) {
  797. $current_user_id = $this->container->get('current_user')->id();
  798. }
  799. // If there is a session, close and save it.
  800. if ($this->container->initialized('session')) {
  801. $session = $this->container->get('session');
  802. if ($session->isStarted()) {
  803. $session_started = TRUE;
  804. $session->save();
  805. }
  806. unset($session);
  807. }
  808. }
  809. // If we haven't booted yet but there is a container, then we're asked to
  810. // boot the container injected via setContainer().
  811. // @see \Drupal\KernelTests\KernelTestBase::setUp()
  812. if (isset($this->container) && !$this->booted) {
  813. $container = $this->container;
  814. }
  815. // If the module list hasn't already been set in updateModules and we are
  816. // not forcing a rebuild, then try and load the container from the cache.
  817. if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
  818. $container_definition = $this->getCachedContainerDefinition();
  819. }
  820. // If there is no container and no cached container definition, build a new
  821. // one from scratch.
  822. if (!isset($container) && !isset($container_definition)) {
  823. $container = $this->compileContainer();
  824. // Only dump the container if dumping is allowed. This is useful for
  825. // KernelTestBase, which never wants to use the real container, but always
  826. // the container builder.
  827. if ($this->allowDumping) {
  828. $dumper = new $this->phpArrayDumperClass($container);
  829. $container_definition = $dumper->getArray();
  830. }
  831. }
  832. // The container was rebuilt successfully.
  833. $this->containerNeedsRebuild = FALSE;
  834. // Only create a new class if we have a container definition.
  835. if (isset($container_definition)) {
  836. $class = Settings::get('container_base_class', '\Drupal\Core\DependencyInjection\Container');
  837. $container = new $class($container_definition);
  838. }
  839. $this->attachSynthetic($container);
  840. $this->container = $container;
  841. if ($session_started) {
  842. $this->container->get('session')->start();
  843. }
  844. // The request stack is preserved across container rebuilds. Reinject the
  845. // new session into the master request if one was present before.
  846. if (($request_stack = $this->container->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE))) {
  847. if ($request = $request_stack->getMasterRequest()) {
  848. $subrequest = TRUE;
  849. if ($request->hasSession()) {
  850. $request->setSession($this->container->get('session'));
  851. }
  852. }
  853. }
  854. if (!empty($current_user_id)) {
  855. $this->container->get('current_user')->setInitialAccountId($current_user_id);
  856. }
  857. \Drupal::setContainer($this->container);
  858. // Allow other parts of the codebase to react on container initialization in
  859. // subrequest.
  860. if (!empty($subrequest)) {
  861. $this->container->get('event_dispatcher')->dispatch(self::CONTAINER_INITIALIZE_SUBREQUEST_FINISHED);
  862. }
  863. // If needs dumping flag was set, dump the container.
  864. if ($this->containerNeedsDumping && !$this->cacheDrupalContainer($container_definition)) {
  865. $this->container->get('logger.factory')->get('DrupalKernel')->error('Container cannot be saved to cache.');
  866. }
  867. return $this->container;
  868. }
  869. /**
  870. * Setup a consistent PHP environment.
  871. *
  872. * This method sets PHP environment options we want to be sure are set
  873. * correctly for security or just saneness.
  874. *
  875. * @param string $app_root
  876. * (optional) The path to the application root as a string. If not supplied,
  877. * the application root will be computed.
  878. */
  879. public static function bootEnvironment($app_root = NULL) {
  880. if (static::$isEnvironmentInitialized) {
  881. return;
  882. }
  883. // Determine the application root if it's not supplied.
  884. if ($app_root === NULL) {
  885. $app_root = static::guessApplicationRoot();
  886. }
  887. // Include our bootstrap file.
  888. require_once $app_root . '/core/includes/bootstrap.inc';
  889. // Enforce E_STRICT, but allow users to set levels not part of E_STRICT.
  890. error_reporting(E_STRICT | E_ALL);
  891. // Override PHP settings required for Drupal to work properly.
  892. // sites/default/default.settings.php contains more runtime settings.
  893. // The .htaccess file contains settings that cannot be changed at runtime.
  894. if (PHP_SAPI !== 'cli') {
  895. // Use session cookies, not transparent sessions that puts the session id
  896. // in the query string.
  897. ini_set('session.use_cookies', '1');
  898. ini_set('session.use_only_cookies', '1');
  899. ini_set('session.use_trans_sid', '0');
  900. // Don't send HTTP headers using PHP's session handler.
  901. // Send an empty string to disable the cache limiter.
  902. ini_set('session.cache_limiter', '');
  903. // Use httponly session cookies.
  904. ini_set('session.cookie_httponly', '1');
  905. }
  906. // Set sane locale settings, to ensure consistent string, dates, times and
  907. // numbers handling.
  908. setlocale(LC_ALL, 'C');
  909. // Set appropriate configuration for multi-byte strings.
  910. mb_internal_encoding('utf-8');
  911. mb_language('uni');
  912. // Indicate that code is operating in a test child site.
  913. if (!defined('DRUPAL_TEST_IN_CHILD_SITE')) {
  914. if ($test_prefix = drupal_valid_test_ua()) {
  915. $test_db = new TestDatabase($test_prefix);
  916. // Only code that interfaces directly with tests should rely on this
  917. // constant; e.g., the error/exception handler conditionally adds further
  918. // error information into HTTP response headers that are consumed by
  919. // Simpletest's internal browser.
  920. define('DRUPAL_TEST_IN_CHILD_SITE', TRUE);
  921. // Web tests are to be conducted with runtime assertions active.
  922. assert_options(ASSERT_ACTIVE, TRUE);
  923. Handle::register();
  924. // Log fatal errors to the test site directory.
  925. ini_set('log_errors', 1);
  926. ini_set('error_log', $app_root . '/' . $test_db->getTestSitePath() . '/error.log');
  927. // Ensure that a rewritten settings.php is used if opcache is on.
  928. ini_set('opcache.validate_timestamps', 'on');
  929. ini_set('opcache.revalidate_freq', 0);
  930. }
  931. else {
  932. // Ensure that no other code defines this.
  933. define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
  934. }
  935. }
  936. // Set the Drupal custom error handler.
  937. set_error_handler('_drupal_error_handler');
  938. set_exception_handler('_drupal_exception_handler');
  939. static::$isEnvironmentInitialized = TRUE;
  940. }
  941. /**
  942. * Locate site path and initialize settings singleton.
  943. *
  944. * @param \Symfony\Component\HttpFoundation\Request $request
  945. * The current request.
  946. *
  947. * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
  948. * In case the host name in the request is not trusted.
  949. */
  950. protected function initializeSettings(Request $request) {
  951. $site_path = static::findSitePath($request);
  952. $this->setSitePath($site_path);
  953. $class_loader_class = get_class($this->classLoader);
  954. Settings::initialize($this->root, $site_path, $this->classLoader);
  955. // Initialize our list of trusted HTTP Host headers to protect against
  956. // header attacks.
  957. $host_patterns = Settings::get('trusted_host_patterns', []);
  958. if (PHP_SAPI !== 'cli' && !empty($host_patterns)) {
  959. if (static::setupTrustedHosts($request, $host_patterns) === FALSE) {
  960. throw new BadRequestHttpException('The provided host name is not valid for this server.');
  961. }
  962. }
  963. // If the class loader is still the same, possibly
  964. // upgrade to an optimized class loader.
  965. if ($class_loader_class == get_class($this->classLoader)
  966. && Settings::get('class_loader_auto_detect', TRUE)) {
  967. $prefix = Settings::getApcuPrefix('class_loader', $this->root);
  968. $loader = NULL;
  969. // We autodetect one of the following three optimized classloaders, if
  970. // their underlying extension exists.
  971. if (function_exists('apcu_fetch')) {
  972. $loader = new ApcClassLoader($prefix, $this->classLoader);
  973. }
  974. elseif (extension_loaded('wincache')) {
  975. $loader = new WinCacheClassLoader($prefix, $this->classLoader);
  976. }
  977. elseif (extension_loaded('xcache')) {
  978. $loader = new XcacheClassLoader($prefix, $this->classLoader);
  979. }
  980. if (!empty($loader)) {
  981. $this->classLoader->unregister();
  982. // The optimized classloader might be persistent and store cache misses.
  983. // For example, once a cache miss is stored in APCu clearing it on a
  984. // specific web-head will not clear any other web-heads. Therefore
  985. // fallback to the composer class loader that only statically caches
  986. // misses.
  987. $old_loader = $this->classLoader;
  988. $this->classLoader = $loader;
  989. // Our class loaders are prepended to ensure they come first like the
  990. // class loader they are replacing.
  991. $old_loader->register(TRUE);
  992. $loader->register(TRUE);
  993. }
  994. }
  995. }
  996. /**
  997. * Bootstraps the legacy global request variables.
  998. *
  999. * @param \Symfony\Component\HttpFoundation\Request $request
  1000. * The current request.
  1001. *
  1002. * @todo D8: Eliminate this entirely in favor of Request object.
  1003. */
  1004. protected function initializeRequestGlobals(Request $request) {
  1005. global $base_url;
  1006. // Set and derived from $base_url by this function.
  1007. global $base_path, $base_root;
  1008. global $base_secure_url, $base_insecure_url;
  1009. // Create base URL.
  1010. $base_root = $request->getSchemeAndHttpHost();
  1011. $base_url = $base_root;
  1012. // For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is
  1013. // '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'.
  1014. if ($dir = rtrim(dirname($request->server->get('SCRIPT_NAME')), '\/')) {
  1015. // Remove "core" directory if present, allowing install.php,
  1016. // authorize.php, and others to auto-detect a base path.
  1017. $core_position = strrpos($dir, '/core');
  1018. if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
  1019. $base_path = substr($dir, 0, $core_position);
  1020. }
  1021. else {
  1022. $base_path = $dir;
  1023. }
  1024. $base_url .= $base_path;
  1025. $base_path .= '/';
  1026. }
  1027. else {
  1028. $base_path = '/';
  1029. }
  1030. $base_secure_url = str_replace('http://', 'https://', $base_url);
  1031. $base_insecure_url = str_replace('https://', 'http://', $base_url);
  1032. }
  1033. /**
  1034. * Returns service instances to persist from an old container to a new one.
  1035. */
  1036. protected function getServicesToPersist(ContainerInterface $container) {
  1037. $persist = [];
  1038. foreach ($container->getParameter('persist_ids') as $id) {
  1039. // It's pointless to persist services not yet initialized.
  1040. if ($container->initialized($id)) {
  1041. $persist[$id] = $container->get($id);
  1042. }
  1043. }
  1044. return $persist;
  1045. }
  1046. /**
  1047. * Moves persistent service instances into a new container.
  1048. */
  1049. protected function persistServices(ContainerInterface $container, array $persist) {
  1050. foreach ($persist as $id => $object) {
  1051. // Do not override services already set() on the new container, for
  1052. // example 'service_container'.
  1053. if (!$container->initialized($id)) {
  1054. $container->set($id, $object);
  1055. }
  1056. }
  1057. }
  1058. /**
  1059. * {@inheritdoc}
  1060. */
  1061. public function rebuildContainer() {
  1062. // Empty module properties and for them to be reloaded from scratch.
  1063. $this->moduleList = NULL;
  1064. $this->moduleData = [];
  1065. $this->containerNeedsRebuild = TRUE;
  1066. return $this->initializeContainer();
  1067. }
  1068. /**
  1069. * {@inheritdoc}
  1070. */
  1071. public function invalidateContainer() {
  1072. // An invalidated container needs a rebuild.
  1073. $this->containerNeedsRebuild = TRUE;
  1074. // If we have not yet booted, settings or bootstrap services might not yet
  1075. // be available. In that case the container will not be loaded from cache
  1076. // due to the above setting when the Kernel is booted.
  1077. if (!$this->booted) {
  1078. return;
  1079. }
  1080. // Also remove the container definition from the cache backend.
  1081. $this->bootstrapContainer->get('cache.container')->deleteAll();
  1082. }
  1083. /**
  1084. * Attach synthetic values on to kernel.
  1085. *
  1086. * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
  1087. * Container object
  1088. *
  1089. * @return \Symfony\Component\DependencyInjection\ContainerInterface
  1090. */
  1091. protected function attachSynthetic(ContainerInterface $container) {
  1092. $persist = [];
  1093. if (isset($this->container)) {
  1094. $persist = $this->getServicesToPersist($this->container);
  1095. }
  1096. $this->persistServices($container, $persist);
  1097. // All namespaces must be registered before we attempt to use any service
  1098. // from the container.
  1099. $this->classLoaderAddMultiplePsr4($container->getParameter('container.namespaces'));
  1100. $container->set('kernel', $this);
  1101. // Set the class loader which was registered as a synthetic service.
  1102. $container->set('class_loader', $this->classLoader);
  1103. return $container;
  1104. }
  1105. /**
  1106. * Compiles a new service container.
  1107. *
  1108. * @return \Drupal\Core\DependencyInjection\ContainerBuilder The compiled service container
  1109. */
  1110. protected function compileContainer() {
  1111. // We are forcing a container build so it is reasonable to assume that the
  1112. // calling method knows something about the system has changed requiring the
  1113. // container to be dumped to the filesystem.
  1114. if ($this->allowDumping) {
  1115. $this->containerNeedsDumping = TRUE;
  1116. }
  1117. $this->initializeServiceProviders();
  1118. $container = $this->getContainerBuilder();
  1119. $container->set('kernel', $this);
  1120. $container->setParameter('container.modules', $this->getModulesParameter());
  1121. $container->setParameter('install_profile', $this->getInstallProfile());
  1122. // Get a list of namespaces and put it onto the container.
  1123. $namespaces = $this->getModuleNamespacesPsr4($this->getModuleFileNames());
  1124. // Add all components in \Drupal\Core and \Drupal\Component that have one of
  1125. // the following directories:
  1126. // - Element
  1127. // - Entity
  1128. // - Plugin
  1129. foreach (['Core', 'Component'] as $parent_directory) {
  1130. $path = 'core/lib/Drupal/' . $parent_directory;
  1131. $parent_namespace = 'Drupal\\' . $parent_directory;
  1132. foreach (new \DirectoryIterator($this->root . '/' . $path) as $component) {
  1133. /** @var $component \DirectoryIterator */
  1134. $pathname = $component->getPathname();
  1135. if (!$component->isDot() && $component->isDir() && (
  1136. is_dir($pathname . '/Plugin') ||
  1137. is_dir($pathname . '/Entity') ||
  1138. is_dir($pathname . '/Element')
  1139. )) {
  1140. $namespaces[$parent_namespace . '\\' . $component->getFilename()] = $path . '/' . $component->getFilename();
  1141. }
  1142. }
  1143. }
  1144. $container->setParameter('container.namespaces', $namespaces);
  1145. // Store the default language values on the container. This is so that the
  1146. // default language can be configured using the configuration factory. This
  1147. // avoids the circular dependencies that would created by
  1148. // \Drupal\language\LanguageServiceProvider::alter() and allows the default
  1149. // language to not be English in the installer.
  1150. $default_language_values = Language::$defaultValues;
  1151. if ($system = $this->getConfigStorage()->read('system.site')) {
  1152. if ($default_language_values['id'] != $system['langcode']) {
  1153. $default_language_values = ['id' => $system['langcode']];
  1154. }
  1155. }
  1156. $container->setParameter('language.default_values', $default_language_values);
  1157. // Register synthetic services.
  1158. $container->register('class_loader')->setSynthetic(TRUE);
  1159. $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
  1160. $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
  1161. // Register application services.
  1162. $yaml_loader = new YamlFileLoader($container);
  1163. foreach ($this->serviceYamls['app'] as $filename) {
  1164. $yaml_loader->load($filename);
  1165. }
  1166. foreach ($this->serviceProviders['app'] as $provider) {
  1167. if ($provider instanceof ServiceProviderInterface) {
  1168. $provider->register($container);
  1169. }
  1170. }
  1171. // Register site-specific service overrides.
  1172. foreach ($this->serviceYamls['site'] as $filename) {
  1173. $yaml_loader->load($filename);
  1174. }
  1175. foreach ($this->serviceProviders['site'] as $provider) {
  1176. if ($provider instanceof ServiceProviderInterface) {
  1177. $provider->register($container);
  1178. }
  1179. }
  1180. // Identify all services whose instances should be persisted when rebuilding
  1181. // the container during the lifetime of the kernel (e.g., during a kernel
  1182. // reboot). Include synthetic services, because by definition, they cannot
  1183. // be automatically reinstantiated. Also include services tagged to persist.
  1184. $persist_ids = [];
  1185. foreach ($container->getDefinitions() as $id => $definition) {
  1186. // It does not make sense to persist the container itself, exclude it.
  1187. if ($id !== 'service_container' && ($definition->isSynthetic() || $definition->getTag('persist'))) {
  1188. $persist_ids[] = $id;
  1189. }
  1190. }
  1191. $container->setParameter('persist_ids', $persist_ids);
  1192. $container->compile();
  1193. return $container;
  1194. }
  1195. /**
  1196. * Registers all service providers to the kernel.
  1197. *
  1198. * @throws \LogicException
  1199. */
  1200. protected function initializeServiceProviders() {
  1201. $this->discoverServiceProviders();
  1202. $this->serviceProviders = [
  1203. 'app' => [],
  1204. 'site' => [],
  1205. ];
  1206. foreach ($this->serviceProviderClasses as $origin => $classes) {
  1207. foreach ($classes as $name => $class) {
  1208. if (!is_object($class)) {
  1209. $this->serviceProviders[$origin][$name] = new $class();
  1210. }
  1211. else {
  1212. $this->serviceProviders[$origin][$name] = $class;
  1213. }
  1214. }
  1215. }
  1216. }
  1217. /**
  1218. * Gets a new ContainerBuilder instance used to build the service container.
  1219. *
  1220. * @return \Drupal\Core\DependencyInjection\ContainerBuilder
  1221. */
  1222. protected function getContainerBuilder() {
  1223. return new ContainerBuilder(new ParameterBag($this->getKernelParameters()));
  1224. }
  1225. /**
  1226. * Stores the container definition in a cache.
  1227. *
  1228. * @param array $container_definition
  1229. * The container definition to cache.
  1230. *
  1231. * @return bool
  1232. * TRUE if the container was successfully cached.
  1233. */
  1234. protected function cacheDrupalContainer(array $container_definition) {
  1235. $saved = TRUE;
  1236. try {
  1237. $this->bootstrapContainer->get('cache.container')->set($this->getContainerCacheKey(), $container_definition);
  1238. }
  1239. catch (\Exception $e) {
  1240. // There is no way to get from the Cache API if the cache set was
  1241. // successful or not, hence an Exception is caught and the caller informed
  1242. // about the error condition.
  1243. $saved = FALSE;
  1244. }
  1245. return $saved;
  1246. }
  1247. /**
  1248. * Gets a http kernel from the container
  1249. *
  1250. * @return \Symfony\Component\HttpKernel\HttpKernelInterface
  1251. */
  1252. protected function getHttpKernel() {
  1253. return $this->container->get('http_kernel');
  1254. }
  1255. /**
  1256. * Returns the active configuration storage to use during building the container.
  1257. *
  1258. * @return \Drupal\Core\Config\StorageInterface
  1259. */
  1260. protected function getConfigStorage() {
  1261. if (!isset($this->configStorage)) {
  1262. // The active configuration storage may not exist yet; e.g., in the early
  1263. // installer. Catch the exception thrown by config_get_config_directory().
  1264. try {
  1265. $this->configStorage = BootstrapConfigStorageFactory::get($this->classLoader);
  1266. }
  1267. catch (\Exception $e) {
  1268. $this->configStorage = new NullStorage();
  1269. }
  1270. }
  1271. return $this->configStorage;
  1272. }
  1273. /**
  1274. * Returns an array of Extension class parameters for all enabled modules.
  1275. *
  1276. * @return array
  1277. */
  1278. protected function getModulesParameter() {
  1279. $extensions = [];
  1280. foreach ($this->moduleList as $name => $weight) {
  1281. if ($data = $this->moduleData($name)) {
  1282. $extensions[$name] = [
  1283. 'type' => $data->getType(),
  1284. 'pathname' => $data->getPathname(),
  1285. 'filename' => $data->getExtensionFilename(),
  1286. ];
  1287. }
  1288. }
  1289. return $extensions;
  1290. }
  1291. /**
  1292. * Gets the file name for each enabled module.
  1293. *
  1294. * @return array
  1295. * Array where each key is a module name, and each value is a path to the
  1296. * respective *.info.yml file.
  1297. */
  1298. protected function getModuleFileNames() {
  1299. $filenames = [];
  1300. foreach ($this->moduleList as $module => $weight) {
  1301. if ($data = $this->moduleData($module)) {
  1302. $filenames[$module] = $data->getPathname();
  1303. }
  1304. }
  1305. return $filenames;
  1306. }
  1307. /**
  1308. * Gets the PSR-4 base directories for module namespaces.
  1309. *
  1310. * @param string[] $module_file_names
  1311. * Array where each key is a module name, and each value is a path to the
  1312. * respective *.info.yml file.
  1313. *
  1314. * @return string[]
  1315. * Array where each key is a module namespace like 'Drupal\system', and each
  1316. * value is the PSR-4 base directory associated with the module namespace.
  1317. */
  1318. protected function getModuleNamespacesPsr4($module_file_names) {
  1319. $namespaces = [];
  1320. foreach ($module_file_names as $module => $filename) {
  1321. $namespaces["Drupal\\$module"] = dirname($filename) . '/src';
  1322. }
  1323. return $namespaces;
  1324. }
  1325. /**
  1326. * Registers a list of namespaces with PSR-4 directories for class loading.
  1327. *
  1328. * @param array $namespaces
  1329. * Array where each key is a namespace like 'Drupal\system', and each value
  1330. * is either a PSR-4 base directory, or an array of PSR-4 base directories
  1331. * associated with this namespace.
  1332. * @param object $class_loader
  1333. * The class loader. Normally \Composer\Autoload\ClassLoader, as included by
  1334. * the front controller, but may also be decorated; e.g.,
  1335. * \Symfony\Component\ClassLoader\ApcClassLoader.
  1336. */
  1337. protected function classLoaderAddMultiplePsr4(array $namespaces = [], $class_loader = NULL) {
  1338. if ($class_loader === NULL) {
  1339. $class_loader = $this->classLoader;
  1340. }
  1341. foreach ($namespaces as $prefix => $paths) {
  1342. if (is_array($paths)) {
  1343. foreach ($paths as $key => $value) {
  1344. $paths[$key] = $this->root . '/' . $value;
  1345. }
  1346. }
  1347. elseif (is_string($paths)) {
  1348. $paths = $this->root . '/' . $paths;
  1349. }
  1350. $class_loader->addPsr4($prefix . '\\', $paths);
  1351. }
  1352. }
  1353. /**
  1354. * Validates a hostname length.
  1355. *
  1356. * @param string $host
  1357. * A hostname.
  1358. *
  1359. * @return bool
  1360. * TRUE if the length is appropriate, or FALSE otherwise.
  1361. */
  1362. protected static function validateHostnameLength($host) {
  1363. // Limit the length of the host name to 1000 bytes to prevent DoS attacks
  1364. // with long host names.
  1365. return strlen($host) <= 1000
  1366. // Limit the number of subdomains and port separators to prevent DoS attacks
  1367. // in findSitePath().
  1368. && substr_count($host, '.') <= 100
  1369. && substr_count($host, ':') <= 100;
  1370. }
  1371. /**
  1372. * Validates the hostname supplied from the HTTP request.
  1373. *
  1374. * @param \Symfony\Component\HttpFoundation\Request $request
  1375. * The request object
  1376. *
  1377. * @return bool
  1378. * TRUE if the hostname is valid, or FALSE otherwise.
  1379. */
  1380. public static function validateHostname(Request $request) {
  1381. // $request->getHost() can throw an UnexpectedValueException if it
  1382. // detects a bad hostname, but it does not validate the length.
  1383. try {
  1384. $http_host = $request->getHost();
  1385. }
  1386. catch (\UnexpectedValueException $e) {
  1387. return FALSE;
  1388. }
  1389. if (static::validateHostnameLength($http_host) === FALSE) {
  1390. return FALSE;
  1391. }
  1392. return TRUE;
  1393. }
  1394. /**
  1395. * Sets up the lists of trusted HTTP Host headers.
  1396. *
  1397. * Since the HTTP Host header can be set by the user making the request, it
  1398. * is possible to create an attack vectors against a site by overriding this.
  1399. * Symfony provides a mechanism for creating a list of trusted Host values.
  1400. *
  1401. * Host patterns (as regular expressions) can be configured through
  1402. * settings.php for multisite installations, sites using ServerAlias without
  1403. * canonical redirection, or configurations where the site responds to default
  1404. * requests. For example,
  1405. *
  1406. * @code
  1407. * $settings['trusted_host_patterns'] = array(
  1408. * '^example\.com$',
  1409. * '^*.example\.com$',
  1410. * );
  1411. * @endcode
  1412. *
  1413. * @param \Symfony\Component\HttpFoundation\Request $request
  1414. * The request object.
  1415. * @param array $host_patterns
  1416. * The array of trusted host patterns.
  1417. *
  1418. * @return bool
  1419. * TRUE if the Host header is trusted, FALSE otherwise.
  1420. *
  1421. * @see https://www.drupal.org/docs/8/install/trusted-host-settings
  1422. * @see \Drupal\Core\Http\TrustedHostsRequestFactory
  1423. */
  1424. protected static function setupTrustedHosts(Request $request, $host_patterns) {
  1425. $request->setTrustedHosts($host_patterns);
  1426. // Get the host, which will validate the current request.
  1427. try {
  1428. $host = $request->getHost();
  1429. // Fake requests created through Request::create() without passing in the
  1430. // server variables from the main request have a default host of
  1431. // 'localhost'. If 'localhost' does not match any of the trusted host
  1432. // patterns these fake requests would fail the host verification. Instead,
  1433. // TrustedHostsRequestFactory makes sure to pass in the server variables
  1434. // from the main request.
  1435. $request_factory = new TrustedHostsRequestFactory($host);
  1436. Request::setFactory([$request_factory, 'createRequest']);
  1437. }
  1438. catch (\UnexpectedValueException $e) {
  1439. return FALSE;
  1440. }
  1441. return TRUE;
  1442. }
  1443. /**
  1444. * Add service files.
  1445. *
  1446. * @param string[] $service_yamls
  1447. * A list of service files.
  1448. */
  1449. protected function addServiceFiles(array $service_yamls) {
  1450. $this->serviceYamls['site'] = array_filter($service_yamls, 'file_exists');
  1451. }
  1452. /**
  1453. * Gets the active install profile.
  1454. *
  1455. * @return string|null
  1456. * The name of the any active install profile or distribution.
  1457. */
  1458. protected function getInstallProfile() {
  1459. $config = $this->getConfigStorage()->read('core.extension');
  1460. if (isset($config['profile'])) {
  1461. $install_profile = $config['profile'];
  1462. }
  1463. // @todo https://www.drupal.org/node/2831065 remove the BC layer.
  1464. else {
  1465. // If system_update_8300() has not yet run fallback to using settings.
  1466. $settings = Settings::getAll();
  1467. $install_profile = isset($settings['install_profile']) ? $settings['install_profile'] : NULL;
  1468. }
  1469. // Normalize an empty string to a NULL value.
  1470. return empty($install_profile) ? NULL : $install_profile;
  1471. }
  1472. }