diff --git a/.travis.yml b/.travis.yml
index 1e97ef466d..7ed8c84c6e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -133,7 +133,15 @@ before_install:
- |
# Install extra PHP extensions
if [[ ! $skip ]]; then
+ # install libsodium
+ if [[ ! -e ~/php-ext/$(php -r "echo basename(ini_get('extension_dir'));")/libsodium/sodium.so ]]; then
+ sudo add-apt-repository ppa:ondrej/php -y
+ sudo apt-get update -q
+ sudo apt-get install libsodium-dev -y
+ fi
+
tfold ext.apcu tpecl apcu-5.1.6 apcu.so
+ tfold ext.libsodium tpecl libsodium sodium.so
tfold ext.mongodb tpecl mongodb-1.4.0RC1 mongodb.so
fi
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml
index e88debf658..70381f8d85 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_encoder.yml
@@ -1,6 +1,6 @@
security:
encoders:
- JMS\FooBundle\Entity\User6:
+ JMS\FooBundle\Entity\User7:
algorithm: argon2i
providers:
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php
index b38b665798..629ff18a0c 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php
@@ -86,7 +86,7 @@ class UserPasswordEncoderCommandTest extends WebTestCase
$this->assertContains('Password encoding succeeded', $output);
$encoder = new Argon2iPasswordEncoder();
- preg_match('# Encoded password\s+(\$argon2i\$[\w\d,=\$+\/]+={0,2})\s+#', $output, $matches);
+ preg_match('# Encoded password\s+(\$argon2id\$[\w\d,=\$+\/]+={0,2})\s+#', $output, $matches);
$hash = $matches[1];
$this->assertTrue($encoder->isPasswordValid($hash, 'password', null));
}
@@ -250,7 +250,7 @@ EOTXT
private function setupArgon2i()
{
putenv('COLUMNS='.(119 + strlen(PHP_EOL)));
- $kernel = $this->createKernel(array('test_case' => 'PasswordEncode', 'root_config' => 'argon2i'));
+ $kernel = $this->createKernel(array('test_case' => 'PasswordEncode', 'root_config' => 'argon2i.yml'));
$kernel->boot();
$application = new Application($kernel);
diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php
index 918290feab..082870249b 100644
--- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php
+++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php
@@ -14,6 +14,7 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Reference;
/**
* Adds tagged twig.extension services to twig service.
@@ -36,11 +37,22 @@ class TwigEnvironmentPass implements CompilerPassInterface
// For instance, global variable definitions must be registered
// afterward. If not, the globals from the extensions will never
// be registered.
- $calls = $definition->getMethodCalls();
- $definition->setMethodCalls(array());
+ $currentMethodCalls = $definition->getMethodCalls();
+ $twigBridgeExtensionsMethodCalls = array();
+ $othersExtensionsMethodCalls = array();
foreach ($this->findAndSortTaggedServices('twig.extension', $container) as $extension) {
- $definition->addMethodCall('addExtension', array($extension));
+ $methodCall = array('addExtension', array($extension));
+ $extensionClass = $container->getDefinition((string) $extension)->getClass();
+
+ if (is_string($extensionClass) && 0 === strpos($extensionClass, 'Symfony\Bridge\Twig\Extension')) {
+ $twigBridgeExtensionsMethodCalls[] = $methodCall;
+ } else {
+ $othersExtensionsMethodCalls[] = $methodCall;
+ }
+ }
+
+ if (!empty($twigBridgeExtensionsMethodCalls) || !empty($othersExtensionsMethodCalls)) {
+ $definition->setMethodCalls(array_merge($twigBridgeExtensionsMethodCalls, $othersExtensionsMethodCalls, $currentMethodCalls));
}
- $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls));
}
}
diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php
index 11b5077b41..aa6aac2428 100644
--- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php
+++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigEnvironmentPassTest.php
@@ -12,9 +12,11 @@
namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\Twig\Extension\FormExtension;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
class TwigEnvironmentPassTest extends TestCase
{
@@ -42,4 +44,28 @@ class TwigEnvironmentPassTest extends TestCase
$this->assertEquals('test_extension_2', (string) $calls[0][1][0]);
$this->assertEquals('test_extension_1', (string) $calls[1][1][0]);
}
+
+ public function testTwigBridgeExtensionsAreRegisteredFirst()
+ {
+ $container = new ContainerBuilder();
+ $twigDefinition = $container->register('twig');
+ $container->register('other_extension', 'Foo\Bar')
+ ->addTag('twig.extension');
+ $container->register('twig_bridge_extension', FormExtension::class)
+ ->addTag('twig.extension');
+
+ $twigEnvironmentPass = new TwigEnvironmentPass();
+ $twigEnvironmentPass->process($container);
+
+ $methodCalls = $twigDefinition->getMethodCalls();
+ $this->assertCount(2, $methodCalls);
+
+ $twigBridgeExtensionReference = $methodCalls[0][1][0];
+ $this->assertInstanceOf(Reference::class, $twigBridgeExtensionReference);
+ $this->assertSame('twig_bridge_extension', (string) $twigBridgeExtensionReference);
+
+ $otherExtensionReference = $methodCalls[1][1][0];
+ $this->assertInstanceOf(Reference::class, $otherExtensionReference);
+ $this->assertSame('other_extension', (string) $otherExtensionReference);
+ }
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig
index b275a806b4..57daf555a3 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig
@@ -272,9 +272,7 @@
{% for child in profile.children %}
diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php
index 4441296b7f..0cf7381c8a 100644
--- a/src/Symfony/Component/Console/Input/ArgvInput.php
+++ b/src/Symfony/Component/Console/Input/ArgvInput.php
@@ -277,7 +277,11 @@ class ArgvInput extends Input
return false;
}
foreach ($values as $value) {
- if ($token === $value || 0 === strpos($token, $value.'=')) {
+ // Options with values:
+ // For long options, test for '--option=' at beginning
+ // For short options, test for '-o' at beginning
+ $leading = 0 === strpos($value, '--') ? $value.'=' : $value;
+ if ($token === $value || 0 === strpos($token, $leading)) {
return true;
}
}
@@ -301,13 +305,16 @@ class ArgvInput extends Input
}
foreach ($values as $value) {
- if ($token === $value || 0 === strpos($token, $value.'=')) {
- if (false !== $pos = strpos($token, '=')) {
- return substr($token, $pos + 1);
- }
-
+ if ($token === $value) {
return array_shift($tokens);
}
+ // Options with values:
+ // For long options, test for '--option=' at beginning
+ // For short options, test for '-o' at beginning
+ $leading = 0 === strpos($value, '--') ? $value.'=' : $value;
+ if (0 === strpos($token, $leading)) {
+ return substr($token, strlen($leading));
+ }
}
}
diff --git a/src/Symfony/Component/Console/Input/InputInterface.php b/src/Symfony/Component/Console/Input/InputInterface.php
index b9e0651faf..43810f7ac2 100644
--- a/src/Symfony/Component/Console/Input/InputInterface.php
+++ b/src/Symfony/Component/Console/Input/InputInterface.php
@@ -33,6 +33,8 @@ interface InputInterface
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
+ * Does not necessarily return the correct result for short options
+ * when multiple flags are combined in the same option.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
@@ -46,6 +48,8 @@ interface InputInterface
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
+ * Does not necessarily return the correct result for short options
+ * when multiple flags are combined in the same option.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
index 8287bce521..8febf4875b 100644
--- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
+++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
@@ -314,6 +314,10 @@ class ArgvInputTest extends TestCase
$input = new ArgvInput(array('cli.php', '-f', 'foo'));
$this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ $input = new ArgvInput(array('cli.php', '-etest'));
+ $this->assertTrue($input->hasParameterOption('-e'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ $this->assertFalse($input->hasParameterOption('-s'), '->hasParameterOption() returns true if the given short option is in the raw input');
+
$input = new ArgvInput(array('cli.php', '--foo', 'foo'));
$this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input');
@@ -339,6 +343,33 @@ class ArgvInputTest extends TestCase
$this->assertFalse($input->hasParameterOption('--foo', true), '->hasParameterOption() returns false if the given option is in the raw input but after an end of options signal');
}
+ public function testHasParameterOptionEdgeCasesAndLimitations()
+ {
+ $input = new ArgvInput(array('cli.php', '-fh'));
+ // hasParameterOption does not know if the previous short option, -f,
+ // takes a value or not. If -f takes a value, then -fh does NOT include
+ // -h; Otherwise it does. Since we do not know which short options take
+ // values, hasParameterOption does not support this use-case.
+ $this->assertFalse($input->hasParameterOption('-h'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ // hasParameterOption does detect that `-fh` contains `-f`, since
+ // `-f` is the first short option in the set.
+ $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ // The test below happens to pass, although it might make more sense
+ // to disallow it, and require the use of
+ // $input->hasParameterOption('-f') && $input->hasParameterOption('-h')
+ // instead.
+ $this->assertTrue($input->hasParameterOption('-fh'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ // In theory, if -fh is supported, then -hf should also work.
+ // However, this is not supported.
+ $this->assertFalse($input->hasParameterOption('-hf'), '->hasParameterOption() returns true if the given short option is in the raw input');
+
+ $input = new ArgvInput(array('cli.php', '-f', '-h'));
+ // If hasParameterOption('-fh') is supported for 'cli.php -fh', then
+ // one might also expect that it should also be supported for
+ // 'cli.php -f -h'. However, this is not supported.
+ $this->assertFalse($input->hasParameterOption('-fh'), '->hasParameterOption() returns true if the given short option is in the raw input');
+ }
+
public function testToString()
{
$input = new ArgvInput(array('cli.php', '-f', 'foo'));
diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php
index 989725d53d..9865adbd7a 100644
--- a/src/Symfony/Component/DependencyInjection/Definition.php
+++ b/src/Symfony/Component/DependencyInjection/Definition.php
@@ -128,7 +128,7 @@ class Definition
*/
public function setDecoratedService($id, $renamedId = null, $priority = 0)
{
- if ($renamedId && $id == $renamedId) {
+ if ($renamedId && $id === $renamedId) {
throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
}
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index 76f9348e1d..1bb68a98cd 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -115,7 +115,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env));
}
- return constant($name);
+ return constant($env);
}
if ('base64' === $prefix) {
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index ae6afadaab..273db0dbd2 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -113,6 +113,7 @@ class FormValidator extends ConstraintValidator
? (string) $form->getViewData()
: gettype($form->getViewData());
+ $this->context->setConstraint($constraint);
$this->context->buildViolation($config->getOption('invalid_message'))
->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')))
->setInvalidValue($form->getViewData())
@@ -124,6 +125,7 @@ class FormValidator extends ConstraintValidator
// Mark the form with an error if it contains extra fields
if (!$config->getOption('allow_extra_fields') && count($form->getExtraData()) > 0) {
+ $this->context->setConstraint($constraint);
$this->context->buildViolation($config->getOption('extra_fields_message'))
->setParameter('{{ extra_fields }}', implode('", "', array_keys($form->getExtraData())))
->setInvalidValue($form->getExtraData())
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
index 55b4ac278c..6301d2bf8a 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -51,6 +51,8 @@ class FormValidatorTest extends ConstraintValidatorTestCase
$this->serverParams = $this->getMockBuilder('Symfony\Component\Form\Extension\Validator\Util\ServerParams')->setMethods(array('getNormalizedIniPostMaxSize', 'getContentLength'))->getMock();
parent::setUp();
+
+ $this->constraint = new Form();
}
protected function createValidator()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 53b7cedf91..544467b045 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -2064,11 +2064,11 @@ class RequestTest extends TestCase
/**
* @dataProvider methodCacheableProvider
*/
- public function testMethodCacheable($method, $chacheable)
+ public function testMethodCacheable($method, $cacheable)
{
$request = new Request();
$request->setMethod($method);
- $this->assertEquals($chacheable, $request->isMethodCacheable());
+ $this->assertEquals($cacheable, $request->isMethodCacheable());
}
public function methodCacheableProvider()
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
index bd18783da5..ca4f71d8ea 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
@@ -131,7 +131,9 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
$types = array();
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName($tag) as $tag) {
- $types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType()));
+ if ($tag && null !== $tag->getType()) {
+ $types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType()));
+ }
}
if (!isset($types[0])) {
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php
index 10e7fbcf9e..a888ab03fb 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php
@@ -40,6 +40,11 @@ class PhpDocExtractorTest extends TestCase
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
+ public function testParamTagTypeIsOmitted()
+ {
+ $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType'));
+ }
+
/**
* @dataProvider typesWithCustomPrefixesProvider
*/
@@ -177,3 +182,15 @@ class EmptyDocBlock
{
public $foo;
}
+
+class OmittedParamTagTypeDocBlock
+{
+ /**
+ * The type is omitted here to ensure that the extractor doesn't choke on missing types.
+ *
+ * @param $omittedTagType
+ */
+ public function setOmittedType(array $omittedTagType)
+ {
+ }
+}
diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
index cd3fdd6798..c4ee1bffaf 100644
--- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
@@ -77,9 +77,13 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
}
}
- $requestBag = $this->options['post_only'] ? $request->request : $request;
- $username = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['username_parameter']);
- $password = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['password_parameter']);
+ if ($this->options['post_only']) {
+ $username = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']);
+ $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']);
+ } else {
+ $username = ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']);
+ $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
+ }
if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
index f3962a391e..6ab543225c 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php
@@ -77,14 +77,14 @@ class UsernamePasswordFormAuthenticationListenerTest extends TestCase
}
/**
+ * @dataProvider postOnlyDataProvider
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* @expectedExceptionMessage The key "_username" must be a string, "array" given.
*/
- public function testHandleNonStringUsername()
+ public function testHandleNonStringUsername($postOnly)
{
$request = Request::create('/login_check', 'POST', array('_username' => array()));
$request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
-
$listener = new UsernamePasswordFormAuthenticationListener(
new TokenStorage(),
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
@@ -93,14 +93,20 @@ class UsernamePasswordFormAuthenticationListenerTest extends TestCase
'foo',
new DefaultAuthenticationSuccessHandler($httpUtils),
new DefaultAuthenticationFailureHandler($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $httpUtils),
- array('require_previous_session' => false)
+ array('require_previous_session' => false, 'post_only' => $postOnly)
);
-
$event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
-
$listener->handle($event);
}
+ public function postOnlyDataProvider()
+ {
+ return array(
+ array(true),
+ array(false),
+ );
+ }
+
public function getUsernameForLength()
{
return array(