vendor/symfony/validator/ValidatorBuilder.php line 41

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