Merge branch '4.4' into 5.1

* 4.4:
  Fix transient tests
  Fix class resolution in Doctrine EventListenerPass
  [Serializer] Fix tests  marked as incomplete
  [Translator] fix handling plural for floating numbers
  fix redis messenger options with dsn
  Update ConsoleEvents.php
  allow Doctrine persistence 2 too
  [Messenger] Fix transporting non-UTF8 payloads by encoding them using base 64
  add doctrine/persistence as a dev requirement
  Exclude non-initialized properties accessed with getters
This commit is contained in:
Nicolas Grekas 2021-01-27 10:04:36 +01:00
commit abf8010eae
16 changed files with 341 additions and 21 deletions

View File

@ -85,7 +85,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
if (!isset($managerDefs[$con])) {
$managerDef = $parentDef = $this->getEventManagerDef($container, $con);
while ($parentDef instanceof ChildDefinition) {
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
$parentDef = $container->findDefinition($parentDef->getParent());
}
$managerClass = $container->getParameterBag()->resolveValue($parentDef->getClass());

View File

@ -157,9 +157,6 @@ class AbstractControllerTest extends TestCase
$controller->getUser();
}
/**
* @param $token
*/
private function getContainerWithTokenStorage($token = null): Container
{
$tokenStorage = $this->getMockBuilder(\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage::class)->getMock();
@ -484,10 +481,10 @@ class AbstractControllerTest extends TestCase
public function testRedirect()
{
$controller = $this->createController();
$response = $controller->redirect('http://dunglas.fr', 301);
$response = $controller->redirect('https://dunglas.fr', 301);
$this->assertInstanceOf(\Symfony\Component\HttpFoundation\RedirectResponse::class, $response);
$this->assertSame('http://dunglas.fr', $response->getTargetUrl());
$this->assertSame('https://dunglas.fr', $response->getTargetUrl());
$this->assertSame(301, $response->getStatusCode());
}
@ -532,7 +529,7 @@ class AbstractControllerTest extends TestCase
public function testGetDoctrine()
{
$doctrine = $this->getMockBuilder(\Doctrine\Persistence\ManagerRegistry::class)->getMock();
$doctrine = $this->createMock(ManagerRegistry::class);
$container = new Container();
$container->set('doctrine', $doctrine);

View File

@ -35,6 +35,7 @@
"require-dev": {
"doctrine/annotations": "^1.10.4",
"doctrine/cache": "~1.0",
"doctrine/persistence": "^1.3|^2.0",
"symfony/asset": "^5.1",
"symfony/browser-kit": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",

View File

@ -21,7 +21,7 @@ final class ConsoleEvents
/**
* The COMMAND event allows you to attach listeners before any command is
* executed by the console. It also allows you to modify the command, input and output
* before they are handled to the command.
* before they are handed to the command.
*
* @Event("Symfony\Component\Console\Event\ConsoleCommandEvent")
*/

View File

@ -23,6 +23,9 @@ use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* @group time-sensitive
*/
class InlineFragmentRendererTest extends TestCase
{
public function testRender()
@ -253,8 +256,7 @@ class InlineFragmentRendererTest extends TestCase
}
/**
* Creates a Kernel expecting a request equals to $request
* Allows delta in comparison in case REQUEST_TIME changed by 1 second.
* Creates a Kernel expecting a request equals to $request.
*/
private function getKernelExpectingRequest(Request $request, $strict = false)
{
@ -262,7 +264,7 @@ class InlineFragmentRendererTest extends TestCase
$kernel
->expects($this->once())
->method('handle')
->with($this->equalTo($request, 1))
->with($request)
->willReturn(new Response('foo'));
return $kernel;

View File

@ -121,6 +121,19 @@ class ConnectionTest extends TestCase
);
}
public function testFromDsnWithMixDsnQueryOptions()
{
$this->assertEquals(
Connection::fromDsn('redis://localhost/queue/group1?serializer=2', ['consumer' => 'specific-consumer']),
Connection::fromDsn('redis://localhost/queue/group1/specific-consumer?serializer=2')
);
$this->assertEquals(
Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['consumer' => 'specific-consumer']),
Connection::fromDsn('redis://localhost/queue/group1/consumer1')
);
}
/**
* @group legacy
*/

View File

@ -105,7 +105,8 @@ class Connection
throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn));
}
if (isset($parsedUrl['query'])) {
parse_str($parsedUrl['query'], $redisOptions);
parse_str($parsedUrl['query'], $dsnOptions);
$redisOptions = array_merge($redisOptions, $dsnOptions);
}
self::validateOptions($redisOptions);

