PageRenderTime 37ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/validator/Constraints/UuidValidator.php

https://gitlab.com/reasonat/test8
PHP | 346 lines | 219 code | 48 blank | 79 comment | 45 complexity | cd517f2c3566f82b5de36befaac87cb0 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\Constraints;
  11. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  12. use Symfony\Component\Validator\Constraint;
  13. use Symfony\Component\Validator\ConstraintValidator;
  14. use Symfony\Component\Validator\Constraints\Deprecated\UuidValidator as Deprecated;
  15. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  16. /**
  17. * Validates whether the value is a valid UUID per RFC 4122.
  18. *
  19. * @author Colin O'Dell <colinodell@gmail.com>
  20. * @author Bernhard Schussek <bschussek@gmail.com>
  21. *
  22. * @see http://tools.ietf.org/html/rfc4122
  23. * @see https://en.wikipedia.org/wiki/Universally_unique_identifier
  24. */
  25. class UuidValidator extends ConstraintValidator
  26. {
  27. // The strict pattern matches UUIDs like this:
  28. // xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
  29. // Roughly speaking:
  30. // x = any hexadecimal character
  31. // M = any allowed version {1..5}
  32. // N = any allowed variant {8, 9, a, b}
  33. const STRICT_LENGTH = 36;
  34. const STRICT_FIRST_HYPHEN_POSITION = 8;
  35. const STRICT_LAST_HYPHEN_POSITION = 23;
  36. const STRICT_VERSION_POSITION = 14;
  37. const STRICT_VARIANT_POSITION = 19;
  38. // The loose pattern validates similar yet non-compliant UUIDs.
  39. // Hyphens are completely optional. If present, they should only appear
  40. // between every fourth character:
  41. // xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx
  42. // xxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx
  43. // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  44. // The value can also be wrapped with characters like []{}:
  45. // {xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx}
  46. // Neither the version nor the variant is validated by this pattern.
  47. const LOOSE_MAX_LENGTH = 39;
  48. const LOOSE_FIRST_HYPHEN_POSITION = 4;
  49. /**
  50. * @deprecated since version 2.6, to be removed in 3.0
  51. */
  52. const STRICT_PATTERN = '/^[a-f0-9]{8}-[a-f0-9]{4}-[%s][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i';
  53. /**
  54. * @deprecated since version 2.6, to be removed in 3.0
  55. */
  56. const LOOSE_PATTERN = '/^[a-f0-9]{4}(?:-?[a-f0-9]{4}){7}$/i';
  57. /**
  58. * @deprecated since version 2.6, to be removed in 3.0
  59. */
  60. const STRICT_UUID_LENGTH = 36;
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function validate($value, Constraint $constraint)
  65. {
  66. if (null === $value || '' === $value) {
  67. return;
  68. }
  69. if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
  70. throw new UnexpectedTypeException($value, 'string');
  71. }
  72. $value = (string) $value;
  73. if ($constraint->strict) {
  74. $this->validateStrict($value, $constraint);
  75. return;
  76. }
  77. $this->validateLoose($value, $constraint);
  78. }
  79. private function validateLoose($value, Uuid $constraint)
  80. {
  81. // Error priority:
  82. // 1. ERROR_INVALID_CHARACTERS
  83. // 2. ERROR_INVALID_HYPHEN_PLACEMENT
  84. // 3. ERROR_TOO_SHORT/ERROR_TOO_LONG
  85. // Trim any wrapping characters like [] or {} used by some legacy systems
  86. $trimmed = trim($value, '[]{}');
  87. // Position of the next expected hyphen
  88. $h = self::LOOSE_FIRST_HYPHEN_POSITION;
  89. // Expected length
  90. $l = self::LOOSE_MAX_LENGTH;
  91. for ($i = 0; $i < $l; ++$i) {
  92. // Check length
  93. if (!isset($trimmed{$i})) {
  94. if ($this->context instanceof ExecutionContextInterface) {
  95. $this->context->buildViolation($constraint->message)
  96. ->setParameter('{{ value }}', $this->formatValue($value))
  97. ->setCode(Uuid::TOO_SHORT_ERROR)
  98. ->addViolation();
  99. } else {
  100. $this->buildViolation($constraint->message)
  101. ->setParameter('{{ value }}', $this->formatValue($value))
  102. ->setCode(Uuid::TOO_SHORT_ERROR)
  103. ->addViolation();
  104. }
  105. return;
  106. }
  107. // Hyphens must occur every fifth position
  108. // xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx
  109. // ^ ^ ^ ^ ^ ^ ^
  110. if ('-' === $trimmed{$i}) {
  111. if ($i !== $h) {
  112. if ($this->context instanceof ExecutionContextInterface) {
  113. $this->context->buildViolation($constraint->message)
  114. ->setParameter('{{ value }}', $this->formatValue($value))
  115. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  116. ->addViolation();
  117. } else {
  118. $this->buildViolation($constraint->message)
  119. ->setParameter('{{ value }}', $this->formatValue($value))
  120. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  121. ->addViolation();
  122. }
  123. return;
  124. }
  125. $h += 5;
  126. continue;
  127. }
  128. // Missing hyphens are ignored
  129. if ($i === $h) {
  130. $h += 4;
  131. --$l;
  132. }
  133. // Check characters
  134. if (!ctype_xdigit($trimmed{$i})) {
  135. if ($this->context instanceof ExecutionContextInterface) {
  136. $this->context->buildViolation($constraint->message)
  137. ->setParameter('{{ value }}', $this->formatValue($value))
  138. ->setCode(Uuid::INVALID_CHARACTERS_ERROR)
  139. ->addViolation();
  140. } else {
  141. $this->buildViolation($constraint->message)
  142. ->setParameter('{{ value }}', $this->formatValue($value))
  143. ->setCode(Uuid::INVALID_CHARACTERS_ERROR)
  144. ->addViolation();
  145. }
  146. return;
  147. }
  148. }
  149. // Check length again
  150. if (isset($trimmed{$i})) {
  151. if ($this->context instanceof ExecutionContextInterface) {
  152. $this->context->buildViolation($constraint->message)
  153. ->setParameter('{{ value }}', $this->formatValue($value))
  154. ->setCode(Uuid::TOO_LONG_ERROR)
  155. ->addViolation();
  156. } else {
  157. $this->buildViolation($constraint->message)
  158. ->setParameter('{{ value }}', $this->formatValue($value))
  159. ->setCode(Uuid::TOO_LONG_ERROR)
  160. ->addViolation();
  161. }
  162. }
  163. }
  164. private function validateStrict($value, Uuid $constraint)
  165. {
  166. // Error priority:
  167. // 1. ERROR_INVALID_CHARACTERS
  168. // 2. ERROR_INVALID_HYPHEN_PLACEMENT
  169. // 3. ERROR_TOO_SHORT/ERROR_TOO_LONG
  170. // 4. ERROR_INVALID_VERSION
  171. // 5. ERROR_INVALID_VARIANT
  172. // Position of the next expected hyphen
  173. $h = self::STRICT_FIRST_HYPHEN_POSITION;
  174. for ($i = 0; $i < self::STRICT_LENGTH; ++$i) {
  175. // Check length
  176. if (!isset($value{$i})) {
  177. if ($this->context instanceof ExecutionContextInterface) {
  178. $this->context->buildViolation($constraint->message)
  179. ->setParameter('{{ value }}', $this->formatValue($value))
  180. ->setCode(Uuid::TOO_SHORT_ERROR)
  181. ->addViolation();
  182. } else {
  183. $this->buildViolation($constraint->message)
  184. ->setParameter('{{ value }}', $this->formatValue($value))
  185. ->setCode(Uuid::TOO_SHORT_ERROR)
  186. ->addViolation();
  187. }
  188. return;
  189. }
  190. // Check hyphen placement
  191. // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  192. // ^ ^ ^ ^
  193. if ('-' === $value{$i}) {
  194. if ($i !== $h) {
  195. if ($this->context instanceof ExecutionContextInterface) {
  196. $this->context->buildViolation($constraint->message)
  197. ->setParameter(
  198. '{{ value }}',
  199. $this->formatValue($value)
  200. )
  201. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  202. ->addViolation();
  203. } else {
  204. $this->buildViolation($constraint->message)
  205. ->setParameter(
  206. '{{ value }}',
  207. $this->formatValue($value)
  208. )
  209. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  210. ->addViolation();
  211. }
  212. return;
  213. }
  214. // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  215. // ^
  216. if ($h < self::STRICT_LAST_HYPHEN_POSITION) {
  217. $h += 5;
  218. }
  219. continue;
  220. }
  221. // Check characters
  222. if (!ctype_xdigit($value{$i})) {
  223. if ($this->context instanceof ExecutionContextInterface) {
  224. $this->context->buildViolation($constraint->message)
  225. ->setParameter('{{ value }}', $this->formatValue($value))
  226. ->setCode(Uuid::INVALID_CHARACTERS_ERROR)
  227. ->addViolation();
  228. } else {
  229. $this->buildViolation($constraint->message)
  230. ->setParameter('{{ value }}', $this->formatValue($value))
  231. ->setCode(Uuid::INVALID_CHARACTERS_ERROR)
  232. ->addViolation();
  233. }
  234. return;
  235. }
  236. // Missing hyphen
  237. if ($i === $h) {
  238. if ($this->context instanceof ExecutionContextInterface) {
  239. $this->context->buildViolation($constraint->message)
  240. ->setParameter('{{ value }}', $this->formatValue($value))
  241. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  242. ->addViolation();
  243. } else {
  244. $this->buildViolation($constraint->message)
  245. ->setParameter('{{ value }}', $this->formatValue($value))
  246. ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR)
  247. ->addViolation();
  248. }
  249. return;
  250. }
  251. }
  252. // Check length again
  253. if (isset($value{$i})) {
  254. if ($this->context instanceof ExecutionContextInterface) {
  255. $this->context->buildViolation($constraint->message)
  256. ->setParameter('{{ value }}', $this->formatValue($value))
  257. ->setCode(Uuid::TOO_LONG_ERROR)
  258. ->addViolation();
  259. } else {
  260. $this->buildViolation($constraint->message)
  261. ->setParameter('{{ value }}', $this->formatValue($value))
  262. ->setCode(Uuid::TOO_LONG_ERROR)
  263. ->addViolation();
  264. }
  265. }
  266. // Check version
  267. if (!in_array($value{self::STRICT_VERSION_POSITION}, $constraint->versions)) {
  268. if ($this->context instanceof ExecutionContextInterface) {
  269. $this->context->buildViolation($constraint->message)
  270. ->setParameter('{{ value }}', $this->formatValue($value))
  271. ->setCode(Uuid::INVALID_VERSION_ERROR)
  272. ->addViolation();
  273. } else {
  274. $this->buildViolation($constraint->message)
  275. ->setParameter('{{ value }}', $this->formatValue($value))
  276. ->setCode(Uuid::INVALID_VERSION_ERROR)
  277. ->addViolation();
  278. }
  279. }
  280. // Check variant - first two bits must equal "10"
  281. // 0b10xx
  282. // & 0b1100 (12)
  283. // = 0b1000 (8)
  284. if ((hexdec($value{self::STRICT_VARIANT_POSITION}) & 12) !== 8) {
  285. if ($this->context instanceof ExecutionContextInterface) {
  286. $this->context->buildViolation($constraint->message)
  287. ->setParameter('{{ value }}', $this->formatValue($value))
  288. ->setCode(Uuid::INVALID_VARIANT_ERROR)
  289. ->addViolation();
  290. } else {
  291. $this->buildViolation($constraint->message)
  292. ->setParameter('{{ value }}', $this->formatValue($value))
  293. ->setCode(Uuid::INVALID_VARIANT_ERROR)
  294. ->addViolation();
  295. }
  296. }
  297. }
  298. }