Merge branch '2.6' into 2.7

* 2.6:
  Minor plural/singular change
  print error message if server couldn't be started
  [HttpFoundation] [Request] fix baseUrl parsing to fix wrong path_info
  [Twig][Bridge][TranslationDefaultDomain] add support of named arguments.
  [Form] Improved exception message if the data class is not found
  Fixes ArgvInput's argument getter with empty tokens
  execute cheaper checks before more expensive ones
  [DependencyInjection] Fix missing ExpressionLanguageProviders on extension bild
  [FrameworkBundle] FormDataCollector should be loaded only if form config is enabled
This commit is contained in:
Fabien Potencier 2015-01-20 02:40:35 +01:00
commit ba9392ce4f
17 changed files with 209 additions and 41 deletions

View File

@ -62,8 +62,13 @@ class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
} }
if ($node instanceof \Twig_Node_Expression_Filter && in_array($node->getNode('filter')->getAttribute('value'), array('trans', 'transchoice'))) { if ($node instanceof \Twig_Node_Expression_Filter && in_array($node->getNode('filter')->getAttribute('value'), array('trans', 'transchoice'))) {
$ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2;
$arguments = $node->getNode('arguments'); $arguments = $node->getNode('arguments');
$ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2;
if ($this->isNamedArguments($arguments)) {
if (!$arguments->hasNode('domain') && !$arguments->hasNode($ind)) {
$arguments->setNode('domain', $this->scope->get('domain'));
}
} else {
if (!$arguments->hasNode($ind)) { if (!$arguments->hasNode($ind)) {
if (!$arguments->hasNode($ind - 1)) { if (!$arguments->hasNode($ind - 1)) {
$arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine())); $arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine()));
@ -71,6 +76,7 @@ class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
$arguments->setNode($ind, $this->scope->get('domain')); $arguments->setNode($ind, $this->scope->get('domain'));
} }
}
} elseif ($node instanceof TransNode) { } elseif ($node instanceof TransNode) {
if (null === $node->getNode('domain')) { if (null === $node->getNode('domain')) {
$node->setNode('domain', $this->scope->get('domain')); $node->setNode('domain', $this->scope->get('domain'));
@ -103,4 +109,18 @@ class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
{ {
return -10; return -10;
} }
/**
* @return bool
*/
private function isNamedArguments($arguments)
{
foreach ($arguments as $name => $node) {
if (!is_int($name)) {
return true;
}
}
return false;
}
} }

View File

@ -147,6 +147,40 @@ class TranslationExtensionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array()))); $this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array())));
} }
public function testDefaultTranslationDomainWithNamedArguments()
{
$templates = array(
'index' => '
{%- trans_default_domain "foo" %}
{%- block content %}
{{- "foo"|trans(arguments = {}, domain = "custom") }}
{{- "foo"|transchoice(count = 1) }}
{{- "foo"|transchoice(count = 1, arguments = {}, domain = "custom") }}
{{- "foo"|trans({}, domain = "custom") }}
{{- "foo"|trans({}, "custom", locale = "fr") }}
{{- "foo"|transchoice(1, arguments = {}, domain = "custom") }}
{{- "foo"|transchoice(1, {}, "custom", locale = "fr") }}
{% endblock %}
',
'base' => '
{%- block content "" %}
',
);
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foo (messages)'), 'en');
$translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom');
$translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo');
$translator->addResource('array', array('foo' => 'foo (fr)'), 'fr', 'custom');
$template = $this->getTemplate($templates, $translator);
$this->assertEquals('foo (custom)foo (foo)foo (custom)foo (custom)foo (fr)foo (custom)foo (fr)', trim($template->render(array())));
}
protected function getTemplate($template, $translator = null) protected function getTemplate($template, $translator = null)
{ {
if (null === $translator) { if (null === $translator) {

View File

@ -77,6 +77,13 @@ class TranslationDefaultDomainNodeVisitorTest extends \PHPUnit_Framework_TestCas
array(TwigNodeProvider::getTransFilter(self::$message)), array(TwigNodeProvider::getTransFilter(self::$message)),
array(TwigNodeProvider::getTransChoiceFilter(self::$message)), array(TwigNodeProvider::getTransChoiceFilter(self::$message)),
array(TwigNodeProvider::getTransTag(self::$message)), array(TwigNodeProvider::getTransTag(self::$message)),
// with named arguments
array(TwigNodeProvider::getTransFilter(self::$message, null, array(
'arguments' => new \Twig_Node_Expression_Array(array(), 0),
))),
array(TwigNodeProvider::getTransChoiceFilter(self::$message), null, array(
'arguments' => new \Twig_Node_Expression_Array(array(), 0),
)),
); );
} }
} }

