Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Validator\Constraints; Chris@0: Chris@0: use Symfony\Component\Validator\Constraint; Chris@0: use Symfony\Component\Validator\ConstraintValidator; Chris@0: use Symfony\Component\Validator\Exception\UnexpectedTypeException; Chris@0: Chris@0: /** Chris@0: * Validates whether the value is a valid ISSN. Chris@0: * Chris@0: * @author Antonio J. GarcĂ­a Lagar Chris@0: * @author Bernhard Schussek Chris@0: * Chris@0: * @see https://en.wikipedia.org/wiki/Issn Chris@0: */ Chris@0: class IssnValidator extends ConstraintValidator Chris@0: { Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function validate($value, Constraint $constraint) Chris@0: { Chris@0: if (!$constraint instanceof Issn) { Chris@0: throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Issn'); Chris@0: } Chris@0: Chris@0: if (null === $value || '' === $value) { Chris@0: return; Chris@0: } Chris@0: Chris@17: if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { Chris@0: throw new UnexpectedTypeException($value, 'string'); Chris@0: } Chris@0: Chris@0: $value = (string) $value; Chris@0: $canonical = $value; Chris@0: Chris@0: // 1234-567X Chris@0: // ^ Chris@0: if (isset($canonical[4]) && '-' === $canonical[4]) { Chris@0: // remove hyphen Chris@0: $canonical = substr($canonical, 0, 4).substr($canonical, 5); Chris@0: } elseif ($constraint->requireHyphen) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::MISSING_HYPHEN_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@17: $length = \strlen($canonical); Chris@0: Chris@0: if ($length < 8) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::TOO_SHORT_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: if ($length > 8) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::TOO_LONG_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: // 1234567X Chris@0: // ^^^^^^^ digits only Chris@0: if (!ctype_digit(substr($canonical, 0, 7))) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::INVALID_CHARACTERS_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: // 1234567X Chris@0: // ^ digit, x or X Chris@0: if (!ctype_digit($canonical[7]) && 'x' !== $canonical[7] && 'X' !== $canonical[7]) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::INVALID_CHARACTERS_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: // 1234567X Chris@0: // ^ case-sensitive? Chris@0: if ($constraint->caseSensitive && 'x' === $canonical[7]) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::INVALID_CASE_ERROR) Chris@0: ->addViolation(); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: // Calculate a checksum. "X" equals 10. Chris@0: $checkSum = 'X' === $canonical[7] Chris@0: || 'x' === $canonical[7] Chris@0: ? 10 Chris@0: : $canonical[7]; Chris@0: Chris@0: for ($i = 0; $i < 7; ++$i) { Chris@0: // Multiply the first digit by 8, the second by 7, etc. Chris@0: $checkSum += (8 - $i) * $canonical[$i]; Chris@0: } Chris@0: Chris@0: if (0 !== $checkSum % 11) { Chris@0: $this->context->buildViolation($constraint->message) Chris@0: ->setParameter('{{ value }}', $this->formatValue($value)) Chris@0: ->setCode(Issn::CHECKSUM_FAILED_ERROR) Chris@0: ->addViolation(); Chris@0: } Chris@0: } Chris@0: }