Skip to content
This repository has been archived by the owner on Dec 11, 2020. It is now read-only.

use Luhn to calculate ar_SA id numbers. #875

Merged
merged 11 commits into from
Oct 7, 2016
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@ Fugiat non in itaque sunt nobis totam. Sed nesciunt est deleniti cumque alias. R
echo $faker->idNumber; // ID number
echo $faker->nationalIdNumber // Citizen ID number
echo $faker->foreignerIdNumber // Foreigner ID number
echo $faker->companyIdNumber // Company ID number
```

### `Faker\Provider\at_AT\Payment`
Expand Down
42 changes: 41 additions & 1 deletion src/Faker/Calculator/Luhn.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Faker\Calculator;

use Faker\Provider\Base as BaseProvider;
use InvalidArgumentException;

/**
* Utility class for generating Luhn checksum and validating a number
* Utility class for generating and validating Luhn numbers.
*
* Luhn algorithm is used to validate credit card numbers, IMEI numbers, and
* National Provider Identifier numbers.
Expand Down Expand Up @@ -55,4 +58,41 @@ public static function isValid($number)
{
return self::checksum($number) === 0;
}

/**
* Generate a Luhn compliant number.
*
* @param string $prefix
* @param int $length
* @return string
*/
public static function generateLuhnNumber($prefix, $length)
{
$length = intval($length);
$prefix = trim($prefix);

if (!preg_match('/^\d*$/', $prefix)) {
throw new InvalidArgumentException('prefix should be all digits.');
}

if ($length <= 0) {
throw new InvalidArgumentException('length should be greater than zero.');
}

if (strlen($prefix) > $length) {
throw new InvalidArgumentException('prefix should be longer than the length.');
}

if (strlen($prefix) == $length) {
if (static::isValid($prefix)) {
return $prefix;
} else {
throw new InvalidArgumentException('prefix length equals length but prefix is not a Luhn number.');
}
}

$pattern = $prefix . str_repeat('#', $length - strlen($prefix) - 1);
$partialValue = BaseProvider::numerify($pattern);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should pass the partialValue as parameter to this method, to separate the responsibilities: Luhn calculation (this class), random generation (Company.php)

return $partialValue . Luhn::computeCheckDigit($partialValue);
}
}
10 changes: 10 additions & 0 deletions src/Faker/Provider/ar_SA/Company.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Faker\Provider\ar_SA;

use Faker\Calculator\Luhn;

class Company extends \Faker\Provider\Company
{
protected static $formats = array(
Expand Down Expand Up @@ -60,4 +62,12 @@ public function bs()

return join($result, ' ');
}

/**
* example 7001010101
**/
public static function companyIdNumber()
{
return Luhn::generateLuhnNumber(700, 10);
}
}
50 changes: 8 additions & 42 deletions src/Faker/Provider/ar_SA/Person.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Faker\Provider\ar_SA;

use Faker\Calculator\Luhn;

class Person extends \Faker\Provider\Person
{
protected static $maleNameFormats = array(
Expand Down Expand Up @@ -90,59 +92,23 @@ public static function prefix()
*/
public static function idNumber()
{
$firstDigit = static::numberBetween(1, 2);

do {
$number = $firstDigit.static::numerify('#########');
} while (!static::checkSum($number));

return $number;
$firstDigit = static::randomElement(array(1, 2));
return Luhn::generateLuhnNumber($firstDigit, 10);
}

/**
* @example
* @example 1010101010
*/
public static function nationalIdNumber()
{
do {
$number = '1'.static::numerify('#########');
} while (!static::checkSum($number));

return $number;
return Luhn::generateLuhnNumber(1, 10);
}

/**
* @example
* @example 2010101010
*/
public static function foreignerIdNumber()
{
do {
$number = '2'.static::numerify('#########');
} while (!static::checkSum($number));

return $number;
}

/**
* Check sum the number.
* @param $number
*
* @return bool
*/
protected static function checkSum($number)
{
$sum = 0;
$nums = str_split($number);

for ($i = 0; $i < 10; $i++) {
if ($i % 2 == 0) {
$s = $nums[$i] * 2;
$sum += $s % 10 + floor($s / 10);
} else {
$sum += $nums[$i];
}
}

return ($sum % 10 == 0);
return Luhn::generateLuhnNumber(2, 10);
}
}
56 changes: 56 additions & 0 deletions test/Faker/Calculator/LuhnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,60 @@ public function testIsValid($number, $isValid)
{
$this->assertEquals($isValid, Luhn::isValid($number));
}

public function generateLuhnNumberProvider()
{
return array(
array(' ', 5),
array(null, 7),
array(443, 10),
array(5, 15),
array(203, 25),
);
}

/**
* @dataProvider generateLuhnNumberProvider
*/
public function testGenerateLuhnNumber($prefix, $length)
{
$this->assertEquals($length, strlen(Luhn::generateLuhnNumber($prefix, $length)));
$this->assertTrue(Luhn::isValid(Luhn::generateLuhnNumber($prefix, $length)));
}

/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage prefix should be all digits.
*/
public function testGenerateLuhnNumberWithInvalidPrefix()
{
Luhn::generateLuhnNumber('abc', 10);
}

/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage length should be greater than zero.
*/
public function testGenerateLuhnNumberWithNegativeLength()
{
Luhn::generateLuhnNumber(1, -5);
}

/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage prefix should be longer than the length.
*/
public function testGenerateLuhnNumberWithPrefixGreaterThanLength()
{
Luhn::generateLuhnNumber(123456, 5);
}

/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage prefix length equals length but prefix is not a Luhn number.
*/
public function testGenerateLuhnNumberWithPrefixEqualLengthButInvalid()
{
Luhn::generateLuhnNumber(123457, 6);
}
}