Merge branch '4.3' into 4.4
* 4.3: [Debug] fix ClassNotFoundFatalErrorHandler [Routing] Fix using a custom matcher & generator dumper class [Dotenv] Fixed infinite loop with missing quote followed by quoted value [HttpClient] Added missing sprintf [TwigBridge] button_widget now has its title attr translated even if its label = null or false [PhpUnitBridge] When using phpenv + phpenv-composer plugin, composer executable is wrapped into a bash script [Messenger] Added check if json_encode succeeded [Security] Prevent canceled remember-me cookie from being accepted [FrameworkBundle][TranslationUpdateCommand] Do not output positive feedback on stderr [Security\Guard] Fix missing typehints
This commit is contained in:
commit
80cd480254
|
@ -243,6 +243,7 @@ FrameworkBundle
|
|||
* Removed `routing.loader.service`.
|
||||
* Added support for PHPUnit 8. A `void` return-type was added to the `KernelTestCase::tearDown()` and `WebTestCase::tearDown()` method.
|
||||
* Removed the `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract` services.
|
||||
* Removed the `router.cache_class_prefix` parameter.
|
||||
|
||||
HttpClient
|
||||
----------
|
||||
|
|
|
@ -106,7 +106,7 @@ $COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
|
|||
|| ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
|
||||
|| ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`))
|
||||
|| file_exists($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`).DIRECTORY_SEPARATOR.'composer.phar')
|
||||
? $PHP.' '.escapeshellarg($COMPOSER)
|
||||
? (file_get_contents($COMPOSER, null, 0, 18) === '#!/usr/bin/env php' ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
|
||||
: 'composer';
|
||||
|
||||
$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml': ''));
|
||||
|
|
|
@ -228,13 +228,11 @@
|
|||
'%name%': name,
|
||||
'%id%': id,
|
||||
}) %}
|
||||
{%- elseif label is same as(false) -%}
|
||||
{% set translation_domain = false %}
|
||||
{%- else -%}
|
||||
{%- elseif label is not same as(false) -%}
|
||||
{% set label = name|humanize %}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ translation_domain is same as(false) ? label : label|trans(label_translation_parameters, translation_domain) }}</button>
|
||||
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ translation_domain is same as(false) or label is same as(false) ? label : label|trans(label_translation_parameters, translation_domain) }}</button>
|
||||
{%- endblock button_widget -%}
|
||||
|
||||
{%- block submit_widget -%}
|
||||
|
|
|
@ -213,12 +213,12 @@ EOF
|
|||
}
|
||||
}
|
||||
|
||||
$errorIo->title('Translation Messages Extractor and Dumper');
|
||||
$errorIo->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
|
||||
$io->title('Translation Messages Extractor and Dumper');
|
||||
$io->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
|
||||
|
||||
// load any messages from templates
|
||||
$extractedCatalogue = new MessageCatalogue($input->getArgument('locale'));
|
||||
$errorIo->comment('Parsing templates...');
|
||||
$io->comment('Parsing templates...');
|
||||
$this->extractor->setPrefix($input->getOption('prefix'));
|
||||
foreach ($viewsPaths as $path) {
|
||||
if (is_dir($path) || is_file($path)) {
|
||||
|
@ -228,7 +228,7 @@ EOF
|
|||
|
||||
// load any existing messages from the translation files
|
||||
$currentCatalogue = new MessageCatalogue($input->getArgument('locale'));
|
||||
$errorIo->comment('Loading translation files...');
|
||||
$io->comment('Loading translation files...');
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path)) {
|
||||
$this->reader->read($path, $currentCatalogue);
|
||||
|
@ -296,7 +296,7 @@ EOF
|
|||
}
|
||||
|
||||
if ('xlf' === $input->getOption('output-format')) {
|
||||
$errorIo->comment(sprintf('Xliff output version is <info>%s</info>', $input->getOption('xliff-version')));
|
||||
$io->comment(sprintf('Xliff output version is <info>%s</info>', $input->getOption('xliff-version')));
|
||||
}
|
||||
|
||||
$resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was');
|
||||
|
@ -308,7 +308,7 @@ EOF
|
|||
|
||||
// save the files
|
||||
if (true === $input->getOption('force')) {
|
||||
$errorIo->comment('Writing files...');
|
||||
$io->comment('Writing files...');
|
||||
|
||||
$bundleTransPath = false;
|
||||
foreach ($transPaths as $path) {
|
||||
|
@ -328,7 +328,7 @@ EOF
|
|||
}
|
||||
}
|
||||
|
||||
$errorIo->success($resultMessage.'.');
|
||||
$io->success($resultMessage.'.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class ClearRememberMeTest extends AbstractWebTestCase
|
|||
$this->assertNotNull($cookieJar->get('REMEMBERME'));
|
||||
|
||||
$client->request('GET', '/foo');
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
$this->assertRedirect($client->getResponse(), '/login');
|
||||
$this->assertNull($cookieJar->get('REMEMBERME'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"symfony/security-core": "^4.4",
|
||||
"symfony/security-csrf": "^4.2|^5.0",
|
||||
"symfony/security-guard": "^4.2|^5.0",
|
||||
"symfony/security-http": "^4.4.1"
|
||||
"symfony/security-http": "^4.4.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/doctrine-bundle": "^1.5|^2.0",
|
||||
|
|
|
@ -32,6 +32,10 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
|||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
|
||||
if (!\is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader) {
|
||||
|
|
|
@ -277,7 +277,10 @@ final class Dotenv
|
|||
$this->cursor += 1 + $len;
|
||||
} elseif ('"' === $this->data[$this->cursor]) {
|
||||
$value = '';
|
||||
++$this->cursor;
|
||||
|
||||
if (++$this->cursor === $this->end) {
|
||||
throw $this->createFormatException('Missing quote to end the value');
|
||||
}
|
||||
|
||||
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
|
||||
$value .= $this->data[$this->cursor];
|
||||
|
|
|
@ -40,6 +40,7 @@ class DotenvTest extends TestCase
|
|||
['FOO', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO...\n ^ line 1 offset 3"],
|
||||
['FOO="foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo...\n ^ line 1 offset 8"],
|
||||
['FOO=\'foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo...\n ^ line 1 offset 8"],
|
||||
["FOO=\"foo\nBAR=\"bar\"", "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo\\nBAR=\"bar\"...\n ^ line 1 offset 18"],
|
||||
['FOO=\'foo'."\n", "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo\\n...\n ^ line 1 offset 9"],
|
||||
['export FOO', "Unable to unset an environment variable in \".env\" at line 1.\n...export FOO...\n ^ line 1 offset 10"],
|
||||
['FOO=${FOO', "Unclosed braces on variable expansion in \".env\" at line 1.\n...FOO=\${FOO...\n ^ line 1 offset 9"],
|
||||
|
|
|
@ -30,6 +30,10 @@ class ClassNotFoundErrorEnhancerTest extends TestCase
|
|||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
|
||||
if (!\is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader) {
|
||||
|
|
|
@ -353,7 +353,7 @@ trait ResponseTrait
|
|||
}
|
||||
|
||||
if ('' !== $chunk && null !== $response->content && \strlen($chunk) !== fwrite($response->content, $chunk)) {
|
||||
$multi->handlesActivity[$j] = [null, new TransportException('Failed writing %d bytes to the response buffer.', \strlen($chunk))];
|
||||
$multi->handlesActivity[$j] = [null, new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($chunk)))];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -189,9 +189,7 @@ class ConnectionTest extends TestCase
|
|||
public function testJsonError()
|
||||
{
|
||||
$redis = new \Redis();
|
||||
|
||||
$connection = Connection::fromDsn('redis://localhost/json-error', [], $redis);
|
||||
|
||||
try {
|
||||
$connection->add("\xB1\x31", []);
|
||||
} catch (TransportException $e) {
|
||||
|
|
|
@ -285,7 +285,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
|||
return $this->matcher;
|
||||
}
|
||||
|
||||
$compiled = is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true) && (UrlMatcher::class === $this->options['matcher_base_class'] || RedirectableUrlMatcher::class === $this->options['matcher_base_class']);
|
||||
$compiled = is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true) && (UrlMatcher::class === $this->options['matcher_base_class'] || RedirectableUrlMatcher::class === $this->options['matcher_base_class']) && is_a($this->options['matcher_dumper_class'], CompiledUrlMatcherDumper::class, true);
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
|
||||
$routes = $this->getRouteCollection();
|
||||
|
@ -342,7 +342,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
|||
return $this->generator;
|
||||
}
|
||||
|
||||
$compiled = is_a($this->options['generator_class'], CompiledUrlGenerator::class, true) && UrlGenerator::class === $this->options['generator_base_class'];
|
||||
$compiled = is_a($this->options['generator_class'], CompiledUrlGenerator::class, true) && UrlGenerator::class === $this->options['generator_base_class'] && is_a($this->options['generator_dumper_class'], CompiledUrlGeneratorDumper::class, true);
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
|
||||
$routes = $this->getRouteCollection();
|
||||
|
@ -392,8 +392,8 @@ class Router implements RouterInterface, RequestMatcherInterface
|
|||
*/
|
||||
protected function getGeneratorDumperInstance()
|
||||
{
|
||||
// For BC, fallback to PhpGeneratorDumper if the UrlGenerator and UrlGeneratorDumper are not consistent with each other
|
||||
if (is_a($this->options['generator_class'], CompiledUrlGenerator::class, true) !== is_a($this->options['generator_dumper_class'], CompiledUrlGeneratorDumper::class, true)) {
|
||||
// For BC, fallback to PhpGeneratorDumper (which is the old default value) if the old UrlGenerator is used with the new default CompiledUrlGeneratorDumper
|
||||
if (!is_a($this->options['generator_class'], CompiledUrlGenerator::class, true) && is_a($this->options['generator_dumper_class'], CompiledUrlGeneratorDumper::class, true)) {
|
||||
return new PhpGeneratorDumper($this->getRouteCollection());
|
||||
}
|
||||
|
||||
|
@ -405,8 +405,8 @@ class Router implements RouterInterface, RequestMatcherInterface
|
|||
*/
|
||||
protected function getMatcherDumperInstance()
|
||||
{
|
||||
// For BC, fallback to PhpMatcherDumper if the UrlMatcher and UrlMatcherDumper are not consistent with each other
|
||||
if (is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true) !== is_a($this->options['matcher_dumper_class'], CompiledUrlMatcherDumper::class, true)) {
|
||||
// For BC, fallback to PhpMatcherDumper (which is the old default value) if the old UrlMatcher is used with the new default CompiledUrlMatcherDumper
|
||||
if (!is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true) && is_a($this->options['matcher_dumper_class'], CompiledUrlMatcherDumper::class, true)) {
|
||||
return new PhpMatcherDumper($this->getRouteCollection());
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ class GuardAuthenticationListener extends AbstractListener implements ListenerIn
|
|||
* @param string $providerKey The provider (i.e. firewall) key
|
||||
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
|
||||
*/
|
||||
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, $guardAuthenticators, LoggerInterface $logger = null)
|
||||
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
|
|
|
@ -48,7 +48,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
|||
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
|
||||
* @param string $providerKey The provider (i.e. firewall) key
|
||||
*/
|
||||
public function __construct($guardAuthenticators, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker, UserPasswordEncoderInterface $passwordEncoder = null)
|
||||
public function __construct(iterable $guardAuthenticators, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker, UserPasswordEncoderInterface $passwordEncoder = null)
|
||||
{
|
||||
$this->guardAuthenticators = $guardAuthenticators;
|
||||
$this->userProvider = $userProvider;
|
||||
|
|
|
@ -94,6 +94,10 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
|
|||
*/
|
||||
final public function autoLogin(Request $request): ?TokenInterface
|
||||
{
|
||||
if (($cookie = $request->attributes->get(self::COOKIE_ATTR_NAME)) && null === $cookie->getValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $cookie = $request->cookies->get($this->options['name'])) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,17 @@ class AbstractRememberMeServicesTest extends TestCase
|
|||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
public function testAutoLoginReturnsNullAfterLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, ['name' => 'foo', 'path' => null, 'domain' => null]);
|
||||
|
||||
$request = new Request();
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service->loginFail($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
}
|
||||
|
||||
public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnUserInterface()
|
||||
{
|
||||
$this->expectException('RuntimeException');
|
||||
|
|
Reference in New Issue