View File

@ -76,6 +76,17 @@ class PhpSerializerTest extends TestCase
$encoded = $serializer->encode($envelope);
$this->assertStringNotContainsString('DummyPhpSerializerNonSendableStamp', $encoded['body']);
}
public function testNonUtf8IsBase64Encoded()
{
$serializer = new PhpSerializer();
$envelope = new Envelope(new DummyMessage("\xE9"));
$encoded = $serializer->encode($envelope);
$this->assertTrue((bool) preg_match('//u', $encoded['body']), 'Encodes non-UTF8 payloads');
$this->assertEquals($envelope, $serializer->decode($encoded));
}
}
class DummyPhpSerializerNonSendableStamp implements NonSendableStampInterface

View File

@ -29,6 +29,10 @@ class PhpSerializer implements SerializerInterface
throw new MessageDecodingFailedException('Encoded envelope should have at least a "body".');
}
if (false === strpos($encodedEnvelope['body'], '}', -1)) {
$encodedEnvelope['body'] = base64_decode($encodedEnvelope['body']);
}
$serializeEnvelope = stripslashes($encodedEnvelope['body']);
return $this->safelyUnserialize($serializeEnvelope);
@ -43,6 +47,10 @@ class PhpSerializer implements SerializerInterface
$body = addslashes(serialize($envelope));
if (!preg_match('//u', $body)) {
$body = base64_encode($body);
}
return [
'body' => $body,
];

View File

