IsinValidator.php 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  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\Constraint;
  12. use Symfony\Component\Validator\ConstraintValidator;
  13. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  14. use Symfony\Component\Validator\Exception\UnexpectedValueException;
  15. /**
  16. * @author Laurent Masforné <l.masforne@gmail.com>
  17. *
  18. * @see https://en.wikipedia.org/wiki/International_Securities_Identification_Number
  19. */
  20. class IsinValidator extends ConstraintValidator
  21. {
  22. /**
  23. * {@inheritdoc}
  24. */
  25. public function validate($value, Constraint $constraint)
  26. {
  27. if (!$constraint instanceof Isin) {
  28. throw new UnexpectedTypeException($constraint, Isin::class);
  29. }
  30. if (null === $value || '' === $value) {
  31. return;
  32. }
  33. if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
  34. throw new UnexpectedValueException($value, 'string');
  35. }
  36. $value = strtoupper($value);
  37. if (Isin::VALIDATION_LENGTH !== \strlen($value)) {
  38. $this->context->buildViolation($constraint->message)
  39. ->setParameter('{{ value }}', $this->formatValue($value))
  40. ->setCode(Isin::INVALID_LENGTH_ERROR)
  41. ->addViolation();
  42. return;
  43. }
  44. if (!preg_match(Isin::VALIDATION_PATTERN, $value)) {
  45. $this->context->buildViolation($constraint->message)
  46. ->setParameter('{{ value }}', $this->formatValue($value))
  47. ->setCode(Isin::INVALID_PATTERN_ERROR)
  48. ->addViolation();
  49. return;
  50. }
  51. if (!$this->isCorrectChecksum($value)) {
  52. $this->context->buildViolation($constraint->message)
  53. ->setParameter('{{ value }}', $this->formatValue($value))
  54. ->setCode(Isin::INVALID_CHECKSUM_ERROR)
  55. ->addViolation();
  56. }
  57. }
  58. private function isCorrectChecksum(string $input): bool
  59. {
  60. $characters = str_split($input);
  61. foreach ($characters as $i => $char) {
  62. $characters[$i] = \intval($char, 36);
  63. }
  64. $number = implode('', $characters);
  65. return 0 === $this->context->getValidator()->validate($number, new Luhn())->count();
  66. }
  67. }