vendor/symfony/validator/ValidatorBuilder.php line 250

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