Skip to content

Commit 20a1bfc

Browse files
committed
Unit tests and bugfixes
1 parent b229756 commit 20a1bfc

File tree

3 files changed

+175
-26
lines changed

3 files changed

+175
-26
lines changed

LuhnAlgorithm.php

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,11 @@ class LuhnAlgorithm {
3232
private $checkDigit;
3333

3434
/**
35-
* The number can contain other signs then numbers, for instance
36-
* whitespace; <b>123 456 789</b>, these will be stripped
37-
* away.
38-
* @param string|int $number String or int
39-
* @param bool $withCheckDigit If the number contains a checkdigit already
40-
* @throws InvalidArgumentException If the string is less than 2 numbers
35+
* The number must be in the format <b>XXXXXX-XXX(D)</b> or
36+
* <b>XXXXXX - XXX(x)</b> or any permutations of those two. If the
37+
* checkdigit (D) is not supplied, it will be calculated
38+
* @param string|int $number The personnumer or organizational number
39+
* @throws InvalidArgumentException
4140
*/
4241
function __construct($number, $withCheckDigit = true) {
4342
$this->setNumber($number, $withCheckDigit);
@@ -60,10 +59,13 @@ public function isValid() {
6059
* @return int Checksum
6160
*/
6261
public static function calculateChecksum($number, $length = null) {
63-
if (!is_string($number)) {
64-
$number = strval($number);
62+
// Validate the number
63+
if (preg_match(self::numberRegex(), $number) !== 1) {
64+
throw new \InvalidArgumentException("{$number} is an invalid format");
6565
}
6666

67+
$number = strval(self::stringToInteger($number));
68+
6769
if ($length === null) {
6870
$length = strlen($number);
6971
}
@@ -100,7 +102,7 @@ public static function calculateChecksum($number, $length = null) {
100102
*/
101103
public static function calculcateCheckDigit($number) {
102104
// Get the checksum
103-
$checkSum = strval(self::calculateChecksum($number));
105+
$checkSum = strval(self::calculateChecksum($number . 0));
104106
// Get the last digit of the checksum
105107
$checkDigit = intval($checkSum[strlen($checkSum) - 1]);
106108

@@ -113,7 +115,7 @@ public static function calculcateCheckDigit($number) {
113115
* @return bool true if checkdigit is correct
114116
*/
115117
public function isValidCheckDigit() {
116-
$checkDigit = self::calculcateCheckDigit($this->number . 0, $this->nDigits + 1);
118+
$checkDigit = self::calculcateCheckDigit($this->number);
117119
// Validate
118120
return $checkDigit === $this->checkDigit;
119121
}
@@ -144,30 +146,41 @@ public function getCheckDigit() {
144146
}
145147

146148
/**
147-
* The number can contain other signs then numbers, for instance
148-
* whitespace; <b>123 456 789</b>, these will be stripped
149-
* away.
150-
* @param string|int $number String or int
151-
* @param bool $withCheckDigit If the number contains a checkdigit already
152-
* @throws InvalidArgumentException If the string is less than 2 numbers
149+
* What regex to use when validating numbers? Override this to provide
150+
* something else
151+
* @return string
152+
*/
153+
protected static function numberRegex() {
154+
return "/\d{6}\s?-?\s?\d{3}\d?/";
155+
}
156+
157+
/**
158+
* Fix a string so that it only contains numbers
159+
* @param string $integer String to convert to integer
160+
* @return int An integer
161+
*/
162+
public static function stringToInteger($integer) {
163+
return intval(preg_replace("/[^0-9]/", "", $integer));
164+
}
165+
166+
/**
167+
* The number must be in the format <b>XXXXXX-XXX(D)</b> or
168+
* <b>XXXXXX - XXX(x)</b> or any permutations of those two. If the
169+
* checkdigit (D) is not supplied, it will be calculated
170+
* @param string|int $number The personnumer or organizational number
171+
* @throws InvalidArgumentException
153172
*/
154173
public function setNumber($number, $withCheckDigit = true) {
155174
// Validate the number
156-
if (preg_match("/\d{6}\s?-?\s?\d{4}/", $number) !== 0) {
157-
throw new InvalidArgumentException('Number must be numeric');
175+
if (preg_match(self::numberRegex(), $number) !== 1) {
176+
throw new \InvalidArgumentException("{$number} is an invalid format");
158177
}
159178

160-
$number = strval(intval($number));
161-
162-
// Pretty safe to say that we need more then 1 number to be able to do
163-
// the Luhn Algorithm
179+
$number = strval(self::stringToInteger($number));
164180
$length = strlen($number);
165-
if ($length <= 1) {
166-
throw new InvalidArgumentException('Length must be longer then 1');
167-
}
168181

169182
// If number does not include checkdigit, calculate it!
170-
if ($withCheckDigit === false) {
183+
if (!$withCheckDigit) {
171184
$this->checkDigit = self::calculcateCheckDigit($number);
172185
} else {
173186
// Extract check digit from the number

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ checksum of a number:
1616
$number = '123456789';
1717
$luhnCheckSum = LuhnAlgorithm::calculateChecksum($number);
1818

19+
## Regex
20+
There will be a regex check agains the number provided as argument
21+
to this class. Override the function below to provide your own regex.
22+
23+
protected static function numberRegex() {
24+
return "/\d{6}\s?-?\s?\d{3}\d?/";
25+
}
1926

2027
## License
2128
Copyright 2013 Niklas Ekman, nikl.ekman@gmail.com

tests/LuhnAlgorithmTest.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
require_once(dirname(__FILE__) . "/../LuhnAlgorithm.php");
4+
5+
/**
6+
* Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2014-02-04 at 21:57:34.
7+
*/
8+
class LuhnAlgorithmTest extends PHPUnit_Framework_TestCase {
9+
10+
/**
11+
* @var LuhnAlgorithm
12+
*/
13+
protected $object;
14+
15+
/**
16+
* Sets up the fixture, for example, opens a network connection.
17+
* This method is called before a test is executed.
18+
*/
19+
protected function setUp() {
20+
$this->object = new LuhnAlgorithm("410321-9202", true);
21+
}
22+
23+
public function provider() {
24+
$data = array();
25+
$data[] = array("410321-9202");
26+
$data[] = array("410321 - 9202");
27+
$data[] = array(4103219202);
28+
return $data;
29+
}
30+
31+
/**
32+
* Tears down the fixture, for example, closes a network connection.
33+
* This method is called after a test is executed.
34+
*/
35+
protected function tearDown() {
36+
37+
}
38+
39+
/**
40+
* @covers LuhnAlgorithm::isValid
41+
* @dataProvider provider
42+
*/
43+
public function testIsValid($number) {
44+
$this->object->setNumber($number, true);
45+
$this->assertTrue($this->object->isValid());
46+
}
47+
48+
/**
49+
* @covers LuhnAlgorithm::calculateChecksum
50+
* @dataProvider provider
51+
*/
52+
public function testCalculateChecksum($number) {
53+
$number = \LuhnAlgorithm::stringToInteger($number);
54+
$checkSum = \LuhnAlgorithm::calculateChecksum($number);
55+
$this->assertEquals(0, $checkSum % 10);
56+
}
57+
58+
/**
59+
* @covers LuhnAlgorithm::calculcateCheckDigit
60+
* @dataProvider provider
61+
*/
62+
public function testCalculcateCheckDigit($number) {
63+
$number = strval(\LuhnAlgorithm::stringToInteger($number));
64+
$last = strlen($number) - 1;
65+
66+
// Check digit is the last number
67+
$checkDigit = $number[$last];
68+
$number = substr($number, 0, $last);
69+
70+
$calcCheckDigit = \LuhnAlgorithm::calculcateCheckDigit($number);
71+
$this->assertEquals($checkDigit, $calcCheckDigit);
72+
}
73+
74+
/**
75+
* @covers LuhnAlgorithm::isValidCheckDigit
76+
* @dataProvider provider
77+
*/
78+
public function testIsValidCheckDigit($number) {
79+
$this->object->setNumber($number, true);
80+
$this->assertTrue($this->object->isValidCheckDigit());
81+
}
82+
83+
/**
84+
* @covers LuhnAlgorithm::isCompletelyValid
85+
* @dataProvider provider
86+
*/
87+
public function testIsCompletelyValid($number) {
88+
$this->object->setNumber($number, true);
89+
$this->assertTrue($this->object->isCompletelyValid());
90+
}
91+
92+
/**
93+
* @covers LuhnAlgorithm::getNumber
94+
* @dataProvider provider
95+
*/
96+
public function testGetNumber($number) {
97+
$this->object->setNumber($number, true);
98+
$number = \LuhnAlgorithm::stringToInteger($number);
99+
$this->assertEquals($number, $this->object->getNumber());
100+
}
101+
102+
/**
103+
* @covers LuhnAlgorithm::getCheckDigit
104+
* @dataProvider provider
105+
*/
106+
public function testGetCheckDigit($number) {
107+
$number = strval($number);
108+
$checkDigit = $number[strlen($number) - 1];
109+
$this->object->setNumber($number, true);
110+
$this->assertEquals($this->object->getCheckDigit(), $checkDigit);
111+
}
112+
113+
/**
114+
* @covers LuhnAlgorithm::stringToInteger
115+
* @dataProvider provider
116+
*/
117+
public function testStringToInteger($number) {
118+
$int = \LuhnAlgorithm::stringToInteger($number);
119+
$this->assertTrue(is_int($int));
120+
}
121+
122+
/**
123+
* @covers LuhnAlgorithm::setNumber
124+
*/
125+
public function testSetNumber() {
126+
$this->object->setNumber("410321-9202");
127+
}
128+
129+
}

0 commit comments

Comments
 (0)