vendor/symfony/validator/ValidatorBuilder.php line 340

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\Validator;
  11. use Doctrine\Common\Annotations\AnnotationReader;
  12. use Doctrine\Common\Annotations\PsrCachedReader;
  13. use Doctrine\Common\Annotations\Reader;
  14. use Psr\Cache\CacheItemPoolInterface;
  15. use Symfony\Component\Cache\Adapter\ArrayAdapter;
  16. use Symfony\Component\Validator\Context\ExecutionContextFactory;
  17. use Symfony\Component\Validator\Exception\LogicException;
  18. use Symfony\Component\Validator\Exception\ValidatorException;
  19. use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
  20. use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
  21. use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
  22. use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
  23. use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
  24. use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
  25. use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader;
  26. use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader;
  27. use Symfony\Component\Validator\Validator\RecursiveValidator;
  28. use Symfony\Component\Validator\Validator\ValidatorInterface;
  29. use Symfony\Contracts\Translation\LocaleAwareInterface;
  30. use Symfony\Contracts\Translation\TranslatorInterface;
  31. use Symfony\Contracts\Translation\TranslatorTrait;
  32. // Help opcache.preload discover always-needed symbols
  33. class_exists(TranslatorInterface::class);
  34. class_exists(LocaleAwareInterface::class);
  35. class_exists(TranslatorTrait::class);
  36. /**
  37.  * @author Bernhard Schussek <bschussek@gmail.com>
  38.  */
  39. class ValidatorBuilder
  40. {
  41.     private array $initializers = [];
  42.     private array $loaders = [];
  43.     private array $xmlMappings = [];
  44.     private array $yamlMappings = [];
  45.     private array $methodMappings = [];
  46.     private ?Reader $annotationReader null;
  47.     private bool $enableAnnotationMapping false;
  48.     private ?MetadataFactoryInterface $metadataFactory null;
  49.     private ConstraintValidatorFactoryInterface $validatorFactory;
  50.     private ?CacheItemPoolInterface $mappingCache null;
  51.     private ?TranslatorInterface $translator null;
  52.     private ?string $translationDomain null;
  53.     /**
  54.      * Adds an object initializer to the validator.
  55.      *
  56.      * @return $this
  57.      */
  58.     public function addObjectInitializer(ObjectInitializerInterface $initializer): static
  59.     {
  60.         $this->initializers[] = $initializer;
  61.         return $this;
  62.     }
  63.     /**
  64.      * Adds a list of object initializers to the validator.
  65.      *
  66.      * @param ObjectInitializerInterface[] $initializers
  67.      *
  68.      * @return $this
  69.      */
  70.     public function addObjectInitializers(array $initializers): static
  71.     {
  72.         $this->initializers array_merge($this->initializers$initializers);
  73.         return $this;
  74.     }
  75.     /**
  76.      * Adds an XML constraint mapping file to the validator.
  77.      *
  78.      * @return $this
  79.      */
  80.     public function addXmlMapping(string $path): static
  81.     {
  82.         if (null !== $this->metadataFactory) {
  83.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  84.         }
  85.         $this->xmlMappings[] = $path;
  86.         return $this;
  87.     }
  88.     /**
  89.      * Adds a list of XML constraint mapping files to the validator.
  90.      *
  91.      * @param string[] $paths The paths to the mapping files
  92.      *
  93.      * @return $this
  94.      */
  95.     public function addXmlMappings(array $paths): static
  96.     {
  97.         if (null !== $this->metadataFactory) {
  98.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  99.         }
  100.         $this->xmlMappings array_merge($this->xmlMappings$paths);
  101.         return $this;
  102.     }
  103.     /**
  104.      * Adds a YAML constraint mapping file to the validator.
  105.      *
  106.      * @param string $path The path to the mapping file
  107.      *
  108.      * @return $this
  109.      */
  110.     public function addYamlMapping(string $path): static
  111.     {
  112.         if (null !== $this->metadataFactory) {
  113.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  114.         }
  115.         $this->yamlMappings[] = $path;
  116.         return $this;
  117.     }
  118.     /**
  119.      * Adds a list of YAML constraint mappings file to the validator.
  120.      *
  121.      * @param string[] $paths The paths to the mapping files
  122.      *
  123.      * @return $this
  124.      */
  125.     public function addYamlMappings(array $paths): static
  126.     {
  127.         if (null !== $this->metadataFactory) {
  128.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  129.         }
  130.         $this->yamlMappings array_merge($this->yamlMappings$paths);
  131.         return $this;
  132.     }
  133.     /**
  134.      * Enables constraint mapping using the given static method.
  135.      *
  136.      * @return $this
  137.      */
  138.     public function addMethodMapping(string $methodName): static
  139.     {
  140.         if (null !== $this->metadataFactory) {
  141.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  142.         }
  143.         $this->methodMappings[] = $methodName;
  144.         return $this;
  145.     }
  146.     /**
  147.      * Enables constraint mapping using the given static methods.
  148.      *
  149.      * @param string[] $methodNames The names of the methods
  150.      *
  151.      * @return $this
  152.      */
  153.     public function addMethodMappings(array $methodNames): static
  154.     {
  155.         if (null !== $this->metadataFactory) {
  156.             throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  157.         }
  158.         $this->methodMappings array_merge($this->methodMappings$methodNames);
  159.         return $this;
  160.     }
  161.     /**
  162.      * Enables annotation based constraint mapping.
  163.      *
  164.      * @return $this
  165.      */
  166.     public function enableAnnotationMapping(): static
  167.     {
  168.         if (null !== $this->metadataFactory) {
  169.             throw new ValidatorException('You cannot enable annotation mapping after setting a custom metadata factory. Configure your metadata factory instead.');
  170.         }
  171.         $this->enableAnnotationMapping true;
  172.         return $this;
  173.     }
  174.     /**
  175.      * Disables annotation based constraint mapping.
  176.      *
  177.      * @return $this
  178.      */
  179.     public function disableAnnotationMapping(): static
  180.     {
  181.         $this->enableAnnotationMapping false;
  182.         $this->annotationReader null;
  183.         return $this;
  184.     }
  185.     /**
  186.      * @return $this
  187.      */
  188.     public function setDoctrineAnnotationReader(?Reader $reader): static
  189.     {
  190.         $this->annotationReader $reader;
  191.         return $this;
  192.     }
  193.     /**
  194.      * @return $this
  195.      */
  196.     public function addDefaultDoctrineAnnotationReader(): static
  197.     {
  198.         $this->annotationReader $this->createAnnotationReader();
  199.         return $this;
  200.     }
  201.     /**
  202.      * Sets the class metadata factory used by the validator.
  203.      *
  204.      * @return $this
  205.      */
  206.     public function setMetadataFactory(MetadataFactoryInterface $metadataFactory): static
  207.     {
  208.         if (\count($this->xmlMappings) > || \count($this->yamlMappings) > || \count($this->methodMappings) > || $this->enableAnnotationMapping) {
  209.             throw new ValidatorException('You cannot set a custom metadata factory after adding custom mappings. You should do either of both.');
  210.         }
  211.         $this->metadataFactory $metadataFactory;
  212.         return $this;
  213.     }
  214.     /**
  215.      * Sets the cache for caching class metadata.
  216.      *
  217.      * @return $this
  218.      */
  219.     public function setMappingCache(CacheItemPoolInterface $cache): static
  220.     {
  221.         if (null !== $this->metadataFactory) {
  222.             throw new ValidatorException('You cannot set a custom mapping cache after setting a custom metadata factory. Configure your metadata factory instead.');
  223.         }
  224.         $this->mappingCache $cache;
  225.         return $this;
  226.     }
  227.     /**
  228.      * Sets the constraint validator factory used by the validator.
  229.      *
  230.      * @return $this
  231.      */
  232.     public function setConstraintValidatorFactory(ConstraintValidatorFactoryInterface $validatorFactory): static
  233.     {
  234.         $this->validatorFactory $validatorFactory;
  235.         return $this;
  236.     }
  237.     /**
  238.      * Sets the translator used for translating violation messages.
  239.      *
  240.      * @return $this
  241.      */
  242.     public function setTranslator(TranslatorInterface $translator): static
  243.     {
  244.         $this->translator $translator;
  245.         return $this;
  246.     }
  247.     /**
  248.      * Sets the default translation domain of violation messages.
  249.      *
  250.      * The same message can have different translations in different domains.
  251.      * Pass the domain that is used for violation messages by default to this
  252.      * method.
  253.      *
  254.      * @return $this
  255.      */
  256.     public function setTranslationDomain(?string $translationDomain): static
  257.     {
  258.         $this->translationDomain $translationDomain;
  259.         return $this;
  260.     }
  261.     /**
  262.      * @return $this
  263.      */
  264.     public function addLoader(LoaderInterface $loader): static
  265.     {
  266.         $this->loaders[] = $loader;
  267.         return $this;
  268.     }
  269.     /**
  270.      * @return LoaderInterface[]
  271.      */
  272.     public function getLoaders(): array
  273.     {
  274.         $loaders = [];
  275.         foreach ($this->xmlMappings as $xmlMapping) {
  276.             $loaders[] = new XmlFileLoader($xmlMapping);
  277.         }
  278.         foreach ($this->yamlMappings as $yamlMappings) {
  279.             $loaders[] = new YamlFileLoader($yamlMappings);
  280.         }
  281.         foreach ($this->methodMappings as $methodName) {
  282.             $loaders[] = new StaticMethodLoader($methodName);
  283.         }
  284.         if ($this->enableAnnotationMapping) {
  285.             $loaders[] = new AnnotationLoader($this->annotationReader);
  286.         }
  287.         return array_merge($loaders$this->loaders);
  288.     }
  289.     /**
  290.      * Builds and returns a new validator object.
  291.      */
  292.     public function getValidator(): ValidatorInterface
  293.     {
  294.         $metadataFactory $this->metadataFactory;
  295.         if (!$metadataFactory) {
  296.             $loaders $this->getLoaders();
  297.             $loader null;
  298.             if (\count($loaders) > 1) {
  299.                 $loader = new LoaderChain($loaders);
  300.             } elseif (=== \count($loaders)) {
  301.                 $loader $loaders[0];
  302.             }
  303.             $metadataFactory = new LazyLoadingMetadataFactory($loader$this->mappingCache);
  304.         }
  305.         $validatorFactory $this->validatorFactory ?? new ConstraintValidatorFactory();
  306.         $translator $this->translator;
  307.         if (null === $translator) {
  308.             $translator = new class() implements TranslatorInterfaceLocaleAwareInterface {
  309.                 use TranslatorTrait;
  310.             };
  311.             // Force the locale to be 'en' when no translator is provided rather than relying on the Intl default locale
  312.             // This avoids depending on Intl or the stub implementation being available. It also ensures that Symfony
  313.             // validation messages are pluralized properly even when the default locale gets changed because they are in
  314.             // English.
  315.             $translator->setLocale('en');
  316.         }
  317.         $contextFactory = new ExecutionContextFactory($translator$this->translationDomain);
  318.         return new RecursiveValidator($contextFactory$metadataFactory$validatorFactory$this->initializers);
  319.     }
  320.     private function createAnnotationReader(): Reader
  321.     {
  322.         if (!class_exists(AnnotationReader::class)) {
  323.             throw new LogicException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and symfony/cache to be installed.');
  324.         }
  325.         if (class_exists(ArrayAdapter::class)) {
  326.             return new PsrCachedReader(new AnnotationReader(), new ArrayAdapter());
  327.         }
  328.         throw new LogicException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and symfony/cache to be installed.');
  329.     }
  330. }