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

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