View File

@ -29,12 +29,14 @@ class TwigNodeProvider
); );
} }
public static function getTransFilter($message, $domain = null) public static function getTransFilter($message, $domain = null, $arguments = null)
{ {
if (!$arguments) {
$arguments = $domain ? array( $arguments = $domain ? array(
new \Twig_Node_Expression_Array(array(), 0), new \Twig_Node_Expression_Array(array(), 0),
new \Twig_Node_Expression_Constant($domain, 0), new \Twig_Node_Expression_Constant($domain, 0),
) : array(); ) : array();
}
return new \Twig_Node_Expression_Filter( return new \Twig_Node_Expression_Filter(
new \Twig_Node_Expression_Constant($message, 0), new \Twig_Node_Expression_Constant($message, 0),
@ -44,13 +46,15 @@ class TwigNodeProvider
); );
} }
public static function getTransChoiceFilter($message, $domain = null) public static function getTransChoiceFilter($message, $domain = null, $arguments = null)
{ {
if (!$arguments) {
$arguments = $domain ? array( $arguments = $domain ? array(
new \Twig_Node_Expression_Constant(0, 0), new \Twig_Node_Expression_Constant(0, 0),
new \Twig_Node_Expression_Array(array(), 0), new \Twig_Node_Expression_Array(array(), 0),
new \Twig_Node_Expression_Constant($domain, 0), new \Twig_Node_Expression_Constant($domain, 0),
) : array(); ) : array();
}
return new \Twig_Node_Expression_Filter( return new \Twig_Node_Expression_Filter(
new \Twig_Node_Expression_Constant($message, 0), new \Twig_Node_Expression_Constant($message, 0),

View File

@ -91,6 +91,19 @@ EOF
} }
$env = $this->getContainer()->getParameter('kernel.environment'); $env = $this->getContainer()->getParameter('kernel.environment');
$address = $input->getArgument('address');
if (false === strpos($address, ':')) {
$output->writeln('The address has to be of the form <comment>bind-address:port</comment>.');
return 1;
}
if ($this->isOtherServerProcessRunning($address)) {
$output->writeln(sprintf('<error>A process is already listening on http://%s.</error>', $address));
return 1;
}
if ('prod' === $env) { if ('prod' === $env) {
$output->writeln('<error>Running PHP built-in server in production environment is NOT recommended!</error>'); $output->writeln('<error>Running PHP built-in server in production environment is NOT recommended!</error>');
@ -104,8 +117,6 @@ EOF
return 1; return 1;
} }
$address = $input->getArgument('address');
if ($pid > 0) { if ($pid > 0) {
$output->writeln(sprintf('<info>Web server listening on http://%s</info>', $address)); $output->writeln(sprintf('<info>Web server listening on http://%s</info>', $address));
@ -144,6 +155,27 @@ EOF
} }
} }
private function isOtherServerProcessRunning($address)
{
$lockFile = $this->getLockFile($address);
if (file_exists($lockFile)) {
return true;
}
list($hostname, $port) = explode(':', $address);
$fp = @fsockopen($hostname, $port, $errno, $errstr, 5);
if (false !== $fp) {
fclose($fp);
return true;
}
return false;
}
/** /**
* Creates a process to start PHP's built-in web server. * Creates a process to start PHP's built-in web server.
* *

View File

@ -13,8 +13,6 @@
<parameter key="data_collector.time.class">Symfony\Component\HttpKernel\DataCollector\TimeDataCollector</parameter> <parameter key="data_collector.time.class">Symfony\Component\HttpKernel\DataCollector\TimeDataCollector</parameter>
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter> <parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter>
<parameter key="data_collector.router.class">Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector</parameter> <parameter key="data_collector.router.class">Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector</parameter>
<parameter key="data_collector.form.class">Symfony\Component\Form\Extension\DataCollector\FormDataCollector</parameter>
<parameter key="data_collector.form.extractor.class">Symfony\Component\Form\Extension\DataCollector\FormDataExtractor</parameter>
</parameters> </parameters>
<services> <services>
@ -61,12 +59,5 @@
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/> <tag name="kernel.event_listener" event="kernel.controller" method="onKernelController"/>
<tag name="data_collector" template="@WebProfiler/Collector/router.html.twig" id="router" priority="255" /> <tag name="data_collector" template="@WebProfiler/Collector/router.html.twig" id="router" priority="255" />
</service> </service>
<service id="data_collector.form.extractor" class="%data_collector.form.extractor.class%" />
<service id="data_collector.form" class="%data_collector.form.class%">
<tag name="data_collector" template="@WebProfiler/Collector/form.html.twig" id="form" priority="255" />
<argument type="service" id="data_collector.form.extractor" />
</service>
</services> </services>
</container> </container>

View File

@ -7,6 +7,8 @@
<parameters> <parameters>
<parameter key="form.resolved_type_factory.data_collector_proxy.class">Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy</parameter> <parameter key="form.resolved_type_factory.data_collector_proxy.class">Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy</parameter>
<parameter key="form.type_extension.form.data_collector.class">Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension</parameter> <parameter key="form.type_extension.form.data_collector.class">Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension</parameter>
<parameter key="data_collector.form.class">Symfony\Component\Form\Extension\DataCollector\FormDataCollector</parameter>
<parameter key="data_collector.form.extractor.class">Symfony\Component\Form\Extension\DataCollector\FormDataExtractor</parameter>
</parameters> </parameters>
<services> <services>
@ -22,5 +24,13 @@
<tag name="form.type_extension" alias="form" /> <tag name="form.type_extension" alias="form" />
<argument type="service" id="data_collector.form" /> <argument type="service" id="data_collector.form" />
</service> </service>
<!-- DataCollector -->
<service id="data_collector.form.extractor" class="%data_collector.form.extractor.class%" />
<service id="data_collector.form" class="%data_collector.form.class%">
<tag name="data_collector" template="@WebProfiler/Collector/form.html.twig" id="form" priority="255" />
<argument type="service" id="data_collector.form.extractor" />
</service>
</services> </services>
</container> </container>

View File

@ -144,7 +144,7 @@
tbody.appendChild(rows); tbody.appendChild(rows);
if (infoSpan) { if (infoSpan) {
var text = requestStack.length + ' calls'; var text = requestStack.length + ' call' + (requestStack.length > 1 ? 's' : '');
infoSpan.textContent = text; infoSpan.textContent = text;
} }
} else { } else {

View File

@ -309,9 +309,11 @@ class ArgvInput extends Input
public function getParameterOption($values, $default = false) public function getParameterOption($values, $default = false)
{ {
$values = (array) $values; $values = (array) $values;
$tokens = $this->tokens; $tokens = $this->tokens;
while ($token = array_shift($tokens)) {
while (0 < count($tokens)) {
$token = array_shift($tokens);
foreach ($values as $value) { foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) { if ($token === $value || 0 === strpos($token, $value.'=')) {
if (false !== $pos = strpos($token, '=')) { if (false !== $pos = strpos($token, '=')) {

View File

@ -304,6 +304,7 @@ class ArgvInputTest extends \PHPUnit_Framework_TestCase
array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'), array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'),
array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'), array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'),
array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), '1'), array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), '1'),
array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), '1'),
); );
} }

View File

@ -29,6 +29,7 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
$parameters = $container->getParameterBag()->all(); $parameters = $container->getParameterBag()->all();
$definitions = $container->getDefinitions(); $definitions = $container->getDefinitions();
$aliases = $container->getAliases(); $aliases = $container->getAliases();
$exprLangProviders = $container->getExpressionLanguageProviders();
foreach ($container->getExtensions() as $extension) { foreach ($container->getExtensions() as $extension) {
if ($extension instanceof PrependExtensionInterface) { if ($extension instanceof PrependExtensionInterface) {
@ -47,6 +48,10 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
$tmpContainer->setResourceTracking($container->isTrackingResources()); $tmpContainer->setResourceTracking($container->isTrackingResources());
$tmpContainer->addObjectResource($extension); $tmpContainer->addObjectResource($extension);
foreach ($exprLangProviders as $provider) {
$tmpContainer->addExpressionLanguageProvider($provider);
}
$extension->load($config, $tmpContainer); $extension->load($config, $tmpContainer);
$container->merge($tmpContainer); $container->merge($tmpContainer);

View File

@ -0,0 +1,47 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase
{
public function testExpressionLanguageProviderForwarding()
{
if (true !== class_exists('Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage')) {
$this->markTestSkipped('The ExpressionLanguage component isn\'t available!');
}
$tmpProviders = array();
$extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface');
$extension->expects($this->any())
->method('getXsdValidationBasePath')
->will($this->returnValue(false));
$extension->expects($this->any())
->method('getNamespace')
->will($this->returnValue('http://example.org/schema/dic/foo'));
$extension->expects($this->any())
->method('getAlias')
->will($this->returnValue('foo'));
$extension->expects($this->once())
->method('load')
->will($this->returnCallback(function (array $config, ContainerBuilder $container) use (&$tmpProviders) {
$tmpProviders = $container->getExpressionLanguageProviders();
}));
$provider = $this->getMock('Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface');
$container = new ContainerBuilder(new ParameterBag());
$container->registerExtension($extension);
$container->prependExtensionConfig('foo', array('bar' => true ));
$container->addExpressionLanguageProvider($provider);
$pass = new MergeExtensionConfigurationPass();
$pass->process($container);
$this->assertEquals(array($provider), $tmpProviders);
}
}

View File

@ -193,7 +193,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
self::validateName($name); self::validateName($name);
if (null !== $dataClass && !class_exists($dataClass) && !interface_exists($dataClass)) { if (null !== $dataClass && !class_exists($dataClass) && !interface_exists($dataClass)) {
throw new InvalidArgumentException(sprintf('The data class "%s" is not a valid class.', $dataClass)); throw new InvalidArgumentException(sprintf('Class "%s" not found. Is the "data_class" form option set correctly?', $dataClass));
} }
$this->name = (string) $name; $this->name = (string) $name;

View File

@ -1815,7 +1815,7 @@ class Request
return $prefix; return $prefix;
} }
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl))) { if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl).'/')) {
// directory portion of $baseUrl matches // directory portion of $baseUrl matches
return rtrim($prefix, '/'); return rtrim($prefix, '/');
} }

View File

@ -223,6 +223,21 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$request = Request::create('http://test.com/?foo'); $request = Request::create('http://test.com/?foo');
$this->assertEquals('/?foo', $request->getRequestUri()); $this->assertEquals('/?foo', $request->getRequestUri());
$this->assertEquals(array('foo' => ''), $request->query->all()); $this->assertEquals(array('foo' => ''), $request->query->all());
## assume rewrite rule: (.*) --> app/app.php ; app/ is a symlink to a symfony web/ directory
$request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(),
array(
'DOCUMENT_ROOT' => '/var/www/www.test.com',
'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php',
'SCRIPT_NAME' => '/app/app.php',
'PHP_SELF' => '/app/app.php/apparthotel-1234',
));
$this->assertEquals('http://test.com/apparthotel-1234', $request->getUri());
$this->assertEquals('/apparthotel-1234', $request->getPathInfo());
$this->assertEquals('', $request->getQueryString());
$this->assertEquals(80, $request->getPort());
$this->assertEquals('test.com', $request->getHttpHost());
$this->assertFalse($request->isSecure());
} }
/** /**
@ -1347,7 +1362,7 @@ class RequestTest extends \PHPUnit_Framework_TestCase
{ {
return array( return array(
array( array(
'/foo%20bar', '/foo%20bar/',
array( array(
'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
'SCRIPT_NAME' => '/foo bar/app.php', 'SCRIPT_NAME' => '/foo bar/app.php',

View File

@ -72,15 +72,15 @@ class Escaper
*/ */
public static function requiresSingleQuoting($value) public static function requiresSingleQuoting($value)
{ {
// Determines if the PHP value contains any single characters that would // Determines if a PHP value is entirely composed of a value that would
// cause it to require single quoting in YAML. // require single quoting in YAML.
if (preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value)) { if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) {
return true; return true;
} }
// Determines if a PHP value is entirely composed of a value that would // Determines if the PHP value contains any single characters that would
// require single quoting in YAML. // cause it to require single quoting in YAML.
return in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off')); return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
} }
/** /**

View File

@ -144,13 +144,13 @@ class Inline
} }
return $repr; return $repr;
case '' == $value:
return "''";
case Escaper::requiresDoubleQuoting($value): case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value); return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value): case Escaper::requiresSingleQuoting($value):
case preg_match(self::getTimestampRegex(), $value): case preg_match(self::getTimestampRegex(), $value):
return Escaper::escapeWithSingleQuotes($value); return Escaper::escapeWithSingleQuotes($value);
case '' == $value:
return "''";
default: default:
return $value; return $value;
} }