vendor/symfony/form/Extension/Validator/ValidatorTypeGuesser.php line 67

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Form\Extension\Validator;
  11. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  12. use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  13. use Symfony\Component\Form\Extension\Core\Type\CountryType;
  14. use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
  15. use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
  16. use Symfony\Component\Form\Extension\Core\Type\DateType;
  17. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  18. use Symfony\Component\Form\Extension\Core\Type\FileType;
  19. use Symfony\Component\Form\Extension\Core\Type\IntegerType;
  20. use Symfony\Component\Form\Extension\Core\Type\LanguageType;
  21. use Symfony\Component\Form\Extension\Core\Type\LocaleType;
  22. use Symfony\Component\Form\Extension\Core\Type\NumberType;
  23. use Symfony\Component\Form\Extension\Core\Type\TextType;
  24. use Symfony\Component\Form\Extension\Core\Type\TimeType;
  25. use Symfony\Component\Form\Extension\Core\Type\UrlType;
  26. use Symfony\Component\Form\FormTypeGuesserInterface;
  27. use Symfony\Component\Form\Guess\Guess;
  28. use Symfony\Component\Form\Guess\TypeGuess;
  29. use Symfony\Component\Form\Guess\ValueGuess;
  30. use Symfony\Component\Validator\Constraint;
  31. use Symfony\Component\Validator\Constraints\Count;
  32. use Symfony\Component\Validator\Constraints\Country;
  33. use Symfony\Component\Validator\Constraints\Currency;
  34. use Symfony\Component\Validator\Constraints\Date;
  35. use Symfony\Component\Validator\Constraints\DateTime;
  36. use Symfony\Component\Validator\Constraints\Email;
  37. use Symfony\Component\Validator\Constraints\File;
  38. use Symfony\Component\Validator\Constraints\Image;
  39. use Symfony\Component\Validator\Constraints\Ip;
  40. use Symfony\Component\Validator\Constraints\IsFalse;
  41. use Symfony\Component\Validator\Constraints\IsTrue;
  42. use Symfony\Component\Validator\Constraints\Language;
  43. use Symfony\Component\Validator\Constraints\Length;
  44. use Symfony\Component\Validator\Constraints\Locale;
  45. use Symfony\Component\Validator\Constraints\NotBlank;
  46. use Symfony\Component\Validator\Constraints\NotNull;
  47. use Symfony\Component\Validator\Constraints\Range;
  48. use Symfony\Component\Validator\Constraints\Regex;
  49. use Symfony\Component\Validator\Constraints\Time;
  50. use Symfony\Component\Validator\Constraints\Type;
  51. use Symfony\Component\Validator\Constraints\Url;
  52. use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
  53. use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
  54. class ValidatorTypeGuesser implements FormTypeGuesserInterface
  55. {
  56.     private MetadataFactoryInterface $metadataFactory;
  57.     public function __construct(MetadataFactoryInterface $metadataFactory)
  58.     {
  59.         $this->metadataFactory $metadataFactory;
  60.     }
  61.     public function guessType(string $classstring $property): ?TypeGuess
  62.     {
  63.         return $this->guess($class$property, function (Constraint $constraint) {
  64.             return $this->guessTypeForConstraint($constraint);
  65.         });
  66.     }
  67.     public function guessRequired(string $classstring $property): ?ValueGuess
  68.     {
  69.         return $this->guess($class$property, function (Constraint $constraint) {
  70.             return $this->guessRequiredForConstraint($constraint);
  71.         // If we don't find any constraint telling otherwise, we can assume
  72.         // that a field is not required (with LOW_CONFIDENCE)
  73.         }, false);
  74.     }
  75.     public function guessMaxLength(string $classstring $property): ?ValueGuess
  76.     {
  77.         return $this->guess($class$property, function (Constraint $constraint) {
  78.             return $this->guessMaxLengthForConstraint($constraint);
  79.         });
  80.     }
  81.     public function guessPattern(string $classstring $property): ?ValueGuess
  82.     {
  83.         return $this->guess($class$property, function (Constraint $constraint) {
  84.             return $this->guessPatternForConstraint($constraint);
  85.         });
  86.     }
  87.     /**
  88.      * Guesses a field class name for a given constraint.
  89.      */
  90.     public function guessTypeForConstraint(Constraint $constraint): ?TypeGuess
  91.     {
  92.         switch ($constraint::class) {
  93.             case Type::class:
  94.                 switch ($constraint->type) {
  95.                     case 'array':
  96.                         return new TypeGuess(CollectionType::class, [], Guess::MEDIUM_CONFIDENCE);
  97.                     case 'boolean':
  98.                     case 'bool':
  99.                         return new TypeGuess(CheckboxType::class, [], Guess::MEDIUM_CONFIDENCE);
  100.                     case 'double':
  101.                     case 'float':
  102.                     case 'numeric':
  103.                     case 'real':
  104.                         return new TypeGuess(NumberType::class, [], Guess::MEDIUM_CONFIDENCE);
  105.                     case 'integer':
  106.                     case 'int':
  107.                     case 'long':
  108.                         return new TypeGuess(IntegerType::class, [], Guess::MEDIUM_CONFIDENCE);
  109.                     case \DateTime::class:
  110.                     case '\DateTime':
  111.                         return new TypeGuess(DateType::class, [], Guess::MEDIUM_CONFIDENCE);
  112.                     case 'string':
  113.                         return new TypeGuess(TextType::class, [], Guess::LOW_CONFIDENCE);
  114.                 }
  115.                 break;
  116.             case Country::class:
  117.                 return new TypeGuess(CountryType::class, [], Guess::HIGH_CONFIDENCE);
  118.             case Currency::class:
  119.                 return new TypeGuess(CurrencyType::class, [], Guess::HIGH_CONFIDENCE);
  120.             case Date::class:
  121.                 return new TypeGuess(DateType::class, ['input' => 'string'], Guess::HIGH_CONFIDENCE);
  122.             case DateTime::class:
  123.                 return new TypeGuess(DateTimeType::class, ['input' => 'string'], Guess::HIGH_CONFIDENCE);
  124.             case Email::class:
  125.                 return new TypeGuess(EmailType::class, [], Guess::HIGH_CONFIDENCE);
  126.             case File::class:
  127.             case Image::class:
  128.                 $options = [];
  129.                 if ($constraint->mimeTypes) {
  130.                     $options = ['attr' => ['accept' => implode(',', (array) $constraint->mimeTypes)]];
  131.                 }
  132.                 return new TypeGuess(FileType::class, $optionsGuess::HIGH_CONFIDENCE);
  133.             case Language::class:
  134.                 return new TypeGuess(LanguageType::class, [], Guess::HIGH_CONFIDENCE);
  135.             case Locale::class:
  136.                 return new TypeGuess(LocaleType::class, [], Guess::HIGH_CONFIDENCE);
  137.             case Time::class:
  138.                 return new TypeGuess(TimeType::class, ['input' => 'string'], Guess::HIGH_CONFIDENCE);
  139.             case Url::class:
  140.                 return new TypeGuess(UrlType::class, [], Guess::HIGH_CONFIDENCE);
  141.             case Ip::class:
  142.                 return new TypeGuess(TextType::class, [], Guess::MEDIUM_CONFIDENCE);
  143.             case Length::class:
  144.             case Regex::class:
  145.                 return new TypeGuess(TextType::class, [], Guess::LOW_CONFIDENCE);
  146.             case Range::class:
  147.                 return new TypeGuess(NumberType::class, [], Guess::LOW_CONFIDENCE);
  148.             case Count::class:
  149.                 return new TypeGuess(CollectionType::class, [], Guess::LOW_CONFIDENCE);
  150.             case IsTrue::class:
  151.             case IsFalse::class:
  152.                 return new TypeGuess(CheckboxType::class, [], Guess::MEDIUM_CONFIDENCE);
  153.         }
  154.         return null;
  155.     }
  156.     /**
  157.      * Guesses whether a field is required based on the given constraint.
  158.      */
  159.     public function guessRequiredForConstraint(Constraint $constraint): ?ValueGuess
  160.     {
  161.         return match ($constraint::class) {
  162.             NotNull::class,
  163.             NotBlank::class,
  164.             IsTrue::class => new ValueGuess(trueGuess::HIGH_CONFIDENCE),
  165.             default => null,
  166.         };
  167.     }
  168.     /**
  169.      * Guesses a field's maximum length based on the given constraint.
  170.      */
  171.     public function guessMaxLengthForConstraint(Constraint $constraint): ?ValueGuess
  172.     {
  173.         switch ($constraint::class) {
  174.             case Length::class:
  175.                 if (is_numeric($constraint->max)) {
  176.                     return new ValueGuess($constraint->maxGuess::HIGH_CONFIDENCE);
  177.                 }
  178.                 break;
  179.             case Type::class:
  180.                 if (\in_array($constraint->type, ['double''float''numeric''real'])) {
  181.                     return new ValueGuess(nullGuess::MEDIUM_CONFIDENCE);
  182.                 }
  183.                 break;
  184.             case Range::class:
  185.                 if (is_numeric($constraint->max)) {
  186.                     return new ValueGuess(\strlen((string) $constraint->max), Guess::LOW_CONFIDENCE);
  187.                 }
  188.                 break;
  189.         }
  190.         return null;
  191.     }
  192.     /**
  193.      * Guesses a field's pattern based on the given constraint.
  194.      */
  195.     public function guessPatternForConstraint(Constraint $constraint): ?ValueGuess
  196.     {
  197.         switch ($constraint::class) {
  198.             case Length::class:
  199.                 if (is_numeric($constraint->min)) {
  200.                     return new ValueGuess(sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE);
  201.                 }
  202.                 break;
  203.             case Regex::class:
  204.                 $htmlPattern $constraint->getHtmlPattern();
  205.                 if (null !== $htmlPattern) {
  206.                     return new ValueGuess($htmlPatternGuess::HIGH_CONFIDENCE);
  207.                 }
  208.                 break;
  209.             case Range::class:
  210.                 if (is_numeric($constraint->min)) {
  211.                     return new ValueGuess(sprintf('.{%s,}'\strlen((string) $constraint->min)), Guess::LOW_CONFIDENCE);
  212.                 }
  213.                 break;
  214.             case Type::class:
  215.                 if (\in_array($constraint->type, ['double''float''numeric''real'])) {
  216.                     return new ValueGuess(nullGuess::MEDIUM_CONFIDENCE);
  217.                 }
  218.                 break;
  219.         }
  220.         return null;
  221.     }
  222.     /**
  223.      * Iterates over the constraints of a property, executes a constraints on
  224.      * them and returns the best guess.
  225.      *
  226.      * @param \Closure $closure      The closure that returns a guess
  227.      *                               for a given constraint
  228.      * @param mixed    $defaultValue The default value assumed if no other value
  229.      *                               can be guessed
  230.      */
  231.     protected function guess(string $classstring $property\Closure $closuremixed $defaultValue null): ?Guess
  232.     {
  233.         $guesses = [];
  234.         $classMetadata $this->metadataFactory->getMetadataFor($class);
  235.         if ($classMetadata instanceof ClassMetadataInterface && $classMetadata->hasPropertyMetadata($property)) {
  236.             foreach ($classMetadata->getPropertyMetadata($property) as $memberMetadata) {
  237.                 foreach ($memberMetadata->getConstraints() as $constraint) {
  238.                     if ($guess $closure($constraint)) {
  239.                         $guesses[] = $guess;
  240.                     }
  241.                 }
  242.             }
  243.         }
  244.         if (null !== $defaultValue) {
  245.             $guesses[] = new ValueGuess($defaultValueGuess::LOW_CONFIDENCE);
  246.         }
  247.         return Guess::getBestGuess($guesses);
  248.     }
  249. }