FrameworkExtension.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  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\Bundle\FrameworkBundle\DependencyInjection;
  11. use Symfony\Component\Config\FileLocator;
  12. use Symfony\Component\Config\Resource\DirectoryResource;
  13. use Symfony\Component\Config\Resource\FileResource;
  14. use Symfony\Component\DependencyInjection\ContainerBuilder;
  15. use Symfony\Component\DependencyInjection\ContainerInterface;
  16. use Symfony\Component\DependencyInjection\Definition;
  17. use Symfony\Component\DependencyInjection\DefinitionDecorator;
  18. use Symfony\Component\DependencyInjection\Exception\LogicException;
  19. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  20. use Symfony\Component\DependencyInjection\Reference;
  21. use Symfony\Component\Finder\Finder;
  22. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  23. /**
  24. * FrameworkExtension.
  25. *
  26. * @author Fabien Potencier <fabien@symfony.com>
  27. * @author Jeremy Mikola <jmikola@gmail.com>
  28. * @author Kévin Dunglas <dunglas@gmail.com>
  29. */
  30. class FrameworkExtension extends Extension
  31. {
  32. private $formConfigEnabled = false;
  33. private $translationConfigEnabled = false;
  34. private $sessionConfigEnabled = false;
  35. /**
  36. * @var string|null
  37. */
  38. private $kernelRootHash;
  39. /**
  40. * Responds to the app.config configuration parameter.
  41. *
  42. * @throws LogicException
  43. */
  44. public function load(array $configs, ContainerBuilder $container)
  45. {
  46. $loader = new XmlFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));
  47. $loader->load('web.xml');
  48. $loader->load('services.xml');
  49. $loader->load('fragment_renderer.xml');
  50. // A translator must always be registered (as support is included by
  51. // default in the Form component). If disabled, an identity translator
  52. // will be used and everything will still work as expected.
  53. $loader->load('translation.xml');
  54. $configuration = $this->getConfiguration($configs, $container);
  55. $config = $this->processConfiguration($configuration, $configs);
  56. if (isset($config['secret'])) {
  57. $container->setParameter('kernel.secret', $config['secret']);
  58. }
  59. $container->setParameter('kernel.http_method_override', $config['http_method_override']);
  60. $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']);
  61. $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']);
  62. $container->setParameter('kernel.default_locale', $config['default_locale']);
  63. if (!empty($config['test'])) {
  64. $loader->load('test.xml');
  65. }
  66. if (isset($config['session'])) {
  67. $this->sessionConfigEnabled = true;
  68. $this->registerSessionConfiguration($config['session'], $container, $loader);
  69. }
  70. if (isset($config['request'])) {
  71. $this->registerRequestConfiguration($config['request'], $container, $loader);
  72. }
  73. $loader->load('security.xml');
  74. if ($this->isConfigEnabled($container, $config['form'])) {
  75. $this->formConfigEnabled = true;
  76. $this->registerFormConfiguration($config, $container, $loader);
  77. $config['validation']['enabled'] = true;
  78. if (!class_exists('Symfony\Component\Validator\Validation')) {
  79. throw new LogicException('The Validator component is required to use the Form component.');
  80. }
  81. if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) {
  82. $config['csrf_protection']['enabled'] = true;
  83. }
  84. }
  85. $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
  86. if (isset($config['assets'])) {
  87. $this->registerAssetsConfiguration($config['assets'], $container, $loader);
  88. }
  89. if (isset($config['templating'])) {
  90. $this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader);
  91. }
  92. $this->registerValidationConfiguration($config['validation'], $container, $loader);
  93. $this->registerEsiConfiguration($config['esi'], $container, $loader);
  94. $this->registerSsiConfiguration($config['ssi'], $container, $loader);
  95. $this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
  96. $this->registerTranslatorConfiguration($config['translator'], $container);
  97. $this->registerProfilerConfiguration($config['profiler'], $container, $loader);
  98. if (isset($config['router'])) {
  99. $this->registerRouterConfiguration($config['router'], $container, $loader);
  100. }
  101. $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader);
  102. $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader);
  103. if (isset($config['serializer'])) {
  104. $this->registerSerializerConfiguration($config['serializer'], $container, $loader);
  105. }
  106. if (isset($config['property_info'])) {
  107. $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader);
  108. }
  109. $loader->load('debug_prod.xml');
  110. $definition = $container->findDefinition('debug.debug_handlers_listener');
  111. if ($container->hasParameter('templating.helper.code.file_link_format')) {
  112. $definition->replaceArgument(5, '%templating.helper.code.file_link_format%');
  113. }
  114. if ($container->getParameter('kernel.debug')) {
  115. $definition->replaceArgument(2, -1 & ~(E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR | E_RECOVERABLE_ERROR));
  116. $loader->load('debug.xml');
  117. $definition = $container->findDefinition('http_kernel');
  118. $definition->replaceArgument(2, new Reference('debug.controller_resolver'));
  119. // replace the regular event_dispatcher service with the debug one
  120. $definition = $container->findDefinition('event_dispatcher');
  121. $definition->setPublic(false);
  122. $container->setDefinition('debug.event_dispatcher.parent', $definition);
  123. $container->setAlias('event_dispatcher', 'debug.event_dispatcher');
  124. } else {
  125. $definition->replaceArgument(1, null);
  126. }
  127. $this->addClassesToCompile(array(
  128. 'Symfony\\Component\\Config\\FileLocator',
  129. 'Symfony\\Component\\Debug\\ErrorHandler',
  130. 'Symfony\\Component\\EventDispatcher\\Event',
  131. 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher',
  132. 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener',
  133. 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener',
  134. 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
  135. 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent',
  136. 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent',
  137. 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent',
  138. 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent',
  139. 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent',
  140. 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent',
  141. 'Symfony\\Component\\HttpKernel\\KernelEvents',
  142. 'Symfony\\Component\\HttpKernel\\Config\\FileLocator',
  143. 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
  144. 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
  145. // Cannot be included because annotations will parse the big compiled class file
  146. // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
  147. ));
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function getConfiguration(array $config, ContainerBuilder $container)
  153. {
  154. return new Configuration($container->getParameter('kernel.debug'));
  155. }
  156. private function registerFormConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  157. {
  158. $loader->load('form.xml');
  159. if (null === $config['form']['csrf_protection']['enabled']) {
  160. $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled'];
  161. }
  162. if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) {
  163. $loader->load('form_csrf.xml');
  164. $container->setParameter('form.type_extension.csrf.enabled', true);
  165. if (null !== $config['form']['csrf_protection']['field_name']) {
  166. $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']);
  167. } else {
  168. $container->setParameter('form.type_extension.csrf.field_name', $config['csrf_protection']['field_name']);
  169. }
  170. } else {
  171. $container->setParameter('form.type_extension.csrf.enabled', false);
  172. }
  173. }
  174. private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  175. {
  176. if (!$this->isConfigEnabled($container, $config)) {
  177. $container->removeDefinition('fragment.renderer.esi');
  178. return;
  179. }
  180. $loader->load('esi.xml');
  181. }
  182. private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  183. {
  184. if (!$this->isConfigEnabled($container, $config)) {
  185. $container->removeDefinition('fragment.renderer.ssi');
  186. return;
  187. }
  188. $loader->load('ssi.xml');
  189. }
  190. private function registerFragmentsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  191. {
  192. if (!$this->isConfigEnabled($container, $config)) {
  193. return;
  194. }
  195. $loader->load('fragment_listener.xml');
  196. $container->setParameter('fragment.path', $config['path']);
  197. }
  198. private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  199. {
  200. if (!$this->isConfigEnabled($container, $config)) {
  201. // this is needed for the WebProfiler to work even if the profiler is disabled
  202. $container->setParameter('data_collector.templates', array());
  203. return;
  204. }
  205. $loader->load('profiling.xml');
  206. $loader->load('collectors.xml');
  207. if ($this->formConfigEnabled) {
  208. $loader->load('form_debug.xml');
  209. }
  210. if ($this->translationConfigEnabled) {
  211. $loader->load('translation_debug.xml');
  212. $container->getDefinition('translator.data_collector')->setDecoratedService('translator');
  213. }
  214. $container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
  215. $container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
  216. // Choose storage class based on the DSN
  217. $supported = array(
  218. 'sqlite' => 'Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage',
  219. 'mysql' => 'Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage',
  220. 'file' => 'Symfony\Component\HttpKernel\Profiler\FileProfilerStorage',
  221. 'mongodb' => 'Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage',
  222. 'memcache' => 'Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage',
  223. 'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage',
  224. 'redis' => 'Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage',
  225. );
  226. list($class) = explode(':', $config['dsn'], 2);
  227. if (!isset($supported[$class])) {
  228. throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class));
  229. }
  230. $container->setParameter('profiler.storage.dsn', $config['dsn']);
  231. $container->setParameter('profiler.storage.username', $config['username']);
  232. $container->setParameter('profiler.storage.password', $config['password']);
  233. $container->setParameter('profiler.storage.lifetime', $config['lifetime']);
  234. $container->getDefinition('profiler.storage')->setClass($supported[$class]);
  235. if (isset($config['matcher'])) {
  236. if (isset($config['matcher']['service'])) {
  237. $container->setAlias('profiler.request_matcher', $config['matcher']['service']);
  238. } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path']) || isset($config['matcher']['ips'])) {
  239. $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher');
  240. $definition->setPublic(false);
  241. if (isset($config['matcher']['ip'])) {
  242. $definition->addMethodCall('matchIp', array($config['matcher']['ip']));
  243. }
  244. if (isset($config['matcher']['ips'])) {
  245. $definition->addMethodCall('matchIps', array($config['matcher']['ips']));
  246. }
  247. if (isset($config['matcher']['path'])) {
  248. $definition->addMethodCall('matchPath', array($config['matcher']['path']));
  249. }
  250. }
  251. }
  252. if (!$config['collect']) {
  253. $container->getDefinition('profiler')->addMethodCall('disable', array());
  254. }
  255. }
  256. private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  257. {
  258. $loader->load('routing.xml');
  259. $container->setParameter('router.resource', $config['resource']);
  260. $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.container_class'));
  261. $router = $container->findDefinition('router.default');
  262. $argument = $router->getArgument(2);
  263. $argument['strict_requirements'] = $config['strict_requirements'];
  264. if (isset($config['type'])) {
  265. $argument['resource_type'] = $config['type'];
  266. }
  267. $router->replaceArgument(2, $argument);
  268. $container->setParameter('request_listener.http_port', $config['http_port']);
  269. $container->setParameter('request_listener.https_port', $config['https_port']);
  270. $this->addClassesToCompile(array(
  271. 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  272. 'Symfony\\Component\\Routing\\RequestContext',
  273. 'Symfony\\Component\\Routing\\Router',
  274. 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher',
  275. $container->findDefinition('router.default')->getClass(),
  276. ));
  277. }
  278. private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  279. {
  280. $loader->load('session.xml');
  281. // session storage
  282. $container->setAlias('session.storage', $config['storage_id']);
  283. $options = array();
  284. foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
  285. if (isset($config[$key])) {
  286. $options[$key] = $config[$key];
  287. }
  288. }
  289. $container->setParameter('session.storage.options', $options);
  290. // session handler (the internal callback registered with PHP session management)
  291. if (null === $config['handler_id']) {
  292. // Set the handler class to be null
  293. $container->getDefinition('session.storage.native')->replaceArgument(1, null);
  294. $container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
  295. } else {
  296. $handlerId = $config['handler_id'];
  297. if ($config['metadata_update_threshold'] > 0) {
  298. $container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId));
  299. $handlerId = 'session.handler.write_check';
  300. }
  301. $container->setAlias('session.handler', $handlerId);
  302. }
  303. $container->setParameter('session.save_path', $config['save_path']);
  304. $this->addClassesToCompile(array(
  305. 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
  306. 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
  307. 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage',
  308. 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler',
  309. 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
  310. 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy',
  311. $container->getDefinition('session')->getClass(),
  312. ));
  313. if ($container->hasDefinition($config['storage_id'])) {
  314. $this->addClassesToCompile(array(
  315. $container->findDefinition('session.storage')->getClass(),
  316. ));
  317. }
  318. $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']);
  319. }
  320. private function registerRequestConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  321. {
  322. if ($config['formats']) {
  323. $loader->load('request.xml');
  324. $container
  325. ->getDefinition('request.add_request_formats_listener')
  326. ->replaceArgument(0, $config['formats'])
  327. ;
  328. }
  329. }
  330. private function registerTemplatingConfiguration(array $config, $ide, ContainerBuilder $container, XmlFileLoader $loader)
  331. {
  332. $loader->load('templating.xml');
  333. if (!$container->hasParameter('templating.helper.code.file_link_format')) {
  334. $links = array(
  335. 'textmate' => 'txmt://open?url=file://%%f&line=%%l',
  336. 'macvim' => 'mvim://open?url=file://%%f&line=%%l',
  337. 'emacs' => 'emacs://open?url=file://%%f&line=%%l',
  338. 'sublime' => 'subl://open?url=file://%%f&line=%%l',
  339. );
  340. $container->setParameter('templating.helper.code.file_link_format', str_replace('%', '%%', ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')) ?: (isset($links[$ide]) ? $links[$ide] : $ide));
  341. }
  342. $container->setParameter('fragment.renderer.hinclude.global_template', $config['hinclude_default_template']);
  343. if ($container->getParameter('kernel.debug')) {
  344. $logger = new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE);
  345. $container->getDefinition('templating.loader.cache')
  346. ->addTag('monolog.logger', array('channel' => 'templating'))
  347. ->addMethodCall('setLogger', array($logger));
  348. $container->getDefinition('templating.loader.chain')
  349. ->addTag('monolog.logger', array('channel' => 'templating'))
  350. ->addMethodCall('setLogger', array($logger));
  351. }
  352. if (!empty($config['loaders'])) {
  353. $loaders = array_map(function ($loader) { return new Reference($loader); }, $config['loaders']);
  354. // Use a delegation unless only a single loader was registered
  355. if (1 === \count($loaders)) {
  356. $container->setAlias('templating.loader', (string) reset($loaders));
  357. } else {
  358. $container->getDefinition('templating.loader.chain')->addArgument($loaders);
  359. $container->setAlias('templating.loader', 'templating.loader.chain');
  360. }
  361. }
  362. $container->setParameter('templating.loader.cache.path', null);
  363. if (isset($config['cache'])) {
  364. // Wrap the existing loader with cache (must happen after loaders are registered)
  365. $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader'));
  366. $loaderCache = $container->getDefinition('templating.loader.cache');
  367. $container->setParameter('templating.loader.cache.path', $config['cache']);
  368. $container->setDefinition('templating.loader', $loaderCache);
  369. }
  370. $this->addClassesToCompile(array(
  371. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables',
  372. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference',
  373. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser',
  374. $container->findDefinition('templating.locator')->getClass(),
  375. ));
  376. $container->setParameter('templating.engines', $config['engines']);
  377. $engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']);
  378. // Use a delegation unless only a single engine was registered
  379. if (1 === \count($engines)) {
  380. $container->setAlias('templating', (string) reset($engines));
  381. } else {
  382. $templateEngineDefinition = $container->getDefinition('templating.engine.delegating');
  383. foreach ($engines as $engine) {
  384. $templateEngineDefinition->addMethodCall('addEngine', array($engine));
  385. }
  386. $container->setAlias('templating', 'templating.engine.delegating');
  387. }
  388. $container->getDefinition('fragment.renderer.hinclude')
  389. ->addTag('kernel.fragment_renderer', array('alias' => 'hinclude'))
  390. ->replaceArgument(0, new Reference('templating'))
  391. ;
  392. // configure the PHP engine if needed
  393. if (\in_array('php', $config['engines'], true)) {
  394. $loader->load('templating_php.xml');
  395. $container->setParameter('templating.helper.form.resources', $config['form']['resources']);
  396. if ($container->getParameter('kernel.debug')) {
  397. $loader->load('templating_debug.xml');
  398. $container->setDefinition('templating.engine.php', $container->findDefinition('debug.templating.engine.php'));
  399. $container->setAlias('debug.templating.engine.php', 'templating.engine.php');
  400. }
  401. $this->addClassesToCompile(array(
  402. 'Symfony\\Component\\Templating\\Storage\\FileStorage',
  403. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine',
  404. 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader',
  405. ));
  406. }
  407. if ($container->hasDefinition('assets.packages')) {
  408. $container->getDefinition('templating.helper.assets')->replaceArgument(0, new Reference('assets.packages'));
  409. } else {
  410. $container->removeDefinition('templating.helper.assets');
  411. }
  412. }
  413. private function registerAssetsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  414. {
  415. $loader->load('assets.xml');
  416. $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], '_default');
  417. $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion);
  418. $container->setDefinition('assets._default_package', $defaultPackage);
  419. $namedPackages = array();
  420. foreach ($config['packages'] as $name => $package) {
  421. if (!array_key_exists('version', $package)) {
  422. $version = $defaultVersion;
  423. } else {
  424. $format = $package['version_format'] ?: $config['version_format'];
  425. $version = $this->createVersion($container, $package['version'], $format, $name);
  426. }
  427. $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version));
  428. $namedPackages[$name] = new Reference('assets._package_'.$name);
  429. }
  430. $container->getDefinition('assets.packages')
  431. ->replaceArgument(0, new Reference('assets._default_package'))
  432. ->replaceArgument(1, $namedPackages)
  433. ;
  434. }
  435. /**
  436. * Returns a definition for an asset package.
  437. */
  438. private function createPackageDefinition($basePath, array $baseUrls, Reference $version)
  439. {
  440. if ($basePath && $baseUrls) {
  441. throw new \LogicException('An asset package cannot have base URLs and base paths.');
  442. }
  443. $package = new DefinitionDecorator($baseUrls ? 'assets.url_package' : 'assets.path_package');
  444. $package
  445. ->setPublic(false)
  446. ->replaceArgument(0, $baseUrls ?: $basePath)
  447. ->replaceArgument(1, $version)
  448. ;
  449. return $package;
  450. }
  451. private function createVersion(ContainerBuilder $container, $version, $format, $name)
  452. {
  453. if (null === $version) {
  454. return new Reference('assets.empty_version_strategy');
  455. }
  456. $def = new DefinitionDecorator('assets.static_version_strategy');
  457. $def
  458. ->replaceArgument(0, $version)
  459. ->replaceArgument(1, $format)
  460. ;
  461. $container->setDefinition('assets._version_'.$name, $def);
  462. return new Reference('assets._version_'.$name);
  463. }
  464. private function registerTranslatorConfiguration(array $config, ContainerBuilder $container)
  465. {
  466. if (!$this->isConfigEnabled($container, $config)) {
  467. return;
  468. }
  469. $this->translationConfigEnabled = true;
  470. // Use the "real" translator instead of the identity default
  471. $container->setAlias('translator', 'translator.default');
  472. $translator = $container->findDefinition('translator.default');
  473. $translator->addMethodCall('setFallbackLocales', array($config['fallbacks']));
  474. $container->setParameter('translator.logging', $config['logging']);
  475. // Discover translation directories
  476. $dirs = array();
  477. if (class_exists('Symfony\Component\Validator\Validation')) {
  478. $r = new \ReflectionClass('Symfony\Component\Validator\Validation');
  479. $dirs[] = \dirname($r->getFileName()).'/Resources/translations';
  480. }
  481. if (class_exists('Symfony\Component\Form\Form')) {
  482. $r = new \ReflectionClass('Symfony\Component\Form\Form');
  483. $dirs[] = \dirname($r->getFileName()).'/Resources/translations';
  484. }
  485. if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) {
  486. $r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException');
  487. $dirs[] = \dirname(\dirname($r->getFileName())).'/Resources/translations';
  488. }
  489. $rootDir = $container->getParameter('kernel.root_dir');
  490. foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
  491. if (is_dir($dir = $bundle['path'].'/Resources/translations')) {
  492. $dirs[] = $dir;
  493. }
  494. if (is_dir($dir = $rootDir.sprintf('/Resources/%s/translations', $name))) {
  495. $dirs[] = $dir;
  496. }
  497. }
  498. foreach ($config['paths'] as $dir) {
  499. if (is_dir($dir)) {
  500. $dirs[] = $dir;
  501. } else {
  502. throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir));
  503. }
  504. }
  505. if (is_dir($dir = $rootDir.'/Resources/translations')) {
  506. $dirs[] = $dir;
  507. }
  508. // Register translation resources
  509. if ($dirs) {
  510. foreach ($dirs as $dir) {
  511. $container->addResource(new DirectoryResource($dir));
  512. }
  513. $files = array();
  514. $finder = Finder::create()
  515. ->files()
  516. ->filter(function (\SplFileInfo $file) {
  517. return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename());
  518. })
  519. ->in($dirs)
  520. ;
  521. foreach ($finder as $file) {
  522. list(, $locale) = explode('.', $file->getBasename(), 3);
  523. if (!isset($files[$locale])) {
  524. $files[$locale] = array();
  525. }
  526. $files[$locale][] = (string) $file;
  527. }
  528. $options = array_merge(
  529. $translator->getArgument(3),
  530. array('resource_files' => $files)
  531. );
  532. $translator->replaceArgument(3, $options);
  533. }
  534. }
  535. private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  536. {
  537. if (!$this->isConfigEnabled($container, $config)) {
  538. return;
  539. }
  540. if (!class_exists('Symfony\Component\Validator\Validation')) {
  541. throw new LogicException('Validation support cannot be enabled as the Validator component is not installed.');
  542. }
  543. $loader->load('validator.xml');
  544. $validatorBuilder = $container->getDefinition('validator.builder');
  545. $container->setParameter('validator.translation_domain', $config['translation_domain']);
  546. list($xmlMappings, $yamlMappings) = $this->getValidatorMappingFiles($container);
  547. if (\count($xmlMappings) > 0) {
  548. $validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings));
  549. }
  550. if (\count($yamlMappings) > 0) {
  551. $validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings));
  552. }
  553. $definition = $container->findDefinition('validator.email');
  554. $definition->replaceArgument(0, $config['strict_email']);
  555. if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
  556. $validatorBuilder->addMethodCall('enableAnnotationMapping', array(new Reference('annotation_reader')));
  557. }
  558. if (array_key_exists('static_method', $config) && $config['static_method']) {
  559. foreach ($config['static_method'] as $methodName) {
  560. $validatorBuilder->addMethodCall('addMethodMapping', array($methodName));
  561. }
  562. }
  563. if (isset($config['cache'])) {
  564. $container->setParameter(
  565. 'validator.mapping.cache.prefix',
  566. 'validator_'.$this->getKernelRootHash($container)
  567. );
  568. $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference($config['cache'])));
  569. }
  570. // You can use this parameter to check the API version in your own
  571. // bundle extension classes
  572. // This is set to 2.5-bc for compatibility with Symfony 2.5 and 2.6.
  573. // @deprecated since version 2.7, to be removed in 3.0
  574. $container->setParameter('validator.api', '2.5-bc');
  575. }
  576. private function getValidatorMappingFiles(ContainerBuilder $container)
  577. {
  578. $files = array(array(), array());
  579. if (interface_exists('Symfony\Component\Form\FormInterface')) {
  580. $reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
  581. $files[0][] = \dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
  582. $container->addResource(new FileResource($files[0][0]));
  583. }
  584. foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
  585. $dirname = $bundle['path'];
  586. if (is_file($file = $dirname.'/Resources/config/validation.xml')) {
  587. $files[0][] = $file;
  588. $container->addResource(new FileResource($file));
  589. }
  590. if (is_file($file = $dirname.'/Resources/config/validation.yml')) {
  591. $files[1][] = $file;
  592. $container->addResource(new FileResource($file));
  593. }
  594. if (is_dir($dir = $dirname.'/Resources/config/validation')) {
  595. foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) {
  596. $files[0][] = $file->getPathname();
  597. }
  598. foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) {
  599. $files[1][] = $file->getPathname();
  600. }
  601. $container->addResource(new DirectoryResource($dir));
  602. }
  603. }
  604. return $files;
  605. }
  606. private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader)
  607. {
  608. $loader->load('annotations.xml');
  609. if ('none' !== $config['cache']) {
  610. if ('file' === $config['cache']) {
  611. $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']);
  612. if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
  613. throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
  614. }
  615. $container
  616. ->getDefinition('annotations.filesystem_cache')
  617. ->replaceArgument(0, $cacheDir)
  618. ;
  619. // The annotations.file_cache_reader service is deprecated
  620. $container
  621. ->getDefinition('annotations.file_cache_reader')
  622. ->replaceArgument(1, $cacheDir)
  623. ->replaceArgument(2, $config['debug'])
  624. ;
  625. }
  626. $container
  627. ->getDefinition('annotations.cached_reader')
  628. ->replaceArgument(1, new Reference('file' !== $config['cache'] ? $config['cache'] : 'annotations.filesystem_cache'))
  629. ->replaceArgument(2, $config['debug'])
  630. ;
  631. $container->setAlias('annotation_reader', 'annotations.cached_reader');
  632. }
  633. }
  634. private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  635. {
  636. if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
  637. return;
  638. }
  639. $loader->load('property_access.xml');
  640. $container
  641. ->getDefinition('property_accessor')
  642. ->replaceArgument(0, $config['magic_call'])
  643. ->replaceArgument(1, $config['throw_exception_on_invalid_index'])
  644. ;
  645. }
  646. private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  647. {
  648. if (!$this->isConfigEnabled($container, $config)) {
  649. return;
  650. }
  651. if (!$this->sessionConfigEnabled) {
  652. throw new \LogicException('CSRF protection needs sessions to be enabled.');
  653. }
  654. // Enable services for CSRF protection (even without forms)
  655. $loader->load('security_csrf.xml');
  656. }
  657. private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  658. {
  659. if (!$config['enabled']) {
  660. return;
  661. }
  662. $loader->load('serializer.xml');
  663. $chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
  664. if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
  665. $container->removeAlias('serializer.property_accessor');
  666. $container->removeDefinition('serializer.normalizer.object');
  667. }
  668. $serializerLoaders = array();
  669. if (isset($config['enable_annotations']) && $config['enable_annotations']) {
  670. $annotationLoader = new Definition(
  671. 'Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader',
  672. array(new Reference('annotation_reader'))
  673. );
  674. $annotationLoader->setPublic(false);
  675. $serializerLoaders[] = $annotationLoader;
  676. }
  677. foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
  678. $dirname = $bundle['path'];
  679. if (is_file($file = $dirname.'/Resources/config/serialization.xml')) {
  680. $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file));
  681. $definition->setPublic(false);
  682. $serializerLoaders[] = $definition;
  683. $container->addResource(new FileResource($file));
  684. }
  685. if (is_file($file = $dirname.'/Resources/config/serialization.yml')) {
  686. $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file));
  687. $definition->setPublic(false);
  688. $serializerLoaders[] = $definition;
  689. $container->addResource(new FileResource($file));
  690. }
  691. if (is_dir($dir = $dirname.'/Resources/config/serialization')) {
  692. foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) {
  693. $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getPathname()));
  694. $definition->setPublic(false);
  695. $serializerLoaders[] = $definition;
  696. }
  697. foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) {
  698. $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getPathname()));
  699. $definition->setPublic(false);
  700. $serializerLoaders[] = $definition;
  701. }
  702. $container->addResource(new DirectoryResource($dir));
  703. }
  704. }
  705. $chainLoader->replaceArgument(0, $serializerLoaders);
  706. if (isset($config['cache']) && $config['cache']) {
  707. $container->setParameter(
  708. 'serializer.mapping.cache.prefix',
  709. 'serializer_'.$this->getKernelRootHash($container)
  710. );
  711. $container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument(
  712. 1, new Reference($config['cache'])
  713. );
  714. }
  715. if (isset($config['name_converter']) && $config['name_converter']) {
  716. $container->getDefinition('serializer.normalizer.object')->replaceArgument(1, new Reference($config['name_converter']));
  717. }
  718. }
  719. /**
  720. * Loads property info configuration.
  721. *
  722. * @param array $config
  723. * @param ContainerBuilder $container
  724. * @param XmlFileLoader $loader
  725. */
  726. private function registerPropertyInfoConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
  727. {
  728. if (!$config['enabled']) {
  729. return;
  730. }
  731. $loader->load('property_info.xml');
  732. if (class_exists('phpDocumentor\Reflection\ClassReflector')) {
  733. $definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor');
  734. $definition->addTag('property_info.description_extractor', array('priority' => -1000));
  735. $definition->addTag('property_info.type_extractor', array('priority' => -1001));
  736. }
  737. }
  738. /**
  739. * Gets a hash of the kernel root directory.
  740. *
  741. * @return string
  742. */
  743. private function getKernelRootHash(ContainerBuilder $container)
  744. {
  745. if (!$this->kernelRootHash) {
  746. $this->kernelRootHash = hash('sha256', $container->getParameter('kernel.root_dir'));
  747. }
  748. return $this->kernelRootHash;
  749. }
  750. /**
  751. * Returns the base path for the XSD files.
  752. *
  753. * @return string The XSD base path
  754. */
  755. public function getXsdValidationBasePath()
  756. {
  757. return \dirname(__DIR__).'/Resources/config/schema';
  758. }
  759. public function getNamespace()
  760. {
  761. return 'http://symfony.com/schema/dic/symfony';
  762. }
  763. }