N°8017 - Security - dependabot - Symfony's VarDumper vulnerable to un… (#731)

Upgrade all Symfony components to last security fix (~6.4.0)
This commit is contained in:
Benjamin Dalsass
2025-08-06 08:54:56 +02:00
committed by GitHub
parent 603340b852
commit cdbcd14767
608 changed files with 5020 additions and 3793 deletions

View File

@@ -37,7 +37,7 @@ final class CachePoolClearerCacheWarmer implements CacheWarmerInterface
$this->pools = $pools;
}
public function warmUp(string $cacheDir, string $buildDir = null): array
public function warmUp(string $cacheDir, ?string $buildDir = null): array
{
foreach ($this->pools as $pool) {
if ($this->poolClearer->hasPool($pool)) {

View File

@@ -15,10 +15,14 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\Config\Builder\ConfigBuilderGenerator;
use Symfony\Component\Config\Builder\ConfigBuilderGeneratorInterface;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\KernelInterface;
/**
@@ -31,7 +35,7 @@ class ConfigBuilderCacheWarmer implements CacheWarmerInterface
private KernelInterface $kernel;
private ?LoggerInterface $logger;
public function __construct(KernelInterface $kernel, LoggerInterface $logger = null)
public function __construct(KernelInterface $kernel, ?LoggerInterface $logger = null)
{
$this->kernel = $kernel;
$this->logger = $logger;
@@ -50,12 +54,27 @@ class ConfigBuilderCacheWarmer implements CacheWarmerInterface
$generator = new ConfigBuilderGenerator($buildDir);
foreach ($this->kernel->getBundles() as $bundle) {
$extension = $bundle->getContainerExtension();
if (null === $extension) {
continue;
}
if ($this->kernel instanceof Kernel) {
/** @var ContainerBuilder $container */
$container = \Closure::bind(function (Kernel $kernel) {
$containerBuilder = $kernel->getContainerBuilder();
$kernel->prepareContainer($containerBuilder);
return $containerBuilder;
}, null, $this->kernel)($this->kernel);
$extensions = $container->getExtensions();
} else {
$extensions = [];
foreach ($this->kernel->getBundles() as $bundle) {
$extension = $bundle->getContainerExtension();
if (null !== $extension) {
$extensions[] = $extension;
}
}
}
foreach ($extensions as $extension) {
try {
$this->dumpExtension($extension, $generator);
} catch (\Exception $e) {
@@ -73,7 +92,8 @@ class ConfigBuilderCacheWarmer implements CacheWarmerInterface
if ($extension instanceof ConfigurationInterface) {
$configuration = $extension;
} elseif ($extension instanceof ConfigurationExtensionInterface) {
$configuration = $extension->getConfiguration([], new ContainerBuilder($this->kernel->getContainer()->getParameterBag()));
$container = $this->kernel->getContainer();
$configuration = $extension->getConfiguration([], new ContainerBuilder($container instanceof Container ? new ContainerBag($container) : new ParameterBag()));
}
if (!$configuration) {
@@ -85,6 +105,6 @@ class ConfigBuilderCacheWarmer implements CacheWarmerInterface
public function isOptional(): bool
{
return true;
return false;
}
}

View File

@@ -34,7 +34,7 @@ class RouterCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterf
$this->container = $container;
}
public function warmUp(string $cacheDir, string $buildDir = null): array
public function warmUp(string $cacheDir, ?string $buildDir = null): array
{
$router = $this->container->get('router');

View File

@@ -40,7 +40,7 @@ class CacheClearCommand extends Command
private CacheClearerInterface $cacheClearer;
private Filesystem $filesystem;
public function __construct(CacheClearerInterface $cacheClearer, Filesystem $filesystem = null)
public function __construct(CacheClearerInterface $cacheClearer, ?Filesystem $filesystem = null)
{
parent::__construct();
@@ -146,6 +146,16 @@ EOF
}
$this->warmupOptionals($useBuildDir ? $realCacheDir : $warmupDir, $warmupDir, $io);
}
// fix references to cached files with the real cache directory name
$search = [$warmupDir, str_replace('/', '\\/', $warmupDir), str_replace('\\', '\\\\', $warmupDir)];
$replace = str_replace('\\', '/', $realBuildDir);
foreach (Finder::create()->files()->in($warmupDir) as $file) {
$content = str_replace($search, $replace, file_get_contents($file), $count);
if ($count) {
file_put_contents($file, $content);
}
}
}
if (!$fs->exists($warmupDir.'/'.$containerDir)) {
@@ -154,7 +164,7 @@ EOF
}
if ($this->isNfs($realBuildDir)) {
$io->note('For better performances, you should move the cache and log directories to a non-shared folder of the VM.');
$io->note('For better performance, you should move the cache and log directories to a non-shared folder of the VM.');
$fs->remove($realBuildDir);
} else {
$fs->rename($realBuildDir, $oldBuildDir);
@@ -200,7 +210,7 @@ EOF
if (null === $mounts) {
$mounts = [];
if ('/' === \DIRECTORY_SEPARATOR && $files = @file('/proc/mounts')) {
if ('/' === \DIRECTORY_SEPARATOR && @is_readable('/proc/mounts') && $files = @file('/proc/mounts')) {
foreach ($files as $mount) {
$mount = \array_slice(explode(' ', $mount), 1, -3);
if (!\in_array(array_pop($mount), ['vboxsf', 'nfs'])) {
@@ -227,16 +237,6 @@ EOF
throw new \LogicException('Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is not supported.');
}
$kernel->reboot($warmupDir);
// fix references to cached files with the real cache directory name
$search = [$warmupDir, str_replace('\\', '\\\\', $warmupDir)];
$replace = str_replace('\\', '/', $realBuildDir);
foreach (Finder::create()->files()->in($warmupDir) as $file) {
$content = str_replace($search, $replace, file_get_contents($file), $count);
if ($count) {
file_put_contents($file, $content);
}
}
}
private function warmupOptionals(string $cacheDir, string $warmupDir, SymfonyStyle $io): void

View File

@@ -38,7 +38,7 @@ final class CachePoolClearCommand extends Command
/**
* @param string[]|null $poolNames
*/
public function __construct(Psr6CacheClearer $poolClearer, array $poolNames = null)
public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null)
{
parent::__construct();
@@ -72,7 +72,7 @@ EOF
$poolNames = $input->getArgument('pools');
$excludedPoolNames = $input->getOption('exclude');
if ($input->getOption('all')) {
if ($clearAll = $input->getOption('all')) {
if (!$this->poolNames) {
throw new InvalidArgumentException('Could not clear all cache pools, try specifying a specific pool or cache clearer.');
}
@@ -91,7 +91,7 @@ EOF
foreach ($poolNames as $id) {
if ($this->poolClearer->hasPool($id)) {
$pools[$id] = $id;
} else {
} elseif (!$clearAll || $kernel->getContainer()->has($id)) {
$pool = $kernel->getContainer()->get($id);
if ($pool instanceof CacheItemPoolInterface) {

View File

@@ -35,7 +35,7 @@ final class CachePoolDeleteCommand extends Command
/**
* @param string[]|null $poolNames
*/
public function __construct(Psr6CacheClearer $poolClearer, array $poolNames = null)
public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null)
{
parent::__construct();

View File

@@ -284,7 +284,9 @@ EOF
return $matchingServices[0];
}
return $io->choice('Select one of the following services to display its information', $matchingServices);
natsort($matchingServices);
return $io->choice('Select one of the following services to display its information', array_values($matchingServices));
}
private function findProperTagName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $container, string $tagName): string
@@ -302,7 +304,9 @@ EOF
return $matchingTags[0];
}
return $io->choice('Select one of the following tags to display its information', $matchingTags);
natsort($matchingTags);
return $io->choice('Select one of the following tags to display its information', array_values($matchingTags));
}
private function findServiceIdsContaining(ContainerBuilder $container, string $name, bool $showHidden): array

View File

@@ -35,7 +35,7 @@ class DebugAutowiringCommand extends ContainerDebugCommand
{
private ?FileLinkFormatter $fileLinkFormatter;
public function __construct(string $name = null, FileLinkFormatter $fileLinkFormatter = null)
public function __construct(?string $name = null, ?FileLinkFormatter $fileLinkFormatter = null)
{
$this->fileLinkFormatter = $fileLinkFormatter;
parent::__construct($name);

View File

@@ -42,7 +42,7 @@ class RouterDebugCommand extends Command
private RouterInterface $router;
private ?FileLinkFormatter $fileLinkFormatter;
public function __construct(RouterInterface $router, FileLinkFormatter $fileLinkFormatter = null)
public function __construct(RouterInterface $router, ?FileLinkFormatter $fileLinkFormatter = null)
{
parent::__construct();

View File

@@ -31,7 +31,7 @@ final class SecretsDecryptToLocalCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;
@@ -48,7 +48,7 @@ The <info>%command.name%</info> command decrypts all secrets and copies them in
<info>%command.full_name%</info>
When the option <info>--force</info> is provided, secrets that already exist in the local vault are overriden.
When the <info>--force</info> option is provided, secrets that already exist in the local vault are overridden.
<info>%command.full_name% --force</info>
EOF

View File

@@ -30,7 +30,7 @@ final class SecretsEncryptFromLocalCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

View File

@@ -33,7 +33,7 @@ final class SecretsGenerateKeysCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

View File

@@ -34,7 +34,7 @@ final class SecretsListCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

View File

@@ -35,7 +35,7 @@ final class SecretsRemoveCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

View File

@@ -36,7 +36,7 @@ final class SecretsSetCommand extends Command
private AbstractVault $vault;
private ?AbstractVault $localVault;
public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

View File

@@ -59,7 +59,7 @@ class TranslationDebugCommand extends Command
private array $codePaths;
private array $enabledLocales;
public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
{
parent::__construct();
@@ -79,7 +79,7 @@ class TranslationDebugCommand extends Command
->setDefinition([
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'),
new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'The messages domain'),
new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Display only missing messages'),
new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Display only unused messages'),
new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'),

View File

@@ -19,7 +19,6 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
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\HttpKernel\KernelInterface;
@@ -60,10 +59,14 @@ class TranslationUpdateCommand extends Command
private array $codePaths;
private array $enabledLocales;
public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
{
parent::__construct();
if (!method_exists($writer, 'getFormats')) {
throw new \InvalidArgumentException(sprintf('The writer class "%s" does not implement the "getFormats()" method.', $writer::class));
}
$this->writer = $writer;
$this->reader = $reader;
$this->extractor = $extractor;
@@ -81,14 +84,14 @@ class TranslationUpdateCommand extends Command
->setDefinition([
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'),
new InputOption('prefix', null, InputOption::VALUE_REQUIRED, 'Override the default prefix', '__'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'Override the default output format', 'xlf12'),
new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'),
new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'),
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to extract'),
new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'Specify the domain to extract'),
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically (only works with --dump-messages)', 'asc'),
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
new InputOption('as-tree', null, InputOption::VALUE_REQUIRED, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
])
->setHelp(<<<'EOF'
The <info>%command.name%</info> command extracts translation strings from templates
@@ -124,13 +127,6 @@ EOF
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$errorIo = $output instanceof ConsoleOutputInterface ? new SymfonyStyle($input, $output->getErrorOutput()) : $io;
if ('translation:update' === $input->getFirstArgument()) {
$errorIo->caution('Command "translation:update" is deprecated since version 5.4 and will be removed in Symfony 6.0. Use "translation:extract" instead.');
}
$io = new SymfonyStyle($input, $output);
$errorIo = $io->getErrorStyle();

View File

@@ -157,7 +157,7 @@ class Application extends BaseApplication
return $command;
}
public function all(string $namespace = null): array
public function all(?string $namespace = null): array
{
$this->registerCommands();

View File

@@ -96,7 +96,7 @@ abstract class Descriptor implements DescriptorInterface
*
* @param Definition|Alias|object $service
*/
abstract protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void;
abstract protected function describeContainerService(object $service, array $options = [], ?ContainerBuilder $container = null): void;
/**
* Describes container services.
@@ -108,9 +108,9 @@ abstract class Descriptor implements DescriptorInterface
abstract protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void;
abstract protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void;
abstract protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void;
abstract protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void;
abstract protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void;
abstract protected function describeContainerParameter(mixed $parameter, ?array $deprecation, array $options = []): void;
@@ -278,7 +278,7 @@ abstract class Descriptor implements DescriptorInterface
return $reverseAliases;
}
public static function getClassDescription(string $class, string &$resolvedClass = null): string
public static function getClassDescription(string $class, ?string &$resolvedClass = null): string
{
$resolvedClass = $class;
try {

View File

@@ -70,7 +70,7 @@ class JsonDescriptor extends Descriptor
$this->writeData($data, $options);
}
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerService(object $service, array $options = [], ?ContainerBuilder $container = null): void
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -121,12 +121,12 @@ class JsonDescriptor extends Descriptor
$this->writeData($data, $options);
}
protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void
{
$this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, $options['id'] ?? null), $options);
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void
{
if (!$container) {
$this->writeData($this->getContainerAliasData($alias), $options);
@@ -245,7 +245,7 @@ class JsonDescriptor extends Descriptor
return $sortedParameters;
}
private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $container = null, string $id = null): array
private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false, ?ContainerBuilder $container = null, ?string $id = null): array
{
$data = [
'class' => (string) $definition->getClass(),
@@ -393,7 +393,7 @@ class JsonDescriptor extends Descriptor
$data['type'] = 'closure';
$r = new \ReflectionFunction($callable);
if (str_contains($r->name, '{closure}')) {
if (str_contains($r->name, '{closure')) {
return $data;
}
$data['name'] = $r->name;
@@ -418,7 +418,7 @@ class JsonDescriptor extends Descriptor
throw new \InvalidArgumentException('Callable is not describable.');
}
private function describeValue($value, bool $omitTags, bool $showArguments, ContainerBuilder $container = null, string $id = null): mixed
private function describeValue($value, bool $omitTags, bool $showArguments, ?ContainerBuilder $container = null, ?string $id = null): mixed
{
if (\is_array($value)) {
$data = [];

View File

@@ -98,7 +98,7 @@ class MarkdownDescriptor extends Descriptor
}
}
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerService(object $service, array $options = [], ?ContainerBuilder $container = null): void
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -206,7 +206,7 @@ class MarkdownDescriptor extends Descriptor
}
}
protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void
{
$output = '';
@@ -276,7 +276,7 @@ class MarkdownDescriptor extends Descriptor
$this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output);
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void
{
$output = '- Service: `'.$alias.'`'
."\n".'- Public: '.($alias->isPublic() && !$alias->isPrivate() ? 'yes' : 'no');
@@ -403,7 +403,7 @@ class MarkdownDescriptor extends Descriptor
$string .= "\n- Type: `closure`";
$r = new \ReflectionFunction($callable);
if (str_contains($r->name, '{closure}')) {
if (str_contains($r->name, '{closure')) {
$this->write($string."\n");
return;

View File

@@ -40,7 +40,7 @@ class TextDescriptor extends Descriptor
{
private ?FileLinkFormatter $fileLinkFormatter;
public function __construct(FileLinkFormatter $fileLinkFormatter = null)
public function __construct(?FileLinkFormatter $fileLinkFormatter = null)
{
$this->fileLinkFormatter = $fileLinkFormatter;
}
@@ -159,7 +159,7 @@ class TextDescriptor extends Descriptor
}
}
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerService(object $service, array $options = [], ?ContainerBuilder $container = null): void
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -281,7 +281,7 @@ class TextDescriptor extends Descriptor
$options['output']->table($tableHeaders, $tableRows);
}
protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void
{
if (isset($options['id'])) {
$options['output']->title(sprintf('Information for Service "<info>%s</info>"', $options['id']));
@@ -420,7 +420,7 @@ class TextDescriptor extends Descriptor
$options['output']->listing($formattedLogs);
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void
{
if ($alias->isPublic() && !$alias->isPrivate()) {
$options['output']->comment(sprintf('This service is a <info>public</info> alias for the service <info>%s</info>', (string) $alias));
@@ -579,7 +579,7 @@ class TextDescriptor extends Descriptor
return trim($configAsString);
}
private function formatControllerLink(mixed $controller, string $anchorText, callable $getContainer = null): string
private function formatControllerLink(mixed $controller, string $anchorText, ?callable $getContainer = null): string
{
if (null === $this->fileLinkFormatter) {
return $anchorText;
@@ -597,7 +597,7 @@ class TextDescriptor extends Descriptor
} elseif (!\is_string($controller)) {
return $anchorText;
} elseif (str_contains($controller, '::')) {
$r = new \ReflectionMethod($controller);
$r = new \ReflectionMethod(...explode('::', $controller, 2));
} else {
$r = new \ReflectionFunction($controller);
}
@@ -649,7 +649,7 @@ class TextDescriptor extends Descriptor
if ($callable instanceof \Closure) {
$r = new \ReflectionFunction($callable);
if (str_contains($r->name, '{closure}')) {
if (str_contains($r->name, '{closure')) {
return 'Closure()';
}
if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {

View File

@@ -53,7 +53,7 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($this->getContainerTagsDocument($container, isset($options['show_hidden']) && $options['show_hidden']));
}
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerService(object $service, array $options = [], ?ContainerBuilder $container = null): void
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -67,12 +67,12 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null, $options['id'] ?? null));
}
protected function describeContainerDefinition(Definition $definition, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void
{
$this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container));
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $container = null): void
protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, $options['id'] ?? null)->childNodes->item(0), true));
@@ -162,7 +162,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
private function getRouteDocument(Route $route, string $name = null): \DOMDocument
private function getRouteDocument(Route $route, ?string $name = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($routeXML = $dom->createElement('route'));
@@ -268,7 +268,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
private function getContainerServiceDocument(object $service, string $id, ContainerBuilder $container = null, bool $showArguments = false): \DOMDocument
private function getContainerServiceDocument(object $service, string $id, ?ContainerBuilder $container = null, bool $showArguments = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
@@ -288,7 +288,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
private function getContainerServicesDocument(ContainerBuilder $container, string $tag = null, bool $showHidden = false, bool $showArguments = false, callable $filter = null, string $id = null): \DOMDocument
private function getContainerServicesDocument(ContainerBuilder $container, ?string $tag = null, bool $showHidden = false, bool $showArguments = false, ?callable $filter = null, ?string $id = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($containerXML = $dom->createElement('container'));
@@ -318,7 +318,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
private function getContainerDefinitionDocument(Definition $definition, string $id = null, bool $omitTags = false, bool $showArguments = false, ContainerBuilder $container = null): \DOMDocument
private function getContainerDefinitionDocument(Definition $definition, ?string $id = null, bool $omitTags = false, bool $showArguments = false, ?ContainerBuilder $container = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($serviceXML = $dom->createElement('definition'));
@@ -418,7 +418,7 @@ class XmlDescriptor extends Descriptor
/**
* @return \DOMNode[]
*/
private function getArgumentNodes(array $arguments, \DOMDocument $dom, ContainerBuilder $container = null): array
private function getArgumentNodes(array $arguments, \DOMDocument $dom, ?ContainerBuilder $container = null): array
{
$nodes = [];
@@ -466,7 +466,7 @@ class XmlDescriptor extends Descriptor
return $nodes;
}
private function getContainerAliasDocument(Alias $alias, string $id = null): \DOMDocument
private function getContainerAliasDocument(Alias $alias, ?string $id = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($aliasXML = $dom->createElement('alias'));
@@ -581,7 +581,7 @@ class XmlDescriptor extends Descriptor
$callableXML->setAttribute('type', 'closure');
$r = new \ReflectionFunction($callable);
if (str_contains($r->name, '{closure}')) {
if (str_contains($r->name, '{closure')) {
return $dom;
}
$callableXML->setAttribute('name', $r->name);

View File

@@ -25,7 +25,7 @@ use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
*/
class DescriptorHelper extends BaseDescriptorHelper
{
public function __construct(FileLinkFormatter $fileLinkFormatter = null)
public function __construct(?FileLinkFormatter $fileLinkFormatter = null)
{
$this
->register('txt', new TextDescriptor($fileLinkFormatter))

View File

@@ -163,7 +163,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
/**
* Returns a BinaryFileResponse object with original or customized file name and disposition header.
*/
protected function file(\SplFileInfo|string $file, string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse
protected function file(\SplFileInfo|string $file, ?string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse
{
$response = new BinaryFileResponse($file);
$response->setContentDisposition($disposition, $fileName ?? $response->getFile()->getFilename());
@@ -248,7 +248,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
* If an invalid form is found in the list of parameters, a 422 status code is returned.
* Forms found in parameters are auto-cast to form views.
*/
protected function render(string $view, array $parameters = [], Response $response = null): Response
protected function render(string $view, array $parameters = [], ?Response $response = null): Response
{
return $this->doRender($view, null, $parameters, $response, __FUNCTION__);
}
@@ -259,7 +259,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
* If an invalid form is found in the list of parameters, a 422 status code is returned.
* Forms found in parameters are auto-cast to form views.
*/
protected function renderBlock(string $view, string $block, array $parameters = [], Response $response = null): Response
protected function renderBlock(string $view, string $block, array $parameters = [], ?Response $response = null): Response
{
return $this->doRender($view, $block, $parameters, $response, __FUNCTION__);
}
@@ -271,7 +271,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
*
* @deprecated since Symfony 6.2, use render() instead
*/
protected function renderForm(string $view, array $parameters = [], Response $response = null): Response
protected function renderForm(string $view, array $parameters = [], ?Response $response = null): Response
{
trigger_deprecation('symfony/framework-bundle', '6.2', 'The "%s::renderForm()" method is deprecated, use "render()" instead.', get_debug_type($this));
@@ -281,7 +281,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
/**
* Streams a view.
*/
protected function stream(string $view, array $parameters = [], StreamedResponse $response = null): StreamedResponse
protected function stream(string $view, array $parameters = [], ?StreamedResponse $response = null): StreamedResponse
{
if (!$this->container->has('twig')) {
throw new \LogicException('You cannot use the "stream" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".');
@@ -309,7 +309,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
*
* throw $this->createNotFoundException('Page not found!');
*/
protected function createNotFoundException(string $message = 'Not Found', \Throwable $previous = null): NotFoundHttpException
protected function createNotFoundException(string $message = 'Not Found', ?\Throwable $previous = null): NotFoundHttpException
{
return new NotFoundHttpException($message, $previous);
}
@@ -323,7 +323,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
*
* @throws \LogicException If the Security component is not available
*/
protected function createAccessDeniedException(string $message = 'Access Denied.', \Throwable $previous = null): AccessDeniedException
protected function createAccessDeniedException(string $message = 'Access Denied.', ?\Throwable $previous = null): AccessDeniedException
{
if (!class_exists(AccessDeniedException::class)) {
throw new \LogicException('You cannot use the "createAccessDeniedException" method if the Security component is not available. Try running "composer require symfony/security-bundle".');
@@ -406,7 +406,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
/**
* @param LinkInterface[] $links
*/
protected function sendEarlyHints(iterable $links = [], Response $response = null): Response
protected function sendEarlyHints(iterable $links = [], ?Response $response = null): Response
{
if (!$this->container->has('web_link.http_header_serializer')) {
throw new \LogicException('You cannot use the "sendEarlyHints" method if the WebLink component is not available. Try running "composer require symfony/web-link".');

View File

@@ -31,7 +31,7 @@ class RedirectController
private ?int $httpPort;
private ?int $httpsPort;
public function __construct(UrlGeneratorInterface $router = null, int $httpPort = null, int $httpsPort = null)
public function __construct(?UrlGeneratorInterface $router = null, ?int $httpPort = null, ?int $httpsPort = null)
{
$this->router = $router;
$this->httpPort = $httpPort;
@@ -107,9 +107,9 @@ class RedirectController
*
* @throws HttpException In case the path is empty
*/
public function urlRedirectAction(Request $request, string $path, bool $permanent = false, string $scheme = null, int $httpPort = null, int $httpsPort = null, bool $keepRequestMethod = false): Response
public function urlRedirectAction(Request $request, string $path, bool $permanent = false, ?string $scheme = null, ?int $httpPort = null, ?int $httpsPort = null, bool $keepRequestMethod = false): Response
{
if ('' == $path) {
if ('' === $path) {
throw new HttpException($permanent ? 410 : 404);
}
@@ -119,13 +119,17 @@ class RedirectController
$statusCode = $permanent ? 301 : 302;
}
$scheme ??= $request->getScheme();
if (str_starts_with($path, '//')) {
$path = $scheme.':'.$path;
}
// redirect if the path is a full URL
if (parse_url($path, \PHP_URL_SCHEME)) {
return new RedirectResponse($path, $statusCode);
}
$scheme ??= $request->getScheme();
if ($qs = $request->server->get('QUERY_STRING') ?: $request->getQueryString()) {
if (!str_contains($path, '?')) {
$qs = '?'.$qs;

View File

@@ -25,7 +25,7 @@ class TemplateController
{
private ?Environment $twig;
public function __construct(Environment $twig = null)
public function __construct(?Environment $twig = null)
{
$this->twig = $twig;
}
@@ -40,7 +40,7 @@ class TemplateController
* @param array $context The context (arguments) of the template
* @param int $statusCode The HTTP status code to return with the response (200 "OK" by default)
*/
public function templateAction(string $template, int $maxAge = null, int $sharedAge = null, bool $private = null, array $context = [], int $statusCode = 200): Response
public function templateAction(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200): Response
{
if (null === $this->twig) {
throw new \LogicException('You cannot use the TemplateController if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".');
@@ -68,7 +68,7 @@ class TemplateController
/**
* @param int $statusCode The HTTP status code (200 "OK" by default)
*/
public function __invoke(string $template, int $maxAge = null, int $sharedAge = null, bool $private = null, array $context = [], int $statusCode = 200): Response
public function __invoke(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200): Response
{
return $this->templateAction($template, $maxAge, $sharedAge, $private, $context, $statusCode);
}

View File

@@ -28,7 +28,7 @@ class RouterDataCollector extends BaseRouterDataCollector
$controller = $controller[0];
}
if ($controller instanceof RedirectController) {
if ($controller instanceof RedirectController && $request->attributes->has('_route')) {
return $request->attributes->get('_route');
}

View File

@@ -0,0 +1,31 @@
<?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\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TranslationUpdateCommandPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->hasDefinition('console.command.translation_extract')) {
return;
}
$translationWriterClass = $container->getParameterBag()->resolveValue($container->findDefinition('translation.writer')->getClass());
if (!method_exists($translationWriterClass, 'getFormats')) {
$container->removeDefinition('console.command.translation_extract');
}
}
}

View File

@@ -45,6 +45,7 @@ use Symfony\Component\Serializer\Encoder\JsonDecode;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Uid\Factory\UuidFactory;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Webhook\Controller\WebhookController;
use Symfony\Component\WebLink\HttpHeaderSerializer;
@@ -146,7 +147,7 @@ class Configuration implements ConfigurationInterface
->end()
;
$willBeAvailable = static function (string $package, string $class, string $parentPackage = null) {
$willBeAvailable = static function (string $package, string $class, ?string $parentPackage = null) {
$parentPackages = (array) $parentPackage;
$parentPackages[] = 'symfony/framework-bundle';
@@ -930,6 +931,10 @@ class Configuration implements ConfigurationInterface
->end()
->scalarNode('importmap_polyfill')
->info('The importmap name that will be used to load the polyfill. Set to false to disable.')
->validate()
->ifTrue()
->thenInvalid('Invalid "importmap_polyfill" value. Must be either an importmap name or false.')
->end()
->defaultValue('es-module-shims')
->end()
->arrayNode('importmap_script_attributes')
@@ -1062,7 +1067,7 @@ class Configuration implements ConfigurationInterface
->validate()->castToArray()->end()
->end()
->scalarNode('translation_domain')->defaultValue('validators')->end()
->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->end()
->enumNode('email_validation_mode')->values(array_merge(class_exists(Email::class) ? Email::VALIDATION_MODES : ['html5-allow-no-tld', 'html5', 'strict'], ['loose']))->end()
->arrayNode('mapping')
->addDefaultsIfNotSet()
->fixXmlConfig('path')
@@ -1192,8 +1197,7 @@ class Configuration implements ConfigurationInterface
->end()
->arrayNode('default_context')
->normalizeKeys(false)
->useAttributeAsKey('name')
->beforeNormalization()
->validate()
->ifTrue(fn () => $this->debug && class_exists(JsonParser::class))
->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true])
->end()
@@ -2089,6 +2093,9 @@ class Configuration implements ConfigurationInterface
->variableNode('md5')->end()
->end()
->end()
->scalarNode('crypto_method')
->info('The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.')
->end()
->arrayNode('extra')
->info('Extra options for specific HTTP client')
->normalizeKeys(false)
@@ -2195,6 +2202,7 @@ class Configuration implements ConfigurationInterface
->end()
->arrayNode('envelope')
->info('Mailer Envelope configuration')
->fixXmlConfig('recipient')
->children()
->scalarNode('sender')->end()
->arrayNode('recipients')

View File

@@ -122,6 +122,8 @@ use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Notifier\Bridge as NotifierBridge;
use Symfony\Component\Notifier\Bridge\FakeChat\FakeChatTransportFactory;
use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory;
use Symfony\Component\Notifier\ChatterInterface;
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Recipient\Recipient;
@@ -349,7 +351,7 @@ class FrameworkExtension extends Extension
throw new LogicException('AssetMapper support cannot be enabled as the AssetMapper component is not installed. Try running "composer require symfony/asset-mapper".');
}
$this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets']));
$this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets']), $this->readConfigEnabled('http_client', $container, $config['http_client']));
} else {
$container->removeDefinition('cache.asset_mapper');
}
@@ -733,7 +735,7 @@ class FrameworkExtension extends Extension
$container->getDefinition('config_cache_factory')->setArguments([]);
}
if (!$config['disallow_search_engine_index'] ?? false) {
if (!$config['disallow_search_engine_index']) {
$container->removeDefinition('disallow_search_engine_index_response_listener');
}
@@ -1330,7 +1332,7 @@ class FrameworkExtension extends Extension
}
}
private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled): void
private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled, bool $httpClientEnabled): void
{
$loader->load('asset_mapper.php');
@@ -1338,6 +1340,12 @@ class FrameworkExtension extends Extension
$container->removeDefinition('asset_mapper.asset_package');
}
if (!$httpClientEnabled) {
$container->register('asset_mapper.http_client', HttpClientInterface::class)
->addTag('container.error')
->addError('You cannot use the AssetMapper integration since the HttpClient component is not enabled. Try enabling the "framework.http_client" config option.');
}
$paths = $config['paths'];
foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
if ($container->fileExists($dir = $bundle['path'].'/Resources/public') || $container->fileExists($dir = $bundle['path'].'/public')) {
@@ -1507,7 +1515,7 @@ class FrameworkExtension extends Extension
$defaultDir = $container->getParameterBag()->resolveValue($config['default_path']);
foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
if ($container->fileExists($dir = $bundle['path'].'/Resources/translations') || $container->fileExists($dir = $bundle['path'].'/translations')) {
$dirs[] = $dir;
$dirs[] = $transPaths[] = $dir;
} else {
$nonExistingDirs[] = $dir;
}
@@ -1996,18 +2004,23 @@ class FrameworkExtension extends Extension
$container->setParameter('serializer.default_context', $defaultContext);
}
if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
if ($container->hasDefinition('serializer.normalizer.object')) {
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
$context = ($arguments[6] ?? $defaultContext) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(5, null);
$context = $arguments[6] ?? $defaultContext;
if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
$context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(5, null);
}
if ($config['max_depth_handler'] ?? false) {
$context += ['max_depth_handler' => new Reference($config['max_depth_handler'])];
}
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
}
if ($config['max_depth_handler'] ?? false) {
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
$context = ($arguments[6] ?? $defaultContext) + ['max_depth_handler' => new Reference($config['max_depth_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
}
$container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext);
}
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void
@@ -2201,16 +2214,18 @@ class FrameworkExtension extends Extension
$defaultMiddleware['after'][0]['arguments'] = [$bus['default_middleware']['allow_no_senders']];
$defaultMiddleware['after'][1]['arguments'] = [$bus['default_middleware']['allow_no_handlers']];
// argument to add_bus_name_stamp_middleware
$defaultMiddleware['before'][0]['arguments'] = [$busId];
$middleware = array_merge($defaultMiddleware['before'], $middleware, $defaultMiddleware['after']);
}
foreach ($middleware as $middlewareItem) {
foreach ($middleware as $key => $middlewareItem) {
if (!$validationEnabled && \in_array($middlewareItem['id'], ['validation', 'messenger.middleware.validation'], true)) {
throw new LogicException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".');
}
// argument to add_bus_name_stamp_middleware
if ('add_bus_name_stamp_middleware' === $middlewareItem['id']) {
$middleware[$key]['arguments'] = [$busId];
}
}
if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class)) {
@@ -2267,14 +2282,17 @@ class FrameworkExtension extends Extension
$transportRateLimiterReferences = [];
foreach ($config['transports'] as $name => $transport) {
$serializerId = $transport['serializer'] ?? 'messenger.default_serializer';
$tags = [
'alias' => $name,
'is_failure_transport' => \in_array($name, $failureTransports),
];
if (str_starts_with($transport['dsn'], 'sync://')) {
$tags['is_consumable'] = false;
}
$transportDefinition = (new Definition(TransportInterface::class))
->setFactory([new Reference('messenger.transport_factory'), 'createTransport'])
->setArguments([$transport['dsn'], $transport['options'] + ['transport_name' => $name], new Reference($serializerId)])
->addTag('messenger.receiver', [
'alias' => $name,
'is_failure_transport' => \in_array($name, $failureTransports),
]
)
->addTag('messenger.receiver', $tags)
;
$container->setDefinition($transportId = 'messenger.transport.'.$name, $transportDefinition);
$senderAliases[$name] = $transportId;
@@ -2553,11 +2571,13 @@ class FrameworkExtension extends Extension
->setFactory([ScopingHttpClient::class, 'forBaseUri'])
->setArguments([new Reference('http_client.transport'), $baseUri, $scopeConfig])
->addTag('http_client.client')
->addTag('kernel.reset', ['method' => 'reset', 'on_invalid' => 'ignore'])
;
} else {
$container->register($name, ScopingHttpClient::class)
->setArguments([new Reference('http_client.transport'), [$scope => $scopeConfig], $scope])
->addTag('http_client.client')
->addTag('kernel.reset', ['method' => 'reset', 'on_invalid' => 'ignore'])
;
}
@@ -2644,7 +2664,6 @@ class FrameworkExtension extends Extension
}
$transports = $config['dsn'] ? ['main' => $config['dsn']] : $config['transports'];
$container->getDefinition('mailer.transports')->setArgument(0, $transports);
$container->getDefinition('mailer.default_transport')->setArgument(0, current($transports));
$mailer = $container->getDefinition('mailer.mailer');
if (false === $messageBus = $config['message_bus']) {
@@ -2753,7 +2772,7 @@ class FrameworkExtension extends Extension
$container->removeDefinition('notifier.channel.email');
}
foreach (['texter', 'chatter', 'notifier.channel.chat', 'notifier.channel.email', 'notifier.channel.sms'] as $serviceId) {
foreach (['texter', 'chatter', 'notifier.channel.chat', 'notifier.channel.email', 'notifier.channel.sms', 'notifier.channel.push'] as $serviceId) {
if (!$container->hasDefinition($serviceId)) {
continue;
}
@@ -2800,8 +2819,6 @@ class FrameworkExtension extends Extension
NotifierBridge\Engagespot\EngagespotTransportFactory::class => 'notifier.transport_factory.engagespot',
NotifierBridge\Esendex\EsendexTransportFactory::class => 'notifier.transport_factory.esendex',
NotifierBridge\Expo\ExpoTransportFactory::class => 'notifier.transport_factory.expo',
NotifierBridge\FakeChat\FakeChatTransportFactory::class => 'notifier.transport_factory.fake-chat',
NotifierBridge\FakeSms\FakeSmsTransportFactory::class => 'notifier.transport_factory.fake-sms',
NotifierBridge\Firebase\FirebaseTransportFactory::class => 'notifier.transport_factory.firebase',
NotifierBridge\FortySixElks\FortySixElksTransportFactory::class => 'notifier.transport_factory.forty-six-elks',
NotifierBridge\FreeMobile\FreeMobileTransportFactory::class => 'notifier.transport_factory.free-mobile',
@@ -2820,7 +2837,7 @@ class FrameworkExtension extends Extension
NotifierBridge\Mastodon\MastodonTransportFactory::class => 'notifier.transport_factory.mastodon',
NotifierBridge\Mattermost\MattermostTransportFactory::class => 'notifier.transport_factory.mattermost',
NotifierBridge\Mercure\MercureTransportFactory::class => 'notifier.transport_factory.mercure',
NotifierBridge\MessageBird\MessageBirdTransport::class => 'notifier.transport_factory.message-bird',
NotifierBridge\MessageBird\MessageBirdTransportFactory::class => 'notifier.transport_factory.message-bird',
NotifierBridge\MessageMedia\MessageMediaTransportFactory::class => 'notifier.transport_factory.message-media',
NotifierBridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class => 'notifier.transport_factory.microsoft-teams',
NotifierBridge\Mobyt\MobytTransportFactory::class => 'notifier.transport_factory.mobyt',
@@ -2851,7 +2868,7 @@ class FrameworkExtension extends Extension
NotifierBridge\Telegram\TelegramTransportFactory::class => 'notifier.transport_factory.telegram',
NotifierBridge\Telnyx\TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx',
NotifierBridge\Termii\TermiiTransportFactory::class => 'notifier.transport_factory.termii',
NotifierBridge\TurboSms\TurboSmsTransport::class => 'notifier.transport_factory.turbo-sms',
NotifierBridge\TurboSms\TurboSmsTransportFactory::class => 'notifier.transport_factory.turbo-sms',
NotifierBridge\Twilio\TwilioTransportFactory::class => 'notifier.transport_factory.twilio',
NotifierBridge\Twitter\TwitterTransportFactory::class => 'notifier.transport_factory.twitter',
NotifierBridge\Vonage\VonageTransportFactory::class => 'notifier.transport_factory.vonage',
@@ -2872,27 +2889,33 @@ class FrameworkExtension extends Extension
if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', NotifierBridge\Mercure\MercureTransportFactory::class, $parentPackages) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages) && \in_array(MercureBundle::class, $container->getParameter('kernel.bundles'), true)) {
$container->getDefinition($classToServices[NotifierBridge\Mercure\MercureTransportFactory::class])
->replaceArgument('$registry', new Reference(HubRegistry::class))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
->replaceArgument(0, new Reference(HubRegistry::class))
->replaceArgument(1, new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
} elseif (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', NotifierBridge\Mercure\MercureTransportFactory::class, $parentPackages)) {
$container->removeDefinition($classToServices[NotifierBridge\Mercure\MercureTransportFactory::class]);
}
if (ContainerBuilder::willBeAvailable('symfony/fake-chat-notifier', NotifierBridge\FakeChat\FakeChatTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
$container->getDefinition($classToServices[NotifierBridge\FakeChat\FakeChatTransportFactory::class])
->replaceArgument('$mailer', new Reference('mailer'))
->replaceArgument('$logger', new Reference('logger'))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
// don't use ContainerBuilder::willBeAvailable() as these are not needed in production
if (class_exists(FakeChatTransportFactory::class)) {
$container->getDefinition('notifier.transport_factory.fake-chat')
->replaceArgument(0, new Reference('mailer', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument(1, new Reference('logger', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->addArgument(new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
} else {
$container->removeDefinition('notifier.transport_factory.fake-chat');
}
if (ContainerBuilder::willBeAvailable('symfony/fake-sms-notifier', NotifierBridge\FakeSms\FakeSmsTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
$container->getDefinition($classToServices[NotifierBridge\FakeSms\FakeSmsTransportFactory::class])
->replaceArgument('$mailer', new Reference('mailer'))
->replaceArgument('$logger', new Reference('logger'))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
// don't use ContainerBuilder::willBeAvailable() as these are not needed in production
if (class_exists(FakeSmsTransportFactory::class)) {
$container->getDefinition('notifier.transport_factory.fake-sms')
->replaceArgument(0, new Reference('mailer', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument(1, new Reference('logger', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->addArgument(new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
} else {
$container->removeDefinition('notifier.transport_factory.fake-sms');
}
if (isset($config['admin_recipients'])) {

View File

@@ -38,6 +38,8 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
/** @var \SplObjectStorage<Request, ?Request> */
private \SplObjectStorage $parents;
private bool $disabled = false;
public function __construct(
private readonly Profiler $profiler,
private readonly RequestStack $requestStack,
@@ -66,7 +68,7 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
$input = $event->getInput();
if (!$input->hasOption('profile') || !$input->getOption('profile')) {
$this->profiler->disable();
$this->disabled = true;
return;
}
@@ -92,7 +94,12 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
public function profile(ConsoleTerminateEvent $event): void
{
if (!$this->cliMode || !$this->profiler->isEnabled()) {
$error = $this->error;
$this->error = null;
if (!$this->cliMode || $this->disabled) {
$this->disabled = false;
return;
}
@@ -114,8 +121,7 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
$request->command->exitCode = $event->getExitCode();
$request->command->interruptedBySignal = $event->getInterruptingSignal();
$profile = $this->profiler->collect($request, $request->getResponse(), $this->error);
$this->error = null;
$profile = $this->profiler->collect($request, $request->getResponse(), $error);
$this->profiles[$request] = $profile;
if ($this->parents[$request] = $this->requestStack->getParentRequest()) {

View File

@@ -20,6 +20,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationUpdateCommandPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\VirtualRequestStackPass;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
@@ -58,6 +59,7 @@ use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass;
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
use Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass;
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
use Symfony\Component\Runtime\SymfonyRuntime;
use Symfony\Component\Scheduler\DependencyInjection\AddScheduleMessengerPass;
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
use Symfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPass;
@@ -99,13 +101,19 @@ class FrameworkBundle extends Bundle
{
$_ENV['DOCTRINE_DEPRECATIONS'] = $_SERVER['DOCTRINE_DEPRECATIONS'] ??= 'trigger';
$handler = ErrorHandler::register(null, false);
if (class_exists(SymfonyRuntime::class)) {
$handler = set_error_handler('var_dump');
restore_error_handler();
} else {
$handler = [ErrorHandler::register(null, false)];
}
// When upgrading an existing Symfony application from 6.2 to 6.3, and
// the cache is warmed up, the service is not available yet, so we need
// to check if it exists.
if ($this->container->has('debug.error_handler_configurator')) {
$this->container->get('debug.error_handler_configurator')->configure($handler);
if (!$this->container->has('debug.error_handler_configurator')) {
// When upgrading an existing Symfony application from 6.2 to 6.3, and
// the cache is warmed up, the service is not available yet, so we need
// to check if it exists.
} elseif (\is_array($handler) && $handler[0] instanceof ErrorHandler) {
$this->container->get('debug.error_handler_configurator')->configure($handler[0]);
}
if ($this->container->getParameter('kernel.http_method_override')) {
@@ -186,6 +194,7 @@ class FrameworkBundle extends Bundle
// must be registered after MonologBundle's LoggerChannelPass
$container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
$container->addCompilerPass(new VirtualRequestStackPass());
$container->addCompilerPass(new TranslationUpdateCommandPass(), PassConfig::TYPE_BEFORE_REMOVING);
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2);

View File

@@ -37,7 +37,7 @@ class HttpCache extends BaseHttpCache
/**
* @param $cache The cache directory (default used if null) or the storage instance
*/
public function __construct(KernelInterface $kernel, string|StoreInterface $cache = null, SurrogateInterface $surrogate = null, array $options = null)
public function __construct(KernelInterface $kernel, string|StoreInterface|null $cache = null, ?SurrogateInterface $surrogate = null, ?array $options = null)
{
$this->kernel = $kernel;
$this->surrogate = $surrogate;
@@ -60,7 +60,7 @@ class HttpCache extends BaseHttpCache
parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge($this->options, $this->getOptions()));
}
protected function forward(Request $request, bool $catch = false, Response $entry = null): Response
protected function forward(Request $request, bool $catch = false, ?Response $entry = null): Response
{
$this->getKernel()->boot();
$this->getKernel()->getContainer()->set('cache', $this);

View File

@@ -217,7 +217,7 @@ trait MicroKernelTrait
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
$route->setDefault('_controller', ['kernel', $controller[1]]);
} elseif ($controller instanceof \Closure && $this === ($r = new \ReflectionFunction($controller))->getClosureThis() && !str_contains($r->name, '{closure}')) {
} elseif ($controller instanceof \Closure && $this === ($r = new \ReflectionFunction($controller))->getClosureThis() && !str_contains($r->name, '{closure')) {
$route->setDefault('_controller', ['kernel', $r->name]);
}
}

View File

@@ -34,7 +34,7 @@ class KernelBrowser extends HttpKernelBrowser
private bool $profiler = false;
private bool $reboot = true;
public function __construct(KernelInterface $kernel, array $server = [], History $history = null, CookieJar $cookieJar = null)
public function __construct(KernelInterface $kernel, array $server = [], ?History $history = null, ?CookieJar $cookieJar = null)
{
parent::__construct($kernel, $server, $history, $cookieJar);
}

View File

@@ -17,4 +17,4 @@ Resources
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
[3]: https://symfony.com/sponsor
[1]: https://symfony.com/sponsor

View File

@@ -54,6 +54,8 @@ return static function (ContainerConfigurator $container) {
])
->alias(AssetMapperInterface::class, 'asset_mapper')
->alias('asset_mapper.http_client', 'http_client')
->set('asset_mapper.mapped_asset_factory', MappedAssetFactory::class)
->args([
service('asset_mapper.public_assets_path_resolver'),
@@ -75,6 +77,7 @@ return static function (ContainerConfigurator $container) {
param('kernel.project_dir'),
abstract_arg('array of excluded path patterns'),
abstract_arg('exclude dot files'),
param('kernel.debug'),
])
->set('asset_mapper.public_assets_path_resolver', PublicAssetsPathResolver::class)
@@ -196,7 +199,7 @@ return static function (ContainerConfigurator $container) {
])
->set('asset_mapper.importmap.resolver', JsDelivrEsmResolver::class)
->args([service('http_client')])
->args([service('asset_mapper.http_client')])
->set('asset_mapper.importmap.renderer', ImportMapRenderer::class)
->args([
@@ -211,12 +214,12 @@ return static function (ContainerConfigurator $container) {
->set('asset_mapper.importmap.auditor', ImportMapAuditor::class)
->args([
service('asset_mapper.importmap.config_reader'),
service('http_client'),
service('asset_mapper.http_client'),
])
->set('asset_mapper.importmap.update_checker', ImportMapUpdateChecker::class)
->args([
service('asset_mapper.importmap.config_reader'),
service('http_client'),
service('asset_mapper.http_client'),
])
->set('asset_mapper.importmap.command.require', ImportMapRequireCommand::class)

View File

@@ -39,6 +39,7 @@ return static function (ContainerConfigurator $container) {
->factory('current')
->args([[service('http_client.transport')]])
->tag('http_client.client')
->tag('kernel.reset', ['method' => 'reset', 'on_invalid' => 'ignore'])
->alias(HttpClientInterface::class, 'http_client')

View File

@@ -45,11 +45,7 @@ return static function (ContainerConfigurator $container) {
tagged_iterator('mailer.transport_factory'),
])
->set('mailer.default_transport', TransportInterface::class)
->factory([service('mailer.transport_factory'), 'fromString'])
->args([
abstract_arg('env(MAILER_DSN)'),
])
->alias('mailer.default_transport', 'mailer.transports')
->alias(TransportInterface::class, 'mailer.default_transport')
->set('mailer.messenger.message_handler', MessageHandler::class)

View File

@@ -73,7 +73,10 @@ return static function (ContainerConfigurator $container) {
->tag('notifier.channel', ['channel' => 'email'])
->set('notifier.channel.push', PushChannel::class)
->args([service('texter.transports'), service('messenger.default_bus')->ignoreOnInvalid()])
->args([
service('texter.transports'),
abstract_arg('message bus'),
])
->tag('notifier.channel', ['channel' => 'push'])
->set('notifier.monolog_handler', NotifierHandler::class)

View File

@@ -40,7 +40,7 @@ return static function (ContainerConfigurator $container) {
->set('console_profiler_listener', ConsoleProfilerListener::class)
->args([
service('profiler'),
service('.lazy_profiler'),
service('.virtual_request_stack'),
service('debug.stopwatch'),
param('kernel.runtime_mode.cli'),
@@ -48,6 +48,11 @@ return static function (ContainerConfigurator $container) {
])
->tag('kernel.event_subscriber')
->set('.lazy_profiler', Profiler::class)
->factory('current')
->args([[service('profiler')]])
->lazy()
->set('.virtual_request_stack', VirtualRequestStack::class)
->args([service('request_stack')])
->public()

View File

@@ -266,6 +266,7 @@
<xsd:element name="static-method" type="xsd:string" />
<xsd:element name="mapping" type="file_mapping" />
<xsd:element name="auto-mapping" type="auto_mapping" />
<xsd:element name="not-compromised-password" type="not-compromised-password" />
</xsd:choice>
<xsd:attribute name="enabled" type="xsd:boolean" />
@@ -299,6 +300,11 @@
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="not-compromised-password">
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="endpoint" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="annotations">
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="debug" type="xsd:string" />
@@ -551,7 +557,7 @@
<xsd:complexType name="lock">
<xsd:sequence>
<xsd:element name="resource" type="lock_resource" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="resource" type="lock_resource" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="enabled" type="xsd:boolean" />
</xsd:complexType>
@@ -781,7 +787,7 @@
<xsd:complexType name="mailer_envelope">
<xsd:sequence>
<xsd:element name="sender" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="recipients" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="recipient" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>

View File

@@ -60,7 +60,7 @@ return static function (ContainerConfigurator $container) {
$container->services()
->set('serializer', Serializer::class)
->args([[], []])
->args([[], [], []])
->alias(SerializerInterface::class, 'serializer')
->alias(NormalizerInterface::class, 'serializer')
@@ -116,7 +116,7 @@ return static function (ContainerConfigurator $container) {
->set('serializer.normalizer.translatable', TranslatableNormalizer::class)
->args(['$translator' => service('translator')])
->tag('serializer.normalizer', ['priority' => -890])
->tag('serializer.normalizer', ['priority' => -920])
->set('serializer.normalizer.form_error', FormErrorNormalizer::class)
->tag('serializer.normalizer', ['priority' => -915])
@@ -129,6 +129,8 @@ return static function (ContainerConfigurator $container) {
service('property_info')->ignoreOnInvalid(),
service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(),
null,
null,
service('property_info')->ignoreOnInvalid(),
])
->tag('serializer.normalizer', ['priority' => -1000])

View File

@@ -100,6 +100,7 @@ return static function (ContainerConfigurator $container) {
->alias(HttpKernelInterface::class, 'http_kernel')
->set('request_stack', RequestStack::class)
->tag('kernel.reset', ['method' => 'resetRequestFormats', 'on_invalid' => 'ignore'])
->public()
->alias(RequestStack::class, 'request_stack')

View File

@@ -90,6 +90,7 @@ return static function (ContainerConfigurator $container) {
'session_factory' => service('session.factory')->ignoreOnInvalid(),
'logger' => service('logger')->ignoreOnInvalid(),
'session_collector' => service('data_collector.request.session_collector')->ignoreOnInvalid(),
'request_stack' => service('request_stack')->ignoreOnInvalid(),
]),
param('kernel.debug'),
param('session.storage.options'),

View File

@@ -42,7 +42,7 @@ use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
class AsRoutingConditionService extends AutoconfigureTag
{
public function __construct(
string $alias = null,
?string $alias = null,
int $priority = 0,
) {
parent::__construct('routing.condition_service', ['alias' => $alias, 'priority' => $priority]);

View File

@@ -40,7 +40,7 @@ class DelegatingLoader extends BaseDelegatingLoader
parent::__construct($resolver);
}
public function load(mixed $resource, string $type = null): RouteCollection
public function load(mixed $resource, ?string $type = null): RouteCollection
{
if ($this->loading) {
// This can happen if a fatal error occurs in parent::load().

View File

@@ -21,7 +21,7 @@ use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
*/
class RedirectableCompiledUrlMatcher extends CompiledUrlMatcher implements RedirectableUrlMatcherInterface
{
public function redirect(string $path, string $route, string $scheme = null): array
public function redirect(string $path, string $route, ?string $scheme = null): array
{
return [
'_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction',

View File

@@ -40,7 +40,7 @@ class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberI
/**
* @param mixed $resource The main resource to load
*/
public function __construct(ContainerInterface $container, mixed $resource, array $options = [], RequestContext $context = null, ContainerInterface $parameters = null, LoggerInterface $logger = null, string $defaultLocale = null)
public function __construct(ContainerInterface $container, mixed $resource, array $options = [], ?RequestContext $context = null, ?ContainerInterface $parameters = null, ?LoggerInterface $logger = null, ?string $defaultLocale = null)
{
$this->container = $container;
$this->resource = $resource;

View File

@@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle\Secrets;
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
use Symfony\Component\String\LazyString;
use Symfony\Component\VarExporter\VarExporter;
/**
@@ -30,7 +31,7 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface
* @param $decryptionKey A string or a stringable object that defines the private key to use to decrypt the vault
* or null to store generated keys in the provided $secretsDir
*/
public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable $decryptionKey = null)
public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable|null $decryptionKey = null)
{
$this->pathPrefix = rtrim(strtr($secretsDir, '/', \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.basename($secretsDir).'.';
$this->decryptionKey = $decryptionKey;
@@ -169,7 +170,14 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface
public function loadEnvVars(): array
{
return $this->list(true);
$envs = [];
$reveal = $this->reveal(...);
foreach ($this->list() as $name => $value) {
$envs[$name] = LazyString::fromCallable($reveal, $name);
}
return $envs;
}
private function loadKeys(): void

View File

@@ -43,7 +43,7 @@ trait BrowserKitAssertionsTrait
self::assertThatForResponse(new ResponseConstraint\ResponseFormatSame(self::getRequest(), $expectedFormat), $message);
}
public static function assertResponseRedirects(string $expectedLocation = null, int $expectedCode = null, string $message = ''): void
public static function assertResponseRedirects(?string $expectedLocation = null, ?int $expectedCode = null, string $message = ''): void
{
$constraint = new ResponseConstraint\ResponseIsRedirected();
if ($expectedLocation) {
@@ -82,17 +82,17 @@ trait BrowserKitAssertionsTrait
self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue)), $message);
}
public static function assertResponseHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
public static function assertResponseHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForResponse(new ResponseConstraint\ResponseHasCookie($name, $path, $domain), $message);
}
public static function assertResponseNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
public static function assertResponseNotHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHasCookie($name, $path, $domain)), $message);
}
public static function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', string $domain = null, string $message = ''): void
public static function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForResponse(LogicalAnd::fromConstraints(
new ResponseConstraint\ResponseHasCookie($name, $path, $domain),
@@ -105,17 +105,17 @@ trait BrowserKitAssertionsTrait
self::assertThatForResponse(new ResponseConstraint\ResponseIsUnprocessable(), $message);
}
public static function assertBrowserHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
public static function assertBrowserHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForClient(new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain), $message);
}
public static function assertBrowserNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
public static function assertBrowserNotHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForClient(new LogicalNot(new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain)), $message);
}
public static function assertBrowserCookieValueSame(string $name, string $expectedValue, bool $raw = false, string $path = '/', string $domain = null, string $message = ''): void
public static function assertBrowserCookieValueSame(string $name, string $expectedValue, bool $raw = false, string $path = '/', ?string $domain = null, string $message = ''): void
{
self::assertThatForClient(LogicalAnd::fromConstraints(
new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain),
@@ -162,7 +162,7 @@ trait BrowserKitAssertionsTrait
self::assertThat(self::getClient(), $constraint, $message);
}
protected static function getClient(AbstractBrowser $newClient = null): ?AbstractBrowser
protected static function getClient(?AbstractBrowser $newClient = null): ?AbstractBrowser
{
static $client;

View File

@@ -14,13 +14,12 @@ namespace Symfony\Bundle\FrameworkBundle\Test;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector;
/*
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
trait HttpClientAssertionsTrait
{
public static function assertHttpClientRequest(string $expectedUrl, string $expectedMethod = 'GET', string|array $expectedBody = null, array $expectedHeaders = [], string $httpClientId = 'http_client'): void
public static function assertHttpClientRequest(string $expectedUrl, string $expectedMethod = 'GET', string|array|null $expectedBody = null, array $expectedHeaders = [], string $httpClientId = 'http_client'): void
{
/** @var KernelBrowser $client */
$client = static::getClient();

View File

@@ -45,6 +45,14 @@ abstract class KernelTestCase extends TestCase
static::$booted = false;
}
public static function tearDownAfterClass(): void
{
static::ensureKernelShutdown();
static::$class = null;
static::$kernel = null;
static::$booted = false;
}
/**
* @throws \RuntimeException
* @throws \LogicException
@@ -126,6 +134,12 @@ abstract class KernelTestCase extends TestCase
if (null !== static::$kernel) {
static::$kernel->boot();
$container = static::$kernel->getContainer();
if ($container->has('services_resetter')) {
// Instantiate the service because Container::reset() only resets services that have been used
$container->get('services_resetter');
}
static::$kernel->shutdown();
static::$booted = false;

View File

@@ -20,12 +20,12 @@ use Symfony\Component\Mime\Test\Constraint as MimeConstraint;
trait MailerAssertionsTrait
{
public static function assertEmailCount(int $count, string $transport = null, string $message = ''): void
public static function assertEmailCount(int $count, ?string $transport = null, string $message = ''): void
{
self::assertThat(self::getMessageMailerEvents(), new MailerConstraint\EmailCount($count, $transport), $message);
}
public static function assertQueuedEmailCount(int $count, string $transport = null, string $message = ''): void
public static function assertQueuedEmailCount(int $count, ?string $transport = null, string $message = ''): void
{
self::assertThat(self::getMessageMailerEvents(), new MailerConstraint\EmailCount($count, $transport, true), $message);
}
@@ -103,12 +103,12 @@ trait MailerAssertionsTrait
/**
* @return MessageEvent[]
*/
public static function getMailerEvents(string $transport = null): array
public static function getMailerEvents(?string $transport = null): array
{
return self::getMessageMailerEvents()->getEvents($transport);
}
public static function getMailerEvent(int $index = 0, string $transport = null): ?MessageEvent
public static function getMailerEvent(int $index = 0, ?string $transport = null): ?MessageEvent
{
return self::getMailerEvents($transport)[$index] ?? null;
}
@@ -116,12 +116,12 @@ trait MailerAssertionsTrait
/**
* @return RawMessage[]
*/
public static function getMailerMessages(string $transport = null): array
public static function getMailerMessages(?string $transport = null): array
{
return self::getMessageMailerEvents()->getMessages($transport);
}
public static function getMailerMessage(int $index = 0, string $transport = null): ?RawMessage
public static function getMailerMessage(int $index = 0, ?string $transport = null): ?RawMessage
{
return self::getMailerMessages($transport)[$index] ?? null;
}

View File

@@ -17,17 +17,17 @@ use Symfony\Component\Notifier\Event\NotificationEvents;
use Symfony\Component\Notifier\Message\MessageInterface;
use Symfony\Component\Notifier\Test\Constraint as NotifierConstraint;
/*
/**
* @author Smaïne Milianni <smaine.milianni@gmail.com>
*/
trait NotificationAssertionsTrait
{
public static function assertNotificationCount(int $count, string $transportName = null, string $message = ''): void
public static function assertNotificationCount(int $count, ?string $transportName = null, string $message = ''): void
{
self::assertThat(self::getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName), $message);
}
public static function assertQueuedNotificationCount(int $count, string $transportName = null, string $message = ''): void
public static function assertQueuedNotificationCount(int $count, ?string $transportName = null, string $message = ''): void
{
self::assertThat(self::getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName, true), $message);
}
@@ -52,12 +52,12 @@ trait NotificationAssertionsTrait
self::assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationSubjectContains($text)), $message);
}
public static function assertNotificationTransportIsEqual(MessageInterface $notification, string $transportName = null, string $message = ''): void
public static function assertNotificationTransportIsEqual(MessageInterface $notification, ?string $transportName = null, string $message = ''): void
{
self::assertThat($notification, new NotifierConstraint\NotificationTransportIsEqual($transportName), $message);
}
public static function assertNotificationTransportIsNotEqual(MessageInterface $notification, string $transportName = null, string $message = ''): void
public static function assertNotificationTransportIsNotEqual(MessageInterface $notification, ?string $transportName = null, string $message = ''): void
{
self::assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationTransportIsEqual($transportName)), $message);
}
@@ -65,12 +65,12 @@ trait NotificationAssertionsTrait
/**
* @return MessageEvent[]
*/
public static function getNotifierEvents(string $transportName = null): array
public static function getNotifierEvents(?string $transportName = null): array
{
return self::getNotificationEvents()->getEvents($transportName);
}
public static function getNotifierEvent(int $index = 0, string $transportName = null): ?MessageEvent
public static function getNotifierEvent(int $index = 0, ?string $transportName = null): ?MessageEvent
{
return self::getNotifierEvents($transportName)[$index] ?? null;
}
@@ -78,12 +78,12 @@ trait NotificationAssertionsTrait
/**
* @return MessageInterface[]
*/
public static function getNotifierMessages(string $transportName = null): array
public static function getNotifierMessages(?string $transportName = null): array
{
return self::getNotificationEvents()->getMessages($transportName);
}
public static function getNotifierMessage(int $index = 0, string $transportName = null): ?MessageInterface
public static function getNotifierMessage(int $index = 0, ?string $transportName = null): ?MessageInterface
{
return self::getNotifierMessages($transportName)[$index] ?? null;
}

View File

@@ -23,7 +23,7 @@ class TestBrowserToken extends AbstractToken
{
private string $firewallName;
public function __construct(array $roles = [], UserInterface $user = null, string $firewallName = 'main')
public function __construct(array $roles = [], ?UserInterface $user = null, string $firewallName = 'main')
{
parent::__construct($roles);

View File

@@ -118,7 +118,7 @@ class Translator extends BaseTranslator implements WarmableInterface
return [];
}
public function addResource(string $format, mixed $resource, string $locale, string $domain = null): void
public function addResource(string $format, mixed $resource, string $locale, ?string $domain = null): void
{
if ($this->resourceFiles) {
$this->addResourceFiles();

View File

@@ -21,7 +21,7 @@
"ext-xml": "*",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/config": "^6.1|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4.12|^7.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/error-handler": "^6.1|^7.0",
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
@@ -57,7 +57,7 @@
"symfony/notifier": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|^6.0|^7.0",
"symfony/rate-limiter": "^5.4|^6.0|^7.0",
"symfony/scheduler": "^6.4|^7.0",
"symfony/scheduler": "^6.4.4|^7.0.4",
"symfony/security-bundle": "^5.4|^6.0|^7.0",
"symfony/semaphore": "^5.4|^6.0|^7.0",
"symfony/serializer": "^6.4|^7.0",
@@ -72,7 +72,7 @@
"symfony/uid": "^5.4|^6.0|^7.0",
"symfony/web-link": "^5.4|^6.0|^7.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"twig/twig": "^2.10|^3.0"
"twig/twig": "^2.10|^3.0.4"
},
"conflict": {
"doctrine/annotations": "<1.13.1",
@@ -93,7 +93,8 @@
"symfony/mime": "<6.4",
"symfony/property-info": "<5.4",
"symfony/property-access": "<5.4",
"symfony/scheduler": "<6.4",
"symfony/runtime": "<5.4.45|>=6.0,<6.4.13|>=7.0,<7.1.6",
"symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4",
"symfony/serializer": "<6.4",
"symfony/security-csrf": "<5.4",
"symfony/security-core": "<5.4",