{{- form_errors(form) -}}
diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
new file mode 100644
index 0000000000000..8d5bd991368e2
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
@@ -0,0 +1,114 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Command;
+
+use Symfony\Bridge\Twig\Command\LintCommand;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Tester\CommandTester;
+
+/**
+ * @covers \Symfony\Bridge\Twig\Command\LintCommand
+ */
+class LintCommandTest extends \PHPUnit_Framework_TestCase
+{
+ private $files;
+
+ public function testLintCorrectFile()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo }}');
+
+ $ret = $tester->execute(array('filename' => $filename), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE));
+
+ $this->assertEquals(0, $ret, 'Returns 0 in case of success');
+ $this->assertRegExp('/^OK in /', $tester->getDisplay());
+ }
+
+ public function testLintIncorrectFile()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo');
+
+ $ret = $tester->execute(array('filename' => $filename));
+
+ $this->assertEquals(1, $ret, 'Returns 1 in case of error');
+ $this->assertRegExp('/^KO in /', $tester->getDisplay());
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testLintFileNotReadable()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('');
+ unlink($filename);
+
+ $ret = $tester->execute(array('filename' => $filename));
+ }
+
+ public function testLintFileCompileTimeException()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile("{{ 2|number_format(2, decimal_point='.', ',') }}");
+
+ $ret = $tester->execute(array('filename' => $filename));
+
+ $this->assertEquals(1, $ret, 'Returns 1 in case of error');
+ $this->assertRegExp('/^KO in /', $tester->getDisplay());
+ }
+
+ /**
+ * @return CommandTester
+ */
+ private function createCommandTester()
+ {
+ $twig = new \Twig_Environment(new \Twig_Loader_Filesystem());
+
+ $command = new LintCommand();
+ $command->setTwigEnvironment($twig);
+
+ $application = new Application();
+ $application->add($command);
+ $command = $application->find('twig:lint');
+
+ return new CommandTester($command);
+ }
+
+ /**
+ * @return string Path to the new file
+ */
+ private function createFile($content)
+ {
+ $filename = tempnam(sys_get_temp_dir(), 'sf-');
+ file_put_contents($filename, $content);
+
+ $this->files[] = $filename;
+
+ return $filename;
+ }
+
+ public function setUp()
+ {
+ $this->files = array();
+ }
+
+ public function tearDown()
+ {
+ foreach ($this->files as $file) {
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php
new file mode 100644
index 0000000000000..749133c65c5a4
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\ExpressionExtension;
+use Symfony\Component\ExpressionLanguage\Expression;
+
+class ExpressionExtensionTest extends \PHPUnit_Framework_TestCase
+{
+ protected $helper;
+
+ public function testExpressionCreation()
+ {
+ $template = "{{ expression('1 == 1') }}";
+ $twig = new \Twig_Environment(new \Twig_Loader_String(), array('debug' => true, 'cache' => false, 'autoescape' => true, 'optimizations' => 0));
+ $twig->addExtension(new ExpressionExtension());
+
+ $output = $twig->render($template);
+ $this->assertEquals('1 == 1', $output);
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php b/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php
index 36c61cd6cca08..5a63537a2fa57 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php
@@ -11,20 +11,13 @@
namespace Symfony\Bridge\Twig\Tests\Extension\Fixtures;
-// Preventing autoloader throwing E_FATAL when Twig is now available
-if (!class_exists('Twig_Environment')) {
- class StubFilesystemLoader
+class StubFilesystemLoader extends \Twig_Loader_Filesystem
+{
+ protected function findTemplate($name)
{
- }
-} else {
- class StubFilesystemLoader extends \Twig_Loader_Filesystem
- {
- protected function findTemplate($name)
- {
- // strip away bundle name
- $parts = explode(':', $name);
+ // strip away bundle name
+ $parts = explode(':', $name);
- return parent::findTemplate(end($parts));
- }
+ return parent::findTemplate(end($parts));
}
}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
index f0e2cb3f884fd..4b347fe2efe0e 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php
@@ -30,22 +30,6 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
protected function setUp()
{
- if (!class_exists('Symfony\Component\Locale\Locale')) {
- $this->markTestSkipped('The "Locale" component is not available');
- }
-
- if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
- $this->markTestSkipped('The "EventDispatcher" component is not available');
- }
-
- if (!class_exists('Symfony\Component\Form\Form')) {
- $this->markTestSkipped('The "Form" component is not available');
- }
-
- if (!class_exists('Twig_Environment')) {
- $this->markTestSkipped('Twig is not available.');
- }
-
parent::setUp();
$rendererEngine = new TwigRendererEngine(array(
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
index 99a782178a9a4..b5a08e47a57dd 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php
@@ -29,22 +29,6 @@ class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
protected function setUp()
{
- if (!class_exists('Symfony\Component\Locale\Locale')) {
- $this->markTestSkipped('The "Locale" component is not available');
- }
-
- if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
- $this->markTestSkipped('The "EventDispatcher" component is not available');
- }
-
- if (!class_exists('Symfony\Component\Form\Form')) {
- $this->markTestSkipped('The "Form" component is not available');
- }
-
- if (!class_exists('Twig_Environment')) {
- $this->markTestSkipped('Twig is not available.');
- }
-
parent::setUp();
$rendererEngine = new TwigRendererEngine(array(
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php
index 077927cd6d5fc..48bebdc13f8f5 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php
@@ -12,37 +12,41 @@
namespace Symfony\Bridge\Twig\Tests\Extension;
use Symfony\Bridge\Twig\Extension\HttpKernelExtension;
-use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
-class HttpKernelExtensionTest extends TestCase
+class HttpKernelExtensionTest extends \PHPUnit_Framework_TestCase
{
- protected function setUp()
- {
- parent::setUp();
-
- if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) {
- $this->markTestSkipped('The "HttpKernel" component is not available');
- }
-
- if (!class_exists('Twig_Environment')) {
- $this->markTestSkipped('Twig is not available.');
- }
- }
-
/**
* @expectedException \Twig_Error_Runtime
*/
public function testFragmentWithError()
{
- $kernel = $this->getFragmentHandler($this->throwException(new \Exception('foo')));
+ $renderer = $this->getFragmentHandler($this->throwException(new \Exception('foo')));
- $loader = new \Twig_Loader_Array(array('index' => '{{ fragment("foo") }}'));
- $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
- $twig->addExtension(new HttpKernelExtension($kernel));
+ $this->renderTemplate($renderer);
+ }
+
+ public function testRenderFragment()
+ {
+ $renderer = $this->getFragmentHandler($this->returnValue(new Response('html')));
+
+ $response = $this->renderTemplate($renderer);
+
+ $this->assertEquals('html', $response);
+ }
- $this->renderTemplate($kernel);
+ public function testUnknownFragmentRenderer()
+ {
+ $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack')
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+ $renderer = new FragmentHandler(array(), false, $context);
+
+ $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.');
+ $renderer->render('/foo');
}
protected function getFragmentHandler($return)
@@ -51,8 +55,14 @@ protected function getFragmentHandler($return)
$strategy->expects($this->once())->method('getName')->will($this->returnValue('inline'));
$strategy->expects($this->once())->method('render')->will($return);
- $renderer = new FragmentHandler(array($strategy));
- $renderer->setRequest(Request::create('/'));
+ $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack')
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+
+ $context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/')));
+
+ $renderer = new FragmentHandler(array($strategy), false, $context);
return $renderer;
}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php
index 3c5d762ca008e..cd0bbdf0ec7d4 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php
@@ -12,19 +12,9 @@
namespace Symfony\Bridge\Twig\Tests\Extension;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class RoutingExtensionTest extends TestCase
+class RoutingExtensionTest extends \PHPUnit_Framework_TestCase
{
- protected function setUp()
- {
- parent::setUp();
-
- if (!class_exists('Symfony\Component\Routing\Route')) {
- $this->markTestSkipped('The "Routing" component is not available');
- }
- }
-
/**
* @dataProvider getEscapingTemplates
*/
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php
new file mode 100644
index 0000000000000..bac855390f440
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\StopwatchExtension;
+
+class StopwatchExtensionTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \Twig_Error_Syntax
+ */
+ public function testFailIfStoppingWrongEvent()
+ {
+ $this->testTiming('{% stopwatch "foo" %}{% endstopwatch "bar" %}', array());
+ }
+
+ /**
+ * @dataProvider getTimingTemplates
+ */
+ public function testTiming($template, $events)
+ {
+ $twig = new \Twig_Environment(new \Twig_Loader_String(), array('debug' => true, 'cache' => false, 'autoescape' => true, 'optimizations' => 0));
+ $twig->addExtension(new StopwatchExtension($this->getStopwatch($events)));
+
+ try {
+ $nodes = $twig->render($template);
+ } catch (\Twig_Error_Runtime $e) {
+ throw $e->getPrevious();
+ }
+ }
+
+ public function getTimingTemplates()
+ {
+ return array(
+ array('{% stopwatch "foo" %}something{% endstopwatch %}', 'foo'),
+ array('{% stopwatch "foo" %}symfony2 is fun{% endstopwatch %}{% stopwatch "bar" %}something{% endstopwatch %}', array('foo', 'bar')),
+ array('{% set foo = "foo" %}{% stopwatch foo %}something{% endstopwatch %}', 'foo'),
+ array('{% set foo = "foo" %}{% stopwatch foo %}something {% set foo = "bar" %}{% endstopwatch %}', 'foo'),
+ array('{% stopwatch "foo.bar" %}something{% endstopwatch %}', 'foo.bar'),
+ array('{% stopwatch "foo" %}something{% endstopwatch %}{% stopwatch "foo" %}something else{% endstopwatch %}', array('foo', 'foo')),
+ );
+ }
+
+ protected function getStopwatch($events = array())
+ {
+ $events = is_array($events) ? $events : array($events);
+ $stopwatch = $this->getMock('Symfony\Component\Stopwatch\Stopwatch');
+
+ $i = -1;
+ foreach ($events as $eventName) {
+ $stopwatch->expects($this->at(++$i))
+ ->method('start')
+ ->with($this->equalTo($eventName), 'template')
+ ;
+ $stopwatch->expects($this->at(++$i))
+ ->method('stop')
+ ->with($this->equalTo($eventName))
+ ;
+ }
+
+ return $stopwatch;
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
index 4e03e8a0043ac..777000efd174f 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
@@ -15,23 +15,9 @@
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class TranslationExtensionTest extends TestCase
+class TranslationExtensionTest extends \PHPUnit_Framework_TestCase
{
- protected function setUp()
- {
- parent::setUp();
-
- if (!class_exists('Symfony\Component\Translation\Translator')) {
- $this->markTestSkipped('The "Translation" component is not available');
- }
-
- if (!class_exists('Twig_Environment')) {
- $this->markTestSkipped('Twig is not available.');
- }
- }
-
public function testEscaping()
{
$output = $this->getTemplate('{% trans %}Percent: %value%%% (%msg%){% endtrans %}')->render(array('value' => 12, 'msg' => 'approx.'));
diff --git a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php
index a4a4698ed8a50..5f8ae112118fb 100644
--- a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php
@@ -11,10 +11,9 @@
namespace Symfony\Bridge\Twig\Tests\Node;
-use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Bridge\Twig\Node\FormThemeNode;
-class FormThemeTest extends TestCase
+class FormThemeTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
diff --git a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php
index 38be27cfbed41..60ef9edfb9751 100644
--- a/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php
@@ -11,10 +11,9 @@
namespace Symfony\Bridge\Twig\Tests\Node;
-use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode;
-class SearchAndRenderBlockNodeTest extends TestCase
+class SearchAndRenderBlockNodeTest extends \PHPUnit_Framework_TestCase
{
public function testCompileWidget()
{
diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php
index bcae5919b597c..aa9d204e5606f 100644
--- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php
@@ -12,9 +12,8 @@
namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
use Symfony\Bridge\Twig\NodeVisitor\Scope;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class ScopeTest extends TestCase
+class ScopeTest extends \PHPUnit_Framework_TestCase
{
public function testScopeInitiation()
{
diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php
index 24a6215e6772c..65e0dd80184cf 100644
--- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php
@@ -13,9 +13,8 @@
use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class TranslationDefaultDomainNodeVisitorTest extends TestCase
+class TranslationDefaultDomainNodeVisitorTest extends \PHPUnit_Framework_TestCase
{
private static $message = 'message';
private static $domain = 'domain';
diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php
index 4e3ee6fdfa37f..0f3ec40091d1f 100644
--- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php
@@ -12,9 +12,8 @@
namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class TranslationNodeVisitorTest extends TestCase
+class TranslationNodeVisitorTest extends \PHPUnit_Framework_TestCase
{
/** @dataProvider getMessagesExtractionTestData */
public function testMessagesExtraction(\Twig_Node $node, array $expectedMessages)
diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php
index c844239e533a8..69c46fd220f1d 100644
--- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php
@@ -11,11 +11,10 @@
namespace Symfony\Bridge\Twig\Tests\TokenParser;
-use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
use Symfony\Bridge\Twig\Node\FormThemeNode;
-class FormThemeTokenParserTest extends TestCase
+class FormThemeTokenParserTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getTestsForFormTheme
diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php
index d5614e7ec7c64..a483ab857aa53 100644
--- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php
@@ -14,17 +14,9 @@
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Bridge\Twig\Translation\TwigExtractor;
use Symfony\Component\Translation\MessageCatalogue;
-use Symfony\Bridge\Twig\Tests\TestCase;
-class TwigExtractorTest extends TestCase
+class TwigExtractorTest extends \PHPUnit_Framework_TestCase
{
- protected function setUp()
- {
- if (!class_exists('Symfony\Component\Translation\Translator')) {
- $this->markTestSkipped('The "Translation" component is not available');
- }
- }
-
/**
* @dataProvider getExtractData
*/
diff --git a/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php b/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php
index 1e6a3c4933675..e7047c354d080 100644
--- a/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php
@@ -12,8 +12,9 @@
namespace Symfony\Bridge\Twig\Tests;
use Symfony\Bridge\Twig\TwigEngine;
+use Symfony\Component\Templating\TemplateReference;
-class TwigEngineTest extends TestCase
+class TwigEngineTest extends \PHPUnit_Framework_TestCase
{
public function testExistsWithTemplateInstances()
{
@@ -27,6 +28,7 @@ public function testExistsWithNonExistentTemplates()
$engine = $this->getTwig();
$this->assertFalse($engine->exists('foobar'));
+ $this->assertFalse($engine->exists(new TemplateReference('foorbar')));
}
public function testExistsWithTemplateWithSyntaxErrors()
@@ -34,6 +36,7 @@ public function testExistsWithTemplateWithSyntaxErrors()
$engine = $this->getTwig();
$this->assertTrue($engine->exists('error'));
+ $this->assertTrue($engine->exists(new TemplateReference('error')));
}
public function testExists()
@@ -41,6 +44,25 @@ public function testExists()
$engine = $this->getTwig();
$this->assertTrue($engine->exists('index'));
+ $this->assertTrue($engine->exists(new TemplateReference('index')));
+ }
+
+ public function testRender()
+ {
+ $engine = $this->getTwig();
+
+ $this->assertSame('foo', $engine->render('index'));
+ $this->assertSame('foo', $engine->render(new TemplateReference('index')));
+ }
+
+ /**
+ * @expectedException \Twig_Error_Syntax
+ */
+ public function testRenderWithError()
+ {
+ $engine = $this->getTwig();
+
+ $engine->render(new TemplateReference('error'));
}
protected function getTwig()
diff --git a/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php
new file mode 100644
index 0000000000000..2983e4cb6b03b
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\TokenParser;
+
+use Symfony\Bridge\Twig\Node\StopwatchNode;
+
+/**
+ * Token Parser for the stopwatch tag.
+ *
+ * @author Wouter J
+ */
+class StopwatchTokenParser extends \Twig_TokenParser
+{
+ protected $stopwatchIsAvailable;
+
+ public function __construct($stopwatchIsAvailable)
+ {
+ $this->stopwatchIsAvailable = $stopwatchIsAvailable;
+ }
+
+ public function parse(\Twig_Token $token)
+ {
+ $lineno = $token->getLine();
+ $stream = $this->parser->getStream();
+
+ // {% stopwatch 'bar' %}
+ $name = $this->parser->getExpressionParser()->parseExpression();
+
+ $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+ // {% endstopwatch %}
+ $body = $this->parser->subparse(array($this, 'decideStopwatchEnd'), true);
+ $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+ if ($this->stopwatchIsAvailable) {
+ return new StopwatchNode($name, $body, new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $lineno, $this->getTag());
+ }
+
+ return $body;
+ }
+
+ public function decideStopwatchEnd(\Twig_Token $token)
+ {
+ return $token->test('endstopwatch');
+ }
+
+ public function getTag()
+ {
+ return 'stopwatch';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php
index fe36436c7189a..c43a0bded0a56 100644
--- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php
+++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php
@@ -56,7 +56,7 @@ public function extract($directory, MessageCatalogue $catalogue)
{
// load any existing translation files
$finder = new Finder();
- $files = $finder->files()->name('*.twig')->in($directory);
+ $files = $finder->files()->name('*.twig')->sortByName()->in($directory);
foreach ($files as $file) {
try {
$this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
diff --git a/src/Symfony/Bridge/Twig/TwigEngine.php b/src/Symfony/Bridge/Twig/TwigEngine.php
index 9483a9d05ad17..3e3257e7fa0f5 100644
--- a/src/Symfony/Bridge/Twig/TwigEngine.php
+++ b/src/Symfony/Bridge/Twig/TwigEngine.php
@@ -14,6 +14,7 @@
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Templating\StreamingEngineInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
+use Symfony\Component\Templating\TemplateReferenceInterface;
/**
* This engine knows how to render Twig templates.
@@ -38,15 +39,11 @@ public function __construct(\Twig_Environment $environment, TemplateNameParserIn
}
/**
- * Renders a template.
+ * {@inheritdoc}
*
- * @param mixed $name A template name
- * @param array $parameters An array of parameters to pass to the template
+ * It also supports \Twig_Template as name parameter.
*
- * @return string The evaluated template as a string
- *
- * @throws \InvalidArgumentException if the template does not exist
- * @throws \RuntimeException if the template cannot be rendered
+ * @throws \Twig_Error if something went wrong like a thrown exception while rendering the template
*/
public function render($name, array $parameters = array())
{
@@ -54,12 +51,11 @@ public function render($name, array $parameters = array())
}
/**
- * Streams a template.
+ * {@inheritdoc}
*
- * @param mixed $name A template name or a TemplateReferenceInterface instance
- * @param array $parameters An array of parameters to pass to the template
+ * It also supports \Twig_Template as name parameter.
*
- * @throws \RuntimeException if the template cannot be rendered
+ * @throws \Twig_Error if something went wrong like a thrown exception while rendering the template
*/
public function stream($name, array $parameters = array())
{
@@ -67,11 +63,9 @@ public function stream($name, array $parameters = array())
}
/**
- * Returns true if the template exists.
+ * {@inheritdoc}
*
- * @param mixed $name A template name
- *
- * @return bool true if the template exists, false otherwise
+ * It also supports \Twig_Template as name parameter.
*/
public function exists($name)
{
@@ -82,11 +76,13 @@ public function exists($name)
$loader = $this->environment->getLoader();
if ($loader instanceof \Twig_ExistsLoaderInterface) {
- return $loader->exists($name);
+ return $loader->exists((string) $name);
}
try {
- $loader->getSource($name);
+ // cast possible TemplateReferenceInterface to string because the
+ // EngineInterface supports them but Twig_LoaderInterface does not
+ $loader->getSource((string) $name);
} catch (\Twig_Error_Loader $e) {
return false;
}
@@ -95,11 +91,9 @@ public function exists($name)
}
/**
- * Returns true if this class is able to render the given template.
- *
- * @param string $name A template name
+ * {@inheritdoc}
*
- * @return bool True if this class supports the given resource, false otherwise
+ * It also supports \Twig_Template as name parameter.
*/
public function supports($name)
{
@@ -115,7 +109,8 @@ public function supports($name)
/**
* Loads the given template.
*
- * @param mixed $name A template name or an instance of Twig_Template
+ * @param string|TemplateReferenceInterface|\Twig_Template $name A template name or an instance of
+ * TemplateReferenceInterface or \Twig_Template
*
* @return \Twig_TemplateInterface A \Twig_TemplateInterface instance
*
@@ -128,7 +123,7 @@ protected function load($name)
}
try {
- return $this->environment->loadTemplate($name);
+ return $this->environment->loadTemplate((string) $name);
} catch (\Twig_Error_Loader $e) {
throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index e4314931b536f..fce7130327646 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -17,28 +17,34 @@
],
"require": {
"php": ">=5.3.3",
- "twig/twig": "~1.12,>=1.12.3"
+ "symfony/security-csrf": "~2.4",
+ "twig/twig": "~1.13,>=1.13.1"
},
"require-dev": {
"symfony/finder": "~2.3",
- "symfony/form": "~2.3,>=2.3.5",
+ "symfony/form": "2.5.*,>=2.5.2",
"symfony/http-kernel": "~2.3",
"symfony/locale": "~2.0,>=2.0.5",
"symfony/routing": "~2.2",
"symfony/templating": "~2.1",
"symfony/translation": "~2.2",
"symfony/yaml": "~2.0,>=2.0.5",
- "symfony/security": "~2.0,>=2.0.5"
+ "symfony/security": "~2.4",
+ "symfony/stopwatch": "~2.2",
+ "symfony/console": "~2.4",
+ "symfony/expression-language": "~2.4"
},
"suggest": {
"symfony/finder": "",
- "symfony/form": "",
- "symfony/http-kernel": "",
- "symfony/routing": "",
- "symfony/templating": "",
- "symfony/translation": "",
- "symfony/yaml": "",
- "symfony/security": ""
+ "symfony/form": "For using the FormExtension",
+ "symfony/http-kernel": "For using the HttpKernelExtension",
+ "symfony/routing": "For using the RoutingExtension",
+ "symfony/templating": "For using the TwigEngine",
+ "symfony/translation": "For using the TranslationExtension",
+ "symfony/yaml": "For using the YamlExtension",
+ "symfony/security": "For using the SecurityExtension",
+ "symfony/stopwatch": "For using the StopwatchExtension",
+ "symfony/expression-language": "For using the ExpressionExtension"
},
"autoload": {
"psr-0": { "Symfony\\Bridge\\Twig\\": "" }
@@ -47,7 +53,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "2.3-dev"
+ "dev-master": "2.5-dev"
}
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
index 11ab65cff4831..25247608e59ea 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
@@ -1,6 +1,23 @@
CHANGELOG
=========
+2.5.0
+-----
+
+ * Added `translation:debug` command
+ * Added `--no-backup` option to `translation:update` command
+ * Added `config:debug` command
+ * Added `yaml:lint` command
+ * Deprecated the `RouterApacheDumperCommand` which will be removed in Symfony 3.0.
+
+2.4.0
+-----
+
+ * allowed multiple IP addresses in profiler matcher settings
+ * added stopwatch helper to time templates with the WebProfilerBundle
+ * added service definition for "security.secure_random" service
+ * added service definitions for the new Security CSRF sub-component
+
2.3.0
-----
diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php
index 7c441a8748a4e..07f81f5878ea1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php
+++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php
@@ -55,7 +55,7 @@ public function findAllTemplates()
$templates = array();
- foreach ($this->kernel->getBundles() as $name => $bundle) {
+ foreach ($this->kernel->getBundles() as $bundle) {
$templates = array_merge($templates, $this->findTemplatesInBundle($bundle));
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php
new file mode 100644
index 0000000000000..21b23c9aa4069
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\Extension\Extension;
+
+/**
+ * A console command for dumping available configuration reference.
+ *
+ * @author Kevin Bond
+ * @author Wouter J
+ * @author Grégoire Pineau
+ */
+abstract class AbstractConfigCommand extends ContainerDebugCommand
+{
+ protected function listBundles(OutputInterface $output)
+ {
+ $output->writeln('Available registered bundles with their extension alias if available:');
+
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(array('Bundle name', 'Extension alias'));
+ foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
+ $extension = $bundle->getContainerExtension();
+ $table->addRow(array($bundle->getName(), $extension ? $extension->getAlias() : ''));
+ }
+
+ $table->render($output);
+ }
+
+ protected function findExtension($name)
+ {
+ $extension = null;
+
+ $bundles = $this->getContainer()->get('kernel')->getBundles();
+
+ if (preg_match('/Bundle$/', $name)) {
+ // input is bundle name
+
+ if (isset($bundles[$name])) {
+ $extension = $bundles[$name]->getContainerExtension();
+ }
+
+ if (!$extension) {
+ throw new \LogicException(sprintf('No extensions with configuration available for "%s"', $name));
+ }
+ } else {
+ foreach ($bundles as $bundle) {
+ $extension = $bundle->getContainerExtension();
+
+ if ($extension && $name === $extension->getAlias()) {
+ break;
+ }
+
+ $extension = null;
+ }
+
+ if (!$extension) {
+ throw new \LogicException(sprintf('No extension with alias "%s" is enabled', $name));
+ }
+ }
+
+ return $extension;
+ }
+
+ public function validateConfiguration(Extension $extension, $configuration)
+ {
+ if (!$configuration) {
+ throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup', $extension->getAlias()));
+ }
+
+ if (!$configuration instanceof ConfigurationInterface) {
+ throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable', get_class($configuration)));
+ }
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
index f60867d771e1d..d482fe1669ac4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
@@ -83,7 +83,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$bundlesDir = $targetArg.'/bundles/';
$filesystem->mkdir($bundlesDir, 0777);
- $output->writeln(sprintf("Installing assets using the %s option", $input->getOption('symlink') ? 'symlink' : 'hard copy'));
+ $output->writeln(sprintf('Installing assets as %s', $input->getOption('symlink') ? 'symlinks' : 'hard copies'));
foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
if (is_dir($originDir = $bundle->getPath().'/Resources/public')) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
index 1fea1b91a4e72..6d4f04bce0bd9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
@@ -43,6 +43,7 @@ protected function configure()
php %command.full_name% --env=dev
php %command.full_name% --env=prod --no-debug
+
EOF
)
;
@@ -77,9 +78,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
$warmupDir = substr($realCacheDir, 0, -1).'_';
if ($filesystem->exists($warmupDir)) {
+ if ($output->isVerbose()) {
+ $output->writeln(' Clearing outdated warmup directory');
+ }
$filesystem->remove($warmupDir);
}
+ if ($output->isVerbose()) {
+ $output->writeln(' Warming up cache');
+ }
$this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers'));
$filesystem->rename($realCacheDir, $oldCacheDir);
@@ -89,7 +96,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
$filesystem->rename($warmupDir, $realCacheDir);
}
+ if ($output->isVerbose()) {
+ $output->writeln(' Removing old cache directory');
+ }
+
$filesystem->remove($oldCacheDir);
+
+ if ($output->isVerbose()) {
+ $output->writeln(' Done');
+ }
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
index 71f071105b62c..8e08153d4a964 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
@@ -42,6 +42,7 @@ protected function configure()
command, too many classes that should be part of the cache are already loaded
in memory). Use curl or any other similar tool to warm up
the classes cache if you want.
+
EOF
)
;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
new file mode 100644
index 0000000000000..777628623f924
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
@@ -0,0 +1,91 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * A console command for dumping available configuration reference.
+ *
+ * @author Grégoire Pineau
+ */
+class ConfigDebugCommand extends AbstractConfigCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('config:debug')
+ ->setDefinition(array(
+ new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'),
+ ))
+ ->setDescription('Dumps the current configuration for an extension')
+ ->setHelp(<<%command.name% command dumps the current configuration for an
+extension/bundle.
+
+Either the extension alias or bundle name can be used:
+
+ php %command.full_name% framework
+ php %command.full_name% FrameworkBundle
+
+EOF
+ )
+ ;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $name = $input->getArgument('name');
+
+ if (empty($name)) {
+ $this->listBundles($output);
+
+ return;
+ }
+
+ $extension = $this->findExtension($name);
+
+ $kernel = $this->getContainer()->get('kernel');
+ $method = new \ReflectionMethod($kernel, 'buildContainer');
+ $method->setAccessible(true);
+ $container = $method->invoke($kernel);
+
+ $configs = $container->getExtensionConfig($extension->getAlias());
+ $configuration = $extension->getConfiguration($configs, $container);
+
+ $this->validateConfiguration($extension, $configuration);
+
+ $configs = $container->getParameterBag()->resolveValue($configs);
+
+ $processor = new Processor();
+ $config = $processor->processConfiguration($configuration, $configs);
+
+ if ($name === $extension->getAlias()) {
+ $output->writeln(sprintf('# Current configuration for extension with alias: "%s"', $name));
+ } else {
+ $output->writeln(sprintf('# Current configuration for "%s"', $name));
+ }
+
+ $output->writeln(Yaml::dump(array($extension->getAlias() => $config), 3));
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
index bf118407b0200..4a92fd63a8906 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
@@ -11,18 +11,21 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
-use Symfony\Component\Config\Definition\ReferenceDumper;
+use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper;
+use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper;
use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
- * A console command for dumping available configuration reference
+ * A console command for dumping available configuration reference.
*
* @author Kevin Bond
+ * @author Wouter J
+ * @author Grégoire Pineau
*/
-class ConfigDumpReferenceCommand extends ContainerDebugCommand
+class ConfigDumpReferenceCommand extends AbstractConfigCommand
{
/**
* {@inheritdoc}
@@ -32,21 +35,25 @@ protected function configure()
$this
->setName('config:dump-reference')
->setDefinition(array(
- new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle or extension alias'),
+ new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'),
+ new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The format, either yaml or xml', 'yaml'),
))
- ->setDescription('Dumps default configuration for an extension')
+ ->setDescription('Dumps the default configuration for an extension')
->setHelp(<<%command.name% command dumps the default configuration for an extension/bundle.
+The %command.name% command dumps the default configuration for an
+extension/bundle.
-The extension alias or bundle name can be used:
-
-Example:
+Either the extension alias or bundle name can be used:
php %command.full_name% framework
+ php %command.full_name% FrameworkBundle
-or
+With the format option specifies the format of the configuration,
+this is either yaml or xml.
+When the option is not provided, yaml is used.
+
+ php %command.full_name% FrameworkBundle --format=xml
- php %command.full_name% FrameworkBundle
EOF
)
;
@@ -59,66 +66,40 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
- $bundles = $this->getContainer()->get('kernel')->getBundles();
- $containerBuilder = $this->getContainerBuilder();
-
$name = $input->getArgument('name');
if (empty($name)) {
- $output->writeln('Available registered bundles with their extension alias if available:');
- foreach ($bundles as $bundle) {
- $extension = $bundle->getContainerExtension();
- $output->writeln($bundle->getName().($extension ? ': '.$extension->getAlias() : ''));
- }
+ $this->listBundles($output);
return;
}
- $extension = null;
-
- if (preg_match('/Bundle$/', $name)) {
- // input is bundle name
+ $extension = $this->findExtension($name);
- if (isset($bundles[$name])) {
- $extension = $bundles[$name]->getContainerExtension();
- }
+ $configuration = $extension->getConfiguration(array(), $this->getContainerBuilder());
- if (!$extension) {
- throw new \LogicException(sprintf('No extensions with configuration available for "%s"', $name));
- }
+ $this->validateConfiguration($extension, $configuration);
- $message = 'Default configuration for "'.$name.'"';
+ if ($name === $extension->getAlias()) {
+ $message = sprintf('Default configuration for extension with alias: "%s"', $name);
} else {
- foreach ($bundles as $bundle) {
- $extension = $bundle->getContainerExtension();
-
- if ($extension && $extension->getAlias() === $name) {
- break;
- }
-
- $extension = null;
- }
-
- if (!$extension) {
- throw new \LogicException(sprintf('No extension with alias "%s" is enabled', $name));
- }
-
- $message = 'Default configuration for extension with alias: "'.$name.'"';
+ $message = sprintf('Default configuration for "%s"', $name);
}
- $configuration = $extension->getConfiguration(array(), $containerBuilder);
-
- if (!$configuration) {
- throw new \LogicException(sprintf('The extension with alias "%s" does not have it\'s getConfiguration() method setup', $extension->getAlias()));
+ switch ($input->getOption('format')) {
+ case 'yaml':
+ $output->writeln(sprintf('# %s', $message));
+ $dumper = new YamlReferenceDumper();
+ break;
+ case 'xml':
+ $output->writeln(sprintf('', $message));
+ $dumper = new XmlReferenceDumper();
+ break;
+ default:
+ $output->writeln($message);
+ throw new \InvalidArgumentException('Only the yaml and xml formats are supported.');
}
- if (!$configuration instanceof ConfigurationInterface) {
- throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable', get_class($configuration)));
- }
-
- $output->writeln($message);
-
- $dumper = new ReferenceDumper();
- $output->writeln($dumper->dump($configuration));
+ $output->writeln($dumper->dump($configuration, $extension->getNamespace()));
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
index 17fe850019170..84be032173313 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
@@ -11,18 +11,17 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
+use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\DependencyInjection\Alias;
-use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
/**
- * A console command for retrieving information about services
+ * A console command for retrieving information about services.
*
* @author Ryan Weaver
*/
@@ -42,11 +41,13 @@ protected function configure()
->setName('container:debug')
->setDefinition(array(
new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'),
- new InputOption('show-private', null, InputOption::VALUE_NONE, 'Use to show public *and* private services'),
- new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Show all services with a specific tag'),
+ new InputOption('show-private', null, InputOption::VALUE_NONE, 'Used to show public *and* private services'),
+ new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Shows all services with a specific tag'),
new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays tagged services for an application'),
new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'),
new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application'),
+ new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output description in other formats', 'txt'),
+ new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
))
->setDescription('Displays current services for an application')
->setHelp(<<php %command.full_name% --parameter=kernel.debug
+
EOF
)
;
@@ -85,57 +87,44 @@ protected function configure()
/**
* {@inheritdoc}
- *
- * @throws \LogicException
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->validateInput($input);
- $this->containerBuilder = $this->getContainerBuilder();
-
if ($input->getOption('parameters')) {
- $parameters = $this->getContainerBuilder()->getParameterBag()->all();
-
- // Sort parameters alphabetically
- ksort($parameters);
-
- $this->outputParameters($output, $parameters);
-
- return;
- }
-
- $parameter = $input->getOption('parameter');
- if (null !== $parameter) {
- $output->write($this->formatParameter($this->getContainerBuilder()->getParameter($parameter)));
-
- return;
- }
-
- if ($input->getOption('tags')) {
- $this->outputTags($output, $input->getOption('show-private'));
-
- return;
- }
-
- $tag = $input->getOption('tag');
- if (null !== $tag) {
- $serviceIds = array_keys($this->containerBuilder->findTaggedServiceIds($tag));
+ $object = $this->getContainerBuilder()->getParameterBag();
+ $options = array();
+ } elseif ($parameter = $input->getOption('parameter')) {
+ $object = $this->getContainerBuilder();
+ $options = array('parameter' => $parameter);
+ } elseif ($input->getOption('tags')) {
+ $object = $this->getContainerBuilder();
+ $options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private'));
+ } elseif ($tag = $input->getOption('tag')) {
+ $object = $this->getContainerBuilder();
+ $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private'));
+ } elseif ($name = $input->getArgument('name')) {
+ $object = $this->getContainerBuilder();
+ $options = array('id' => $name);
} else {
- $serviceIds = $this->containerBuilder->getServiceIds();
+ $object = $this->getContainerBuilder();
+ $options = array('show_private' => $input->getOption('show-private'));
}
- // sort so that it reads like an index of services
- asort($serviceIds);
-
- $name = $input->getArgument('name');
- if ($name) {
- $this->outputService($output, $name);
- } else {
- $this->outputServices($output, $serviceIds, $input->getOption('show-private'), $tag);
- }
+ $helper = new DescriptorHelper();
+ $options['format'] = $input->getOption('format');
+ $options['raw_text'] = $input->getOption('raw');
+ $helper->describe($output, $object, $options);
}
+ /**
+ * Validates input arguments and options.
+ *
+ * @param InputInterface $input
+ *
+ * @throws \InvalidArgumentException
+ */
protected function validateInput(InputInterface $input)
{
$options = array('tags', 'tag', 'parameters', 'parameter');
@@ -155,211 +144,6 @@ protected function validateInput(InputInterface $input)
}
}
- protected function outputServices(OutputInterface $output, $serviceIds, $showPrivate = false, $showTagAttributes = null)
- {
- // set the label to specify public or public+private
- if ($showPrivate) {
- $label = 'Public and private services';
- } else {
- $label = 'Public services';
- }
- if ($showTagAttributes) {
- $label .= ' with tag '.$showTagAttributes.'';
- }
-
- $output->writeln($this->getHelper('formatter')->formatSection('container', $label));
-
- // loop through to get space needed and filter private services
- $maxName = 4;
- $maxScope = 6;
- $maxTags = array();
- foreach ($serviceIds as $key => $serviceId) {
- $definition = $this->resolveServiceDefinition($serviceId);
-
- if ($definition instanceof Definition) {
- // filter out private services unless shown explicitly
- if (!$showPrivate && !$definition->isPublic()) {
- unset($serviceIds[$key]);
- continue;
- }
-
- if (strlen($definition->getScope()) > $maxScope) {
- $maxScope = strlen($definition->getScope());
- }
-
- if (null !== $showTagAttributes) {
- $tags = $definition->getTag($showTagAttributes);
- foreach ($tags as $tag) {
- foreach ($tag as $key => $value) {
- if (!isset($maxTags[$key])) {
- $maxTags[$key] = strlen($key);
- }
- if (strlen($value) > $maxTags[$key]) {
- $maxTags[$key] = strlen($value);
- }
- }
- }
- }
- }
-
- if (strlen($serviceId) > $maxName) {
- $maxName = strlen($serviceId);
- }
- }
- $format = '%-'.$maxName.'s ';
- $format .= implode("", array_map(function ($length) { return "%-{$length}s "; }, $maxTags));
- $format .= '%-'.$maxScope.'s %s';
-
- // the title field needs extra space to make up for comment tags
- $format1 = '%-'.($maxName + 19).'s ';
- $format1 .= implode("", array_map(function ($length) { return '%-'.($length + 19).'s '; }, $maxTags));
- $format1 .= '%-'.($maxScope + 19).'s %s';
-
- $tags = array();
- foreach ($maxTags as $tagName => $length) {
- $tags[] = ''.$tagName.'';
- }
- $output->writeln(vsprintf($format1, $this->buildArgumentsArray('Service Id', 'Scope', 'Class Name', $tags)));
-
- foreach ($serviceIds as $serviceId) {
- $definition = $this->resolveServiceDefinition($serviceId);
-
- if ($definition instanceof Definition) {
- $lines = array();
- if (null !== $showTagAttributes) {
- foreach ($definition->getTag($showTagAttributes) as $key => $tag) {
- $tagValues = array();
- foreach (array_keys($maxTags) as $tagName) {
- $tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : "";
- }
- if (0 === $key) {
- $lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass(), $tagValues);
- } else {
- $lines[] = $this->buildArgumentsArray(' "', '', '', $tagValues);
- }
- }
- } else {
- $lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass());
- }
-
- foreach ($lines as $arguments) {
- $output->writeln(vsprintf($format, $arguments));
- }
- } elseif ($definition instanceof Alias) {
- $alias = $definition;
- $output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, 'n/a', sprintf('alias for %s', (string) $alias), count($maxTags) ? array_fill(0, count($maxTags), "") : array())));
- } else {
- // we have no information (happens with "service_container")
- $service = $definition;
- $output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, '', get_class($service), count($maxTags) ? array_fill(0, count($maxTags), "") : array())));
- }
- }
- }
-
- protected function buildArgumentsArray($serviceId, $scope, $className, array $tagAttributes = array())
- {
- $arguments = array($serviceId);
- foreach ($tagAttributes as $tagAttribute) {
- $arguments[] = $tagAttribute;
- }
- $arguments[] = $scope;
- $arguments[] = $className;
-
- return $arguments;
- }
-
- /**
- * Renders detailed service information about one service
- */
- protected function outputService(OutputInterface $output, $serviceId)
- {
- $definition = $this->resolveServiceDefinition($serviceId);
-
- $label = sprintf('Information for service %s', $serviceId);
- $output->writeln($this->getHelper('formatter')->formatSection('container', $label));
- $output->writeln('');
-
- if ($definition instanceof Definition) {
- $output->writeln(sprintf('Service Id %s', $serviceId));
- $output->writeln(sprintf('Class %s', $definition->getClass() ?: "-"));
-
- $tags = $definition->getTags();
- if (count($tags)) {
- $output->writeln('Tags');
- foreach ($tags as $tagName => $tagData) {
- foreach ($tagData as $singleTagData) {
- $output->writeln(sprintf(' - %-30s (%s)', $tagName, implode(', ', array_map(function ($key, $value) {
- return sprintf('%s: %s', $key, $value);
- }, array_keys($singleTagData), array_values($singleTagData)))));
- }
- }
- } else {
- $output->writeln('Tags -');
- }
-
- $output->writeln(sprintf('Scope %s', $definition->getScope()));
-
- $public = $definition->isPublic() ? 'yes' : 'no';
- $output->writeln(sprintf('Public %s', $public));
-
- $synthetic = $definition->isSynthetic() ? 'yes' : 'no';
- $output->writeln(sprintf('Synthetic %s', $synthetic));
-
- $file = $definition->getFile() ? $definition->getFile() : '-';
- $output->writeln(sprintf('Required File %s', $file));
- } elseif ($definition instanceof Alias) {
- $alias = $definition;
- $output->writeln(sprintf('This service is an alias for the service %s', (string) $alias));
- } else {
- // edge case (but true for "service_container", all we have is the service itself
- $service = $definition;
- $output->writeln(sprintf('Service Id %s', $serviceId));
- $output->writeln(sprintf('Class %s', get_class($service)));
- }
- }
-
- protected function outputParameters(OutputInterface $output, $parameters)
- {
- $output->writeln($this->getHelper('formatter')->formatSection('container', 'List of parameters'));
-
- $terminalDimensions = $this->getApplication()->getTerminalDimensions();
- $maxTerminalWidth = $terminalDimensions[0];
- $maxParameterWidth = 0;
- $maxValueWidth = 0;
-
- // Determine max parameter & value length
- foreach ($parameters as $parameter => $value) {
- $parameterWidth = strlen($parameter);
- if ($parameterWidth > $maxParameterWidth) {
- $maxParameterWidth = $parameterWidth;
- }
-
- $valueWith = strlen($this->formatParameter($value));
- if ($valueWith > $maxValueWidth) {
- $maxValueWidth = $valueWith;
- }
- }
-
- $maxValueWidth = min($maxValueWidth, $maxTerminalWidth - $maxParameterWidth - 1);
-
- $formatTitle = '%-'.($maxParameterWidth + 19).'s %-'.($maxValueWidth + 19).'s';
- $format = '%-'.$maxParameterWidth.'s %-'.$maxValueWidth.'s';
-
- $output->writeln(sprintf($formatTitle, 'Parameter', 'Value'));
-
- foreach ($parameters as $parameter => $value) {
- $splits = str_split($this->formatParameter($value), $maxValueWidth);
-
- foreach ($splits as $index => $split) {
- if (0 === $index) {
- $output->writeln(sprintf($format, $parameter, $split));
- } else {
- $output->writeln(sprintf($format, ' ', $split));
- }
- }
- }
- }
-
/**
* Loads the ContainerBuilder from the cache.
*
@@ -384,77 +168,4 @@ protected function getContainerBuilder()
return $container;
}
-
- /**
- * Given an array of service IDs, this returns the array of corresponding
- * Definition and Alias objects that those ids represent.
- *
- * @param string $serviceId The service id to resolve
- *
- * @return Definition|Alias
- */
- protected function resolveServiceDefinition($serviceId)
- {
- if ($this->containerBuilder->hasDefinition($serviceId)) {
- return $this->containerBuilder->getDefinition($serviceId);
- }
-
- // Some service IDs don't have a Definition, they're simply an Alias
- if ($this->containerBuilder->hasAlias($serviceId)) {
- return $this->containerBuilder->getAlias($serviceId);
- }
-
- // the service has been injected in some special way, just return the service
- return $this->containerBuilder->get($serviceId);
- }
-
- /**
- * Renders list of tagged services grouped by tag
- *
- * @param OutputInterface $output
- * @param bool $showPrivate
- */
- protected function outputTags(OutputInterface $output, $showPrivate = false)
- {
- $tags = $this->containerBuilder->findTags();
- asort($tags);
-
- $label = 'Tagged services';
- $output->writeln($this->getHelper('formatter')->formatSection('container', $label));
-
- foreach ($tags as $tag) {
- $serviceIds = $this->containerBuilder->findTaggedServiceIds($tag);
-
- foreach ($serviceIds as $serviceId => $attributes) {
- $definition = $this->resolveServiceDefinition($serviceId);
- if ($definition instanceof Definition) {
- if (!$showPrivate && !$definition->isPublic()) {
- unset($serviceIds[$serviceId]);
- continue;
- }
- }
- }
-
- if (count($serviceIds) === 0) {
- continue;
- }
-
- $output->writeln($this->getHelper('formatter')->formatSection('tag', $tag));
-
- foreach ($serviceIds as $serviceId => $attributes) {
- $output->writeln($serviceId);
- }
-
- $output->writeln('');
- }
- }
-
- protected function formatParameter($value)
- {
- if (is_bool($value) || is_array($value) || (null === $value)) {
- return json_encode($value);
- }
-
- return $value;
- }
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php
index c8a17e8904b05..63487b750aa5f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php
@@ -21,6 +21,10 @@
/**
* RouterApacheDumperCommand.
*
+ * @deprecated Deprecated since version 2.5, to be removed in 3.0.
+ * The performance gains are minimal and it's very hard to replicate
+ * the behavior of PHP implementation.
+ *
* @author Fabien Potencier
*/
class RouterApacheDumperCommand extends ContainerAwareCommand
@@ -59,6 +63,7 @@ protected function configure()
matching.
php %command.full_name%
+
EOF
)
;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
index 7bd026c6aed96..4a3717dc7d8a3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
@@ -11,13 +11,16 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
+use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Route;
/**
- * A console command for retrieving information about routes
+ * A console command for retrieving information about routes.
*
* @author Fabien Potencier
* @author Tobias Schultze
@@ -49,12 +52,16 @@ protected function configure()
->setName('router:debug')
->setDefinition(array(
new InputArgument('name', InputArgument::OPTIONAL, 'A route name'),
+ new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'),
+ new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output route(s) in other formats', 'txt'),
+ new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
))
->setDescription('Displays current routes for an application')
->setHelp(<<%command.name% displays the configured routes:
php %command.full_name%
+
EOF
)
;
@@ -68,125 +75,42 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
+ $helper = new DescriptorHelper();
if ($name) {
- $this->outputRoute($output, $name);
+ $route = $this->getContainer()->get('router')->getRouteCollection()->get($name);
+ if (!$route) {
+ throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
+ }
+ $this->convertController($route);
+ $helper->describe($output, $route, array(
+ 'format' => $input->getOption('format'),
+ 'raw_text' => $input->getOption('raw'),
+ 'name' => $name,
+ ));
} else {
- $this->outputRoutes($output);
- }
- }
-
- protected function outputRoutes(OutputInterface $output, $routes = null)
- {
- if (null === $routes) {
- $routes = $this->getContainer()->get('router')->getRouteCollection()->all();
- }
-
- $output->writeln($this->getHelper('formatter')->formatSection('router', 'Current routes'));
-
- $maxName = strlen('name');
- $maxMethod = strlen('method');
- $maxScheme = strlen('scheme');
- $maxHost = strlen('host');
-
- foreach ($routes as $name => $route) {
- $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
- $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
- $host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
- $maxName = max($maxName, strlen($name));
- $maxMethod = max($maxMethod, strlen($method));
- $maxScheme = max($maxScheme, strlen($scheme));
- $maxHost = max($maxHost, strlen($host));
- }
+ $routes = $this->getContainer()->get('router')->getRouteCollection();
- $format = '%-'.$maxName.'s %-'.$maxMethod.'s %-'.$maxScheme.'s %-'.$maxHost.'s %s';
- $formatHeader = '%-'.($maxName + 19).'s %-'.($maxMethod + 19).'s %-'.($maxScheme + 19).'s %-'.($maxHost + 19).'s %s';
- $output->writeln(sprintf($formatHeader, 'Name', 'Method', 'Scheme', 'Host', 'Path'));
+ foreach ($routes as $route) {
+ $this->convertController($route);
+ }
- foreach ($routes as $name => $route) {
- $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
- $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
- $host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
- $output->writeln(sprintf($format, $name, $method, $scheme, $host, $route->getPath()), OutputInterface::OUTPUT_RAW);
+ $helper->describe($output, $routes, array(
+ 'format' => $input->getOption('format'),
+ 'raw_text' => $input->getOption('raw'),
+ 'show_controllers' => $input->getOption('show-controllers'),
+ ));
}
}
- /**
- * @throws \InvalidArgumentException When route does not exist
- */
- protected function outputRoute(OutputInterface $output, $name)
+ private function convertController(Route $route)
{
- $route = $this->getContainer()->get('router')->getRouteCollection()->get($name);
- if (!$route) {
- throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
+ $nameParser = $this->getContainer()->get('controller_name_converter');
+ if ($route->hasDefault('_controller')) {
+ try {
+ $route->setDefault('_controller', $nameParser->build($route->getDefault('_controller')));
+ } catch (\InvalidArgumentException $e) {
+ }
}
-
- $output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
-
- $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
- $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
- $host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
-
- $output->write('Name ');
- $output->writeln($name, OutputInterface::OUTPUT_RAW);
-
- $output->write('Path ');
- $output->writeln($route->getPath(), OutputInterface::OUTPUT_RAW);
-
- $output->write('Host ');
- $output->writeln($host, OutputInterface::OUTPUT_RAW);
-
- $output->write('Scheme ');
- $output->writeln($scheme, OutputInterface::OUTPUT_RAW);
-
- $output->write('Method ');
- $output->writeln($method, OutputInterface::OUTPUT_RAW);
-
- $output->write('Class ');
- $output->writeln(get_class($route), OutputInterface::OUTPUT_RAW);
-
- $output->write('Defaults ');
- $output->writeln($this->formatConfigs($route->getDefaults()), OutputInterface::OUTPUT_RAW);
-
- $output->write('Requirements ');
- // we do not want to show the schemes and methods again that are also in the requirements for BC
- $requirements = $route->getRequirements();
- unset($requirements['_scheme'], $requirements['_method']);
- $output->writeln($this->formatConfigs($requirements) ?: 'NO CUSTOM', OutputInterface::OUTPUT_RAW);
-
- $output->write('Options ');
- $output->writeln($this->formatConfigs($route->getOptions()), OutputInterface::OUTPUT_RAW);
-
- $output->write('Path-Regex ');
- $output->writeln($route->compile()->getRegex(), OutputInterface::OUTPUT_RAW);
-
- if (null !== $route->compile()->getHostRegex()) {
- $output->write('Host-Regex ');
- $output->writeln($route->compile()->getHostRegex(), OutputInterface::OUTPUT_RAW);
- }
- }
-
- protected function formatValue($value)
- {
- if (is_object($value)) {
- return sprintf('object(%s)', get_class($value));
- }
-
- if (is_string($value)) {
- return $value;
- }
-
- return preg_replace("/\n\s*/s", '', var_export($value, true));
- }
-
- private function formatConfigs(array $array)
- {
- $string = '';
- ksort($array);
- foreach ($array as $name => $value) {
- $string .= ($string ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
- }
-
- return $string;
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
index 7d1f16696ae4e..72b322bca3589 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
@@ -13,6 +13,7 @@
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Routing\RouterInterface;
@@ -50,12 +51,18 @@ protected function configure()
->setName('router:match')
->setDefinition(array(
new InputArgument('path_info', InputArgument::REQUIRED, 'A path info'),
+ new InputOption('method', null, InputOption::VALUE_REQUIRED, 'Sets the HTTP method'),
+ new InputOption('scheme', null, InputOption::VALUE_REQUIRED, 'Sets the URI scheme (usually http or https)'),
+ new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Sets the URI host'),
))
->setDescription('Helps debug routes by simulating a path info match')
->setHelp(<<%command.name% simulates a path info match:
+The %command.name% shows which routes match a given request and which don't and for what reason:
php %command.full_name% /foo
+ or
+ php %command.full_name% /foo --method POST --scheme https --host symfony.com --verbose
+
EOF
)
;
@@ -67,12 +74,23 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
$router = $this->getContainer()->get('router');
- $matcher = new TraceableUrlMatcher($router->getRouteCollection(), $router->getContext());
+ $context = $router->getContext();
+ if (null !== $method = $input->getOption('method')) {
+ $context->setMethod($method);
+ }
+ if (null !== $scheme = $input->getOption('scheme')) {
+ $context->setScheme($scheme);
+ }
+ if (null !== $host = $input->getOption('host')) {
+ $context->setHost($host);
+ }
+
+ $matcher = new TraceableUrlMatcher($router->getRouteCollection(), $context);
$traces = $matcher->getTraces($input->getArgument('path_info'));
$matches = false;
- foreach ($traces as $i => $trace) {
+ foreach ($traces as $trace) {
if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) {
$output->writeln(sprintf('Route "%s" almost matches but %s>', $trace['name'], lcfirst($trace['log'])));
} elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
index 462ff09ca5b8c..42329ddfaa709 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
@@ -44,7 +44,7 @@ protected function configure()
{
$this
->setDefinition(array(
- new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', 'localhost:8000'),
+ new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null),
new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'),
))
@@ -72,6 +72,7 @@ protected function configure()
"prod".
See also: http://www.php.net/manual/en/features.commandline.webserver.php
+
EOF
)
;
@@ -100,21 +101,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->writeln('Running PHP built-in server in production environment is NOT recommended!');
}
+ $output->writeln(sprintf("Server running on http://%s\n", $input->getArgument('address')));
+ $output->writeln('Quit the server with CONTROL-C.');
+
if (null === $builder = $this->createPhpProcessBuilder($input, $output, $env)) {
return 1;
}
- $output->writeln(sprintf("Server running on http://%s\n", $input->getArgument('address')));
- $output->writeln('Quit the server with CONTROL-C.');
-
$builder->setWorkingDirectory($documentRoot);
$builder->setTimeout(null);
$process = $builder->getProcess();
- $process->run(function ($type, $buffer) use ($output) {
- if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+
+ if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+ $callback = function ($type, $buffer) use ($output) {
$output->write($buffer);
- }
- });
+ };
+ } else {
+ $callback = null;
+ $process->disableOutput();
+ }
+
+ $process->run($callback);
if (!$process->isSuccessful()) {
$output->writeln('Built-in server terminated unexpectedly');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
new file mode 100644
index 0000000000000..eff9dc194728a
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
@@ -0,0 +1,232 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Translation\Catalogue\MergeOperation;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Translator;
+
+/**
+ * Helps finding unused or missing translation messages in a given locale
+ * and comparing them with the fallback ones.
+ *
+ * @author Florian Voutzinos
+ */
+class TranslationDebugCommand extends ContainerAwareCommand
+{
+ const MESSAGE_MISSING = 0;
+ const MESSAGE_UNUSED = 1;
+ const MESSAGE_EQUALS_FALLBACK = 2;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('translation:debug')
+ ->setDefinition(array(
+ new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
+ new InputArgument('bundle', InputArgument::REQUIRED, 'The bundle name'),
+ new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'),
+ new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages'),
+ new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'),
+ ))
+ ->setDescription('Displays translation messages informations')
+ ->setHelp(<<%command.name% command helps finding unused or missing translation
+messages and comparing them with the fallback ones by inspecting the
+templates and translation files of a given bundle.
+
+You can display information about bundle translations in a specific locale:
+
+php %command.full_name% en AcmeDemoBundle
+
+You can also specify a translation domain for the search:
+
+php %command.full_name% --domain=messages en AcmeDemoBundle
+
+You can only display missing messages:
+
+php %command.full_name% --only-missing en AcmeDemoBundle
+
+You can only display unused messages:
+
+php %command.full_name% --only-unused en AcmeDemoBundle
+
+EOF
+ )
+ ;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $locale = $input->getArgument('locale');
+ $domain = $input->getOption('domain');
+ $bundle = $this->getContainer()->get('kernel')->getBundle($input->getArgument('bundle'));
+ $loader = $this->getContainer()->get('translation.loader');
+
+ // Extract used messages
+ $extractedCatalogue = new MessageCatalogue($locale);
+ $this->getContainer()->get('translation.extractor')->extract($bundle->getPath().'/Resources/views', $extractedCatalogue);
+
+ // Load defined messages
+ $currentCatalogue = new MessageCatalogue($locale);
+ if (is_dir($bundle->getPath().'/Resources/translations')) {
+ $loader->loadMessages($bundle->getPath().'/Resources/translations', $currentCatalogue);
+ }
+
+ // Merge defined and extracted messages to get all message ids
+ $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);
+ $allMessages = $mergeOperation->getResult()->all($domain);
+ if (null !== $domain) {
+ $allMessages = array($domain => $allMessages);
+ }
+
+ // No defined or extracted messages
+ if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) {
+ $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale);
+
+ if (null !== $domain) {
+ $outputMessage .= sprintf(' and domain "%s"', $domain);
+ }
+
+ $output->writeln($outputMessage);
+
+ return;
+ }
+
+ // Load the fallback catalogues
+ $fallbackCatalogues = array();
+ $translator = $this->getContainer()->get('translator');
+ if ($translator instanceof Translator) {
+ foreach ($translator->getFallbackLocales() as $fallbackLocale) {
+ if ($fallbackLocale === $locale) {
+ continue;
+ }
+
+ $fallbackCatalogue = new MessageCatalogue($fallbackLocale);
+ $loader->loadMessages($bundle->getPath().'/Resources/translations', $fallbackCatalogue);
+ $fallbackCatalogues[] = $fallbackCatalogue;
+ }
+ }
+
+ /** @var \Symfony\Component\Console\Helper\Table $table */
+ $table = new Table($output);
+
+ // Display header line
+ $headers = array('State(s)', 'Id', sprintf('Message Preview (%s)', $locale));
+ foreach ($fallbackCatalogues as $fallbackCatalogue) {
+ $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale());
+ }
+ $table->setHeaders($headers);
+
+ // Iterate all message ids and determine their state
+ foreach ($allMessages as $domain => $messages) {
+ foreach (array_keys($messages) as $messageId) {
+ $value = $currentCatalogue->get($messageId, $domain);
+ $states = array();
+
+ if ($extractedCatalogue->defines($messageId, $domain)) {
+ if (!$currentCatalogue->defines($messageId, $domain)) {
+ $states[] = self::MESSAGE_MISSING;
+ }
+ } elseif ($currentCatalogue->defines($messageId, $domain)) {
+ $states[] = self::MESSAGE_UNUSED;
+ }
+
+ if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused')
+ || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) {
+ continue;
+ }
+
+ foreach ($fallbackCatalogues as $fallbackCatalogue) {
+ if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) {
+ $states[] = self::MESSAGE_EQUALS_FALLBACK;
+
+ break;
+ }
+ }
+
+ $row = array($this->formatStates($states), $this->formatId($messageId), $this->sanitizeString($value));
+ foreach ($fallbackCatalogues as $fallbackCatalogue) {
+ $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain));
+ }
+
+ $table->addRow($row);
+ }
+ }
+
+ $table->render();
+
+ $output->writeln('');
+ $output->writeln('Legend:');
+ $output->writeln(sprintf(' %s Missing message', $this->formatState(self::MESSAGE_MISSING)));
+ $output->writeln(sprintf(' %s Unused message', $this->formatState(self::MESSAGE_UNUSED)));
+ $output->writeln(sprintf(' %s Same as the fallback message', $this->formatState(self::MESSAGE_EQUALS_FALLBACK)));
+ }
+
+ private function formatState($state)
+ {
+ if (self::MESSAGE_MISSING === $state) {
+ return 'x>';
+ }
+
+ if (self::MESSAGE_UNUSED === $state) {
+ return 'o>';
+ }
+
+ if (self::MESSAGE_EQUALS_FALLBACK === $state) {
+ return '=>';
+ }
+
+ return $state;
+ }
+
+ private function formatStates(array $states)
+ {
+ $result = array();
+ foreach ($states as $state) {
+ $result[] = $this->formatState($state);
+ }
+
+ return implode(' ', $result);
+ }
+
+ private function formatId($id)
+ {
+ return sprintf('%s', $id);
+ }
+
+ private function sanitizeString($string, $length = 40)
+ {
+ $string = trim(preg_replace('/\s+/', ' ', $string));
+
+ if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) {
+ if (mb_strlen($string, $encoding) > $length) {
+ return mb_substr($string, 0, $length - 3, $encoding).'...';
+ }
+ } elseif (strlen($string) > $length) {
+ return substr($string, 0, $length - 3).'...';
+ }
+
+ return $string;
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
index c9fac59318aaa..10c8b903a24d0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
@@ -52,6 +52,10 @@ protected function configure()
'force', null, InputOption::VALUE_NONE,
'Should the update be done'
),
+ new InputOption(
+ 'no-backup', null, InputOption::VALUE_NONE,
+ 'Should backup be disabled'
+ ),
new InputOption(
'clean', null, InputOption::VALUE_NONE,
'Should clean not found messages'
@@ -66,6 +70,7 @@ protected function configure()
php %command.full_name% --dump-messages en AcmeBundle
php %command.full_name% --force --prefix="new_" fr AcmeBundle
+
EOF
)
;
@@ -138,6 +143,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
}
+ if ($input->getOption('no-backup') === true) {
+ $writer->disableBackup();
+ }
+
// save the files
if ($input->getOption('force') === true) {
$output->writeln('Writing files');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php
new file mode 100644
index 0000000000000..967c3b18a317d
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php
@@ -0,0 +1,163 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Command;
+
+if (!defined('JSON_PRETTY_PRINT')) {
+ define('JSON_PRETTY_PRINT', 128);
+}
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Parser;
+
+/**
+ * Validates YAML files syntax and outputs encountered errors.
+ *
+ * @author Grégoire Pineau
+ */
+class YamlLintCommand extends Command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('yaml:lint')
+ ->setDescription('Lints a file and outputs encountered errors')
+ ->addArgument('filename', null, 'A file or a directory or STDIN')
+ ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
+ ->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT
+the first encountered syntax error.
+
+You can validate the syntax of a file:
+
+php %command.full_name% filename
+
+Or of a whole directory:
+
+php %command.full_name% dirname
+php %command.full_name% dirname --format=json
+
+Or all YAML files in a bundle:
+
+php %command.full_name% @AcmeDemoBundle
+
+You can also pass the YAML contents from STDIN:
+
+cat filename | php %command.full_name%
+
+EOF
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $filename = $input->getArgument('filename');
+
+ if (!$filename) {
+ if (0 !== ftell(STDIN)) {
+ throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
+ }
+
+ $content = '';
+ while (!feof(STDIN)) {
+ $content .= fread(STDIN, 1024);
+ }
+
+ return $this->display($input, $output, array($this->validate($content)));
+ }
+
+ if (0 !== strpos($filename, '@') && !is_readable($filename)) {
+ throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename));
+ }
+
+ $files = array();
+ if (is_file($filename)) {
+ $files = array($filename);
+ } elseif (is_dir($filename)) {
+ $files = Finder::create()->files()->in($filename)->name('*.yml');
+ } else {
+ $dir = $this->getApplication()->getKernel()->locateResource($filename);
+ $files = Finder::create()->files()->in($dir)->name('*.yml');
+ }
+
+ $filesInfo = array();
+ foreach ($files as $file) {
+ $filesInfo[] = $this->validate(file_get_contents($file), $file);
+ }
+
+ return $this->display($input, $output, $filesInfo);
+ }
+
+ private function validate($content, $file = null)
+ {
+ $this->parser = new Parser();
+ try {
+ $this->parser->parse($content);
+ } catch (ParseException $e) {
+ return array('file' => $file, 'valid' => false, 'message' => $e->getMessage());
+ }
+
+ return array('file' => $file, 'valid' => true);
+ }
+
+ private function display(InputInterface $input, OutputInterface $output, $files)
+ {
+ switch ($input->getOption('format')) {
+ case 'txt':
+ return $this->displayTxt($output, $files);
+ case 'json':
+ return $this->displayJson($output, $files);
+ default:
+ throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
+ }
+ }
+
+ private function displayTxt(OutputInterface $output, $filesInfo)
+ {
+ $errors = 0;
+
+ foreach ($filesInfo as $info) {
+ if ($info['valid'] && $output->isVerbose()) {
+ $output->writeln('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ } elseif (!$info['valid']) {
+ $errors++;
+ $output->writeln(sprintf('KO in %s', $info['file']));
+ $output->writeln(sprintf('>> %s', $info['message']));
+ }
+ }
+
+ $output->writeln(sprintf('%d/%d valid files', count($filesInfo) - $errors, count($filesInfo)));
+
+ return min($errors, 1);
+ }
+
+ private function displayJson(OutputInterface $output, $filesInfo)
+ {
+ $errors = 0;
+
+ array_walk($filesInfo, function (&$v) use (&$errors) {
+ $v['file'] = (string) $v['file'];
+ if (!$v['valid']) {
+ $errors++;
+ }
+ });
+
+ $output->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT));
+
+ return min($errors, 1);
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php
index 6b02d3c5d2d6a..d10db38f37fa8 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php
@@ -98,10 +98,18 @@ public function doRun(InputInterface $input, OutputInterface $output)
protected function registerCommands()
{
+ $container = $this->kernel->getContainer();
+
foreach ($this->kernel->getBundles() as $bundle) {
if ($bundle instanceof Bundle) {
$bundle->registerCommands($this);
}
}
+
+ if ($container->hasParameter('console.command.ids')) {
+ foreach ($container->getParameter('console.command.ids') as $id) {
+ $this->add($container->get($id));
+ }
+ }
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
new file mode 100644
index 0000000000000..7aaea0b9c3df6
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
@@ -0,0 +1,287 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+
+use Symfony\Component\Console\Descriptor\DescriptorInterface;
+use Symfony\Component\Console\Helper\TableHelper;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * @author Jean-François Simon
+ */
+abstract class Descriptor implements DescriptorInterface
+{
+ /**
+ * @var OutputInterface
+ */
+ private $output;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function describe(OutputInterface $output, $object, array $options = array())
+ {
+ $this->output = $output;
+
+ switch (true) {
+ case $object instanceof RouteCollection:
+ $this->describeRouteCollection($object, $options);
+ break;
+ case $object instanceof Route:
+ $this->describeRoute($object, $options);
+ break;
+ case $object instanceof ParameterBag:
+ $this->describeContainerParameters($object, $options);
+ break;
+ case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']:
+ $this->describeContainerTags($object, $options);
+ break;
+ case $object instanceof ContainerBuilder && isset($options['id']):
+ $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options);
+ break;
+ case $object instanceof ContainerBuilder && isset($options['parameter']):
+ $this->describeContainerParameter($object->getParameter($options['parameter']), $options);
+ break;
+ case $object instanceof ContainerBuilder:
+ $this->describeContainerServices($object, $options);
+ break;
+ case $object instanceof Definition:
+ $this->describeContainerDefinition($object, $options);
+ break;
+ case $object instanceof Alias:
+ $this->describeContainerAlias($object, $options);
+ break;
+ default:
+ throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
+ }
+ }
+
+ /**
+ * Writes content to output.
+ *
+ * @param string $content
+ * @param bool $decorated
+ */
+ protected function write($content, $decorated = false)
+ {
+ $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
+ }
+
+ /**
+ * Writes content to output.
+ *
+ * @param TableHelper $table
+ * @param bool $decorated
+ */
+ protected function renderTable(TableHelper $table, $decorated = false)
+ {
+ if (!$decorated) {
+ $table->setCellRowFormat('%s');
+ $table->setCellHeaderFormat('%s');
+ }
+
+ $table->render($this->output);
+ }
+
+ /**
+ * Describes an InputArgument instance.
+ *
+ * @param RouteCollection $routes
+ * @param array $options
+ */
+ abstract protected function describeRouteCollection(RouteCollection $routes, array $options = array());
+
+ /**
+ * Describes an InputOption instance.
+ *
+ * @param Route $route
+ * @param array $options
+ */
+ abstract protected function describeRoute(Route $route, array $options = array());
+
+ /**
+ * Describes container parameters.
+ *
+ * @param ParameterBag $parameters
+ * @param array $options
+ */
+ abstract protected function describeContainerParameters(ParameterBag $parameters, array $options = array());
+
+ /**
+ * Describes container tags.
+ *
+ * @param ContainerBuilder $builder
+ * @param array $options
+ */
+ abstract protected function describeContainerTags(ContainerBuilder $builder, array $options = array());
+
+ /**
+ * Describes a container service by its name.
+ *
+ * Common options are:
+ * * name: name of described service
+ *
+ * @param Definition|Alias|object $service
+ * @param array $options
+ */
+ abstract protected function describeContainerService($service, array $options = array());
+
+ /**
+ * Describes container services.
+ *
+ * Common options are:
+ * * tag: filters described services by given tag
+ *
+ * @param ContainerBuilder $builder
+ * @param array $options
+ */
+ abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = array());
+
+ /**
+ * Describes a service definition.
+ *
+ * @param Definition $definition
+ * @param array $options
+ */
+ abstract protected function describeContainerDefinition(Definition $definition, array $options = array());
+
+ /**
+ * Describes a service alias.
+ *
+ * @param Alias $alias
+ * @param array $options
+ */
+ abstract protected function describeContainerAlias(Alias $alias, array $options = array());
+
+ /**
+ * Describes a container parameter.
+ *
+ * @param string $parameter
+ * @param array $options
+ */
+ abstract protected function describeContainerParameter($parameter, array $options = array());
+
+ /**
+ * Formats a value as string.
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ protected function formatValue($value)
+ {
+ if (is_object($value)) {
+ return sprintf('object(%s)', get_class($value));
+ }
+
+ if (is_string($value)) {
+ return $value;
+ }
+
+ return preg_replace("/\n\s*/s", '', var_export($value, true));
+ }
+
+ /**
+ * Formats a parameter.
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ protected function formatParameter($value)
+ {
+ if (is_bool($value) || is_array($value) || (null === $value)) {
+ $jsonString = json_encode($value);
+
+ if (preg_match('/^(.{60})./us', $jsonString, $matches)) {
+ return $matches[1].'...';
+ }
+
+ return $jsonString;
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * @param ContainerBuilder $builder
+ * @param string $serviceId
+ *
+ * @return mixed
+ */
+ protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceId)
+ {
+ if ($builder->hasDefinition($serviceId)) {
+ return $builder->getDefinition($serviceId);
+ }
+
+ // Some service IDs don't have a Definition, they're simply an Alias
+ if ($builder->hasAlias($serviceId)) {
+ return $builder->getAlias($serviceId);
+ }
+
+ // the service has been injected in some special way, just return the service
+ return $builder->get($serviceId);
+ }
+
+ /**
+ * @param ContainerBuilder $builder
+ * @param bool $showPrivate
+ *
+ * @return array
+ */
+ protected function findDefinitionsByTag(ContainerBuilder $builder, $showPrivate)
+ {
+ $definitions = array();
+ $tags = $builder->findTags();
+ asort($tags);
+
+ foreach ($tags as $tag) {
+ foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) {
+ $definition = $this->resolveServiceDefinition($builder, $serviceId);
+
+ if (!$definition instanceof Definition || !$showPrivate && !$definition->isPublic()) {
+ continue;
+ }
+
+ if (!isset($definitions[$tag])) {
+ $definitions[$tag] = array();
+ }
+
+ $definitions[$tag][$serviceId] = $definition;
+ }
+ }
+
+ return $definitions;
+ }
+
+ protected function sortParameters(ParameterBag $parameters)
+ {
+ $parameters = $parameters->all();
+ ksort($parameters);
+
+ return $parameters;
+ }
+
+ protected function sortServiceIds(array $serviceIds)
+ {
+ asort($serviceIds);
+
+ return $serviceIds;
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
new file mode 100644
index 0000000000000..7748143702fbf
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
@@ -0,0 +1,225 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+
+if (!defined('JSON_PRETTY_PRINT')) {
+ define('JSON_PRETTY_PRINT', 128);
+}
+
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * @author Jean-François Simon
+ */
+class JsonDescriptor extends Descriptor
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRouteCollection(RouteCollection $routes, array $options = array())
+ {
+ $data = array();
+ foreach ($routes->all() as $name => $route) {
+ $data[$name] = $this->getRouteData($route);
+ }
+
+ $this->writeData($data, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRoute(Route $route, array $options = array())
+ {
+ $this->writeData($this->getRouteData($route), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
+ {
+ $this->writeData($this->sortParameters($parameters), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
+ {
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $data = array();
+
+ foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
+ $data[$tag] = array();
+ foreach ($definitions as $definition) {
+ $data[$tag][] = $this->getContainerDefinitionData($definition, true);
+ }
+ }
+
+ $this->writeData($data, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerService($service, array $options = array())
+ {
+ if (!isset($options['id'])) {
+ throw new \InvalidArgumentException('An "id" option must be provided.');
+ }
+
+ if ($service instanceof Alias) {
+ $this->writeData($this->getContainerAliasData($service), $options);
+ } elseif ($service instanceof Definition) {
+ $this->writeData($this->getContainerDefinitionData($service), $options);
+ } else {
+ $this->writeData(get_class($service), $options);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
+ {
+ $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $data = array('definitions' => array(), 'aliases' => array(), 'services' => array());
+
+ foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
+ $service = $this->resolveServiceDefinition($builder, $serviceId);
+
+ if ($service instanceof Alias) {
+ $data['aliases'][$serviceId] = $this->getContainerAliasData($service);
+ } elseif ($service instanceof Definition) {
+ if (($showPrivate || $service->isPublic())) {
+ $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service);
+ }
+ } else {
+ $data['services'][$serviceId] = get_class($service);
+ }
+ }
+
+ $this->writeData($data, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerDefinition(Definition $definition, array $options = array())
+ {
+ $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags']), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerAlias(Alias $alias, array $options = array())
+ {
+ $this->writeData($this->getContainerAliasData($alias), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameter($parameter, array $options = array())
+ {
+ $key = isset($options['parameter']) ? $options['parameter'] : '';
+
+ $this->writeData(array($key => $this->formatParameter($parameter)), $options);
+ }
+
+ /**
+ * Writes data as json.
+ *
+ * @param array $data
+ * @param array $options
+ *
+ * @return array|string
+ */
+ private function writeData(array $data, array $options)
+ {
+ $this->write(json_encode($data, (isset($options['json_encoding']) ? $options['json_encoding'] : 0) | JSON_PRETTY_PRINT)."\n");
+ }
+
+ /**
+ * @param Route $route
+ *
+ * @return array
+ */
+ protected function getRouteData(Route $route)
+ {
+ $requirements = $route->getRequirements();
+ unset($requirements['_scheme'], $requirements['_method']);
+
+ return array(
+ 'path' => $route->getPath(),
+ 'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY',
+ 'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
+ 'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
+ 'class' => get_class($route),
+ 'defaults' => $route->getDefaults(),
+ 'requirements' => $requirements ?: 'NO CUSTOM',
+ 'options' => $route->getOptions(),
+ 'pathRegex' => $route->compile()->getRegex(),
+ );
+ }
+
+ /**
+ * @param Definition $definition
+ * @param bool $omitTags
+ *
+ * @return array
+ */
+ private function getContainerDefinitionData(Definition $definition, $omitTags = false)
+ {
+ $data = array(
+ 'class' => (string) $definition->getClass(),
+ 'scope' => $definition->getScope(),
+ 'public' => $definition->isPublic(),
+ 'synthetic' => $definition->isSynthetic(),
+ 'file' => $definition->getFile(),
+ );
+
+ if (!$omitTags) {
+ $data['tags'] = array();
+ if (count($definition->getTags())) {
+ foreach ($definition->getTags() as $tagName => $tagData) {
+ foreach ($tagData as $parameters) {
+ $data['tags'][] = array('name' => $tagName, 'parameters' => $parameters);
+ }
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param Alias $alias
+ *
+ * @return array
+ */
+ private function getContainerAliasData(Alias $alias)
+ {
+ return array(
+ 'service' => (string) $alias,
+ 'public' => $alias->isPublic(),
+ );
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
new file mode 100644
index 0000000000000..b0cfad0cc5aea
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
@@ -0,0 +1,232 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * @author Jean-François Simon
+ */
+class MarkdownDescriptor extends Descriptor
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRouteCollection(RouteCollection $routes, array $options = array())
+ {
+ $first = true;
+ foreach ($routes->all() as $name => $route) {
+ if ($first) {
+ $first = false;
+ } else {
+ $this->write("\n\n");
+ }
+ $this->describeRoute($route, array('name' => $name));
+ }
+ $this->write("\n");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRoute(Route $route, array $options = array())
+ {
+ $requirements = $route->getRequirements();
+ unset($requirements['_scheme'], $requirements['_method']);
+
+ $output = '- Path: '.$route->getPath()
+ ."\n".'- Host: '.('' !== $route->getHost() ? $route->getHost() : 'ANY')
+ ."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY')
+ ."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')
+ ."\n".'- Class: '.get_class($route)
+ ."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults())
+ ."\n".'- Requirements: '.$this->formatRouterConfig($requirements) ?: 'NONE'
+ ."\n".'- Options: '.$this->formatRouterConfig($route->getOptions())
+ ."\n".'- Path-Regex: '.$route->compile()->getRegex();
+
+ $this->write(isset($options['name'])
+ ? $options['name']."\n".str_repeat('-', strlen($options['name']))."\n\n".$output
+ : $output);
+ $this->write("\n");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
+ {
+ $this->write("Container parameters\n====================\n");
+ foreach ($this->sortParameters($parameters) as $key => $value) {
+ $this->write(sprintf("\n- `%s`: `%s`", $key, $this->formatParameter($value)));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
+ {
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $this->write("Container tags\n==============");
+
+ foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
+ $this->write("\n\n".$tag."\n".str_repeat('-', strlen($tag)));
+ foreach ($definitions as $serviceId => $definition) {
+ $this->write("\n\n");
+ $this->describeContainerDefinition($definition, array('omit_tags' => true, 'id' => $serviceId));
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerService($service, array $options = array())
+ {
+ if (!isset($options['id'])) {
+ throw new \InvalidArgumentException('An "id" option must be provided.');
+ }
+
+ $childOptions = array('id' => $options['id'], 'as_array' => true);
+
+ if ($service instanceof Alias) {
+ $this->describeContainerAlias($service, $childOptions);
+ } elseif ($service instanceof Definition) {
+ $this->describeContainerDefinition($service, $childOptions);
+ } else {
+ $this->write(sprintf("**`%s`:** `%s`", $options['id'], get_class($service)));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
+ {
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+
+ $title = $showPrivate ? 'Public and private services' : 'Public services';
+ if (isset($options['tag'])) {
+ $title .= ' with tag `'.$options['tag'].'`';
+ }
+ $this->write($title."\n".str_repeat('=', strlen($title)));
+
+ $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $services = array('definitions' => array(), 'aliases' => array(), 'services' => array());
+
+ foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
+ $service = $this->resolveServiceDefinition($builder, $serviceId);
+
+ if ($service instanceof Alias) {
+ $services['aliases'][$serviceId] = $service;
+ } elseif ($service instanceof Definition) {
+ if (($showPrivate || $service->isPublic())) {
+ $services['definitions'][$serviceId] = $service;
+ }
+ } else {
+ $services['services'][$serviceId] = $service;
+ }
+ }
+
+ if (!empty($services['definitions'])) {
+ $this->write("\n\nDefinitions\n-----------\n");
+ foreach ($services['definitions'] as $id => $service) {
+ $this->write("\n");
+ $this->describeContainerDefinition($service, array('id' => $id));
+ }
+ }
+
+ if (!empty($services['aliases'])) {
+ $this->write("\n\nAliases\n-------\n");
+ foreach ($services['aliases'] as $id => $service) {
+ $this->write("\n");
+ $this->describeContainerAlias($service, array('id' => $id));
+ }
+ }
+
+ if (!empty($services['services'])) {
+ $this->write("\n\nServices\n--------\n");
+ foreach ($services['services'] as $id => $service) {
+ $this->write("\n");
+ $this->write(sprintf('- `%s`: `%s`', $id, get_class($service)));
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerDefinition(Definition $definition, array $options = array())
+ {
+ $output = '- Class: `'.$definition->getClass().'`'
+ ."\n".'- Scope: `'.$definition->getScope().'`'
+ ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no')
+ ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no');
+
+ if ($definition->getFile()) {
+ $output .= "\n".'- File: `'.$definition->getFile().'`';
+ }
+
+ if (!(isset($options['omit_tags']) && $options['omit_tags'])) {
+ foreach ($definition->getTags() as $tagName => $tagData) {
+ foreach ($tagData as $parameters) {
+ $output .= "\n".'- Tag: `'.$tagName.'`';
+ foreach ($parameters as $name => $value) {
+ $output .= "\n".' - '.ucfirst($name).': '.$value;
+ }
+ }
+ }
+ }
+
+ $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerAlias(Alias $alias, array $options = array())
+ {
+ $output = '- Service: `'.$alias.'`'
+ ."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no');
+
+ $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameter($parameter, array $options = array())
+ {
+ $this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter);
+ }
+
+ private function formatRouterConfig(array $array)
+ {
+ if (!count($array)) {
+ return 'NONE';
+ }
+
+ $string = '';
+ ksort($array);
+ foreach ($array as $name => $value) {
+ $string .= "\n".' - `'.$name.'`: '.$this->formatValue($value);
+ }
+
+ return $string;
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
new file mode 100644
index 0000000000000..67cb356bc39ba
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
@@ -0,0 +1,325 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+
+use Symfony\Component\Console\Helper\TableHelper;
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * @author Jean-François Simon
+ */
+class TextDescriptor extends Descriptor
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRouteCollection(RouteCollection $routes, array $options = array())
+ {
+ $showControllers = isset($options['show_controllers']) && $options['show_controllers'];
+ $headers = array('Name', 'Method', 'Scheme', 'Host', 'Path');
+ $table = new TableHelper();
+ $table->setLayout(TableHelper::LAYOUT_COMPACT);
+ $table->setHeaders($showControllers ? array_merge($headers, array('Controller')) : $headers);
+
+ foreach ($routes->all() as $name => $route) {
+ $row = array(
+ $name,
+ $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
+ $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
+ '' !== $route->getHost() ? $route->getHost() : 'ANY',
+ $route->getPath(),
+ );
+
+ if ($showControllers) {
+ $controller = $route->getDefault('_controller');
+ if ($controller instanceof \Closure) {
+ $controller = 'Closure';
+ } elseif (is_object($controller)) {
+ $controller = get_class($controller);
+ }
+ $row[] = $controller;
+ }
+
+ $table->addRow($row);
+ }
+
+ $this->writeText($this->formatSection('router', 'Current routes')."\n", $options);
+ $this->renderTable($table, !(isset($options['raw_output']) && $options['raw_output']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRoute(Route $route, array $options = array())
+ {
+ $requirements = $route->getRequirements();
+ unset($requirements['_scheme'], $requirements['_method']);
+
+ // fixme: values were originally written as raw
+ $description = array(
+ 'Path '.$route->getPath(),
+ 'Host '.('' !== $route->getHost() ? $route->getHost() : 'ANY'),
+ 'Scheme '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'),
+ 'Method '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'),
+ 'Class '.get_class($route),
+ 'Defaults '.$this->formatRouterConfig($route->getDefaults()),
+ 'Requirements '.$this->formatRouterConfig($requirements) ?: 'NO CUSTOM',
+ 'Options '.$this->formatRouterConfig($route->getOptions()),
+ 'Path-Regex '.$route->compile()->getRegex(),
+ );
+
+ if (isset($options['name'])) {
+ array_unshift($description, 'Name '.$options['name']);
+ array_unshift($description, $this->formatSection('router', sprintf('Route "%s"', $options['name'])));
+ }
+
+ if (null !== $route->compile()->getHostRegex()) {
+ $description[] = 'Host-Regex '.$route->compile()->getHostRegex();
+ }
+
+ $this->writeText(implode("\n", $description)."\n", $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
+ {
+ $table = new TableHelper();
+ $table->setLayout(TableHelper::LAYOUT_COMPACT);
+ $table->setHeaders(array('Parameter', 'Value'));
+
+ foreach ($this->sortParameters($parameters) as $parameter => $value) {
+ $table->addRow(array($parameter, $this->formatParameter($value)));
+ }
+
+ $this->writeText($this->formatSection('container', 'List of parameters')."\n", $options);
+ $this->renderTable($table, !(isset($options['raw_output']) && $options['raw_output']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
+ {
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $description = array($this->formatSection('container', 'Tagged services'));
+
+ foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
+ $description[] = $this->formatSection('tag', $tag);
+ $description = array_merge($description, array_keys($definitions));
+ $description[] = '';
+ }
+
+ $this->writeText(implode("\n", $description), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerService($service, array $options = array())
+ {
+ if (!isset($options['id'])) {
+ throw new \InvalidArgumentException('An "id" option must be provided.');
+ }
+
+ if ($service instanceof Alias) {
+ $this->describeContainerAlias($service, $options);
+ } elseif ($service instanceof Definition) {
+ $this->describeContainerDefinition($service, $options);
+ } else {
+ $description = $this->formatSection('container', sprintf('Information for service %s', $options['id']))
+ ."\n".sprintf('Service Id %s', isset($options['id']) ? $options['id'] : '-')
+ ."\n".sprintf('Class %s', get_class($service));
+
+ $this->writeText($description, $options);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
+ {
+ $showPrivate = isset($options['show_private']) && $options['show_private'];
+ $showTag = isset($options['tag']) ? $options['tag'] : null;
+
+ if ($showPrivate) {
+ $label = 'Public and private services';
+ } else {
+ $label = 'Public services';
+ }
+
+ if ($showTag) {
+ $label .= ' with tag '.$options['tag'].'';
+ }
+
+ $this->writeText($this->formatSection('container', $label)."\n", $options);
+
+ $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
+ $maxTags = array();
+
+ foreach ($serviceIds as $key => $serviceId) {
+ $definition = $this->resolveServiceDefinition($builder, $serviceId);
+ if ($definition instanceof Definition) {
+ // filter out private services unless shown explicitly
+ if (!$showPrivate && !$definition->isPublic()) {
+ unset($serviceIds[$key]);
+ continue;
+ }
+ if ($showTag) {
+ $tags = $definition->getTag($showTag);
+ foreach ($tags as $tag) {
+ foreach ($tag as $key => $value) {
+ if (!isset($maxTags[$key])) {
+ $maxTags[$key] = strlen($key);
+ }
+ if (strlen($value) > $maxTags[$key]) {
+ $maxTags[$key] = strlen($value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ $tagsCount = count($maxTags);
+ $tagsNames = array_keys($maxTags);
+
+ $table = new TableHelper();
+ $table->setLayout(TableHelper::LAYOUT_COMPACT);
+ $table->setHeaders(array_merge(array('Service ID'), $tagsNames, array('Scope', 'Class name')));
+
+ foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
+ $definition = $this->resolveServiceDefinition($builder, $serviceId);
+ if ($definition instanceof Definition) {
+ if ($showTag) {
+ foreach ($definition->getTag($showTag) as $key => $tag) {
+ $tagValues = array();
+ foreach ($tagsNames as $tagName) {
+ $tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : "";
+ }
+ if (0 === $key) {
+ $table->addRow(array_merge(array($serviceId), $tagValues, array($definition->getScope(), $definition->getClass())));
+ } else {
+ $table->addRow(array_merge(array(' "'), $tagValues, array('', '')));
+ }
+ }
+ } else {
+ $table->addRow(array($serviceId, $definition->getScope(), $definition->getClass()));
+ }
+ } elseif ($definition instanceof Alias) {
+ $alias = $definition;
+ $table->addRow(array_merge(array($serviceId, 'n/a', sprintf('alias for "%s"', $alias)), $tagsCount ? array_fill(0, $tagsCount, "") : array()));
+ } else {
+ // we have no information (happens with "service_container")
+ $table->addRow(array_merge(array($serviceId, '', get_class($definition)), $tagsCount ? array_fill(0, $tagsCount, "") : array()));
+ }
+ }
+
+ $this->renderTable($table);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerDefinition(Definition $definition, array $options = array())
+ {
+ $description = isset($options['id'])
+ ? array($this->formatSection('container', sprintf('Information for service %s', $options['id'])))
+ : array();
+
+ $description[] = sprintf('Service Id %s', isset($options['id']) ? $options['id'] : '-');
+ $description[] = sprintf('Class %s', $definition->getClass() ?: "-");
+
+ $tags = $definition->getTags();
+ if (count($tags)) {
+ $description[] = 'Tags';
+ foreach ($tags as $tagName => $tagData) {
+ foreach ($tagData as $parameters) {
+ $description[] = sprintf(' - %-30s (%s)', $tagName, implode(', ', array_map(function ($key, $value) {
+ return sprintf('%s: %s', $key, $value);
+ }, array_keys($parameters), array_values($parameters))));
+ }
+ }
+ } else {
+ $description[] = 'Tags -';
+ }
+
+ $description[] = sprintf('Scope %s', $definition->getScope());
+ $description[] = sprintf('Public %s', $definition->isPublic() ? 'yes' : 'no');
+ $description[] = sprintf('Synthetic %s', $definition->isSynthetic() ? 'yes' : 'no');
+ $description[] = sprintf('Required File %s', $definition->getFile() ? $definition->getFile() : '-');
+
+ $this->writeText(implode("\n", $description)."\n", $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerAlias(Alias $alias, array $options = array())
+ {
+ $this->writeText(sprintf('This service is an alias for the service %s', (string) $alias), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameter($parameter, array $options = array())
+ {
+ $this->writeText($this->formatParameter($parameter), $options);
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return string
+ */
+ private function formatRouterConfig(array $array)
+ {
+ $string = '';
+ ksort($array);
+ foreach ($array as $name => $value) {
+ $string .= ($string ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
+ }
+
+ return $string;
+ }
+
+ /**
+ * @param string $section
+ * @param string $message
+ *
+ * @return string
+ */
+ private function formatSection($section, $message)
+ {
+ return sprintf('[%s] %s', $section, $message);
+ }
+
+ /**
+ * @param string $content
+ * @param array $options
+ */
+ private function writeText($content, array $options = array())
+ {
+ $this->write(
+ isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
+ isset($options['raw_output']) ? !$options['raw_output'] : true
+ );
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
new file mode 100644
index 0000000000000..73f693c3f715a
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
@@ -0,0 +1,396 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
+
+use Symfony\Component\DependencyInjection\Alias;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * @author Jean-François Simon
+ */
+class XmlDescriptor extends Descriptor
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRouteCollection(RouteCollection $routes, array $options = array())
+ {
+ $this->writeDocument($this->getRouteCollectionDocument($routes));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeRoute(Route $route, array $options = array())
+ {
+ $this->writeDocument($this->getRouteDocument($route, isset($options['name']) ? $options['name'] : null));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
+ {
+ $this->writeDocument($this->getContainerParametersDocument($parameters));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
+ {
+ $this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_private']) && $options['show_private']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerService($service, array $options = array())
+ {
+ if (!isset($options['id'])) {
+ throw new \InvalidArgumentException('An "id" option must be provided.');
+ }
+
+ $this->writeDocument($this->getContainerServiceDocument($service, $options['id']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
+ {
+ $this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerDefinition(Definition $definition, array $options = array())
+ {
+ $this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerAlias(Alias $alias, array $options = array())
+ {
+ $this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function describeContainerParameter($parameter, array $options = array())
+ {
+ $this->writeDocument($this->getContainerParameterDocument($parameter, $options));
+ }
+
+ /**
+ * Writes DOM document.
+ *
+ * @param \DOMDocument $dom
+ *
+ * @return \DOMDocument|string
+ */
+ private function writeDocument(\DOMDocument $dom)
+ {
+ $dom->formatOutput = true;
+ $this->write($dom->saveXML());
+ }
+
+ /**
+ * @param RouteCollection $routes
+ *
+ * @return \DOMDocument
+ */
+ private function getRouteCollectionDocument(RouteCollection $routes)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($routesXML = $dom->createElement('routes'));
+
+ foreach ($routes->all() as $name => $route) {
+ $routeXML = $this->getRouteDocument($route, $name);
+ $routesXML->appendChild($routesXML->ownerDocument->importNode($routeXML->childNodes->item(0), true));
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param Route $route
+ * @param string|null $name
+ *
+ * @return \DOMDocument
+ */
+ private function getRouteDocument(Route $route, $name = null)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($routeXML = $dom->createElement('route'));
+
+ if ($name) {
+ $routeXML->setAttribute('name', $name);
+ }
+
+ $routeXML->setAttribute('class', get_class($route));
+
+ $routeXML->appendChild($pathXML = $dom->createElement('path'));
+ $pathXML->setAttribute('regex', $route->compile()->getRegex());
+ $pathXML->appendChild(new \DOMText($route->getPath()));
+
+ if ('' !== $route->getHost()) {
+ $routeXML->appendChild($hostXML = $dom->createElement('host'));
+ $hostXML->setAttribute('regex', $route->compile()->getHostRegex());
+ $hostXML->appendChild(new \DOMText($route->getHost()));
+ }
+
+ foreach ($route->getSchemes() as $scheme) {
+ $routeXML->appendChild($schemeXML = $dom->createElement('scheme'));
+ $schemeXML->appendChild(new \DOMText($scheme));
+ }
+
+ foreach ($route->getMethods() as $method) {
+ $routeXML->appendChild($methodXML = $dom->createElement('method'));
+ $methodXML->appendChild(new \DOMText($method));
+ }
+
+ if (count($route->getDefaults())) {
+ $routeXML->appendChild($defaultsXML = $dom->createElement('defaults'));
+ foreach ($route->getDefaults() as $attribute => $value) {
+ $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
+ $defaultXML->setAttribute('key', $attribute);
+ $defaultXML->appendChild(new \DOMText($this->formatValue($value)));
+ }
+ }
+
+ $requirements = $route->getRequirements();
+ unset($requirements['_scheme'], $requirements['_method']);
+ if (count($requirements)) {
+ $routeXML->appendChild($requirementsXML = $dom->createElement('requirements'));
+ foreach ($requirements as $attribute => $pattern) {
+ $requirementsXML->appendChild($requirementXML = $dom->createElement('requirement'));
+ $requirementXML->setAttribute('key', $attribute);
+ $requirementXML->appendChild(new \DOMText($pattern));
+ }
+ }
+
+ if (count($route->getOptions())) {
+ $routeXML->appendChild($optionsXML = $dom->createElement('options'));
+ foreach ($route->getOptions() as $name => $value) {
+ $optionsXML->appendChild($optionXML = $dom->createElement('option'));
+ $optionXML->setAttribute('key', $name);
+ $optionXML->appendChild(new \DOMText($this->formatValue($value)));
+ }
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param ParameterBag $parameters
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerParametersDocument(ParameterBag $parameters)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($parametersXML = $dom->createElement('parameters'));
+
+ foreach ($this->sortParameters($parameters) as $key => $value) {
+ $parametersXML->appendChild($parameterXML = $dom->createElement('parameter'));
+ $parameterXML->setAttribute('key', $key);
+ $parameterXML->appendChild(new \DOMText($this->formatParameter($value)));
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param ContainerBuilder $builder
+ * @param bool $showPrivate
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($containerXML = $dom->createElement('container'));
+
+ foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
+ $containerXML->appendChild($tagXML = $dom->createElement('tag'));
+ $tagXML->setAttribute('name', $tag);
+
+ foreach ($definitions as $serviceId => $definition) {
+ $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true);
+ $tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true));
+ }
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param mixed $service
+ * @param string $id
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerServiceDocument($service, $id)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+
+ if ($service instanceof Alias) {
+ $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true));
+ } elseif ($service instanceof Definition) {
+ $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id)->childNodes->item(0), true));
+ } else {
+ $dom->appendChild($serviceXML = $dom->createElement('service'));
+ $serviceXML->setAttribute('id', $id);
+ $serviceXML->setAttribute('class', get_class($service));
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param ContainerBuilder $builder
+ * @param string|null $tag
+ * @param bool $showPrivate
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($containerXML = $dom->createElement('container'));
+
+ $serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds();
+
+ foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
+ $service = $this->resolveServiceDefinition($builder, $serviceId);
+
+ if ($service instanceof Definition && !($showPrivate || $service->isPublic())) {
+ continue;
+ }
+
+ $serviceXML = $this->getContainerServiceDocument($service, $serviceId);
+ $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true));
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param Definition $definition
+ * @param string|null $id
+ * @param bool $omitTags
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($serviceXML = $dom->createElement('definition'));
+
+ if ($id) {
+ $serviceXML->setAttribute('id', $id);
+ }
+
+ $serviceXML->setAttribute('class', $definition->getClass());
+
+ if ($definition->getFactoryClass()) {
+ $serviceXML->setAttribute('factory-class', $definition->getFactoryClass());
+ }
+
+ if ($definition->getFactoryService()) {
+ $serviceXML->setAttribute('factory-service', $definition->getFactoryService());
+ }
+
+ if ($definition->getFactoryMethod()) {
+ $serviceXML->setAttribute('factory-method', $definition->getFactoryMethod());
+ }
+
+ $serviceXML->setAttribute('scope', $definition->getScope());
+ $serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false');
+ $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false');
+ $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false');
+ $serviceXML->setAttribute('synchronized', $definition->isSynchronized() ? 'true' : 'false');
+ $serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false');
+ $serviceXML->setAttribute('file', $definition->getFile());
+
+ if (!$omitTags) {
+ $tags = $definition->getTags();
+
+ if (count($tags) > 0) {
+ $serviceXML->appendChild($tagsXML = $dom->createElement('tags'));
+ foreach ($tags as $tagName => $tagData) {
+ foreach ($tagData as $parameters) {
+ $tagsXML->appendChild($tagXML = $dom->createElement('tag'));
+ $tagXML->setAttribute('name', $tagName);
+ foreach ($parameters as $name => $value) {
+ $tagXML->appendChild($parameterXML = $dom->createElement('parameter'));
+ $parameterXML->setAttribute('name', $name);
+ $parameterXML->appendChild(new \DOMText($this->formatParameter($value)));
+ }
+ }
+ }
+ }
+ }
+
+ return $dom;
+ }
+
+ /**
+ * @param Alias $alias
+ * @param string|null $id
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerAliasDocument(Alias $alias, $id = null)
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($aliasXML = $dom->createElement('alias'));
+
+ if ($id) {
+ $aliasXML->setAttribute('id', $id);
+ }
+
+ $aliasXML->setAttribute('service', (string) $alias);
+ $aliasXML->setAttribute('public', $alias->isPublic() ? 'true' : 'false');
+
+ return $dom;
+ }
+
+ /**
+ * @param string $parameter
+ * @param array $options
+ *
+ * @return \DOMDocument
+ */
+ private function getContainerParameterDocument($parameter, $options = array())
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->appendChild($parameterXML = $dom->createElement('parameter'));
+
+ if (isset($options['parameter'])) {
+ $parameterXML->setAttribute('key', $options['parameter']);
+ }
+
+ $parameterXML->appendChild(new \DOMText($this->formatParameter($parameter)));
+
+ return $dom;
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php b/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php
new file mode 100644
index 0000000000000..acb2a517ab931
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Console\Helper;
+
+use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor;
+use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor;
+use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor;
+use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor;
+use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper;
+
+/**
+ * @author Jean-François Simon
+ */
+class DescriptorHelper extends BaseDescriptorHelper
+{
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ $this
+ ->register('txt', new TextDescriptor())
+ ->register('xml', new XmlDescriptor())
+ ->register('json', new JsonDescriptor())
+ ->register('md', new MarkdownDescriptor())
+ ;
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
index f4f1fbd99d063..a452ac5bb27c2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
@@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilder;
@@ -61,7 +62,7 @@ public function generateUrl($route, $parameters = array(), $referenceType = UrlG
public function forward($controller, array $path = array(), array $query = array())
{
$path['_controller'] = $controller;
- $subRequest = $this->container->get('request')->duplicate($query, null, $path);
+ $subRequest = $this->container->get('request_stack')->getCurrentRequest()->duplicate($query, null, $path);
return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
@@ -139,8 +140,8 @@ public function stream($view, array $parameters = array(), StreamedResponse $res
*
* throw $this->createNotFoundException('Page not found!');
*
- * @param string $message A message
- * @param \Exception $previous The previous exception
+ * @param string $message A message
+ * @param \Exception|null $previous The previous exception
*
* @return NotFoundHttpException
*/
@@ -149,6 +150,23 @@ public function createNotFoundException($message = 'Not Found', \Exception $prev
return new NotFoundHttpException($message, $previous);
}
+ /**
+ * Returns an AccessDeniedException.
+ *
+ * This will result in a 403 response code. Usage example:
+ *
+ * throw $this->createAccessDeniedException('Unable to access this page!');
+ *
+ * @param string $message A message
+ * @param \Exception|null $previous The previous exception
+ *
+ * @return AccessDeniedException
+ */
+ public function createAccessDeniedException($message = 'Access Denied', \Exception $previous = null)
+ {
+ return new AccessDeniedException($message, $previous);
+ }
+
/**
* Creates and returns a Form instance from the type of the form.
*
@@ -180,10 +198,14 @@ public function createFormBuilder($data = null, array $options = array())
* Shortcut to return the request service.
*
* @return Request
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in 3.0. Ask
+ * Symfony to inject the Request object into your controller
+ * method instead by type hinting it in the method's signature.
*/
public function getRequest()
{
- return $this->container->get('request');
+ return $this->container->get('request_stack')->getCurrentRequest();
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php
new file mode 100644
index 0000000000000..3c96761ba3a6c
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * AddConsoleCommandPass.
+ *
+ * @author Grégoire Pineau
+ */
+class AddConsoleCommandPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ $commandServices = $container->findTaggedServiceIds('console.command');
+
+ foreach ($commandServices as $id => $tags) {
+ $definition = $container->getDefinition($id);
+
+ if (!$definition->isPublic()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be public.', $id));
+ }
+
+ if ($definition->isAbstract()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must not be abstract.', $id));
+ }
+
+ $class = $container->getParameterBag()->resolveValue($definition->getClass());
+ $r = new \ReflectionClass($class);
+ if (!$r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command')) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be a subclass of "Symfony\\Component\\Console\\Command\\Command".', $id));
+ }
+ $container->setAlias('console.command.'.strtolower(str_replace('\\', '_', $class)), $id);
+ }
+
+ $container->setParameter('console.command.ids', array_keys($commandServices));
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php
index bf9f33811199a..6f58fc21bebf1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php
@@ -19,15 +19,17 @@ class AddValidatorInitializersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
- if (!$container->hasDefinition('validator')) {
+ if (!$container->hasDefinition('validator.builder')) {
return;
}
+ $validatorBuilder = $container->getDefinition('validator.builder');
+
$initializers = array();
foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) {
$initializers[] = new Reference($id);
}
- $container->getDefinition('validator')->replaceArgument(4, $initializers);
+ $validatorBuilder->addMethodCall('addObjectInitializers', array($initializers));
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index c7f8a4de4c752..d430e302fc5c2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -36,7 +36,7 @@ public function getConfigTreeBuilder()
->children()
->scalarNode('secret')->end()
->scalarNode('http_method_override')
- ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests.")
+ ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead")
->defaultTrue()
->end()
->arrayNode('trusted_proxies')
@@ -78,12 +78,14 @@ public function getConfigTreeBuilder()
->end()
;
+ $this->addCsrfSection($rootNode);
$this->addFormSection($rootNode);
$this->addEsiSection($rootNode);
$this->addFragmentsSection($rootNode);
$this->addProfilerSection($rootNode);
$this->addRouterSection($rootNode);
$this->addSessionSection($rootNode);
+ $this->addRequestSection($rootNode);
$this->addTemplatingSection($rootNode);
$this->addTranslatorSection($rootNode);
$this->addValidationSection($rootNode);
@@ -93,6 +95,23 @@ public function getConfigTreeBuilder()
return $treeBuilder;
}
+ private function addCsrfSection(ArrayNodeDefinition $rootNode)
+ {
+ $rootNode
+ ->children()
+ ->arrayNode('csrf_protection')
+ ->canBeEnabled()
+ ->children()
+ ->scalarNode('field_name')
+ ->defaultValue('_token')
+ ->info('Deprecated since 2.4, to be removed in 3.0. Use form.csrf_protection.field_name instead')
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+ }
+
private function addFormSection(ArrayNodeDefinition $rootNode)
{
$rootNode
@@ -100,11 +119,17 @@ private function addFormSection(ArrayNodeDefinition $rootNode)
->arrayNode('form')
->info('form configuration')
->canBeEnabled()
- ->end()
- ->arrayNode('csrf_protection')
- ->canBeDisabled()
->children()
- ->scalarNode('field_name')->defaultValue('_token')->end()
+ ->arrayNode('csrf_protection')
+ ->treatFalseLike(array('enabled' => false))
+ ->treatTrueLike(array('enabled' => true))
+ ->treatNullLike(array('enabled' => true))
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled
+ ->scalarNode('field_name')->defaultNull()->end()
+ ->end()
+ ->end()
->end()
->end()
->end()
@@ -156,13 +181,17 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode)
->arrayNode('matcher')
->canBeUnset()
->performNoDeepMerging()
+ ->fixXmlConfig('ip')
->children()
- ->scalarNode('ip')->end()
->scalarNode('path')
->info('use the urldecoded format')
->example('^/path to resource/')
->end()
->scalarNode('service')->end()
+ ->arrayNode('ips')
+ ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
+ ->prototype('scalar')->end()
+ ->end()
->end()
->end()
->end()
@@ -215,9 +244,42 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
->booleanNode('cookie_secure')->end()
->booleanNode('cookie_httponly')->end()
->scalarNode('gc_divisor')->end()
- ->scalarNode('gc_probability')->end()
+ ->scalarNode('gc_probability')->defaultValue(1)->end()
->scalarNode('gc_maxlifetime')->end()
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
+ ->integerNode('metadata_update_threshold')
+ ->defaultValue('0')
+ ->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed')
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+ }
+
+ private function addRequestSection(ArrayNodeDefinition $rootNode)
+ {
+ $rootNode
+ ->children()
+ ->arrayNode('request')
+ ->info('request configuration')
+ ->canBeUnset()
+ ->fixXmlConfig('format')
+ ->children()
+ ->arrayNode('formats')
+ ->useAttributeAsKey('name')
+ ->prototype('array')
+ ->beforeNormalization()
+ ->ifTrue(function ($v) { return is_array($v) && isset($v['mime_type']); })
+ ->then(function ($v) { return $v['mime_type']; })
+ ->end()
+ ->beforeNormalization()
+ ->ifTrue(function ($v) { return !is_array($v); })
+ ->then(function ($v) { return array($v); })
+ ->end()
+ ->prototype('scalar')->end()
+ ->end()
+ ->end()
->end()
->end()
->end()
@@ -382,10 +444,43 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
->children()
->scalarNode('cache')->end()
->booleanNode('enable_annotations')->defaultFalse()->end()
+ ->arrayNode('static_method')
+ ->defaultValue(array('loadValidatorMetadata'))
+ ->prototype('scalar')->end()
+ ->treatFalseLike(array())
+ ->validate()
+ ->ifTrue(function ($v) { return !is_array($v); })
+ ->then(function ($v) { return (array) $v; })
+ ->end()
+ ->end()
->scalarNode('translation_domain')->defaultValue('validators')->end()
+ ->booleanNode('strict_email')->defaultFalse()->end()
+ ->enumNode('api')
+ ->values(array('2.4', '2.5', '2.5-bc', 'auto'))
+ ->beforeNormalization()
+ // XML/YAML parse as numbers, not as strings
+ ->ifTrue(function ($v) { return is_scalar($v); })
+ ->then(function ($v) { return (string) $v; })
+ ->end()
+ ->end()
->end()
->end()
->end()
+ ->validate()
+ ->ifTrue(function ($v) { return !isset($v['validation']['api']) || 'auto' === $v['validation']['api']; })
+ ->then(function ($v) {
+ // This condition is duplicated in ValidatorBuilder. This
+ // duplication is necessary in order to know the desired
+ // API version already during container configuration
+ // (to adjust service classes etc.)
+ // See https://github.com/symfony/symfony/issues/11580
+ $v['validation']['api'] = PHP_VERSION_ID < 50309
+ ? '2.4'
+ : '2.5-bc';
+
+ return $v;
+ })
+ ->end()
;
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 0988c5fe1b908..8b87c2fd235e7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -12,7 +12,9 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
+use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\Resource\FileResource;
@@ -20,6 +22,7 @@
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Validator\Validation;
/**
* FrameworkExtension.
@@ -29,6 +32,9 @@
*/
class FrameworkExtension extends Extension
{
+ private $formConfigEnabled = false;
+ private $sessionConfigEnabled = false;
+
/**
* Responds to the app.config configuration parameter.
*
@@ -48,17 +54,22 @@ public function load(array $configs, ContainerBuilder $container)
// will be used and everything will still work as expected.
$loader->load('translation.xml');
+ // Property access is used by both the Form and the Validator component
+ $loader->load('property_access.xml');
+
$loader->load('debug_prod.xml');
if ($container->getParameter('kernel.debug')) {
$loader->load('debug.xml');
- // only HttpKernel needs the debug event dispatcher
$definition = $container->findDefinition('http_kernel');
- $arguments = $definition->getArguments();
- $arguments[0] = new Reference('debug.event_dispatcher');
- $arguments[2] = new Reference('debug.controller_resolver');
- $definition->setArguments($arguments);
+ $definition->replaceArgument(2, new Reference('debug.controller_resolver'));
+
+ // replace the regular event_dispatcher service with the debug one
+ $definition = $container->findDefinition('event_dispatcher');
+ $definition->setPublic(false);
+ $container->setDefinition('debug.event_dispatcher.parent', $definition);
+ $container->setAlias('event_dispatcher', 'debug.event_dispatcher');
}
$configuration = $this->getConfiguration($configs, $container);
@@ -78,14 +89,32 @@ public function load(array $configs, ContainerBuilder $container)
}
if (isset($config['session'])) {
+ $this->sessionConfigEnabled = true;
$this->registerSessionConfiguration($config['session'], $container, $loader);
}
+ if (isset($config['request'])) {
+ $this->registerRequestConfiguration($config['request'], $container, $loader);
+ }
+
+ $loader->load('security.xml');
+
if ($this->isConfigEnabled($container, $config['form'])) {
+ $this->formConfigEnabled = true;
$this->registerFormConfiguration($config, $container, $loader);
$config['validation']['enabled'] = true;
+
+ if (!class_exists('Symfony\Component\Validator\Validator')) {
+ throw new LogicException('The Validator component is required to use the Form component.');
+ }
+
+ if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) {
+ $config['csrf_protection']['enabled'] = true;
+ }
}
+ $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
+
if (isset($config['templating'])) {
$this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader);
}
@@ -143,17 +172,20 @@ public function load(array $configs, ContainerBuilder $container)
private function registerFormConfiguration($config, ContainerBuilder $container, XmlFileLoader $loader)
{
$loader->load('form.xml');
- if ($this->isConfigEnabled($container, $config['csrf_protection'])) {
- if (!isset($config['session'])) {
- throw new \LogicException('CSRF protection needs that sessions are enabled.');
- }
- if (!isset($config['secret'])) {
- throw new \LogicException('CSRF protection needs a secret to be set.');
- }
+ if (null === $config['form']['csrf_protection']['enabled']) {
+ $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled'];
+ }
+
+ if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) {
$loader->load('form_csrf.xml');
$container->setParameter('form.type_extension.csrf.enabled', true);
- $container->setParameter('form.type_extension.csrf.field_name', $config['csrf_protection']['field_name']);
+
+ if (null !== $config['form']['csrf_protection']['field_name']) {
+ $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']);
+ } else {
+ $container->setParameter('form.type_extension.csrf.field_name', $config['csrf_protection']['field_name']);
+ }
} else {
$container->setParameter('form.type_extension.csrf.enabled', false);
}
@@ -210,6 +242,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
return;
}
+ if (true === $this->formConfigEnabled) {
+ $loader->load('form_debug.xml');
+ }
+
$loader->load('profiling.xml');
$loader->load('collectors.xml');
@@ -241,7 +277,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
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'])) {
+ } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path']) || isset($config['matcher']['ips'])) {
$definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher');
$definition->setPublic(false);
@@ -249,6 +285,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
$definition->addMethodCall('matchIp', array($config['matcher']['ip']));
}
+ if (isset($config['matcher']['ips'])) {
+ $definition->addMethodCall('matchIps', array($config['matcher']['ips']));
+ }
+
if (isset($config['matcher']['path'])) {
$definition->addMethodCall('matchPath', array($config['matcher']['path']));
}
@@ -321,7 +361,14 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
} else {
- $container->setAlias('session.handler', $config['handler_id']);
+ $handlerId = $config['handler_id'];
+
+ if ($config['metadata_update_threshold'] > 0) {
+ $container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId));
+ $handlerId = 'session.handler.write_check';
+ }
+
+ $container->setAlias('session.handler', $handlerId);
}
$container->setParameter('session.save_path', $config['save_path']);
@@ -341,6 +388,26 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
$container->findDefinition('session.storage')->getClass(),
));
}
+
+ $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']);
+ }
+
+ /**
+ * Loads the request configuration.
+ *
+ * @param array $config A session configuration array
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ * @param XmlFileLoader $loader An XmlFileLoader instance
+ */
+ private function registerRequestConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
+ {
+ if ($config['formats']) {
+ $loader->load('request.xml');
+ $container
+ ->getDefinition('request.add_request_formats_listener')
+ ->replaceArgument(0, $config['formats'])
+ ;
+ }
}
/**
@@ -370,6 +437,15 @@ private function registerTemplatingConfiguration(array $config, $ide, ContainerB
if ($container->getParameter('kernel.debug')) {
$loader->load('templating_debug.xml');
+ $logger = new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE);
+
+ $container->getDefinition('templating.loader.cache')
+ ->addTag('monolog.logger', array('channel' => 'templating'))
+ ->addMethodCall('setLogger', array($logger));
+ $container->getDefinition('templating.loader.chain')
+ ->addTag('monolog.logger', array('channel' => 'templating'))
+ ->addMethodCall('setLogger', array($logger));
+
$container->setDefinition('templating.engine.php', $container->findDefinition('debug.templating.engine.php'));
$container->setAlias('debug.templating.engine.php', 'templating.engine.php');
}
@@ -554,7 +630,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) {
$r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException');
- $dirs[] = dirname($r->getFilename()).'/../../Resources/translations';
+ $dirs[] = dirname($r->getFilename()).'/../Resources/translations';
}
$overridePath = $container->getParameter('kernel.root_dir').'/Resources/%s/translations';
foreach ($container->getParameter('kernel.bundles') as $bundle => $class) {
@@ -606,25 +682,63 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
$loader->load('validator.xml');
+ $validatorBuilder = $container->getDefinition('validator.builder');
+
$container->setParameter('validator.translation_domain', $config['translation_domain']);
- $container->setParameter('validator.mapping.loader.xml_files_loader.mapping_files', $this->getValidatorXmlMappingFiles($container));
- $container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $this->getValidatorYamlMappingFiles($container));
+
+ $xmlMappings = $this->getValidatorXmlMappingFiles($container);
+ $yamlMappings = $this->getValidatorYamlMappingFiles($container);
+
+ if (count($xmlMappings) > 0) {
+ $validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings));
+ }
+
+ if (count($yamlMappings) > 0) {
+ $validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings));
+ }
+
+ $definition = $container->findDefinition('validator.email');
+ $definition->replaceArgument(0, $config['strict_email']);
if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) {
- $loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain');
- $arguments = $loaderChain->getArguments();
- array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader'));
- $loaderChain->setArguments($arguments);
+ $validatorBuilder->addMethodCall('enableAnnotationMapping', array(new Reference('annotation_reader')));
+ }
+
+ if (array_key_exists('static_method', $config) && $config['static_method']) {
+ foreach ($config['static_method'] as $methodName) {
+ $validatorBuilder->addMethodCall('addMethodMapping', array($methodName));
+ }
}
if (isset($config['cache'])) {
- $container->getDefinition('validator.mapping.class_metadata_factory')
- ->replaceArgument(1, new Reference('validator.mapping.cache.'.$config['cache']));
$container->setParameter(
'validator.mapping.cache.prefix',
- 'validator_'.md5($container->getParameter('kernel.root_dir'))
+ 'validator_'.hash('sha256', $container->getParameter('kernel.root_dir'))
);
+
+ $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference('validator.mapping.cache.'.$config['cache'])));
+ }
+
+ switch ($config['api']) {
+ case '2.4':
+ $api = Validation::API_VERSION_2_4;
+ break;
+ case '2.5':
+ $api = Validation::API_VERSION_2_5;
+ // the validation class needs to be changed only for the 2.5 api since the deprecated interface is
+ // set as the default interface
+ $container->setParameter('validator.class', 'Symfony\Component\Validator\Validator\ValidatorInterface');
+ break;
+ default:
+ $api = Validation::API_VERSION_2_5_BC;
+ break;
}
+
+ $validatorBuilder->addMethodCall('setApiVersion', array($api));
+
+ // You can use this parameter to check the API version in your own
+ // bundle extension classes
+ $container->setParameter('validator.api', $api);
}
private function getValidatorXmlMappingFiles(ContainerBuilder $container)
@@ -689,6 +803,29 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde
}
}
+ /**
+ * Loads the security configuration.
+ *
+ * @param array $config A CSRF configuration array
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ * @param XmlFileLoader $loader An XmlFileLoader instance
+ *
+ * @throws \LogicException
+ */
+ private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
+ {
+ if (!$this->isConfigEnabled($container, $config)) {
+ return;
+ }
+
+ if (!$this->sessionConfigEnabled) {
+ throw new \LogicException('CSRF protection needs sessions to be enabled.');
+ }
+
+ // Enable services for CSRF protection (even without forms)
+ $loader->load('security_csrf.xml');
+ }
+
/**
* Returns the base path for the XSD files.
*
diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php
index 014ecb3ebde27..6c248d8a42880 100644
--- a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php
+++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php
@@ -11,18 +11,15 @@
namespace Symfony\Bundle\FrameworkBundle\EventListener;
+use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener;
use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Sets the session in the request.
*
- * @author Johannes M. Schmitt
+ * @author Fabien Potencier
*/
-class SessionListener implements EventSubscriberInterface
+class SessionListener extends BaseSessionListener
{
/**
* @var ContainerInterface
@@ -34,24 +31,12 @@ public function __construct(ContainerInterface $container)
$this->container = $container;
}
- public function onKernelRequest(GetResponseEvent $event)
+ protected function getSession()
{
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
+ if (!$this->container->has('session')) {
return;
}
- $request = $event->getRequest();
- if (!$this->container->has('session') || $request->hasSession()) {
- return;
- }
-
- $request->setSession($this->container->get('session'));
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- KernelEvents::REQUEST => array('onKernelRequest', 128),
- );
+ return $this->container->get('session');
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
index d0360c3e2c969..b32faa2f05668 100644
--- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
+++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
@@ -11,23 +11,15 @@
namespace Symfony\Bundle\FrameworkBundle\EventListener;
-use Symfony\Component\HttpFoundation\Cookie;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\EventListener\TestSessionListener as BaseTestSessionListener;
use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* TestSessionListener.
*
- * Saves session in test environment.
- *
- * @author Bulat Shakirzyanov
* @author Fabien Potencier
*/
-class TestSessionListener implements EventSubscriberInterface
+class TestSessionListener extends BaseTestSessionListener
{
protected $container;
@@ -36,50 +28,12 @@ public function __construct(ContainerInterface $container)
$this->container = $container;
}
- public function onKernelRequest(GetResponseEvent $event)
+ protected function getSession()
{
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
- return;
- }
-
- // bootstrap the session
if (!$this->container->has('session')) {
return;
}
- $session = $this->container->get('session');
- $cookies = $event->getRequest()->cookies;
-
- if ($cookies->has($session->getName())) {
- $session->setId($cookies->get($session->getName()));
- }
- }
-
- /**
- * Checks if session was initialized and saves if current request is master
- * Runs on 'kernel.response' in test environment
- *
- * @param FilterResponseEvent $event
- */
- public function onKernelResponse(FilterResponseEvent $event)
- {
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
- return;
- }
-
- $session = $event->getRequest()->getSession();
- if ($session && $session->isStarted()) {
- $session->save();
- $params = session_get_cookie_params();
- $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
- }
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- KernelEvents::REQUEST => array('onKernelRequest', 192),
- KernelEvents::RESPONSE => array('onKernelResponse', -128),
- );
+ return $this->container->get('session');
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
index 3aaf22a623ab0..30d06fd7af952 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -13,6 +13,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
+use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
@@ -29,9 +30,9 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Scope;
+use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Bundle\Bundle;
-use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass;
/**
* Bundle.
@@ -71,6 +72,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new TemplatingPass());
$container->addCompilerPass(new AddConstraintValidatorsPass());
$container->addCompilerPass(new AddValidatorInitializersPass());
+ $container->addCompilerPass(new AddConsoleCommandPass());
$container->addCompilerPass(new FormPass());
$container->addCompilerPass(new TranslatorPass());
$container->addCompilerPass(new AddCacheWarmerPass());
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
index 0e07cdb5d9f63..def9aea920699 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
@@ -13,6 +13,8 @@
Symfony\Component\HttpKernel\DataCollector\TimeDataCollector
Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector
Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector
+ Symfony\Component\Form\Extension\DataCollector\FormDataCollector
+ Symfony\Component\Form\Extension\DataCollector\FormDataExtractor
@@ -32,6 +34,7 @@
+
@@ -43,6 +46,7 @@
+
@@ -53,5 +57,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
index e7d1c3c7d47b5..c457e4f903a36 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
@@ -9,6 +9,7 @@
Symfony\Component\Stopwatch\Stopwatch
%kernel.cache_dir%/%kernel.container_class%.xml
Symfony\Component\HttpKernel\Controller\TraceableControllerResolver
+ Symfony\Component\HttpKernel\EventListener\DebugHandlersListener
@@ -16,10 +17,9 @@
-
+
-
@@ -33,5 +33,20 @@
deprecation
+
+
+
+
+ scream
+
+
+
+
+
+
+
+ terminateWithException
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml
index c0202e1516a7c..3038f40e97b46 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml
@@ -7,7 +7,6 @@
Symfony\Component\HttpKernel\HttpCache\Esi
Symfony\Component\HttpKernel\EventListener\EsiListener
- Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer
@@ -17,13 +16,5 @@
-
-
-
-
-
-
- %fragment.path%
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
index bf63332ced1e6..23d8ec260cafb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
@@ -10,7 +10,7 @@
Symfony\Component\Form\FormFactory
Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension
Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser
- Symfony\Component\PropertyAccess\PropertyAccessor
+ Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler
@@ -54,9 +54,6 @@
-
-
-
@@ -152,9 +149,13 @@
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
index 57cad204aa386..4903004f32bac 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
@@ -4,14 +4,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
- Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider
-
-
-
-
- %kernel.secret%
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml
new file mode 100644
index 0000000000000..5d4faac4acf71
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy
+ Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml
index 595db6d2741c6..a6320b7f04219 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml
@@ -9,6 +9,7 @@
Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer
Symfony\Bundle\FrameworkBundle\Fragment\ContainerAwareHIncludeFragmentRenderer
+ Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer
/_fragment
@@ -16,7 +17,7 @@
%kernel.debug%
-
+
@@ -33,5 +34,13 @@
%fragment.renderer.hinclude.global_template%
%fragment.path%
+
+
+
+
+
+
+ %fragment.path%
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
index a8666606e150f..cee86b3b72cfa 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml
@@ -29,6 +29,7 @@
%profiler_listener.only_exceptions%
%profiler_listener.only_master_requests%
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml
new file mode 100644
index 0000000000000..18026144e4573
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Symfony\Component\PropertyAccess\PropertyAccessor
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml
new file mode 100644
index 0000000000000..cc836c35e3b0f
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
index 9e21db4519151..6b2f7c9688699 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
@@ -94,7 +94,7 @@
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 17b817c325303..4d37c96e1314a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -7,6 +7,15 @@
+
+
+
+
+
+
+
+
+
@@ -16,6 +25,7 @@
+
@@ -31,9 +41,17 @@
+
+
+
+
+
+
+
+
@@ -93,6 +111,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -130,9 +161,15 @@
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
new file mode 100644
index 0000000000000..2b6307a9ef5e1
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Symfony\Component\Security\Core\Util\SecureRandom
+
+
+
+
+
+
+ %kernel.cache_dir%/secure_random.seed
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml
new file mode 100644
index 0000000000000..143c8a68efe83
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+ Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator
+ Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage
+ Symfony\Component\Security\Csrf\CsrfTokenManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
index 674e28f1c98a0..4457dbb27669c 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
@@ -12,6 +12,7 @@
Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer
Symfony\Component\HttpKernel\Config\FileLocator
Symfony\Component\HttpKernel\UriSigner
+ Symfony\Component\HttpFoundation\RequestStack
@@ -23,8 +24,11 @@
+
+
+
@@ -39,6 +43,8 @@
YourRequestClass to the Kernel.
This service definition only defines the scope of the request.
It is used to check references scope.
+
+ This service is deprecated, you should use the request_stack service instead.
-->
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
index a6a129ee0a73e..591f92e456ddc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
@@ -8,10 +8,13 @@
Symfony\Component\HttpFoundation\Session\Session
Symfony\Component\HttpFoundation\Session\Flash\FlashBag
Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
+ Symfony\Component\HttpFoundation\Session\Storage\MetadataBag
+ _sf2_meta
Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage
Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage
Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage
Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler
+ Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler
Symfony\Bundle\FrameworkBundle\EventListener\SessionListener
@@ -22,13 +25,20 @@
+
+ %session.metadata.storage_key%
+ %session.metadata.update_threshold%
+
+
%session.storage.options%
+
+
@@ -37,12 +47,16 @@
%kernel.cache_dir%/sessions
+ MOCKSESSID
+
%session.save_path%
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml
index b06c9af82f952..59da78fc41689 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml
@@ -52,11 +52,9 @@
%templating.loader.cache.path%
-
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml
index 0c6e0dcba4dc4..49e79416e1eba 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml
@@ -5,15 +5,10 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- Symfony\Bundle\FrameworkBundle\Templating\Debugger
Symfony\Bundle\FrameworkBundle\Templating\TimedPhpEngine
-
-
-
-
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml
index 3caa29fb84332..0ebb44bbd2708 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml
@@ -15,6 +15,7 @@
Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper
Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper
Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper
+ Symfony\Bundle\FrameworkBundle\Templating\Helper\StopwatchHelper
Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine
Symfony\Component\Form\FormRenderer
Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables
@@ -66,12 +67,12 @@
-
+
-
+
@@ -101,6 +102,11 @@
+
+
+
+
+
%templating.helper.form.resources%
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
index 5e6ec40ff4cb9..300f24da7f6e7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
@@ -18,6 +18,7 @@
Symfony\Component\Translation\Loader\IcuResFileLoader
Symfony\Component\Translation\Loader\IcuDatFileLoader
Symfony\Component\Translation\Loader\IniFileLoader
+ Symfony\Component\Translation\Loader\JsonFileLoader
Symfony\Component\Translation\Dumper\PhpFileDumper
Symfony\Component\Translation\Dumper\XliffFileDumper
Symfony\Component\Translation\Dumper\PoFileDumper
@@ -26,6 +27,7 @@
Symfony\Component\Translation\Dumper\QtFileDumper
Symfony\Component\Translation\Dumper\CsvFileDumper
Symfony\Component\Translation\Dumper\IniFileDumper
+ Symfony\Component\Translation\Dumper\JsonFileDumper
Symfony\Component\Translation\Dumper\IcuResFileDumper
Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor
Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader
@@ -90,6 +92,10 @@
+
+
+
+
@@ -122,6 +128,10 @@
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
index 0bc70040a2068..336379a11bd66 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
@@ -5,34 +5,33 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- Symfony\Component\Validator\Validator
- Symfony\Component\Validator\Mapping\ClassMetadataFactory
+ Symfony\Component\Validator\ValidatorInterface
+ Symfony\Component\Validator\ValidatorBuilderInterface
+ Symfony\Component\Validator\Validation
Symfony\Component\Validator\Mapping\Cache\ApcCache
- Symfony\Component\Validator\Mapping\Loader\LoaderChain
- Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader
- Symfony\Component\Validator\Mapping\Loader\AnnotationLoader
- Symfony\Component\Validator\Mapping\Loader\XmlFilesLoader
- Symfony\Component\Validator\Mapping\Loader\YamlFilesLoader
Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory
-
-
+ Symfony\Component\Validator\Constraints\ExpressionValidator
+ Symfony\Component\Validator\Constraints\EmailValidator
-
-
-
-
- %validator.translation_domain%
-
-
+
-
-
- null
+
+
+
+
+
+
+
+
+ %validator.translation_domain%
+
+
+
%validator.mapping.cache.prefix%
@@ -42,26 +41,14 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- %validator.mapping.loader.xml_files_loader.mapping_files%
+
+
+
-
- %validator.mapping.loader.yaml_files_loader.mapping_files%
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml
index 177821a5afb24..6c1dd73e2e615 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml
@@ -38,7 +38,7 @@
%kernel.default_locale%
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php
index 63d16bd357dc6..ac1077a205ae3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php
@@ -1,6 +1,10 @@
-id="escape($id) ?>"
-name="escape($full_name) ?>"
-disabled="disabled"
+id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled"
$v): ?>
- escape($k), $view->escape($v)) ?>
-
+
+escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?>
+
+escape($k), $view->escape($k)) ?>
+
+escape($k), $view->escape($v)) ?>
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php
index da9bec42ec543..77c60d7dfb3d3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php
@@ -1,4 +1,4 @@
-
+ 0): ?>
- getMessage() ?>
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php
index 9c3af35ffb9aa..e7b23d394daec 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php
@@ -1,6 +1,6 @@
- |