FrameworkExtension.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.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\Bundle\FrameworkBundle\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\ContainerBuilder;
  12. use Symfony\Component\DependencyInjection\Definition;
  13. use Symfony\Component\DependencyInjection\Parameter;
  14. use Symfony\Component\DependencyInjection\Reference;
  15. use Symfony\Component\DependencyInjection\Configuration\Processor;
  16. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  17. use Symfony\Component\DependencyInjection\Loader\FileLocator;
  18. use Symfony\Component\DependencyInjection\Resource\FileResource;
  19. use Symfony\Component\Finder\Finder;
  20. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  21. /**
  22. * FrameworkExtension.
  23. *
  24. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  25. * @author Jeremy Mikola <jmikola@gmail.com>
  26. */
  27. class FrameworkExtension extends Extension
  28. {
  29. /**
  30. * Responds to the app.config configuration parameter.
  31. *
  32. * @param array $configs
  33. * @param ContainerBuilder $container
  34. */
  35. public function configLoad(array $configs, ContainerBuilder $container)
  36. {
  37. $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  38. $loader->load('web.xml');
  39. $loader->load('form.xml');
  40. $loader->load('services.xml');
  41. // A translator must always be registered (as support is included by
  42. // default in the Form component). If disabled, an identity translator
  43. // will be used and everything will still work as expected.
  44. $loader->load('translation.xml');
  45. if ($container->getParameter('kernel.debug')) {
  46. $loader->load('debug.xml');
  47. $container->setDefinition('event_dispatcher', $container->findDefinition('debug.event_dispatcher'));
  48. $container->setAlias('debug.event_dispatcher', 'event_dispatcher');
  49. }
  50. $processor = new Processor();
  51. $configuration = new Configuration();
  52. $config = $processor->process($configuration->getConfigTree($container->getParameter('kernel.debug')), $configs);
  53. $container->setParameter('kernel.cache_warmup', $config['cache_warmer']);
  54. if (isset($config['charset'])) {
  55. $container->setParameter('kernel.charset', $config['charset']);
  56. }
  57. if (isset($config['document_root'])) {
  58. $container->setParameter('document_root', $config['document_root']);
  59. }
  60. if (isset($config['error_handler'])) {
  61. if (false === $config['error_handler']) {
  62. $container->getDefinition('error_handler')->setMethodCalls(array());
  63. } else {
  64. $container->getDefinition('error_handler')->addMethodCall('register', array());
  65. $container->setParameter('error_handler.level', $config['error_handler']);
  66. }
  67. }
  68. if (isset($config['exception_controller'])) {
  69. $container->setParameter('exception_listener.controller', $config['exception_controller']);
  70. }
  71. if (isset($config['ide'])) {
  72. $patterns = array(
  73. 'textmate' => 'txmt://open?url=file://%%f&line=%%l',
  74. 'macvim' => 'mvim://open?url=file://%%f&line=%%l',
  75. );
  76. $pattern = isset($patterns[$config['ide']]) ? $patterns[$config['ide']] : $config['ide'];
  77. $container->setParameter('debug.file_link_format', $pattern);
  78. }
  79. if (isset($config['test']) && $config['test']) {
  80. $loader->load('test.xml');
  81. $config['session']['storage_id'] = 'array';
  82. }
  83. if (isset($config['csrf_protection'])) {
  84. $this->registerCsrfProtectionConfiguration($config['csrf_protection'], $container);
  85. }
  86. if (isset($config['esi'])) {
  87. $this->registerEsiConfiguration($config['esi'], $loader);
  88. }
  89. if (isset($config['profiler'])) {
  90. $this->registerProfilerConfiguration($config['profiler'], $container, $loader);
  91. }
  92. if (isset($config['router'])) {
  93. $this->registerRouterConfiguration($config['router'], $container, $loader);
  94. }
  95. if (isset($config['session'])) {
  96. $this->registerSessionConfiguration($config['session'], $container, $loader);
  97. }
  98. if (isset($config['templating'])) {
  99. $this->registerTemplatingConfiguration($config['templating'], $container, $loader);
  100. }
  101. if (isset($config['translator'])) {
  102. $this->registerTranslatorConfiguration($config['translator'], $container);
  103. }
  104. if (isset($config['validation'])) {
  105. $this->registerValidationConfiguration($config['validation'], $container, $loader);
  106. }
  107. $this->addClassesToCompile(array(
  108. 'Symfony\\Component\\HttpFoundation\\ParameterBag',
  109. 'Symfony\\Component\\HttpFoundation\\HeaderBag',
  110. 'Symfony\\Component\\HttpFoundation\\Request',
  111. 'Symfony\\Component\\HttpFoundation\\Response',
  112. 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag',
  113. 'Symfony\\Component\\HttpKernel\\HttpKernel',
  114. 'Symfony\\Component\\HttpKernel\\ResponseListener',
  115. 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
  116. 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface',
  117. 'Symfony\\Bundle\\FrameworkBundle\\RequestListener',
  118. 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
  119. 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
  120. 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
  121. 'Symfony\\Component\\EventDispatcher\\EventInterface',
  122. 'Symfony\\Component\\EventDispatcher\\Event',
  123. 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface',
  124. 'Symfony\\Component\\EventDispatcher\\EventDispatcher',
  125. 'Symfony\\Bundle\\FrameworkBundle\\EventDispatcher',
  126. ));
  127. }
  128. /**
  129. * Loads the CSRF protection configuration.
  130. *
  131. * @param array $config A CSRF protection configuration array
  132. * @param ContainerBuilder $container A ContainerBuilder instance
  133. */
  134. private function registerCsrfProtectionConfiguration(array $config, ContainerBuilder $container)
  135. {
  136. foreach (array('enabled', 'field_name', 'secret') as $key) {
  137. if (isset($config[$key])) {
  138. $container->setParameter('form.csrf_protection.'.$key, $config[$key]);
  139. }
  140. }
  141. }
  142. /**
  143. * Loads the ESI configuration.
  144. *
  145. * @param array $config An ESI configuration array
  146. * @param XmlFileLoader $loader An XmlFileLoader instance
  147. */
  148. private function registerEsiConfiguration(array $config, XmlFileLoader $loader)
  149. {
  150. if (isset($config['enabled']) && $config['enabled']) {
  151. $loader->load('esi.xml');
  152. }
  153. }
  154. /**
  155. * Loads the profiler configuration.
  156. *
  157. * @param array $config A profiler configuration array
  158. * @param ContainerBuilder $container A ContainerBuilder instance
  159. * @param XmlFileLoader $loader An XmlFileLoader instance
  160. */
  161. private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  162. {
  163. $loader->load('profiling.xml');
  164. $loader->load('collectors.xml');
  165. if (isset($config['only_exceptions'])) {
  166. $container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
  167. }
  168. if (isset($config['matcher'])) {
  169. if (isset($config['matcher']['service'])) {
  170. $container->setAlias('profiler.request_matcher', $config['matcher']['service']);
  171. } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path'])) {
  172. $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher');
  173. $definition->setPublic(false);
  174. if (isset($config['matcher']['ip'])) {
  175. $definition->addMethodCall('matchIp', array($config['matcher']['ip']));
  176. }
  177. if (isset($config['matcher']['path'])) {
  178. $definition->addMethodCall('matchPath', array($config['matcher']['path']));
  179. }
  180. }
  181. }
  182. }
  183. /**
  184. * Loads the router configuration.
  185. *
  186. * @param array $config A router configuration array
  187. * @param ContainerBuilder $container A ContainerBuilder instance
  188. * @param XmlFileLoader $loader An XmlFileLoader instance
  189. * @throws \InvalidArgumentException if resource option is not set
  190. */
  191. private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  192. {
  193. $loader->load('routing.xml');
  194. if (!isset($config['resource'])) {
  195. throw new \InvalidArgumentException('Router configuration requires a resource option.');
  196. }
  197. $container->setParameter('routing.resource', $config['resource']);
  198. if (isset($config['cache_warmer']) && $config['cache_warmer']) {
  199. $container->getDefinition('router.cache_warmer')->addTag('kernel.cache_warmer');
  200. $container->setAlias('router', 'router.cached');
  201. }
  202. $this->addClassesToCompile(array(
  203. 'Symfony\\Component\\Routing\\RouterInterface',
  204. 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface',
  205. 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  206. 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface',
  207. 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  208. $container->findDefinition('router')->getClass(),
  209. ));
  210. }
  211. /**
  212. * Loads the session configuration.
  213. *
  214. * @param array $config A session configuration array
  215. * @param ContainerBuilder $container A ContainerBuilder instance
  216. * @param XmlFileLoader $loader An XmlFileLoader instance
  217. */
  218. private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  219. {
  220. $loader->load('session.xml');
  221. if (isset($config['auto_start']) && $config['auto_start']) {
  222. $container->getDefinition('session')->addMethodCall('start');
  223. }
  224. if (isset($config['class'])) {
  225. $container->setParameter('session.class', $config['class']);
  226. }
  227. if (isset($config['default_locale'])) {
  228. $container->setParameter('session.default_locale', $config['default_locale']);
  229. }
  230. $container->setAlias('session.storage', 'session.storage.'.$config['storage_id']);
  231. $options = $container->getParameter('session.storage.'.$config['storage_id'].'.options');
  232. foreach (array('name', 'lifetime', 'path', 'domain', 'secure', 'httponly', 'db_table', 'db_id_col', 'db_data_col', 'db_time_col') as $key) {
  233. if (isset($config[$key])) {
  234. $options[$key] = $config[$key];
  235. }
  236. }
  237. $container->setParameter('session.storage.'.$config['storage_id'].'.options', $options);
  238. $this->addClassesToCompile(array(
  239. 'Symfony\\Component\\HttpFoundation\\Session',
  240. 'Symfony\\Component\\HttpFoundation\\SessionStorage\\SessionStorageInterface',
  241. $container->getParameter('session.class'),
  242. ));
  243. }
  244. /**
  245. * Loads the templating configuration.
  246. *
  247. * @param array $config A templating configuration array
  248. * @param ContainerBuilder $container A ContainerBuilder instance
  249. * @param XmlFileLoader $loader An XmlFileLoader instance
  250. * @throws \LogicException if no engines are defined
  251. */
  252. private function registerTemplatingConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  253. {
  254. $loader->load('templating.xml');
  255. $loader->load('templating_php.xml');
  256. if ($container->getParameter('kernel.debug')) {
  257. $loader->load('templating_debug.xml');
  258. }
  259. if (isset($config['assets_version'])) {
  260. $container->setParameter('templating.assets.version', $config['assets_version']);
  261. }
  262. if (isset($config['assets_base_urls'])) {
  263. $container->setParameter('templating.assets.base_urls', $config['assets_base_urls']);
  264. }
  265. if (isset($config['loaders']) && $config['loaders']) {
  266. $loaders = array_map(function($loader) { return new Reference($loader); }, $config['loaders']);
  267. // Use a delegation unless only a single loader was registered
  268. if (1 === count($loaders)) {
  269. $container->setAlias('templating.loader', (string) reset($loaders));
  270. } else {
  271. $container->getDefinition('templating.loader.chain')->addArgument($loaders);
  272. $container->setAlias('templating.loader', 'templating.loader.chain');
  273. }
  274. }
  275. if (isset($config['cache'])) {
  276. // Wrap the existing loader with cache (must happen after loaders are registered)
  277. $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader'));
  278. $container->setDefinition('templating.loader', $container->getDefinition('templating.loader.cache'));
  279. $container->setParameter('templating.loader.cache.path', $config['cache']);
  280. } else {
  281. $container->setParameter('templating.loader.cache.path', null);
  282. }
  283. if (isset($config['cache_warmer'])) {
  284. $container->getDefinition('templating.cache_warmer.template_paths')->addTag('kernel.cache_warmer');
  285. $container->setAlias('templating.locator', 'templating.locator.cached');
  286. }
  287. if (empty($config['engines'])) {
  288. throw new \LogicException('You must register at least one templating engine.');
  289. }
  290. $this->addClassesToCompile(array(
  291. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface',
  292. 'Symfony\\Component\\Templating\\EngineInterface',
  293. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocatorInterface',
  294. $container->findDefinition('templating.locator')->getClass(),
  295. ));
  296. if (in_array('php', $config['engines'], true)) {
  297. $this->addClassesToCompile(array(
  298. 'Symfony\\Component\\Templating\\PhpEngine',
  299. 'Symfony\\Component\\Templating\\TemplateNameParserInterface',
  300. 'Symfony\\Component\\Templating\\TemplateNameParser',
  301. 'Symfony\\Component\\Templating\\Loader\\LoaderInterface',
  302. 'Symfony\\Component\\Templating\\Storage\\Storage',
  303. 'Symfony\\Component\\Templating\\Storage\\FileStorage',
  304. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine',
  305. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser',
  306. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader',
  307. ));
  308. }
  309. $engines = array_map(function($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']);
  310. // Use a deligation unless only a single engine was registered
  311. if (1 === count($engines)) {
  312. $container->setAlias('templating', (string) reset($engines));
  313. } else {
  314. $container->getDefinition('templating.engine.delegating')->setArgument(1, $engines);
  315. $container->setAlias('templating', 'templating.engine.delegating');
  316. }
  317. }
  318. /**
  319. * Loads the translator configuration.
  320. *
  321. * @param array $config A translator configuration array
  322. * @param ContainerBuilder $container A ContainerBuilder instance
  323. */
  324. private function registerTranslatorConfiguration(array $config, ContainerBuilder $container)
  325. {
  326. if (isset($config['enabled']) && $config['enabled']) {
  327. // Use the "real" translator instead of the identity default
  328. $container->setDefinition('translator', $container->findDefinition('translator.real'));
  329. // Discover translation directories
  330. $dirs = array();
  331. foreach ($container->getParameter('kernel.bundles') as $bundle) {
  332. $reflection = new \ReflectionClass($bundle);
  333. if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) {
  334. $dirs[] = $dir;
  335. }
  336. }
  337. if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) {
  338. $dirs[] = $dir;
  339. }
  340. // Register translation resources
  341. $resources = array();
  342. if ($dirs) {
  343. $finder = new Finder();
  344. $finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs);
  345. foreach ($finder as $file) {
  346. // filename is domain.locale.format
  347. list($domain, $locale, $format) = explode('.', $file->getBasename());
  348. $resources[] = array($format, (string) $file, $locale, $domain);
  349. }
  350. }
  351. $container->setParameter('translation.resources', $resources);
  352. }
  353. if (isset($config['fallback'])) {
  354. $container->setParameter('translator.fallback_locale', $config['fallback']);
  355. }
  356. }
  357. /**
  358. * Loads the validator configuration.
  359. *
  360. * @param array $config A validation configuration array
  361. * @param ContainerBuilder $container A ContainerBuilder instance
  362. * @param XmlFileLoader $loader An XmlFileLoader instance
  363. */
  364. private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  365. {
  366. if (empty($config['enabled'])) {
  367. return;
  368. }
  369. $loader->load('validator.xml');
  370. $xmlMappingFiles = array();
  371. $yamlMappingFiles = array();
  372. // Include default entries from the framework
  373. $xmlMappingFiles[] = __DIR__.'/../../../Component/Form/Resources/config/validation.xml';
  374. foreach ($container->getParameter('kernel.bundles') as $bundle) {
  375. $reflection = new \ReflectionClass($bundle);
  376. if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) {
  377. $xmlMappingFiles[] = realpath($file);
  378. }
  379. if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) {
  380. $yamlMappingFiles[] = realpath($file);
  381. }
  382. }
  383. $xmlFilesLoader = new Definition('%validator.mapping.loader.xml_files_loader.class%', array($xmlMappingFiles));
  384. $xmlFilesLoader->setPublic(false);
  385. $yamlFilesLoader = new Definition('%validator.mapping.loader.yaml_files_loader.class%', array($yamlMappingFiles));
  386. $yamlFilesLoader->setPublic(false);
  387. $container->setDefinition('validator.mapping.loader.xml_files_loader', $xmlFilesLoader);
  388. $container->setDefinition('validator.mapping.loader.yaml_files_loader', $yamlFilesLoader);
  389. foreach ($xmlMappingFiles as $file) {
  390. $container->addResource(new FileResource($file));
  391. }
  392. foreach ($yamlMappingFiles as $file) {
  393. $container->addResource(new FileResource($file));
  394. }
  395. if (isset($config['annotations'])) {
  396. // Register prefixes for constraint namespaces
  397. if (!empty($config['annotations']['namespaces'])) {
  398. $container->setParameter('validator.annotations.namespaces', array_merge(
  399. $container->getParameter('validator.annotations.namespaces'),
  400. $config['annotations']['namespaces']
  401. ));
  402. }
  403. // Register annotation loader
  404. $annotationLoader = new Definition('%validator.mapping.loader.annotation_loader.class%');
  405. $annotationLoader->setPublic(false);
  406. $annotationLoader->addArgument(new Parameter('validator.annotations.namespaces'));
  407. $container->setDefinition('validator.mapping.loader.annotation_loader', $annotationLoader);
  408. $loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain');
  409. $arguments = $loaderChain->getArguments();
  410. array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader'));
  411. $loaderChain->setArguments($arguments);
  412. }
  413. }
  414. /**
  415. * Returns the base path for the XSD files.
  416. *
  417. * @return string The XSD base path
  418. */
  419. public function getXsdValidationBasePath()
  420. {
  421. return __DIR__.'/../Resources/config/schema';
  422. }
  423. public function getNamespace()
  424. {
  425. return 'http://www.symfony-project.org/schema/dic/symfony';
  426. }
  427. public function getAlias()
  428. {
  429. return 'app';
  430. }
  431. }