PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/HttpKernel/Kernel.php

https://github.com/thewiredman/symfony
PHP | 503 lines | 379 code | 45 blank | 79 comment | 15 complexity | d4ec5a9fcb5b5b9b2f81bfdf7020f556 MD5 | raw file
  1. <?php
  2. namespace Symfony\Component\HttpKernel;
  3. use Symfony\Component\DependencyInjection\ContainerInterface;
  4. use Symfony\Component\DependencyInjection\ContainerBuilder;
  5. use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
  6. use Symfony\Component\DependencyInjection\Resource\FileResource;
  7. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  8. use Symfony\Component\DependencyInjection\Loader\DelegatingLoader;
  9. use Symfony\Component\DependencyInjection\Loader\LoaderResolver;
  10. use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
  11. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  12. use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
  13. use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
  14. use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
  15. use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpKernel\HttpKernelInterface;
  18. use Symfony\Component\HttpKernel\ClassCollectionLoader;
  19. /*
  20. * This file is part of the Symfony package.
  21. *
  22. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  23. *
  24. * For the full copyright and license information, please view the LICENSE
  25. * file that was distributed with this source code.
  26. */
  27. /**
  28. * The Kernel is the heart of the Symfony system. It manages an environment
  29. * that can host bundles.
  30. *
  31. * @author Fabien Potencier <fabien.potencier@symfony-project.org>
  32. */
  33. abstract class Kernel implements HttpKernelInterface, \Serializable
  34. {
  35. protected $bundles;
  36. protected $bundleDirs;
  37. protected $container;
  38. protected $rootDir;
  39. protected $environment;
  40. protected $debug;
  41. protected $booted;
  42. protected $name;
  43. protected $startTime;
  44. protected $request;
  45. const VERSION = '2.0.0-DEV';
  46. /**
  47. * Constructor.
  48. *
  49. * @param string $environment The environment
  50. * @param Boolean $debug Whether to enable debugging or not
  51. */
  52. public function __construct($environment, $debug)
  53. {
  54. $this->environment = $environment;
  55. $this->debug = (Boolean) $debug;
  56. $this->booted = false;
  57. $this->rootDir = realpath($this->registerRootDir());
  58. $this->name = basename($this->rootDir);
  59. if ($this->debug) {
  60. ini_set('display_errors', 1);
  61. error_reporting(-1);
  62. $this->startTime = microtime(true);
  63. } else {
  64. ini_set('display_errors', 0);
  65. }
  66. }
  67. public function __clone()
  68. {
  69. if ($this->debug) {
  70. $this->startTime = microtime(true);
  71. }
  72. $this->booted = false;
  73. $this->container = null;
  74. $this->request = null;
  75. }
  76. abstract public function registerRootDir();
  77. abstract public function registerBundles();
  78. abstract public function registerBundleDirs();
  79. abstract public function registerContainerConfiguration(LoaderInterface $loader);
  80. /**
  81. * Checks whether the current kernel has been booted or not.
  82. *
  83. * @return boolean $booted
  84. */
  85. public function isBooted()
  86. {
  87. return $this->booted;
  88. }
  89. /**
  90. * Boots the current kernel.
  91. *
  92. * This method boots the bundles, which MUST set
  93. * the DI container.
  94. *
  95. * @throws \LogicException When the Kernel is already booted
  96. */
  97. public function boot()
  98. {
  99. if (true === $this->booted) {
  100. throw new \LogicException('The kernel is already booted.');
  101. }
  102. if (!$this->isDebug()) {
  103. require_once __DIR__.'/bootstrap.php';
  104. }
  105. $this->bundles = $this->registerBundles();
  106. $this->bundleDirs = $this->registerBundleDirs();
  107. $this->container = $this->initializeContainer();
  108. // load core classes
  109. ClassCollectionLoader::load(
  110. $this->container->getParameter('kernel.compiled_classes'),
  111. $this->container->getParameter('kernel.cache_dir'),
  112. 'classes',
  113. $this->container->getParameter('kernel.debug'),
  114. true
  115. );
  116. foreach ($this->bundles as $bundle) {
  117. $bundle->setContainer($this->container);
  118. $bundle->boot();
  119. }
  120. $this->booted = true;
  121. }
  122. /**
  123. * Shutdowns the kernel.
  124. *
  125. * This method is mainly useful when doing functional testing.
  126. */
  127. public function shutdown()
  128. {
  129. $this->booted = false;
  130. foreach ($this->bundles as $bundle) {
  131. $bundle->shutdown();
  132. $bundle->setContainer(null);
  133. }
  134. $this->container = null;
  135. }
  136. /**
  137. * Reboots the kernel.
  138. *
  139. * This method is mainly useful when doing functional testing.
  140. *
  141. * It is a shortcut for the call to shutdown() and boot().
  142. */
  143. public function reboot()
  144. {
  145. $this->shutdown();
  146. $this->boot();
  147. }
  148. /**
  149. * Gets the Request instance associated with the master request.
  150. *
  151. * @return Request A Request instance
  152. */
  153. public function getRequest()
  154. {
  155. return $this->request;
  156. }
  157. /**
  158. * Handles a request to convert it to a response by calling the HttpKernel service.
  159. *
  160. * @param Request $request A Request instance
  161. * @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
  162. * @param Boolean $raw Whether to catch exceptions or not
  163. *
  164. * @return Response $response A Response instance
  165. */
  166. public function handle(Request $request = null, $type = HttpKernelInterface::MASTER_REQUEST, $raw = false)
  167. {
  168. if (false === $this->booted) {
  169. $this->boot();
  170. }
  171. if (null === $request) {
  172. $request = $this->container->get('request');
  173. } else {
  174. $this->container->set('request', $request);
  175. }
  176. if (HttpKernelInterface::MASTER_REQUEST === $type) {
  177. $this->request = $request;
  178. }
  179. $response = $this->container->getHttpKernelService()->handle($request, $type, $raw);
  180. $this->container->set('request', $this->request);
  181. return $response;
  182. }
  183. /**
  184. * Gets the directories where bundles can be stored.
  185. *
  186. * @return array An array of directories where bundles can be stored
  187. */
  188. public function getBundleDirs()
  189. {
  190. return $this->bundleDirs;
  191. }
  192. /**
  193. * Gets the registered bundle names.
  194. *
  195. * @return array An array of registered bundle names
  196. */
  197. public function getBundles()
  198. {
  199. return $this->bundles;
  200. }
  201. /**
  202. * Checks if a given class name belongs to an active bundle.
  203. *
  204. * @param string $class A class name
  205. *
  206. * @return Boolean true if the class belongs to an active bundle, false otherwise
  207. */
  208. public function isClassInActiveBundle($class)
  209. {
  210. foreach ($this->bundles as $bundle) {
  211. $bundleClass = get_class($bundle);
  212. if (0 === strpos($class, substr($bundleClass, 0, strrpos($bundleClass, '\\')))) {
  213. return true;
  214. }
  215. }
  216. return false;
  217. }
  218. /**
  219. * Returns the Bundle name for a given class.
  220. *
  221. * @param string $class A class name
  222. *
  223. * @return string The Bundle name or null if the class does not belongs to a bundle
  224. */
  225. public function getBundleForClass($class)
  226. {
  227. $namespace = substr($class, 0, strrpos($class, '\\'));
  228. foreach (array_keys($this->getBundleDirs()) as $prefix) {
  229. if (0 === $pos = strpos($namespace, $prefix)) {
  230. return substr($namespace, strlen($prefix) + 1, strpos($class, 'Bundle\\') + 7);
  231. }
  232. }
  233. }
  234. public function getName()
  235. {
  236. return $this->name;
  237. }
  238. public function getSafeName()
  239. {
  240. return preg_replace('/[^a-zA-Z0-9_]+/', '', $this->name);
  241. }
  242. public function getEnvironment()
  243. {
  244. return $this->environment;
  245. }
  246. public function isDebug()
  247. {
  248. return $this->debug;
  249. }
  250. public function getRootDir()
  251. {
  252. return $this->rootDir;
  253. }
  254. public function getContainer()
  255. {
  256. return $this->container;
  257. }
  258. public function getStartTime()
  259. {
  260. return $this->debug ? $this->startTime : -INF;
  261. }
  262. public function getCacheDir()
  263. {
  264. return $this->rootDir.'/cache/'.$this->environment;
  265. }
  266. public function getLogDir()
  267. {
  268. return $this->rootDir.'/logs';
  269. }
  270. protected function initializeContainer()
  271. {
  272. $class = $this->getSafeName().ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
  273. $location = $this->getCacheDir().'/'.$class;
  274. $reload = $this->debug ? $this->needsReload($class, $location) : false;
  275. if ($reload || !file_exists($location.'.php')) {
  276. $this->buildContainer($class, $location.'.php');
  277. }
  278. require_once $location.'.php';
  279. $container = new $class();
  280. $container->set('kernel', $this);
  281. return $container;
  282. }
  283. public function getKernelParameters()
  284. {
  285. $bundles = array();
  286. foreach ($this->bundles as $bundle) {
  287. $bundles[] = get_class($bundle);
  288. }
  289. return array_merge(
  290. array(
  291. 'kernel.root_dir' => $this->rootDir,
  292. 'kernel.environment' => $this->environment,
  293. 'kernel.debug' => $this->debug,
  294. 'kernel.name' => $this->name,
  295. 'kernel.cache_dir' => $this->getCacheDir(),
  296. 'kernel.logs_dir' => $this->getLogDir(),
  297. 'kernel.bundle_dirs' => $this->bundleDirs,
  298. 'kernel.bundles' => $bundles,
  299. 'kernel.charset' => 'UTF-8',
  300. 'kernel.compiled_classes' => array(),
  301. ),
  302. $this->getEnvParameters()
  303. );
  304. }
  305. protected function getEnvParameters()
  306. {
  307. $parameters = array();
  308. foreach ($_SERVER as $key => $value) {
  309. if ('SYMFONY__' === substr($key, 0, 9)) {
  310. $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
  311. }
  312. }
  313. return $parameters;
  314. }
  315. protected function needsReload($class, $location)
  316. {
  317. if (!file_exists($location.'.meta') || !file_exists($location.'.php')) {
  318. return true;
  319. }
  320. $meta = unserialize(file_get_contents($location.'.meta'));
  321. $time = filemtime($location.'.php');
  322. foreach ($meta as $resource) {
  323. if (!$resource->isUptodate($time)) {
  324. return true;
  325. }
  326. }
  327. return false;
  328. }
  329. protected function buildContainer($class, $file)
  330. {
  331. $parameterBag = new ParameterBag($this->getKernelParameters());
  332. $container = new ContainerBuilder($parameterBag);
  333. foreach ($this->bundles as $bundle) {
  334. $bundle->registerExtensions($container);
  335. if ($this->debug) {
  336. $container->addObjectResource($bundle);
  337. }
  338. }
  339. if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
  340. $container->merge($cont);
  341. }
  342. $container->freeze();
  343. foreach (array('cache', 'logs') as $name) {
  344. $dir = $container->getParameter(sprintf('kernel.%s_dir', $name));
  345. if (!is_dir($dir)) {
  346. if (false === @mkdir($dir, 0777, true)) {
  347. die(sprintf('Unable to create the %s directory (%s)', $name, dirname($dir)));
  348. }
  349. } elseif (!is_writable($dir)) {
  350. die(sprintf('Unable to write in the %s directory (%s)', $name, $dir));
  351. }
  352. }
  353. // cache the container
  354. $dumper = new PhpDumper($container);
  355. $content = $dumper->dump(array('class' => $class));
  356. if (!$this->debug) {
  357. $content = self::stripComments($content);
  358. }
  359. $this->writeCacheFile($file, $content);
  360. if ($this->debug) {
  361. $container->addObjectResource($this);
  362. // save the resources
  363. $this->writeCacheFile($this->getCacheDir().'/'.$class.'.meta', serialize($container->getResources()));
  364. }
  365. }
  366. protected function getContainerLoader(ContainerInterface $container)
  367. {
  368. $resolver = new LoaderResolver(array(
  369. new XmlFileLoader($container, $this->getBundleDirs()),
  370. new YamlFileLoader($container, $this->getBundleDirs()),
  371. new IniFileLoader($container, $this->getBundleDirs()),
  372. new PhpFileLoader($container, $this->getBundleDirs()),
  373. new ClosureLoader($container),
  374. ));
  375. return new DelegatingLoader($resolver);
  376. }
  377. /**
  378. * Removes comments from a PHP source string.
  379. *
  380. * We don't use the PHP php_strip_whitespace() function
  381. * as we want the content to be readable and well-formatted.
  382. *
  383. * @param string $source A PHP string
  384. *
  385. * @return string The PHP string with the comments removed
  386. */
  387. static public function stripComments($source)
  388. {
  389. if (!function_exists('token_get_all')) {
  390. return $source;
  391. }
  392. $output = '';
  393. foreach (token_get_all($source) as $token) {
  394. if (is_string($token)) {
  395. $output .= $token;
  396. } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
  397. $output .= $token[1];
  398. }
  399. }
  400. // replace multiple new lines with a single newline
  401. $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
  402. // reformat {} "a la python"
  403. $output = preg_replace(array('/\n\s*\{/', '/\n\s*\}/'), array(' {', ' }'), $output);
  404. return $output;
  405. }
  406. protected function writeCacheFile($file, $content)
  407. {
  408. $tmpFile = tempnam(dirname($file), basename($file));
  409. if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
  410. chmod($file, 0644);
  411. return;
  412. }
  413. throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
  414. }
  415. public function serialize()
  416. {
  417. return serialize(array($this->environment, $this->debug));
  418. }
  419. public function unserialize($data)
  420. {
  421. list($environment, $debug) = unserialize($data);
  422. $this->__construct($environment, $debug);
  423. }
  424. }