This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
Fabien Potencier 3e99f4e976 merged branch asm89/issue-837 (PR #4935)
This PR was merged into the master branch.

Commits
-------

73db84f [Security] Move translations file to 'security' domain
324703a [Security] Switch to English messages as message keys
aa74769 [Security] Fix CS + unreachable code
2d7a7ba [Security] Fix `AuthenticationException` serialization
50d5724 [Security] Introduced `UsernameNotFoundException#get/setUsername`
39da27a [Security] Removed `get/setExtraInformation`, added `get/set(Token|User)`
837ae15 [Security] Add note about changed constructor to changelog
d6c57cf [FrameworkBundle] Register security exception translations
d7129b9 [Security] Fix exception constructors called in `UserChecker`
0038fbb [Security] Add initial translations for AccountStatusException childs
50e2cfc [Security] Add custom `getMessageKey` AccountStatusException childs
1147977 [Security] Fix InsufficientAuthenticationException constructor calls
79430b8 [Security] Fix AuthenticationServiceException constructor calls
42cced4 [Security] Fix AuthenticationException constructor calls
963a1d7 [Security] Add initial translations for the exceptions
ed6eed4 [Security] Add `getMessageKey` and `getMessageData` to auth exceptions
694c47c [Security] Change signature of `AuthenticationException` to match `\Exception`

Discussion
----------

[2.2][Security] AuthenticationException enhancements

Bug fix: semi
Feature addition: yes
Backwards compatibility break: yes
Symfony2 tests pass: [![Build Status](https://secure.travis-ci.org/asm89/symfony.png?branch=issue-837)](http://travis-ci.org/asm89/symfony)
Fixes the following tickets: #837
License of the code: MIT

This PR adds the functionality discussed in #837 and changes the constructor of the `AuthenticationException` to match that of `\Exception`. This PR will allow developers to show a translated (save) authentication exception message to the user. :)

*Todo:*
- Add some functional test to check that the exceptions can indeed be translated?
- Get feedback on the current English messages

---------------------------------------------------------------------------

by asm89 at 2012-07-15T14:04:11Z

ping @schmittjoh

---------------------------------------------------------------------------

by schmittjoh at 2012-07-15T14:57:32Z

Looks good to me.

While you are at the exceptions, I think we can also get rid of the "extra information" thing and replace it by explicit getters/setters. Mostly that will mean adding set/getToken, set/getUser, set/getUsername. Bundles might add custom exceptions which have other data. This will make it a bit more useful and predictable.

---------------------------------------------------------------------------

by asm89 at 2012-07-15T15:40:45Z

@schmittjoh I removed the `get/setExtraInformation` and added the more explicit getters/setters as you suggested.

---------------------------------------------------------------------------

by asm89 at 2012-07-15T19:33:15Z

@fabpot Did you reschedule this for 2.2? Why? It was originally a 2.1 ticket. I think it is an important one because at the moment there is no reliable way to show users the cause of an `AuthenticationException` without the threat of exposing sensitive information. This issue has been around for a while, see the original issue this PR refers to, or for example [this TODO comment in FOSUB](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Controller/SecurityController.php#L37).

The PR itself is ready to merge now. My only question that remains is about whether the actual translations should be functional tested?

---------------------------------------------------------------------------

by fabpot at 2012-07-15T19:43:19Z

We need to stop at some point. If not, we never release anything. beta3 was scheduled for today and I don't plan any other one before the first RC and I won't have time to review this PR next week. So, if you, @schmittjoh, @vicb, @stof, and a few other core devs "validate" this PR, I might consider merging it before 2.1.

---------------------------------------------------------------------------

by asm89 at 2012-07-15T19:46:09Z

@fabpot I totally agree with your point of view. I just have been trying to pickup some security issues that were still open. :)

---------------------------------------------------------------------------

by stof at 2012-07-15T19:50:29Z

This looks good to me

---------------------------------------------------------------------------

by asm89 at 2012-08-12T09:06:24Z

Since the beta period is over I assume the window was missed to get this security related PR in 2.1. If I have feedback from @fabpot I'll still try to make it mergeable asap though.

---------------------------------------------------------------------------

by fabpot at 2012-08-13T10:10:32Z

@asm89 This would indeed be considered for merging in 2.2.

---------------------------------------------------------------------------

by Antek88 at 2012-10-03T10:30:46Z

+1

---------------------------------------------------------------------------

by stof at 2012-10-04T21:27:15Z

@asm89 could you rebase this PR ? It conflicts with master

---------------------------------------------------------------------------

by fabpot at 2012-10-05T17:16:44Z

What's the status of this PR? @asm89 Have you taken all the feedback into account?

---------------------------------------------------------------------------

by stof at 2012-10-13T17:48:48Z

@asm89 ping

---------------------------------------------------------------------------

by fabpot at 2012-10-29T09:48:40Z

@asm89 If you don't have time, I can finish the work on this PR, but can you just tell me what's left?

---------------------------------------------------------------------------

by asm89 at 2012-10-29T10:02:22Z

I can pick this up, but I have two outstanding questions:
- One about adding `::create()`? https://github.com/symfony/symfony/pull/4935#discussion_r1358297
- And what is the final verdict on the messages? https://github.com/symfony/symfony/pull/4935#discussion_r1165701 The initial idea was that the exception itself have an exception message which is plain english and informative for the developer. If you want to display the 'safe' user messages you have the optional dependency on the translator. There is a comparison made with the Validator component, but in my opinion that's a different case because the violations always contain the message directed at the user and have no plain english message for the developer. Apart from that the Validator component contains it's own code for replacing `{{ }}` variables in messages (duplication? not as flexible as the translator). Concluding I'd opt for: optional dependency on translator component if you want to show 'safe' user messages + message keys.

@schmittjoh Any things to add?

---------------------------------------------------------------------------

by schmittjoh at 2012-10-29T10:14:09Z

Message keys sound good to me. I wouldn't add the ``create`` method for now.

On Mon, Oct 29, 2012 at 11:02 AM, Alexander <notifications@github.com>wrote:

> I can pick this up, but I have two outstanding questions:
>
>    - One about adding ::create()? symfony/symfony#4935<https://github.com/symfony/symfony/issues/4935#discussion_r1358297>
>    - And what is the final verdict on the messages? symfony/symfony#4935<https://github.com/symfony/symfony/issues/4935#discussion_r1165701>The initial idea was that the exception itself have an exception message
>    which is plain english and informative for the developer. If you want to
>    display the 'safe' user messages you have the optional dependency on the
>    translator. There is a comparison made with the Validator component, but in
>    my opinion that's a different case because the violations always contain
>    the message directed at the user and have no plain english message for the
>    developer. Apart from that the Validator component contains it's own code
>    for replacing {{ }} variables in messages (duplication? not as
>    flexible as the translator). Concluding I'd opt for: optional dependency on
>    translator component if you want to show 'safe' user messages + message
>    keys.
>
> @schmittjoh <https://github.com/schmittjoh> Any things to add?
>
> —
> Reply to this email directly or view it on GitHub<https://github.com/symfony/symfony/pull/4935#issuecomment-9861016>.
>
>

---------------------------------------------------------------------------

by fabpot at 2012-10-29T10:27:37Z

As I said in the discussion about the translations, I'm -1 for the message keys to be consistent with how we manage translations everywhere else in the framework.

---------------------------------------------------------------------------

by stof at 2012-10-29T10:30:50Z

@fabpot When we changed the English translation for the validation errors in 2.1, we had to tag the commit as a BC rbeak as it was changing the source for all other translations. And if you look at the state of the files now, you will see that we are *not* using the English as source anymore in some places as some validation errors have a pluralized translation but the source has not been changed.
So I think using a key is more future-proof.

---------------------------------------------------------------------------

by asm89 at 2012-10-30T19:44:49Z

Any final decision on this? On one hand I have @stof and @schmittjoh +1 on message keys, on the other @fabpot -1. I guess it's your call @fabpot.

Edit: also @vicb seemed to be +1 on message keys earlier on.

---------------------------------------------------------------------------

by drak at 2012-11-01T20:19:00Z

I am also -1, I agree with @fabpot

---------------------------------------------------------------------------

by asm89 at 2012-11-12T09:38:51Z

@fabpot Can you please give a definite answer on this? I personally think @stof and @vicb have good points to do message keys, but with all these different people +1 and -1'ing the PR I'm lost on what it should actually do.

---------------------------------------------------------------------------

by asm89 at 2012-11-14T09:59:06Z

ping @fabpot

---------------------------------------------------------------------------

by asm89 at 2012-11-26T10:01:27Z

ping @fabpot We talked about this in Berlin. Any final thoughts on the PR? :) One idea was to do message keys + opt depend on the translator component if you want to use them, or use your own implementation.

---------------------------------------------------------------------------

by fabpot at 2012-11-26T14:01:37Z

The conclusion is: keep using plain English.

On Mon, Nov 26, 2012 at 11:01 AM, Alexander <notifications@github.com>wrote:

> ping @fabpot <https://github.com/fabpot> We talked about this in Berlin.
> Any final thoughts on the PR? :) One idea was to do message keys + opt
> depend on the translator component if you want to use them, or use your own
> implementation.
>
> —
> Reply to this email directly or view it on GitHub<https://github.com/symfony/symfony/pull/4935#issuecomment-10709997>.
>
>

---------------------------------------------------------------------------

by Inori at 2012-11-26T15:00:22Z

is this final? if not, then +1 for message keys

---------------------------------------------------------------------------

by vicb at 2012-11-27T22:33:47Z

@fabpot I can't understand why we keep discussing this for months as this implementation use *both* keys and plain Englis, ie using  keys  is optional ( if it was not it would not be an issue according to #6129)

---------------------------------------------------------------------------

by asm89 at 2013-01-02T21:43:46Z

@fabpot @vicb I'll rebase this PR, fix the comments and refactor the message keys to use plain English + {{ }} syntax for the placeholders.

---------------------------------------------------------------------------

by asm89 at 2013-01-07T15:00:58Z

@fabpot If I fix this tonight, will it make the beta?

---------------------------------------------------------------------------

by fabpot at 2013-01-07T15:53:00Z

yes, definitely.

---------------------------------------------------------------------------

by asm89 at 2013-01-07T20:13:38Z

@fabpot I switched the implementation to English messages instead of message keys and fixed the final comments + rebased. Anything you want me to do after this?

Still happy with `getMessageKey()`?
2013-01-10 14:38:06 +01:00

676 lines
29 KiB
PHP

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
/**
* FrameworkExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class FrameworkExtension extends Extension
{
/**
* Responds to the app.config configuration parameter.
*
* @param array $configs
* @param ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('web.xml');
$loader->load('services.xml');
// A translator must always be registered (as support is included by
// default in the Form component). If disabled, an identity translator
// will be used and everything will still work as expected.
$loader->load('translation.xml');
if ($container->getParameter('kernel.debug')) {
$loader->load('debug.xml');
// only HttpKernel needs the debug event dispatcher
$definition = $container->findDefinition('http_kernel');
$arguments = $definition->getArguments();
$arguments[0] = new Reference('debug.event_dispatcher');
$arguments[2] = new Reference('debug.controller_resolver');
$definition->setArguments($arguments);
}
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
if (isset($config['secret'])) {
$container->setParameter('kernel.secret', $config['secret']);
}
$container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']);
// @deprecated, to be removed in 2.3
$container->setParameter('kernel.trust_proxy_headers', $config['trust_proxy_headers']);
$container->setParameter('kernel.default_locale', $config['default_locale']);
if (!empty($config['test'])) {
$loader->load('test.xml');
}
if (isset($config['session'])) {
$this->registerSessionConfiguration($config['session'], $container, $loader);
}
if (isset($config['form']) && !empty($config['form']['enabled'])) {
$this->registerFormConfiguration($config, $container, $loader);
$config['validation']['enabled'] = true;
}
if (!empty($config['validation']['enabled'])) {
$this->registerValidationConfiguration($config['validation'], $container, $loader);
}
if (isset($config['esi'])) {
$this->registerEsiConfiguration($config['esi'], $loader);
}
if (isset($config['profiler'])) {
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
}
if (isset($config['router'])) {
$this->registerRouterConfiguration($config['router'], $container, $loader);
}
if (isset($config['templating'])) {
$this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader);
}
if (isset($config['translator'])) {
$this->registerTranslatorConfiguration($config['translator'], $container);
}
$this->registerAnnotationsConfiguration($config['annotations'], $container, $loader);
$this->addClassesToCompile(array(
'Symfony\\Component\\HttpFoundation\\ParameterBag',
'Symfony\\Component\\HttpFoundation\\HeaderBag',
'Symfony\\Component\\HttpFoundation\\FileBag',
'Symfony\\Component\\HttpFoundation\\ServerBag',
'Symfony\\Component\\HttpFoundation\\Request',
'Symfony\\Component\\HttpFoundation\\Response',
'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag',
'Symfony\\Component\\Config\\FileLocator',
'Symfony\\Component\\EventDispatcher\\Event',
'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher',
'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener',
'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener',
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
'Symfony\\Component\\HttpKernel\\Event\\KernelEvent',
'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent',
'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent',
'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent',
'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent',
'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent',
'Symfony\\Component\\HttpKernel\\KernelEvents',
'Symfony\\Component\\HttpKernel\\Config\\FileLocator',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
// Cannot be included because annotations will parse the big compiled class file
// 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
));
}
/**
* Loads Form configuration.
*
* @param array $config A configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*
* @throws \LogicException
*/
private function registerFormConfiguration($config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('form.xml');
if (isset($config['csrf_protection'])) {
if (!isset($config['session'])) {
throw new \LogicException('CSRF protection needs that sessions are enabled.');
}
if (!isset($config['secret'])) {
throw new \LogicException('CSRF protection needs a secret to be set.');
}
$loader->load('form_csrf.xml');
$container->setParameter('form.type_extension.csrf.enabled', $config['csrf_protection']['enabled']);
$container->setParameter('form.type_extension.csrf.field_name', $config['csrf_protection']['field_name']);
}
}
/**
* Loads the ESI configuration.
*
* @param array $config An ESI configuration array
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerEsiConfiguration(array $config, XmlFileLoader $loader)
{
if (!empty($config['enabled'])) {
$loader->load('esi.xml');
}
}
/**
* Loads the profiler configuration.
*
* @param array $config A profiler configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*
* @throws \LogicException
*/
private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('profiling.xml');
$loader->load('collectors.xml');
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
$container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
// Choose storage class based on the DSN
$supported = array(
'sqlite' => 'Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage',
'mysql' => 'Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage',
'file' => 'Symfony\Component\HttpKernel\Profiler\FileProfilerStorage',
'mongodb' => 'Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage',
'memcache' => 'Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage',
'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage',
'redis' => 'Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage',
);
list($class, ) = explode(':', $config['dsn'], 2);
if (!isset($supported[$class])) {
throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class));
}
$container->setParameter('profiler.storage.dsn', $config['dsn']);
$container->setParameter('profiler.storage.username', $config['username']);
$container->setParameter('profiler.storage.password', $config['password']);
$container->setParameter('profiler.storage.lifetime', $config['lifetime']);
$container->getDefinition('profiler.storage')->setClass($supported[$class]);
if (isset($config['matcher'])) {
if (isset($config['matcher']['service'])) {
$container->setAlias('profiler.request_matcher', $config['matcher']['service']);
} elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path'])) {
$definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher');
$definition->setPublic(false);
if (isset($config['matcher']['ip'])) {
$definition->addMethodCall('matchIp', array($config['matcher']['ip']));
}
if (isset($config['matcher']['path'])) {
$definition->addMethodCall('matchPath', array($config['matcher']['path']));
}
}
}
if (!$config['enabled']) {
$container->getDefinition('profiler')->addMethodCall('disable', array());
}
}
/**
* Loads the router configuration.
*
* @param array $config A router configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('routing.xml');
$container->setParameter('router.resource', $config['resource']);
$router = $container->findDefinition('router.default');
$argument = $router->getArgument(2);
$argument['strict_requirements'] = $config['strict_requirements'];
if (isset($config['type'])) {
$argument['resource_type'] = $config['type'];
}
$router->replaceArgument(2, $argument);
$container->setParameter('request_listener.http_port', $config['http_port']);
$container->setParameter('request_listener.https_port', $config['https_port']);
$this->addClassesToCompile(array(
'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
'Symfony\\Component\\Routing\\RequestContext',
'Symfony\\Component\\Routing\\Router',
'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher',
$container->findDefinition('router.default')->getClass(),
));
}
/**
* Loads the session configuration.
*
* @param array $config A session configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('session.xml');
// session storage
$container->setAlias('session.storage', $config['storage_id']);
$options = array();
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'gc_maxlifetime', 'gc_probability', 'gc_divisor') as $key) {
if (isset($config[$key])) {
$options[$key] = $config[$key];
}
}
//we deprecated session options without cookie_ prefix, but we are still supporting them,
//Let's merge the ones that were supplied without prefix
foreach (array('lifetime', 'path', 'domain', 'secure', 'httponly') as $key) {
if (!isset($options['cookie_'.$key]) && isset($config[$key])) {
$options['cookie_'.$key] = $config[$key];
}
}
$container->setParameter('session.storage.options', $options);
// session handler (the internal callback registered with PHP session management)
if (null == $config['handler_id']) {
// Set the handler class to be null
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
} else {
$container->setAlias('session.handler', $config['handler_id']);
}
$container->setParameter('session.save_path', $config['save_path']);
$this->addClassesToCompile(array(
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy',
$container->getDefinition('session')->getClass(),
));
if ($container->hasDefinition($config['storage_id'])) {
$this->addClassesToCompile(array(
$container->findDefinition('session.storage')->getClass(),
));
}
}
/**
* Loads the templating configuration.
*
* @param array $config A templating configuration array
* @param string $ide
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerTemplatingConfiguration(array $config, $ide, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('templating.xml');
$loader->load('templating_php.xml');
$links = array(
'textmate' => 'txmt://open?url=file://%%f&line=%%l',
'macvim' => 'mvim://open?url=file://%%f&line=%%l',
);
$container->setParameter('templating.helper.code.file_link_format', isset($links[$ide]) ? $links[$ide] : $ide);
$container->setParameter('templating.helper.form.resources', $config['form']['resources']);
$container->setParameter('templating.hinclude.default_template', $config['hinclude_default_template']);
if ($container->getParameter('kernel.debug')) {
$loader->load('templating_debug.xml');
}
// create package definitions and add them to the assets helper
$defaultPackage = $this->createPackageDefinition($container, $config['assets_base_urls']['http'], $config['assets_base_urls']['ssl'], $config['assets_version'], $config['assets_version_format']);
$container->setDefinition('templating.asset.default_package', $defaultPackage);
$namedPackages = array();
foreach ($config['packages'] as $name => $package) {
$namedPackage = $this->createPackageDefinition($container, $package['base_urls']['http'], $package['base_urls']['ssl'], $package['version'], $package['version_format'], $name);
$container->setDefinition('templating.asset.package.'.$name, $namedPackage);
$namedPackages[$name] = new Reference('templating.asset.package.'.$name);
}
$container->getDefinition('templating.helper.assets')->setArguments(array(
new Reference('templating.asset.default_package'),
$namedPackages,
));
// Apply request scope to assets helper if one or more packages are request-scoped
$requireRequestScope = array_reduce(
$namedPackages,
function($v, Reference $ref) use ($container) {
return $v || 'request' === $container->getDefinition($ref)->getScope();
},
'request' === $defaultPackage->getScope()
);
if ($requireRequestScope) {
$container->getDefinition('templating.helper.assets')->setScope('request');
}
if (!empty($config['loaders'])) {
$loaders = array_map(function($loader) { return new Reference($loader); }, $config['loaders']);
// Use a delegation unless only a single loader was registered
if (1 === count($loaders)) {
$container->setAlias('templating.loader', (string) reset($loaders));
} else {
$container->getDefinition('templating.loader.chain')->addArgument($loaders);
$container->setAlias('templating.loader', 'templating.loader.chain');
}
}
$container->setParameter('templating.loader.cache.path', null);
if (isset($config['cache'])) {
// Wrap the existing loader with cache (must happen after loaders are registered)
$container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader'));
$loaderCache = $container->getDefinition('templating.loader.cache');
$container->setParameter('templating.loader.cache.path', $config['cache']);
$container->setDefinition('templating.loader', $loaderCache);
}
$this->addClassesToCompile(array(
'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser',
$container->findDefinition('templating.locator')->getClass(),
));
if (in_array('php', $config['engines'], true)) {
$this->addClassesToCompile(array(
'Symfony\\Component\\Templating\\Storage\\FileStorage',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader',
));
}
$container->setParameter('templating.engines', $config['engines']);
$engines = array_map(function($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']);
// Use a delegation unless only a single engine was registered
if (1 === count($engines)) {
$container->setAlias('templating', (string) reset($engines));
} else {
$container->getDefinition('templating.engine.delegating')->replaceArgument(1, $engines);
$container->setAlias('templating', 'templating.engine.delegating');
}
}
/**
* Returns a definition for an asset package.
*/
private function createPackageDefinition(ContainerBuilder $container, array $httpUrls, array $sslUrls, $version, $format, $name = null)
{
if (!$httpUrls) {
$package = new DefinitionDecorator('templating.asset.path_package');
$package
->setPublic(false)
->setScope('request')
->replaceArgument(1, $version)
->replaceArgument(2, $format)
;
return $package;
}
if ($httpUrls == $sslUrls) {
$package = new DefinitionDecorator('templating.asset.url_package');
$package
->setPublic(false)
->replaceArgument(0, $sslUrls)
->replaceArgument(1, $version)
->replaceArgument(2, $format)
;
return $package;
}
$prefix = $name ? 'templating.asset.package.'.$name : 'templating.asset.default_package';
$httpPackage = new DefinitionDecorator('templating.asset.url_package');
$httpPackage
->replaceArgument(0, $httpUrls)
->replaceArgument(1, $version)
->replaceArgument(2, $format)
;
$container->setDefinition($prefix.'.http', $httpPackage);
if ($sslUrls) {
$sslPackage = new DefinitionDecorator('templating.asset.url_package');
$sslPackage
->replaceArgument(0, $sslUrls)
->replaceArgument(1, $version)
->replaceArgument(2, $format)
;
} else {
$sslPackage = new DefinitionDecorator('templating.asset.path_package');
$sslPackage
->setScope('request')
->replaceArgument(1, $version)
->replaceArgument(2, $format)
;
}
$container->setDefinition($prefix.'.ssl', $sslPackage);
$package = new DefinitionDecorator('templating.asset.request_aware_package');
$package
->setPublic(false)
->setScope('request')
->replaceArgument(1, $prefix.'.http')
->replaceArgument(2, $prefix.'.ssl')
;
return $package;
}
/**
* Loads the translator configuration.
*
* @param array $config A translator configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
*/
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container)
{
if (!empty($config['enabled'])) {
// Use the "real" translator instead of the identity default
$container->setAlias('translator', 'translator.default');
$translator = $container->findDefinition('translator.default');
$translator->addMethodCall('setFallbackLocale', array($config['fallback']));
// Discover translation directories
$dirs = array();
if (class_exists('Symfony\Component\Validator\Validator')) {
$r = new \ReflectionClass('Symfony\Component\Validator\Validator');
$dirs[] = dirname($r->getFilename()).'/Resources/translations';
}
if (class_exists('Symfony\Component\Form\Form')) {
$r = new \ReflectionClass('Symfony\Component\Form\Form');
$dirs[] = dirname($r->getFilename()).'/Resources/translations';
}
if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) {
$r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException');
$dirs[] = dirname($r->getFilename()).'/../../Resources/translations';
}
$overridePath = $container->getParameter('kernel.root_dir').'/Resources/%s/translations';
foreach ($container->getParameter('kernel.bundles') as $bundle => $class) {
$reflection = new \ReflectionClass($class);
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) {
$dirs[] = $dir;
}
if (is_dir($dir = sprintf($overridePath, $bundle))) {
$dirs[] = $dir;
}
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/translations')) {
$dirs[] = $dir;
}
// Register translation resources
if ($dirs) {
foreach ($dirs as $dir) {
$container->addResource(new DirectoryResource($dir));
}
$finder = Finder::create()
->files()
->filter(function (\SplFileInfo $file) {
return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename());
})
->in($dirs)
;
foreach ($finder as $file) {
// filename is domain.locale.format
list($domain, $locale, $format) = explode('.', $file->getBasename(), 3);
$translator->addMethodCall('addResource', array($format, (string) $file, $locale, $domain));
}
}
}
}
/**
* Loads the validator configuration.
*
* @param array $config A validation configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('validator.xml');
$container->setParameter('validator.translation_domain', $config['translation_domain']);
$container->setParameter('validator.mapping.loader.xml_files_loader.mapping_files', $this->getValidatorXmlMappingFiles($container));
$container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $this->getValidatorYamlMappingFiles($container));
if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
$loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain');
$arguments = $loaderChain->getArguments();
array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader'));
$loaderChain->setArguments($arguments);
}
if (isset($config['cache'])) {
$container->getDefinition('validator.mapping.class_metadata_factory')
->replaceArgument(1, new Reference('validator.mapping.cache.'.$config['cache']));
$container->setParameter(
'validator.mapping.cache.prefix',
'validator_'.md5($container->getParameter('kernel.root_dir'))
);
}
}
private function getValidatorXmlMappingFiles(ContainerBuilder $container)
{
$reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
$files = array(dirname($reflClass->getFileName()).'/Resources/config/validation.xml');
$container->addResource(new FileResource($files[0]));
foreach ($container->getParameter('kernel.bundles') as $bundle) {
$reflection = new \ReflectionClass($bundle);
if (is_file($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) {
$files[] = realpath($file);
$container->addResource(new FileResource($file));
}
}
return $files;
}
private function getValidatorYamlMappingFiles(ContainerBuilder $container)
{
$files = array();
foreach ($container->getParameter('kernel.bundles') as $bundle) {
$reflection = new \ReflectionClass($bundle);
if (is_file($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) {
$files[] = realpath($file);
$container->addResource(new FileResource($file));
}
}
return $files;
}
private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container,$loader)
{
$loader->load('annotations.xml');
if ('file' === $config['cache']) {
$cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']);
if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true)) {
throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
}
$container
->getDefinition('annotations.file_cache_reader')
->replaceArgument(1, $cacheDir)
->replaceArgument(2, $config['debug'])
;
$container->setAlias('annotation_reader', 'annotations.file_cache_reader');
} elseif ('none' !== $config['cache']) {
$container
->getDefinition('annotations.cached_reader')
->replaceArgument(1, new Reference($config['cache']))
->replaceArgument(2, $config['debug'])
;
$container->setAlias('annotation_reader', 'annotations.cached_reader');
}
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/symfony';
}
}