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