/src/Symfony/Component/Validator/ValidatorBuilder.php

https://github.com/gimler/symfony · PHP · 348 lines · 190 code · 67 blank · 91 comment · 19 complexity · bbfe80bca4dd16e0da0d805f8388ab71 MD5 · raw file

  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\Reader;
  14. use Doctrine\Common\Cache\ArrayCache;
  15. use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
  16. use Symfony\Component\Validator\Context\ExecutionContextFactory;
  17. use Symfony\Component\Validator\Exception\ValidatorException;
  18. use Symfony\Component\Validator\Mapping\Cache\CacheInterface;
  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\Util\LegacyTranslatorProxy;
  28. use Symfony\Component\Validator\Validator\RecursiveValidator;
  29. use Symfony\Contracts\Translation\TranslatorInterface;
  30. use Symfony\Contracts\Translation\TranslatorTrait;
  31. /**
  32. * The default implementation of {@link ValidatorBuilderInterface}.
  33. *
  34. * @author Bernhard Schussek <bschussek@gmail.com>
  35. *
  36. * @final since Symfony 4.2
  37. */
  38. class ValidatorBuilder implements ValidatorBuilderInterface
  39. {
  40. private $initializers = array();
  41. private $loaders = array();
  42. private $xmlMappings = array();
  43. private $yamlMappings = array();
  44. private $methodMappings = array();
  45. /**
  46. * @var Reader|null
  47. */
  48. private $annotationReader;
  49. /**
  50. * @var MetadataFactoryInterface|null
  51. */
  52. private $metadataFactory;
  53. /**
  54. * @var ConstraintValidatorFactoryInterface|null
  55. */
  56. private $validatorFactory;
  57. /**
  58. * @var CacheInterface|null
  59. */
  60. private $metadataCache;
  61. /**
  62. * @var TranslatorInterface|null
  63. */
  64. private $translator;
  65. /**
  66. * @var null|string
  67. */
  68. private $translationDomain;
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public function addObjectInitializer(ObjectInitializerInterface $initializer)
  73. {
  74. $this->initializers[] = $initializer;
  75. return $this;
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function addObjectInitializers(array $initializers)
  81. {
  82. $this->initializers = array_merge($this->initializers, $initializers);
  83. return $this;
  84. }
  85. /**
  86. * {@inheritdoc}
  87. */
  88. public function addXmlMapping($path)
  89. {
  90. if (null !== $this->metadataFactory) {
  91. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  92. }
  93. $this->xmlMappings[] = $path;
  94. return $this;
  95. }
  96. /**
  97. * {@inheritdoc}
  98. */
  99. public function addXmlMappings(array $paths)
  100. {
  101. if (null !== $this->metadataFactory) {
  102. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  103. }
  104. $this->xmlMappings = array_merge($this->xmlMappings, $paths);
  105. return $this;
  106. }
  107. /**
  108. * {@inheritdoc}
  109. */
  110. public function addYamlMapping($path)
  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. * {@inheritdoc}
  120. */
  121. public function addYamlMappings(array $paths)
  122. {
  123. if (null !== $this->metadataFactory) {
  124. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  125. }
  126. $this->yamlMappings = array_merge($this->yamlMappings, $paths);
  127. return $this;
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. public function addMethodMapping($methodName)
  133. {
  134. if (null !== $this->metadataFactory) {
  135. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  136. }
  137. $this->methodMappings[] = $methodName;
  138. return $this;
  139. }
  140. /**
  141. * {@inheritdoc}
  142. */
  143. public function addMethodMappings(array $methodNames)
  144. {
  145. if (null !== $this->metadataFactory) {
  146. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  147. }
  148. $this->methodMappings = array_merge($this->methodMappings, $methodNames);
  149. return $this;
  150. }
  151. /**
  152. * {@inheritdoc}
  153. */
  154. public function enableAnnotationMapping(Reader $annotationReader = null)
  155. {
  156. if (null !== $this->metadataFactory) {
  157. throw new ValidatorException('You cannot enable annotation mapping after setting a custom metadata factory. Configure your metadata factory instead.');
  158. }
  159. if (null === $annotationReader) {
  160. if (!class_exists('Doctrine\Common\Annotations\AnnotationReader') || !class_exists('Doctrine\Common\Cache\ArrayCache')) {
  161. throw new \RuntimeException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and doctrine/cache to be installed.');
  162. }
  163. $annotationReader = new CachedReader(new AnnotationReader(), new ArrayCache());
  164. }
  165. $this->annotationReader = $annotationReader;
  166. return $this;
  167. }
  168. /**
  169. * {@inheritdoc}
  170. */
  171. public function disableAnnotationMapping()
  172. {
  173. $this->annotationReader = null;
  174. return $this;
  175. }
  176. /**
  177. * {@inheritdoc}
  178. */
  179. public function setMetadataFactory(MetadataFactoryInterface $metadataFactory)
  180. {
  181. if (\count($this->xmlMappings) > 0 || \count($this->yamlMappings) > 0 || \count($this->methodMappings) > 0 || null !== $this->annotationReader) {
  182. throw new ValidatorException('You cannot set a custom metadata factory after adding custom mappings. You should do either of both.');
  183. }
  184. $this->metadataFactory = $metadataFactory;
  185. return $this;
  186. }
  187. /**
  188. * {@inheritdoc}
  189. */
  190. public function setMetadataCache(CacheInterface $cache)
  191. {
  192. if (null !== $this->metadataFactory) {
  193. throw new ValidatorException('You cannot set a custom metadata cache after setting a custom metadata factory. Configure your metadata factory instead.');
  194. }
  195. $this->metadataCache = $cache;
  196. return $this;
  197. }
  198. /**
  199. * {@inheritdoc}
  200. */
  201. public function setConstraintValidatorFactory(ConstraintValidatorFactoryInterface $validatorFactory)
  202. {
  203. $this->validatorFactory = $validatorFactory;
  204. return $this;
  205. }
  206. /**
  207. * {@inheritdoc}
  208. */
  209. public function setTranslator(LegacyTranslatorInterface $translator)
  210. {
  211. $this->translator = $translator instanceof LegacyTranslatorProxy ? $translator->getTranslator() : $translator;
  212. return $this;
  213. }
  214. /**
  215. * {@inheritdoc}
  216. */
  217. public function setTranslationDomain($translationDomain)
  218. {
  219. $this->translationDomain = $translationDomain;
  220. return $this;
  221. }
  222. /**
  223. * @return $this
  224. */
  225. public function addLoader(LoaderInterface $loader)
  226. {
  227. $this->loaders[] = $loader;
  228. return $this;
  229. }
  230. /**
  231. * @return LoaderInterface[]
  232. */
  233. public function getLoaders()
  234. {
  235. $loaders = array();
  236. foreach ($this->xmlMappings as $xmlMapping) {
  237. $loaders[] = new XmlFileLoader($xmlMapping);
  238. }
  239. foreach ($this->yamlMappings as $yamlMappings) {
  240. $loaders[] = new YamlFileLoader($yamlMappings);
  241. }
  242. foreach ($this->methodMappings as $methodName) {
  243. $loaders[] = new StaticMethodLoader($methodName);
  244. }
  245. if ($this->annotationReader) {
  246. $loaders[] = new AnnotationLoader($this->annotationReader);
  247. }
  248. return array_merge($loaders, $this->loaders);
  249. }
  250. /**
  251. * {@inheritdoc}
  252. */
  253. public function getValidator()
  254. {
  255. $metadataFactory = $this->metadataFactory;
  256. if (!$metadataFactory) {
  257. $loaders = $this->getLoaders();
  258. $loader = null;
  259. if (\count($loaders) > 1) {
  260. $loader = new LoaderChain($loaders);
  261. } elseif (1 === \count($loaders)) {
  262. $loader = $loaders[0];
  263. }
  264. $metadataFactory = new LazyLoadingMetadataFactory($loader, $this->metadataCache);
  265. }
  266. $validatorFactory = $this->validatorFactory ?: new ConstraintValidatorFactory();
  267. $translator = $this->translator;
  268. if (null === $translator) {
  269. $translator = new class() implements TranslatorInterface {
  270. use TranslatorTrait;
  271. };
  272. // Force the locale to be 'en' when no translator is provided rather than relying on the Intl default locale
  273. // This avoids depending on Intl or the stub implementation being available. It also ensures that Symfony
  274. // validation messages are pluralized properly even when the default locale gets changed because they are in
  275. // English.
  276. $translator->setLocale('en');
  277. }
  278. $contextFactory = new ExecutionContextFactory($translator, $this->translationDomain);
  279. return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
  280. }
  281. }