From 0981f0b0955a764393f77bd22ed961e0449a3aa6 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 01/41] Prefix all sprintf() calls --- AbstractExtension.php | 2 +- Command/DebugCommand.php | 10 +++++----- Console/Descriptor/Descriptor.php | 2 +- Console/Descriptor/TextDescriptor.php | 8 ++++---- DependencyInjection/FormPass.php | 2 +- Exception/UnexpectedTypeException.php | 2 +- .../DataTransformer/ArrayToPartsTransformer.php | 2 +- .../DataTransformer/BaseDateTimeTransformer.php | 4 ++-- .../DataTransformer/ChoiceToValueTransformer.php | 2 +- .../DateIntervalToArrayTransformer.php | 8 ++++---- .../DateIntervalToStringTransformer.php | 2 +- .../DataTransformer/DateTimeToArrayTransformer.php | 4 ++-- .../DateTimeToHtml5LocalDateTimeTransformer.php | 4 ++-- .../DateTimeToLocalizedStringTransformer.php | 4 ++-- .../DateTimeToRfc3339Transformer.php | 4 ++-- .../IntegerToLocalizedStringTransformer.php | 2 +- .../IntlTimeZoneToStringTransformer.php | 2 +- .../MoneyToLocalizedStringTransformer.php | 2 +- .../NumberToLocalizedStringTransformer.php | 2 +- .../PercentToLocalizedStringTransformer.php | 2 +- .../DataTransformer/UlidToStringTransformer.php | 2 +- .../DataTransformer/UuidToStringTransformer.php | 4 ++-- .../ValueToDuplicatesTransformer.php | 2 +- .../DataTransformer/WeekToArrayTransformer.php | 14 +++++++------- Extension/Core/Type/BaseType.php | 6 +++--- Extension/Core/Type/ChoiceType.php | 4 ++-- Extension/Core/Type/CountryType.php | 2 +- Extension/Core/Type/CurrencyType.php | 2 +- Extension/Core/Type/DateTimeType.php | 10 +++++----- Extension/Core/Type/DateType.php | 8 ++++---- Extension/Core/Type/LanguageType.php | 2 +- Extension/Core/Type/LocaleType.php | 2 +- Extension/Core/Type/TimeType.php | 8 ++++---- Extension/Core/Type/TimezoneType.php | 2 +- Extension/Core/Type/WeekType.php | 2 +- Extension/DataCollector/FormDataCollector.php | 2 +- .../DependencyInjectionExtension.php | 4 ++-- .../EventListener/PasswordHasherListener.php | 2 +- Extension/Validator/Constraints/FormValidator.php | 6 +++--- Extension/Validator/ValidatorTypeGuesser.php | 4 ++-- .../Validator/ViolationMapper/MappingRule.php | 2 +- .../Validator/ViolationMapper/ViolationPath.php | 8 ++++---- Form.php | 12 ++++++------ FormBuilder.php | 2 +- FormConfigBuilder.php | 4 ++-- FormErrorIterator.php | 4 ++-- FormRegistry.php | 6 +++--- FormRenderer.php | 8 ++++---- PreloadedExtension.php | 2 +- ResolvedFormType.php | 2 +- Test/FormPerformanceTestCase.php | 2 +- Test/Traits/ValidatorExtensionTrait.php | 2 +- .../Descriptor/AbstractDescriptorTestCase.php | 2 +- .../Core/Type/ChoiceTypeTranslationTest.php | 2 +- .../DependencyInjectionExtensionTest.php | 2 +- Tests/ResolvedFormTypeTest.php | 2 +- Tests/Resources/TranslationFilesTest.php | 4 ++-- Tests/VersionAwareTest.php | 2 +- Util/OrderedHashMap.php | 2 +- 59 files changed, 114 insertions(+), 114 deletions(-) diff --git a/AbstractExtension.php b/AbstractExtension.php index 908f4f86f..27665727c 100644 --- a/AbstractExtension.php +++ b/AbstractExtension.php @@ -50,7 +50,7 @@ public function getType(string $name): FormTypeInterface } if (!isset($this->types[$name])) { - throw new InvalidArgumentException(sprintf('The type "%s" cannot be loaded by this extension.', $name)); + throw new InvalidArgumentException(\sprintf('The type "%s" cannot be loaded by this extension.', $name)); } return $this->types[$name]; diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index 4d9901229..18d208cbe 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -53,7 +53,7 @@ protected function configure(): void new InputArgument('class', InputArgument::OPTIONAL, 'The form type class'), new InputArgument('option', InputArgument::OPTIONAL, 'The form type option'), new InputOption('show-deprecated', null, InputOption::VALUE_NONE, 'Display deprecated options in form types'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), ]) ->setHelp(<<<'EOF' The %command.name% command displays information about form types. @@ -114,7 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $object = $resolvedType->getOptionsResolver(); if (!$object->isDefined($option)) { - $message = sprintf('Option "%s" is not defined in "%s".', $option, $resolvedType->getInnerType()::class); + $message = \sprintf('Option "%s" is not defined in "%s".', $option, $resolvedType->getInnerType()::class); if ($alternatives = $this->findAlternatives($option, $object->getDefinedOptions())) { if (1 === \count($alternatives)) { @@ -148,7 +148,7 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, strin $classes = $this->getFqcnTypeClasses($shortClassName); if (0 === $count = \count($classes)) { - $message = sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces)); + $message = \sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces)); $allTypes = array_merge($this->getCoreTypes(), $this->types); if ($alternatives = $this->findAlternatives($shortClassName, $allTypes)) { @@ -166,10 +166,10 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, strin return $classes[0]; } if (!$input->isInteractive()) { - throw new InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\n\nDid you mean one of these?\n %s.", $shortClassName, implode("\n ", $classes))); + throw new InvalidArgumentException(\sprintf("The type \"%s\" is ambiguous.\n\nDid you mean one of these?\n %s.", $shortClassName, implode("\n ", $classes))); } - return $io->choice(sprintf("The type \"%s\" is ambiguous.\n\nSelect one of the following form types to display its information:", $shortClassName), $classes, $classes[0]); + return $io->choice(\sprintf("The type \"%s\" is ambiguous.\n\nSelect one of the following form types to display its information:", $shortClassName), $classes, $classes[0]); } private function getFqcnTypeClasses(string $shortClassName): array diff --git a/Console/Descriptor/Descriptor.php b/Console/Descriptor/Descriptor.php index f4835fb1e..c910acdf4 100644 --- a/Console/Descriptor/Descriptor.php +++ b/Console/Descriptor/Descriptor.php @@ -46,7 +46,7 @@ public function describe(OutputInterface $output, ?object $object, array $option null === $object => $this->describeDefaults($options), $object instanceof ResolvedFormTypeInterface => $this->describeResolvedFormType($object, $options), $object instanceof OptionsResolver => $this->describeOption($object, $options), - default => throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))), + default => throw new \InvalidArgumentException(\sprintf('Object of type "%s" is not describable.', get_debug_type($object))), }; } diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index 7b723a0af..630b87253 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -80,7 +80,7 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF 'extension' => 'Extension options', ], $formOptions); - $this->output->title(sprintf('%s (Block prefix: "%s")', $resolvedFormType->getInnerType()::class, $resolvedFormType->getInnerType()->getBlockPrefix())); + $this->output->title(\sprintf('%s (Block prefix: "%s")', $resolvedFormType->getInnerType()::class, $resolvedFormType->getInnerType()->getBlockPrefix())); if ($formOptions) { $this->output->table($tableHeaders, $this->buildTableRows($tableHeaders, $formOptions)); @@ -131,7 +131,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio } array_pop($rows); - $this->output->title(sprintf('%s (%s)', $options['type']::class, $options['option'])); + $this->output->title(\sprintf('%s (%s)', $options['type']::class, $options['option'])); $this->output->table([], $rows); } @@ -172,7 +172,7 @@ private function normalizeAndSortOptionsColumns(array $options): array } else { $options[$group][] = null; } - $options[$group][] = sprintf('%s', (new \ReflectionClass($class))->getShortName()); + $options[$group][] = \sprintf('%s', (new \ReflectionClass($class))->getShortName()); $options[$group][] = new TableSeparator(); sort($opt); @@ -196,7 +196,7 @@ private function formatClassLink(string $class, ?string $text = null): string return $text; } - return sprintf('%s', $fileLink, $text); + return \sprintf('%s', $fileLink, $text); } private function getFileLink(string $class): string diff --git a/DependencyInjection/FormPass.php b/DependencyInjection/FormPass.php index 408731163..1d2b2a87e 100644 --- a/DependencyInjection/FormPass.php +++ b/DependencyInjection/FormPass.php @@ -89,7 +89,7 @@ private function processFormTypeExtensions(ContainerBuilder $container): array } if (!$extendsTypes) { - throw new InvalidArgumentException(sprintf('The getExtendedTypes() method for service "%s" does not return any extended types.', $serviceId)); + throw new InvalidArgumentException(\sprintf('The getExtendedTypes() method for service "%s" does not return any extended types.', $serviceId)); } } } diff --git a/Exception/UnexpectedTypeException.php b/Exception/UnexpectedTypeException.php index 7a4dc295c..223061b77 100644 --- a/Exception/UnexpectedTypeException.php +++ b/Exception/UnexpectedTypeException.php @@ -15,6 +15,6 @@ class UnexpectedTypeException extends InvalidArgumentException { public function __construct(mixed $value, string $expectedType) { - parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value))); + parent::__construct(\sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value))); } } diff --git a/Extension/Core/DataTransformer/ArrayToPartsTransformer.php b/Extension/Core/DataTransformer/ArrayToPartsTransformer.php index 828bd811e..92f59c906 100644 --- a/Extension/Core/DataTransformer/ArrayToPartsTransformer.php +++ b/Extension/Core/DataTransformer/ArrayToPartsTransformer.php @@ -72,7 +72,7 @@ public function reverseTransform(mixed $array): mixed return null; } - throw new TransformationFailedException(sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys))); + throw new TransformationFailedException(\sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys))); } return $result; diff --git a/Extension/Core/DataTransformer/BaseDateTimeTransformer.php b/Extension/Core/DataTransformer/BaseDateTimeTransformer.php index 8d311b3f8..ca85fd44d 100644 --- a/Extension/Core/DataTransformer/BaseDateTimeTransformer.php +++ b/Extension/Core/DataTransformer/BaseDateTimeTransformer.php @@ -47,13 +47,13 @@ public function __construct(?string $inputTimezone = null, ?string $outputTimezo try { new \DateTimeZone($this->inputTimezone); } catch (\Exception $e) { - throw new InvalidArgumentException(sprintf('Input timezone is invalid: "%s".', $this->inputTimezone), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Input timezone is invalid: "%s".', $this->inputTimezone), $e->getCode(), $e); } try { new \DateTimeZone($this->outputTimezone); } catch (\Exception $e) { - throw new InvalidArgumentException(sprintf('Output timezone is invalid: "%s".', $this->outputTimezone), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Output timezone is invalid: "%s".', $this->outputTimezone), $e->getCode(), $e); } } } diff --git a/Extension/Core/DataTransformer/ChoiceToValueTransformer.php b/Extension/Core/DataTransformer/ChoiceToValueTransformer.php index 52ee25e93..e75bc31de 100644 --- a/Extension/Core/DataTransformer/ChoiceToValueTransformer.php +++ b/Extension/Core/DataTransformer/ChoiceToValueTransformer.php @@ -45,7 +45,7 @@ public function reverseTransform(mixed $value): mixed return null; } - throw new TransformationFailedException(sprintf('The choice "%s" does not exist or is not unique.', $value)); + throw new TransformationFailedException(\sprintf('The choice "%s" does not exist or is not unique.', $value)); } return current($choices); diff --git a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php index 08c058597..0475b55bf 100644 --- a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php +++ b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php @@ -124,19 +124,19 @@ public function reverseTransform(mixed $value): ?\DateInterval } } if (\count($emptyFields) > 0) { - throw new TransformationFailedException(sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields))); + throw new TransformationFailedException(\sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields))); } if (isset($value['invert']) && !\is_bool($value['invert'])) { throw new TransformationFailedException('The value of "invert" must be boolean.'); } foreach (self::AVAILABLE_FIELDS as $field => $char) { if ('invert' !== $field && isset($value[$field]) && !ctype_digit((string) $value[$field])) { - throw new TransformationFailedException(sprintf('This amount of "%s" is invalid.', $field)); + throw new TransformationFailedException(\sprintf('This amount of "%s" is invalid.', $field)); } } try { if (!empty($value['weeks'])) { - $interval = sprintf( + $interval = \sprintf( 'P%sY%sM%sWT%sH%sM%sS', empty($value['years']) ? '0' : $value['years'], empty($value['months']) ? '0' : $value['months'], @@ -146,7 +146,7 @@ public function reverseTransform(mixed $value): ?\DateInterval empty($value['seconds']) ? '0' : $value['seconds'] ); } else { - $interval = sprintf( + $interval = \sprintf( 'P%sY%sM%sDT%sH%sM%sS', empty($value['years']) ? '0' : $value['years'], empty($value['months']) ? '0' : $value['months'], diff --git a/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php b/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php index e1a3d9724..527836bf5 100644 --- a/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php +++ b/Extension/Core/DataTransformer/DateIntervalToStringTransformer.php @@ -79,7 +79,7 @@ public function reverseTransform(mixed $value): ?\DateInterval } $valuePattern = '/^'.preg_replace('/%([yYmMdDhHiIsSwW])(\w)/', '(?P<$1>\d+)$2', $this->format).'$/'; if (!preg_match($valuePattern, $value)) { - throw new TransformationFailedException(sprintf('Value "%s" contains intervals not accepted by format "%s".', $value, $this->format)); + throw new TransformationFailedException(\sprintf('Value "%s" contains intervals not accepted by format "%s".', $value, $this->format)); } try { $dateInterval = new \DateInterval($value); diff --git a/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php index 8c3d2d226..06d24cf8e 100644 --- a/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php +++ b/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php @@ -126,7 +126,7 @@ public function reverseTransform(mixed $value): ?\DateTime } if (\count($emptyFields) > 0) { - throw new TransformationFailedException(sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields))); + throw new TransformationFailedException(\sprintf('The fields "%s" should not be empty.', implode('", "', $emptyFields))); } if (isset($value['month']) && !ctype_digit((string) $value['month'])) { @@ -158,7 +158,7 @@ public function reverseTransform(mixed $value): ?\DateTime } try { - $dateTime = new \DateTime(sprintf( + $dateTime = new \DateTime(\sprintf( '%s-%s-%s %s:%s:%s', empty($value['year']) ? $this->referenceDate->format('Y') : $value['year'], empty($value['month']) ? $this->referenceDate->format('m') : $value['month'], diff --git a/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php b/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php index 855b22a49..dbeed8e49 100644 --- a/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php +++ b/Extension/Core/DataTransformer/DateTimeToHtml5LocalDateTimeTransformer.php @@ -85,7 +85,7 @@ public function reverseTransform(mixed $dateTimeLocal): ?\DateTime // to maintain backwards compatibility we do not strictly validate the submitted date // see https://github.com/symfony/symfony/issues/28699 if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})[T ]\d{2}:\d{2}(?::\d{2})?/', $dateTimeLocal, $matches)) { - throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $dateTimeLocal)); + throw new TransformationFailedException(\sprintf('The date "%s" is not a valid date.', $dateTimeLocal)); } try { @@ -99,7 +99,7 @@ public function reverseTransform(mixed $dateTimeLocal): ?\DateTime } if (!checkdate($matches[2], $matches[3], $matches[1])) { - throw new TransformationFailedException(sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3])); + throw new TransformationFailedException(\sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3])); } return $dateTime; diff --git a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index b7ea09259..a93803cd8 100644 --- a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -128,7 +128,7 @@ public function reverseTransform(mixed $value): ?\DateTime } elseif (false === $timestamp) { // the value couldn't be parsed but the Intl extension didn't report an error code, this // could be the case when the Intl polyfill is used which always returns 0 as the error code - throw new TransformationFailedException(sprintf('"%s" could not be parsed as a date.', $value)); + throw new TransformationFailedException(\sprintf('"%s" could not be parsed as a date.', $value)); } try { @@ -137,7 +137,7 @@ public function reverseTransform(mixed $value): ?\DateTime $dateTime = new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->outputTimezone)); } else { // read timestamp into DateTime object - the formatter delivers a timestamp - $dateTime = new \DateTime(sprintf('@%s', $timestamp)); + $dateTime = new \DateTime(\sprintf('@%s', $timestamp)); } // set timezone separately, as it would be ignored if set via the constructor, // see https://php.net/datetime.construct diff --git a/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php b/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php index 41e63e57c..d32b3eae2 100644 --- a/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php +++ b/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php @@ -64,7 +64,7 @@ public function reverseTransform(mixed $rfc3339): ?\DateTime } if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))$/', $rfc3339, $matches)) { - throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $rfc3339)); + throw new TransformationFailedException(\sprintf('The date "%s" is not a valid date.', $rfc3339)); } try { @@ -78,7 +78,7 @@ public function reverseTransform(mixed $rfc3339): ?\DateTime } if (!checkdate($matches[2], $matches[3], $matches[1])) { - throw new TransformationFailedException(sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3])); + throw new TransformationFailedException(\sprintf('The date "%s-%s-%s" is not a valid date.', $matches[1], $matches[2], $matches[3])); } return $dateTime; diff --git a/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php index eb5a2d6ff..d83e31b42 100644 --- a/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -38,7 +38,7 @@ public function reverseTransform(mixed $value): int|float|null $decimalSeparator = $this->getNumberFormatter()->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); if (\is_string($value) && str_contains($value, $decimalSeparator)) { - throw new TransformationFailedException(sprintf('The value "%s" is not a valid integer.', $value)); + throw new TransformationFailedException(\sprintf('The value "%s" is not a valid integer.', $value)); } $result = parent::reverseTransform($value); diff --git a/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php b/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php index 446a95f70..19626e49c 100644 --- a/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php +++ b/Extension/Core/DataTransformer/IntlTimeZoneToStringTransformer.php @@ -70,7 +70,7 @@ public function reverseTransform(mixed $value): mixed $intlTimeZone = \IntlTimeZone::createTimeZone($value); if ('Etc/Unknown' === $intlTimeZone->getID()) { - throw new TransformationFailedException(sprintf('Unknown timezone identifier "%s".', $value)); + throw new TransformationFailedException(\sprintf('Unknown timezone identifier "%s".', $value)); } return $intlTimeZone; diff --git a/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php index caf6750e9..b03f8da44 100644 --- a/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php @@ -75,7 +75,7 @@ public function reverseTransform(mixed $value): int|float|null } if ($value > \PHP_INT_MAX || $value < \PHP_INT_MIN) { - throw new TransformationFailedException(sprintf('Cannot cast "%s" to an integer. Try setting the input to "float" instead.', $value)); + throw new TransformationFailedException(\sprintf('Cannot cast "%s" to an integer. Try setting the input to "float" instead.', $value)); } $value = (int) $value; diff --git a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 6e1c495ae..2a6d146f9 100644 --- a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -140,7 +140,7 @@ public function reverseTransform(mixed $value): int|float|null $remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0"); if ('' !== $remainder) { - throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s".', $remainder)); + throw new TransformationFailedException(\sprintf('The number contains unrecognized characters: "%s".', $remainder)); } } diff --git a/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php index d4d8aad9d..8ae5e4c96 100644 --- a/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php @@ -158,7 +158,7 @@ public function reverseTransform(mixed $value): int|float|null $remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0"); if ('' !== $remainder) { - throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s".', $remainder)); + throw new TransformationFailedException(\sprintf('The number contains unrecognized characters: "%s".', $remainder)); } } diff --git a/Extension/Core/DataTransformer/UlidToStringTransformer.php b/Extension/Core/DataTransformer/UlidToStringTransformer.php index 7ace73ad0..9365cab15 100644 --- a/Extension/Core/DataTransformer/UlidToStringTransformer.php +++ b/Extension/Core/DataTransformer/UlidToStringTransformer.php @@ -65,7 +65,7 @@ public function reverseTransform(mixed $value): ?Ulid try { $ulid = new Ulid($value); } catch (\InvalidArgumentException $e) { - throw new TransformationFailedException(sprintf('The value "%s" is not a valid ULID.', $value), $e->getCode(), $e); + throw new TransformationFailedException(\sprintf('The value "%s" is not a valid ULID.', $value), $e->getCode(), $e); } return $ulid; diff --git a/Extension/Core/DataTransformer/UuidToStringTransformer.php b/Extension/Core/DataTransformer/UuidToStringTransformer.php index cc794a024..43326eb64 100644 --- a/Extension/Core/DataTransformer/UuidToStringTransformer.php +++ b/Extension/Core/DataTransformer/UuidToStringTransformer.php @@ -63,13 +63,13 @@ public function reverseTransform(mixed $value): ?Uuid } if (!Uuid::isValid($value)) { - throw new TransformationFailedException(sprintf('The value "%s" is not a valid UUID.', $value)); + throw new TransformationFailedException(\sprintf('The value "%s" is not a valid UUID.', $value)); } try { return Uuid::fromString($value); } catch (\InvalidArgumentException $e) { - throw new TransformationFailedException(sprintf('The value "%s" is not a valid UUID.', $value), $e->getCode(), $e); + throw new TransformationFailedException(\sprintf('The value "%s" is not a valid UUID.', $value), $e->getCode(), $e); } } } diff --git a/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php b/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php index 9700b0424..43ed17649 100644 --- a/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php +++ b/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php @@ -71,7 +71,7 @@ public function reverseTransform(mixed $array): mixed return null; } - throw new TransformationFailedException(sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys))); + throw new TransformationFailedException(\sprintf('The keys "%s" should not be empty.', implode('", "', $emptyKeys))); } return $result; diff --git a/Extension/Core/DataTransformer/WeekToArrayTransformer.php b/Extension/Core/DataTransformer/WeekToArrayTransformer.php index c10bc735f..448ae4278 100644 --- a/Extension/Core/DataTransformer/WeekToArrayTransformer.php +++ b/Extension/Core/DataTransformer/WeekToArrayTransformer.php @@ -40,7 +40,7 @@ public function transform(mixed $value): array } if (!\is_string($value)) { - throw new TransformationFailedException(sprintf('Value is expected to be a string but was "%s".', get_debug_type($value))); + throw new TransformationFailedException(\sprintf('Value is expected to be a string but was "%s".', get_debug_type($value))); } if (0 === preg_match('/^(?P\d{4})-W(?P\d{2})$/', $value, $matches)) { @@ -70,7 +70,7 @@ public function reverseTransform(mixed $value): ?string } if (!\is_array($value)) { - throw new TransformationFailedException(sprintf('Value is expected to be an array, but was "%s".', get_debug_type($value))); + throw new TransformationFailedException(\sprintf('Value is expected to be an array, but was "%s".', get_debug_type($value))); } if (!\array_key_exists('year', $value)) { @@ -82,7 +82,7 @@ public function reverseTransform(mixed $value): ?string } if ($additionalKeys = array_diff(array_keys($value), ['year', 'week'])) { - throw new TransformationFailedException(sprintf('Expected only keys "year" and "week" to be present, but also got ["%s"].', implode('", "', $additionalKeys))); + throw new TransformationFailedException(\sprintf('Expected only keys "year" and "week" to be present, but also got ["%s"].', implode('", "', $additionalKeys))); } if (null === $value['year'] && null === $value['week']) { @@ -90,18 +90,18 @@ public function reverseTransform(mixed $value): ?string } if (!\is_int($value['year'])) { - throw new TransformationFailedException(sprintf('Year is expected to be an integer, but was "%s".', get_debug_type($value['year']))); + throw new TransformationFailedException(\sprintf('Year is expected to be an integer, but was "%s".', get_debug_type($value['year']))); } if (!\is_int($value['week'])) { - throw new TransformationFailedException(sprintf('Week is expected to be an integer, but was "%s".', get_debug_type($value['week']))); + throw new TransformationFailedException(\sprintf('Week is expected to be an integer, but was "%s".', get_debug_type($value['week']))); } // The 28th December is always in the last week of the year if (date('W', strtotime('28th December '.$value['year'])) < $value['week']) { - throw new TransformationFailedException(sprintf('Week "%d" does not exist for year "%d".', $value['week'], $value['year'])); + throw new TransformationFailedException(\sprintf('Week "%d" does not exist for year "%d".', $value['week'], $value['year'])); } - return sprintf('%d-W%02d', $value['year'], $value['week']); + return \sprintf('%d-W%02d', $value['year'], $value['week']); } } diff --git a/Extension/Core/Type/BaseType.php b/Extension/Core/Type/BaseType.php index 68190c742..a7f745d78 100644 --- a/Extension/Core/Type/BaseType.php +++ b/Extension/Core/Type/BaseType.php @@ -46,9 +46,9 @@ public function buildView(FormView $view, FormInterface $form, array $options): if ($view->parent) { if ('' !== ($parentFullName = $view->parent->vars['full_name'])) { - $id = sprintf('%s_%s', $view->parent->vars['id'], $name); - $fullName = sprintf('%s[%s]', $parentFullName, $name); - $uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName); + $id = \sprintf('%s_%s', $view->parent->vars['id'], $name); + $fullName = \sprintf('%s[%s]', $parentFullName, $name); + $uniqueBlockPrefix = \sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName); } else { $id = $name; $fullName = $name; diff --git a/Extension/Core/Type/ChoiceType.php b/Extension/Core/Type/ChoiceType.php index 2e9cb7087..36ebe6c84 100644 --- a/Extension/Core/Type/ChoiceType.php +++ b/Extension/Core/Type/ChoiceType.php @@ -158,7 +158,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void // Throw exception if unknown values were submitted (multiple choices will be handled in a different event listener below) if (\count($unknownValues) > 0 && !$options['multiple']) { - throw new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', implode('", "', array_keys($unknownValues)))); + throw new TransformationFailedException(\sprintf('The choices "%s" do not exist in the choice list.', implode('", "', array_keys($unknownValues)))); } $event->setData($knownValues); @@ -182,7 +182,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $message = strtr($messageTemplate, ['{{ value }}' => $clientDataAsString]); } - $form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', $clientDataAsString)))); + $form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(\sprintf('The choices "%s" do not exist in the choice list.', $clientDataAsString)))); } }); diff --git a/Extension/Core/Type/CountryType.php b/Extension/Core/Type/CountryType.php index adef745c5..503bc8327 100644 --- a/Extension/Core/Type/CountryType.php +++ b/Extension/Core/Type/CountryType.php @@ -27,7 +27,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setDefaults([ 'choice_loader' => function (Options $options) { if (!class_exists(Intl::class)) { - throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); + throw new LogicException(\sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); } $choiceTranslationLocale = $options['choice_translation_locale']; diff --git a/Extension/Core/Type/CurrencyType.php b/Extension/Core/Type/CurrencyType.php index 3581a77d9..abfa39729 100644 --- a/Extension/Core/Type/CurrencyType.php +++ b/Extension/Core/Type/CurrencyType.php @@ -27,7 +27,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setDefaults([ 'choice_loader' => function (Options $options) { if (!class_exists(Intl::class)) { - throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); + throw new LogicException(\sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); } $choiceTranslationLocale = $options['choice_translation_locale']; diff --git a/Extension/Core/Type/DateTimeType.php b/Extension/Core/Type/DateTimeType.php index 1c67eeb15..cf4c2b741 100644 --- a/Extension/Core/Type/DateTimeType.php +++ b/Extension/Core/Type/DateTimeType.php @@ -203,7 +203,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } if ($date->getTimezone()->getName() !== $options['model_timezone']) { - throw new LogicException(sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); + throw new LogicException(\sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); } }); } @@ -311,7 +311,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setNormalizer('date_format', static function (Options $options, $dateFormat) { if (null !== $dateFormat && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { - throw new LogicException(sprintf('Cannot use the "date_format" option of the "%s" with an HTML5 date.', self::class)); + throw new LogicException(\sprintf('Cannot use the "date_format" option of the "%s" with an HTML5 date.', self::class)); } return $dateFormat; @@ -319,10 +319,10 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setNormalizer('widget', static function (Options $options, $widget) { if ('single_text' === $widget) { if (null !== $options['date_widget']) { - throw new LogicException(sprintf('Cannot use the "date_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class)); + throw new LogicException(\sprintf('Cannot use the "date_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class)); } if (null !== $options['time_widget']) { - throw new LogicException(sprintf('Cannot use the "time_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class)); + throw new LogicException(\sprintf('Cannot use the "time_widget" option of the "%s" when the "widget" option is set to "single_text".', self::class)); } } elseif (null === $widget && null === $options['date_widget'] && null === $options['time_widget']) { return 'single_text'; @@ -332,7 +332,7 @@ public function configureOptions(OptionsResolver $resolver): void }); $resolver->setNormalizer('html5', static function (Options $options, $html5) { if ($html5 && self::HTML5_FORMAT !== $options['format']) { - throw new LogicException(sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class)); + throw new LogicException(\sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class)); } return $html5; diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index cfbbaf4c0..773a51cbd 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -58,7 +58,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void if ('single_text' === $options['widget']) { if ('' !== $pattern && !str_contains($pattern, 'y') && !str_contains($pattern, 'M') && !str_contains($pattern, 'd')) { - throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" or "d". Its current value is "%s".', $pattern)); + throw new InvalidOptionsException(\sprintf('The "format" option should contain the letters "y", "M" or "d". Its current value is "%s".', $pattern)); } $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( @@ -71,7 +71,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void )); } else { if ('' !== $pattern && (!str_contains($pattern, 'y') || !str_contains($pattern, 'M') || !str_contains($pattern, 'd'))) { - throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern)); + throw new InvalidOptionsException(\sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern)); } $yearOptions = $monthOptions = $dayOptions = [ @@ -187,7 +187,7 @@ class_exists(\IntlTimeZone::class, false) ? \IntlTimeZone::createDefault() : nul } if ($date->getTimezone()->getName() !== $options['model_timezone']) { - throw new LogicException(sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); + throw new LogicException(\sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); } }); } @@ -323,7 +323,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setNormalizer('html5', static function (Options $options, $html5) { if ($html5 && 'single_text' === $options['widget'] && self::HTML5_FORMAT !== $options['format']) { - throw new LogicException(sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class)); + throw new LogicException(\sprintf('Cannot use the "format" option of "%s" when the "html5" option is enabled.', self::class)); } return $html5; diff --git a/Extension/Core/Type/LanguageType.php b/Extension/Core/Type/LanguageType.php index e81571f8c..638976a8c 100644 --- a/Extension/Core/Type/LanguageType.php +++ b/Extension/Core/Type/LanguageType.php @@ -28,7 +28,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setDefaults([ 'choice_loader' => function (Options $options) { if (!class_exists(Intl::class)) { - throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); + throw new LogicException(\sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); } $choiceTranslationLocale = $options['choice_translation_locale']; $useAlpha3Codes = $options['alpha3']; diff --git a/Extension/Core/Type/LocaleType.php b/Extension/Core/Type/LocaleType.php index d0124e600..1529ca83f 100644 --- a/Extension/Core/Type/LocaleType.php +++ b/Extension/Core/Type/LocaleType.php @@ -27,7 +27,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setDefaults([ 'choice_loader' => function (Options $options) { if (!class_exists(Intl::class)) { - throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); + throw new LogicException(\sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); } $choiceTranslationLocale = $options['choice_translation_locale']; diff --git a/Extension/Core/Type/TimeType.php b/Extension/Core/Type/TimeType.php index 4e01489e7..ad559760d 100644 --- a/Extension/Core/Type/TimeType.php +++ b/Extension/Core/Type/TimeType.php @@ -45,7 +45,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } if (null !== $options['reference_date'] && $options['reference_date']->getTimezone()->getName() !== $options['model_timezone']) { - throw new InvalidConfigurationException(sprintf('The configured "model_timezone" (%s) must match the timezone of the "reference_date" (%s).', $options['model_timezone'], $options['reference_date']->getTimezone()->getName())); + throw new InvalidConfigurationException(\sprintf('The configured "model_timezone" (%s) must match the timezone of the "reference_date" (%s).', $options['model_timezone'], $options['reference_date']->getTimezone()->getName())); } if ($options['with_minutes']) { @@ -66,9 +66,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void if ($options['with_seconds']) { // handle seconds ignored by user's browser when with_seconds enabled // https://codereview.chromium.org/450533009/ - $e->setData(sprintf('%s:%s:%s', $matches['hours'], $matches['minutes'], $matches['seconds'] ?? '00')); + $e->setData(\sprintf('%s:%s:%s', $matches['hours'], $matches['minutes'], $matches['seconds'] ?? '00')); } else { - $e->setData(sprintf('%s:%s', $matches['hours'], $matches['minutes'])); + $e->setData(\sprintf('%s:%s', $matches['hours'], $matches['minutes'])); } } }); @@ -217,7 +217,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } if ($date->getTimezone()->getName() !== $options['model_timezone']) { - throw new LogicException(sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); + throw new LogicException(\sprintf('Using a "%s" instance with a timezone ("%s") not matching the configured model timezone "%s" is not supported.', get_debug_type($date), $date->getTimezone()->getName(), $options['model_timezone'])); } }); } diff --git a/Extension/Core/Type/TimezoneType.php b/Extension/Core/Type/TimezoneType.php index 01ce68ce3..2316d666b 100644 --- a/Extension/Core/Type/TimezoneType.php +++ b/Extension/Core/Type/TimezoneType.php @@ -43,7 +43,7 @@ public function configureOptions(OptionsResolver $resolver): void if ($options['intl']) { if (!class_exists(Intl::class)) { - throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s" with option "intl=true". Try running "composer require symfony/intl".', static::class)); + throw new LogicException(\sprintf('The "symfony/intl" component is required to use "%s" with option "intl=true". Try running "composer require symfony/intl".', static::class)); } $choiceTranslationLocale = $options['choice_translation_locale']; diff --git a/Extension/Core/Type/WeekType.php b/Extension/Core/Type/WeekType.php index c3ffae061..ec5cc9303 100644 --- a/Extension/Core/Type/WeekType.php +++ b/Extension/Core/Type/WeekType.php @@ -145,7 +145,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); $resolver->setNormalizer('html5', static function (Options $options, $html5) { if ($html5 && 'single_text' !== $options['widget']) { - throw new LogicException(sprintf('The "widget" option of "%s" must be set to "single_text" when the "html5" option is enabled.', self::class)); + throw new LogicException(\sprintf('The "widget" option of "%s" must be set to "single_text" when the "html5" option is enabled.', self::class)); } return $html5; diff --git a/Extension/DataCollector/FormDataCollector.php b/Extension/DataCollector/FormDataCollector.php index 348be44aa..e6cae863e 100644 --- a/Extension/DataCollector/FormDataCollector.php +++ b/Extension/DataCollector/FormDataCollector.php @@ -64,7 +64,7 @@ public function __construct( private FormDataExtractorInterface $dataExtractor, ) { if (!class_exists(ClassStub::class)) { - throw new \LogicException(sprintf('The VarDumper component is needed for using the "%s" class. Install symfony/var-dumper version 3.4 or above.', __CLASS__)); + throw new \LogicException(\sprintf('The VarDumper component is needed for using the "%s" class. Install symfony/var-dumper version 3.4 or above.', __CLASS__)); } $this->reset(); diff --git a/Extension/DependencyInjection/DependencyInjectionExtension.php b/Extension/DependencyInjection/DependencyInjectionExtension.php index 420f26b74..f986bda41 100644 --- a/Extension/DependencyInjection/DependencyInjectionExtension.php +++ b/Extension/DependencyInjection/DependencyInjectionExtension.php @@ -37,7 +37,7 @@ public function __construct( public function getType(string $name): FormTypeInterface { if (!$this->typeContainer->has($name)) { - throw new InvalidArgumentException(sprintf('The field type "%s" is not registered in the service container.', $name)); + throw new InvalidArgumentException(\sprintf('The field type "%s" is not registered in the service container.', $name)); } return $this->typeContainer->get($name); @@ -63,7 +63,7 @@ public function getTypeExtensions(string $name): array // validate the result of getExtendedTypes() to ensure it is consistent with the service definition if (!\in_array($name, $extendedTypes, true)) { - throw new InvalidArgumentException(sprintf('The extended type "%s" specified for the type extension class "%s" does not match any of the actual extended types (["%s"]).', $name, $extension::class, implode('", "', $extendedTypes))); + throw new InvalidArgumentException(\sprintf('The extended type "%s" specified for the type extension class "%s" does not match any of the actual extended types (["%s"]).', $name, $extension::class, implode('", "', $extendedTypes))); } } } diff --git a/Extension/PasswordHasher/EventListener/PasswordHasherListener.php b/Extension/PasswordHasher/EventListener/PasswordHasherListener.php index 3ddac5ff3..0144cc3ff 100644 --- a/Extension/PasswordHasher/EventListener/PasswordHasherListener.php +++ b/Extension/PasswordHasher/EventListener/PasswordHasherListener.php @@ -95,7 +95,7 @@ private function getUser(FormInterface $form): PasswordAuthenticatedUserInterfac $parent = $this->getTargetForm($form)->getParent(); if (!($user = $parent?->getData()) || !$user instanceof PasswordAuthenticatedUserInterface) { - throw new InvalidConfigurationException(sprintf('The "hash_property_path" option only supports "%s" objects, "%s" given.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); + throw new InvalidConfigurationException(\sprintf('The "hash_property_path" option only supports "%s" objects, "%s" given.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); } return $user; diff --git a/Extension/Validator/Constraints/FormValidator.php b/Extension/Validator/Constraints/FormValidator.php index 416358757..703bac148 100644 --- a/Extension/Validator/Constraints/FormValidator.php +++ b/Extension/Validator/Constraints/FormValidator.php @@ -92,7 +92,7 @@ public function validate(mixed $form, Constraint $formConstraint): void $fieldFormConstraint = new Form(); $fieldFormConstraint->groups = $group; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group); + $validator->atPath(\sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group); } } @@ -139,7 +139,7 @@ public function validate(mixed $form, Constraint $formConstraint): void if ($field->isSubmitted()) { $this->resolvedGroups[$field] = $groups; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint); + $validator->atPath(\sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint); } } } @@ -156,7 +156,7 @@ public function validate(mixed $form, Constraint $formConstraint): void if (!$child->isSynchronized()) { $childrenSynchronized = false; $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint); + $validator->atPath(\sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint); } } diff --git a/Extension/Validator/ValidatorTypeGuesser.php b/Extension/Validator/ValidatorTypeGuesser.php index 72ae8ddd5..08dc6e2d5 100644 --- a/Extension/Validator/ValidatorTypeGuesser.php +++ b/Extension/Validator/ValidatorTypeGuesser.php @@ -230,7 +230,7 @@ public function guessPatternForConstraint(Constraint $constraint): ?ValueGuess switch ($constraint::class) { case Length::class: if (is_numeric($constraint->min)) { - return new ValueGuess(sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE); + return new ValueGuess(\sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE); } break; @@ -244,7 +244,7 @@ public function guessPatternForConstraint(Constraint $constraint): ?ValueGuess case Range::class: if (is_numeric($constraint->min)) { - return new ValueGuess(sprintf('.{%s,}', \strlen((string) $constraint->min)), Guess::LOW_CONFIDENCE); + return new ValueGuess(\sprintf('.{%s,}', \strlen((string) $constraint->min)), Guess::LOW_CONFIDENCE); } break; diff --git a/Extension/Validator/ViolationMapper/MappingRule.php b/Extension/Validator/ViolationMapper/MappingRule.php index f9a61cc81..3263f66df 100644 --- a/Extension/Validator/ViolationMapper/MappingRule.php +++ b/Extension/Validator/ViolationMapper/MappingRule.php @@ -64,7 +64,7 @@ public function getTarget(): FormInterface foreach ($childNames as $childName) { if (!$target->has($childName)) { - throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName())); + throw new ErrorMappingException(\sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName())); } $target = $target->get($childName); } diff --git a/Extension/Validator/ViolationMapper/ViolationPath.php b/Extension/Validator/ViolationMapper/ViolationPath.php index a9a0f15d6..0c2a130cc 100644 --- a/Extension/Validator/ViolationMapper/ViolationPath.php +++ b/Extension/Validator/ViolationMapper/ViolationPath.php @@ -132,7 +132,7 @@ public function getElements(): array public function getElement(int $index): string { if (!isset($this->elements[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the violation path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the violation path.', $index)); } return $this->elements[$index]; @@ -141,7 +141,7 @@ public function getElement(int $index): string public function isProperty(int $index): bool { if (!isset($this->isIndex[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the violation path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the violation path.', $index)); } return !$this->isIndex[$index]; @@ -150,7 +150,7 @@ public function isProperty(int $index): bool public function isIndex(int $index): bool { if (!isset($this->isIndex[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the violation path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the violation path.', $index)); } return $this->isIndex[$index]; @@ -176,7 +176,7 @@ public function isNullSafe(int $index): bool public function mapsForm(int $index): bool { if (!isset($this->mapsForm[$index])) { - throw new OutOfBoundsException(sprintf('The index "%s" is not within the violation path.', $index)); + throw new OutOfBoundsException(\sprintf('The index "%s" is not within the violation path.', $index)); } return $this->mapsForm[$index]; diff --git a/Form.php b/Form.php index 44b97261b..72c60ee41 100644 --- a/Form.php +++ b/Form.php @@ -738,7 +738,7 @@ public function add(FormInterface|string $child, ?string $type = null, array $op $child = $this->config->getFormFactory()->createNamed($child, $type, null, $options); } } elseif ($child->getConfig()->getAutoInitialize()) { - throw new RuntimeException(sprintf('Automatic initialization is only supported on root forms. You should set the "auto_initialize" option to false on the field "%s".', $child->getName())); + throw new RuntimeException(\sprintf('Automatic initialization is only supported on root forms. You should set the "auto_initialize" option to false on the field "%s".', $child->getName())); } $this->children[$child->getName()] = $child; @@ -800,7 +800,7 @@ public function get(string $name): FormInterface return $this->children[$name]; } - throw new OutOfBoundsException(sprintf('Child "%s" does not exist.', $name)); + throw new OutOfBoundsException(\sprintf('Child "%s" does not exist.', $name)); } /** @@ -933,7 +933,7 @@ private function modelToNorm(mixed $value): mixed $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(\sprintf('Unable to transform data for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -953,7 +953,7 @@ private function normToModel(mixed $value): mixed $value = $transformers[$i]->reverseTransform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(\sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -980,7 +980,7 @@ private function normToView(mixed $value): mixed $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(\sprintf('Unable to transform value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -1002,7 +1002,7 @@ private function viewToNorm(mixed $value): mixed $value = $transformers[$i]->reverseTransform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(\sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; diff --git a/FormBuilder.php b/FormBuilder.php index 58bc9c86d..2bfd92a68 100644 --- a/FormBuilder.php +++ b/FormBuilder.php @@ -97,7 +97,7 @@ public function get(string $name): FormBuilderInterface return $this->children[$name]; } - throw new InvalidArgumentException(sprintf('The child with the name "%s" does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The child with the name "%s" does not exist.', $name)); } public function remove(string $name): static diff --git a/FormConfigBuilder.php b/FormConfigBuilder.php index e0fb01dad..5d6bd653d 100644 --- a/FormConfigBuilder.php +++ b/FormConfigBuilder.php @@ -76,7 +76,7 @@ public function __construct( self::validateName($name); if (null !== $dataClass && !class_exists($dataClass) && !interface_exists($dataClass, false)) { - throw new InvalidArgumentException(sprintf('Class "%s" not found. Is the "data_class" form option set correctly?', $dataClass)); + throw new InvalidArgumentException(\sprintf('Class "%s" not found. Is the "data_class" form option set correctly?', $dataClass)); } $this->name = (string) $name; @@ -632,7 +632,7 @@ public function setIsEmptyCallback(?callable $isEmptyCallback): static final public static function validateName(?string $name): void { if (!self::isValidName($name)) { - throw new InvalidArgumentException(sprintf('The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").', $name)); + throw new InvalidArgumentException(\sprintf('The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").', $name)); } } diff --git a/FormErrorIterator.php b/FormErrorIterator.php index a614e72c2..4f4a2574d 100644 --- a/FormErrorIterator.php +++ b/FormErrorIterator.php @@ -58,7 +58,7 @@ public function __construct( ) { foreach ($errors as $error) { if (!($error instanceof FormError || $error instanceof self)) { - throw new InvalidArgumentException(sprintf('The errors must be instances of "Symfony\Component\Form\FormError" or "%s". Got: "%s".', __CLASS__, get_debug_type($error))); + throw new InvalidArgumentException(\sprintf('The errors must be instances of "Symfony\Component\Form\FormError" or "%s". Got: "%s".', __CLASS__, get_debug_type($error))); } } @@ -198,7 +198,7 @@ public function hasChildren(): bool public function getChildren(): self { if (!$this->hasChildren()) { - throw new LogicException(sprintf('The current element is not iterable. Use "%s" to get the current element.', self::class.'::current()')); + throw new LogicException(\sprintf('The current element is not iterable. Use "%s" to get the current element.', self::class.'::current()')); } /** @var self $children */ diff --git a/FormRegistry.php b/FormRegistry.php index 95a007737..ecf654a2a 100644 --- a/FormRegistry.php +++ b/FormRegistry.php @@ -69,10 +69,10 @@ public function getType(string $name): ResolvedFormTypeInterface if (!$type) { // Support fully-qualified class names if (!class_exists($name)) { - throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('Could not load type "%s": class does not exist.', $name)); } if (!is_subclass_of($name, FormTypeInterface::class)) { - throw new InvalidArgumentException(sprintf('Could not load type "%s": class does not implement "Symfony\Component\Form\FormTypeInterface".', $name)); + throw new InvalidArgumentException(\sprintf('Could not load type "%s": class does not implement "Symfony\Component\Form\FormTypeInterface".', $name)); } $type = new $name(); @@ -94,7 +94,7 @@ private function resolveType(FormTypeInterface $type): ResolvedFormTypeInterface if (isset($this->checkedTypes[$fqcn])) { $types = implode(' > ', array_merge(array_keys($this->checkedTypes), [$fqcn])); - throw new LogicException(sprintf('Circular reference detected for form type "%s" (%s).', $fqcn, $types)); + throw new LogicException(\sprintf('Circular reference detected for form type "%s" (%s).', $fqcn, $types)); } $this->checkedTypes[$fqcn] = true; diff --git a/FormRenderer.php b/FormRenderer.php index a9ffd4f41..4478432b2 100644 --- a/FormRenderer.php +++ b/FormRenderer.php @@ -59,7 +59,7 @@ public function renderBlock(FormView $view, string $blockName, array $variables $resource = $this->engine->getResourceForBlockName($view, $blockName); if (!$resource) { - throw new LogicException(sprintf('No block "%s" found while rendering the form.', $blockName)); + throw new LogicException(\sprintf('No block "%s" found while rendering the form.', $blockName)); } $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; @@ -116,7 +116,7 @@ public function searchAndRenderBlock(FormView $view, string $blockNameSuffix, ar if ($renderOnlyOnce && $view->isRendered()) { // This is not allowed, because it would result in rendering same IDs multiple times, which is not valid. - throw new BadMethodCallException(sprintf('Field "%s" has already been rendered, save the result of previous render call to a variable and output that instead.', $view->vars['name'])); + throw new BadMethodCallException(\sprintf('Field "%s" has already been rendered, save the result of previous render call to a variable and output that instead.', $view->vars['name'])); } // The cache key for storing the variables and types @@ -203,10 +203,10 @@ public function searchAndRenderBlock(FormView $view, string $blockNameSuffix, ar // Escape if no resource exists for this block if (!$resource) { if (\count($blockNameHierarchy) !== \count(array_unique($blockNameHierarchy))) { - throw new LogicException(sprintf('Unable to render the form because the block names array contains duplicates: "%s".', implode('", "', array_reverse($blockNameHierarchy)))); + throw new LogicException(\sprintf('Unable to render the form because the block names array contains duplicates: "%s".', implode('", "', array_reverse($blockNameHierarchy)))); } - throw new LogicException(sprintf('Unable to render the form as none of the following blocks exist: "%s".', implode('", "', array_reverse($blockNameHierarchy)))); + throw new LogicException(\sprintf('Unable to render the form as none of the following blocks exist: "%s".', implode('", "', array_reverse($blockNameHierarchy)))); } // Merge the passed with the existing attributes diff --git a/PreloadedExtension.php b/PreloadedExtension.php index 58d8f13b1..26090e00d 100644 --- a/PreloadedExtension.php +++ b/PreloadedExtension.php @@ -41,7 +41,7 @@ public function __construct( public function getType(string $name): FormTypeInterface { if (!isset($this->types[$name])) { - throw new InvalidArgumentException(sprintf('The type "%s" cannot be loaded by this extension.', $name)); + throw new InvalidArgumentException(\sprintf('The type "%s" cannot be loaded by this extension.', $name)); } return $this->types[$name]; diff --git a/ResolvedFormType.php b/ResolvedFormType.php index 964619c39..82065f651 100644 --- a/ResolvedFormType.php +++ b/ResolvedFormType.php @@ -72,7 +72,7 @@ public function createBuilder(FormFactoryInterface $factory, string $name, array try { $options = $this->getOptionsResolver()->resolve($options); } catch (ExceptionInterface $e) { - throw new $e(sprintf('An error has occurred resolving the options of the form "%s": ', get_debug_type($this->getInnerType())).$e->getMessage(), $e->getCode(), $e); + throw new $e(\sprintf('An error has occurred resolving the options of the form "%s": ', get_debug_type($this->getInnerType())).$e->getMessage(), $e->getCode(), $e); } // Should be decoupled from the specific option at some point diff --git a/Test/FormPerformanceTestCase.php b/Test/FormPerformanceTestCase.php index 54ccc67cf..1d631a2fd 100644 --- a/Test/FormPerformanceTestCase.php +++ b/Test/FormPerformanceTestCase.php @@ -34,7 +34,7 @@ protected function runTest(): mixed $time = microtime(true) - $s; if (0 != $this->maxRunningTime && $time > $this->maxRunningTime) { - $this->fail(sprintf('expected running time: <= %s but was: %s', $this->maxRunningTime, $time)); + $this->fail(\sprintf('expected running time: <= %s but was: %s', $this->maxRunningTime, $time)); } return $result; diff --git a/Test/Traits/ValidatorExtensionTrait.php b/Test/Traits/ValidatorExtensionTrait.php index b89095de7..5d0486e8c 100644 --- a/Test/Traits/ValidatorExtensionTrait.php +++ b/Test/Traits/ValidatorExtensionTrait.php @@ -28,7 +28,7 @@ protected function getValidatorExtension(): ValidatorExtension } if (!$this instanceof TypeTestCase) { - throw new \Exception(sprintf('The trait "ValidatorExtensionTrait" can only be added to a class that extends "%s".', TypeTestCase::class)); + throw new \Exception(\sprintf('The trait "ValidatorExtensionTrait" can only be added to a class that extends "%s".', TypeTestCase::class)); } $this->validator = $this->createMock(ValidatorInterface::class); diff --git a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php index fa8745b58..8a99c205c 100644 --- a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php +++ b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php @@ -153,7 +153,7 @@ private function getExpectedDescription($name) private function getFixtureFilename($name) { - return sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat()); + return \sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat()); } } diff --git a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php index 26055c203..5d1e77eec 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php @@ -31,7 +31,7 @@ protected function getExtensions() { $translator = $this->createMock(TranslatorInterface::class); $translator->expects($this->any())->method('trans') - ->willReturnCallback(fn ($key, $params) => strtr(sprintf('Translation of: %s', $key), $params) + ->willReturnCallback(fn ($key, $params) => strtr(\sprintf('Translation of: %s', $key), $params) ); return array_merge(parent::getExtensions(), [new CoreExtension(null, null, $translator)]); diff --git a/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php index b99240c8c..8f2cbdcd5 100644 --- a/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php +++ b/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php @@ -44,7 +44,7 @@ public function testGetTypeExtensions() public function testThrowExceptionForInvalidExtendedType() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(sprintf('The extended type "unmatched" specified for the type extension class "%s" does not match any of the actual extended types (["test"]).', TestTypeExtension::class)); + $this->expectExceptionMessage(\sprintf('The extended type "unmatched" specified for the type extension class "%s" does not match any of the actual extended types (["test"]).', TestTypeExtension::class)); $extensions = [ 'unmatched' => new \ArrayIterator([new TestTypeExtension()]), diff --git a/Tests/ResolvedFormTypeTest.php b/Tests/ResolvedFormTypeTest.php index ba0bf243d..fa28f7547 100644 --- a/Tests/ResolvedFormTypeTest.php +++ b/Tests/ResolvedFormTypeTest.php @@ -100,7 +100,7 @@ public function testCreateBuilderWithDataClassOption() public function testFailsCreateBuilderOnInvalidFormOptionsResolution() { $this->expectException(MissingOptionsException::class); - $this->expectExceptionMessage(sprintf('An error has occurred resolving the options of the form "%s": The required option "foo" is missing.', UsageTrackingFormType::class)); + $this->expectExceptionMessage(\sprintf('An error has occurred resolving the options of the form "%s": The required option "foo" is missing.', UsageTrackingFormType::class)); $this->resolvedType->createBuilder($this->formFactory, 'name'); } diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index 3b2fe40f4..157335dc6 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -26,7 +26,7 @@ public function testTranslationFileIsValid($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } /** @@ -39,7 +39,7 @@ public function testTranslationFileIsValidWithoutEntityLoader($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public static function provideTranslationFiles() diff --git a/Tests/VersionAwareTest.php b/Tests/VersionAwareTest.php index 1a35b72fc..90e0d10af 100644 --- a/Tests/VersionAwareTest.php +++ b/Tests/VersionAwareTest.php @@ -18,7 +18,7 @@ trait VersionAwareTest protected function requiresFeatureSet(int $requiredFeatureSetVersion) { if ($requiredFeatureSetVersion > static::$supportedFeatureSetVersion) { - $this->markTestSkipped(sprintf('Test requires features from symfony/form %.2f but only version %.2f is supported.', $requiredFeatureSetVersion / 100, static::$supportedFeatureSetVersion / 100)); + $this->markTestSkipped(\sprintf('Test requires features from symfony/form %.2f but only version %.2f is supported.', $requiredFeatureSetVersion / 100, static::$supportedFeatureSetVersion / 100)); } } } diff --git a/Util/OrderedHashMap.php b/Util/OrderedHashMap.php index 145bd2e25..7f81c0739 100644 --- a/Util/OrderedHashMap.php +++ b/Util/OrderedHashMap.php @@ -105,7 +105,7 @@ public function offsetExists(mixed $key): bool public function offsetGet(mixed $key): mixed { if (!isset($this->elements[$key])) { - throw new \OutOfBoundsException(sprintf('The offset "%s" does not exist.', $key)); + throw new \OutOfBoundsException(\sprintf('The offset "%s" does not exist.', $key)); } return $this->elements[$key]; From d131e439bb20f5c3d39cfde0b0d8eedf7c7a9fa5 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 16 Jun 2024 17:17:26 +0200 Subject: [PATCH 02/41] chore: CS fixes --- AbstractTypeExtension.php | 3 -- FormTypeExtensionInterface.php | 3 -- .../Factory/DefaultChoiceListFactoryTest.php | 10 +++---- .../Loader/CallbackChoiceLoaderTest.php | 4 +-- Tests/Extension/Core/Type/ChoiceTypeTest.php | 4 +-- Tests/Extension/Core/Type/FormTypeTest.php | 18 +++++------ .../DataCollector/FormDataCollectorTest.php | 14 ++++----- .../FormValidatorFunctionalTest.php | 6 ++-- .../Constraints/FormValidatorTest.php | 30 +++++++++---------- Tests/NativeRequestHandlerTest.php | 4 +-- Tests/SimpleFormTest.php | 6 ++-- Tests/Util/StringUtilTest.php | 2 +- 12 files changed, 49 insertions(+), 55 deletions(-) diff --git a/AbstractTypeExtension.php b/AbstractTypeExtension.php index 9f6da0a33..b32f3b522 100644 --- a/AbstractTypeExtension.php +++ b/AbstractTypeExtension.php @@ -18,9 +18,6 @@ */ abstract class AbstractTypeExtension implements FormTypeExtensionInterface { - /** - * @return void - */ public function configureOptions(OptionsResolver $resolver): void { } diff --git a/FormTypeExtensionInterface.php b/FormTypeExtensionInterface.php index 3e2d2d03e..838406d2a 100644 --- a/FormTypeExtensionInterface.php +++ b/FormTypeExtensionInterface.php @@ -25,9 +25,6 @@ interface FormTypeExtensionInterface */ public static function getExtendedTypes(): iterable; - /** - * @return void - */ public function configureOptions(OptionsResolver $resolver): void; /** diff --git a/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index e7bf26d17..f82fdc2f9 100644 --- a/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -159,9 +159,9 @@ public function testCreateFromChoicesGroupedTraversable() { $list = $this->factory->createListFromChoices( new \ArrayIterator([ - 'Group 1' => ['A' => $this->obj1, 'B' => $this->obj2], - 'Group 2' => ['C' => $this->obj3, 'D' => $this->obj4], - ]) + 'Group 1' => ['A' => $this->obj1, 'B' => $this->obj2], + 'Group 2' => ['C' => $this->obj3, 'D' => $this->obj4], + ]) ); $this->assertObjectListWithGeneratedValues($list); @@ -941,7 +941,7 @@ private function assertFlatViewWithAttr($view) 'C', ['attr2' => 'value2'] ), - ] + ] ), $view); } @@ -987,7 +987,7 @@ private function assertGroupedView($view) 'Group 2', [2 => new ChoiceView($this->obj3, '2', 'C')] ), - ] + ] ), $view); } diff --git a/Tests/ChoiceList/Loader/CallbackChoiceLoaderTest.php b/Tests/ChoiceList/Loader/CallbackChoiceLoaderTest.php index d394196ee..791a6f006 100644 --- a/Tests/ChoiceList/Loader/CallbackChoiceLoaderTest.php +++ b/Tests/ChoiceList/Loader/CallbackChoiceLoaderTest.php @@ -69,8 +69,8 @@ public function testLoadChoicesForValuesLoadsChoiceListOnFirstCall() public function testLoadValuesForChoicesCastsCallbackItemsToString() { $choices = [ - (object) ['id' => 2], - (object) ['id' => 3], + (object) ['id' => 2], + (object) ['id' => 3], ]; $value = fn ($item) => $item->id; diff --git a/Tests/Extension/Core/Type/ChoiceTypeTest.php b/Tests/Extension/Core/Type/ChoiceTypeTest.php index 8e2372d7e..c4f18734f 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1895,8 +1895,8 @@ public function testInitializeWithEmptyChoices() { $this->assertInstanceOf( FormInterface::class, $this->factory->createNamed('name', static::TESTED_TYPE, null, [ - 'choices' => [], - ])); + 'choices' => [], + ])); } public function testInitializeWithDefaultObjectChoice() diff --git a/Tests/Extension/Core/Type/FormTypeTest.php b/Tests/Extension/Core/Type/FormTypeTest.php index be89c559f..fe838885e 100644 --- a/Tests/Extension/Core/Type/FormTypeTest.php +++ b/Tests/Extension/Core/Type/FormTypeTest.php @@ -156,24 +156,24 @@ public function testDataClassMayBeNull() { $this->assertInstanceOf( FormBuilderInterface::class, $this->factory->createBuilder(static::TESTED_TYPE, null, [ - 'data_class' => null, - ])); + 'data_class' => null, + ])); } public function testDataClassMayBeAbstractClass() { $this->assertInstanceOf( FormBuilderInterface::class, $this->factory->createBuilder(static::TESTED_TYPE, null, [ - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AbstractAuthor', - ])); + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AbstractAuthor', + ])); } public function testDataClassMayBeInterface() { $this->assertInstanceOf( FormBuilderInterface::class, $this->factory->createBuilder(static::TESTED_TYPE, null, [ - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AuthorInterface', - ])); + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AuthorInterface', + ])); } public function testDataClassMustBeValidClassOrInterface() @@ -402,7 +402,7 @@ public function testSubformCallsSettersIfTheObjectChanged() // referenceCopy has a getter that returns a copy 'referenceCopy' => [ 'firstName' => 'Foo', - ], + ], ]); $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); @@ -680,8 +680,8 @@ public function testDataMapperTransformationFailedExceptionInvalidMessageIsUsed( public function testPassZeroLabelToView() { $view = $this->factory->create(static::TESTED_TYPE, null, [ - 'label' => '0', - ]) + 'label' => '0', + ]) ->createView(); $this->assertSame('0', $view->vars['label']); diff --git a/Tests/Extension/DataCollector/FormDataCollectorTest.php b/Tests/Extension/DataCollector/FormDataCollectorTest.php index 798faa0c5..0517b26c3 100644 --- a/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -71,7 +71,7 @@ public function testBuildPreliminaryFormTree() ], 'errors' => [], 'children' => [], - ]; + ]; $formData = [ 'id' => 'name', @@ -87,11 +87,11 @@ public function testBuildPreliminaryFormTree() 'norm' => null, ], 'errors' => [], - 'has_children_error' => false, - 'children' => [ - 'child' => $childFormData, - ], - ]; + 'has_children_error' => false, + 'children' => [ + 'child' => $childFormData, + ], + ]; $this->assertEquals([ 'forms' => [ @@ -102,7 +102,7 @@ public function testBuildPreliminaryFormTree() spl_object_hash($this->childForm) => $childFormData, ], 'nb_errors' => 0, - ], $this->dataCollector->getData()); + ], $this->dataCollector->getData()); } public function testBuildMultiplePreliminaryFormTrees() diff --git a/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php b/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php index aa6056c13..14595e8cf 100644 --- a/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php +++ b/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php @@ -88,9 +88,9 @@ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted() public function testNonCompositeConstraintValidatedOnce() { $form = $this->formFactory->create(TextType::class, null, [ - 'constraints' => [new NotBlank(['groups' => ['foo', 'bar']])], - 'validation_groups' => ['foo', 'bar'], - ]); + 'constraints' => [new NotBlank(['groups' => ['foo', 'bar']])], + 'validation_groups' => ['foo', 'bar'], + ]); $form->submit(''); $violations = $this->validator->validate($form); diff --git a/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 4e1588a9c..86b53ac3a 100644 --- a/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -184,8 +184,8 @@ public function testDontValidateIfNoValidationGroups() $object = new \stdClass(); $form = $this->getBuilder('name', '\stdClass', [ - 'validation_groups' => [], - ]) + 'validation_groups' => [], + ]) ->setData($object) ->setCompound(true) ->setDataMapper(new DataMapper()) @@ -256,12 +256,12 @@ public function testDontValidateIfNotSynchronized() $object = new \stdClass(); $form = $this->getBuilder('name', '\stdClass', [ - 'invalid_message' => 'invalid_message_key', - // Invalid message parameters must be supported, because the - // invalid message can be a translation key - // see https://github.com/symfony/symfony/issues/5144 - 'invalid_message_parameters' => ['{{ foo }}' => 'bar'], - ]) + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => ['{{ foo }}' => 'bar'], + ]) ->setData($object) ->addViewTransformer(new CallbackTransformer( static fn ($data) => $data, @@ -292,13 +292,13 @@ public function testAddInvalidErrorEvenIfNoValidationGroups() $object = new \stdClass(); $form = $this->getBuilder('name', '\stdClass', [ - 'invalid_message' => 'invalid_message_key', - // Invalid message parameters must be supported, because the - // invalid message can be a translation key - // see https://github.com/symfony/symfony/issues/5144 - 'invalid_message_parameters' => ['{{ foo }}' => 'bar'], - 'validation_groups' => [], - ]) + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => ['{{ foo }}' => 'bar'], + 'validation_groups' => [], + ]) ->setData($object) ->addViewTransformer(new CallbackTransformer( static fn ($data) => $data, diff --git a/Tests/NativeRequestHandlerTest.php b/Tests/NativeRequestHandlerTest.php index 679c3366d..3770e1219 100644 --- a/Tests/NativeRequestHandlerTest.php +++ b/Tests/NativeRequestHandlerTest.php @@ -172,8 +172,8 @@ public function testMethodOverrideHeaderIgnoredIfNotPost() $form = $this->createForm('param1', 'POST'); $this->setRequestData('GET', [ - 'param1' => 'DATA', - ]); + 'param1' => 'DATA', + ]); $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; diff --git a/Tests/SimpleFormTest.php b/Tests/SimpleFormTest.php index 7ded9b853..d5d3549d2 100644 --- a/Tests/SimpleFormTest.php +++ b/Tests/SimpleFormTest.php @@ -503,9 +503,9 @@ public function testSetDataConvertsScalarToStringIfOnlyModelTransformer() { $form = $this->getBuilder() ->addModelTransformer(new FixedDataTransformer([ - '' => '', - 1 => 23, - ])) + '' => '', + 1 => 23, + ])) ->getForm(); $form->setData(1); diff --git a/Tests/Util/StringUtilTest.php b/Tests/Util/StringUtilTest.php index 8199d6843..d51481f6c 100644 --- a/Tests/Util/StringUtilTest.php +++ b/Tests/Util/StringUtilTest.php @@ -56,7 +56,7 @@ public static function spaceProvider(): array ['0020'], ['00A0'], ['1680'], -// ['180E'], + // ['180E'], ['2000'], ['2001'], ['2002'], From 717223d83e3a1df104d0fe989b6f43c15c8e44b9 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 25 Jun 2024 14:58:00 +0200 Subject: [PATCH 03/41] Add more precise types in reusable test cases --- Test/FormIntegrationTestCase.php | 16 ++++++++++++++++ Test/TypeTestCase.php | 10 ++++++++++ .../Core/Type/ChoiceTypeTranslationTest.php | 2 +- Tests/Extension/Core/Type/FileTypeTest.php | 2 +- .../Csrf/Type/FormTypeCsrfExtensionTest.php | 2 +- .../Type/TextTypeHtmlSanitizerExtensionTest.php | 2 +- .../PasswordTypePasswordHasherExtensionTest.php | 2 +- .../Constraints/FormValidatorPerformanceTest.php | 2 +- 8 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Test/FormIntegrationTestCase.php b/Test/FormIntegrationTestCase.php index 5bf37fd48..8756d9968 100644 --- a/Test/FormIntegrationTestCase.php +++ b/Test/FormIntegrationTestCase.php @@ -12,8 +12,12 @@ namespace Symfony\Component\Form\Test; use PHPUnit\Framework\TestCase; +use Symfony\Component\Form\FormExtensionInterface; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\Forms; +use Symfony\Component\Form\FormTypeExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormTypeInterface; /** * @author Bernhard Schussek @@ -32,21 +36,33 @@ protected function setUp(): void ->getFormFactory(); } + /** + * @return FormExtensionInterface[] + */ protected function getExtensions() { return []; } + /** + * @return FormTypeExtensionInterface[] + */ protected function getTypeExtensions() { return []; } + /** + * @return FormTypeInterface[] + */ protected function getTypes() { return []; } + /** + * @return FormTypeGuesserInterface[] + */ protected function getTypeGuessers() { return []; diff --git a/Test/TypeTestCase.php b/Test/TypeTestCase.php index 960b44228..1bbb66d25 100644 --- a/Test/TypeTestCase.php +++ b/Test/TypeTestCase.php @@ -13,6 +13,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormExtensionInterface; use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait; abstract class TypeTestCase extends FormIntegrationTestCase @@ -28,6 +29,9 @@ protected function setUp(): void $this->builder = new FormBuilder('', null, $this->dispatcher, $this->factory); } + /** + * @return FormExtensionInterface[] + */ protected function getExtensions() { $extensions = []; @@ -39,11 +43,17 @@ protected function getExtensions() return $extensions; } + /** + * @return void + */ public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual) { self::assertEquals($expected->format('c'), $actual->format('c')); } + /** + * @return void + */ public static function assertDateIntervalEquals(\DateInterval $expected, \DateInterval $actual) { self::assertEquals($expected->format('%RP%yY%mM%dDT%hH%iM%sS'), $actual->format('%RP%yY%mM%dDT%hH%iM%sS')); diff --git a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php index 5d1e77eec..af58e2ecf 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php @@ -27,7 +27,7 @@ class ChoiceTypeTranslationTest extends TypeTestCase 'Roman' => 'e', ]; - protected function getExtensions() + protected function getExtensions(): array { $translator = $this->createMock(TranslatorInterface::class); $translator->expects($this->any())->method('trans') diff --git a/Tests/Extension/Core/Type/FileTypeTest.php b/Tests/Extension/Core/Type/FileTypeTest.php index b7f3332c1..9c3a8efd0 100644 --- a/Tests/Extension/Core/Type/FileTypeTest.php +++ b/Tests/Extension/Core/Type/FileTypeTest.php @@ -23,7 +23,7 @@ class FileTypeTest extends BaseTypeTestCase { public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\FileType'; - protected function getExtensions() + protected function getExtensions(): array { return array_merge(parent::getExtensions(), [new CoreExtension(null, null, new IdentityTranslator())]); } diff --git a/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php index bfa302555..d5bce6527 100644 --- a/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php +++ b/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -42,7 +42,7 @@ protected function setUp(): void parent::setUp(); } - protected function getExtensions() + protected function getExtensions(): array { return array_merge(parent::getExtensions(), [ new CsrfExtension($this->tokenManager, new IdentityTranslator()), diff --git a/Tests/Extension/HtmlSanitizer/Type/TextTypeHtmlSanitizerExtensionTest.php b/Tests/Extension/HtmlSanitizer/Type/TextTypeHtmlSanitizerExtensionTest.php index 39b8d0332..6784576b0 100644 --- a/Tests/Extension/HtmlSanitizer/Type/TextTypeHtmlSanitizerExtensionTest.php +++ b/Tests/Extension/HtmlSanitizer/Type/TextTypeHtmlSanitizerExtensionTest.php @@ -20,7 +20,7 @@ class TextTypeHtmlSanitizerExtensionTest extends TypeTestCase { - protected function getExtensions() + protected function getExtensions(): array { $fooSanitizer = $this->createMock(HtmlSanitizerInterface::class); $fooSanitizer->expects($this->once()) diff --git a/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php b/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php index 4ec91c827..07d1292a3 100644 --- a/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php +++ b/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php @@ -40,7 +40,7 @@ protected function setUp(): void parent::setUp(); } - protected function getExtensions() + protected function getExtensions(): array { return array_merge(parent::getExtensions(), [ new PasswordHasherExtension(new PasswordHasherListener($this->passwordHasher)), diff --git a/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php b/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php index e8bfbc64a..b0c7d719a 100644 --- a/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php +++ b/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php @@ -20,7 +20,7 @@ */ class FormValidatorPerformanceTest extends FormPerformanceTestCase { - protected function getExtensions() + protected function getExtensions(): array { return [ new ValidatorExtension(Validation::createValidator(), false), From 8c1030ce9365f4e2885c55149991aae266159347 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 25 Jun 2024 08:08:32 +0200 Subject: [PATCH 04/41] Unify how --format is handle by commands --- Command/DebugCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index 18d208cbe..91db6f1a9 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -272,6 +272,7 @@ private function completeOptions(string $class, CompletionSuggestions $suggestio $suggestions->suggestValues($resolvedType->getOptionsResolver()->getDefinedOptions()); } + /** @return string[] */ private function getAvailableFormatOptions(): array { return (new DescriptorHelper())->getFormats(); From a1664a2508e19556bf20e2c5a6e0fe8483eec4c9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 05/41] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add05..14c3c3594 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore From f63434d11b165831a089e0b90f74582184ec9679 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 18 Jul 2024 10:20:59 +0200 Subject: [PATCH 06/41] [Validator] Use CPP in `ConstraintViolation` --- Tests/Extension/DataCollector/FormDataExtractorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Extension/DataCollector/FormDataExtractorTest.php b/Tests/Extension/DataCollector/FormDataExtractorTest.php index ec01721c7..4aa9cf4d2 100644 --- a/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -314,12 +314,12 @@ public function testExtractSubmittedDataStoresErrorCause() -message: "Foo" -messageTemplate: "Foo" -parameters: [] - -plural: null -root: "Root" -propertyPath: "property.path" -invalidValue: "Invalid!" - -constraint: null + -plural: null -code: null + -constraint: null -cause: Exception {%A} } 1 => Exception {#1} From 2b2fc826310d853594889c3c4ab9cd619170c932 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sat, 13 Apr 2024 14:18:00 +0200 Subject: [PATCH 07/41] [PhpUnitBridge] Add ExpectUserDeprecationMessageTrait --- Tests/Extension/Core/Type/UrlTypeTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Extension/Core/Type/UrlTypeTest.php b/Tests/Extension/Core/Type/UrlTypeTest.php index 28e8b9ac7..243d15297 100644 --- a/Tests/Extension/Core/Type/UrlTypeTest.php +++ b/Tests/Extension/Core/Type/UrlTypeTest.php @@ -11,12 +11,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class UrlTypeTest extends TextTypeTest { - use ExpectDeprecationTrait; + use ExpectUserDeprecationMessageTrait; public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\UrlType'; @@ -25,7 +25,7 @@ class UrlTypeTest extends TextTypeTest */ public function testSubmitAddsDefaultProtocolIfNoneIsIncluded() { - $this->expectDeprecation('Since symfony/form 7.1: Not configuring the "default_protocol" option when using the UrlType is deprecated. It will default to "null" in 8.0.'); + $this->expectUserDeprecationMessage('Since symfony/form 7.1: Not configuring the "default_protocol" option when using the UrlType is deprecated. It will default to "null" in 8.0.'); $form = $this->factory->create(static::TESTED_TYPE, 'name'); $form->submit('www.domain.com'); From 56575c475e10066cfb3c580a2e911b8d4df92efe Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 22 Jul 2024 10:27:43 +0200 Subject: [PATCH 08/41] Use CPP where possible --- ChoiceList/Factory/CachingFactoryDecorator.php | 8 +++----- ChoiceList/Factory/PropertyAccessDecorator.php | 8 ++++---- .../DataTransformer/BooleanToStringTransformer.php | 12 ++++-------- Extension/Core/Type/ChoiceType.php | 8 ++++---- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ChoiceList/Factory/CachingFactoryDecorator.php b/ChoiceList/Factory/CachingFactoryDecorator.php index 687fcec1e..1f373228b 100644 --- a/ChoiceList/Factory/CachingFactoryDecorator.php +++ b/ChoiceList/Factory/CachingFactoryDecorator.php @@ -27,8 +27,6 @@ */ class CachingFactoryDecorator implements ChoiceListFactoryInterface, ResetInterface { - private ChoiceListFactoryInterface $decoratedFactory; - /** * @var ChoiceListInterface[] */ @@ -64,9 +62,9 @@ public static function generateHash(mixed $value, string $namespace = ''): strin return hash('sha256', $namespace.':'.serialize($value)); } - public function __construct(ChoiceListFactoryInterface $decoratedFactory) - { - $this->decoratedFactory = $decoratedFactory; + public function __construct( + private ChoiceListFactoryInterface $decoratedFactory, + ) { } /** diff --git a/ChoiceList/Factory/PropertyAccessDecorator.php b/ChoiceList/Factory/PropertyAccessDecorator.php index c83ef17e9..f73a8fc2a 100644 --- a/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/ChoiceList/Factory/PropertyAccessDecorator.php @@ -38,12 +38,12 @@ */ class PropertyAccessDecorator implements ChoiceListFactoryInterface { - private ChoiceListFactoryInterface $decoratedFactory; private PropertyAccessorInterface $propertyAccessor; - public function __construct(ChoiceListFactoryInterface $decoratedFactory, ?PropertyAccessorInterface $propertyAccessor = null) - { - $this->decoratedFactory = $decoratedFactory; + public function __construct( + private ChoiceListFactoryInterface $decoratedFactory, + ?PropertyAccessorInterface $propertyAccessor = null, + ) { $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); } diff --git a/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/Extension/Core/DataTransformer/BooleanToStringTransformer.php index e91bdb4db..7ef84bb44 100644 --- a/Extension/Core/DataTransformer/BooleanToStringTransformer.php +++ b/Extension/Core/DataTransformer/BooleanToStringTransformer.php @@ -25,17 +25,13 @@ */ class BooleanToStringTransformer implements DataTransformerInterface { - private string $trueValue; - - private array $falseValues; - /** * @param string $trueValue The value emitted upon transform if the input is true */ - public function __construct(string $trueValue, array $falseValues = [null]) - { - $this->trueValue = $trueValue; - $this->falseValues = $falseValues; + public function __construct( + private string $trueValue, + private array $falseValues = [null], + ) { if (\in_array($this->trueValue, $this->falseValues, true)) { throw new InvalidArgumentException('The specified "true" value is contained in the false-values.'); } diff --git a/Extension/Core/Type/ChoiceType.php b/Extension/Core/Type/ChoiceType.php index 36ebe6c84..0042d7580 100644 --- a/Extension/Core/Type/ChoiceType.php +++ b/Extension/Core/Type/ChoiceType.php @@ -51,16 +51,16 @@ class ChoiceType extends AbstractType { private ChoiceListFactoryInterface $choiceListFactory; - private ?TranslatorInterface $translator; - public function __construct(?ChoiceListFactoryInterface $choiceListFactory = null, ?TranslatorInterface $translator = null) - { + public function __construct( + ?ChoiceListFactoryInterface $choiceListFactory = null, + private ?TranslatorInterface $translator = null, + ) { $this->choiceListFactory = $choiceListFactory ?? new CachingFactoryDecorator( new PropertyAccessDecorator( new DefaultChoiceListFactory() ) ); - $this->translator = $translator; } public function buildForm(FormBuilderInterface $builder, array $options): void From 7b9c1292dbcd0f47e741fc4bb7c6e95a930458d5 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Fri, 26 Jul 2024 13:12:13 +0200 Subject: [PATCH 09/41] [Form] Deprecate VersionAwareTest trait --- CHANGELOG.md | 5 +++++ Tests/Extension/Core/Type/BaseTypeTestCase.php | 16 ---------------- Tests/VersionAwareTest.php | 8 ++++++++ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0420af341..631041da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Deprecate the `VersionAwareTest` trait, use feature detection instead + 7.1 --- diff --git a/Tests/Extension/Core/Type/BaseTypeTestCase.php b/Tests/Extension/Core/Type/BaseTypeTestCase.php index 5238e2fd8..14b602a4b 100644 --- a/Tests/Extension/Core/Type/BaseTypeTestCase.php +++ b/Tests/Extension/Core/Type/BaseTypeTestCase.php @@ -114,8 +114,6 @@ public function testDefaultTranslationDomain() public function testPassLabelTranslationParametersToView() { - $this->requiresFeatureSet(403); - $view = $this->factory->create($this->getTestedType(), null, array_merge($this->getTestOptions(), [ 'label_translation_parameters' => ['%param%' => 'value'], ])) @@ -126,8 +124,6 @@ public function testPassLabelTranslationParametersToView() public function testPassAttrTranslationParametersToView() { - $this->requiresFeatureSet(403); - $view = $this->factory->create($this->getTestedType(), null, array_merge($this->getTestOptions(), [ 'attr_translation_parameters' => ['%param%' => 'value'], ])) @@ -138,8 +134,6 @@ public function testPassAttrTranslationParametersToView() public function testInheritLabelTranslationParametersFromParent() { - $this->requiresFeatureSet(403); - $view = $this->factory ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'label_translation_parameters' => ['%param%' => 'value'], @@ -153,8 +147,6 @@ public function testInheritLabelTranslationParametersFromParent() public function testInheritAttrTranslationParametersFromParent() { - $this->requiresFeatureSet(403); - $view = $this->factory ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'attr_translation_parameters' => ['%param%' => 'value'], @@ -168,8 +160,6 @@ public function testInheritAttrTranslationParametersFromParent() public function testPreferOwnLabelTranslationParameters() { - $this->requiresFeatureSet(403); - $view = $this->factory ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'label_translation_parameters' => ['%parent_param%' => 'parent_value', '%override_param%' => 'parent_override_value'], @@ -185,8 +175,6 @@ public function testPreferOwnLabelTranslationParameters() public function testPreferOwnAttrTranslationParameters() { - $this->requiresFeatureSet(403); - $view = $this->factory ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'attr_translation_parameters' => ['%parent_param%' => 'parent_value', '%override_param%' => 'parent_override_value'], @@ -202,8 +190,6 @@ public function testPreferOwnAttrTranslationParameters() public function testDefaultLabelTranslationParameters() { - $this->requiresFeatureSet(403); - $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) ->add('child', $this->getTestedType(), $this->getTestOptions()) ->getForm() @@ -214,8 +200,6 @@ public function testDefaultLabelTranslationParameters() public function testDefaultAttrTranslationParameters() { - $this->requiresFeatureSet(403); - $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) ->add('child', $this->getTestedType(), $this->getTestOptions()) ->getForm() diff --git a/Tests/VersionAwareTest.php b/Tests/VersionAwareTest.php index 90e0d10af..530c17473 100644 --- a/Tests/VersionAwareTest.php +++ b/Tests/VersionAwareTest.php @@ -11,12 +11,20 @@ namespace Symfony\Component\Form\Tests; +/** + * @deprecated since Symfony 7.2, use feature detection instead. + */ trait VersionAwareTest { protected static int $supportedFeatureSetVersion = 404; + /** + * @deprecated since Symfony 7.2, use feature detection instead. + */ protected function requiresFeatureSet(int $requiredFeatureSetVersion) { + trigger_deprecation('symfony/form', '7.2', 'The "%s" trait is deprecated, use feature detection instead.', VersionAwareTest::class); + if ($requiredFeatureSetVersion > static::$supportedFeatureSetVersion) { $this->markTestSkipped(\sprintf('Test requires features from symfony/form %.2f but only version %.2f is supported.', $requiredFeatureSetVersion / 100, static::$supportedFeatureSetVersion / 100)); } From b2a3f779d67f476600f6e45524fb9d0e03bf5aac Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 31 Jul 2024 16:13:26 +0200 Subject: [PATCH 10/41] Remove unused code and unnecessary `else` branches --- .../Core/DataTransformer/DateIntervalToArrayTransformer.php | 3 +-- .../DataTransformer/NumberToLocalizedStringTransformer.php | 4 +--- .../DataTransformer/PercentToLocalizedStringTransformer.php | 4 +--- Extension/Core/Type/ChoiceType.php | 4 ++-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php index 0475b55bf..18679a910 100644 --- a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php +++ b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php @@ -93,9 +93,8 @@ public function transform(mixed $dateInterval): array } } $result['invert'] = '-' === $result['invert']; - $result = array_intersect_key($result, array_flip($this->fields)); - return $result; + return array_intersect_key($result, array_flip($this->fields)); } /** diff --git a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 2a6d146f9..dc108de79 100644 --- a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -64,9 +64,7 @@ public function transform(mixed $value): string } // Convert non-breaking and narrow non-breaking spaces to normal ones - $value = str_replace(["\xc2\xa0", "\xe2\x80\xaf"], ' ', $value); - - return $value; + return str_replace(["\xc2\xa0", "\xe2\x80\xaf"], ' ', $value); } /** diff --git a/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php index 8ae5e4c96..3b103b49b 100644 --- a/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php @@ -210,8 +210,6 @@ private function round(int|float $number): int|float \NumberFormatter::ROUND_HALFDOWN => round($number, 0, \PHP_ROUND_HALF_DOWN), }; - $number = 1 === $roundingCoef ? (int) $number : $number / $roundingCoef; - - return $number; + return 1 === $roundingCoef ? (int) $number : $number / $roundingCoef; } } diff --git a/Extension/Core/Type/ChoiceType.php b/Extension/Core/Type/ChoiceType.php index 0042d7580..d0a1b6e06 100644 --- a/Extension/Core/Type/ChoiceType.php +++ b/Extension/Core/Type/ChoiceType.php @@ -141,9 +141,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $knownValues[$child->getName()] = $value; unset($unknownValues[$value]); continue; - } else { - $knownValues[$child->getName()] = null; } + + $knownValues[$child->getName()] = null; } } else { foreach ($choiceList->getChoicesForValues($data) as $key => $choice) { From 669faf533c2e714e769f3c9dfdc175237e5fae6d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 5 Aug 2024 09:12:25 +0200 Subject: [PATCH 11/41] Fix multiple CS errors --- Tests/Extension/DataCollector/FormDataExtractorTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/Extension/DataCollector/FormDataExtractorTest.php b/Tests/Extension/DataCollector/FormDataExtractorTest.php index b8a1fee37..29f9359df 100644 --- a/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -362,8 +362,7 @@ public function testExtractSubmittedDataStoresErrorCause() ] EODUMP; } - $this->assertDumpMatchesFormat($expectedFormat - , + $this->assertDumpMatchesFormat($expectedFormat, $this->dataExtractor->extractSubmittedData($form) ); } From 1824d39c04d9ea22231c23a5955c95b1c96bd592 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Aug 2024 17:21:17 +0200 Subject: [PATCH 12/41] Code style change in `@PER-CS2.0` affecting `@Symfony` (parentheses for anonymous classes) --- Tests/AbstractRequestHandlerTestCase.php | 2 +- Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/AbstractRequestHandlerTestCase.php b/Tests/AbstractRequestHandlerTestCase.php index d050edb41..f80efffb7 100644 --- a/Tests/AbstractRequestHandlerTestCase.php +++ b/Tests/AbstractRequestHandlerTestCase.php @@ -39,7 +39,7 @@ abstract class AbstractRequestHandlerTestCase extends TestCase protected function setUp(): void { - $this->serverParams = new class() extends ServerParams { + $this->serverParams = new class extends ServerParams { public ?int $contentLength = null; public string $postMaxSize = ''; diff --git a/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index f82fdc2f9..2b1b239e5 100644 --- a/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -728,7 +728,7 @@ public function testPassTranslatableMessageAsLabelDoesntCastItToString() public function testPassTranslatableInterfaceAsLabelDoesntCastItToString() { - $message = new class() implements TranslatableInterface { + $message = new class implements TranslatableInterface { public function trans(TranslatorInterface $translator, ?string $locale = null): string { return 'my_message'; From 6a4471250f8899f86b6202a11fb4a3eb1cce9949 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 29 Jul 2024 09:33:48 +0200 Subject: [PATCH 13/41] Remove useless code --- Extension/Core/Type/TimeType.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Extension/Core/Type/TimeType.php b/Extension/Core/Type/TimeType.php index ad559760d..d79921066 100644 --- a/Extension/Core/Type/TimeType.php +++ b/Extension/Core/Type/TimeType.php @@ -60,7 +60,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void if ('single_text' === $options['widget']) { $builder->addEventListener(FormEvents::PRE_SUBMIT, static function (FormEvent $e) use ($options) { - /** @var PreSubmitEvent $event */ $data = $e->getData(); if ($data && preg_match('/^(?P\d{2}):(?P\d{2})(?::(?P\d{2})(?:\.\d+)?)?$/', $data, $matches)) { if ($options['with_seconds']) { From e6f725489b96dde6b8d51702574186aa6152cacf Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 9 Aug 2024 10:02:21 +0200 Subject: [PATCH 14/41] [Form] Replace class-string by `::class` for `BaseTypeTestCase::TESTED_TYPE` --- Tests/Extension/Core/Type/BirthdayTypeTest.php | 3 ++- Tests/Extension/Core/Type/ButtonTypeTest.php | 3 ++- Tests/Extension/Core/Type/CheckboxTypeTest.php | 3 ++- Tests/Extension/Core/Type/ChoiceTypeTest.php | 3 ++- Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php | 3 ++- Tests/Extension/Core/Type/CollectionTypeTest.php | 3 ++- Tests/Extension/Core/Type/CountryTypeTest.php | 3 ++- Tests/Extension/Core/Type/CurrencyTypeTest.php | 3 ++- Tests/Extension/Core/Type/DateTimeTypeTest.php | 3 ++- Tests/Extension/Core/Type/DateTypeTest.php | 3 ++- Tests/Extension/Core/Type/FileTypeTest.php | 3 ++- Tests/Extension/Core/Type/FormTypeTest.php | 2 +- Tests/Extension/Core/Type/IntegerTypeTest.php | 3 ++- Tests/Extension/Core/Type/LanguageTypeTest.php | 3 ++- Tests/Extension/Core/Type/LocaleTypeTest.php | 3 ++- Tests/Extension/Core/Type/MoneyTypeTest.php | 3 ++- Tests/Extension/Core/Type/NumberTypeTest.php | 3 ++- Tests/Extension/Core/Type/PasswordTypeTest.php | 4 +++- Tests/Extension/Core/Type/RepeatedTypeTest.php | 3 ++- Tests/Extension/Core/Type/SubmitTypeTest.php | 3 ++- Tests/Extension/Core/Type/TextTypeTest.php | 4 +++- Tests/Extension/Core/Type/TimeTypeTest.php | 3 ++- Tests/Extension/Core/Type/TimezoneTypeTest.php | 3 ++- Tests/Extension/Core/Type/UrlTypeTest.php | 3 ++- Tests/Extension/Core/Type/WeekTypeTest.php | 3 ++- 25 files changed, 51 insertions(+), 25 deletions(-) diff --git a/Tests/Extension/Core/Type/BirthdayTypeTest.php b/Tests/Extension/Core/Type/BirthdayTypeTest.php index 048457141..53e5c959c 100644 --- a/Tests/Extension/Core/Type/BirthdayTypeTest.php +++ b/Tests/Extension/Core/Type/BirthdayTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\BirthdayType; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; /** @@ -18,7 +19,7 @@ */ class BirthdayTypeTest extends DateTypeTest { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\BirthdayType'; + public const TESTED_TYPE = BirthdayType::class; public function testSetInvalidYearsOption() { diff --git a/Tests/Extension/Core/Type/ButtonTypeTest.php b/Tests/Extension/Core/Type/ButtonTypeTest.php index 0125631c5..4825015d2 100644 --- a/Tests/Extension/Core/Type/ButtonTypeTest.php +++ b/Tests/Extension/Core/Type/ButtonTypeTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\Button; use Symfony\Component\Form\Exception\BadMethodCallException; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\ButtonType; use Symfony\Component\Form\Extension\Core\Type\FormType; /** @@ -21,7 +22,7 @@ */ class ButtonTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\ButtonType'; + public const TESTED_TYPE = ButtonType::class; public function testCreateButtonInstances() { diff --git a/Tests/Extension/Core/Type/CheckboxTypeTest.php b/Tests/Extension/Core/Type/CheckboxTypeTest.php index 62312e28d..69fd0fd60 100644 --- a/Tests/Extension/Core/Type/CheckboxTypeTest.php +++ b/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class CheckboxTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\CheckboxType'; + public const TESTED_TYPE = CheckboxType::class; public function testDataIsFalseByDefault() { diff --git a/Tests/Extension/Core/Type/ChoiceTypeTest.php b/Tests/Extension/Core/Type/ChoiceTypeTest.php index c4f18734f..948d682fc 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -15,12 +15,13 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class ChoiceTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\ChoiceType'; + public const TESTED_TYPE = ChoiceType::class; private array $choices = [ 'Bernhard' => 'a', diff --git a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php index af58e2ecf..f60c6664c 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php @@ -12,12 +12,13 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Test\TypeTestCase; use Symfony\Contracts\Translation\TranslatorInterface; class ChoiceTypeTranslationTest extends TypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\ChoiceType'; + public const TESTED_TYPE = ChoiceType::class; private array $choices = [ 'Bernhard' => 'a', diff --git a/Tests/Extension/Core/Type/CollectionTypeTest.php b/Tests/Extension/Core/Type/CollectionTypeTest.php index 79134db99..95e1d9ca7 100644 --- a/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Form; use Symfony\Component\Form\Tests\Fixtures\Author; use Symfony\Component\Form\Tests\Fixtures\AuthorType; @@ -20,7 +21,7 @@ class CollectionTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\CollectionType'; + public const TESTED_TYPE = CollectionType::class; public function testContainsNoChildByDefault() { diff --git a/Tests/Extension/Core/Type/CountryTypeTest.php b/Tests/Extension/Core/Type/CountryTypeTest.php index 57146e1ec..44073ef7b 100644 --- a/Tests/Extension/Core/Type/CountryTypeTest.php +++ b/Tests/Extension/Core/Type/CountryTypeTest.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\Extension\Core\Type\CountryType; use Symfony\Component\Intl\Util\IntlTestHelper; class CountryTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\CountryType'; + public const TESTED_TYPE = CountryType::class; protected function setUp(): void { diff --git a/Tests/Extension/Core/Type/CurrencyTypeTest.php b/Tests/Extension/Core/Type/CurrencyTypeTest.php index 51aaf6b37..3e8a53dfb 100644 --- a/Tests/Extension/Core/Type/CurrencyTypeTest.php +++ b/Tests/Extension/Core/Type/CurrencyTypeTest.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\Extension\Core\Type\CurrencyType; use Symfony\Component\Intl\Util\IntlTestHelper; class CurrencyTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\CurrencyType'; + public const TESTED_TYPE = CurrencyType::class; protected function setUp(): void { diff --git a/Tests/Extension/Core/Type/DateTimeTypeTest.php b/Tests/Extension/Core/Type/DateTimeTypeTest.php index a402a70f9..5067bb05e 100644 --- a/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -12,12 +12,13 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; class DateTimeTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\DateTimeType'; + public const TESTED_TYPE = DateTimeType::class; private string $defaultLocale; diff --git a/Tests/Extension/Core/Type/DateTypeTest.php b/Tests/Extension/Core/Type/DateTypeTest.php index dfbae9330..4b6a302f3 100644 --- a/Tests/Extension/Core/Type/DateTypeTest.php +++ b/Tests/Extension/Core/Type/DateTypeTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; use Symfony\Component\Intl\Intl; @@ -21,7 +22,7 @@ class DateTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\DateType'; + public const TESTED_TYPE = DateType::class; private string $defaultTimezone; private string $defaultLocale; diff --git a/Tests/Extension/Core/Type/FileTypeTest.php b/Tests/Extension/Core/Type/FileTypeTest.php index 9c3a8efd0..85907b695 100644 --- a/Tests/Extension/Core/Type/FileTypeTest.php +++ b/Tests/Extension/Core/Type/FileTypeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; use Symfony\Component\Form\NativeRequestHandler; use Symfony\Component\Form\RequestHandlerInterface; @@ -21,7 +22,7 @@ class FileTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\FileType'; + public const TESTED_TYPE = FileType::class; protected function getExtensions(): array { diff --git a/Tests/Extension/Core/Type/FormTypeTest.php b/Tests/Extension/Core/Type/FormTypeTest.php index fe838885e..fe19f3b12 100644 --- a/Tests/Extension/Core/Type/FormTypeTest.php +++ b/Tests/Extension/Core/Type/FormTypeTest.php @@ -64,7 +64,7 @@ public function setReferenceCopy($reference) class FormTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\FormType'; + public const TESTED_TYPE = FormType::class; public function testCreateFormInstances() { diff --git a/Tests/Extension/Core/Type/IntegerTypeTest.php b/Tests/Extension/Core/Type/IntegerTypeTest.php index 1e143b342..ff33c17c6 100644 --- a/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -11,11 +11,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Intl\Util\IntlTestHelper; class IntegerTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\IntegerType'; + public const TESTED_TYPE = IntegerType::class; private string $previousLocale; diff --git a/Tests/Extension/Core/Type/LanguageTypeTest.php b/Tests/Extension/Core/Type/LanguageTypeTest.php index e214e0afd..8eb085112 100644 --- a/Tests/Extension/Core/Type/LanguageTypeTest.php +++ b/Tests/Extension/Core/Type/LanguageTypeTest.php @@ -13,11 +13,12 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\LanguageType; use Symfony\Component\Intl\Util\IntlTestHelper; class LanguageTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\LanguageType'; + public const TESTED_TYPE = LanguageType::class; protected function setUp(): void { diff --git a/Tests/Extension/Core/Type/LocaleTypeTest.php b/Tests/Extension/Core/Type/LocaleTypeTest.php index 8486b6656..a2a820b39 100644 --- a/Tests/Extension/Core/Type/LocaleTypeTest.php +++ b/Tests/Extension/Core/Type/LocaleTypeTest.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\Extension\Core\Type\LocaleType; use Symfony\Component\Intl\Util\IntlTestHelper; class LocaleTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\LocaleType'; + public const TESTED_TYPE = LocaleType::class; protected function setUp(): void { diff --git a/Tests/Extension/Core/Type/MoneyTypeTest.php b/Tests/Extension/Core/Type/MoneyTypeTest.php index 58e33af19..302f09c22 100644 --- a/Tests/Extension/Core/Type/MoneyTypeTest.php +++ b/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -11,11 +11,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Intl\Util\IntlTestHelper; class MoneyTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\MoneyType'; + public const TESTED_TYPE = MoneyType::class; private string $defaultLocale; diff --git a/Tests/Extension/Core/Type/NumberTypeTest.php b/Tests/Extension/Core/Type/NumberTypeTest.php index 9efe05221..e744563d1 100644 --- a/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/Tests/Extension/Core/Type/NumberTypeTest.php @@ -13,11 +13,12 @@ use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Intl\Util\IntlTestHelper; class NumberTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\NumberType'; + public const TESTED_TYPE = NumberType::class; private string $defaultLocale; diff --git a/Tests/Extension/Core/Type/PasswordTypeTest.php b/Tests/Extension/Core/Type/PasswordTypeTest.php index 8d428a26a..945437bcb 100644 --- a/Tests/Extension/Core/Type/PasswordTypeTest.php +++ b/Tests/Extension/Core/Type/PasswordTypeTest.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; + class PasswordTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\PasswordType'; + public const TESTED_TYPE = PasswordType::class; public function testEmptyIfNotSubmitted() { diff --git a/Tests/Extension/Core/Type/RepeatedTypeTest.php b/Tests/Extension/Core/Type/RepeatedTypeTest.php index 06b9151fb..62d101900 100644 --- a/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -11,13 +11,14 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Form; use Symfony\Component\Form\Tests\Fixtures\NotMappedType; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class RepeatedTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\RepeatedType'; + public const TESTED_TYPE = RepeatedType::class; protected Form $form; diff --git a/Tests/Extension/Core/Type/SubmitTypeTest.php b/Tests/Extension/Core/Type/SubmitTypeTest.php index 8a16175d7..af5ab8400 100644 --- a/Tests/Extension/Core/Type/SubmitTypeTest.php +++ b/Tests/Extension/Core/Type/SubmitTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\SubmitButton; /** @@ -18,7 +19,7 @@ */ class SubmitTypeTest extends ButtonTypeTest { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\SubmitType'; + public const TESTED_TYPE = SubmitType::class; public function testCreateSubmitButtonInstances() { diff --git a/Tests/Extension/Core/Type/TextTypeTest.php b/Tests/Extension/Core/Type/TextTypeTest.php index a28dfa9af..483215168 100644 --- a/Tests/Extension/Core/Type/TextTypeTest.php +++ b/Tests/Extension/Core/Type/TextTypeTest.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\TextType; + class TextTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\TextType'; + public const TESTED_TYPE = TextType::class; public function testSubmitNull($expected = null, $norm = null, $view = null) { diff --git a/Tests/Extension/Core/Type/TimeTypeTest.php b/Tests/Extension/Core/Type/TimeTypeTest.php index 155657038..8a2baf1b4 100644 --- a/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/Tests/Extension/Core/Type/TimeTypeTest.php @@ -14,13 +14,14 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\InvalidConfigurationException; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\TimeType; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class TimeTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\TimeType'; + public const TESTED_TYPE = TimeType::class; public function testSubmitDateTime() { diff --git a/Tests/Extension/Core/Type/TimezoneTypeTest.php b/Tests/Extension/Core/Type/TimezoneTypeTest.php index 9966b4043..4f2343974 100644 --- a/Tests/Extension/Core/Type/TimezoneTypeTest.php +++ b/Tests/Extension/Core/Type/TimezoneTypeTest.php @@ -13,11 +13,12 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Intl\Util\IntlTestHelper; class TimezoneTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\TimezoneType'; + public const TESTED_TYPE = TimezoneType::class; public function testTimezonesAreSelectable() { diff --git a/Tests/Extension/Core/Type/UrlTypeTest.php b/Tests/Extension/Core/Type/UrlTypeTest.php index 243d15297..a0d335647 100644 --- a/Tests/Extension/Core/Type/UrlTypeTest.php +++ b/Tests/Extension/Core/Type/UrlTypeTest.php @@ -12,13 +12,14 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; +use Symfony\Component\Form\Extension\Core\Type\UrlType; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; class UrlTypeTest extends TextTypeTest { use ExpectUserDeprecationMessageTrait; - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\UrlType'; + public const TESTED_TYPE = UrlType::class; /** * @group legacy diff --git a/Tests/Extension/Core/Type/WeekTypeTest.php b/Tests/Extension/Core/Type/WeekTypeTest.php index a69b96a38..b4d58fd95 100644 --- a/Tests/Extension/Core/Type/WeekTypeTest.php +++ b/Tests/Extension/Core/Type/WeekTypeTest.php @@ -11,11 +11,12 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\Type\WeekType; use Symfony\Component\Form\FormError; class WeekTypeTest extends BaseTypeTestCase { - public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\WeekType'; + public const TESTED_TYPE = WeekType::class; public function testSubmitArray() { From ad5e8613911580583e554c3b1a79a63e1cefebfc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 9 Aug 2024 09:46:47 +0200 Subject: [PATCH 15/41] [Form] Add support for the `calendar` option in `DateType` --- CHANGELOG.md | 1 + .../DateTimeToLocalizedStringTransformer.php | 14 ++--- Extension/Core/Type/DateType.php | 6 ++- ...teTimeToLocalizedStringTransformerTest.php | 54 +++++++++++++++++++ Tests/Extension/Core/Type/DateTypeTest.php | 34 ++++++++++++ 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 631041da4..6ad9ab93f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Deprecate the `VersionAwareTest` trait, use feature detection instead + * Add support for the `calendar` option in `DateType` 7.1 --- diff --git a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index a93803cd8..5e93d289d 100644 --- a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -30,12 +30,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer /** * @see BaseDateTimeTransformer::formats for available format options * - * @param string|null $inputTimezone The name of the input timezone - * @param string|null $outputTimezone The name of the output timezone - * @param int|null $dateFormat The date format - * @param int|null $timeFormat The time format - * @param int $calendar One of the \IntlDateFormatter calendar constants - * @param string|null $pattern A pattern to pass to \IntlDateFormatter + * @param string|null $inputTimezone The name of the input timezone + * @param string|null $outputTimezone The name of the output timezone + * @param int|null $dateFormat The date format + * @param int|null $timeFormat The time format + * @param int|\IntlCalendar $calendar One of the \IntlDateFormatter calendar constants or an \IntlCalendar instance + * @param string|null $pattern A pattern to pass to \IntlDateFormatter * * @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string */ @@ -44,7 +44,7 @@ public function __construct( ?string $outputTimezone = null, ?int $dateFormat = null, ?int $timeFormat = null, - private int $calendar = \IntlDateFormatter::GREGORIAN, + private int|\IntlCalendar $calendar = \IntlDateFormatter::GREGORIAN, private ?string $pattern = null, ) { parent::__construct($inputTimezone, $outputTimezone); diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index 773a51cbd..d30946d7c 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -49,7 +49,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void { $dateFormat = \is_int($options['format']) ? $options['format'] : self::DEFAULT_FORMAT; $timeFormat = \IntlDateFormatter::NONE; - $calendar = \IntlDateFormatter::GREGORIAN; + $calendar = $options['calendar'] ?? \IntlDateFormatter::GREGORIAN; $pattern = \is_string($options['format']) ? $options['format'] : ''; if (!\in_array($dateFormat, self::ACCEPTED_FORMATS, true)) { @@ -281,6 +281,7 @@ public function configureOptions(OptionsResolver $resolver): void 'format' => $format, 'model_timezone' => null, 'view_timezone' => null, + 'calendar' => null, 'placeholder' => $placeholderDefault, 'html5' => true, // Don't modify \DateTime classes by reference, we treat @@ -320,6 +321,9 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('months', 'array'); $resolver->setAllowedTypes('days', 'array'); $resolver->setAllowedTypes('input_format', 'string'); + $resolver->setAllowedTypes('calendar', ['null', \IntlCalendar::class]); + + $resolver->setInfo('calendar', 'The calendar to use for formatting and parsing the date. The value should be one of the \IntlDateFormatter calendar constants or an instance of the \IntlCalendar to use.'); $resolver->setNormalizer('html5', static function (Options $options, $html5) { if ($html5 && 'single_text' === $options['widget'] && self::HTML5_FORMAT !== $options['format']) { diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index c7918ae8b..0cf8b22f6 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -392,6 +392,60 @@ public function testReverseTransformWrapsIntlErrorsWithExceptionsAndErrorLevel() } } + public function testTransformDateTimeWithCustomCalendar() + { + $dateTime = new \DateTimeImmutable('2024-03-31'); + + $weekBeginsOnSunday = \IntlCalendar::createInstance(); + $weekBeginsOnSunday->setFirstDayOfWeek(\IntlCalendar::DOW_SUNDAY); + + $this->assertSame( + '2024-03-31 2024w14', + (new DateTimeToLocalizedStringTransformer(calendar: $weekBeginsOnSunday, pattern: "y-MM-dd y'w'w"))->transform($dateTime), + ); + + $weekBeginsOnMonday = \IntlCalendar::createInstance(); + $weekBeginsOnMonday->setFirstDayOfWeek(\IntlCalendar::DOW_MONDAY); + + $this->assertSame( + '2024-03-31 2024w13', + (new DateTimeToLocalizedStringTransformer(calendar: $weekBeginsOnMonday, pattern: "y-MM-dd y'w'w"))->transform($dateTime), + ); + } + + public function testReverseTransformDateTimeWithCustomCalendar() + { + $weekBeginsOnSunday = \IntlCalendar::createInstance(); + $weekBeginsOnSunday->setFirstDayOfWeek(\IntlCalendar::DOW_SUNDAY); + + $this->assertSame( + '2024-03-31', + (new DateTimeToLocalizedStringTransformer(calendar: $weekBeginsOnSunday, pattern: "y-MM-dd y'w'w")) + ->reverseTransform('2024-03-31 2024w14') + ->format('Y-m-d'), + ); + + $weekBeginsOnMonday = \IntlCalendar::createInstance(); + $weekBeginsOnMonday->setFirstDayOfWeek(\IntlCalendar::DOW_MONDAY); + + $this->assertSame( + '2024-03-31', + (new DateTimeToLocalizedStringTransformer(calendar: $weekBeginsOnMonday, pattern: "y-MM-dd y'w'w")) + ->reverseTransform('2024-03-31 2024w13') + ->format('Y-m-d'), + ); + } + + public function testDefaultCalendarIsGregorian() + { + $now = new \DateTimeImmutable(); + + $this->assertSame( + (new DateTimeToLocalizedStringTransformer(calendar: \IntlDateFormatter::GREGORIAN, pattern: "y-MM-dd y'w'w"))->transform($now), + (new DateTimeToLocalizedStringTransformer(pattern: "y-MM-dd y'w'w"))->transform($now), + ); + } + protected function createDateTimeTransformer(?string $inputTimezone = null, ?string $outputTimezone = null): BaseDateTimeTransformer { return new DateTimeToLocalizedStringTransformer($inputTimezone, $outputTimezone); diff --git a/Tests/Extension/Core/Type/DateTypeTest.php b/Tests/Extension/Core/Type/DateTypeTest.php index dfbae9330..61787512a 100644 --- a/Tests/Extension/Core/Type/DateTypeTest.php +++ b/Tests/Extension/Core/Type/DateTypeTest.php @@ -1156,6 +1156,40 @@ public function testDateTimeImmutableInputTimezoneNotMatchingModelTimezone() ]); } + public function testSubmitWithCustomCalendarOption() + { + // Creates a new form using the "roc" (Republic Of China) calendar. This calendar starts in 1912, the year 2024 in + // the Gregorian calendar is the year 113 in the "roc" calendar. + $form = $this->factory->create(static::TESTED_TYPE, options: [ + 'format' => 'y-MM-dd', + 'html5' => false, + 'input' => 'array', + 'calendar' => \IntlCalendar::createInstance(locale: 'zh_TW@calendar=roc'), + ]); + $form->submit('113-03-31'); + + $this->assertSame('2024', $form->getData()['year'], 'The year should be converted to the default locale (en)'); + $this->assertSame('31', $form->getData()['day']); + $this->assertSame('3', $form->getData()['month']); + + $this->assertSame('113-03-31', $form->getViewData()); + } + + public function testSetDataWithCustomCalendarOption() + { + // Creates a new form using the "roc" (Republic Of China) calendar. This calendar starts in 1912, the year 2024 in + // the Gregorian calendar is the year 113 in the "roc" calendar. + $form = $this->factory->create(static::TESTED_TYPE, options: [ + 'format' => 'y-MM-dd', + 'html5' => false, + 'input' => 'array', + 'calendar' => \IntlCalendar::createInstance(locale: 'zh_TW@calendar=roc'), + ]); + $form->setData(['year' => '2024', 'month' => '3', 'day' => '31']); + + $this->assertSame('113-03-31', $form->getViewData()); + } + protected function getTestOptions(): array { return ['widget' => 'choice']; From a3ffc5a7d21538b4ac371d45d2a7a6a0761c1f7b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 22 Aug 2024 09:26:39 +0200 Subject: [PATCH 16/41] [Form] Fix info for the `calendar` option of the `DateType` form type --- Extension/Core/Type/DateType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index d30946d7c..41dcf570c 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -323,7 +323,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('input_format', 'string'); $resolver->setAllowedTypes('calendar', ['null', \IntlCalendar::class]); - $resolver->setInfo('calendar', 'The calendar to use for formatting and parsing the date. The value should be one of the \IntlDateFormatter calendar constants or an instance of the \IntlCalendar to use.'); + $resolver->setInfo('calendar', 'The calendar to use for formatting and parsing the date. The value should be an instance of \IntlCalendar. By default, the Gregorian calendar with the default locale is used.'); $resolver->setNormalizer('html5', static function (Options $options, $html5) { if ($html5 && 'single_text' === $options['widget'] && self::HTML5_FORMAT !== $options['format']) { From 7168ea22c680b88305c22183a5278cb07039b786 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 22 Aug 2024 10:42:25 +0200 Subject: [PATCH 17/41] [Form] Remove stalled PHP bug mentions --- .../DateTimeToLocalizedStringTransformer.php | 13 +++++-------- Extension/Core/Type/DateType.php | 8 +------- .../DateTimeToLocalizedStringTransformerTest.php | 9 +++++++++ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 5e93d289d..426c4bf89 100644 --- a/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer; +use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Exception\UnexpectedTypeException; @@ -60,6 +61,10 @@ public function __construct( throw new UnexpectedTypeException($timeFormat, implode('", "', self::$formats)); } + if (\is_int($calendar) && !\in_array($calendar, [\IntlDateFormatter::GREGORIAN, \IntlDateFormatter::TRADITIONAL], true)) { + throw new InvalidArgumentException('The "calendar" option should be either an \IntlDateFormatter constant or an \IntlCalendar instance.'); + } + $this->dateFormat = $dateFormat; $this->timeFormat = $timeFormat; } @@ -157,8 +162,6 @@ public function reverseTransform(mixed $value): ?\DateTime * Returns a preconfigured IntlDateFormatter instance. * * @param bool $ignoreTimezone Use UTC regardless of the configured timezone - * - * @throws TransformationFailedException in case the date formatter cannot be constructed */ protected function getIntlDateFormatter(bool $ignoreTimezone = false): \IntlDateFormatter { @@ -170,12 +173,6 @@ protected function getIntlDateFormatter(bool $ignoreTimezone = false): \IntlDate $pattern = $this->pattern; $intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern ?? ''); - - // new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/66323 - if (!$intlDateFormatter) { - throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code()); - } - $intlDateFormatter->setLenient(false); return $intlDateFormatter; diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index 41dcf570c..54f24b89a 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -120,17 +120,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void \Locale::getDefault(), $dateFormat, $timeFormat, - // see https://bugs.php.net/66323 - class_exists(\IntlTimeZone::class, false) ? \IntlTimeZone::createDefault() : null, + null, $calendar, $pattern ); - // new \IntlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/66323 - if (!$formatter) { - throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code()); - } - $formatter->setLenient(false); if ('choice' === $options['widget']) { diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 0cf8b22f6..a14e4a44e 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; +use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; @@ -446,6 +447,14 @@ public function testDefaultCalendarIsGregorian() ); } + public function testInvalidCalendar() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The "calendar" option should be either an \IntlDateFormatter constant or an \IntlCalendar instance.'); + + new DateTimeToLocalizedStringTransformer(calendar: 123456); + } + protected function createDateTimeTransformer(?string $inputTimezone = null, ?string $outputTimezone = null): BaseDateTimeTransformer { return new DateTimeToLocalizedStringTransformer($inputTimezone, $outputTimezone); From 964d2ea563193b98618310b18d3a0374a20d7698 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 22 Aug 2024 13:58:26 +0200 Subject: [PATCH 18/41] skip tests requiring the intl extension if it's not installed --- Tests/Extension/Core/Type/DateTypeTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/Extension/Core/Type/DateTypeTest.php b/Tests/Extension/Core/Type/DateTypeTest.php index f7f7d925c..5f4f896b5 100644 --- a/Tests/Extension/Core/Type/DateTypeTest.php +++ b/Tests/Extension/Core/Type/DateTypeTest.php @@ -1159,6 +1159,8 @@ public function testDateTimeImmutableInputTimezoneNotMatchingModelTimezone() public function testSubmitWithCustomCalendarOption() { + IntlTestHelper::requireFullIntl($this); + // Creates a new form using the "roc" (Republic Of China) calendar. This calendar starts in 1912, the year 2024 in // the Gregorian calendar is the year 113 in the "roc" calendar. $form = $this->factory->create(static::TESTED_TYPE, options: [ @@ -1178,6 +1180,8 @@ public function testSubmitWithCustomCalendarOption() public function testSetDataWithCustomCalendarOption() { + IntlTestHelper::requireFullIntl($this); + // Creates a new form using the "roc" (Republic Of China) calendar. This calendar starts in 1912, the year 2024 in // the Gregorian calendar is the year 113 in the "roc" calendar. $form = $this->factory->create(static::TESTED_TYPE, options: [ From 64b30ef5a6bb466d7bcc9e4b31d4e7f26b9b8432 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Wed, 18 Oct 2023 17:16:44 -0400 Subject: [PATCH 19/41] add LazyChoiceLoader and choice_lazy option --- CHANGELOG.md | 1 + ChoiceList/Loader/LazyChoiceLoader.php | 54 +++++++++ Extension/Core/Type/ChoiceType.php | 19 +++ .../Loader/LazyChoiceLoaderTest.php | 50 ++++++++ Tests/Extension/Core/Type/ChoiceTypeTest.php | 108 ++++++++++++++++++ .../Descriptor/resolved_form_type_1.json | 1 + .../Descriptor/resolved_form_type_1.txt | 32 +++--- 7 files changed, 249 insertions(+), 16 deletions(-) create mode 100644 ChoiceList/Loader/LazyChoiceLoader.php create mode 100644 Tests/ChoiceList/Loader/LazyChoiceLoaderTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad9ab93f..1cef48696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Deprecate the `VersionAwareTest` trait, use feature detection instead * Add support for the `calendar` option in `DateType` + * Add `LazyChoiceLoader` and `choice_lazy` option in `ChoiceType` for loading and rendering choices on demand 7.1 --- diff --git a/ChoiceList/Loader/LazyChoiceLoader.php b/ChoiceList/Loader/LazyChoiceLoader.php new file mode 100644 index 000000000..03451be36 --- /dev/null +++ b/ChoiceList/Loader/LazyChoiceLoader.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\ChoiceList\Loader; + +use Symfony\Component\Form\ChoiceList\ArrayChoiceList; +use Symfony\Component\Form\ChoiceList\ChoiceListInterface; + +/** + * A choice loader that loads its choices and values lazily, only when necessary. + * + * @author Yonel Ceruto + */ +class LazyChoiceLoader implements ChoiceLoaderInterface +{ + private ?ChoiceListInterface $choiceList = null; + + public function __construct( + private readonly ChoiceLoaderInterface $loader, + ) { + } + + public function loadChoiceList(?callable $value = null): ChoiceListInterface + { + return $this->choiceList ??= new ArrayChoiceList([], $value); + } + + public function loadChoicesForValues(array $values, ?callable $value = null): array + { + $choices = $this->loader->loadChoicesForValues($values, $value); + $this->choiceList = new ArrayChoiceList($choices, $value); + + return $choices; + } + + public function loadValuesForChoices(array $choices, ?callable $value = null): array + { + $values = $this->loader->loadValuesForChoices($choices, $value); + + if ($this->choiceList?->getValuesForChoices($choices) !== $values) { + $this->loadChoicesForValues($values, $value); + } + + return $values; + } +} diff --git a/Extension/Core/Type/ChoiceType.php b/Extension/Core/Type/ChoiceType.php index d0a1b6e06..35a317592 100644 --- a/Extension/Core/Type/ChoiceType.php +++ b/Extension/Core/Type/ChoiceType.php @@ -27,10 +27,12 @@ use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; +use Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader; use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\View\ChoiceListView; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Event\PreSubmitEvent; +use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\DataMapper\CheckboxListMapper; use Symfony\Component\Form\Extension\Core\DataMapper\RadioListMapper; @@ -333,11 +335,24 @@ public function configureOptions(OptionsResolver $resolver): void return $choiceTranslationDomain; }; + $choiceLoaderNormalizer = static function (Options $options, ?ChoiceLoaderInterface $choiceLoader) { + if (!$options['choice_lazy']) { + return $choiceLoader; + } + + if (null === $choiceLoader) { + throw new LogicException('The "choice_lazy" option can only be used if the "choice_loader" option is set.'); + } + + return new LazyChoiceLoader($choiceLoader); + }; + $resolver->setDefaults([ 'multiple' => false, 'expanded' => false, 'choices' => [], 'choice_filter' => null, + 'choice_lazy' => false, 'choice_loader' => null, 'choice_label' => null, 'choice_name' => null, @@ -365,9 +380,11 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); + $resolver->setNormalizer('choice_loader', $choiceLoaderNormalizer); $resolver->setAllowedTypes('choices', ['null', 'array', \Traversable::class]); $resolver->setAllowedTypes('choice_translation_domain', ['null', 'bool', 'string']); + $resolver->setAllowedTypes('choice_lazy', 'bool'); $resolver->setAllowedTypes('choice_loader', ['null', ChoiceLoaderInterface::class, ChoiceLoader::class]); $resolver->setAllowedTypes('choice_filter', ['null', 'callable', 'string', PropertyPath::class, ChoiceFilter::class]); $resolver->setAllowedTypes('choice_label', ['null', 'bool', 'callable', 'string', PropertyPath::class, ChoiceLabel::class]); @@ -381,6 +398,8 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('separator_html', ['bool']); $resolver->setAllowedTypes('duplicate_preferred_choices', 'bool'); $resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', PropertyPath::class, GroupBy::class]); + + $resolver->setInfo('choice_lazy', 'Load choices on demand. When set to true, only the selected choices are loaded and rendered.'); } public function getBlockPrefix(): string diff --git a/Tests/ChoiceList/Loader/LazyChoiceLoaderTest.php b/Tests/ChoiceList/Loader/LazyChoiceLoaderTest.php new file mode 100644 index 000000000..0c1bcf3c2 --- /dev/null +++ b/Tests/ChoiceList/Loader/LazyChoiceLoaderTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader; +use Symfony\Component\Form\Tests\Fixtures\ArrayChoiceLoader; + +class LazyChoiceLoaderTest extends TestCase +{ + private LazyChoiceLoader $loader; + + protected function setUp(): void + { + $this->loader = new LazyChoiceLoader(new ArrayChoiceLoader(['A', 'B', 'C'])); + } + + public function testInitialEmptyChoiceListLoading() + { + $this->assertSame([], $this->loader->loadChoiceList()->getChoices()); + } + + public function testOnDemandChoiceListAfterLoadingValuesForChoices() + { + $this->loader->loadValuesForChoices(['A']); + $this->assertSame(['A' => 'A'], $this->loader->loadChoiceList()->getChoices()); + } + + public function testOnDemandChoiceListAfterLoadingChoicesForValues() + { + $this->loader->loadChoicesForValues(['B']); + $this->assertSame(['B' => 'B'], $this->loader->loadChoiceList()->getChoices()); + } + + public function testOnDemandChoiceList() + { + $this->loader->loadValuesForChoices(['A']); + $this->loader->loadChoicesForValues(['B']); + $this->assertSame(['B' => 'B'], $this->loader->loadChoiceList()->getChoices()); + } +} diff --git a/Tests/Extension/Core/Type/ChoiceTypeTest.php b/Tests/Extension/Core/Type/ChoiceTypeTest.php index 948d682fc..28810bbc7 100644 --- a/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormInterface; @@ -2277,4 +2278,111 @@ public function testWithSameLoaderAndDifferentChoiceValueCallbacks() $this->assertSame('20', $view['choice_two']->vars['choices'][1]->value); $this->assertSame('30', $view['choice_two']->vars['choices'][2]->value); } + + public function testChoiceLazyThrowsWhenChoiceLoaderIsNotSet() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The "choice_lazy" option can only be used if the "choice_loader" option is set.'); + + $this->factory->create(static::TESTED_TYPE, null, [ + 'choice_lazy' => true, + ]); + } + + public function testChoiceLazyLoadsAndRendersNothingWhenNoDataSet() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B']), + 'choice_lazy' => true, + ]); + + $this->assertNull($form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertSame([], $view->vars['choices']); + } + + public function testChoiceLazyLoadsAndRendersOnlyDataSetViaDefault() + { + $form = $this->factory->create(static::TESTED_TYPE, 'A', [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B']), + 'choice_lazy' => true, + ]); + + $this->assertSame('A', $form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertCount(1, $view->vars['choices']); + $this->assertSame('A', $view->vars['choices'][0]->value); + } + + public function testChoiceLazyLoadsAndRendersOnlyDataSetViaSubmit() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B']), + 'choice_lazy' => true, + ]); + + $form->submit('B'); + $this->assertSame('B', $form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertCount(1, $view->vars['choices']); + $this->assertSame('B', $view->vars['choices'][0]->value); + } + + public function testChoiceLazyErrorWhenInvalidSubmitData() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B']), + 'choice_lazy' => true, + ]); + + $form->submit('invalid'); + $this->assertNull($form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertCount(0, $view->vars['choices']); + $this->assertCount(1, $form->getErrors()); + $this->assertSame('ERROR: The selected choice is invalid.', trim((string) $form->getErrors())); + } + + public function testChoiceLazyMultipleWithDefaultData() + { + $form = $this->factory->create(static::TESTED_TYPE, ['A', 'B'], [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B', 'c' => 'C']), + 'choice_lazy' => true, + 'multiple' => true, + ]); + + $this->assertSame(['A', 'B'], $form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertCount(2, $view->vars['choices']); + $this->assertSame('A', $view->vars['choices'][0]->value); + $this->assertSame('B', $view->vars['choices'][1]->value); + } + + public function testChoiceLazyMultipleWithSubmittedData() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'choice_loader' => new CallbackChoiceLoader(fn () => ['a' => 'A', 'b' => 'B', 'c' => 'C']), + 'choice_lazy' => true, + 'multiple' => true, + ]); + + $form->submit(['B', 'C']); + $this->assertSame(['B', 'C'], $form->getData()); + + $view = $form->createView(); + $this->assertArrayHasKey('choices', $view->vars); + $this->assertCount(2, $view->vars['choices']); + $this->assertSame('B', $view->vars['choices'][0]->value); + $this->assertSame('C', $view->vars['choices'][1]->value); + } } diff --git a/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/Tests/Fixtures/Descriptor/resolved_form_type_1.json index e071ec712..5590018c0 100644 --- a/Tests/Fixtures/Descriptor/resolved_form_type_1.json +++ b/Tests/Fixtures/Descriptor/resolved_form_type_1.json @@ -6,6 +6,7 @@ "choice_attr", "choice_filter", "choice_label", + "choice_lazy", "choice_loader", "choice_name", "choice_translation_domain", diff --git a/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/Tests/Fixtures/Descriptor/resolved_form_type_1.txt index 005bfd3e9..93c6b66d9 100644 --- a/Tests/Fixtures/Descriptor/resolved_form_type_1.txt +++ b/Tests/Fixtures/Descriptor/resolved_form_type_1.txt @@ -8,22 +8,22 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") choice_attr FormType FormType FormTypeCsrfExtension choice_filter -------------------- ------------------------------ ----------------------- choice_label compound action csrf_field_name - choice_loader data_class allow_file_upload csrf_message - choice_name empty_data attr csrf_protection - choice_translation_domain error_bubbling attr_translation_parameters csrf_token_id - choice_translation_parameters invalid_message auto_initialize csrf_token_manager - choice_value trim block_name - choices block_prefix - duplicate_preferred_choices by_reference - expanded data - group_by disabled - multiple form_attr - placeholder getter - placeholder_attr help - preferred_choices help_attr - separator help_html - separator_html help_translation_parameters - inherit_data + choice_lazy data_class allow_file_upload csrf_message + choice_loader empty_data attr csrf_protection + choice_name error_bubbling attr_translation_parameters csrf_token_id + choice_translation_domain invalid_message auto_initialize csrf_token_manager + choice_translation_parameters trim block_name + choice_value block_prefix + choices by_reference + duplicate_preferred_choices data + expanded disabled + group_by form_attr + multiple getter + placeholder help + placeholder_attr help_attr + preferred_choices help_html + separator help_translation_parameters + separator_html inherit_data invalid_message_parameters is_empty_callback label From afc4cf934134f84769a08d68ed319fbfb76422a1 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 31 Aug 2024 00:31:12 +0200 Subject: [PATCH 20/41] CS: re-apply `trailing_comma_in_multiline` --- Tests/Extension/Core/Type/RepeatedTypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Extension/Core/Type/RepeatedTypeTest.php b/Tests/Extension/Core/Type/RepeatedTypeTest.php index b6b74f1aa..2d19a0613 100644 --- a/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -199,7 +199,7 @@ public function testSubmitNullForTextTypeWithEmptyDataOptionSetToEmptyString($em 'type' => TextType::class, 'options' => [ 'empty_data' => $emptyData, - ] + ], ]); $form->submit($submittedData); From 81c5c3281ae822a13f1271d261954ef28feee6cd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 23 Sep 2024 12:51:56 +0200 Subject: [PATCH 21/41] do not use the Test suffix for non test classes --- Test/FormPerformanceTestCase.php | 4 ++-- Tests/Extension/Core/Type/BaseTypeTestCase.php | 4 ++-- Tests/{VersionAwareTest.php => VersionAwareTestTrait.php} | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename Tests/{VersionAwareTest.php => VersionAwareTestTrait.php} (95%) diff --git a/Test/FormPerformanceTestCase.php b/Test/FormPerformanceTestCase.php index 1d631a2fd..12217e44b 100644 --- a/Test/FormPerformanceTestCase.php +++ b/Test/FormPerformanceTestCase.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Form\Test; -use Symfony\Component\Form\Tests\VersionAwareTest; +use Symfony\Component\Form\Tests\VersionAwareTestTrait; /** * Base class for performance tests. @@ -23,7 +23,7 @@ */ abstract class FormPerformanceTestCase extends FormIntegrationTestCase { - use VersionAwareTest; + use VersionAwareTestTrait; protected int $maxRunningTime = 0; diff --git a/Tests/Extension/Core/Type/BaseTypeTestCase.php b/Tests/Extension/Core/Type/BaseTypeTestCase.php index 14b602a4b..6ba9c6858 100644 --- a/Tests/Extension/Core/Type/BaseTypeTestCase.php +++ b/Tests/Extension/Core/Type/BaseTypeTestCase.php @@ -12,14 +12,14 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\Test\TypeTestCase; -use Symfony\Component\Form\Tests\VersionAwareTest; +use Symfony\Component\Form\Tests\VersionAwareTestTrait; /** * @author Bernhard Schussek */ abstract class BaseTypeTestCase extends TypeTestCase { - use VersionAwareTest; + use VersionAwareTestTrait; public const TESTED_TYPE = ''; diff --git a/Tests/VersionAwareTest.php b/Tests/VersionAwareTestTrait.php similarity index 95% rename from Tests/VersionAwareTest.php rename to Tests/VersionAwareTestTrait.php index 530c17473..62e98934e 100644 --- a/Tests/VersionAwareTest.php +++ b/Tests/VersionAwareTestTrait.php @@ -14,7 +14,7 @@ /** * @deprecated since Symfony 7.2, use feature detection instead. */ -trait VersionAwareTest +trait VersionAwareTestTrait { protected static int $supportedFeatureSetVersion = 404; @@ -23,7 +23,7 @@ trait VersionAwareTest */ protected function requiresFeatureSet(int $requiredFeatureSetVersion) { - trigger_deprecation('symfony/form', '7.2', 'The "%s" trait is deprecated, use feature detection instead.', VersionAwareTest::class); + trigger_deprecation('symfony/form', '7.2', 'The "%s" trait is deprecated, use feature detection instead.', VersionAwareTestTrait::class); if ($requiredFeatureSetVersion > static::$supportedFeatureSetVersion) { $this->markTestSkipped(\sprintf('Test requires features from symfony/form %.2f but only version %.2f is supported.', $requiredFeatureSetVersion / 100, static::$supportedFeatureSetVersion / 100)); From c85e835399b759bd42991fd59f8afb8dc9775982 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 23 Sep 2024 11:18:25 +0200 Subject: [PATCH 22/41] do not override the final runTest() method --- Test/FormPerformanceTestCase.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Test/FormPerformanceTestCase.php b/Test/FormPerformanceTestCase.php index 1d631a2fd..eafdf6c5f 100644 --- a/Test/FormPerformanceTestCase.php +++ b/Test/FormPerformanceTestCase.php @@ -25,19 +25,25 @@ abstract class FormPerformanceTestCase extends FormIntegrationTestCase { use VersionAwareTest; + private float $startTime; protected int $maxRunningTime = 0; - protected function runTest(): mixed + protected function setUp(): void { - $s = microtime(true); - $result = parent::runTest(); - $time = microtime(true) - $s; + parent::setUp(); + + $this->startTime = microtime(true); + } + + protected function assertPostConditions(): void + { + parent::assertPostConditions(); + + $time = microtime(true) - $this->startTime; if (0 != $this->maxRunningTime && $time > $this->maxRunningTime) { $this->fail(\sprintf('expected running time: <= %s but was: %s', $this->maxRunningTime, $time)); } - - return $result; } /** From 9c44ae8084df0ed829f0de020b7a4d8bdc80afa4 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 24 Sep 2024 13:28:07 +0200 Subject: [PATCH 23/41] Remove useless parent method calls in tests --- Tests/ChoiceList/AbstractChoiceListTestCase.php | 2 -- .../DateTimeToLocalizedStringTransformerTest.php | 2 -- .../Core/DataTransformer/DateTimeToRfc3339TransformerTest.php | 2 -- Tests/Extension/Core/Type/MoneyTypeTest.php | 2 -- Tests/Extension/Core/Type/NumberTypeTest.php | 2 -- Tests/Extension/Core/Type/PercentTypeTest.php | 2 -- Tests/NativeRequestHandlerTest.php | 2 -- 7 files changed, 14 deletions(-) diff --git a/Tests/ChoiceList/AbstractChoiceListTestCase.php b/Tests/ChoiceList/AbstractChoiceListTestCase.php index 0b0cb8e79..5ddae5614 100644 --- a/Tests/ChoiceList/AbstractChoiceListTestCase.php +++ b/Tests/ChoiceList/AbstractChoiceListTestCase.php @@ -39,8 +39,6 @@ abstract class AbstractChoiceListTestCase extends TestCase protected function setUp(): void { - parent::setUp(); - $this->list = $this->createChoiceList(); $choices = $this->getChoices(); diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index a14e4a44e..e8527b220 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -31,8 +31,6 @@ class DateTimeToLocalizedStringTransformerTest extends BaseDateTimeTransformerTe protected function setUp(): void { - parent::setUp(); - // Normalize intl. configuration settings. if (\extension_loaded('intl')) { $this->initialTestCaseUseException = ini_set('intl.use_exceptions', 0); diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php index 6a4d77039..c69ba31be 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php @@ -25,8 +25,6 @@ class DateTimeToRfc3339TransformerTest extends BaseDateTimeTransformerTestCase protected function setUp(): void { - parent::setUp(); - $this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC'); $this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC'); } diff --git a/Tests/Extension/Core/Type/MoneyTypeTest.php b/Tests/Extension/Core/Type/MoneyTypeTest.php index 302f09c22..f9112ffca 100644 --- a/Tests/Extension/Core/Type/MoneyTypeTest.php +++ b/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -33,8 +33,6 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - \Locale::setDefault($this->defaultLocale); } diff --git a/Tests/Extension/Core/Type/NumberTypeTest.php b/Tests/Extension/Core/Type/NumberTypeTest.php index e744563d1..95ccdfea9 100644 --- a/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/Tests/Extension/Core/Type/NumberTypeTest.php @@ -35,8 +35,6 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - \Locale::setDefault($this->defaultLocale); } diff --git a/Tests/Extension/Core/Type/PercentTypeTest.php b/Tests/Extension/Core/Type/PercentTypeTest.php index 76595d79b..120aab2f3 100644 --- a/Tests/Extension/Core/Type/PercentTypeTest.php +++ b/Tests/Extension/Core/Type/PercentTypeTest.php @@ -34,8 +34,6 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - \Locale::setDefault($this->defaultLocale); } diff --git a/Tests/NativeRequestHandlerTest.php b/Tests/NativeRequestHandlerTest.php index 3770e1219..6ff64bc65 100644 --- a/Tests/NativeRequestHandlerTest.php +++ b/Tests/NativeRequestHandlerTest.php @@ -41,8 +41,6 @@ protected function setUp(): void protected function tearDown(): void { - parent::tearDown(); - $_GET = []; $_POST = []; $_FILES = []; From bdf4a90b78d7a40f2fc077e435174f7d6f1ce128 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 26 Sep 2024 10:09:09 +0200 Subject: [PATCH 24/41] Remove unused imports --- Extension/Core/Type/TimeType.php | 1 - Tests/Extension/DataCollector/FormDataCollectorTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/Extension/Core/Type/TimeType.php b/Extension/Core/Type/TimeType.php index d79921066..35549b041 100644 --- a/Extension/Core/Type/TimeType.php +++ b/Extension/Core/Type/TimeType.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Event\PreSubmitEvent; use Symfony\Component\Form\Exception\InvalidConfigurationException; use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; diff --git a/Tests/Extension/DataCollector/FormDataCollectorTest.php b/Tests/Extension/DataCollector/FormDataCollectorTest.php index 0517b26c3..4090fc97b 100644 --- a/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -18,7 +18,6 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\DataCollector\FormDataCollector; use Symfony\Component\Form\Extension\DataCollector\FormDataExtractor; -use Symfony\Component\Form\Form; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormFactory; use Symfony\Component\Form\FormInterface; From 0df8534ea695de4db1ba76b6a0a060b66d75e25f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 2 Oct 2024 10:47:28 +0200 Subject: [PATCH 25/41] Make `@var` occurrences consistent --- Extension/Validator/ValidatorExtension.php | 2 +- FormBuilder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Extension/Validator/ValidatorExtension.php b/Extension/Validator/ValidatorExtension.php index 522a76962..2c534481c 100644 --- a/Extension/Validator/ValidatorExtension.php +++ b/Extension/Validator/ValidatorExtension.php @@ -40,7 +40,7 @@ public function __construct( // the DIC, where the XML file is loaded automatically. Thus the following // code must be kept synchronized with validation.xml - /* @var $metadata ClassMetadata */ + /* @var ClassMetadata $metadata */ $metadata->addConstraint(new Form()); $metadata->addConstraint(new Traverse(false)); } diff --git a/FormBuilder.php b/FormBuilder.php index 2bfd92a68..f34c2f76e 100644 --- a/FormBuilder.php +++ b/FormBuilder.php @@ -142,7 +142,7 @@ public function count(): int public function getFormConfig(): FormConfigInterface { - /** @var $config self */ + /** @var self $config */ $config = parent::getFormConfig(); $config->children = []; From 64116e00ea52c0e7f82d91ae55edf2b8fdbfb997 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 6 Oct 2024 18:45:39 +0200 Subject: [PATCH 26/41] CS: clean some whitespaces/indentation --- .../Core/DataTransformer/NumberToLocalizedStringTransformer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 127688e24..7911b47bd 100644 --- a/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -102,7 +102,7 @@ public function reverseTransform(mixed $value): int|float|null $value = str_replace(',', $decSep, $value); } - //If the value is in exponential notation with a negative exponent, we end up with a float value too + // If the value is in exponential notation with a negative exponent, we end up with a float value too if (str_contains($value, $decSep) || false !== stripos($value, 'e-')) { $type = \NumberFormatter::TYPE_DOUBLE; } else { From 6a49eed7c6fc1ab8b4f8916c500468ac1b5046c3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 Aug 2024 16:50:35 +0200 Subject: [PATCH 27/41] [Security] Implement stateless headers/cookies-based CSRF protection --- Extension/Csrf/Type/FormTypeCsrfExtension.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Extension/Csrf/Type/FormTypeCsrfExtension.php b/Extension/Csrf/Type/FormTypeCsrfExtension.php index 0ad4daeb3..10367ae5f 100644 --- a/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -19,6 +19,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\Form\Util\ServerParams; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -35,6 +36,8 @@ public function __construct( private ?TranslatorInterface $translator = null, private ?string $translationDomain = null, private ?ServerParams $serverParams = null, + private array $fieldAttr = [], + private ?string $defaultTokenId = null, ) { } @@ -73,6 +76,7 @@ public function finishView(FormView $view, FormInterface $form, array $options): $csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [ 'block_prefix' => 'csrf_token', 'mapped' => false, + 'attr' => $this->fieldAttr + ['autocomplete' => 'off'], ]); $view->children[$options['csrf_field_name']] = $csrfForm->createView($view); @@ -81,13 +85,24 @@ public function finishView(FormView $view, FormInterface $form, array $options): public function configureOptions(OptionsResolver $resolver): void { + if ($defaultTokenId = $this->defaultTokenId) { + $defaultTokenManager = $this->defaultTokenManager; + $defaultTokenId = static fn (Options $options) => $options['csrf_token_manager'] === $defaultTokenManager ? $defaultTokenId : null; + } + $resolver->setDefaults([ 'csrf_protection' => $this->defaultEnabled, 'csrf_field_name' => $this->defaultFieldName, 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.', 'csrf_token_manager' => $this->defaultTokenManager, - 'csrf_token_id' => null, + 'csrf_token_id' => $defaultTokenId, ]); + + $resolver->setAllowedTypes('csrf_protection', 'bool'); + $resolver->setAllowedTypes('csrf_field_name', 'string'); + $resolver->setAllowedTypes('csrf_message', 'string'); + $resolver->setAllowedTypes('csrf_token_manager', CsrfTokenManagerInterface::class); + $resolver->setAllowedTypes('csrf_token_id', ['null', 'string']); } public static function getExtendedTypes(): iterable From 896c5f267eeb0cee02dd5ce196c6581c7b07575c Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 20 Jul 2023 12:22:19 +0200 Subject: [PATCH 28/41] [Form] Use `form.post_set_data` in `ResizeFormListener` --- CHANGELOG.md | 2 + .../Core/EventListener/ResizeFormListener.php | 48 ++- .../EventListener/DataCollectorListener.php | 4 +- .../EventListener/ResizeFormListenerTest.php | 293 +++++++++++------- 4 files changed, 233 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cef48696..3b1fabfd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ CHANGELOG * Deprecate the `VersionAwareTest` trait, use feature detection instead * Add support for the `calendar` option in `DateType` * Add `LazyChoiceLoader` and `choice_lazy` option in `ChoiceType` for loading and rendering choices on demand + * Use `form.post_set_data` instead of `form.pre_set_data` in `ResizeFormListener` + * Change the priority of `DataCollectorListener` from 255 to -255 7.1 --- diff --git a/Extension/Core/EventListener/ResizeFormListener.php b/Extension/Core/EventListener/ResizeFormListener.php index d67efab31..299f91937 100644 --- a/Extension/Core/EventListener/ResizeFormListener.php +++ b/Extension/Core/EventListener/ResizeFormListener.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Extension\Core\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Event\PostSetDataEvent; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -27,6 +28,9 @@ class ResizeFormListener implements EventSubscriberInterface protected array $prototypeOptions; private \Closure|bool $deleteEmpty; + // BC, to be removed in 8.0 + private bool $overridden = true; + private bool $usePreSetData = false; public function __construct( private string $type, @@ -44,15 +48,57 @@ public function __construct( public static function getSubscribedEvents(): array { return [ - FormEvents::PRE_SET_DATA => 'preSetData', + FormEvents::PRE_SET_DATA => 'preSetData', // deprecated + FormEvents::POST_SET_DATA => ['postSetData', 255], // as early as possible FormEvents::PRE_SUBMIT => 'preSubmit', // (MergeCollectionListener, MergeDoctrineCollectionListener) FormEvents::SUBMIT => ['onSubmit', 50], ]; } + /** + * @deprecated Since Symfony 7.2, use {@see postSetData()} instead. + */ public function preSetData(FormEvent $event): void { + if (__CLASS__ === static::class + || __CLASS__ === (new \ReflectionClass($this))->getMethod('preSetData')->getDeclaringClass()->name + ) { + // not a child class, or child class does not overload PRE_SET_DATA + return; + } + + trigger_deprecation('symfony/form', '7.2', 'Calling "%s()" is deprecated, use "%s::postSetData()" instead.', __METHOD__, __CLASS__); + // parent::preSetData() has been called + $this->overridden = false; + try { + $this->postSetData($event); + } finally { + $this->usePreSetData = true; + } + } + + /** + * Remove FormEvent type hint in 8.0. + * + * @final since Symfony 7.2 + */ + public function postSetData(FormEvent|PostSetDataEvent $event): void + { + if (__CLASS__ !== static::class) { + if ($this->overridden) { + trigger_deprecation('symfony/form', '7.2', 'Calling "%s::preSetData()" is deprecated, use "%s::postSetData()" instead.', static::class, __CLASS__); + // parent::preSetData() has not been called, noop + + return; + } + + if ($this->usePreSetData) { + // nothing else to do + return; + } + } + $form = $event->getForm(); $data = $event->getData() ?? []; diff --git a/Extension/DataCollector/EventListener/DataCollectorListener.php b/Extension/DataCollector/EventListener/DataCollectorListener.php index 02cffbeff..c541efec2 100644 --- a/Extension/DataCollector/EventListener/DataCollectorListener.php +++ b/Extension/DataCollector/EventListener/DataCollectorListener.php @@ -32,8 +32,8 @@ public function __construct( public static function getSubscribedEvents(): array { return [ - // High priority in order to be called as soon as possible - FormEvents::POST_SET_DATA => ['postSetData', 255], + // Low priority in order to be called as late as possible + FormEvents::POST_SET_DATA => ['postSetData', -255], // Low priority in order to be called as late as possible FormEvents::POST_SUBMIT => ['postSubmit', -255], ]; diff --git a/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php b/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php index f63a5c154..934460c8f 100644 --- a/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php +++ b/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -14,28 +14,28 @@ use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormFactoryBuilder; use Symfony\Component\Form\FormFactoryInterface; -use Symfony\Component\Form\FormInterface; class ResizeFormListenerTest extends TestCase { private FormFactoryInterface $factory; - private FormInterface $form; + private FormBuilderInterface $builder; protected function setUp(): void { $this->factory = (new FormFactoryBuilder())->getFormFactory(); - $this->form = $this->getBuilder() + $this->builder = $this->getBuilder() ->setCompound(true) - ->setDataMapper(new DataMapper()) - ->getForm(); + ->setDataMapper(new DataMapper()); } protected function getBuilder($name = 'name') @@ -43,142 +43,221 @@ protected function getBuilder($name = 'name') return new FormBuilder($name, null, new EventDispatcher(), $this->factory); } - protected function getForm($name = 'name') + /** + * @group legacy + */ + public function testPreSetDataResizesForm() { - return $this->getBuilder($name)->getForm(); + $this->builder->add($this->getBuilder('0')); + $this->builder->add($this->getBuilder('1')); + $this->builder->addEventSubscriber(new class(TextType::class, ['attr' => ['maxlength' => 10]], false, false) extends ResizeFormListener { + public function preSetData(FormEvent $event): void + { + parent::preSetData($event); + } + }); + + $form = $this->builder->getForm(); + + $this->assertTrue($form->has('0')); + + // initialize the form + $form->setData([1 => 'string', 2 => 'string']); + + $this->assertFalse($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertTrue($form->has('2')); + + $this->assertSame('string', $form->get('1')->getData()); + $this->assertSame('string', $form->get('2')->getData()); } - public function testPreSetDataResizesForm() + public function testPostSetDataResizesForm() { - $this->form->add($this->getForm('0')); - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('0')); + $this->builder->add($this->getBuilder('1')); + $this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], false, false)); - $data = [1 => 'string', 2 => 'string']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], false, false); - $listener->preSetData($event); + $form = $this->builder->getForm(); + + $this->assertTrue($form->has('0')); - $this->assertFalse($this->form->has('0')); - $this->assertTrue($this->form->has('1')); - $this->assertTrue($this->form->has('2')); + // initialize the form + $form->setData([1 => 'string', 2 => 'string']); + + $this->assertFalse($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertTrue($form->has('2')); + + $this->assertSame('string', $form->get('1')->getData()); + $this->assertSame('string', $form->get('2')->getData()); } + /** + * @group legacy + */ public function testPreSetDataRequiresArrayOrTraversable() { $this->expectException(UnexpectedTypeException::class); $data = 'no array or traversable'; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, false); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new class(TextType::class, [], false, false) extends ResizeFormListener { + public function preSetData(FormEvent $event): void + { + parent::preSetData($event); + } + }; $listener->preSetData($event); } + public function testPostSetDataRequiresArrayOrTraversable() + { + $this->expectException(UnexpectedTypeException::class); + $data = 'no array or traversable'; + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); + $listener->postSetData($event); + } + + /** + * @group legacy + */ public function testPreSetDataDealsWithNullData() { $data = null; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener(TextType::class, [], false, false); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new class(TextType::class, [], false, false) extends ResizeFormListener { + public function preSetData(FormEvent $event): void + { + parent::preSetData($event); + } + }; $listener->preSetData($event); - $this->assertSame(0, $this->form->count()); + $this->assertSame(0, $this->builder->count()); + } + + public function testPostSetDataDealsWithNullData() + { + $data = null; + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); + $listener->postSetData($event); + + $this->assertSame(0, $this->builder->count()); } public function testPreSubmitResizesUpIfAllowAdd() { - $this->form->add($this->getForm('0')); + $this->builder->add($this->getBuilder('0')); + $this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], true, false)); - $data = [0 => 'string', 1 => 'string']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener(TextType::class, ['attr' => ['maxlength' => 10]], true, false); - $listener->preSubmit($event); + $form = $this->builder->getForm(); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); - $this->assertTrue($this->form->has('0')); - $this->assertTrue($this->form->has('1')); + $form->submit([0 => 'string', 1 => 'string']); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); } public function testPreSubmitResizesDownIfAllowDelete() { - $this->form->add($this->getForm('0')); - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('0')); + $this->builder->add($this->getBuilder('1')); + $this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, [], false, true)); - $data = [0 => 'string']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); - $listener->preSubmit($event); + $form = $this->builder->getForm(); + // initialize the form + $form->setData([0 => 'string', 1 => 'string']); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); - $this->assertTrue($this->form->has('0')); - $this->assertFalse($this->form->has('1')); + $form->submit([0 => 'string']); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); } // fix for https://github.com/symfony/symfony/pull/493 public function testPreSubmitRemovesZeroKeys() { - $this->form->add($this->getForm('0')); + $this->builder->add($this->getBuilder('0')); $data = []; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->preSubmit($event); - $this->assertFalse($this->form->has('0')); + $this->assertFalse($form->has('0')); } public function testPreSubmitDoesNothingIfNotAllowAddNorAllowDelete() { - $this->form->add($this->getForm('0')); - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('0')); + $this->builder->add($this->getBuilder('1')); $data = [0 => 'string', 2 => 'string']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, false); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); $listener->preSubmit($event); - $this->assertTrue($this->form->has('0')); - $this->assertTrue($this->form->has('1')); - $this->assertFalse($this->form->has('2')); + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertFalse($form->has('2')); } public function testPreSubmitDealsWithNoArrayOrTraversable() { $data = 'no array or traversable'; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, false); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); $listener->preSubmit($event); - $this->assertFalse($this->form->has('1')); + $this->assertFalse($form->has('1')); } public function testPreSubmitDealsWithNullData() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = null; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->preSubmit($event); - $this->assertFalse($this->form->has('1')); + $this->assertFalse($form->has('1')); } // fixes https://github.com/symfony/symfony/pull/40 public function testPreSubmitDealsWithEmptyData() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = ''; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->preSubmit($event); - $this->assertFalse($this->form->has('1')); + $this->assertFalse($form->has('1')); } public function testOnSubmitNormDataRemovesEntriesMissingInTheFormIfAllowDelete() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = [0 => 'first', 1 => 'second', 2 => 'third']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->onSubmit($event); $this->assertEquals([1 => 'second'], $event->getData()); @@ -186,11 +265,12 @@ public function testOnSubmitNormDataRemovesEntriesMissingInTheFormIfAllowDelete( public function testOnSubmitNormDataDoesNothingIfNotAllowDelete() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = [0 => 'first', 1 => 'second', 2 => 'third']; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, false); + $form = $this->builder->getForm(); + $event = new FormEvent($form, $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); $listener->onSubmit($event); $this->assertEquals($data, $event->getData()); @@ -200,18 +280,18 @@ public function testOnSubmitNormDataRequiresArrayOrTraversable() { $this->expectException(UnexpectedTypeException::class); $data = 'no array or traversable'; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, false); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, false); $listener->onSubmit($event); } public function testOnSubmitNormDataDealsWithNullData() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = null; - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->onSubmit($event); $this->assertEquals([], $event->getData()); @@ -219,11 +299,11 @@ public function testOnSubmitNormDataDealsWithNullData() public function testOnSubmitDealsWithObjectBackedIteratorAggregate() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = new \ArrayObject([0 => 'first', 1 => 'second', 2 => 'third']); - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->onSubmit($event); $this->assertArrayNotHasKey(0, $event->getData()); @@ -232,11 +312,11 @@ public function testOnSubmitDealsWithObjectBackedIteratorAggregate() public function testOnSubmitDealsWithArrayBackedIteratorAggregate() { - $this->form->add($this->getForm('1')); + $this->builder->add($this->getBuilder('1')); $data = new ArrayCollection([0 => 'first', 1 => 'second', 2 => 'third']); - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true); + $event = new FormEvent($this->builder->getForm(), $data); + $listener = new ResizeFormListener(TextType::class, [], false, true); $listener->onSubmit($event); $this->assertArrayNotHasKey(0, $event->getData()); @@ -245,46 +325,37 @@ public function testOnSubmitDealsWithArrayBackedIteratorAggregate() public function testOnSubmitDeleteEmptyNotCompoundEntriesIfAllowDelete() { - $this->form->setData(['0' => 'first', '1' => 'second']); - $this->form->add($this->getForm('0')); - $this->form->add($this->getForm('1')); - - $data = [0 => 'first', 1 => '']; - foreach ($data as $child => $dat) { - $this->form->get($child)->submit($dat); - } - $event = new FormEvent($this->form, $data); - $listener = new ResizeFormListener('text', [], false, true, true); - $listener->onSubmit($event); + $this->builder->setData(['0' => 'first', '1' => 'second']); + $this->builder->add($this->getBuilder('0')); + $this->builder->add($this->getBuilder('1')); + $this->builder->addEventSubscriber(new ResizeFormListener(TextType::class, [], false, true, true)); - $this->assertEquals([0 => 'first'], $event->getData()); + $form = $this->builder->getForm(); + + $form->submit([0 => 'first', 1 => '']); + + $this->assertEquals([0 => 'first'], $form->getData()); } public function testOnSubmitDeleteEmptyCompoundEntriesIfAllowDelete() { - $this->form->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]); - $form1 = $this->getBuilder('0') - ->setCompound(true) - ->setDataMapper(new DataMapper()) - ->getForm(); - $form1->add($this->getForm('name')); - $form2 = $this->getBuilder('1') - ->setCompound(true) - ->setDataMapper(new DataMapper()) - ->getForm(); - $form2->add($this->getForm('name')); - $this->form->add($form1); - $this->form->add($form2); - - $data = ['0' => ['name' => 'John'], '1' => ['name' => '']]; - foreach ($data as $child => $dat) { - $this->form->get($child)->submit($dat); - } - $event = new FormEvent($this->form, $data); - $callback = fn ($data) => null === $data['name']; - $listener = new ResizeFormListener('text', [], false, true, $callback); - $listener->onSubmit($event); + $this->builder->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]); + $this->builder->add('0', NestedType::class); + $this->builder->add('1', NestedType::class); + $callback = fn ($data) => empty($data['name']); + $this->builder->addEventSubscriber(new ResizeFormListener(NestedType::class, [], false, true, $callback)); + + $form = $this->builder->getForm(); + $form->submit(['0' => ['name' => 'John'], '1' => ['name' => '']]); - $this->assertEquals(['0' => ['name' => 'John']], $event->getData()); + $this->assertEquals(['0' => ['name' => 'John']], $form->getData()); + } +} + +class NestedType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('name'); } } From 04cf304af17de3da89851040b6dc6386e4cade86 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 14 Oct 2024 20:03:05 +0200 Subject: [PATCH 29/41] Reduce common control flows --- Extension/DataCollector/FormDataExtractor.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Extension/DataCollector/FormDataExtractor.php b/Extension/DataCollector/FormDataExtractor.php index 158cf3210..f56fe911f 100644 --- a/Extension/DataCollector/FormDataExtractor.php +++ b/Extension/DataCollector/FormDataExtractor.php @@ -103,15 +103,13 @@ public function extractSubmittedData(FormInterface $form): array continue; } + $errorData['trace'][] = $cause; if ($cause instanceof \Exception) { - $errorData['trace'][] = $cause; $cause = $cause->getPrevious(); continue; } - $errorData['trace'][] = $cause; - break; } From 73c00ef4ced12b43647a37c184f05650c636a539 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 18 Oct 2024 16:04:52 +0200 Subject: [PATCH 30/41] Remove always true/false occurrences --- .../Core/DataTransformer/DateIntervalToArrayTransformer.php | 2 +- Extension/Core/Type/DateType.php | 4 +--- Extension/Core/Type/TimeType.php | 4 +--- Extension/Core/Type/WeekType.php | 4 +--- Extension/Validator/Constraints/FormValidator.php | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php index 18679a910..8591db191 100644 --- a/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php +++ b/Extension/Core/DataTransformer/DateIntervalToArrayTransformer.php @@ -139,7 +139,7 @@ public function reverseTransform(mixed $value): ?\DateInterval 'P%sY%sM%sWT%sH%sM%sS', empty($value['years']) ? '0' : $value['years'], empty($value['months']) ? '0' : $value['months'], - empty($value['weeks']) ? '0' : $value['weeks'], + $value['weeks'], empty($value['hours']) ? '0' : $value['hours'], empty($value['minutes']) ? '0' : $value['minutes'], empty($value['seconds']) ? '0' : $value['seconds'] diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index 54f24b89a..e4aee6e8d 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -249,10 +249,8 @@ public function configureOptions(OptionsResolver $resolver): void $choiceTranslationDomainNormalizer = static function (Options $options, $choiceTranslationDomain) { if (\is_array($choiceTranslationDomain)) { - $default = false; - return array_replace( - ['year' => $default, 'month' => $default, 'day' => $default], + ['year' => false, 'month' => false, 'day' => false], $choiceTranslationDomain ); } diff --git a/Extension/Core/Type/TimeType.php b/Extension/Core/Type/TimeType.php index 35549b041..92cf42d96 100644 --- a/Extension/Core/Type/TimeType.php +++ b/Extension/Core/Type/TimeType.php @@ -274,10 +274,8 @@ public function configureOptions(OptionsResolver $resolver): void $choiceTranslationDomainNormalizer = static function (Options $options, $choiceTranslationDomain) { if (\is_array($choiceTranslationDomain)) { - $default = false; - return array_replace( - ['hour' => $default, 'minute' => $default, 'second' => $default], + ['hour' => false, 'minute' => false, 'second' => false], $choiceTranslationDomain ); } diff --git a/Extension/Core/Type/WeekType.php b/Extension/Core/Type/WeekType.php index ec5cc9303..aea4a6c56 100644 --- a/Extension/Core/Type/WeekType.php +++ b/Extension/Core/Type/WeekType.php @@ -113,10 +113,8 @@ public function configureOptions(OptionsResolver $resolver): void $choiceTranslationDomainNormalizer = static function (Options $options, $choiceTranslationDomain) { if (\is_array($choiceTranslationDomain)) { - $default = false; - return array_replace( - ['year' => $default, 'week' => $default], + ['year' => false, 'week' => false], $choiceTranslationDomain ); } diff --git a/Extension/Validator/Constraints/FormValidator.php b/Extension/Validator/Constraints/FormValidator.php index 703bac148..8f4ec60f2 100644 --- a/Extension/Validator/Constraints/FormValidator.php +++ b/Extension/Validator/Constraints/FormValidator.php @@ -56,7 +56,7 @@ public function validate(mixed $form, Constraint $formConstraint): void // Validate the data against its own constraints $validateDataGraph = $form->isRoot() && (\is_object($data) || \is_array($data)) - && (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups)) + && (\is_array($groups) || ($groups instanceof GroupSequence && $groups->groups)) ; // Validate the data against the constraints defined in the form From 264cff30f52f12149aff92bbc23e78160a45c2f3 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 27 Nov 2024 12:55:00 +0100 Subject: [PATCH 31/41] [Form] Allow integer for the `calendar` option of `DateType` --- Extension/Core/Type/DateType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/Core/Type/DateType.php b/Extension/Core/Type/DateType.php index e4aee6e8d..36b430e14 100644 --- a/Extension/Core/Type/DateType.php +++ b/Extension/Core/Type/DateType.php @@ -313,7 +313,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('months', 'array'); $resolver->setAllowedTypes('days', 'array'); $resolver->setAllowedTypes('input_format', 'string'); - $resolver->setAllowedTypes('calendar', ['null', \IntlCalendar::class]); + $resolver->setAllowedTypes('calendar', ['null', 'int', \IntlCalendar::class]); $resolver->setInfo('calendar', 'The calendar to use for formatting and parsing the date. The value should be an instance of \IntlCalendar. By default, the Gregorian calendar with the default locale is used.'); From 092a89345db25f8e4fc1804a4d3f184766e36e69 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 24 Dec 2024 13:01:45 +0100 Subject: [PATCH 32/41] do not render hidden CSRF token forms with autocomplete set to off --- Extension/Csrf/Type/FormTypeCsrfExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/Csrf/Type/FormTypeCsrfExtension.php b/Extension/Csrf/Type/FormTypeCsrfExtension.php index 10367ae5f..1cb2b0342 100644 --- a/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -76,7 +76,7 @@ public function finishView(FormView $view, FormInterface $form, array $options): $csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [ 'block_prefix' => 'csrf_token', 'mapped' => false, - 'attr' => $this->fieldAttr + ['autocomplete' => 'off'], + 'attr' => $this->fieldAttr, ]); $view->children[$options['csrf_field_name']] = $csrfForm->createView($view); From 7209804c018b88cc2b0beabe38eef91b83f1d69a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 7 Feb 2025 19:13:17 +0100 Subject: [PATCH 33/41] [Form][FrameworkBundle] Use auto-configuration to make the default CSRF token id apply only to the app; not to bundles --- DependencyInjection/FormPass.php | 13 ++++++++++++ Extension/Csrf/Type/FormTypeCsrfExtension.php | 16 +++++++++++---- Tests/DependencyInjection/FormPassTest.php | 20 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/FormPass.php b/DependencyInjection/FormPass.php index 1d2b2a87e..bec1782d4 100644 --- a/DependencyInjection/FormPass.php +++ b/DependencyInjection/FormPass.php @@ -47,6 +47,7 @@ private function processFormTypes(ContainerBuilder $container): Reference // Get service locator argument $servicesMap = []; $namespaces = ['Symfony\Component\Form\Extension\Core\Type' => true]; + $csrfTokenIds = []; // Builds an array with fully-qualified type class names as keys and service IDs as values foreach ($container->findTaggedServiceIds('form.type', true) as $serviceId => $tag) { @@ -54,6 +55,10 @@ private function processFormTypes(ContainerBuilder $container): Reference $serviceDefinition = $container->getDefinition($serviceId); $servicesMap[$formType = $serviceDefinition->getClass()] = new Reference($serviceId); $namespaces[substr($formType, 0, strrpos($formType, '\\'))] = true; + + if (isset($tag[0]['csrf_token_id'])) { + $csrfTokenIds[$formType] = $tag[0]['csrf_token_id']; + } } if ($container->hasDefinition('console.command.form_debug')) { @@ -62,6 +67,14 @@ private function processFormTypes(ContainerBuilder $container): Reference $commandDefinition->setArgument(2, array_keys($servicesMap)); } + if ($csrfTokenIds && $container->hasDefinition('form.type_extension.csrf')) { + $csrfExtension = $container->getDefinition('form.type_extension.csrf'); + + if (8 <= \count($csrfExtension->getArguments())) { + $csrfExtension->replaceArgument(7, $csrfTokenIds); + } + } + return ServiceLocatorTagPass::register($container, $servicesMap); } diff --git a/Extension/Csrf/Type/FormTypeCsrfExtension.php b/Extension/Csrf/Type/FormTypeCsrfExtension.php index 1cb2b0342..a12b9a41e 100644 --- a/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -37,7 +37,7 @@ public function __construct( private ?string $translationDomain = null, private ?ServerParams $serverParams = null, private array $fieldAttr = [], - private ?string $defaultTokenId = null, + private string|array|null $defaultTokenId = null, ) { } @@ -50,11 +50,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void return; } + $csrfTokenId = $options['csrf_token_id'] + ?: $this->defaultTokenId[$builder->getType()->getInnerType()::class] + ?? $builder->getName() + ?: $builder->getType()->getInnerType()::class; + $builder->setAttribute('csrf_token_id', $csrfTokenId); + $builder ->addEventSubscriber(new CsrfValidationListener( $options['csrf_field_name'], $options['csrf_token_manager'], - $options['csrf_token_id'] ?: ($builder->getName() ?: $builder->getType()->getInnerType()::class), + $csrfTokenId, $options['csrf_message'], $this->translator, $this->translationDomain, @@ -70,7 +76,7 @@ public function finishView(FormView $view, FormInterface $form, array $options): { if ($options['csrf_protection'] && !$view->parent && $options['compound']) { $factory = $form->getConfig()->getFormFactory(); - $tokenId = $options['csrf_token_id'] ?: ($form->getName() ?: $form->getConfig()->getType()->getInnerType()::class); + $tokenId = $form->getConfig()->getAttribute('csrf_token_id'); $data = (string) $options['csrf_token_manager']->getToken($tokenId); $csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [ @@ -85,9 +91,11 @@ public function finishView(FormView $view, FormInterface $form, array $options): public function configureOptions(OptionsResolver $resolver): void { - if ($defaultTokenId = $this->defaultTokenId) { + if (\is_string($defaultTokenId = $this->defaultTokenId) && $defaultTokenId) { $defaultTokenManager = $this->defaultTokenManager; $defaultTokenId = static fn (Options $options) => $options['csrf_token_manager'] === $defaultTokenManager ? $defaultTokenId : null; + } else { + $defaultTokenId = null; } $resolver->setDefaults([ diff --git a/Tests/DependencyInjection/FormPassTest.php b/Tests/DependencyInjection/FormPassTest.php index e9a7b5034..f0ccd3f09 100644 --- a/Tests/DependencyInjection/FormPassTest.php +++ b/Tests/DependencyInjection/FormPassTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Command\DebugCommand; use Symfony\Component\Form\DependencyInjection\FormPass; +use Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension; use Symfony\Component\Form\FormRegistry; /** @@ -95,6 +96,25 @@ public function testAddTaggedTypesToDebugCommand() ); } + public function testAddTaggedTypesToCsrfTypeExtension() + { + $container = $this->createContainerBuilder(); + + $container->register('form.registry', FormRegistry::class); + $container->register('form.type_extension.csrf', FormTypeCsrfExtension::class) + ->setArguments([null, true, '_token', null, 'validator.translation_domain', null, [], null]) + ->setPublic(true); + + $container->setDefinition('form.extension', $this->createExtensionDefinition()); + $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type', ['csrf_token_id' => 'the_token_id']); + $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type'); + + $container->compile(); + + $csrfDefinition = $container->getDefinition('form.type_extension.csrf'); + $this->assertSame([__CLASS__.'_Type1' => 'the_token_id'], $csrfDefinition->getArgument(7)); + } + /** * @dataProvider addTaggedTypeExtensionsDataProvider */ From e982e148bdaa5e0d70087237ef059528cd8be6d9 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Mon, 17 Mar 2025 01:07:55 -0300 Subject: [PATCH 34/41] fix translation in spanish --- Resources/translations/validators.es.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/translations/validators.es.xlf b/Resources/translations/validators.es.xlf index 301e2b33f..a9989737c 100644 --- a/Resources/translations/validators.es.xlf +++ b/Resources/translations/validators.es.xlf @@ -52,7 +52,7 @@ Please enter a valid date. - Por favor, ingrese una fecha valida. + Por favor, ingrese una fecha válida. Please select a valid file. From 3929e2a60a828f39df6765fb49d224cc629fa529 Mon Sep 17 00:00:00 2001 From: Alexander Hofbauer Date: Wed, 26 Mar 2025 17:02:02 +0100 Subject: [PATCH 35/41] [Form] Use duplicate_preferred_choices to set value of ChoiceType When the preferred choices are not duplicated an option has to be selected in the group of preferred choices. Closes #58561 --- Extension/Core/Type/ChoiceType.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Extension/Core/Type/ChoiceType.php b/Extension/Core/Type/ChoiceType.php index 35dcf1b1b..32bc67766 100644 --- a/Extension/Core/Type/ChoiceType.php +++ b/Extension/Core/Type/ChoiceType.php @@ -281,6 +281,8 @@ public function buildView(FormView $view, FormInterface $form, array $options) */ public function finishView(FormView $view, FormInterface $form, array $options) { + $view->vars['duplicate_preferred_choices'] = $options['duplicate_preferred_choices']; + if ($options['expanded']) { // Radio buttons should have the same name as the parent $childName = $view->vars['full_name']; From 58471f52a058d64b5d128c26695191039b6199b5 Mon Sep 17 00:00:00 2001 From: Tom Hart <1374434+TomHart@users.noreply.github.com> Date: Fri, 4 Apr 2025 10:13:44 +0100 Subject: [PATCH 36/41] Update validators.pt.xlf --- Resources/translations/validators.pt.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/translations/validators.pt.xlf b/Resources/translations/validators.pt.xlf index 755108f35..673e79f42 100644 --- a/Resources/translations/validators.pt.xlf +++ b/Resources/translations/validators.pt.xlf @@ -24,7 +24,7 @@ The selected choice is invalid. - A escolha seleccionada é inválida. + A escolha selecionada é inválida. The collection is invalid. From c87b96dcb7e22673c389f5d06a8a2bb916cc26c4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 8 Apr 2025 16:34:28 +0200 Subject: [PATCH 37/41] skip test if the installed ICU version is too modern --- .../DateTimeToLocalizedStringTransformerTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 189c409f4..91b3cf213 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; use Symfony\Component\Form\Tests\Extension\Core\DataTransformer\Traits\DateTimeEqualsTrait; +use Symfony\Component\Intl\Intl; use Symfony\Component\Intl\Util\IntlTestHelper; class DateTimeToLocalizedStringTransformerTest extends BaseDateTimeTransformerTestCase @@ -236,6 +237,10 @@ public function testReverseTransformFullTime() public function testReverseTransformFromDifferentLocale() { + if (version_compare(Intl::getIcuVersion(), '71.1', '>')) { + $this->markTestSkipped('ICU version 71.1 or lower is required.'); + }; + \Locale::setDefault('en_US'); $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); From 48c8d9bb1f891a84904f1e7f53e876697776fdfc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 27 Apr 2025 15:26:02 +0200 Subject: [PATCH 38/41] Remove unneeded use statements --- Extension/Core/Type/TimeType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/Core/Type/TimeType.php b/Extension/Core/Type/TimeType.php index 512a830bb..4bd1a9433 100644 --- a/Extension/Core/Type/TimeType.php +++ b/Extension/Core/Type/TimeType.php @@ -12,13 +12,13 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Event\PreSubmitEvent; use Symfony\Component\Form\Exception\InvalidConfigurationException; use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; -use Symfony\Component\Form\Event\PreSubmitEvent; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; From de5c3d7dbb5538aba1476ed34f0197e501c06a39 Mon Sep 17 00:00:00 2001 From: wkania Date: Sun, 27 Apr 2025 15:58:34 +0200 Subject: [PATCH 39/41] Unnecessary cast, return, semicolon and comma --- .../DateTimeToLocalizedStringTransformerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 91b3cf213..6cbf6b937 100644 --- a/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -239,7 +239,7 @@ public function testReverseTransformFromDifferentLocale() { if (version_compare(Intl::getIcuVersion(), '71.1', '>')) { $this->markTestSkipped('ICU version 71.1 or lower is required.'); - }; + } \Locale::setDefault('en_US'); From 02275ed54317b830fc54f7be68bb2f1172cdfc43 Mon Sep 17 00:00:00 2001 From: wkania Date: Sun, 27 Apr 2025 16:24:15 +0200 Subject: [PATCH 40/41] Fix overwriting an array element --- Extension/Core/Type/WeekType.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Extension/Core/Type/WeekType.php b/Extension/Core/Type/WeekType.php index 8027a41a9..778cc2aeb 100644 --- a/Extension/Core/Type/WeekType.php +++ b/Extension/Core/Type/WeekType.php @@ -42,7 +42,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) } else { $yearOptions = $weekOptions = [ 'error_bubbling' => true, - 'empty_data' => '', ]; // when the form is compound the entries of the array are ignored in favor of children data // so we need to handle the cascade setting here From aed57cbb5ffe9fe169cf48c7fdcb2d482670ae5e Mon Sep 17 00:00:00 2001 From: wkania Date: Sun, 27 Apr 2025 18:08:38 +0200 Subject: [PATCH 41/41] Fix return type is non-nullable --- Tests/Fixtures/TestExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Fixtures/TestExtension.php b/Tests/Fixtures/TestExtension.php index 44725a69c..2704ee530 100644 --- a/Tests/Fixtures/TestExtension.php +++ b/Tests/Fixtures/TestExtension.php @@ -34,7 +34,7 @@ public function addType(FormTypeInterface $type) public function getType($name): FormTypeInterface { - return $this->types[$name] ?? null; + return $this->types[$name]; } public function hasType($name): bool