Skip to content

Commit 8914597

Browse files
Merge branch '5.4' into 6.3
* 5.4: [Translation] Fix `TranslationNodeVisitor` with constant domain [Messenger] [AMQP] Throw exception on `nack` callback [Validator] revise Latvian translations [ErrorHandler] Fix `RecursiveDirectoryIterator` exception with wrong composer autoload [HttpFoundation] Request without content-type or content-length header should result in null values, not empty strings [Cache] Fix possible infinite loop in `CachePoolPass` [Mime] Fix undefined array key 0 when empty sender [Console] Allow '0' as a $shortcut in InputOption.php fix multi-byte code area to convert [Validator] Make it explicit when English translation differs from its resource name
2 parents 44051bd + c32e249 commit 8914597

File tree

74 files changed

+774
-651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+774
-651
lines changed

.github/sync-translations.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, string $d
3333
$metadata = $messages->getMetadata($source, $domain);
3434

3535
$translation->setAttribute('id', $metadata['id']);
36+
if (isset($metadata['resname'])) {
37+
$translation->setAttribute('resname', $metadata['resname']);
38+
}
3639

3740
$s = $translation->appendChild($dom->createElement('source'));
3841
$s->appendChild($dom->createTextNode($source));
@@ -64,23 +67,32 @@ function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, string $d
6467
$dir = __DIR__.'/../src/Symfony/Component/'.$component.'/Resources/translations';
6568

6669
$enCatalogue = (new XliffFileLoader())->load($dir.'/'.$domain.'.en.xlf', 'en', $domain);
70+
file_put_contents($dir.'/'.$domain.'.en.xlf', dumpXliff1('en', $enCatalogue, $domain));
71+
6772
$finder = new Finder();
6873

