ValidatorBuilder.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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\PropertyAccess\PropertyAccessorInterface;
  16. use Symfony\Component\Translation\IdentityTranslator;
  17. use Symfony\Component\Translation\TranslatorInterface;
  18. use Symfony\Component\Validator\Context\ExecutionContextFactory;
  19. use Symfony\Component\Validator\Exception\InvalidArgumentException;
  20. use Symfony\Component\Validator\Exception\ValidatorException;
  21. use Symfony\Component\Validator\Mapping\Cache\CacheInterface;
  22. use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
  23. use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
  24. use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
  25. use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
  26. use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader;
  27. use Symfony\Component\Validator\Mapping\Loader\XmlFilesLoader;
  28. use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader;
  29. use Symfony\Component\Validator\Mapping\Loader\YamlFilesLoader;
  30. use Symfony\Component\Validator\Validator\RecursiveValidator;
  31. /**
  32. * The default implementation of {@link ValidatorBuilderInterface}.
  33. *
  34. * @author Bernhard Schussek <bschussek@gmail.com>
  35. */
  36. class ValidatorBuilder implements ValidatorBuilderInterface
  37. {
  38. /**
  39. * @var array
  40. */
  41. private $initializers = array();
  42. /**
  43. * @var array
  44. */
  45. private $xmlMappings = array();
  46. /**
  47. * @var array
  48. */
  49. private $yamlMappings = array();
  50. /**
  51. * @var array
  52. */
  53. private $methodMappings = array();
  54. /**
  55. * @var Reader|null
  56. */
  57. private $annotationReader;
  58. /**
  59. * @var MetadataFactoryInterface|null
  60. */
  61. private $metadataFactory;
  62. /**
  63. * @var ConstraintValidatorFactoryInterface|null
  64. */
  65. private $validatorFactory;
  66. /**
  67. * @var CacheInterface|null
  68. */
  69. private $metadataCache;
  70. /**
  71. * @var TranslatorInterface|null
  72. */
  73. private $translator;
  74. /**
  75. * @var null|string
  76. */
  77. private $translationDomain;
  78. /**
  79. * @var PropertyAccessorInterface|null
  80. */
  81. private $propertyAccessor;
  82. /**
  83. * {@inheritdoc}
  84. */
  85. public function addObjectInitializer(ObjectInitializerInterface $initializer)
  86. {
  87. $this->initializers[] = $initializer;
  88. return $this;
  89. }
  90. /**
  91. * {@inheritdoc}
  92. */
  93. public function addObjectInitializers(array $initializers)
  94. {
  95. $this->initializers = array_merge($this->initializers, $initializers);
  96. return $this;
  97. }
  98. /**
  99. * {@inheritdoc}
  100. */
  101. public function addXmlMapping($path)
  102. {
  103. if (null !== $this->metadataFactory) {
  104. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  105. }
  106. $this->xmlMappings[] = $path;
  107. return $this;
  108. }
  109. /**
  110. * {@inheritdoc}
  111. */
  112. public function addXmlMappings(array $paths)
  113. {
  114. if (null !== $this->metadataFactory) {
  115. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  116. }
  117. $this->xmlMappings = array_merge($this->xmlMappings, $paths);
  118. return $this;
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function addYamlMapping($path)
  124. {
  125. if (null !== $this->metadataFactory) {
  126. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  127. }
  128. $this->yamlMappings[] = $path;
  129. return $this;
  130. }
  131. /**
  132. * {@inheritdoc}
  133. */
  134. public function addYamlMappings(array $paths)
  135. {
  136. if (null !== $this->metadataFactory) {
  137. throw new ValidatorException('You cannot add custom mappings after setting a custom metadata factory. Configure your metadata factory instead.');
  138. }
  139. $this->yamlMappings = array_merge($this->yamlMappings, $paths);
  140. return $this;
  141. }
  142. /**
  143. * {@inheritdoc}
  144. */
  145. public function addMethodMapping($methodName)
  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->methodMappings[] = $methodName;
  151. return $this;
  152. }
  153. /**
  154. * {@inheritdoc}
  155. */
  156. public function addMethodMappings(array $methodNames)
  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. * {@inheritdoc}
  166. */
  167. public function enableAnnotationMapping(Reader $annotationReader = null)
  168. {
  169. if (null !== $this->metadataFactory) {
  170. throw new ValidatorException('You cannot enable annotation mapping after setting a custom metadata factory. Configure your metadata factory instead.');
  171. }
  172. if (null === $annotationReader) {
  173. if (!class_exists('Doctrine\Common\Annotations\AnnotationReader') || !class_exists('Doctrine\Common\Cache\ArrayCache')) {
  174. throw new \RuntimeException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and doctrine/cache to be installed.');
  175. }
  176. $annotationReader = new CachedReader(new AnnotationReader(), new ArrayCache());
  177. }
  178. $this->annotationReader = $annotationReader;
  179. return $this;
  180. }
  181. /**
  182. * {@inheritdoc}
  183. */
  184. public function disableAnnotationMapping()
  185. {
  186. $this->annotationReader = null;
  187. return $this;
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function setMetadataFactory(MetadataFactoryInterface $metadataFactory)
  193. {
  194. if (count($this->xmlMappings) > 0 || count($this->yamlMappings) > 0 || count($this->methodMappings) > 0 || null !== $this->annotationReader) {
  195. throw new ValidatorException('You cannot set a custom metadata factory after adding custom mappings. You should do either of both.');
  196. }
  197. $this->metadataFactory = $metadataFactory;
  198. return $this;
  199. }
  200. /**
  201. * {@inheritdoc}
  202. */
  203. public function setMetadataCache(CacheInterface $cache)
  204. {
  205. if (null !== $this->metadataFactory) {
  206. throw new ValidatorException('You cannot set a custom metadata cache after setting a custom metadata factory. Configure your metadata factory instead.');
  207. }
  208. $this->metadataCache = $cache;
  209. return $this;
  210. }
  211. /**
  212. * {@inheritdoc}
  213. */
  214. public function setConstraintValidatorFactory(ConstraintValidatorFactoryInterface $validatorFactory)
  215. {
  216. if (null !== $this->propertyAccessor) {
  217. throw new ValidatorException('You cannot set a validator factory after setting a custom property accessor. Remove the call to setPropertyAccessor() if you want to call setConstraintValidatorFactory().');
  218. }
  219. $this->validatorFactory = $validatorFactory;
  220. return $this;
  221. }
  222. /**
  223. * {@inheritdoc}
  224. */
  225. public function setTranslator(TranslatorInterface $translator)
  226. {
  227. $this->translator = $translator;
  228. return $this;
  229. }
  230. /**
  231. * {@inheritdoc}
  232. */
  233. public function setTranslationDomain($translationDomain)
  234. {
  235. $this->translationDomain = $translationDomain;
  236. return $this;
  237. }
  238. /**
  239. * {@inheritdoc}
  240. *
  241. * @deprecated since version 2.5, to be removed in 3.0.
  242. * The validator will function without a property accessor.
  243. */
  244. public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor)
  245. {
  246. @trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0. The validator will function without a property accessor.', E_USER_DEPRECATED);
  247. if (null !== $this->validatorFactory) {
  248. throw new ValidatorException('You cannot set a property accessor after setting a custom validator factory. Configure your validator factory instead.');
  249. }
  250. $this->propertyAccessor = $propertyAccessor;
  251. return $this;
  252. }
  253. /**
  254. * {@inheritdoc}
  255. *
  256. * @deprecated since version 2.7, to be removed in 3.0.
  257. */
  258. public function setApiVersion($apiVersion)
  259. {
  260. @trigger_error('The '.__METHOD__.' method is deprecated in version 2.7 and will be removed in version 3.0.', E_USER_DEPRECATED);
  261. if (!in_array($apiVersion, array(Validation::API_VERSION_2_4, Validation::API_VERSION_2_5, Validation::API_VERSION_2_5_BC))) {
  262. throw new InvalidArgumentException(sprintf('The requested API version is invalid: "%s"', $apiVersion));
  263. }
  264. return $this;
  265. }
  266. /**
  267. * {@inheritdoc}
  268. */
  269. public function getValidator()
  270. {
  271. $metadataFactory = $this->metadataFactory;
  272. if (!$metadataFactory) {
  273. $loaders = array();
  274. if (count($this->xmlMappings) > 1) {
  275. $loaders[] = new XmlFilesLoader($this->xmlMappings);
  276. } elseif (1 === count($this->xmlMappings)) {
  277. $loaders[] = new XmlFileLoader($this->xmlMappings[0]);
  278. }
  279. if (count($this->yamlMappings) > 1) {
  280. $loaders[] = new YamlFilesLoader($this->yamlMappings);
  281. } elseif (1 === count($this->yamlMappings)) {
  282. $loaders[] = new YamlFileLoader($this->yamlMappings[0]);
  283. }
  284. foreach ($this->methodMappings as $methodName) {
  285. $loaders[] = new StaticMethodLoader($methodName);
  286. }
  287. if ($this->annotationReader) {
  288. $loaders[] = new AnnotationLoader($this->annotationReader);
  289. }
  290. $loader = null;
  291. if (count($loaders) > 1) {
  292. $loader = new LoaderChain($loaders);
  293. } elseif (1 === count($loaders)) {
  294. $loader = $loaders[0];
  295. }
  296. $metadataFactory = new LazyLoadingMetadataFactory($loader, $this->metadataCache);
  297. }
  298. $validatorFactory = $this->validatorFactory ?: new ConstraintValidatorFactory($this->propertyAccessor);
  299. $translator = $this->translator;
  300. if (null === $translator) {
  301. $translator = new IdentityTranslator();
  302. // Force the locale to be 'en' when no translator is provided rather than relying on the Intl default locale
  303. // This avoids depending on Intl or the stub implementation being available. It also ensures that Symfony
  304. // validation messages are pluralized properly even when the default locale gets changed because they are in
  305. // English.
  306. $translator->setLocale('en');
  307. }
  308. $contextFactory = new ExecutionContextFactory($translator, $this->translationDomain);
  309. return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
  310. }
  311. }