diff --git a/src/Symfony/Component/Validator/Constraints/Issn.php b/src/Symfony/Component/Validator/Constraints/Issn.php new file mode 100644 index 0000000000..63e3471b16 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/Issn.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @Annotation + * + * @author Antonio J. García Lagar + */ +class Issn extends Constraint +{ + public $issnInvalidFormatMessage = 'This value has not valid ISSN format.'; + public $issnInvalidValueMessage = 'This value is not a valid ISSN.'; + public $disallowLowerCasedX; + public $disallowNonHyphenated; +} diff --git a/src/Symfony/Component/Validator/Constraints/IssnValidator.php b/src/Symfony/Component/Validator/Constraints/IssnValidator.php new file mode 100644 index 0000000000..4dde35eee5 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/IssnValidator.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * Validates wether the value is a valid ISSN. + * + * @author Antonio J. García Lagar + * + * @see https://en.wikipedia.org/wiki/Issn + */ +class IssnValidator extends ConstraintValidator +{ + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint) + { + if (null === $value || '' === $value) { + return; + } + + if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string'); + } + + // Compose regex pattern + if ($constraint->disallowNonHyphenated) { + $digitsPattern = "\d{4}-\d{3}"; + } else { + $digitsPattern = "\d{4}-?\d{3}"; + } + if ($constraint->disallowLowerCasedX) { + $checksumPattern = "[\d|X]"; + } else { + $checksumPattern = "[\d|X|x]"; + } + $pattern = "/^" . $digitsPattern . $checksumPattern . "$/"; + + if (!preg_match($pattern, $value)) { + $this->context->addViolation($constraint->issnInvalidFormatMessage); + } else { + $digits = str_split(strtoupper(str_replace('-', '', $value))); + + $sum = 0; + for ($i = 8; $i > 1; $i--) { + $sum += $i * (int) array_shift($digits); + } + + $checksum = (reset($digits) == 'X')?10:(int) reset($digits); + + if (($sum + $checksum) % 11 != 0) { + $this->context->addViolation($constraint->issnInvalidValueMessage); + } + } + } +} diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index 5092d4e287..0669a9f079 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -238,6 +238,14 @@ This value is neither a valid ISBN-10 nor a valid ISBN-13. This value is neither a valid ISBN-10 nor a valid ISBN-13. + + This value has not valid ISSN format. + This value has not valid ISSN format. + + + This value is not a valid ISSN. + This value is not a valid ISSN. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index 347c9e4bae..ef8f9786d7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -238,6 +238,14 @@ This value is neither a valid ISBN-10 nor a valid ISBN-13. Este valor no es ni un ISBN-10 válido ni un ISBN-13 válido. + + This value has not valid ISSN format. + Este valor no tiene un formato válido de ISSN. + + + This value is not a valid ISSN. + Este valor no es un ISSN válido. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php new file mode 100644 index 0000000000..b9f738ce47 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/IssnValidatorTest.php @@ -0,0 +1,220 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Issn; +use Symfony\Component\Validator\Constraints\IssnValidator; + +/** + * @see https://en.wikipedia.org/wiki/Issn + */ +class IssnValidatorTest extends \PHPUnit_Framework_TestCase +{ + protected $context; + protected $validator; + + public function setUp() + { + $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false); + $this->validator = new IssnValidator(); + $this->validator->initialize($this->context); + } + + public function getValidLowerCasedIssn() + { + return array( + array('2162-321x'), + array('2160-200x'), + array('1537-453x'), + array('1937-710x'), + array('0002-922x'), + array('1553-345x'), + array('1553-619x'), + ); + } + + public function getValidNonHyphenatedIssn() + { + return array( + array('2162321X'), + array('01896016'), + array('15744647'), + array('14350645'), + array('07174055'), + array('20905076'), + array('14401592'), + ); + } + + public function getFullValidIssn() + { + return array( + array('1550-7416'), + array('1539-8560'), + array('2156-5376'), + array('1119-023X'), + array('1684-5315'), + array('1996-0786'), + array('1684-5374'), + array('1996-0794') + ); + } + + public function getValidIssn() + { + return array_merge( + $this->getValidLowerCasedIssn(), + $this->getValidNonHyphenatedIssn(), + $this->getFullValidIssn() + ); + } + + public function getInvalidFormatedIssn() + { + return array( + array(0), + array('1539'), + array('2156-537A') + ); + } + + public function getInvalidValueIssn() + { + return array( + array('1119-0231'), + array('1684-5312'), + array('1996-0783'), + array('1684-537X'), + array('1996-0795') + ); + + } + + public function getInvalidIssn() + { + return array_merge( + $this->getInvalidFormatedIssn(), + $this->getInvalidValueIssn() + ); + } + + public function testNullIsValid() + { + $constraint = new Issn(); + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate(null, $constraint); + } + + public function testEmptyStringIsValid() + { + $constraint = new Issn(); + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate('', $constraint); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsStringCompatibleType() + { + $constraint = new Issn(); + $this->validator->validate(new \stdClass(), $constraint); + } + + /** + * @dataProvider getValidLowerCasedIssn + */ + public function testDissalowLowerCasedIssns($issn) + { + $constraint = new Issn(array('disallowLowerCasedX' => true)); + $this->context + ->expects($this->once()) + ->method('addViolation') + ->with($constraint->issnInvalidFormatMessage); + + $this->validator->validate($issn, $constraint); + } + + /** + * @dataProvider getValidNonHyphenatedIssn + */ + public function testDissalowNonHyphenatedIssns($issn) + { + $constraint = new Issn(array('disallowNonHyphenated' => true)); + $this->context + ->expects($this->once()) + ->method('addViolation') + ->with($constraint->issnInvalidFormatMessage); + + $this->validator->validate($issn, $constraint); + } + + /** + * @dataProvider getValidIssn + */ + public function testValidIssn($issn) + { + $constraint = new Issn(); + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($issn, $constraint); + } + + /** + * @dataProvider getInvalidFormatedIssn + */ + public function testInvalidFormatIssn($issn) + { + $constraint = new Issn(); + $this->context + ->expects($this->once()) + ->method('addViolation') + ->with($constraint->issnInvalidFormatMessage); + + $this->validator->validate($issn, $constraint); + } + + /** + * @dataProvider getInvalidValueIssn + */ + public function testInvalidValueIssn($issn) + { + $constraint = new Issn(); + $this->context + ->expects($this->once()) + ->method('addViolation') + ->with($constraint->issnInvalidValueMessage); + + $this->validator->validate($issn, $constraint); + } + + /** + * @dataProvider getInvalidIssn + */ + public function testInvalidIssn($issn) + { + $constraint = new Issn(); + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->validator->validate($issn, $constraint); + } +}