Merge branch '4.4' into 5.0
* 4.4: Fix wrong namespaces Fix wrong namespaces Fix the reporting of deprecations in twig:lint forward multiple attributes voting flag bumped Symfony version to 4.4.8 updated VERSION for 4.4.7 updated CHANGELOG for 4.4.7 [Validator] Fixed calling getters before resolving groups [HttpKernel][LoggerDataCollector] Prevent keys collisions in the sanitized logs processing
This commit is contained in:
commit
e1a522b470
@ -7,6 +7,18 @@ in 4.4 minor versions.
|
||||
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
||||
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1
|
||||
|
||||
* 4.4.7 (2020-03-30)
|
||||
|
||||
* security #cve-2020-5255 [HttpFoundation] Do not set the default Content-Type based on the Accept header (yceruto)
|
||||
* security #cve-2020-5275 [Security] Fix access_control behavior with unanimous decision strategy (chalasr)
|
||||
* bug #36262 [DI] fix generating TypedReference from PriorityTaggedServiceTrait (nicolas-grekas)
|
||||
* bug #36252 [Security/Http] Allow setting cookie security settings for delete_cookies (wouterj)
|
||||
* bug #36261 [FrameworkBundle] revert to legacy wiring of the session when circular refs are detected (nicolas-grekas)
|
||||
* bug #36259 [DomCrawler] Fix BC break in assertions breaking Panther (dunglas)
|
||||
* bug #36181 [BrowserKit] fixed missing post request parameters in file uploads (codebay)
|
||||
* bug #36216 [Validator] Assert Valid with many groups (phucwan91)
|
||||
* bug #36222 [Console] Fix OutputStream for PHP 7.4 (guillbdx)
|
||||
|
||||
* 4.4.6 (2020-03-27)
|
||||
|
||||
* bug #36169 [HttpKernel] fix locking for PHP 7.4+ (nicolas-grekas)
|
||||
|
@ -103,7 +103,7 @@ EOF
|
||||
$prevErrorHandler = set_error_handler(static function ($level, $message, $file, $line) use (&$prevErrorHandler) {
|
||||
if (E_USER_DEPRECATED === $level) {
|
||||
$templateLine = 0;
|
||||
if (preg_match('/ at line (\d+) /', $message, $matches)) {
|
||||
if (preg_match('/ at line (\d+)[ .]/', $message, $matches)) {
|
||||
$templateLine = $matches[1];
|
||||
}
|
||||
|
||||
@ -229,6 +229,14 @@ EOF
|
||||
$output->text(sprintf('<error> ERROR </error> (line %s)', $line));
|
||||
}
|
||||
|
||||
// If the line is not known (this might happen for deprecations if we fail at detecting the line for instance),
|
||||
// we render the message without context, to ensure the message is displayed.
|
||||
if ($line <= 0) {
|
||||
$output->text(sprintf('<error> >> %s</error> ', $exception->getRawMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->getContext($template, $line) as $lineNumber => $code) {
|
||||
$output->text(sprintf(
|
||||
'%s %-6s %s',
|
||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
class LintCommandTest extends TestCase
|
||||
{
|
||||
@ -66,6 +67,37 @@ class LintCommandTest extends TestCase
|
||||
$this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay()));
|
||||
}
|
||||
|
||||
/**
|
||||
* When deprecations are not reported by the command, the testsuite reporter will catch them so we need to mark the test as legacy.
|
||||
*
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLintFileWithNotReportedDeprecation()
|
||||
{
|
||||
$tester = $this->createCommandTester();
|
||||
$filename = $this->createFile('{{ foo|deprecated_filter }}');
|
||||
|
||||
$ret = $tester->execute(['filename' => [$filename]], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
|
||||
|
||||
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
|
||||
$this->assertStringContainsString('OK in', trim($tester->getDisplay()));
|
||||
}
|
||||
|
||||
public function testLintFileWithReportedDeprecation()
|
||||
{
|
||||
$tester = $this->createCommandTester();
|
||||
$filename = $this->createFile('{{ foo|deprecated_filter }}');
|
||||
|
||||
$ret = $tester->execute(['filename' => [$filename], '--show-deprecations' => true], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
|
||||
|
||||
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
|
||||
$this->assertRegExp('/ERROR in \S+ \(line 1\)/', trim($tester->getDisplay()));
|
||||
$this->assertStringContainsString('Filter "deprecated_filter" is deprecated', trim($tester->getDisplay()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group tty
|
||||
*/
|
||||
public function testLintDefaultPaths()
|
||||
{
|
||||
$tester = $this->createCommandTester();
|
||||
@ -77,7 +109,12 @@ class LintCommandTest extends TestCase
|
||||
|
||||
private function createCommandTester(): CommandTester
|
||||
{
|
||||
$command = new LintCommand(new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/')));
|
||||
$environment = new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/'));
|
||||
$environment->addFilter(new TwigFilter('deprecated_filter', function ($v) {
|
||||
return $v;
|
||||
}, ['deprecated' => true]));
|
||||
|
||||
$command = new LintCommand($environment);
|
||||
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests;
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\CustomPathBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests;
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\TestBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
|
@ -177,7 +177,7 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = $log['message'];
|
||||
$message = '_'.$log['message'];
|
||||
$exception = $log['context']['exception'];
|
||||
|
||||
if ($exception instanceof SilencedErrorContext) {
|
||||
|
@ -176,13 +176,15 @@ class LoggerDataCollectorTest extends TestCase
|
||||
[
|
||||
['message' => 'foo3', 'context' => ['exception' => new \ErrorException('warning', 0, E_USER_WARNING)], 'priority' => 100, 'priorityName' => 'DEBUG'],
|
||||
['message' => 'foo3', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
|
||||
['message' => '0', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
|
||||
],
|
||||
[
|
||||
['message' => 'foo3', 'context' => ['exception' => ['warning', E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG'],
|
||||
['message' => 'foo3', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
|
||||
['message' => '0', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
|
||||
],
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\Sender;
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\Receiver;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\Sync;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
|
||||
namespace Symfony\Component\Messenger\Tests\Transport\Sync;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor;
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Extractor;
|
||||
|
||||
use phpDocumentor\Reflection\Types\Collection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
@ -47,8 +47,10 @@ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
|
||||
*/
|
||||
public function decide(TokenInterface $token, array $attributes, $object = null): bool
|
||||
public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool
|
||||
{
|
||||
$currentDecisionLog = [
|
||||
'attributes' => $attributes,
|
||||
@ -58,7 +60,7 @@ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface
|
||||
|
||||
$this->currentLog[] = &$currentDecisionLog;
|
||||
|
||||
$result = $this->manager->decide($token, $attributes, $object);
|
||||
$result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3));
|
||||
|
||||
$currentDecisionLog['result'] = $result;
|
||||
|
||||
|
@ -20,6 +20,7 @@ use Symfony\Component\Validator\Mapping\MemberMetadata;
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||
use Symfony\Component\Validator\Util\PropertyPath;
|
||||
use Symfony\Component\Validator\Validator\LazyProperty;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
@ -180,7 +181,7 @@ class ExecutionContext implements ExecutionContextInterface
|
||||
$parameters,
|
||||
$this->root,
|
||||
$this->propertyPath,
|
||||
$this->value,
|
||||
$this->getValue(),
|
||||
null,
|
||||
null,
|
||||
$this->constraint
|
||||
@ -199,7 +200,7 @@ class ExecutionContext implements ExecutionContextInterface
|
||||
$parameters,
|
||||
$this->root,
|
||||
$this->propertyPath,
|
||||
$this->value,
|
||||
$this->getValue(),
|
||||
$this->translator,
|
||||
$this->translationDomain
|
||||
);
|
||||
@ -234,6 +235,10 @@ class ExecutionContext implements ExecutionContextInterface
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
if ($this->value instanceof LazyProperty) {
|
||||
return $this->value->getPropertyValue();
|
||||
}
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
<?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\Component\Validator\Tests\Fixtures;
|
||||
|
||||
class EntityWithGroupedConstraintOnMethods
|
||||
{
|
||||
public $bar;
|
||||
|
||||
public function isValidInFoo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getBar()
|
||||
{
|
||||
throw new \Exception('Should not be called');
|
||||
}
|
||||
}
|
@ -14,14 +14,19 @@ namespace Symfony\Component\Validator\Tests\Validator;
|
||||
use Symfony\Component\Translation\IdentityTranslator;
|
||||
use Symfony\Component\Validator\Constraints\All;
|
||||
use Symfony\Component\Validator\Constraints\Collection;
|
||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactory;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextFactory;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA;
|
||||
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildB;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\Entity;
|
||||
use Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods;
|
||||
use Symfony\Component\Validator\Validator\RecursiveValidator;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
@ -118,6 +123,25 @@ class RecursiveValidatorTest extends AbstractTest
|
||||
$this->assertInstanceOf(NotBlank::class, $violations->get(1)->getConstraint());
|
||||
}
|
||||
|
||||
public function testGroupedMethodConstraintValidateInSequence()
|
||||
{
|
||||
$metadata = new ClassMetadata(EntityWithGroupedConstraintOnMethods::class);
|
||||
$metadata->addPropertyConstraint('bar', new NotNull(['groups' => 'Foo']));
|
||||
$metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(['groups' => 'Foo']));
|
||||
$metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(['groups' => 'Bar']));
|
||||
|
||||
$this->metadataFactory->addMetadata($metadata);
|
||||
|
||||
$entity = new EntityWithGroupedConstraintOnMethods();
|
||||
$groups = new GroupSequence(['EntityWithGroupedConstraintOnMethods', 'Foo', 'Bar']);
|
||||
|
||||
$violations = $this->validator->validate($entity, null, $groups);
|
||||
|
||||
$this->assertCount(2, $violations);
|
||||
$this->assertInstanceOf(NotNull::class, $violations->get(0)->getConstraint());
|
||||
$this->assertInstanceOf(IsTrue::class, $violations->get(1)->getConstraint());
|
||||
}
|
||||
|
||||
public function testAllConstraintValidateAllGroupsForNestedConstraints()
|
||||
{
|
||||
$this->metadata->addPropertyConstraint('data', new All(['constraints' => [
|
||||
|
32
src/Symfony/Component/Validator/Validator/LazyProperty.php
Normal file
32
src/Symfony/Component/Validator/Validator/LazyProperty.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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\Component\Validator\Validator;
|
||||
|
||||
/**
|
||||
* A wrapper for a callable initializing a property from a getter.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LazyProperty
|
||||
{
|
||||
private $propertyValueCallback;
|
||||
|
||||
public function __construct(\Closure $propertyValueCallback)
|
||||
{
|
||||
$this->propertyValueCallback = $propertyValueCallback;
|
||||
}
|
||||
|
||||
public function getPropertyValue()
|
||||
{
|
||||
return \call_user_func($this->propertyValueCallback);
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ use Symfony\Component\Validator\Mapping\CascadingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\Mapping\GenericMetadata;
|
||||
use Symfony\Component\Validator\Mapping\GetterMetadata;
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
||||
@ -502,7 +503,13 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
||||
throw new UnsupportedMetadataException(sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', \is_object($propertyMetadata) ? \get_class($propertyMetadata) : \gettype($propertyMetadata)));
|
||||
}
|
||||
|
||||
$propertyValue = $propertyMetadata->getPropertyValue($object);
|
||||
if ($propertyMetadata instanceof GetterMetadata) {
|
||||
$propertyValue = new LazyProperty(static function () use ($propertyMetadata, $object) {
|
||||
return $propertyMetadata->getPropertyValue($object);
|
||||
});
|
||||
} else {
|
||||
$propertyValue = $propertyMetadata->getPropertyValue($object);
|
||||
}
|
||||
|
||||
$this->validateGenericNode(
|
||||
$propertyValue,
|
||||
@ -729,6 +736,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
||||
$validator = $this->validatorFactory->getInstance($constraint);
|
||||
$validator->initialize($context);
|
||||
|
||||
if ($value instanceof LazyProperty) {
|
||||
$value = $value->getPropertyValue();
|
||||
}
|
||||
|
||||
try {
|
||||
$validator->validate($value, $constraint);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
|
Reference in New Issue
Block a user