diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md
index 885d5e0af2..fb773cc1b8 100644
--- a/UPGRADE-5.0.md
+++ b/UPGRADE-5.0.md
@@ -393,7 +393,7 @@ Routing
-------
* The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router
- options have been removed.
+ options have been removed. If you are using multiple Router instances and need separate caches for them, set a unique `cache_dir` per Router instance instead.
* `Serializable` implementing methods for `Route` and `CompiledRoute` are final.
Instead of overwriting them, use `__serialize` and `__unserialize` as extension points which are forward compatible
with the new serialization methods in PHP 7.4.
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
index 35e806b10c..c5dcb2b043 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
@@ -40,7 +40,6 @@ class SymfonyTestsListenerTrait
private $expectedDeprecations = array();
private $gatheredDeprecations = array();
private $previousErrorHandler;
- private $reportUselessTests;
private $error;
private $runsInSeparateProcess = false;
@@ -194,10 +193,6 @@ class SymfonyTestsListenerTrait
public function startTest($test)
{
if (-2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
- if (null !== $test->getTestResultObject()) {
- $this->reportUselessTests = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything();
- }
-
// This event is triggered before the test is re-run in isolation
if ($this->willBeIsolated($test)) {
$this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec');
@@ -243,11 +238,6 @@ class SymfonyTestsListenerTrait
$className = \get_class($test);
$groups = Test::getGroups($className, $test->getName(false));
- if (null !== $this->reportUselessTests) {
- $test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything($this->reportUselessTests);
- $this->reportUselessTests = null;
- }
-
if ($errored = null !== $this->error) {
$test->getTestResultObject()->addError($test, $this->error, 0);
$this->error = null;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
index cc322847d0..1210b40ee0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
@@ -101,7 +101,7 @@ EOF
(new SymfonyStyle($input, $output))
->table(['Secret', 'Value'] + (null !== $localSecrets ? [2 => 'Local Value'] : []), $rows);
- $io->comment("Local values override secret values.\nUse secrets:set --local to defined them.");
+ $io->comment("Local values override secret values.\nUse secrets:set --local to define them.");
return 0;
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
index f578c2d246..c056928129 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
@@ -23,6 +23,7 @@ use Symfony\Component\Translation\Catalogue\MergeOperation;
use Symfony\Component\Translation\Catalogue\TargetOperation;
use Symfony\Component\Translation\Extractor\ExtractorInterface;
use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\MessageCatalogueInterface;
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
use Symfony\Component\Translation\Writer\TranslationWriterInterface;
@@ -216,6 +217,24 @@ EOF
$resultMessage = 'Translation files were successfully updated';
+ // move new messages to intl domain when possible
+ if (class_exists(\MessageFormatter::class)) {
+ foreach ($operation->getDomains() as $domain) {
+ $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
+ $newMessages = $operation->getNewMessages($domain);
+
+ if ([] === $newMessages || ([] === $currentCatalogue->all($intlDomain) && [] !== $currentCatalogue->all($domain))) {
+ continue;
+ }
+
+ $result = $operation->getResult();
+ $allIntlMessages = $result->all($intlDomain);
+ $currentMessages = array_diff_key($newMessages, $result->all($domain));
+ $result->replace($currentMessages, $domain);
+ $result->replace($allIntlMessages + $newMessages, $intlDomain);
+ }
+ }
+
// show compiled list of messages
if (true === $input->getOption('dump-messages')) {
$extractedMessagesCount = 0;
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 63727d8c1f..9ce51de76b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -311,6 +311,7 @@ class FrameworkExtension extends Extension
$container->removeDefinition('console.command.messenger_failed_messages_retry');
$container->removeDefinition('console.command.messenger_failed_messages_show');
$container->removeDefinition('console.command.messenger_failed_messages_remove');
+ $container->removeDefinition('cache.messenger.restart_workers_signal');
}
if ($this->httpClientConfigEnabled = $this->isConfigEnabled($container, $config['http_client'])) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 0c91768a66..97c392812e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -412,6 +412,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 478b3983a1..07cc383189 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -43,6 +43,7 @@ abstract class KernelTestCase extends TestCase
{
static::ensureKernelShutdown();
static::$kernel = null;
+ static::$booted = false;
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/fragments_and_hinclude.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/fragments_and_hinclude.php
new file mode 100644
index 0000000000..dbcf5b786d
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/fragments_and_hinclude.php
@@ -0,0 +1,8 @@
+loadFromExtension('framework', [
+ 'fragments' => [
+ 'enabled' => true,
+ 'hinclude_default_template' => 'global_hinclude_template',
+ ],
+]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_disabled.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_disabled.php
new file mode 100644
index 0000000000..e02542d977
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_disabled.php
@@ -0,0 +1,5 @@
+loadFromExtension('framework', [
+ 'messenger' => false,
+]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/fragments_and_hinclude.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/fragments_and_hinclude.xml
new file mode 100644
index 0000000000..fb007313b9
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/fragments_and_hinclude.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_disabled.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_disabled.xml
new file mode 100644
index 0000000000..6f57398b30
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_disabled.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/fragments_and_hinclude.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/fragments_and_hinclude.yml
new file mode 100644
index 0000000000..b03f37da79
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/fragments_and_hinclude.yml
@@ -0,0 +1,4 @@
+framework:
+ fragments:
+ enabled: true
+ hinclude_default_template: global_hinclude_template
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_disabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_disabled.yml
new file mode 100644
index 0000000000..1b2d2d1a4f
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_disabled.yml
@@ -0,0 +1,2 @@
+framework:
+ messenger: false
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 28dc65d714..20ae6ae4ac 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -159,6 +159,13 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertFalse($container->hasDefinition('esi'));
}
+ public function testFragmentsAndHinclude()
+ {
+ $container = $this->createContainerFromFile('fragments_and_hinclude');
+ $this->assertTrue($container->hasParameter('fragment.renderer.hinclude.global_template'));
+ $this->assertEquals('global_hinclude_template', $container->getParameter('fragment.renderer.hinclude.global_template'));
+ }
+
public function testSsi()
{
$container = $this->createContainerFromFile('full');
@@ -565,9 +572,23 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertTrue($container->hasDefinition('web_link.add_link_header_listener'));
}
+ public function testMessengerServicesRemovedWhenDisabled()
+ {
+ $container = $this->createContainerFromFile('messenger_disabled');
+ $this->assertFalse($container->hasDefinition('console.command.messenger_consume_messages'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_debug'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_stop_workers'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_setup_transports'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_retry'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_show'));
+ $this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_remove'));
+ $this->assertFalse($container->hasDefinition('cache.messenger.restart_workers_signal'));
+ }
+
public function testMessenger()
{
$container = $this->createContainerFromFile('messenger');
+ $this->assertTrue($container->hasDefinition('console.command.messenger_consume_messages'));
$this->assertTrue($container->hasAlias('messenger.default_bus'));
$this->assertTrue($container->getAlias('messenger.default_bus')->isPublic());
$this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory'));
diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php b/src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php
index 4f889f485c..691c6659d5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php
+++ b/src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php
@@ -11,6 +11,8 @@
namespace Symfony\Bundle\SecurityBundle\Debug;
+use Symfony\Component\VarDumper\Caster\ClassStub;
+
/**
* @author Robin Chalas
*
@@ -35,4 +37,13 @@ trait TraceableListenerTrait
{
return $this->listener;
}
+
+ public function getInfo(): array
+ {
+ return [
+ 'response' => $this->response,
+ 'time' => $this->time,
+ 'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
+ ];
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php
index 736e8e8a25..67b178f3f6 100644
--- a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php
+++ b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php
@@ -59,13 +59,4 @@ final class WrappedLazyListener extends AbstractListener
return $ret;
}
-
- public function getInfo(): array
- {
- return [
- 'response' => $this->response,
- 'time' => $this->time,
- 'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
- ];
- }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php
index 11b921627f..d42a878c9d 100644
--- a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php
+++ b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php
@@ -37,13 +37,4 @@ final class WrappedListener
$this->time = microtime(true) - $startTime;
$this->response = $event->getResponse();
}
-
- public function getInfo(): array
- {
- return [
- 'response' => $this->response,
- 'time' => $this->time,
- 'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
- ];
- }
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
index 77eff72565..761aa314fa 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -114,6 +114,8 @@ class ResolveBindingsPass extends AbstractRecursivePass
return parent::processValue($value, $isRoot);
}
+ $bindingNames = [];
+
foreach ($bindings as $key => $binding) {
list($bindingValue, $bindingId, $used, $bindingType, $file) = $binding->getValues();
if ($used) {
@@ -123,7 +125,11 @@ class ResolveBindingsPass extends AbstractRecursivePass
$this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file];
}
- if (preg_match('/^(?:(?:array|bool|float|int|string) )?\$/', $key)) {
+ if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) {
+ $bindingNames[substr($key, \strlen($m[0]))] = $binding;
+ }
+
+ if (!isset($m[1])) {
continue;
}
@@ -184,11 +190,17 @@ class ResolveBindingsPass extends AbstractRecursivePass
continue;
}
- if (!$typeHint || '\\' !== $typeHint[0] || !isset($bindings[$typeHint = substr($typeHint, 1)])) {
+ if ($typeHint && '\\' === $typeHint[0] && isset($bindings[$typeHint = substr($typeHint, 1)])) {
+ $arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
+
continue;
}
- $arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
+ if (isset($bindingNames[$parameter->name])) {
+ $bindingKey = array_search($binding, $bindings, true);
+ $argumentType = substr($bindingKey, 0, strpos($bindingKey, ' '));
+ $this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name);
+ }
}
if ($arguments !== $call[1]) {
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index 0e23d93382..bd25e3f0ec 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -30,8 +30,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
public function __construct(ContainerInterface $container, \Traversable $loaders = null)
{
$this->container = $container;
- $this->loaders = new \IteratorIterator($loaders ?? new \ArrayIterator());
- $this->loaders = $this->loaders->getInnerIterator();
+ $this->loaders = $loaders ?? new \ArrayIterator();
}
/**
@@ -141,20 +140,32 @@ class EnvVarProcessor implements EnvVarProcessorInterface
}
}
- $loaders = $this->loaders;
- $this->loaders = new \ArrayIterator();
+ if (false === $env || null === $env) {
+ $loaders = $this->loaders;
+ $this->loaders = new \ArrayIterator();
- try {
- while ((false === $env || null === $env) && $loaders->valid()) {
- $loader = $loaders->current();
- $loaders->next();
- $this->loadedVars[] = $vars = $loader->loadEnvVars();
- $env = $vars[$name] ?? false;
+ try {
+ $i = 0;
+ $ended = true;
+ $count = $loaders instanceof \Countable ? $loaders->count() : 0;
+ foreach ($loaders as $loader) {
+ if (\count($this->loadedVars) > $i++) {
+ continue;
+ }
+ $this->loadedVars[] = $vars = $loader->loadEnvVars();
+ if (false !== $env = $vars[$name] ?? false) {
+ $ended = false;
+ break;
+ }
+ }
+ if ($ended || $count === $i) {
+ $loaders = $this->loaders;
+ }
+ } catch (ParameterCircularReferenceException $e) {
+ // skip loaders that need an env var that is not defined
+ } finally {
+ $this->loaders = $loaders;
}
- } catch (ParameterCircularReferenceException $e) {
- // skip loaders that need an env var that is not defined
- } finally {
- $this->loaders = $loaders;
}
if (false === $env || null === $env) {
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
index 87a5cf2261..443114b0a2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
@@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Compiler\DefinitionErrorExceptionPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
@@ -169,4 +170,19 @@ class ResolveBindingsPassTest extends TestCase
$this->assertSame([1 => 'bar'], $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
}
+
+ public function testEmptyBindingTypehint()
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Did you forget to add the type "string" to argument "$apiKey" of method "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy::__construct()"?');
+
+ $container = new ContainerBuilder();
+ $bindings = [
+ 'string $apiKey' => new BoundArgument('foo'),
+ ];
+ $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class);
+ $definition->setBindings($bindings);
+ $pass = new ResolveBindingsPass();
+ $pass->process($container);
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
index 9970eb474f..df329b4f7e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
@@ -3,10 +3,12 @@
namespace Symfony\Component\DependencyInjection\Tests;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
use Symfony\Component\DependencyInjection\EnvVarProcessor;
+use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
class EnvVarProcessorTest extends TestCase
{
@@ -553,4 +555,44 @@ CSV;
$result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
$this->assertSame('123', $result); // check twice
}
+
+ public function testCircularEnvLoader()
+ {
+ $container = new ContainerBuilder();
+ $container->setParameter('env(FOO_CONTAINER)', 'foo');
+ $container->compile();
+
+ $index = 0;
+ $loaders = function () use (&$index) {
+ if (0 === $index++) {
+ throw new ParameterCircularReferenceException(['FOO_CONTAINER']);
+ }
+
+ yield new class() implements EnvVarLoaderInterface {
+ public function loadEnvVars(): array
+ {
+ return [
+ 'FOO_ENV_LOADER' => '123',
+ ];
+ }
+ };
+ };
+
+ $processor = new EnvVarProcessor($container, new RewindableGenerator($loaders, 1));
+
+ $result = $processor->getEnv('string', 'FOO_CONTAINER', function () {});
+ $this->assertSame('foo', $result);
+
+ $result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
+ $this->assertSame('123', $result);
+
+ $result = $processor->getEnv('default', ':BAR_CONTAINER', function ($name) use ($processor) {
+ $this->assertSame('BAR_CONTAINER', $name);
+
+ return $processor->getEnv('string', $name, function () {});
+ });
+ $this->assertNull($result);
+
+ $this->assertSame(2, $index);
+ }
}
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 76c10b015b..3fa4d6bb51 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -206,11 +206,13 @@ class Filesystem
/**
* Change the owner of an array of files or directories.
*
- * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner
+ * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner
+ * @param string|int $user A user name or number
+ * @param bool $recursive Whether change the owner recursively or not
*
* @throws IOException When the change fails
*/
- public function chown($files, string $user, bool $recursive = false)
+ public function chown($files, $user, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
@@ -231,11 +233,13 @@ class Filesystem
/**
* Change the group of an array of files or directories.
*
- * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group
+ * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group
+ * @param string|int $group A group name or number
+ * @param bool $recursive Whether change the group recursively or not
*
* @throws IOException When the change fails
*/
- public function chgrp($files, string $group, bool $recursive = false)
+ public function chgrp($files, $group, bool $recursive = false)
{
foreach ($this->toIterable($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
index d4299da4d9..dcd3c61406 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
@@ -521,7 +521,7 @@ class FilesystemTest extends FilesystemTestCase
$this->assertFilePermissions(753, $subdirectory);
}
- public function testChown()
+ public function testChownByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -534,7 +534,20 @@ class FilesystemTest extends FilesystemTestCase
$this->assertSame($owner, $this->getFileOwner($dir));
}
- public function testChownRecursive()
+ public function testChownById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+
+ $ownerId = $this->getFileOwnerId($dir);
+ $this->filesystem->chown($dir, $ownerId);
+
+ $this->assertSame($ownerId, $this->getFileOwnerId($dir));
+ }
+
+ public function testChownRecursiveByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -549,6 +562,21 @@ class FilesystemTest extends FilesystemTestCase
$this->assertSame($owner, $this->getFileOwner($file));
}
+ public function testChownRecursiveById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+ $file = $dir.\DIRECTORY_SEPARATOR.'file';
+ touch($file);
+
+ $ownerId = $this->getFileOwnerId($dir);
+ $this->filesystem->chown($dir, $ownerId, true);
+
+ $this->assertSame($ownerId, $this->getFileOwnerId($file));
+ }
+
public function testChownSymlink()
{
$this->markAsSkippedIfSymlinkIsMissing();
@@ -624,7 +652,7 @@ class FilesystemTest extends FilesystemTestCase
$this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
}
- public function testChgrp()
+ public function testChgrpByName()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -637,6 +665,19 @@ class FilesystemTest extends FilesystemTestCase
$this->assertSame($group, $this->getFileGroup($dir));
}
+ public function testChgrpById()
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
+ mkdir($dir);
+
+ $groupId = $this->getFileGroupId($dir);
+ $this->filesystem->chgrp($dir, $groupId);
+
+ $this->assertSame($groupId, $this->getFileGroupId($dir));
+ }
+
public function testChgrpRecursive()
{
$this->markAsSkippedIfPosixIsMissing();
@@ -652,7 +693,7 @@ class FilesystemTest extends FilesystemTestCase
$this->assertSame($group, $this->getFileGroup($file));
}
- public function testChgrpSymlink()
+ public function testChgrpSymlinkByName()
{
$this->markAsSkippedIfSymlinkIsMissing();
@@ -669,6 +710,23 @@ class FilesystemTest extends FilesystemTestCase
$this->assertSame($group, $this->getFileGroup($link));
}
+ public function testChgrpSymlinkById()
+ {
+ $this->markAsSkippedIfSymlinkIsMissing();
+
+ $file = $this->workspace.\DIRECTORY_SEPARATOR.'file';
+ $link = $this->workspace.\DIRECTORY_SEPARATOR.'link';
+
+ touch($file);
+
+ $this->filesystem->symlink($file, $link);
+
+ $groupId = $this->getFileGroupId($link);
+ $this->filesystem->chgrp($link, $groupId);
+
+ $this->assertSame($groupId, $this->getFileGroupId($link));
+ }
+
public function testChgrpLink()
{
$this->markAsSkippedIfLinkIsMissing();
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
index 08afefbb24..396a3ab2e8 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php
@@ -105,21 +105,36 @@ class FilesystemTestCase extends TestCase
);
}
- protected function getFileOwner($filepath)
+ protected function getFileOwnerId($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
$infos = stat($filepath);
- return ($datas = posix_getpwuid($infos['uid'])) ? $datas['name'] : null;
+ return $infos['uid'];
+ }
+
+ protected function getFileOwner($filepath)
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ return ($datas = posix_getpwuid($this->getFileOwnerId($filepath))) ? $datas['name'] : null;
+ }
+
+ protected function getFileGroupId($filepath)
+ {
+ $this->markAsSkippedIfPosixIsMissing();
+
+ $infos = stat($filepath);
+
+ return $infos['gid'];
}
protected function getFileGroup($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
- $infos = stat($filepath);
- if ($datas = posix_getgrgid($infos['gid'])) {
+ if ($datas = posix_getgrgid($this->getFileGroupId($filepath))) {
return $datas['name'];
}
diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
index 158bcef04e..b7b576e564 100644
--- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
+++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
@@ -253,7 +253,7 @@ trait ResponseTrait
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
{
foreach ($responseHeaders as $h) {
- if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d) .*#', $h, $m)) {
+ if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d)(?: |$)#', $h, $m)) {
if ($headers) {
$debug .= "< \r\n";
$headers = [];
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
index 698af72ce2..443d77d672 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
@@ -350,6 +350,10 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
return $this->validate($request, $entry, $catch);
}
+ if ($entry->headers->hasCacheControlDirective('no-cache')) {
+ return $this->validate($request, $entry, $catch);
+ }
+
$this->record($request, 'fresh');
$entry->headers->set('Age', $entry->getAge());
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php
index aa2c611455..34f1dc7f3b 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php
@@ -443,6 +443,22 @@ class HttpCacheTest extends HttpCacheTestCase
$this->assertTrue($this->response->headers->has('Age'));
}
+ public function testRevalidatesResponsesWithNoCacheDirectiveEvenIfFresh()
+ {
+ $this->setNextResponse(200, ['Cache-Control' => 'public, no-cache, max-age=10', 'ETag' => 'some-etag'], 'OK');
+ $this->request('GET', '/'); // warm the cache
+
+ sleep(5);
+
+ $this->setNextResponse(304, ['Cache-Control' => 'public, no-cache, max-age=10', 'ETag' => 'some-etag']);
+ $this->request('GET', '/');
+
+ $this->assertHttpKernelIsCalled(); // no-cache -> MUST have revalidated at origin
+ $this->assertTraceContains('valid');
+ $this->assertEquals('OK', $this->response->getContent());
+ $this->assertEquals(0, $this->response->getAge());
+ }
+
public function testCachesResponsesWithAnExpirationHeader()
{
$time = \DateTime::createFromFormat('U', time() + 5);
diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php
index 463a08c75b..e257e72246 100644
--- a/src/Symfony/Component/Translation/Dumper/FileDumper.php
+++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php
@@ -51,37 +51,36 @@ abstract class FileDumper implements DumperInterface
throw new InvalidArgumentException('The file dumper needs a path option.');
}
- $hasMessageFormatter = class_exists(\MessageFormatter::class);
-
// save a file for each domain
foreach ($messages->getDomains() as $domain) {
- if ($hasMessageFormatter) {
- $defaultDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
- $altDomain = $domain;
- } else {
- $defaultDomain = $domain;
- $altDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
- }
- $defaultPath = $options['path'].'/'.$this->getRelativePath($defaultDomain, $messages->getLocale());
- $altPath = $options['path'].'/'.$this->getRelativePath($altDomain, $messages->getLocale());
-
- if (!file_exists($defaultPath) && file_exists($altPath)) {
- [$defaultPath, $altPath] = [$altPath, $defaultPath];
- }
-
- if (!file_exists($defaultPath)) {
- $directory = \dirname($defaultPath);
+ $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
+ if (!file_exists($fullpath)) {
+ $directory = \dirname($fullpath);
if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
}
}
- if (file_exists($altPath)) {
- // clear alternative translation file
- file_put_contents($altPath, $this->formatCatalogue(new MessageCatalogue($messages->getLocale()), $altDomain, $options));
+ $intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
+ $intlMessages = $messages->all($intlDomain);
+
+ if ($intlMessages) {
+ $intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale());
+ file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options));
+
+ $messages->replace([], $intlDomain);
+
+ try {
+ if ($messages->all($domain)) {
+ file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
+ }
+ continue;
+ } finally {
+ $messages->replace($intlMessages, $intlDomain);
+ }
}
- file_put_contents($defaultPath, $this->formatCatalogue($messages, $domain, $options));
+ file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
}
}
diff --git a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php
index 9dd1377e49..6e42b1e568 100644
--- a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php
+++ b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php
@@ -27,15 +27,11 @@ class FileDumperTest extends TestCase
$dumper = new ConcreteFileDumper();
$dumper->dump($catalogue, ['path' => $tempDir]);
- $suffix = class_exists(\MessageFormatter::class) ? '+intl-icu' : '';
- $this->assertFileExists($tempDir."/messages$suffix.en.concrete");
+ $this->assertFileExists($tempDir.'/messages.en.concrete');
- @unlink($tempDir."/messages$suffix.en.concrete");
+ @unlink($tempDir.'/messages.en.concrete');
}
- /**
- * @requires extension intl
- */
public function testDumpIntl()
{
$tempDir = sys_get_temp_dir();
@@ -46,11 +42,13 @@ class FileDumperTest extends TestCase
$catalogue->add(['bar' => 'foo'], 'd2+intl-icu');
$dumper = new ConcreteFileDumper();
+ @unlink($tempDir.'/d2.en.concrete');
$dumper->dump($catalogue, ['path' => $tempDir]);
- $this->assertFileNotExists($tempDir.'/d1.en.concrete');
+ $this->assertStringEqualsFile($tempDir.'/d1.en.concrete', 'foo=bar');
+ @unlink($tempDir.'/d1.en.concrete');
- $this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo&foo=bar');
+ $this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo');
@unlink($tempDir.'/d1+intl-icu.en.concrete');
$this->assertFileNotExists($tempDir.'/d2.en.concrete');
@@ -62,8 +60,7 @@ class FileDumperTest extends TestCase
{
$tempDir = sys_get_temp_dir();
$translationsDir = $tempDir.'/test/translations';
- $suffix = class_exists(\MessageFormatter::class) ? '+intl-icu' : '';
- $file = $translationsDir."/messages$suffix.en.concrete";
+ $file = $translationsDir.'/messages.en.concrete';
$catalogue = new MessageCatalogue('en');
$catalogue->add(['foo' => 'bar']);
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf
index 018dd1233a..20dff43c6d 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf
@@ -24,11 +24,11 @@
- Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.
+ Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.|Morate odabrati bar {{ limit }} mogućnosti.
- Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.
+ Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.|Morate odabrati najviše {{ limit }} mogućnosti.
@@ -76,7 +76,7 @@
- Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.
+ Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.
@@ -84,7 +84,7 @@
- Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.
+ Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.
@@ -180,7 +180,7 @@
- Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.
+ Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.
@@ -204,15 +204,15 @@
- Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.
+ Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.
- Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.
+ Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.
- Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.
+ Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elementa.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.
diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php
index fc662b5d43..40f2bfc8db 100644
--- a/src/Symfony/Component/Yaml/Inline.php
+++ b/src/Symfony/Component/Yaml/Inline.php
@@ -439,6 +439,11 @@ class Inline
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
}
+ if ('!php/const' === $key) {
+ $key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false, []);
+ $key = self::evaluateScalar($key, $flags);
+ }
+
if (false === $i = strpos($mapping, ':', $i)) {
break;
}
diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php
index 9b07f8725d..0f8f8f8b94 100644
--- a/src/Symfony/Component/Yaml/Tests/InlineTest.php
+++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php
@@ -59,6 +59,7 @@ class InlineTest extends TestCase
['!php/const PHP_INT_MAX', PHP_INT_MAX],
['[!php/const PHP_INT_MAX]', [PHP_INT_MAX]],
['{ foo: !php/const PHP_INT_MAX }', ['foo' => PHP_INT_MAX]],
+ ['{ !php/const PHP_INT_MAX: foo }', [PHP_INT_MAX => 'foo']],
['!php/const NULL', null],
];
}