diff --git a/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php b/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php index d191604ef6bac..a767f03227cf0 100644 --- a/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php +++ b/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php @@ -2,6 +2,8 @@ namespace Symfony\Bundle\CompatAssetsBundle; +use Symfony\Component\HttpKernel\Bundle\Bundle; + /* * This file is part of the Symfony framework. * diff --git a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php index 479a1dfce4108..3359c8fb900e9 100644 --- a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php +++ b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php @@ -118,7 +118,6 @@ protected function loadDbalConnection(array $connection, ContainerBuilder $conta $driverOptions = array(); $driverDef = new Definition('Doctrine\DBAL\DriverManager'); - $driverDef->setPublic(false); $driverDef->setFactoryMethod('getConnection'); $container->setDefinition(sprintf('doctrine.dbal.%s_connection', $connection['name']), $driverDef); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php b/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php index 22739721eafac..4f832d8a9cadf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php +++ b/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php @@ -39,11 +39,7 @@ public function __construct(LoggerInterface $logger = null) } /** - * Notifies all listeners of a given event. - * - * @param Event $event A Event instance - * - * @return Event The Event instance + * {@inheritDoc} */ public function notify(Event $event) { @@ -57,11 +53,7 @@ public function notify(Event $event) } /** - * Notifies all listeners of a given event until one returns a non null value. - * - * @param Event $event A Event instance - * - * @return Event The Event instance + * {@inheritDoc} */ public function notifyUntil(Event $event) { @@ -87,12 +79,7 @@ public function notifyUntil(Event $event) } /** - * Filters a value by calling all listeners of a given event. - * - * @param Event $event A Event instance - * @param mixed $value The value to be filtered - * - * @return Event The Event instance + * {@inheritDoc} */ public function filter(Event $event, $value) { @@ -107,12 +94,18 @@ public function filter(Event $event, $value) return $event; } - public function getCalledEvents() + /** + * {@inheritDoc} + */ + public function getCalledListeners() { return $this->called; } - public function getNotCalledEvents() + /** + * {@inheritDoc} + */ + public function getNotCalledListeners() { $notCalled = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 8bd237e0bf414..e8cbfb22c30e8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -55,6 +55,11 @@ public function boot() }); } + public function shutdown() + { + FormConfiguration::clearDefaultCsrfSecrets(); + } + public function registerExtensions(ContainerBuilder $container) { parent::registerExtensions($container); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml index ce6956577345e..efaf59facea95 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml @@ -97,7 +97,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_acl.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_acl.xml index 080546e4d9df1..7682bc8683396 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_acl.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_acl.xml @@ -26,7 +26,7 @@ - + @@ -76,4 +76,4 @@ - \ No newline at end of file + diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 33caa7317d5a9..8afd80a252367 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -50,11 +50,21 @@ public function configLoad($config, ContainerBuilder $container) $globals = $this->fixConfig($config, 'global'); if (isset($globals[0])) { foreach ($globals as $global) { - $def->addMethodCall('addGlobal', array($global['key'], new Reference($global['id']))); + if (isset($global['type']) && 'service' === $global['type']) { + $def->addMethodCall('addGlobal', array($global['key'], new Reference($global['id']))); + } elseif (isset($global['value'])) { + $def->addMethodCall('addGlobal', array($global['key'], $global['value'])); + } else { + throw new \InvalidArgumentException(sprintf('Unable to understand global configuration (%s).', var_export($global, true))); + } } } else { - foreach ($globals as $key => $id) { - $def->addMethodCall('addGlobal', array($key, new Reference($id))); + foreach ($globals as $key => $value) { + if ('@' === substr($value, 0, 1)) { + $def->addMethodCall('addGlobal', array($key, new Reference(substr($value, 1)))); + } else { + $def->addMethodCall('addGlobal', array($key, $value)); + } } } unset($config['globals'], $config['global']); diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd index 2df43123c49a2..9cbbff1c87307 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd @@ -10,7 +10,7 @@ - + @@ -29,8 +29,9 @@ - - + + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index d4e81b6b50f3e..7f0e7850055b0 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -14,6 +14,7 @@ use Symfony\Bundle\TwigBundle\Tests\TestCase; use Symfony\Bundle\TwigBundle\DependencyInjection\TwigExtension; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class TwigExtensionTest extends TestCase { @@ -36,18 +37,28 @@ public function testConfigGlobals() // XML $container = new ContainerBuilder(); $loader = new TwigExtension(); - $loader->configLoad(array('global' => array(array('key' => 'foo', 'id' => 'bar'))), $container); + $loader->configLoad(array('global' => array( + array('key' => 'foo', 'type' => 'service', 'id' => 'bar'), + array('key' => 'pi', 'value' => 3.14), + )), $container); $config = $container->getDefinition('twig')->getMethodCalls(); $this->assertEquals('foo', $config[0][1][0]); - $this->assertEquals('bar', (string) $config[0][1][1]); + $this->assertEquals(new Reference('bar'), $config[0][1][1]); + $this->assertEquals('pi', $config[1][1][0]); + $this->assertEquals(3.14, $config[1][1][1]); // YAML, PHP $container = new ContainerBuilder(); $loader = new TwigExtension(); - $loader->configLoad(array('globals' => array('foo' => 'bar')), $container); + $loader->configLoad(array('globals' => array( + 'foo' => '@bar', + 'pi' => 3.14, + )), $container); $config = $container->getDefinition('twig')->getMethodCalls(); $this->assertEquals('foo', $config[0][1][0]); - $this->assertEquals('bar', (string) $config[0][1][1]); + $this->assertEquals(new Reference('bar'), $config[0][1][1]); + $this->assertEquals('pi', $config[1][1][0]); + $this->assertEquals(3.14, $config[1][1][1]); } public function testConfigExtensions() diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.twig index f8ce2e68ca2b1..f7906683941da 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.twig @@ -6,7 +6,7 @@ Events {% endblock %} {% block panel %} -

Called Events

+

Called Listeners

@@ -14,27 +14,27 @@ Events - {% for event in collector.calledevents %} + {% for elements in collector.calledlisteners %} - - - + + + {% endfor %}
Caller Listener
{{ event.event }}{{ event.caller|abbr_class }}{{ event.listener|abbr_method }}(){{ elements.event }}{{ elements.caller|abbr_class }}{{ elements.listener|abbr_method }}()
- {% if collector.notcalledevents %} -

Not Called Events

+ {% if collector.notcalledlisteners %} +

Not Called Listeners

- {% for event in collector.notcalledevents %} + {% for elements in collector.notcalledlisteners %} - - + + {% endfor %}
Event Listener
{{ event.event }}{{ event.listener|abbr_method }}(){{ elements.event }}{{ elements.listener|abbr_method }}()
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 7db8f1a8605b6..c27c8f52cd148 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -657,55 +657,57 @@ public function renderException($e, $output) return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); }; - $title = sprintf(' [%s] ', get_class($e)); - $len = $strlen($title); - $lines = array(); - foreach (explode("\n", $e->getMessage()) as $line) { - $lines[] = sprintf(' %s ', $line); - $len = max($strlen($line) + 4, $len); - } - - $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); + do { + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $lines = array(); + foreach (explode("\n", $e->getMessage()) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } - foreach ($lines as $line) { - $messages[] = $line.str_repeat(' ', $len - $strlen($line)); - } + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); - $messages[] = str_repeat(' ', $len); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } - $output->writeln("\n"); - foreach ($messages as $message) { - $output->writeln(''.$message.''); - } - $output->writeln("\n"); + $messages[] = str_repeat(' ', $len); - if (null !== $this->runningCommand) { - $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); $output->writeln("\n"); - } - - if (Output::VERBOSITY_VERBOSE === $output->getVerbosity()) { - $output->writeln('Exception trace:'); - - // exception related properties - $trace = $e->getTrace(); - array_unshift($trace, array( - 'function' => '', - 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', - 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', - 'args' => array(), - )); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln("\n"); - for ($i = 0, $count = count($trace); $i < $count; $i++) { - $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; - $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; - $function = $trace[$i]['function']; - $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; - $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + if (Output::VERBOSITY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; $i++) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + } - $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + $output->writeln("\n"); } + } while ($e = $e->getPrevious()); + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); $output->writeln("\n"); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php new file mode 100644 index 0000000000000..2b1cfcf73fa11 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -0,0 +1,89 @@ + + */ +class AnalyzeServiceReferencesPass implements RepeatablePassInterface, CompilerAwareInterface +{ + protected $graph; + protected $container; + protected $currentId; + protected $currentDefinition; + protected $repeatedPass; + + public function setRepeatedPass(RepeatedPass $repeatedPass) { + $this->repeatedPass = $repeatedPass; + } + + public function setCompiler(Compiler $compiler) + { + $this->graph = $compiler->getServiceReferenceGraph(); + } + + public function process(ContainerBuilder $container) + { + $this->container = $container; + + if (null === $this->graph) { + $this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph(); + } + $this->graph->clear(); + + foreach ($container->getDefinitions() as $id => $definition) { + $this->currentId = $id; + $this->currentDefinition = $definition; + $this->processArguments($definition->getArguments()); + $this->processArguments($definition->getMethodCalls()); + } + + foreach ($container->getAliases() as $id => $alias) { + $this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null); + } + } + + protected function processArguments(array $arguments) + { + foreach ($arguments as $k => $argument) { + if (is_array($argument)) { + $this->processArguments($argument); + } else if ($argument instanceof Reference) { + $this->graph->connect( + $this->currentId, + $this->currentDefinition, + (string) $argument, + $this->getDefinition((string) $argument), + $argument + ); + } else if ($argument instanceof Definition) { + $this->processArguments($argument->getArguments()); + $this->processArguments($argument->getMethodCalls()); + } + } + } + + protected function getDefinition($id) + { + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + if (!$this->container->hasDefinition($id)) { + return null; + } + + return $this->container->getDefinition($id); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php new file mode 100644 index 0000000000000..ff6c48fd25e14 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -0,0 +1,76 @@ + + */ +class Compiler +{ + protected $passConfig; + protected $currentPass; + protected $currentStartTime; + protected $log; + protected $serviceReferenceGraph; + + public function __construct() + { + $this->passConfig = new PassConfig(); + $this->serviceReferenceGraph = new ServiceReferenceGraph(); + $this->log = array(); + } + + public function getPassConfig() + { + return $this->passConfig; + } + + public function getServiceReferenceGraph() + { + return $this->serviceReferenceGraph; + } + + public function addPass(CompilerPassInterface $pass) + { + $this->passConfig->addPass($pass); + } + + public function addLogMessage($string) + { + $this->log[] = $string; + } + + public function getLog() + { + return $this->log; + } + + public function compile(ContainerBuilder $container) + { + foreach ($this->passConfig->getPasses() as $pass) { + $this->startPass($pass); + $pass->process($container); + $this->endPass($pass); + } + } + + protected function startPass(CompilerPassInterface $pass) + { + if ($pass instanceof CompilerAwareInterface) { + $pass->setCompiler($this); + } + + $this->currentPass = $pass; + $this->currentStartTime = microtime(true); + } + + protected function endPass(CompilerPassInterface $pass) + { + $this->currentPass = null; + $this->addLogMessage(sprintf('%s finished in %.3fs', get_class($pass), microtime(true) - $this->currentStartTime)); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CompilerAwareInterface.php b/src/Symfony/Component/DependencyInjection/Compiler/CompilerAwareInterface.php new file mode 100644 index 0000000000000..543a9004f4cd7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/CompilerAwareInterface.php @@ -0,0 +1,14 @@ + + */ +interface CompilerAwareInterface +{ + function setCompiler(Compiler $compiler); +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index e63b4f8f69224..09c6a91cfa320 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -3,7 +3,6 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -21,20 +20,19 @@ * * @author Johannes M. Schmitt */ -class InlineServiceDefinitionsPass implements CompilerPassInterface +class InlineServiceDefinitionsPass implements RepeatablePassInterface { - protected $aliasMap; + protected $repeatedPass; + protected $graph; - public function process(ContainerBuilder $container) + public function setRepeatedPass(RepeatedPass $repeatedPass) { - $this->aliasMap = array(); - foreach ($container->getAliases() as $id => $alias) { - if (!$alias->isPublic()) { - continue; - } + $this->repeatedPass = $repeatedPass; + } - $this->aliasMap[$id] = (string) $alias; - } + public function process(ContainerBuilder $container) + { + $this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph(); foreach ($container->getDefinitions() as $id => $definition) { $definition->setArguments( @@ -58,8 +56,15 @@ protected function inlineArguments(ContainerBuilder $container, array $arguments } if ($this->isInlinableDefinition($container, $id, $definition = $container->getDefinition($id))) { - $arguments[$k] = $definition; + if ($definition->isShared()) { + $arguments[$k] = $definition; + } else { + $arguments[$k] = clone $definition; + } } + } else if ($argument instanceof Definition) { + $argument->setArguments($this->inlineArguments($container, $argument->getArguments())); + $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls())); } } @@ -76,43 +81,15 @@ protected function isInlinableDefinition(ContainerBuilder $container, $id, Defin return false; } - $references = count(array_keys($this->aliasMap, $id, true)); - foreach ($container->getDefinitions() as $cDefinition) - { - if ($references > 1) { - break; - } - - if ($this->isReferencedByArgument($id, $cDefinition->getArguments())) { - $references += 1; - continue; - } - - foreach ($cDefinition->getMethodCalls() as $call) { - if ($this->isReferencedByArgument($id, $call[1])) { - $references += 1; - continue 2; - } - } + if (!$this->graph->hasNode($id)) { + return true; } - return $references <= 1; - } - - protected function isReferencedByArgument($id, $argument) - { - if (is_array($argument)) { - foreach ($argument as $arg) { - if ($this->isReferencedByArgument($id, $arg)) { - return true; - } - } - } else if ($argument instanceof Reference) { - if ($id === (string) $argument) { - return true; - } + $ids = array(); + foreach ($this->graph->getNode($id)->getInEdges() as $edge) { + $ids[] = $edge->getSourceNode()->getId(); } - return false; + return count(array_unique($ids)) <= 1; } } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 485656e6ab8d0..62eeef18497a5 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -14,14 +14,22 @@ /** * Compiler Pass Configuration * + * This class has a default configuration embedded. + * * @author Johannes M. Schmitt */ class PassConfig { + const TYPE_AFTER_REMOVING = 'afterRemoving'; + const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; + const TYPE_BEFORE_REMOVING = 'beforeRemoving'; const TYPE_OPTIMIZE = 'optimization'; const TYPE_REMOVE = 'removing'; protected $mergePass; + protected $afterRemovingPasses; + protected $beforeOptimizationPasses; + protected $beforeRemovingPasses; protected $optimizationPasses; protected $removingPasses; @@ -29,6 +37,10 @@ public function __construct() { $this->mergePass = new MergeExtensionConfigurationPass(); + $this->afterRemovingPasses = array(); + $this->beforeOptimizationPasses = array(); + $this->beforeRemovingPasses = array(); + $this->optimizationPasses = array( new ResolveParameterPlaceHoldersPass(), new ResolveReferencesToAliasesPass(), @@ -39,8 +51,12 @@ public function __construct() $this->removingPasses = array( new RemovePrivateAliasesPass(), new ReplaceAliasByActualDefinitionPass(), - new InlineServiceDefinitionsPass(), - new RemoveUnusedDefinitionsPass(), + new RepeatedPass(array( + new AnalyzeServiceReferencesPass(), + new InlineServiceDefinitionsPass(), + new AnalyzeServiceReferencesPass(), + new RemoveUnusedDefinitionsPass(), + )), ); } @@ -48,12 +64,15 @@ public function getPasses() { return array_merge( array($this->mergePass), + $this->beforeOptimizationPasses, $this->optimizationPasses, - $this->removingPasses + $this->beforeRemovingPasses, + $this->removingPasses, + $this->afterRemovingPasses ); } - public function addPass(CompilerPassInterface $pass, $type = self::TYPE_OPTIMIZE) + public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION) { $property = $type.'Passes'; if (!isset($this->$property)) { @@ -64,6 +83,16 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_OPTIMIZE $passes[] = $pass; } + public function getBeforeOptimizationPasses() + { + return $this->beforeOptimizationPasses; + } + + public function getBeforeRemovingPasses() + { + return $this->beforeRemovingPasses; + } + public function getOptimizationPasses() { return $this->optimizationPasses; @@ -84,6 +113,16 @@ public function setMergePass(CompilerPassInterface $pass) $this->mergePass = $pass; } + public function setBeforeOptimizationPasses(array $passes) + { + $this->beforeOptimizationPasses = $passes; + } + + public function setBeforeRemovingPasses(array $passes) + { + $this->beforeRemovingPasses = $passes; + } + public function setOptimizationPasses(array $passes) { $this->optimizationPasses = $passes; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php index 470a0423b7dc0..014fc9a0b9de0 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php @@ -2,6 +2,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,22 +21,46 @@ * * @author Johannes M. Schmitt */ -class RemoveUnusedDefinitionsPass implements CompilerPassInterface +class RemoveUnusedDefinitionsPass implements RepeatablePassInterface { + protected $repeatedPass; + protected $graph; + + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + $this->repeatedPass = $repeatedPass; + } + public function process(ContainerBuilder $container) { + $this->graph = $this->repeatedPass->getCompiler()->getServiceReferenceGraph(); + $hasChanged = false; - $aliases = $container->getAliases(); foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isPublic()) { continue; } - $referencingAliases = array_keys($aliases, $id, true); - $isReferenced = $this->isReferenced($container, $id); + if ($this->graph->hasNode($id)) { + $edges = $this->graph->getNode($id)->getInEdges(); + $referencingAliases = array(); + $sourceIds = array(); + foreach ($edges as $edge) { + $node = $edge->getSourceNode(); + $sourceIds[] = $node->getId(); + + if ($node->isAlias()) { + $referencingAlias[] = $node->getValue(); + } + } + $isReferenced = (count(array_unique($sourceIds)) - count($referencingAliases)) > 0; + } else { + $referencingAliases = array(); + $isReferenced = false; + } if (1 === count($referencingAliases) && false === $isReferenced) { - $container->setDefinition(reset($referencingAliases), $definition); + $container->setDefinition((string) reset($referencingAliases), $definition); $definition->setPublic(true); $container->remove($id); } else if (0 === count($referencingAliases) && false === $isReferenced) { @@ -45,47 +70,7 @@ public function process(ContainerBuilder $container) } if ($hasChanged) { - $this->process($container); + $this->repeatedPass->setRepeat(); } } - - protected function isReferenced(ContainerBuilder $container, $id) - { - foreach ($container->getDefinitions() as $definition) { - if ($this->isReferencedByArgument($id, $definition->getArguments())) { - return true; - } - - if ($this->isReferencedByArgument($id, $definition->getMethodCalls())) { - return true; - } - } - - return false; - } - - protected function isReferencedByArgument($id, $argument) - { - if (is_array($argument)) { - foreach ($argument as $arg) { - if ($this->isReferencedByArgument($id, $arg)) { - return true; - } - } - } else if ($argument instanceof Reference) { - if ($id === (string) $argument) { - return true; - } - } else if ($argument instanceof Definition) { - if ($this->isReferencedByArgument($id, $argument->getArguments())) { - return true; - } - - if ($this->isReferencedByArgument($id, $argument->getMethodCalls())) { - return true; - } - } - - return false; - } } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php new file mode 100644 index 0000000000000..bb31752fc46e9 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php @@ -0,0 +1,14 @@ + + */ +interface RepeatablePassInterface extends CompilerPassInterface +{ + function setRepeatedPass(RepeatedPass $repeatedPass); +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php new file mode 100644 index 0000000000000..987d442c4c186 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php @@ -0,0 +1,61 @@ + + */ +class RepeatedPass implements CompilerPassInterface, CompilerAwareInterface +{ + protected $repeat; + protected $compiler; + protected $passes; + + public function __construct(array $passes) + { + foreach ($passes as $pass) { + if (!$pass instanceof RepeatablePassInterface) { + throw new \InvalidArgumentException('$passes must be an array of RepeatablePassInterface.'); + } + + $pass->setRepeatedPass($this); + } + + $this->passes = $passes; + } + + public function setCompiler(Compiler $compiler) + { + $this->compiler = $compiler; + } + + public function getCompiler() + { + return $this->compiler; + } + + public function process(ContainerBuilder $container) + { + $this->repeat = false; + foreach ($this->passes as $pass) { + $time = microtime(true); + $pass->process($container); + $this->compiler->addLogMessage(sprintf( + '%s finished in %.3fs', get_class($pass), microtime(true) - $time + )); + } + + if ($this->repeat) { + $this->process($container); + } + } + + public function setRepeat() + { + $this->repeat = true; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php new file mode 100644 index 0000000000000..d0e4faefb44f2 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php @@ -0,0 +1,64 @@ + + */ +class ServiceReferenceGraph +{ + protected $nodes; + + public function __construct() + { + $this->nodes = array(); + } + + public function hasNode($id) + { + return isset($this->nodes[$id]); + } + + public function getNode($id) + { + if (!isset($this->nodes[$id])) { + throw new \InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); + } + + return $this->nodes[$id]; + } + + public function getNodes() + { + return $this->nodes; + } + + public function clear() + { + $this->nodes = array(); + } + + public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null) + { + $sourceNode = $this->createNode($sourceId, $sourceValue); + $destNode = $this->createNode($destId, $destValue); + $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference); + + $sourceNode->addOutEdge($edge); + $destNode->addInEdge($edge); + } + + protected function createNode($id, $value) + { + if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { + return $this->nodes[$id]; + } + + return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php new file mode 100644 index 0000000000000..71e775b03223b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php @@ -0,0 +1,39 @@ + + */ +class ServiceReferenceGraphEdge +{ + protected $sourceNode; + protected $destNode; + protected $value; + + public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null) + { + $this->sourceNode = $sourceNode; + $this->destNode = $destNode; + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function getSourceNode() + { + return $this->sourceNode; + } + + public function getDestNode() + { + return $this->destNode; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php new file mode 100644 index 0000000000000..8ab0744124af5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -0,0 +1,69 @@ + + */ +class ServiceReferenceGraphNode +{ + protected $id; + protected $inEdges; + protected $outEdges; + protected $value; + + public function __construct($id, $value) + { + $this->id = $id; + $this->value = $value; + $this->inEdges = array(); + $this->outEdges = array(); + } + + public function addInEdge(ServiceReferenceGraphEdge $edge) + { + $this->inEdges[] = $edge; + } + + public function addOutEdge(ServiceReferenceGraphEdge $edge) + { + $this->outEdges[] = $edge; + } + + public function isAlias() + { + return $this->value instanceof Alias; + } + + public function isDefinition() + { + return $this->value instanceof Definition; + } + + public function getId() + { + return $this->id; + } + + public function getInEdges() + { + return $this->inEdges; + } + + public function getOutEdges() + { + return $this->outEdges; + } + + public function getValue() + { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index cc6cffe0a593a..f1a9c32fce076 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -2,12 +2,7 @@ namespace Symfony\Component\DependencyInjection; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; - -use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; - -use Symfony\Component\DependencyInjection\Compiler\ResolveInterfaceInjectorsPass; -use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\InterfaceInjector; @@ -39,7 +34,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface protected $resources = array(); protected $extensionConfigs = array(); protected $injectors = array(); - protected $compilerPassConfig; + protected $compiler; /** * Constructor @@ -49,7 +44,7 @@ public function __construct(ParameterBagInterface $parameterBag = null) { parent::__construct($parameterBag); - $this->compilerPassConfig = new PassConfig(); + $this->compiler = new Compiler(); } /** @@ -154,7 +149,7 @@ public function loadFromExtension($extension, $tag, array $values = array()) */ public function addCompilerPass(CompilerPassInterface $pass) { - $this->compilerPassConfig->addPass($pass); + $this->compiler->addPass($pass); } /** @@ -164,7 +159,17 @@ public function addCompilerPass(CompilerPassInterface $pass) */ public function getCompilerPassConfig() { - return $this->compilerPassConfig; + return $this->compiler->getPassConfig(); + } + + /** + * Returns the compiler instance + * + * @return Compiler + */ + public function getCompiler() + { + return $this->compiler; } /** @@ -335,9 +340,7 @@ public function setExtensionConfigs(array $config) */ public function freeze() { - foreach ($this->compilerPassConfig->getPasses() as $pass) { - $pass->process($this); - } + $this->compiler->compile($this); parent::freeze(); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 9ac916fab9fd9..310be9cf9eb0a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -182,11 +182,27 @@ protected function addServiceInlinedDefinitions($id, $definition) { $code = ''; $variableMap = $this->definitionVariables; + $nbOccurrences = new \SplObjectStorage(); + $processed = new \SplObjectStorage(); + $inlinedDefinitions = $this->getInlinedDefinitions($definition); + + foreach ($inlinedDefinitions as $definition) { + if (false === $nbOccurrences->contains($definition)) { + $nbOccurrences->offsetSet($definition, 1); + } else { + $i = $nbOccurrences->offsetGet($definition); + $nbOccurrences->offsetSet($definition, $i+1); + } + } + + foreach ($inlinedDefinitions as $sDefinition) { + if ($processed->contains($sDefinition)) { + continue; + } + $processed->offsetSet($sDefinition); - $c = 1; - foreach ($this->getInlinedDefinitions($definition) as $sDefinition) { $class = $this->dumpValue($sDefinition->getClass()); - if (count($sDefinition->getMethodCalls()) > 0 || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { + if ($nbOccurrences->offsetGet($sDefinition) > 1 || count($sDefinition->getMethodCalls()) > 0 || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { $name = $this->getNextVariableName(); $variableMap->offsetSet($sDefinition, new Variable($name)); @@ -326,7 +342,13 @@ protected function addServiceInlinedDefinitionsSetup($id, $definition) $this->referenceVariables[$id] = new Variable('instance'); $code = ''; + $processed = new \SplObjectStorage(); foreach ($this->getInlinedDefinitions($definition) as $iDefinition) { + if ($processed->contains($iDefinition)) { + continue; + } + $processed->offsetSet($iDefinition); + if (!$this->hasReference($id, $iDefinition->getMethodCalls())) { continue; } diff --git a/src/Symfony/Component/Form/FormConfiguration.php b/src/Symfony/Component/Form/FormConfiguration.php index ada2b59f3a9c3..540a10f607ba7 100644 --- a/src/Symfony/Component/Form/FormConfiguration.php +++ b/src/Symfony/Component/Form/FormConfiguration.php @@ -108,6 +108,11 @@ static public function addDefaultCsrfSecret($secret) self::$defaultCsrfSecrets[] = $secret; } + static public function clearDefaultCsrfSecrets() + { + self::$defaultCsrfSecrets = array(); + } + /** * Returns the default CSRF secrets * diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 092405af7650f..c6a49a22b97f8 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -951,6 +951,10 @@ protected function convertFileInformation(array $files) $keys = array_keys($data); sort($keys); + if ($keys == $fileKeys) { + $data['error'] = (int) $data['error']; + } + if ($keys != $fileKeys) { $fixedFiles[$key] = $this->convertFileInformation($data); } else if ($data['error'] === UPLOAD_ERR_NO_FILE) { diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 44ee891c9a9f0..b854b5a1dcc28 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -19,7 +19,7 @@ class Response { /** - * @var \Symfony\Component\HttpFoundation\HeaderBag + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag */ public $headers; diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php index 7e7cee12871f5..cee0e2b20ecdf 100644 --- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -39,6 +39,7 @@ public function set($key, $values, $replace = true) { parent::set($key, $values, $replace); + // ensure the cache-control header has sensible defaults if ('cache-control' === strtr(strtolower($key), '_', '-')) { $computed = $this->computeCacheControlValue(); $this->headers['cache-control'] = array($computed); @@ -115,6 +116,14 @@ public function getCacheControlDirective($key) return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; } + /** + * Returns the calculated value of the cache-control header. + * + * This considers several other headers and calculates or modifies the + * cache-control header to a sensible, conservative value. + * + * @return string + */ protected function computeCacheControlValue() { if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php index 531236715ae8a..ba07ff83095c1 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -38,33 +38,33 @@ public function setEventDispatcher(EventDispatcher $dispatcher) public function collect(Request $request, Response $response, \Exception $exception = null) { $this->data = array( - 'called_events' => null !== $this->dispatcher ? $this->dispatcher->getCalledEvents() : array(), - 'not_called_events' => null !== $this->dispatcher ? $this->dispatcher->getNotCalledEvents() : array(), + 'called_listeners' => null !== $this->dispatcher ? $this->dispatcher->getCalledListeners() : array(), + 'not_called_listeners' => null !== $this->dispatcher ? $this->dispatcher->getNotCalledListeners() : array(), ); } /** - * Gets the called events. + * Gets the called listeners. * - * @return array An array of called events + * @return array An array of called listeners * * @see EventDispatcherTraceableInterface */ - public function getCalledEvents() + public function getCalledListeners() { - return $this->data['called_events']; + return $this->data['called_listeners']; } /** - * Gets the not called events. + * Gets the not called listeners. * - * @return array An array of not called events + * @return array An array of not called listeners * * @see EventDispatcherTraceableInterface */ - public function getNotCalledEvents() + public function getNotCalledListeners() { - return $this->data['not_called_events']; + return $this->data['not_called_listeners']; } /** diff --git a/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php b/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php index cc74a883df9aa..82dd5c139226d 100644 --- a/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php +++ b/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php @@ -16,7 +16,17 @@ */ interface EventDispatcherTraceableInterface { - function getCalledEvents(); + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + function getCalledListeners(); - function getNotCalledEvents(); + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + function getNotCalledListeners(); } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 293725115d7b0..318a0edd4de56 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -297,7 +297,8 @@ protected function initializeContainer() $reload = $this->debug ? $this->needsReload($class, $location) : false; if ($reload || !file_exists($location.'.php')) { - $this->buildContainer($class, $location.'.php'); + $container = $this->buildContainer(); + $this->dumpContainer($container, $class, $location.'.php'); } require_once $location.'.php'; @@ -360,7 +361,7 @@ protected function needsReload($class, $location) return false; } - protected function buildContainer($class, $file) + protected function buildContainer() { $parameterBag = new ParameterBag($this->getKernelParameters()); @@ -377,7 +378,11 @@ protected function buildContainer($class, $file) $container->merge($cont); } $container->freeze(); + return $container; + } + protected function dumpContainer(ContainerBuilder $container, $class, $file) + { foreach (array('cache', 'logs') as $name) { $dir = $container->getParameter(sprintf('kernel.%s_dir', $name)); if (!is_dir($dir)) { @@ -446,9 +451,6 @@ static public function stripComments($source) // replace multiple new lines with a single newline $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output); - // reformat {} "a la python" - $output = preg_replace(array('/\n\s*\{/', '/\n\s*\}/'), array(' {', ' }'), $output); - return $output; } diff --git a/src/Symfony/Component/HttpKernel/bootstrap.php b/src/Symfony/Component/HttpKernel/bootstrap.php index e430c673b26ec..4e38d94f097d3 100644 --- a/src/Symfony/Component/HttpKernel/bootstrap.php +++ b/src/Symfony/Component/HttpKernel/bootstrap.php @@ -5,61 +5,92 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Console\Application; use Symfony\Component\Finder\Finder; -abstract class Bundle extends ContainerAware implements BundleInterface { +abstract class Bundle extends ContainerAware implements BundleInterface +{ protected $name; protected $namespacePrefix; protected $path; protected $reflection; - public function boot() { } - public function shutdown() { } - public function getName() { + public function boot() + { + } + public function shutdown() + { + } + public function getName() + { if (null === $this->name) { - $this->initReflection(); } - return $this->name; } - public function getNamespacePrefix() { + $this->initReflection(); + } + return $this->name; + } + public function getNamespacePrefix() + { if (null === $this->name) { - $this->initReflection(); } - return $this->namespacePrefix; } - public function getPath() { + $this->initReflection(); + } + return $this->namespacePrefix; + } + public function getPath() + { if (null === $this->name) { - $this->initReflection(); } - return $this->path; } - public function getReflection() { + $this->initReflection(); + } + return $this->path; + } + public function getReflection() + { if (null === $this->name) { - $this->initReflection(); } - return $this->reflection; } - public function registerExtensions(ContainerBuilder $container) { + $this->initReflection(); + } + return $this->reflection; + } + public function registerExtensions(ContainerBuilder $container) + { if (!$dir = realpath($this->getPath().'/DependencyInjection')) { - return array(); } + return; + } $finder = new Finder(); $finder->files()->name('*Extension.php')->in($dir); $prefix = $this->namespacePrefix.'\\'.$this->name.'\\DependencyInjection'; foreach ($finder as $file) { - $class = $prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.basename($file, '.php'); - if ('Extension' === substr($class, -9)) { - $container->registerExtension(new $class()); } } } - public function registerCommands(Application $application) { + $class = $prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php'); + $container->registerExtension(new $class()); + } + } + public function registerCommands(Application $application) + { if (!$dir = realpath($this->getPath().'/Command')) { - return; } + return; + } $finder = new Finder(); $finder->files()->name('*Command.php')->in($dir); $prefix = $this->namespacePrefix.'\\'.$this->name.'\\Command'; foreach ($finder as $file) { - $r = new \ReflectionClass($prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.basename($file, '.php')); + $r = new \ReflectionClass($prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php')); if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract()) { - $application->add($r->newInstance()); } } } - protected function initReflection() { + $application->add($r->newInstance()); + } + } + } + protected function initReflection() + { $tmp = dirname(str_replace('\\', '/', get_class($this))); $this->namespacePrefix = str_replace('/', '\\', dirname($tmp)); $this->name = basename($tmp); $this->reflection = new \ReflectionObject($this); - $this->path = dirname($this->reflection->getFilename()); } } + $this->path = str_replace('\\', '/', dirname($this->reflection->getFilename())); + } +} namespace Symfony\Component\HttpKernel\Bundle; -interface BundleInterface { +interface BundleInterface +{ function boot(); - function shutdown(); } + function shutdown(); +} namespace Symfony\Component\HttpKernel\Debug; -class ErrorHandler { +class ErrorHandler +{ protected $levels = array( E_WARNING => 'Warning', E_NOTICE => 'Notice', @@ -70,140 +101,230 @@ class ErrorHandler { E_RECOVERABLE_ERROR => 'Catchable Fatal Error', ); protected $level; - public function __construct($level = null) { - $this->level = null === $level ? error_reporting() : $level; } - public function register() { - set_error_handler(array($this, 'handle')); } - public function handle($level, $message, $file, $line, $context) { + public function __construct($level = null) + { + $this->level = null === $level ? error_reporting() : $level; + } + public function register() + { + set_error_handler(array($this, 'handle')); + } + public function handle($level, $message, $file, $line, $context) + { if (0 === $this->level) { - return false; } + return false; + } if (error_reporting() & $level && $this->level & $level) { - throw new \ErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line)); } - return false; } } + throw new \ErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line)); + } + return false; + } +} namespace Symfony\Component\HttpKernel; -class ClassCollectionLoader { +class ClassCollectionLoader +{ static protected $loaded; - static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false) { + static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false) + { if (isset(self::$loaded[$name])) { - return; } + return; + } self::$loaded[$name] = true; $classes = array_unique($classes); if ($adaptive) { $classes = array_diff($classes, get_declared_classes(), get_declared_interfaces()); - $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5); } + $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5); + } $cache = $cacheDir.'/'.$name.'.php'; $reload = false; if ($autoReload) { $metadata = $cacheDir.'/'.$name.'.meta'; if (!file_exists($metadata) || !file_exists($cache)) { - $reload = true; } else { + $reload = true; + } else { $time = filemtime($cache); $meta = unserialize(file_get_contents($metadata)); if ($meta[1] != $classes) { - $reload = true; } else { + $reload = true; + } else { foreach ($meta[0] as $resource) { if (!file_exists($resource) || filemtime($resource) > $time) { $reload = true; - break; } } } } } + break; + } + } + } + } + } if (!$reload && file_exists($cache)) { require_once $cache; - return; } + return; + } $files = array(); $content = ''; foreach ($classes as $class) { if (!class_exists($class) && !interface_exists($class)) { - throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); } + throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); + } $r = new \ReflectionClass($class); $files[] = $r->getFileName(); - $content .= preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName())); } + $content .= preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName())); + } if (!is_dir(dirname($cache))) { - mkdir(dirname($cache), 0777, true); } + mkdir(dirname($cache), 0777, true); + } self::writeCacheFile($cache, Kernel::stripComments('parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag; $this->services = array(); - $this->set('service_container', $this); } - public function freeze() { + $this->set('service_container', $this); + } + public function freeze() + { $this->parameterBag->resolve(); - $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); } - public function isFrozen() { - return $this->parameterBag instanceof FrozenParameterBag; } - public function getParameterBag() { - return $this->parameterBag; } - public function getParameter($name) { - return $this->parameterBag->get($name); } - public function hasParameter($name) { - return $this->parameterBag->has($name); } - public function setParameter($name, $value) { - $this->parameterBag->set($name, $value); } - public function set($id, $service) { - $this->services[$id] = $service; } - public function has($id) { - return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service'); } - public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) { - $id = (string) $id; + $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + } + public function isFrozen() + { + return $this->parameterBag instanceof FrozenParameterBag; + } + public function getParameterBag() + { + return $this->parameterBag; + } + public function getParameter($name) + { + return $this->parameterBag->get($name); + } + public function hasParameter($name) + { + return $this->parameterBag->has($name); + } + public function setParameter($name, $value) + { + $this->parameterBag->set($name, $value); + } + public function set($id, $service) + { + $this->services[strtolower($id)] = $service; + } + public function has($id) + { + $id = strtolower($id); + return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service'); + } + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + { + static $loading = array(); + $id = strtolower($id); if (isset($this->services[$id])) { - return $this->services[$id]; } + return $this->services[$id]; + } + if (isset($loading[$id])) { + throw new \LogicException(sprintf('Circular reference detected for service "%s" (services currently loading: %s).', $id, implode(', ', array_keys($loading)))); + } if (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) { - return $this->$method(); } + $loading[$id] = true; + $service = $this->$method(); + unset($loading[$id]); + return $service; + } if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { - throw new \InvalidArgumentException(sprintf('The service "%s" does not exist.', $id)); } } - public function getServiceIds() { + throw new \InvalidArgumentException(sprintf('The service "%s" does not exist.', $id)); + } + } + public function getServiceIds() + { $ids = array(); $r = new \ReflectionClass($this); foreach ($r->getMethods() as $method) { if (preg_match('/^get(.+)Service$/', $name = $method->getName(), $match)) { - $ids[] = self::underscore($match[1]); } } - return array_merge($ids, array_keys($this->services)); } - static public function camelize($id) { - return preg_replace(array('/(?:^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\1')", "'_'.strtoupper('\\1')"), $id); } - static public function underscore($id) { - return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.'))); } } + $ids[] = self::underscore($match[1]); + } + } + return array_merge($ids, array_keys($this->services)); + } + static public function camelize($id) + { + return preg_replace(array('/(?:^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\1')", "'_'.strtoupper('\\1')"), $id); + } + static public function underscore($id) + { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.'))); + } +} namespace Symfony\Component\DependencyInjection; -interface ContainerAwareInterface { - function setContainer(ContainerInterface $container = null); } +interface ContainerAwareInterface +{ + function setContainer(ContainerInterface $container = null); +} namespace Symfony\Component\DependencyInjection; -interface ContainerInterface { +interface ContainerInterface +{ const EXCEPTION_ON_INVALID_REFERENCE = 1; const NULL_ON_INVALID_REFERENCE = 2; const IGNORE_ON_INVALID_REFERENCE = 3; function set($id, $service); function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); - function has($id); } + function has($id); +} namespace Symfony\Component\DependencyInjection\ParameterBag; -class FrozenParameterBag extends ParameterBag { - public function __construct(array $parameters = array()) { +class FrozenParameterBag extends ParameterBag +{ + public function __construct(array $parameters = array()) + { foreach ($parameters as $key => $value) { - $this->parameters[strtolower($key)] = $value; } } - public function clear() { - throw new \LogicException('Impossible to call clear() on a frozen ParameterBag.'); } - public function add(array $parameters) { - throw new \LogicException('Impossible to call add() on a frozen ParameterBag.'); } - public function set($name, $value) { - throw new \LogicException('Impossible to call set() on a frozen ParameterBag.'); } } + $this->parameters[strtolower($key)] = $value; + } + } + public function clear() + { + throw new \LogicException('Impossible to call clear() on a frozen ParameterBag.'); + } + public function add(array $parameters) + { + throw new \LogicException('Impossible to call add() on a frozen ParameterBag.'); + } + public function set($name, $value) + { + throw new \LogicException('Impossible to call set() on a frozen ParameterBag.'); + } +} namespace Symfony\Component\DependencyInjection\ParameterBag; -interface ParameterBagInterface { +interface ParameterBagInterface +{ function clear(); function add(array $parameters); function all(); function get($name); function set($name, $value); - function has($name); } + function has($name); +} namespace Symfony\Component\DependencyInjection; -interface TaggedContainerInterface extends ContainerInterface { - function findTaggedServiceIds($name); } +interface TaggedContainerInterface extends ContainerInterface +{ + function findTaggedServiceIds($name); +} diff --git a/src/Symfony/Component/Translation/Loader/CsvFileLoader.php b/src/Symfony/Component/Translation/Loader/CsvFileLoader.php new file mode 100644 index 0000000000000..56d7e6033ce08 --- /dev/null +++ b/src/Symfony/Component/Translation/Loader/CsvFileLoader.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * CsvFileLoader loads translations from CSV files. + * + * @author Saša Stamenković + */ +class CsvFileLoader extends ArrayLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + $messages = array(); + $file = @fopen($resource, 'rb'); + if (!$file) { + throw new \InvalidArgumentException(sprintf('Error opening file "%s".', $resource)); + } + + while(($data = fgetcsv($file, 0, ';')) !== false) { + if (substr($data[0], 0, 1) === '#') { + continue; + } + + if (!isset($data[1])) { + continue; + } + + if (count($data) == 2) { + $messages[$data[0]] = $data[1]; + } else { + continue; + } + } + + $catalogue = parent::load($messages, $locale, $domain); + $catalogue->addResource(new FileResource($resource)); + + return $catalogue; + } +} diff --git a/src/Symfony/Component/Translation/MessageSelector.php b/src/Symfony/Component/Translation/MessageSelector.php index d42da7a3b7867..d227882b653d6 100644 --- a/src/Symfony/Component/Translation/MessageSelector.php +++ b/src/Symfony/Component/Translation/MessageSelector.php @@ -18,6 +18,30 @@ */ class MessageSelector { + /** + * Given a message with different plural translations separated by a + * pipe (|), this method returns the correct portion of the message based + * on the given number, locale and the pluralization rules in the message + * itself. + * + * The message supports two different types of pluralization rules: + * + * interval: {0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples + * indexed: There is one apple|There is %count% apples + * + * The indexed solution can also contain labels (e.g. one: There is one apple). + * This is purely for making the translations more clear - it does not + * affect the functionality. + * + * The two methods can also be mixed: + * {0} There is no apples|one: There is one apple|more: There is %count% apples + * + * @throws InvalidArgumentException + * @param string $message The message being translated + * @param integer $number The number of items represented for the message + * @param string $locale The locale to use for choosing + * @return string + */ public function choose($message, $number, $locale) { $parts = explode('|', $message); diff --git a/tests/Symfony/Tests/Component/Console/ApplicationTest.php b/tests/Symfony/Tests/Component/Console/ApplicationTest.php index 73fb8d248798b..33d8614c6e782 100644 --- a/tests/Symfony/Tests/Component/Console/ApplicationTest.php +++ b/tests/Symfony/Tests/Component/Console/ApplicationTest.php @@ -26,6 +26,7 @@ static public function setUpBeforeClass() require_once self::$fixturesPath.'/FooCommand.php'; require_once self::$fixturesPath.'/Foo1Command.php'; require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; } protected function normalize($text) @@ -245,6 +246,12 @@ public function testRenderException() $tester->run(array('command' => 'list', '--foo' => true)); $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $this->normalize($tester->getDisplay()), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar')); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $this->normalize($tester->getDisplay()), '->renderException() renders a pretty exceptions with previous exceptions'); + } public function testRun() diff --git a/tests/Symfony/Tests/Component/Console/Fixtures/Foo3Command.php b/tests/Symfony/Tests/Component/Console/Fixtures/Foo3Command.php new file mode 100644 index 0000000000000..b0d991646490a --- /dev/null +++ b/tests/Symfony/Tests/Component/Console/Fixtures/Foo3Command.php @@ -0,0 +1,25 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + throw new \Exception("First exception"); + } catch (\Exception $e) { + throw new \Exception("Second exception", 0, $e); + } + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/Console/Fixtures/application_renderexception3.txt b/tests/Symfony/Tests/Component/Console/Fixtures/application_renderexception3.txt new file mode 100644 index 0000000000000..3f623116f05cd --- /dev/null +++ b/tests/Symfony/Tests/Component/Console/Fixtures/application_renderexception3.txt @@ -0,0 +1,19 @@ + + + + [Exception] + Second exception + + + + + + + [Exception] + First exception + + + +foo3:bar [-h|--help] [-q|--quiet] [-v|--verbose] [-V|--version] [-a|--ansi] [-n|--no-interaction] command + + diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPassTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPassTest.php new file mode 100644 index 0000000000000..84bca1687ecad --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPassTest.php @@ -0,0 +1,86 @@ +register('a') + ->addArgument($ref1 = new Reference('b')) + ; + + $b = $container + ->register('b') + ->addMethodCall('setA', array($ref2 = new Reference('a'))) + ; + + $c = $container + ->register('c') + ->addArgument($ref3 = new Reference('a')) + ->addArgument($ref4 = new Reference('b')) + ; + + $graph = $this->process($container); + + $this->assertEquals(2, count($edges = $graph->getNode('b')->getInEdges())); + $this->assertSame($ref1, $edges[0]->getValue()); + $this->assertSame($ref4, $edges[1]->getValue()); + } + + public function testProcessDetectsReferencesFromInlinedDefinitions() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $container + ->register('b') + ->addArgument(new Definition(null, array($ref = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertEquals(1, count($refs = $graph->getNode('a')->getInEdges())); + $this->assertSame($ref, $refs[0]->getValue()); + } + + public function testProcessDoesNotSaveDuplicateReferences() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + $container + ->register('b') + ->addArgument(new Definition(null, array($ref1 = new Reference('a')))) + ->addArgument(new Definition(null, array($ref2 = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertEquals(2, count($graph->getNode('a')->getInEdges())); + } + + protected function process(ContainerBuilder $container) + { + $pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass())); + $pass->setCompiler($compiler = new Compiler()); + $pass->process($container); + + return $compiler->getServiceReferenceGraph(); + } +} diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPassTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPassTest.php index 0dbf97606dda0..b6489f6f00d3a 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPassTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPassTest.php @@ -2,6 +2,14 @@ namespace Symfony\Tests\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; + +use Symfony\Component\DependencyInjection\Compiler\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; + use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -70,14 +78,37 @@ public function testProcessDoesInlineNonSharedService() $this->process($container); $arguments = $container->getDefinition('service')->getArguments(); - $this->assertSame($container->getDefinition('foo'), $arguments[0]); + $this->assertEquals($container->getDefinition('foo'), $arguments[0]); + $this->assertNotSame($container->getDefinition('foo'), $arguments[0]); $this->assertSame($ref, $arguments[1]); - $this->assertSame($container->getDefinition('bar'), $arguments[2]); + $this->assertEquals($container->getDefinition('bar'), $arguments[2]); + $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); + } + + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() + { + $container = new ContainerBuilder(); + + $a = $container->register('a')->setPublic(false); + $b = $container + ->register('b') + ->addArgument(new Reference('a')) + ->addArgument(new Definition(null, array(new Reference('a')))) + ; + + $this->process($container); + + $arguments = $b->getArguments(); + $this->assertSame($a, $arguments[0]); + + $inlinedArguments = $arguments[1]->getArguments(); + $this->assertSame($a, $inlinedArguments[0]); } protected function process(ContainerBuilder $container) { - $pass = new InlineServiceDefinitionsPass(); - $pass->process($container); + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); + $repeatedPass->setCompiler(new Compiler()); + $repeatedPass->process($container); } } \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/IntegrationTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/IntegrationTest.php new file mode 100644 index 0000000000000..73d4200282103 --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/IntegrationTest.php @@ -0,0 +1,103 @@ + C + * B -> C + */ + public function testProcessRemovesAndInlinesRecursively() + { + $container = new ContainerBuilder(); + + $a = $container + ->register('a') + ->addArgument(new Reference('c')) + ; + + $b = $container + ->register('b') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $c = $container + ->register('c') + ->setPublic(false) + ; + + $container->freeze(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesReferencesToAliases() + { + $container = new ContainerBuilder(); + + $a = $container + ->register('a') + ->addArgument(new Reference('b')) + ; + + $container->setAlias('b', new Alias('c', false)); + + $c = $container + ->register('c') + ->setPublic(false) + ; + + $container->freeze(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasAlias('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDefinition() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ->addArgument(new Reference('b')) + ->addMethodCall('setC', array(new Reference('c'))) + ; + + $container + ->register('b') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $container + ->register('c') + ->setPublic(false) + ; + + $container->freeze(); + + $this->assertTrue($container->hasDefinition('a')); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPassTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPassTest.php index 70faa799448f7..c51a3387c9442 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPassTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPassTest.php @@ -2,6 +2,9 @@ namespace Symfony\Tests\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -71,7 +74,8 @@ public function testProcessWorksWithInlinedDefinitions() protected function process(ContainerBuilder $container) { - $pass = new RemoveUnusedDefinitionsPass(); - $pass->process($container); + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass())); + $repeatedPass->setCompiler(new Compiler()); + $repeatedPass->process($container); } } \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/Translation/Loader/CsvFileLoaderTest.php b/tests/Symfony/Tests/Component/Translation/Loader/CsvFileLoaderTest.php new file mode 100644 index 0000000000000..fd7c6aa1a9485 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/Loader/CsvFileLoaderTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Translation\Loader; + +use Symfony\Component\Translation\Loader\CsvFileLoader; +use Symfony\Component\Translation\Resource\FileResource; + +class CsvFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testLoad() + { + $loader = new CsvFileLoader(); + $resource = __DIR__.'/../fixtures/resources.csv'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new CsvFileLoader(); + $resource = __DIR__.'/../fixtures/empty.csv'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } +} diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/empty.csv b/tests/Symfony/Tests/Component/Translation/fixtures/empty.csv new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/Symfony/Tests/Component/Translation/fixtures/resources.csv b/tests/Symfony/Tests/Component/Translation/fixtures/resources.csv new file mode 100644 index 0000000000000..b6467f0653de3 --- /dev/null +++ b/tests/Symfony/Tests/Component/Translation/fixtures/resources.csv @@ -0,0 +1 @@ +"foo"; "bar" \ No newline at end of file