annotate vendor/symfony/validator/Constraints/IsbnValidator.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Component\Validator\Constraints;
Chris@0 13
Chris@0 14 use Symfony\Component\Validator\Constraint;
Chris@0 15 use Symfony\Component\Validator\ConstraintValidator;
Chris@0 16 use Symfony\Component\Validator\Exception\UnexpectedTypeException;
Chris@0 17
Chris@0 18 /**
Chris@0 19 * Validates whether the value is a valid ISBN-10 or ISBN-13.
Chris@0 20 *
Chris@0 21 * @author The Whole Life To Learn <thewholelifetolearn@gmail.com>
Chris@0 22 * @author Manuel Reinhard <manu@sprain.ch>
Chris@0 23 * @author Bernhard Schussek <bschussek@gmail.com>
Chris@0 24 *
Chris@0 25 * @see https://en.wikipedia.org/wiki/Isbn
Chris@0 26 */
Chris@0 27 class IsbnValidator extends ConstraintValidator
Chris@0 28 {
Chris@0 29 /**
Chris@0 30 * {@inheritdoc}
Chris@0 31 */
Chris@0 32 public function validate($value, Constraint $constraint)
Chris@0 33 {
Chris@0 34 if (!$constraint instanceof Isbn) {
Chris@0 35 throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Isbn');
Chris@0 36 }
Chris@0 37
Chris@0 38 if (null === $value || '' === $value) {
Chris@0 39 return;
Chris@0 40 }
Chris@0 41
Chris@17 42 if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
Chris@0 43 throw new UnexpectedTypeException($value, 'string');
Chris@0 44 }
Chris@0 45
Chris@0 46 $value = (string) $value;
Chris@0 47 $canonical = str_replace('-', '', $value);
Chris@0 48
Chris@0 49 // Explicitly validate against ISBN-10
Chris@0 50 if ('isbn10' === $constraint->type) {
Chris@0 51 if (true !== ($code = $this->validateIsbn10($canonical))) {
Chris@0 52 $this->context->buildViolation($this->getMessage($constraint, $constraint->type))
Chris@0 53 ->setParameter('{{ value }}', $this->formatValue($value))
Chris@0 54 ->setCode($code)
Chris@0 55 ->addViolation();
Chris@0 56 }
Chris@0 57
Chris@0 58 return;
Chris@0 59 }
Chris@0 60
Chris@0 61 // Explicitly validate against ISBN-13
Chris@0 62 if ('isbn13' === $constraint->type) {
Chris@0 63 if (true !== ($code = $this->validateIsbn13($canonical))) {
Chris@0 64 $this->context->buildViolation($this->getMessage($constraint, $constraint->type))
Chris@0 65 ->setParameter('{{ value }}', $this->formatValue($value))
Chris@0 66 ->setCode($code)
Chris@0 67 ->addViolation();
Chris@0 68 }
Chris@0 69
Chris@0 70 return;
Chris@0 71 }
Chris@0 72
Chris@0 73 // Try both ISBNs
Chris@0 74
Chris@0 75 // First, try ISBN-10
Chris@0 76 $code = $this->validateIsbn10($canonical);
Chris@0 77
Chris@0 78 // The ISBN can only be an ISBN-13 if the value was too long for ISBN-10
Chris@0 79 if (Isbn::TOO_LONG_ERROR === $code) {
Chris@0 80 // Try ISBN-13 now
Chris@0 81 $code = $this->validateIsbn13($canonical);
Chris@0 82
Chris@0 83 // If too short, this means we have 11 or 12 digits
Chris@0 84 if (Isbn::TOO_SHORT_ERROR === $code) {
Chris@0 85 $code = Isbn::TYPE_NOT_RECOGNIZED_ERROR;
Chris@0 86 }
Chris@0 87 }
Chris@0 88
Chris@0 89 if (true !== $code) {
Chris@0 90 $this->context->buildViolation($this->getMessage($constraint))
Chris@0 91 ->setParameter('{{ value }}', $this->formatValue($value))
Chris@0 92 ->setCode($code)
Chris@0 93 ->addViolation();
Chris@0 94 }
Chris@0 95 }
Chris@0 96
Chris@0 97 protected function validateIsbn10($isbn)
Chris@0 98 {
Chris@0 99 // Choose an algorithm so that ERROR_INVALID_CHARACTERS is preferred
Chris@0 100 // over ERROR_TOO_SHORT/ERROR_TOO_LONG
Chris@0 101 // Otherwise "0-45122-5244" passes, but "0-45122_5244" reports
Chris@0 102 // "too long"
Chris@0 103
Chris@0 104 // Error priority:
Chris@0 105 // 1. ERROR_INVALID_CHARACTERS
Chris@0 106 // 2. ERROR_TOO_SHORT/ERROR_TOO_LONG
Chris@0 107 // 3. ERROR_CHECKSUM_FAILED
Chris@0 108
Chris@0 109 $checkSum = 0;
Chris@0 110
Chris@0 111 for ($i = 0; $i < 10; ++$i) {
Chris@0 112 // If we test the length before the loop, we get an ERROR_TOO_SHORT
Chris@0 113 // when actually an ERROR_INVALID_CHARACTERS is wanted, e.g. for
Chris@0 114 // "0-45122_5244" (typo)
Chris@0 115 if (!isset($isbn[$i])) {
Chris@0 116 return Isbn::TOO_SHORT_ERROR;
Chris@0 117 }
Chris@0 118
Chris@0 119 if ('X' === $isbn[$i]) {
Chris@0 120 $digit = 10;
Chris@0 121 } elseif (ctype_digit($isbn[$i])) {
Chris@0 122 $digit = $isbn[$i];
Chris@0 123 } else {
Chris@0 124 return Isbn::INVALID_CHARACTERS_ERROR;
Chris@0 125 }
Chris@0 126
Chris@0 127 $checkSum += $digit * (10 - $i);
Chris@0 128 }
Chris@0 129
Chris@0 130 if (isset($isbn[$i])) {
Chris@0 131 return Isbn::TOO_LONG_ERROR;
Chris@0 132 }
Chris@0 133
Chris@0 134 return 0 === $checkSum % 11 ? true : Isbn::CHECKSUM_FAILED_ERROR;
Chris@0 135 }
Chris@0 136
Chris@0 137 protected function validateIsbn13($isbn)
Chris@0 138 {
Chris@0 139 // Error priority:
Chris@0 140 // 1. ERROR_INVALID_CHARACTERS
Chris@0 141 // 2. ERROR_TOO_SHORT/ERROR_TOO_LONG
Chris@0 142 // 3. ERROR_CHECKSUM_FAILED
Chris@0 143
Chris@0 144 if (!ctype_digit($isbn)) {
Chris@0 145 return Isbn::INVALID_CHARACTERS_ERROR;
Chris@0 146 }
Chris@0 147
Chris@17 148 $length = \strlen($isbn);
Chris@0 149
Chris@0 150 if ($length < 13) {
Chris@0 151 return Isbn::TOO_SHORT_ERROR;
Chris@0 152 }
Chris@0 153
Chris@0 154 if ($length > 13) {
Chris@0 155 return Isbn::TOO_LONG_ERROR;
Chris@0 156 }
Chris@0 157
Chris@0 158 $checkSum = 0;
Chris@0 159
Chris@0 160 for ($i = 0; $i < 13; $i += 2) {
Chris@0 161 $checkSum += $isbn[$i];
Chris@0 162 }
Chris@0 163
Chris@0 164 for ($i = 1; $i < 12; $i += 2) {
Chris@0 165 $checkSum += $isbn[$i]
Chris@0 166 * 3;
Chris@0 167 }
Chris@0 168
Chris@0 169 return 0 === $checkSum % 10 ? true : Isbn::CHECKSUM_FAILED_ERROR;
Chris@0 170 }
Chris@0 171
Chris@0 172 protected function getMessage($constraint, $type = null)
Chris@0 173 {
Chris@0 174 if (null !== $constraint->message) {
Chris@0 175 return $constraint->message;
Chris@0 176 } elseif ('isbn10' === $type) {
Chris@0 177 return $constraint->isbn10Message;
Chris@0 178 } elseif ('isbn13' === $type) {
Chris@0 179 return $constraint->isbn13Message;
Chris@0 180 }
Chris@0 181
Chris@0 182 return $constraint->bothIsbnMessage;
Chris@0 183 }
Chris@0 184 }