6974
foreach ($finder->files()->in($dir)->name('*.xlf') as $file) {
7075
$locale = substr($file->getBasename(), 1 + strlen($domain), -4);
7176

77+
if ('en' === $locale) {
78+
continue;
79+
}
80+
7281
$catalogue = (new XliffFileLoader())->load($file, $locale, $domain);
7382
$localeCatalogue = new MessageCatalogue($locale);
7483

75-
foreach ($enCatalogue->all($domain) as $id => $translation) {
84+
foreach ($enCatalogue->all($domain) as $resname => $source) {
7685
$metadata = [];
77-
if ($catalogue->defines($id, $domain)) {
78-
$translation = $catalogue->get($id, $domain);
79-
$metadata = $catalogue->getMetadata($id, $domain);
86+
if ($catalogue->defines($resname, $domain)) {
87+
$translation = $catalogue->get($resname, $domain);
88+
$metadata = $catalogue->getMetadata($resname, $domain);
89+
}
90+
$metadata['id'] = $enCatalogue->getMetadata($resname, $domain)['id'];
91+
if ($resname !== $source) {
92+
$metadata['resname'] = $resname;
8093
}
81-
$metadata['id'] = $enCatalogue->getMetadata($id, $domain)['id'];
82-
$localeCatalogue->set($id, $translation, $domain);
83-
$localeCatalogue->setMetadata($id, $metadata, $domain);
94+
$localeCatalogue->set($source, $translation, $domain);
95+
$localeCatalogue->setMetadata($source, $metadata, $domain);
8496
}
8597

8698
file_put_contents($file, dumpXliff1('en', $localeCatalogue, $domain));

src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ private function getReadDomainFromNode(Node $node): ?string
149149
return $node->getAttribute('value');
150150
}
151151

152+
if (
153+
$node instanceof FunctionExpression
154+
&& 'constant' === $node->getAttribute('name')
155+
) {
156+
$nodeArguments = $node->getNode('arguments');
157+
if ($nodeArguments->getIterator()->current() instanceof ConstantExpression) {
158+
$constantName = $nodeArguments->getIterator()->current()->getAttribute('value');
159+
if (\defined($constantName)) {
160+
$value = \constant($constantName);
161+
if (\is_string($value)) {
162+
return $value;
163+
}
164+
}
165+
}
166+
}
167+
152168
return self::UNDEFINED_DOMAIN;
153169
}
154170

src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
class TwigExtractorTest extends TestCase
2424
{
25+
public const CUSTOM_DOMAIN = 'domain';
26+
2527
/**
2628
* @dataProvider getExtractData
2729
*/
@@ -76,6 +78,11 @@ public static function getExtractData()
7678
// make sure this works with twig's named arguments
7779
['{{ "new key" | trans(domain="domain") }}', ['new key' => 'domain']],
7880

81+
// make sure this works with const domain
82+
['{{ "new key" | trans({}, constant(\'Symfony\\\\Bridge\\\\Twig\\\\Tests\\\\Translation\\\\TwigExtractorTest::CUSTOM_DOMAIN\')) }}', ['new key' => self::CUSTOM_DOMAIN]],
83+
['{% trans from constant(\'Symfony\\\\Bridge\\\\Twig\\\\Tests\\\\Translation\\\\TwigExtractorTest::CUSTOM_DOMAIN\') %}new key{% endtrans %}', ['new key' => self::CUSTOM_DOMAIN]],
84+
['{{ t("new key", {}, constant(\'Symfony\\\\Bridge\\\\Twig\\\\Tests\\\\Translation\\\\TwigExtractorTest::CUSTOM_DOMAIN\')) | trans() }}', ['new key' => self::CUSTOM_DOMAIN]],
85+
7986
// concat translations
8087
['{{ ("new" ~ " key") | trans() }}', ['new key' => 'messages']],
8188
['{{ ("another " ~ "new " ~ "key") | trans() }}', ['another new key' => 'messages']],

src/Symfony/Component/Cache/DependencyInjection/CachePoolPass.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ public function process(ContainerBuilder $container)
181181
$container->removeDefinition('cache.early_expiration_handler');
182182
}
183183

184-
$notAliasedCacheClearerId = $aliasedCacheClearerId = 'cache.global_clearer';
185-
while ($container->hasAlias('cache.global_clearer')) {
186-
$aliasedCacheClearerId = (string) $container->getAlias('cache.global_clearer');
184+
$notAliasedCacheClearerId = 'cache.global_clearer';
185+
while ($container->hasAlias($notAliasedCacheClearerId)) {
186+
$notAliasedCacheClearerId = (string) $container->getAlias($notAliasedCacheClearerId);
187187
}
188-
if ($container->hasDefinition($aliasedCacheClearerId)) {
188+
if ($container->hasDefinition($notAliasedCacheClearerId)) {
189189
$clearers[$notAliasedCacheClearerId] = $allPools;
190190
}
191191

src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolPassTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
2121
use Symfony\Component\DependencyInjection\ChildDefinition;
2222
use Symfony\Component\DependencyInjection\ContainerBuilder;
23+
use Symfony\Component\DependencyInjection\ContainerInterface;
2324
use Symfony\Component\DependencyInjection\Definition;
2425
use Symfony\Component\DependencyInjection\Reference;
26+
use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer;
2527

2628
class CachePoolPassTest extends TestCase
2729
{
@@ -232,4 +234,33 @@ public function testChainAdapterPool()
232234
$this->assertInstanceOf(ChildDefinition::class, $doctrineCachePool);
233235
$this->assertSame('cache.app', $doctrineCachePool->getParent());
234236
}
237+
238+
public function testGlobalClearerAlias()
239+
{
240+
$container = new ContainerBuilder();
241+
$container->setParameter('kernel.container_class', 'app');
242+
$container->setParameter('kernel.project_dir', 'foo');
243+
244+
$container->register('cache.default_clearer', Psr6CacheClearer::class);
245+
246+
$container->setDefinition('cache.system_clearer', new ChildDefinition('cache.default_clearer'));
247+
248+
$container->setDefinition('cache.foo_bar_clearer', new ChildDefinition('cache.default_clearer'));
249+
$container->setAlias('cache.global_clearer', 'cache.foo_bar_clearer');
250+
251+
$container->register('cache.adapter.array', ArrayAdapter::class)
252+
->setAbstract(true)
253+
->addTag('cache.pool');
254+
255+
$cachePool = new ChildDefinition('cache.adapter.array');
256+
$cachePool->addTag('cache.pool', ['clearer' => 'cache.system_clearer']);
257+
$container->setDefinition('app.cache_pool', $cachePool);
258+
259+
$this->cachePoolPass->process($container);
260+
261+
$definition = $container->getDefinition('cache.foo_bar_clearer');
262+
263+
$this->assertTrue($definition->hasTag('cache.pool.clearer'));
264+
$this->assertEquals(['app.cache_pool' => new Reference('app.cache_pool', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)], $definition->getArgument(0));
265+
}
235266
}

src/Symfony/Component/Console/Input/InputOption.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function __construct(string $name, string|array $shortcut = null, int $mo
7575
throw new InvalidArgumentException('An option name cannot be empty.');
7676
}
7777

78-
if (empty($shortcut)) {
78+
if ('' === $shortcut || [] === $shortcut) {
7979
$shortcut = null;
8080
}
8181

@@ -84,10 +84,10 @@ public function __construct(string $name, string|array $shortcut = null, int $mo
8484
$shortcut = implode('|', $shortcut);
8585
}
8686
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
87-
$shortcuts = array_filter($shortcuts);
87+
$shortcuts = array_filter($shortcuts, 'strlen');
8888
$shortcut = implode('|', $shortcuts);
8989

90-
if (empty($shortcut)) {
90+
if ('' === $shortcut) {
9191
throw new InvalidArgumentException('An option shortcut cannot be empty.');
9292
}
9393
}

src/Symfony/Component/Console/Tests/Input/InputOptionTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ public function testShortcut()
5959
$this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts');
6060
$option = new InputOption('foo');
6161
$this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default');
62+
$option = new InputOption('foo', '');
63+
$this->assertNull($option->getShortcut(), '__construct() makes the shortcut null when given an empty string');
64+
$option = new InputOption('foo', []);
65+
$this->assertNull($option->getShortcut(), '__construct() makes the shortcut null when given an empty array');
66+
$option = new InputOption('foo', ['f', '', 'fff']);
67+
$this->assertEquals('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts');
68+
$option = new InputOption('foo', 'f||fff');
69+
$this->assertEquals('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts');
70+
$option = new InputOption('foo', '0');
71+
$this->assertEquals('0', $option->getShortcut(), '-0 is an acceptable shortcut value');
72+
$option = new InputOption('foo', ['0', 'z']);
73+
$this->assertEquals('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in an array');
74+
$option = new InputOption('foo', '0|z');
75+
$this->assertEquals('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in a string-list');
6276
}
6377

6478
public function testModes()

src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ private function getClassCandidates(string $class): array
107107

108108
private function findClassInPath(string $path, string $class, string $prefix): array
109109
{
110-
if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
110+
$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path);
111+
if (!$path || !is_dir($path)) {
111112
return [];
112113
}
113114

src/Symfony/Component/HttpFoundation/ServerBag.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function getHeaders(): array
2929
foreach ($this->parameters as $key => $value) {
3030
if (str_starts_with($key, 'HTTP_')) {
3131
$headers[substr($key, 5)] = $value;
32-
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
32+
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true) && '' !== $value) {
3333
$headers[$key] = $value;
3434
}
3535
}

src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,20 @@ public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
177177
'PHP_AUTH_PW' => '',
178178
], $bag->getHeaders());
179179
}
180+
181+
/**
182+
* An HTTP request without content-type and content-length will result in
183+
* the variables $_SERVER['CONTENT_TYPE'] and $_SERVER['CONTENT_LENGTH']
184+
* containing an empty string in PHP.
185+
*/
186+
public function testRequestWithoutContentTypeAndContentLength()
187+
{
188+
$bag = new ServerBag([
189+
'CONTENT_TYPE' => '',
190+
'CONTENT_LENGTH' => '',
191+
'HTTP_USER_AGENT' => 'foo',
192+
]);
193+
194+
$this->assertSame(['USER_AGENT' => 'foo'], $bag->getHeaders());
195+
}
180196
}

0 commit comments

Comments
 (0)