@ -110,9 +110,19 @@ class ObjectNormalizer extends AbstractObjectNormalizer
$checkPropertyInitialization = \PHP_VERSION_ID >= 70400;
// properties
foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) {
if ($checkPropertyInitialization && !$reflProperty->isInitialized($object)) {
continue;
foreach ($reflClass->getProperties() as $reflProperty) {
if ($checkPropertyInitialization) {
$isPublic = $reflProperty->isPublic();
if (!$isPublic) {
$reflProperty->setAccessible(true);
}
if (!$reflProperty->isInitialized($object)) {
unset($attributes[$reflProperty->name]);
continue;
}
if (!$isPublic) {
continue;
}
}
if ($reflProperty->isStatic() || !$this->isAllowedAttribute($object, $reflProperty->name, $format, $context)) {

View 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\Serializer\Tests\Fixtures;
/**
* @author Alexander Borisov <boshurik@gmail.com>
*/
final class Php74DummyPrivate
{
private string $uninitializedProperty;
private string $initializedProperty = 'defaultValue';
public function getUninitializedProperty(): string
{
return $this->uninitializedProperty;
}
public function getInitializedProperty(): string
{
return $this->initializedProperty;
}
}

View File

@ -38,8 +38,6 @@ trait IgnoredAttributesTestTrait
$normalizer->normalize($objectOuter, null, $context)
);
$this->markTestIncomplete('AbstractObjectNormalizer::getAttributes caches attributes by class instead of by class+context, reusing the normalizer with different config therefore fails. This is being fixed in https://github.com/symfony/symfony/pull/30907');
$context = ['ignored_attributes' => ['foo', 'inner']];
$this->assertEquals(
[

View File

@ -32,6 +32,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy;
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
use Symfony\Component\Serializer\Tests\Fixtures\OtherSerializedNameDummy;
use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy;
use Symfony\Component\Serializer\Tests\Fixtures\Php74DummyPrivate;
use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder;
use Symfony\Component\Serializer\Tests\Normalizer\Features\AttributesTestTrait;
use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksTestTrait;
@ -124,6 +125,18 @@ class ObjectNormalizerTest extends TestCase
);
}
/**
* @requires PHP 7.4
*/
public function testNormalizeObjectWithUninitializedPrivateProperties()
{
$obj = new Php74DummyPrivate();
$this->assertEquals(
['initializedProperty' => 'defaultValue'],
$this->normalizer->normalize($obj, 'any')
);
}
public function testDenormalize()
{
$obj = $this->normalizer->denormalize(

View File

@ -0,0 +1,220 @@
<?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\Translation;
/**
* Returns the plural rules for a given locale.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since Symfony 4.2, use IdentityTranslator instead
*/
class PluralizationRules
{
private static $rules = [];
/**
* Returns the plural position to use for the given locale and number.
*
* @param float $number The number
* @param string $locale The locale
*
* @return int The plural position
*/
public static function get($number, $locale/*, bool $triggerDeprecation = true*/)
{
$number = abs($number);
if (3 > \func_num_args() || func_get_arg(2)) {
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), \E_USER_DEPRECATED);
}
if ('pt_BR' === $locale) {
// temporary set a locale for brazilian
$locale = 'xbr';
}
if (\strlen($locale) > 3) {
$locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
}
if (isset(self::$rules[$locale])) {
$return = self::$rules[$locale]($number);
if (!\is_int($return) || $return < 0) {
return 0;
}
return $return;
}
/*
* The plural rules are derived from code of the Zend Framework (2010-09-25),
* which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
*/
switch ($locale) {
case 'az':
case 'bo':
case 'dz':
case 'id':
case 'ja':
case 'jv':
case 'ka':
case 'km':
case 'kn':
case 'ko':
case 'ms':
case 'th':
case 'tr':
case 'vi':
case 'zh':
return 0;
case 'af':
case 'bn':
case 'bg':
case 'ca':
case 'da':
case 'de':
case 'el':
case 'en':
case 'eo':
case 'es':
case 'et':
case 'eu':
case 'fa':
case 'fi':
case 'fo':
case 'fur':
case 'fy':
case 'gl':
case 'gu':
case 'ha':
case 'he':
case 'hu':
case 'is':
case 'it':
case 'ku':
case 'lb':
case 'ml':
case 'mn':
case 'mr':
case 'nah':
case 'nb':
case 'ne':
case 'nl':
case 'nn':
case 'no':
case 'oc':
case 'om':
case 'or':
case 'pa':
case 'pap':
case 'ps':
case 'pt':
case 'so':
case 'sq':
case 'sv':
case 'sw':
case 'ta':
case 'te':
case 'tk':
case 'ur':
case 'zu':
return (1 == $number) ? 0 : 1;
case 'am':
case 'bh':
case 'fil':
case 'fr':
case 'gun':
case 'hi':
case 'hy':
case 'ln':
case 'mg':
case 'nso':
case 'xbr':
case 'ti':
case 'wa':
return ($number < 2) ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sh':
case 'sr':
case 'uk':
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'cs':
case 'sk':
return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
case 'ga':
return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
case 'lt':
return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'sl':
return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
case 'mk':
return (1 == $number % 10) ? 0 : 1;
case 'mt':
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
case 'lv':
return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
case 'pl':
return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
case 'cy':
return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
case 'ro':
return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
case 'ar':
return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
default:
return 0;
}
}
/**
* Overrides the default plural rule for a given locale.
*
* @param callable $rule A PHP callable
* @param string $locale The locale
*/
public static function set(callable $rule, $locale)
{
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), \E_USER_DEPRECATED);
if ('pt_BR' === $locale) {
// temporary set a locale for brazilian
$locale = 'xbr';
}
if (\strlen($locale) > 3) {
$locale = substr($locale, 0, -\strlen(strrchr($locale, '_')));
}
self::$rules[$locale] = $rule;
}
}

View File

@ -142,11 +142,11 @@ class TranslatorTest extends TestCase
/**
* @dataProvider getChooseTests
*/
public function testChoose($expected, $id, $number)
public function testChoose($expected, $id, $number, $locale = null)
{
$translator = $this->getTranslator();
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
$this->assertEquals($expected, $translator->trans($id, ['%count%' => $number], null, $locale));
}
public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
@ -255,6 +255,18 @@ class TranslatorTest extends TestCase
['', '|', 1],
// Empty plural set (3 plural forms) from a .PO file
['', '||', 1],
// Floating values
['1.5 liters', '%count% liter|%count% liters', 1.5],
['1.5 litre', '%count% litre|%count% litres', 1.5, 'fr'],
// Negative values
['-1 degree', '%count% degree|%count% degrees', -1],
['-1 degré', '%count% degré|%count% degrés', -1],
['-1.5 degrees', '%count% degree|%count% degrees', -1.5],
['-1.5 degré', '%count% degré|%count% degrés', -1.5, 'fr'],
['-2 degrees', '%count% degree|%count% degrees', -2],
['-2 degrés', '%count% degré|%count% degrés', -2],
];
}

View File

@ -136,8 +136,10 @@ EOF;
* which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
*/
private function getPluralizationRule(int $number, string $locale): int
private function getPluralizationRule(float $number, string $locale): int
{
$number = abs($number);
switch ('pt_BR' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
case 'af':
case 'bn':
@ -205,7 +207,7 @@ EOF;
case 'pt_BR':
case 'ti':
case 'wa':
return ((0 == $number) || (1 == $number)) ? 0 : 1;
return ($number < 2) ? 0 : 1;
case 'be':
case 'bs':