f1393d7b1f
Doctrine's EventManager implementation has several advantages over the EventDispatcher implementation of Symfony2. Therefore I suggest that we use their implementation. Advantages: * Event Listeners are objects, not callbacks. These objects have handler methods that have the same name as the event. This helps a lot when reading the code and makes the code for adding an event listener shorter. * You can create Event Subscribers, which are event listeners with an additional getSubscribedEvents() method. The benefit here is that the code that registers the subscriber doesn't need to know about its implementation. * All events are defined in static Events classes, so users of IDEs benefit of code completion * The communication between the dispatching class of an event and all listeners is done through a subclass of EventArgs. This subclass can be tailored to the type of event. A constructor, setters and getters can be implemented that verify the validity of the data set into the object. See examples below. * Because each event type corresponds to an EventArgs implementation, developers of event listeners can look up the available EventArgs methods and benefit of code completion. * EventArgs::stopPropagation() is more flexible and (IMO) clearer to use than notifyUntil(). Also, it is a concept that is also used in other event implementations Before: class EventListener { public function handle(EventInterface $event, $data) { ... } } $dispatcher->connect('core.request', array($listener, 'handle')); $dispatcher->notify('core.request', new Event(...)); After (with listeners): final class Events { const onCoreRequest = 'onCoreRequest'; } class EventListener { public function onCoreRequest(RequestEventArgs $eventArgs) { ... } } $evm->addEventListener(Events::onCoreRequest, $listener); $evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...)); After (with subscribers): class EventSubscriber { public function onCoreRequest(RequestEventArgs $eventArgs) { ... } public function getSubscribedEvents() { return Events::onCoreRequest; } } $evm->addEventSubscriber($subscriber); $evm->dispatchEvent(Events::onCoreRequest, new RequestEventArgs(...));
511 lines
21 KiB
PHP
511 lines
21 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.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\Definition;
|
|
use Symfony\Component\DependencyInjection\Parameter;
|
|
use Symfony\Component\DependencyInjection\Reference;
|
|
use Symfony\Component\Config\Definition\Processor;
|
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
|
use Symfony\Component\Config\Resource\FileResource;
|
|
use Symfony\Component\Finder\Finder;
|
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
|
use Symfony\Component\Config\FileLocator;
|
|
|
|
/**
|
|
* FrameworkExtension.
|
|
*
|
|
* @author Fabien Potencier <fabien.potencier@symfony-project.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('form.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');
|
|
$container->setDefinition('event_dispatcher', $container->findDefinition('debug.event_dispatcher'));
|
|
$container->setAlias('debug.event_dispatcher', 'event_dispatcher');
|
|
}
|
|
|
|
$processor = new Processor();
|
|
$configuration = new Configuration();
|
|
|
|
$config = $processor->process($configuration->getConfigTree($container->getParameter('kernel.debug')), $configs);
|
|
|
|
$container->setParameter('kernel.cache_warmup', $config['cache_warmer']);
|
|
|
|
if (isset($config['charset'])) {
|
|
$container->setParameter('kernel.charset', $config['charset']);
|
|
}
|
|
|
|
if (isset($config['document_root'])) {
|
|
$container->setParameter('document_root', $config['document_root']);
|
|
}
|
|
|
|
if (isset($config['error_handler'])) {
|
|
if (false === $config['error_handler']) {
|
|
$container->getDefinition('error_handler')->setMethodCalls(array());
|
|
} else {
|
|
$container->getDefinition('error_handler')->addMethodCall('register', array());
|
|
$container->setParameter('error_handler.level', $config['error_handler']);
|
|
}
|
|
}
|
|
|
|
if (isset($config['exception_controller'])) {
|
|
$container->setParameter('exception_listener.controller', $config['exception_controller']);
|
|
}
|
|
|
|
if (isset($config['ide'])) {
|
|
$patterns = array(
|
|
'textmate' => 'txmt://open?url=file://%%f&line=%%l',
|
|
'macvim' => 'mvim://open?url=file://%%f&line=%%l',
|
|
);
|
|
$pattern = isset($patterns[$config['ide']]) ? $patterns[$config['ide']] : $config['ide'];
|
|
$container->setParameter('debug.file_link_format', $pattern);
|
|
}
|
|
|
|
if (!empty($config['test'])) {
|
|
$loader->load('test.xml');
|
|
$config['session']['storage_id'] = 'array';
|
|
}
|
|
|
|
if (isset($config['csrf_protection'])) {
|
|
$this->registerCsrfProtectionConfiguration($config['csrf_protection'], $container);
|
|
}
|
|
|
|
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['session'])) {
|
|
$this->registerSessionConfiguration($config['session'], $container, $loader);
|
|
}
|
|
|
|
if (isset($config['templating'])) {
|
|
$this->registerTemplatingConfiguration($config['templating'], $container, $loader);
|
|
}
|
|
|
|
if (isset($config['translator'])) {
|
|
$this->registerTranslatorConfiguration($config['translator'], $container);
|
|
}
|
|
|
|
if (isset($config['validation'])) {
|
|
$this->registerValidationConfiguration($config['validation'], $container, $loader);
|
|
}
|
|
|
|
$this->addClassesToCompile(array(
|
|
'Symfony\\Component\\HttpFoundation\\ParameterBag',
|
|
'Symfony\\Component\\HttpFoundation\\HeaderBag',
|
|
'Symfony\\Component\\HttpFoundation\\Request',
|
|
'Symfony\\Component\\HttpFoundation\\Response',
|
|
'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag',
|
|
|
|
'Symfony\\Component\\HttpKernel\\HttpKernel',
|
|
'Symfony\\Component\\HttpKernel\\ResponseListener',
|
|
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
|
|
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface',
|
|
'Symfony\\Component\\HttpKernel\\Event\\RequestEventArgs',
|
|
'Symfony\\Component\\HttpKernel\\Event\\ControllerEventArgs',
|
|
'Symfony\\Component\\HttpKernel\\Event\\ViewEventArgs',
|
|
'Symfony\\Component\\HttpKernel\\Event\\ExceptionEventArgs',
|
|
|
|
'Symfony\\Bundle\\FrameworkBundle\\RequestListener',
|
|
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
|
|
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
|
|
'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
|
|
|
|
'Symfony\\Bundle\\FrameworkBundle\\ContainerAwareEventManager',
|
|
|
|
'Doctrine\\Common\\EventManager',
|
|
'Doctrine\\Common\\EventArgs',
|
|
'Doctrine\\Common\\EventSubscriber',
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Loads the CSRF protection configuration.
|
|
*
|
|
* @param array $config A CSRF protection configuration array
|
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
|
*/
|
|
private function registerCsrfProtectionConfiguration(array $config, ContainerBuilder $container)
|
|
{
|
|
foreach (array('enabled', 'field_name', 'secret') as $key) {
|
|
if (isset($config[$key])) {
|
|
$container->setParameter('form.csrf_protection.'.$key, $config[$key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
|
{
|
|
$loader->load('profiling.xml');
|
|
$loader->load('collectors.xml');
|
|
|
|
if (isset($config['only_exceptions'])) {
|
|
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
|
|
}
|
|
|
|
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']));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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('routing.resource', $config['resource']);
|
|
|
|
if (isset($config['type'])) {
|
|
$container->setParameter('router.options.resource_type', $config['type']);
|
|
}
|
|
|
|
if (!empty($config['cache_warmer'])) {
|
|
$container->getDefinition('router.cache_warmer')->addTag('kernel.cache_warmer');
|
|
$container->setAlias('router', 'router.cached');
|
|
}
|
|
|
|
$this->addClassesToCompile(array(
|
|
'Symfony\\Component\\Routing\\RouterInterface',
|
|
'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface',
|
|
'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
|
'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface',
|
|
'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
|
$container->findDefinition('router')->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');
|
|
|
|
if (!empty($config['auto_start'])) {
|
|
$container->getDefinition('session')->addMethodCall('start');
|
|
}
|
|
|
|
if (isset($config['class'])) {
|
|
$container->setParameter('session.class', $config['class']);
|
|
}
|
|
|
|
if (isset($config['default_locale'])) {
|
|
$container->setParameter('session.default_locale', $config['default_locale']);
|
|
}
|
|
|
|
$container->setAlias('session.storage', 'session.storage.'.$config['storage_id']);
|
|
|
|
$options = $container->getParameter('session.storage.'.$config['storage_id'].'.options');
|
|
foreach (array('name', 'lifetime', 'path', 'domain', 'secure', 'httponly', 'db_table', 'db_id_col', 'db_data_col', 'db_time_col') as $key) {
|
|
if (isset($config[$key])) {
|
|
$options[$key] = $config[$key];
|
|
}
|
|
}
|
|
$container->setParameter('session.storage.'.$config['storage_id'].'.options', $options);
|
|
|
|
$this->addClassesToCompile(array(
|
|
'Symfony\\Component\\HttpFoundation\\Session',
|
|
'Symfony\\Component\\HttpFoundation\\SessionStorage\\SessionStorageInterface',
|
|
$container->getParameter('session.class'),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Loads the templating configuration.
|
|
*
|
|
* @param array $config A templating configuration array
|
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
|
* @param XmlFileLoader $loader An XmlFileLoader instance
|
|
*/
|
|
private function registerTemplatingConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
|
{
|
|
$loader->load('templating.xml');
|
|
$loader->load('templating_php.xml');
|
|
|
|
if ($container->getParameter('kernel.debug')) {
|
|
$loader->load('templating_debug.xml');
|
|
}
|
|
|
|
if (isset($config['assets_version'])) {
|
|
$container->setParameter('templating.assets.version', $config['assets_version']);
|
|
}
|
|
|
|
if (isset($config['assets_base_urls'])) {
|
|
$container->setParameter('templating.assets.base_urls', $config['assets_base_urls']);
|
|
}
|
|
|
|
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');
|
|
}
|
|
}
|
|
|
|
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'));
|
|
$container->setDefinition('templating.loader', $container->getDefinition('templating.loader.cache'));
|
|
$container->setParameter('templating.loader.cache.path', $config['cache']);
|
|
} else {
|
|
$container->setParameter('templating.loader.cache.path', null);
|
|
}
|
|
|
|
if (isset($config['cache_warmer'])) {
|
|
$container->getDefinition('templating.cache_warmer.template_paths')->addTag('kernel.cache_warmer');
|
|
$container->setAlias('templating.locator', 'templating.locator.cached');
|
|
}
|
|
|
|
$this->addClassesToCompile(array(
|
|
'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface',
|
|
'Symfony\\Component\\Templating\\TemplateNameParserInterface',
|
|
'Symfony\\Component\\Templating\\TemplateNameParser',
|
|
'Symfony\\Component\\Templating\\EngineInterface',
|
|
'Symfony\\Component\\Config\\FileLocatorInterface',
|
|
'Symfony\\Component\\Templating\\TemplateReferenceInterface',
|
|
'Symfony\\Component\\Templating\\TemplateReference',
|
|
'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\\PhpEngine',
|
|
'Symfony\\Component\\Templating\\Loader\\LoaderInterface',
|
|
'Symfony\\Component\\Templating\\Storage\\Storage',
|
|
'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')->setArgument(1, $engines);
|
|
$container->setAlias('templating', 'templating.engine.delegating');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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->setDefinition('translator', $container->findDefinition('translator.real'));
|
|
|
|
// Discover translation directories
|
|
$dirs = array();
|
|
foreach ($container->getParameter('kernel.bundles') as $bundle) {
|
|
$reflection = new \ReflectionClass($bundle);
|
|
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) {
|
|
$dirs[] = $dir;
|
|
}
|
|
}
|
|
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) {
|
|
$dirs[] = $dir;
|
|
}
|
|
|
|
// Register translation resources
|
|
$resources = array();
|
|
if ($dirs) {
|
|
$finder = new Finder();
|
|
$finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs);
|
|
foreach ($finder as $file) {
|
|
// filename is domain.locale.format
|
|
list($domain, $locale, $format) = explode('.', $file->getBasename());
|
|
|
|
$resources[] = array($format, (string) $file, $locale, $domain);
|
|
}
|
|
}
|
|
$container->setParameter('translation.resources', $resources);
|
|
}
|
|
|
|
if (isset($config['fallback'])) {
|
|
$container->setParameter('translator.fallback_locale', $config['fallback']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (empty($config['enabled'])) {
|
|
return;
|
|
}
|
|
|
|
$loader->load('validator.xml');
|
|
|
|
$xmlMappingFiles = array();
|
|
$yamlMappingFiles = array();
|
|
|
|
// Include default entries from the framework
|
|
$xmlMappingFiles[] = __DIR__.'/../../../Component/Form/Resources/config/validation.xml';
|
|
|
|
foreach ($container->getParameter('kernel.bundles') as $bundle) {
|
|
$reflection = new \ReflectionClass($bundle);
|
|
if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) {
|
|
$xmlMappingFiles[] = realpath($file);
|
|
}
|
|
if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) {
|
|
$yamlMappingFiles[] = realpath($file);
|
|
}
|
|
}
|
|
|
|
$xmlFilesLoader = new Definition('%validator.mapping.loader.xml_files_loader.class%', array($xmlMappingFiles));
|
|
$xmlFilesLoader->setPublic(false);
|
|
|
|
$yamlFilesLoader = new Definition('%validator.mapping.loader.yaml_files_loader.class%', array($yamlMappingFiles));
|
|
$yamlFilesLoader->setPublic(false);
|
|
|
|
$container->setDefinition('validator.mapping.loader.xml_files_loader', $xmlFilesLoader);
|
|
$container->setDefinition('validator.mapping.loader.yaml_files_loader', $yamlFilesLoader);
|
|
|
|
foreach ($xmlMappingFiles as $file) {
|
|
$container->addResource(new FileResource($file));
|
|
}
|
|
|
|
foreach ($yamlMappingFiles as $file) {
|
|
$container->addResource(new FileResource($file));
|
|
}
|
|
|
|
if (isset($config['annotations'])) {
|
|
// Register prefixes for constraint namespaces
|
|
if (!empty($config['annotations']['namespaces'])) {
|
|
$container->setParameter('validator.annotations.namespaces', array_merge(
|
|
$container->getParameter('validator.annotations.namespaces'),
|
|
$config['annotations']['namespaces']
|
|
));
|
|
}
|
|
|
|
// Register annotation loader
|
|
$annotationLoader = new Definition('%validator.mapping.loader.annotation_loader.class%');
|
|
$annotationLoader->setPublic(false);
|
|
$annotationLoader->addArgument(new Parameter('validator.annotations.namespaces'));
|
|
|
|
$container->setDefinition('validator.mapping.loader.annotation_loader', $annotationLoader);
|
|
|
|
$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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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://www.symfony-project.org/schema/dic/symfony';
|
|
}
|
|
}
|