Merge branch '4.4' into 5.0
* 4.4: [PropertyAccess] fix tests [WebProfilerBundle] fix test remove assertions that can never be reached [PropertyAccess] Improve message of unitialized property in php 7.4 [HttpFoundation] Fixed session migration with custom cookie lifetime [HttpKernel][FrameworkBundle] fix compat with Debug component [Serializer] Remove unused variable Allow URL-encoded special characters in basic auth part of URLs [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key [Validator] Add missing Ukrainian and Russian translations Track session usage when setting the token [4.4][MonologBridge] Fix $level type No need to reconnect the bags to the session Support for Content Security Policy style-src-elem and script-src-elem in WebProfiler [PropertyInfo][ReflectionExtractor] Check the array mutator prefixes last when the property is singular [Security][Http][SwitchUserListener] Ignore all non existent username protection errors Add installation and minimal example to README
This commit is contained in:
commit
cc24b55e04
@ -49,7 +49,10 @@ class ElasticsearchLogstashHandler extends AbstractHandler
|
||||
private $client;
|
||||
private $responses;
|
||||
|
||||
public function __construct(string $endpoint = 'http://127.0.0.1:9200', string $index = 'monolog', HttpClientInterface $client = null, int $level = Logger::DEBUG, bool $bubble = true)
|
||||
/**
|
||||
* @param string|int $level The minimum logging level at which this handler will be triggered
|
||||
*/
|
||||
public function __construct(string $endpoint = 'http://127.0.0.1:9200', string $index = 'monolog', HttpClientInterface $client = null, $level = Logger::DEBUG, bool $bubble = true)
|
||||
{
|
||||
if (!interface_exists(HttpClientInterface::class)) {
|
||||
throw new \LogicException(sprintf('The "%s" handler needs an HTTP client. Try running "composer require symfony/http-client".', __CLASS__));
|
||||
|
@ -19,7 +19,6 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
@ -207,15 +206,7 @@ class Application extends BaseApplication
|
||||
(new SymfonyStyle($input, $output))->warning('Some commands could not be registered:');
|
||||
|
||||
foreach ($this->registrationErrors as $error) {
|
||||
if (method_exists($this, 'doRenderThrowable')) {
|
||||
$this->doRenderThrowable($error, $output);
|
||||
} else {
|
||||
if (!$error instanceof \Exception) {
|
||||
$error = new FatalThrowableError($error);
|
||||
}
|
||||
|
||||
$this->doRenderException($error, $output);
|
||||
}
|
||||
$this->doRenderThrowable($error, $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class AppCustomAuthenticator extends AbstractGuardAuthenticator
|
||||
{
|
||||
public function supports(Request $request)
|
||||
{
|
||||
return true;
|
||||
return '/manual_login' !== $request->getPathInfo() && '/profile' !== $request->getPathInfo();
|
||||
}
|
||||
|
||||
public function getCredentials(Request $request)
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\User\User;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||
|
||||
class AuthenticationController
|
||||
{
|
||||
public function manualLoginAction(GuardAuthenticatorHandler $guardAuthenticatorHandler, Request $request)
|
||||
{
|
||||
$guardAuthenticatorHandler->authenticateWithToken(new PostAuthenticationGuardToken(new User('Jane', 'test', ['ROLE_USER']), 'secure', ['ROLE_USER']), $request, 'secure');
|
||||
|
||||
return new Response('Logged in.');
|
||||
}
|
||||
|
||||
public function profileAction(UserInterface $user = null)
|
||||
{
|
||||
if (null === $user) {
|
||||
return new Response('Not logged in.');
|
||||
}
|
||||
|
||||
return new Response('Username: '.$user->getUsername());
|
||||
}
|
||||
}
|
@ -21,4 +21,14 @@ class GuardedTest extends AbstractWebTestCase
|
||||
|
||||
$this->assertSame(418, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testManualLogin()
|
||||
{
|
||||
$client = $this->createClient(['debug' => true, 'test_case' => 'Guarded', 'root_config' => 'config.yml']);
|
||||
|
||||
$client->request('GET', '/manual_login');
|
||||
$client->request('GET', '/profile');
|
||||
|
||||
$this->assertSame('Username: Jane', $client->getResponse()->getContent());
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,19 @@ framework:
|
||||
services:
|
||||
logger: { class: Psr\Log\NullLogger }
|
||||
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AppCustomAuthenticator: ~
|
||||
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController:
|
||||
tags: [controller.service_arguments]
|
||||
|
||||
security:
|
||||
encoders:
|
||||
Symfony\Component\Security\Core\User\User: plaintext
|
||||
|
||||
providers:
|
||||
in_memory:
|
||||
memory:
|
||||
users:
|
||||
Jane: { password: test, roles: [ROLE_USER] }
|
||||
|
||||
firewalls:
|
||||
secure:
|
||||
pattern: ^/
|
||||
|
@ -3,3 +3,12 @@ main:
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
|
||||
path: /app
|
||||
profile:
|
||||
path: /profile
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::profileAction
|
||||
|
||||
manual_login:
|
||||
path: /manual_login
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::manualLoginAction
|
||||
|
@ -124,7 +124,7 @@ class ContentSecurityPolicyHandler
|
||||
$headers = $this->getCspHeaders($response);
|
||||
|
||||
foreach ($headers as $header => $directives) {
|
||||
foreach (['script-src' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce'] as $type => $tokenName) {
|
||||
foreach (['script-src' => 'csp_script_nonce', 'script-src-elem' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce', 'style-src-elem' => 'csp_style_nonce'] as $type => $tokenName) {
|
||||
if ($this->authorizesInline($directives, $type)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class ContentSecurityPolicyHandlerTest extends TestCase
|
||||
$this->assertFalse($response->headers->has('X-SymfonyProfiler-Style-Nonce'));
|
||||
|
||||
foreach ($expectedCsp as $header => $value) {
|
||||
$this->assertSame($value, $response->headers->get($header));
|
||||
$this->assertSame($value, $response->headers->get($header), $header);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ class ContentSecurityPolicyHandlerTest extends TestCase
|
||||
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
|
||||
$this->createRequest(),
|
||||
$this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']),
|
||||
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
|
||||
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
|
||||
],
|
||||
[
|
||||
$nonce,
|
||||
|
@ -215,8 +215,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $lifetime) {
|
||||
if (null !== $lifetime && $lifetime != ini_get('session.cookie_lifetime')) {
|
||||
$this->save();
|
||||
ini_set('session.cookie_lifetime', $lifetime);
|
||||
$this->start();
|
||||
}
|
||||
|
||||
if ($destroy) {
|
||||
@ -225,10 +227,6 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
|
||||
$isRegenerated = session_regenerate_id($destroy);
|
||||
|
||||
// The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
|
||||
// @see https://bugs.php.net/70013
|
||||
$this->loadSession();
|
||||
|
||||
if (null !== $this->emulateSameSite) {
|
||||
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
|
||||
if (null !== $originalCookie) {
|
||||
|
@ -120,6 +120,19 @@ class NativeSessionStorageTest extends TestCase
|
||||
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
|
||||
}
|
||||
|
||||
public function testRegenerateWithCustomLifetime()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
$storage->start();
|
||||
$id = $storage->getId();
|
||||
$lifetime = 999999;
|
||||
$storage->getBag('attributes')->set('legs', 11);
|
||||
$storage->regenerate(false, $lifetime);
|
||||
$this->assertNotEquals($id, $storage->getId());
|
||||
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
|
||||
$this->assertEquals($lifetime, ini_get('session.cookie_lifetime'));
|
||||
}
|
||||
|
||||
public function testSessionGlobalIsUpToDateAfterIdRegeneration()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
|
@ -123,15 +123,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
||||
$output = $output->getErrorOutput();
|
||||
}
|
||||
$this->exceptionHandler = static function (\Throwable $e) use ($app, $output) {
|
||||
if (method_exists($app, 'renderThrowable')) {
|
||||
$app->renderThrowable($e, $output);
|
||||
} else {
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = new FatalThrowableError($e);
|
||||
}
|
||||
|
||||
$app->renderException($e, $output);
|
||||
}
|
||||
$app->renderThrowable($e, $output);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
"symfony/browser-kit": "<4.4",
|
||||
"symfony/cache": "<5.0",
|
||||
"symfony/config": "<5.0",
|
||||
"symfony/console": "<4.4",
|
||||
"symfony/form": "<5.0",
|
||||
"symfony/dependency-injection": "<4.4",
|
||||
"symfony/doctrine-bridge": "<5.0",
|
||||
|
@ -44,7 +44,7 @@ class OptionsResolverIntrospectorTest extends TestCase
|
||||
$resolver->setDefined($option = 'foo');
|
||||
|
||||
$debug = new OptionsResolverIntrospector($resolver);
|
||||
$this->assertSame('bar', $debug->getDefault($option));
|
||||
$debug->getDefault($option);
|
||||
}
|
||||
|
||||
public function testGetDefaultThrowsOnNotDefinedOption()
|
||||
@ -54,7 +54,7 @@ class OptionsResolverIntrospectorTest extends TestCase
|
||||
$resolver = new OptionsResolver();
|
||||
|
||||
$debug = new OptionsResolverIntrospector($resolver);
|
||||
$this->assertSame('bar', $debug->getDefault('foo'));
|
||||
$debug->getDefault('foo');
|
||||
}
|
||||
|
||||
public function testGetLazyClosures()
|
||||
@ -75,7 +75,7 @@ class OptionsResolverIntrospectorTest extends TestCase
|
||||
$resolver->setDefined($option = 'foo');
|
||||
|
||||
$debug = new OptionsResolverIntrospector($resolver);
|
||||
$this->assertSame('bar', $debug->getLazyClosures($option));
|
||||
$debug->getLazyClosures($option);
|
||||
}
|
||||
|
||||
public function testGetLazyClosuresThrowsOnNotDefinedOption()
|
||||
@ -85,7 +85,7 @@ class OptionsResolverIntrospectorTest extends TestCase
|
||||
$resolver = new OptionsResolver();
|
||||
|
||||
$debug = new OptionsResolverIntrospector($resolver);
|
||||
$this->assertSame('bar', $debug->getLazyClosures('foo'));
|
||||
$debug->getLazyClosures('foo');
|
||||
}
|
||||
|
||||
public function testGetAllowedTypes()
|
||||
|
@ -385,7 +385,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
} catch (\TypeError $e) {
|
||||
// handle uninitialized properties in PHP >= 7
|
||||
if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of the type (\w+), null returned$/', preg_quote(\get_class($object)), $access[self::ACCESS_NAME])), $e->getMessage(), $matches)) {
|
||||
throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Have you forgotten to initialize a property or to make the return type nullable using "?%3$s" instead?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
|
||||
throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
@ -418,7 +418,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
|
||||
$r = new \ReflectionProperty($matches[1], $matches[2]);
|
||||
|
||||
throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%3$s". You should either initialize it or make it nullable using "?%3$s" instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
|
||||
throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
|
@ -139,7 +139,7 @@ class PropertyAccessorTest extends TestCase
|
||||
public function testGetValueThrowsExceptionIfUninitializedProperty()
|
||||
{
|
||||
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
|
||||
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should either initialize it or make it nullable using "?string" instead.');
|
||||
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
|
||||
|
||||
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
|
||||
}
|
||||
@ -150,7 +150,7 @@ class PropertyAccessorTest extends TestCase
|
||||
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
|
||||
{
|
||||
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
|
||||
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Have you forgotten to initialize a property or to make the return type nullable using "?array" instead?');
|
||||
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
|
||||
|
||||
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
|
||||
}
|
||||
|
@ -58,6 +58,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
||||
private $enableConstructorExtraction;
|
||||
private $accessFlags;
|
||||
|
||||
private $arrayMutatorPrefixesFirst;
|
||||
private $arrayMutatorPrefixesLast;
|
||||
|
||||
/**
|
||||
* @param string[]|null $mutatorPrefixes
|
||||
* @param string[]|null $accessorPrefixes
|
||||
@ -70,6 +73,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
||||
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
|
||||
$this->enableConstructorExtraction = $enableConstructorExtraction;
|
||||
$this->accessFlags = $accessFlags;
|
||||
|
||||
$this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
|
||||
$this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,7 +411,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
||||
$ucProperty = ucfirst($property);
|
||||
$ucSingulars = (array) Inflector::singularize($ucProperty);
|
||||
|
||||
foreach ($this->mutatorPrefixes as $prefix) {
|
||||
$mutatorPrefixes = \in_array($ucProperty, $ucSingulars, true) ? $this->arrayMutatorPrefixesLast : $this->arrayMutatorPrefixesFirst;
|
||||
|
||||
foreach ($mutatorPrefixes as $prefix) {
|
||||
$names = [$ucProperty];
|
||||
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||
$names = array_merge($names, $ucSingulars);
|
||||
|
@ -70,6 +70,7 @@ class ReflectionExtractorTest extends TestCase
|
||||
'realParent',
|
||||
'xTotals',
|
||||
'YT',
|
||||
'date',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
@ -109,6 +110,7 @@ class ReflectionExtractorTest extends TestCase
|
||||
'foo4',
|
||||
'foo5',
|
||||
'files',
|
||||
'date',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
@ -173,6 +175,8 @@ class ReflectionExtractorTest extends TestCase
|
||||
['staticSetter', null],
|
||||
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')]],
|
||||
['realParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['date', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class)]],
|
||||
['dates', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class))]],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -210,4 +210,12 @@ class Dummy extends ParentDummy
|
||||
public function getYT()
|
||||
{
|
||||
}
|
||||
|
||||
public function setDate(\DateTime $date)
|
||||
{
|
||||
}
|
||||
|
||||
public function addDate(\DateTime $date)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,48 @@ Routing Component
|
||||
|
||||
The Routing component maps an HTTP request to a set of configuration variables.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
```
|
||||
$ composer require symfony/routing
|
||||
```
|
||||
|
||||
```php
|
||||
use App\Controller\BlogController;
|
||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
$route = new Route('/blog/{slug}', ['_controller' => BlogController::class]);
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('blog_show', $route);
|
||||
|
||||
$context = new RequestContext();
|
||||
|
||||
// Routing can match routes with incoming requests
|
||||
$matcher = new UrlMatcher($routes, $context);
|
||||
$parameters = $matcher->match('/blog/lorem-ipsum');
|
||||
// $parameters = [
|
||||
// '_controller' => 'App\Controller\BlogController',
|
||||
// 'slug' => 'lorem-ipsum',
|
||||
// '_route' => 'blog_show'
|
||||
// ]
|
||||
|
||||
// Routing can also generate URLs for a given route
|
||||
$generator = new UrlGenerator($routes, $context);
|
||||
$url = $generator->generate('blog_show', [
|
||||
'slug' => 'my-blog-post',
|
||||
]);
|
||||
// $url = '/blog/my-blog-post'
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/routing.html)
|
||||
* [Documentation](https://symfony.com/doc/current/routing.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
|
@ -45,6 +45,11 @@ class TokenStorage implements TokenStorageInterface, ResetInterface
|
||||
*/
|
||||
public function setToken(TokenInterface $token = null)
|
||||
{
|
||||
if ($token) {
|
||||
// ensure any initializer is called
|
||||
$this->getToken();
|
||||
}
|
||||
|
||||
$this->initializer = null;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
@ -52,6 +52,11 @@ final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceS
|
||||
public function setToken(TokenInterface $token = null): void
|
||||
{
|
||||
$this->storage->setToken($token);
|
||||
|
||||
if ($token && $this->enableUsageTracking) {
|
||||
// increments the internal session usage index
|
||||
$this->sessionLocator->get('session')->getMetadataBag();
|
||||
}
|
||||
}
|
||||
|
||||
public function enableUsageTracking(): void
|
||||
|
@ -158,7 +158,7 @@ class SwitchUserListener extends AbstractListener
|
||||
|
||||
try {
|
||||
$this->provider->loadUserByUsername($nonExistentUsername);
|
||||
} catch (AuthenticationException $e) {
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
} catch (AuthenticationException $e) {
|
||||
$this->provider->loadUserByUsername($currentUsername);
|
||||
|
@ -406,9 +406,9 @@ class ContextListenerTest extends TestCase
|
||||
|
||||
private function handleEventWithPreviousSession($userProviders, UserInterface $user = null, RememberMeServicesInterface $rememberMeServices = null)
|
||||
{
|
||||
$user = $user ?: new User('foo', 'bar');
|
||||
$tokenUser = $user ?: new User('foo', 'bar');
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('_security_context_key', serialize(new UsernamePasswordToken($user, '', 'context_key', ['ROLE_USER'])));
|
||||
$session->set('_security_context_key', serialize(new UsernamePasswordToken($tokenUser, '', 'context_key', ['ROLE_USER'])));
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
@ -431,6 +431,10 @@ class ContextListenerTest extends TestCase
|
||||
}
|
||||
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
|
||||
|
||||
if (null !== $user) {
|
||||
++$usageIndex;
|
||||
}
|
||||
|
||||
$this->assertSame($usageIndex, $session->getUsageIndex());
|
||||
$tokenStorage->getToken();
|
||||
$this->assertSame(1 + $usageIndex, $session->getUsageIndex());
|
||||
|
@ -17,7 +17,7 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5",
|
||||
"symfony/security-core": "^4.4.7|^5.0.7",
|
||||
"symfony/security-core": "^4.4.8|^5.0.8",
|
||||
"symfony/http-foundation": "^4.4.7|^5.0.7",
|
||||
"symfony/http-kernel": "^4.4|^5.0",
|
||||
"symfony/property-access": "^4.4|^5.0"
|
||||
|
@ -450,7 +450,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
*/
|
||||
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, string $parameterName, $parameterData, array $context, string $format = null)
|
||||
{
|
||||
if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
|
||||
if (null === $this->propertyTypeExtractor || null === $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
|
||||
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
|
||||
}
|
||||
|
||||
@ -576,6 +576,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
unset($context[$key]);
|
||||
}
|
||||
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
|
||||
unset($context[self::OBJECT_TO_POPULATE]);
|
||||
unset($context['cache_key']); // avoid artificially different keys
|
||||
|
||||
try {
|
||||
|
@ -174,6 +174,14 @@ class AbstractObjectNormalizerTest extends TestCase
|
||||
$this->assertEquals('bar', $stringCollection->children[1]);
|
||||
}
|
||||
|
||||
public function testDenormalizeNotSerializableObjectToPopulate()
|
||||
{
|
||||
$normalizer = new AbstractObjectNormalizerDummy();
|
||||
$normalizedData = $normalizer->denormalize(['foo' => 'foo'], Dummy::class, null, [AbstractObjectNormalizer::OBJECT_TO_POPULATE => new NotSerializable()]);
|
||||
|
||||
$this->assertSame('foo', $normalizedData->foo);
|
||||
}
|
||||
|
||||
private function getDenormalizerForStringCollection()
|
||||
{
|
||||
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
|
||||
@ -442,3 +450,15 @@ class ArrayDenormalizerDummy implements DenormalizerInterface, SerializerAwareIn
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
}
|
||||
|
||||
class NotSerializable
|
||||
{
|
||||
public function __sleep()
|
||||
{
|
||||
if (class_exists(\Error::class)) {
|
||||
throw new \Error('not serializable');
|
||||
}
|
||||
|
||||
throw new \Exception('not serializable');
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class UrlValidator extends ConstraintValidator
|
||||
{
|
||||
const PATTERN = '~^
|
||||
(%s):// # protocol
|
||||
(([\_\.\pL\pN-]+:)?([\_\.\pL\pN-]+)@)? # basic auth
|
||||
(((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)? # basic auth
|
||||
(
|
||||
([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
|
||||
| # or
|
||||
|
@ -370,6 +370,18 @@
|
||||
<source>This value is not a valid hostname.</source>
|
||||
<target>Значение не является корректным именем хоста.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="96">
|
||||
<source>The number of elements in this collection should be a multiple of {{ compared_value }}.</source>
|
||||
<target>Количество элементов в этой коллекции должно быть кратным {{ compared_value }}.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="97">
|
||||
<source>This value should satisfy at least one of the following constraints:</source>
|
||||
<target>Значение должно удовлетворять как минимум одному из следующих ограничений:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="98">
|
||||
<source>Each element of this collection should satisfy its own set of constraints.</source>
|
||||
<target>Каждый элемент этой коллекции должен удовлетворять своему собственному набору ограничений.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -366,6 +366,22 @@
|
||||
<source>This value should be between {{ min }} and {{ max }}.</source>
|
||||
<target>Значення має бути між {{ min }} та {{ max }}.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="95">
|
||||
<source>This value is not a valid hostname.</source>
|
||||
<target>Значення не є дійсним іменем хоста.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="96">
|
||||
<source>The number of elements in this collection should be a multiple of {{ compared_value }}.</source>
|
||||
<target>Кількість елементів у цій колекції повинна бути кратною {{ compared_value }}.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="97">
|
||||
<source>This value should satisfy at least one of the following constraints:</source>
|
||||
<target>Значення повинно задовольняти хоча б одному з наступних обмежень:</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="98">
|
||||
<source>Each element of this collection should satisfy its own set of constraints.</source>
|
||||
<target>Кожен елемент цієї колекції повинен задовольняти власному набору обмежень.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -153,6 +153,8 @@ class UrlValidatorTest extends ConstraintValidatorTestCase
|
||||
['http://user.name:pass.word@symfony.com'],
|
||||
['http://user-name@symfony.com'],
|
||||
['http://user_name@symfony.com'],
|
||||
['http://u%24er:password@symfony.com'],
|
||||
['http://user:pa%24%24word@symfony.com'],
|
||||
['http://symfony.com?'],
|
||||
['http://symfony.com?query=1'],
|
||||
['http://symfony.com/?query=1'],
|
||||
@ -251,6 +253,8 @@ class UrlValidatorTest extends ConstraintValidatorTestCase
|
||||
['http://:password@@symfony.com'],
|
||||
['http://username:passwordsymfony.com'],
|
||||
['http://usern@me:password@symfony.com'],
|
||||
['http://nota%hex:password@symfony.com'],
|
||||
['http://username:nota%hex@symfony.com'],
|
||||
['http://example.com/exploit.html?<script>alert(1);</script>'],
|
||||
['http://example.com/exploit.html?hel lo'],
|
||||
['http://example.com/exploit.html?not_a%hex'],
|
||||
|
@ -615,7 +615,7 @@ class Inline
|
||||
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||
}
|
||||
if (self::$exceptionOnInvalidType) {
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
Reference in New Issue
Block a user