Kernel.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel;
  11. use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
  12. use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
  13. use Symfony\Component\Config\ConfigCache;
  14. use Symfony\Component\Config\Loader\DelegatingLoader;
  15. use Symfony\Component\Config\Loader\LoaderResolver;
  16. use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
  17. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  18. use Symfony\Component\DependencyInjection\Compiler\PassConfig;
  19. use Symfony\Component\DependencyInjection\ContainerBuilder;
  20. use Symfony\Component\DependencyInjection\ContainerInterface;
  21. use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
  22. use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
  23. use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
  24. use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
  25. use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
  26. use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
  27. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  28. use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
  29. use Symfony\Component\ErrorHandler\DebugClassLoader;
  30. use Symfony\Component\Filesystem\Filesystem;
  31. use Symfony\Component\HttpFoundation\Request;
  32. use Symfony\Component\HttpFoundation\Response;
  33. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  34. use Symfony\Component\HttpKernel\Config\FileLocator;
  35. use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
  36. use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
  37. /**
  38. * The Kernel is the heart of the Symfony system.
  39. *
  40. * It manages an environment made of bundles.
  41. *
  42. * Environment names must always start with a letter and
  43. * they must only contain letters and numbers.
  44. *
  45. * @author Fabien Potencier <fabien@symfony.com>
  46. */
  47. abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface
  48. {
  49. /**
  50. * @var BundleInterface[]
  51. */
  52. protected $bundles = [];
  53. protected $container;
  54. /**
  55. * @deprecated since Symfony 4.2
  56. */
  57. protected $rootDir;
  58. protected $environment;
  59. protected $debug;
  60. protected $booted = false;
  61. /**
  62. * @deprecated since Symfony 4.2
  63. */
  64. protected $name;
  65. protected $startTime;
  66. private $projectDir;
  67. private $warmupDir;
  68. private $requestStackSize = 0;
  69. private $resetServices = false;
  70. private static $freshCache = [];
  71. const VERSION = '4.4.2';
  72. const VERSION_ID = 40402;
  73. const MAJOR_VERSION = 4;
  74. const MINOR_VERSION = 4;
  75. const RELEASE_VERSION = 2;
  76. const EXTRA_VERSION = '';
  77. const END_OF_MAINTENANCE = '11/2022';
  78. const END_OF_LIFE = '11/2023';
  79. public function __construct(string $environment, bool $debug)
  80. {
  81. $this->environment = $environment;
  82. $this->debug = $debug;
  83. $this->rootDir = $this->getRootDir(false);
  84. $this->name = $this->getName(false);
  85. }
  86. public function __clone()
  87. {
  88. $this->booted = false;
  89. $this->container = null;
  90. $this->requestStackSize = 0;
  91. $this->resetServices = false;
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function boot()
  97. {
  98. if (true === $this->booted) {
  99. if (!$this->requestStackSize && $this->resetServices) {
  100. if ($this->container->has('services_resetter')) {
  101. $this->container->get('services_resetter')->reset();
  102. }
  103. $this->resetServices = false;
  104. if ($this->debug) {
  105. $this->startTime = microtime(true);
  106. }
  107. }
  108. return;
  109. }
  110. if ($this->debug) {
  111. $this->startTime = microtime(true);
  112. }
  113. if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) {
  114. putenv('SHELL_VERBOSITY=3');
  115. $_ENV['SHELL_VERBOSITY'] = 3;
  116. $_SERVER['SHELL_VERBOSITY'] = 3;
  117. }
  118. // init bundles
  119. $this->initializeBundles();
  120. // init container
  121. $this->initializeContainer();
  122. foreach ($this->getBundles() as $bundle) {
  123. $bundle->setContainer($this->container);
  124. $bundle->boot();
  125. }
  126. $this->booted = true;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function reboot($warmupDir)
  132. {
  133. $this->shutdown();
  134. $this->warmupDir = $warmupDir;
  135. $this->boot();
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function terminate(Request $request, Response $response)
  141. {
  142. if (false === $this->booted) {
  143. return;
  144. }
  145. if ($this->getHttpKernel() instanceof TerminableInterface) {
  146. $this->getHttpKernel()->terminate($request, $response);
  147. }
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function shutdown()
  153. {
  154. if (false === $this->booted) {
  155. return;
  156. }
  157. $this->booted = false;
  158. foreach ($this->getBundles() as $bundle) {
  159. $bundle->shutdown();
  160. $bundle->setContainer(null);
  161. }
  162. $this->container = null;
  163. $this->requestStackSize = 0;
  164. $this->resetServices = false;
  165. }
  166. /**
  167. * {@inheritdoc}
  168. */
  169. public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
  170. {
  171. $this->boot();
  172. ++$this->requestStackSize;
  173. $this->resetServices = true;
  174. try {
  175. return $this->getHttpKernel()->handle($request, $type, $catch);
  176. } finally {
  177. --$this->requestStackSize;
  178. }
  179. }
  180. /**
  181. * Gets a HTTP kernel from the container.
  182. *
  183. * @return HttpKernelInterface
  184. */
  185. protected function getHttpKernel()
  186. {
  187. return $this->container->get('http_kernel');
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function getBundles()
  193. {
  194. return $this->bundles;
  195. }
  196. /**
  197. * {@inheritdoc}
  198. */
  199. public function getBundle($name)
  200. {
  201. if (!isset($this->bundles[$name])) {
  202. $class = \get_class($this);
  203. $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
  204. throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, $class));
  205. }
  206. return $this->bundles[$name];
  207. }
  208. /**
  209. * {@inheritdoc}
  210. */
  211. public function locateResource($name/*, $dir = null, $first = true, $triggerDeprecation = true*/)
  212. {
  213. if (2 <= \func_num_args()) {
  214. $dir = func_get_arg(1);
  215. $first = 3 <= \func_num_args() ? func_get_arg(2) : true;
  216. if (4 !== \func_num_args() || func_get_arg(3)) {
  217. @trigger_error(sprintf('Passing more than one argument to %s is deprecated since Symfony 4.4 and will be removed in 5.0.', __METHOD__), E_USER_DEPRECATED);
  218. }
  219. } else {
  220. $dir = null;
  221. $first = true;
  222. }
  223. if ('@' !== $name[0]) {
  224. throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name));
  225. }
  226. if (false !== strpos($name, '..')) {
  227. throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name));
  228. }
  229. $bundleName = substr($name, 1);
  230. $path = '';
  231. if (false !== strpos($bundleName, '/')) {
  232. list($bundleName, $path) = explode('/', $bundleName, 2);
  233. }
  234. $isResource = 0 === strpos($path, 'Resources') && null !== $dir;
  235. $overridePath = substr($path, 9);
  236. $bundle = $this->getBundle($bundleName);
  237. $files = [];
  238. if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) {
  239. $files[] = $file;
  240. // see https://symfony.com/doc/current/bundles/override.html on how to overwrite parts of a bundle
  241. @trigger_error(sprintf('Overwriting the resource "%s" with "%s" is deprecated since Symfony 4.4 and will be removed in 5.0.', $name, $file), E_USER_DEPRECATED);
  242. }
  243. if (file_exists($file = $bundle->getPath().'/'.$path)) {
  244. if ($first && !$isResource) {
  245. return $file;
  246. }
  247. $files[] = $file;
  248. }
  249. if (\count($files) > 0) {
  250. return $first && $isResource ? $files[0] : $files;
  251. }
  252. throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name));
  253. }
  254. /**
  255. * {@inheritdoc}
  256. *
  257. * @deprecated since Symfony 4.2
  258. */
  259. public function getName(/* $triggerDeprecation = true */)
  260. {
  261. if (0 === \func_num_args() || func_get_arg(0)) {
  262. @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
  263. }
  264. if (null === $this->name) {
  265. $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
  266. if (ctype_digit($this->name[0])) {
  267. $this->name = '_'.$this->name;
  268. }
  269. }
  270. return $this->name;
  271. }
  272. /**
  273. * {@inheritdoc}
  274. */
  275. public function getEnvironment()
  276. {
  277. return $this->environment;
  278. }
  279. /**
  280. * {@inheritdoc}
  281. */
  282. public function isDebug()
  283. {
  284. return $this->debug;
  285. }
  286. /**
  287. * {@inheritdoc}
  288. *
  289. * @deprecated since Symfony 4.2, use getProjectDir() instead
  290. */
  291. public function getRootDir(/* $triggerDeprecation = true */)
  292. {
  293. if (0 === \func_num_args() || func_get_arg(0)) {
  294. @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use getProjectDir() instead.', __METHOD__), E_USER_DEPRECATED);
  295. }
  296. if (null === $this->rootDir) {
  297. $r = new \ReflectionObject($this);
  298. $this->rootDir = \dirname($r->getFileName());
  299. }
  300. return $this->rootDir;
  301. }
  302. /**
  303. * Gets the application root dir (path of the project's composer file).
  304. *
  305. * @return string The project root dir
  306. */
  307. public function getProjectDir()
  308. {
  309. if (null === $this->projectDir) {
  310. $r = new \ReflectionObject($this);
  311. if (!file_exists($dir = $r->getFileName())) {
  312. throw new \LogicException(sprintf('Cannot auto-detect project dir for kernel of class "%s".', $r->name));
  313. }
  314. $dir = $rootDir = \dirname($dir);
  315. while (!file_exists($dir.'/composer.json')) {
  316. if ($dir === \dirname($dir)) {
  317. return $this->projectDir = $rootDir;
  318. }
  319. $dir = \dirname($dir);
  320. }
  321. $this->projectDir = $dir;
  322. }
  323. return $this->projectDir;
  324. }
  325. /**
  326. * {@inheritdoc}
  327. */
  328. public function getContainer()
  329. {
  330. if (!$this->container) {
  331. @trigger_error('Getting the container from a non-booted kernel is deprecated since Symfony 4.4.', E_USER_DEPRECATED);
  332. }
  333. return $this->container;
  334. }
  335. /**
  336. * @internal
  337. */
  338. public function setAnnotatedClassCache(array $annotatedClasses)
  339. {
  340. file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
  341. }
  342. /**
  343. * {@inheritdoc}
  344. */
  345. public function getStartTime()
  346. {
  347. return $this->debug && null !== $this->startTime ? $this->startTime : -INF;
  348. }
  349. /**
  350. * {@inheritdoc}
  351. */
  352. public function getCacheDir()
  353. {
  354. return $this->getProjectDir().'/var/cache/'.$this->environment;
  355. }
  356. /**
  357. * {@inheritdoc}
  358. */
  359. public function getLogDir()
  360. {
  361. return $this->getProjectDir().'/var/log';
  362. }
  363. /**
  364. * {@inheritdoc}
  365. */
  366. public function getCharset()
  367. {
  368. return 'UTF-8';
  369. }
  370. /**
  371. * Gets the patterns defining the classes to parse and cache for annotations.
  372. */
  373. public function getAnnotatedClassesToCompile(): array
  374. {
  375. return [];
  376. }
  377. /**
  378. * Initializes bundles.
  379. *
  380. * @throws \LogicException if two bundles share a common name
  381. */
  382. protected function initializeBundles()
  383. {
  384. // init bundles
  385. $this->bundles = [];
  386. foreach ($this->registerBundles() as $bundle) {
  387. $name = $bundle->getName();
  388. if (isset($this->bundles[$name])) {
  389. throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name));
  390. }
  391. $this->bundles[$name] = $bundle;
  392. }
  393. }
  394. /**
  395. * The extension point similar to the Bundle::build() method.
  396. *
  397. * Use this method to register compiler passes and manipulate the container during the building process.
  398. */
  399. protected function build(ContainerBuilder $container)
  400. {
  401. }
  402. /**
  403. * Gets the container class.
  404. *
  405. * @throws \InvalidArgumentException If the generated classname is invalid
  406. *
  407. * @return string The container class
  408. */
  409. protected function getContainerClass()
  410. {
  411. $class = \get_class($this);
  412. $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).str_replace('.', '_', ContainerBuilder::hash($class)) : $class;
  413. $class = $this->name.str_replace('\\', '_', $class).ucfirst($this->environment).($this->debug ? 'Debug' : '').'Container';
  414. if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $class)) {
  415. throw new \InvalidArgumentException(sprintf('The environment "%s" contains invalid characters, it can only contain characters allowed in PHP class names.', $this->environment));
  416. }
  417. return $class;
  418. }
  419. /**
  420. * Gets the container's base class.
  421. *
  422. * All names except Container must be fully qualified.
  423. *
  424. * @return string
  425. */
  426. protected function getContainerBaseClass()
  427. {
  428. return 'Container';
  429. }
  430. /**
  431. * Initializes the service container.
  432. *
  433. * The cached version of the service container is used when fresh, otherwise the
  434. * container is built.
  435. */
  436. protected function initializeContainer()
  437. {
  438. $class = $this->getContainerClass();
  439. $cacheDir = $this->warmupDir ?: $this->getCacheDir();
  440. $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
  441. $cachePath = $cache->getPath();
  442. // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
  443. $errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
  444. try {
  445. if (file_exists($cachePath) && \is_object($this->container = include $cachePath)
  446. && (!$this->debug || (self::$freshCache[$cachePath] ?? $cache->isFresh()))
  447. ) {
  448. self::$freshCache[$cachePath] = true;
  449. $this->container->set('kernel', $this);
  450. error_reporting($errorLevel);
  451. return;
  452. }
  453. } catch (\Throwable $e) {
  454. }
  455. $oldContainer = \is_object($this->container) ? new \ReflectionClass($this->container) : $this->container = null;
  456. try {
  457. is_dir($cacheDir) ?: mkdir($cacheDir, 0777, true);
  458. if ($lock = fopen($cachePath, 'w')) {
  459. chmod($cachePath, 0666 & ~umask());
  460. flock($lock, LOCK_EX | LOCK_NB, $wouldBlock);
  461. if (!flock($lock, $wouldBlock ? LOCK_SH : LOCK_EX)) {
  462. fclose($lock);
  463. } else {
  464. $cache = new class($cachePath, $this->debug) extends ConfigCache {
  465. public $lock;
  466. public function write($content, array $metadata = null)
  467. {
  468. rewind($this->lock);
  469. ftruncate($this->lock, 0);
  470. fwrite($this->lock, $content);
  471. if (null !== $metadata) {
  472. file_put_contents($this->getPath().'.meta', serialize($metadata));
  473. @chmod($this->getPath().'.meta', 0666 & ~umask());
  474. }
  475. if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
  476. @opcache_invalidate($this->getPath(), true);
  477. }
  478. }
  479. public function __destruct()
  480. {
  481. flock($this->lock, LOCK_UN);
  482. fclose($this->lock);
  483. }
  484. };
  485. $cache->lock = $lock;
  486. if (!\is_object($this->container = include $cachePath)) {
  487. $this->container = null;
  488. } elseif (!$oldContainer || \get_class($this->container) !== $oldContainer->name) {
  489. $this->container->set('kernel', $this);
  490. return;
  491. }
  492. }
  493. }
  494. } catch (\Throwable $e) {
  495. } finally {
  496. error_reporting($errorLevel);
  497. }
  498. if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
  499. $collectedLogs = [];
  500. $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
  501. if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
  502. return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
  503. }
  504. if (isset($collectedLogs[$message])) {
  505. ++$collectedLogs[$message]['count'];
  506. return null;
  507. }
  508. $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
  509. // Clean the trace by removing first frames added by the error handler itself.
  510. for ($i = 0; isset($backtrace[$i]); ++$i) {
  511. if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
  512. $backtrace = \array_slice($backtrace, 1 + $i);
  513. break;
  514. }
  515. }
  516. // Remove frames added by DebugClassLoader.
  517. for ($i = \count($backtrace) - 2; 0 < $i; --$i) {
  518. if (\in_array($backtrace[$i]['class'] ?? null, [DebugClassLoader::class, LegacyDebugClassLoader::class], true)) {
  519. $backtrace = [$backtrace[$i + 1]];
  520. break;
  521. }
  522. }
  523. $collectedLogs[$message] = [
  524. 'type' => $type,
  525. 'message' => $message,
  526. 'file' => $file,
  527. 'line' => $line,
  528. 'trace' => [$backtrace[0]],
  529. 'count' => 1,
  530. ];
  531. return null;
  532. });
  533. }
  534. try {
  535. $container = null;
  536. $container = $this->buildContainer();
  537. $container->compile();
  538. } finally {
  539. if ($collectDeprecations) {
  540. restore_error_handler();
  541. file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
  542. file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
  543. }
  544. }
  545. $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
  546. unset($cache);
  547. $this->container = require $cachePath;
  548. $this->container->set('kernel', $this);
  549. if ($oldContainer && \get_class($this->container) !== $oldContainer->name) {
  550. // Because concurrent requests might still be using them,
  551. // old container files are not removed immediately,
  552. // but on a next dump of the container.
  553. static $legacyContainers = [];
  554. $oldContainerDir = \dirname($oldContainer->getFileName());
  555. $legacyContainers[$oldContainerDir.'.legacy'] = true;
  556. foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy', GLOB_NOSORT) as $legacyContainer) {
  557. if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) {
  558. (new Filesystem())->remove(substr($legacyContainer, 0, -7));
  559. }
  560. }
  561. touch($oldContainerDir.'.legacy');
  562. }
  563. if ($this->container->has('cache_warmer')) {
  564. $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
  565. }
  566. }
  567. /**
  568. * Returns the kernel parameters.
  569. *
  570. * @return array An array of kernel parameters
  571. */
  572. protected function getKernelParameters()
  573. {
  574. $bundles = [];
  575. $bundlesMetadata = [];
  576. foreach ($this->bundles as $name => $bundle) {
  577. $bundles[$name] = \get_class($bundle);
  578. $bundlesMetadata[$name] = [
  579. 'path' => $bundle->getPath(),
  580. 'namespace' => $bundle->getNamespace(),
  581. ];
  582. }
  583. return [
  584. /*
  585. * @deprecated since Symfony 4.2, use kernel.project_dir instead
  586. */
  587. 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir,
  588. 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(),
  589. 'kernel.environment' => $this->environment,
  590. 'kernel.debug' => $this->debug,
  591. /*
  592. * @deprecated since Symfony 4.2
  593. */
  594. 'kernel.name' => $this->name,
  595. 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir,
  596. 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(),
  597. 'kernel.bundles' => $bundles,
  598. 'kernel.bundles_metadata' => $bundlesMetadata,
  599. 'kernel.charset' => $this->getCharset(),
  600. 'kernel.container_class' => $this->getContainerClass(),
  601. ];
  602. }
  603. /**
  604. * Builds the service container.
  605. *
  606. * @return ContainerBuilder The compiled service container
  607. *
  608. * @throws \RuntimeException
  609. */
  610. protected function buildContainer()
  611. {
  612. foreach (['cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()] as $name => $dir) {
  613. if (!is_dir($dir)) {
  614. if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
  615. throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir));
  616. }
  617. } elseif (!is_writable($dir)) {
  618. throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir));
  619. }
  620. }
  621. $container = $this->getContainerBuilder();
  622. $container->addObjectResource($this);
  623. $this->prepareContainer($container);
  624. if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
  625. $container->merge($cont);
  626. }
  627. $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
  628. return $container;
  629. }
  630. /**
  631. * Prepares the ContainerBuilder before it is compiled.
  632. */
  633. protected function prepareContainer(ContainerBuilder $container)
  634. {
  635. $extensions = [];
  636. foreach ($this->bundles as $bundle) {
  637. if ($extension = $bundle->getContainerExtension()) {
  638. $container->registerExtension($extension);
  639. }
  640. if ($this->debug) {
  641. $container->addObjectResource($bundle);
  642. }
  643. }
  644. foreach ($this->bundles as $bundle) {
  645. $bundle->build($container);
  646. }
  647. $this->build($container);
  648. foreach ($container->getExtensions() as $extension) {
  649. $extensions[] = $extension->getAlias();
  650. }
  651. // ensure these extensions are implicitly loaded
  652. $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
  653. }
  654. /**
  655. * Gets a new ContainerBuilder instance used to build the service container.
  656. *
  657. * @return ContainerBuilder
  658. */
  659. protected function getContainerBuilder()
  660. {
  661. $container = new ContainerBuilder();
  662. $container->getParameterBag()->add($this->getKernelParameters());
  663. if ($this instanceof CompilerPassInterface) {
  664. $container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000);
  665. }
  666. if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) {
  667. $container->setProxyInstantiator(new RuntimeInstantiator());
  668. }
  669. return $container;
  670. }
  671. /**
  672. * Dumps the service container to PHP code in the cache.
  673. *
  674. * @param string $class The name of the class to generate
  675. * @param string $baseClass The name of the container's base class
  676. */
  677. protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass)
  678. {
  679. // cache the container
  680. $dumper = new PhpDumper($container);
  681. if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) {
  682. $dumper->setProxyDumper(new ProxyDumper());
  683. }
  684. $content = $dumper->dump([
  685. 'class' => $class,
  686. 'base_class' => $baseClass,
  687. 'file' => $cache->getPath(),
  688. 'as_files' => true,
  689. 'debug' => $this->debug,
  690. 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(),
  691. ]);
  692. $rootCode = array_pop($content);
  693. $dir = \dirname($cache->getPath()).'/';
  694. $fs = new Filesystem();
  695. foreach ($content as $file => $code) {
  696. $fs->dumpFile($dir.$file, $code);
  697. @chmod($dir.$file, 0666 & ~umask());
  698. }
  699. $legacyFile = \dirname($dir.key($content)).'.legacy';
  700. if (file_exists($legacyFile)) {
  701. @unlink($legacyFile);
  702. }
  703. $cache->write($rootCode, $container->getResources());
  704. }
  705. /**
  706. * Returns a loader for the container.
  707. *
  708. * @return DelegatingLoader The loader
  709. */
  710. protected function getContainerLoader(ContainerInterface $container)
  711. {
  712. $locator = new FileLocator($this);
  713. $resolver = new LoaderResolver([
  714. new XmlFileLoader($container, $locator),
  715. new YamlFileLoader($container, $locator),
  716. new IniFileLoader($container, $locator),
  717. new PhpFileLoader($container, $locator),
  718. new GlobFileLoader($container, $locator),
  719. new DirectoryLoader($container, $locator),
  720. new ClosureLoader($container),
  721. ]);
  722. return new DelegatingLoader($resolver);
  723. }
  724. /**
  725. * Removes comments from a PHP source string.
  726. *
  727. * We don't use the PHP php_strip_whitespace() function
  728. * as we want the content to be readable and well-formatted.
  729. *
  730. * @param string $source A PHP string
  731. *
  732. * @return string The PHP string with the comments removed
  733. */
  734. public static function stripComments($source)
  735. {
  736. if (!\function_exists('token_get_all')) {
  737. return $source;
  738. }
  739. $rawChunk = '';
  740. $output = '';
  741. $tokens = token_get_all($source);
  742. $ignoreSpace = false;
  743. for ($i = 0; isset($tokens[$i]); ++$i) {
  744. $token = $tokens[$i];
  745. if (!isset($token[1]) || 'b"' === $token) {
  746. $rawChunk .= $token;
  747. } elseif (T_START_HEREDOC === $token[0]) {
  748. $output .= $rawChunk.$token[1];
  749. do {
  750. $token = $tokens[++$i];
  751. $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
  752. } while (T_END_HEREDOC !== $token[0]);
  753. $rawChunk = '';
  754. } elseif (T_WHITESPACE === $token[0]) {
  755. if ($ignoreSpace) {
  756. $ignoreSpace = false;
  757. continue;
  758. }
  759. // replace multiple new lines with a single newline
  760. $rawChunk .= preg_replace(['/\n{2,}/S'], "\n", $token[1]);
  761. } elseif (\in_array($token[0], [T_COMMENT, T_DOC_COMMENT])) {
  762. $ignoreSpace = true;
  763. } else {
  764. $rawChunk .= $token[1];
  765. // The PHP-open tag already has a new-line
  766. if (T_OPEN_TAG === $token[0]) {
  767. $ignoreSpace = true;
  768. }
  769. }
  770. }
  771. $output .= $rawChunk;
  772. unset($tokens, $rawChunk);
  773. gc_mem_caches();
  774. return $output;
  775. }
  776. /**
  777. * @deprecated since Symfony 4.3
  778. */
  779. public function serialize()
  780. {
  781. @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
  782. return serialize([$this->environment, $this->debug]);
  783. }
  784. /**
  785. * @deprecated since Symfony 4.3
  786. */
  787. public function unserialize($data)
  788. {
  789. @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
  790. list($environment, $debug) = unserialize($data, ['allowed_classes' => false]);
  791. $this->__construct($environment, $debug);
  792. }
  793. /**
  794. * @return array
  795. */
  796. public function __sleep()
  797. {
  798. if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) {
  799. @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3.', $c), E_USER_DEPRECATED);
  800. $this->serialized = $this->serialize();
  801. return ['serialized'];
  802. }
  803. return ['environment', 'debug'];
  804. }
  805. public function __wakeup()
  806. {
  807. if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) {
  808. @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3.', $c), E_USER_DEPRECATED);
  809. $this->unserialize($this->serialized);
  810. unset($this->serialized);
  811. return;
  812. }
  813. $this->__construct($this->environment, $this->debug);
  814. }
  815. }