Skip to content

[Validator] Improve regexp for Credit Cards and some more tests #6583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 7, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 53 additions & 16 deletions src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,80 @@
* Validates that a card number belongs to a specified scheme.
*
* @see http://en.wikipedia.org/wiki/Bank_card_number
* @see http://www.regular-expressions.info/creditcard.html
* @author Tim Nagel <t.nagel@infinite.net.au>
*/
class CardSchemeValidator extends ConstraintValidator
{
protected $schemes = array(
/**
* American Express card numbers start with 34 or 37 and have 15 digits.
*/
'AMEX' => array(
'/^(3[47])([0-9]{13})/'
'/^3[47][0-9]{13}$/'
),
/**
* China UnionPay cards start with 62 and have between 16 and 19 digits.
* Please note that these cards do not follow Luhn Algorithm as a checksum.
*/
'CHINA_UNIONPAY' => array(
'/^(62)([0-9]{16,19}/'
'/^62[0-9]{14,17}$/'
),
/**
* Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
* There are Diners Club cards that begin with 5 and have 16 digits.
* These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
*/
'DINERS' => array(
'/^(36)([0-9]{12})/',
'/^(30[0-5])([0-9]{11})/',
'/^(5[45])([0-9]{14})/'
'/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',
),
/**
* Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
* All have 16 digits
*/
'DISCOVER' => array(
'/^(6011)([0-9]{12})/',
'/^(64[4-9])([0-9]{13})/',
'/^(65)([0-9]{14})/',
'/^(622)(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])([0-9]{10})/'
'/^6011[0-9]{12}$/',
'/^64[4-9][0-9]{13}$/',
'/^65[0-9]{14}$/',
'/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/'
),
/**
* InstaPayment cards begin with 637 through 639 and have 16 digits
*/
'INSTAPAYMENT' => array(
'/^(63[7-9])([0-9]{13})/'
'/^63[7-9][0-9]{13}$/'
),
/**
* JCB cards beginning with 2131 or 1800 have 15 digits.
* JCB cards beginning with 35 have 16 digits.
*/
'JCB' => array(
'/^(352[8-9]|35[3-8][0-9])([0-9]{12})/'
'/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/'
),
/**
* Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits
*/
'LASER' => array(
'/^(6304|670[69]|6771)([0-9]{12, 15})/'
'/^(6304|670[69]|6771)[0-9]{12,15}$/'
),
/**
* Maestro cards begin with either 5018, 5020, 5038, 5893, 6304, 6759, 6761, 6762, 6763 or 0604
* They have between 12 and 19 digits
*/
'MAESTRO' => array(
'/^(5018|5020|5038|6304|6759|6761|676[23]|0604)([0-9]{8, 15})/'
'/^(5018|5020|5038|6304|6759|6761|676[23]|0604)[0-9]{8,15}$/'
),
/**
* All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
*/
'MASTERCARD' => array(
'/^(5[1-5])([0-9]{14})/'
'/^5[1-5][0-9]{14}$/'
),
/**
* All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13.
*/
'VISA' => array(
'/^(4)([0-9]{12})/'
'/^4([0-9]{12}|[0-9]{15})$/'
),
);

Expand All @@ -74,9 +109,11 @@ public function validate($value, Constraint $constraint)

if (!is_numeric($value)) {
$this->context->addViolation($constraint->message);

return;
}

$schemes = array_flip($constraint->schemes);
$schemes = array_flip((array) $constraint->schemes);
$schemeRegexes = array_intersect_key($this->schemes, $schemes);

foreach ($schemeRegexes as $regexes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,79 @@ public function testValidNumbers($scheme, $number)
$this->context->expects($this->never())
->method('addViolation');

$this->validator->validate($number, new CardScheme(array('schemes' => array($scheme))));
$this->validator->validate($number, new CardScheme(array('schemes' => $scheme)));
}

/**
* @dataProvider getInvalidNumbers
*/
public function testInvalidNumbers($scheme, $number)
{
$this->context->expects($this->once())
->method('addViolation');

$this->validator->validate($number, new CardScheme(array('schemes' => $scheme)));
}

public function getValidNumbers()
{
return array(
array('VISA', '42424242424242424242'),
array('AMEX', '378282246310005'),
array('AMEX', '371449635398431'),
array('AMEX', '378734493671000'),
array('AMEX', '347298508610146'),
array('CHINA_UNIONPAY', '6228888888888888'),
array('CHINA_UNIONPAY', '62288888888888888'),
array('CHINA_UNIONPAY', '622888888888888888'),
array('CHINA_UNIONPAY', '6228888888888888888'),
array('DINERS', '30569309025904'),
array('DINERS', '36088894118515'),
array('DINERS', '38520000023237'),
array('DISCOVER', '6011111111111117'),
array('DISCOVER', '6011000990139424'),
array('INSTAPAYMENT', '6372476031350068'),
array('INSTAPAYMENT', '6385537775789749'),
array('INSTAPAYMENT', '6393440808445746'),
array('JCB', '3530111333300000'),
array('JCB', '3566002020360505'),
array('JCB', '213112345678901'),
array('JCB', '180012345678901'),
array('LASER', '6304678107004080'),
array('LASER', '6706440607428128629'),
array('LASER', '6771656738314582216'),
array('MAESTRO', '6759744069209'),
array('MAESTRO', '5020507657408074712'),
array('MAESTRO', '6759744069209'),
array('MAESTRO', '6759744069209'),
array('MASTERCARD', '5555555555554444'),
array('MASTERCARD', '5105105105105100'),
array('VISA', '4111111111111111'),
array('VISA', '4012888888881881'),
array('VISA', '4222222222222'),
array(array('AMEX', 'VISA'), '4111111111111111'),
array(array('AMEX', 'VISA'), '378282246310005'),
array(array('JCB', 'MASTERCARD'), '5105105105105100'),
array(array('VISA', 'MASTERCARD'), '5105105105105100'),
);
}

public function getInvalidNumbers()
{
return array(
array('VISA', '42424242424242424242'),
array('AMEX', '357298508610146'),
array('DINERS', '31569309025904'),
array('DINERS', '37088894118515'),
array('INSTAPAYMENT', '6313440808445746'),
array('CHINA_UNIONPAY', '622888888888888'),
array('CHINA_UNIONPAY', '62288888888888888888'),
array('AMEX', '30569309025904'), // DINERS number
array('AMEX', 'invalid'), // A string
array('AMEX', 0), // a lone number
array('AMEX', '0'), // a lone number
array('AMEX', '000000000000'), // a lone number
array('DINERS', '3056930'), // only first part of the number
array('DISCOVER', '1117'), // only last 4 digits
);
}
}