Kernel.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. namespace Symfony\Foundation;
  3. use Symfony\Components\DependencyInjection\ContainerInterface;
  4. use Symfony\Components\DependencyInjection\Builder;
  5. use Symfony\Components\DependencyInjection\BuilderConfiguration;
  6. use Symfony\Components\DependencyInjection\Dumper\PhpDumper;
  7. use Symfony\Components\DependencyInjection\FileResource;
  8. use Symfony\Components\RequestHandler\RequestInterface;
  9. /*
  10. * This file is part of the Symfony package.
  11. *
  12. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  13. *
  14. * For the full copyright and license information, please view the LICENSE
  15. * file that was distributed with this source code.
  16. */
  17. /**
  18. * The Kernel is the heart of the Symfony system. It manages an environment
  19. * that can host bundles.
  20. *
  21. * @package Symfony
  22. * @subpackage Foundation
  23. * @author Fabien Potencier <fabien.potencier@symfony-project.org>
  24. */
  25. abstract class Kernel implements \Serializable
  26. {
  27. protected $bundles;
  28. protected $bundleDirs;
  29. protected $container;
  30. protected $rootDir;
  31. protected $environment;
  32. protected $debug;
  33. protected $booted;
  34. protected $name;
  35. protected $startTime;
  36. const VERSION = '2.0.0-DEV';
  37. /**
  38. * Constructor.
  39. *
  40. * @param string $environment The environment
  41. * @param Boolean $debug Whether to enable debugging or not
  42. */
  43. public function __construct($environment, $debug)
  44. {
  45. $this->debug = (Boolean) $debug;
  46. if ($this->debug)
  47. {
  48. ini_set('display_errors', 1);
  49. error_reporting(-1);
  50. }
  51. else
  52. {
  53. ini_set('display_errors', 0);
  54. }
  55. if ($this->debug)
  56. {
  57. $this->startTime = microtime(true);
  58. }
  59. $this->booted = false;
  60. $this->environment = $environment;
  61. $this->bundles = $this->registerBundles();
  62. $this->bundleDirs = $this->registerBundleDirs();
  63. $this->rootDir = realpath($this->registerRootDir());
  64. $this->name = basename($this->rootDir);
  65. }
  66. abstract public function registerRootDir();
  67. abstract public function registerBundles();
  68. abstract public function registerBundleDirs();
  69. abstract public function registerContainerConfiguration();
  70. abstract public function registerRoutes();
  71. /**
  72. * Checks whether the current kernel has been booted or not.
  73. *
  74. * @return boolean $booted
  75. */
  76. public function isBooted()
  77. {
  78. return $this->booted;
  79. }
  80. /**
  81. * Boots the current kernel.
  82. *
  83. * This method boots the bundles, which MUST set
  84. * the DI container.
  85. *
  86. * @return Kernel The current Kernel instance
  87. *
  88. * @throws \LogicException When the Kernel is already booted
  89. */
  90. public function boot()
  91. {
  92. if (true === $this->booted)
  93. {
  94. throw new \LogicException('The kernel is already booted.');
  95. }
  96. // initialize the container
  97. $this->container = $this->initializeContainer();
  98. $this->container->setService('kernel', $this);
  99. // boot bundles (in reverse order)
  100. foreach ($this->bundles as $bundle)
  101. {
  102. $bundle->boot($this->container);
  103. }
  104. $this->booted = true;
  105. return $this;
  106. }
  107. public function run()
  108. {
  109. $this->handle()->send();
  110. }
  111. public function handle(RequestInterface $request = null)
  112. {
  113. if (false === $this->booted)
  114. {
  115. $this->boot();
  116. }
  117. if (null === $request)
  118. {
  119. $request = $this->container->getRequestService();
  120. }
  121. else
  122. {
  123. $this->container->setService('request', $request);
  124. }
  125. return $this->container->getRequestHandlerService()->handle($request);
  126. }
  127. public function getBundleDirs()
  128. {
  129. return $this->bundleDirs;
  130. }
  131. public function getBundles()
  132. {
  133. return $this->bundles;
  134. }
  135. public function getName()
  136. {
  137. return $this->name;
  138. }
  139. public function getEnvironment()
  140. {
  141. return $this->environment;
  142. }
  143. public function isDebug()
  144. {
  145. return $this->debug;
  146. }
  147. public function getRootDir()
  148. {
  149. return $this->rootDir;
  150. }
  151. public function getContainer()
  152. {
  153. return $this->container;
  154. }
  155. public function getStartTime()
  156. {
  157. return $this->debug ? $this->startTime : -INF;
  158. }
  159. public function getCacheDir()
  160. {
  161. return $this->rootDir.'/cache/'.$this->environment;
  162. }
  163. public function getLogDir()
  164. {
  165. return $this->rootDir.'/logs';
  166. }
  167. protected function initializeContainer()
  168. {
  169. $class = $this->name.'ProjectContainer';
  170. $location = $this->getCacheDir().'/'.$class;
  171. $reload = $this->debug ? $this->needsReload($class, $location) : false;
  172. if ($reload || !file_exists($location.'.php'))
  173. {
  174. $this->buildContainer($class, $location.'.php');
  175. }
  176. require_once $location.'.php';
  177. return new $class();
  178. }
  179. public function getKernelParameters()
  180. {
  181. $bundles = array();
  182. foreach ($this->bundles as $bundle)
  183. {
  184. $bundles[] = get_class($bundle);
  185. }
  186. return array_merge(
  187. array(
  188. 'kernel.root_dir' => $this->rootDir,
  189. 'kernel.environment' => $this->environment,
  190. 'kernel.debug' => $this->debug,
  191. 'kernel.name' => $this->name,
  192. 'kernel.cache_dir' => $this->getCacheDir(),
  193. 'kernel.logs_dir' => $this->getLogDir(),
  194. 'kernel.bundle_dirs' => $this->bundleDirs,
  195. 'kernel.bundles' => $bundles,
  196. 'kernel.charset' => 'UTF-8',
  197. ),
  198. $this->getEnvParameters()
  199. );
  200. }
  201. protected function getEnvParameters()
  202. {
  203. $parameters = array();
  204. foreach ($_SERVER as $key => $value)
  205. {
  206. if ('SYMFONY__' === substr($key, 0, 9))
  207. {
  208. $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
  209. }
  210. }
  211. return $parameters;
  212. }
  213. protected function needsReload($class, $location)
  214. {
  215. if (!file_exists($location.'.meta') || !file_exists($location.'.php'))
  216. {
  217. return true;
  218. }
  219. $meta = unserialize(file_get_contents($location.'.meta'));
  220. $time = filemtime($location.'.php');
  221. foreach ($meta as $resource)
  222. {
  223. if (!$resource->isUptodate($time))
  224. {
  225. return true;
  226. }
  227. }
  228. return false;
  229. }
  230. protected function buildContainer($class, $file)
  231. {
  232. $container = new Builder($this->getKernelParameters());
  233. $configuration = new BuilderConfiguration();
  234. foreach ($this->bundles as $bundle)
  235. {
  236. $configuration->merge($bundle->buildContainer($container));
  237. }
  238. $configuration->merge($this->registerContainerConfiguration());
  239. $container->merge($configuration);
  240. $this->optimizeContainer($container);
  241. foreach (array('cache', 'logs') as $name)
  242. {
  243. $dir = $container->getParameter(sprintf('kernel.%s_dir', $name));
  244. if (!is_dir($dir))
  245. {
  246. if (false === @mkdir($dir, 0777, true))
  247. {
  248. die(sprintf('Unable to create the %s directory (%s)', $name, dirname($dir)));
  249. }
  250. }
  251. elseif (!is_writable($dir))
  252. {
  253. die(sprintf('Unable to write in the %s directory (%s)', $name, $dir));
  254. }
  255. }
  256. // cache the container
  257. $dumper = new PhpDumper($container);
  258. $content = $dumper->dump(array('class' => $class));
  259. if (!$this->debug)
  260. {
  261. $content = self::stripComments($content);
  262. }
  263. $this->writeCacheFile($file, $content);
  264. if ($this->debug)
  265. {
  266. // add the Kernel class hierachy as resources
  267. $parent = new \ReflectionObject($this);
  268. $configuration->addResource(new FileResource($parent->getFileName()));
  269. while ($parent = $parent->getParentClass())
  270. {
  271. $configuration->addResource(new FileResource($parent->getFileName()));
  272. }
  273. // save the resources
  274. $this->writeCacheFile($this->getCacheDir().'/'.$class.'.meta', serialize($configuration->getResources()));
  275. }
  276. }
  277. public function optimizeContainer(Builder $container)
  278. {
  279. // replace all classes with the real value
  280. foreach ($container->getDefinitions() as $definition)
  281. {
  282. if (false !== strpos($class = $definition->getClass(), '%'))
  283. {
  284. $definition->setClass(Builder::resolveValue($class, $container->getParameters()));
  285. unset($container[substr($class, 1, -1)]);
  286. }
  287. }
  288. }
  289. static public function stripComments($source)
  290. {
  291. if (!function_exists('token_get_all'))
  292. {
  293. return $source;
  294. }
  295. $ignore = array(T_COMMENT => true, T_DOC_COMMENT => true);
  296. $output = '';
  297. foreach (token_get_all($source) as $token)
  298. {
  299. // array
  300. if (isset($token[1]))
  301. {
  302. // no action on comments
  303. if (!isset($ignore[$token[0]]))
  304. {
  305. // anything else -> output "as is"
  306. $output .= $token[1];
  307. }
  308. }
  309. else
  310. {
  311. // simple 1-character token
  312. $output .= $token;
  313. }
  314. }
  315. return $output;
  316. }
  317. protected function writeCacheFile($file, $content)
  318. {
  319. $tmpFile = tempnam(dirname($file), basename($file));
  320. if (!$fp = @fopen($tmpFile, 'wb'))
  321. {
  322. die(sprintf('Failed to write cache file "%s".', $tmpFile));
  323. }
  324. @fwrite($fp, $content);
  325. @fclose($fp);
  326. if ($content != file_get_contents($tmpFile))
  327. {
  328. die(sprintf('Failed to write cache file "%s" (cache corrupted).', $tmpFile));
  329. }
  330. @rename($tmpFile, $file);
  331. chmod($file, 0644);
  332. }
  333. public function serialize()
  334. {
  335. return serialize(array($this->environment, $this->debug));
  336. }
  337. public function unserialize($data)
  338. {
  339. list($environment, $debug) = unserialize($data);
  340. $this->__construct($environment, $debug);
  341. }
  342. }