1
0
mirror of https://github.com/fzaninotto/Faker.git synced 2025-03-24 01:09:50 +01:00

Company Provider for french locale now returns valid SIREN/SIRET codes which verify the Luhn algorithm.

Added PHPUnit constraint to assert that a string is a valid code.
This commit is contained in:
Alexandre Segura 2012-12-18 20:37:52 +01:00
parent 46a5171fe2
commit 91e87aa0e0
5 changed files with 174 additions and 21 deletions

View File

@ -51,11 +51,6 @@ class Company extends \Faker\Provider\Company
*/
protected static $companySuffix = array('SA', 'S.A.', 'SARL', 'S.A.R.L.', 'S.A.S.', 'et Fils');
/**
* @var string Siren format.
*/
protected static $sirenFormat = "### ### ###";
/**
* Returns a random catch phrase noun.
*
@ -106,34 +101,95 @@ class Company extends \Faker\Provider\Company
}
/**
* Generates a siret number (14 digits).
* It is in fact the result of the concatenation of a siren number (9 digits),
* a sequential number (4 digits) and a control number (1 digit) concatenation.
* If $maxSequentialDigits is invalid, it is set to 2.
*
* Generates a siret number (14 digits) that passes the Luhn check.
* Use $maxSequentialDigits to make sure the digits at position 2 to 5 are not zeros.
* @see http://en.wikipedia.org/wiki/Luhn_algorithm
* @param int $maxSequentialDigits The maximum number of digits for the sequential number (> 0 && <= 4).
*
* @return string
*/
public static function siret($maxSequentialDigits = 2)
{
if ($maxSequentialDigits > 4 || $maxSequentialDigits <= 0) {
$maxSequentialDigits = 2;
}
$sequentialNumber = str_pad(static::randomNumber($maxSequentialDigits), 4, '0', STR_PAD_LEFT);
return static::numerify(static::siren() . ' ' . $sequentialNumber . '#');
$controlDigit = mt_rand(0, 9);
$siret = $sum = $controlDigit;
$position = 2;
for ($i = 0; $i < $maxSequentialDigits; $i++) {
$sequentialDigit = mt_rand(0, 9);
$isEven = $position++ % 2 === 0;
$tmp = $isEven ? $sequentialDigit * 2 : $sequentialDigit;
if ($tmp >= 10) $tmp -= 9;
$sum += $tmp;
$siret = $sequentialDigit . $siret;
}
$siret = str_pad($siret, 5, '0', STR_PAD_LEFT);
$position = 6;
for ($i = 0; $i < 7; $i++) {
$digit = mt_rand(0, 9);
$isEven = $position++ % 2 === 0;
$tmp = $isEven ? $digit * 2 : $digit;
if ($tmp >= 10) $tmp -= 9;
$sum += $tmp;
$siret = $digit . $siret;
}
$mod = $sum % 10;
if ($mod === 0) {
$siret = '00' . $siret;
} else {
// Use the odd position to avoid multiplying by two
$siret = '0' . (10 - $mod) . $siret;
}
return preg_replace("/([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{5})/", "$1 $2 $3 $4", $siret);
}
/**
* Generates a siren number (9 digits).
*
* Generates a siren number (9 digits) that passes the Luhn check.
* @see http://en.wikipedia.org/wiki/Luhn_algorithm
* @return string
*/
public static function siren()
{
return static::numerify(static::$sirenFormat);
$siren = '';
$sum = 0;
for ($i = 9; $i > 1; $i--) {
$digit = mt_rand(0, 9);
$isEven = $i % 2 === 0;
$tmp = $isEven ? $digit * 2 : $digit;
if ($tmp >= 10) $tmp -= 9;
$sum += $tmp;
$siren = $digit . $siren;
}
$mod = $sum % 10;
if ($mod === 0) {
$siren = '0' . $siren;
} else {
$siren = (10 - $mod) . $siren;
}
return preg_replace("/([0-9]{3})([0-9]{3})([0-9]{3})/", "$1 $2 $3", $siren);
}
/**

View File

@ -0,0 +1,16 @@
<?php
namespace Faker\PHPUnit\Framework\Constraint;
class IsValidSiren extends IsValidSirenSiret
{
protected function getLength() {
return 9;
}
protected function getName() {
return 'SIREN';
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Faker\PHPUnit\Framework\Constraint;
abstract class IsValidSirenSiret extends \PHPUnit_Framework_Constraint {
protected function matches($other)
{
$code = str_replace(' ', '', $other);
if (strlen($code) != $this->getLength())
return false;
$sum = 0;
// IMPORTANT : from right to left
$position = 1;
for ($i = strlen($code) - 1; $i >= 0; $i--) {
$isEven = (($position++ % 2) === 0);
$tmp = $isEven ? $code[$i] * 2 : $code[$i];
if ($tmp >= 10) $tmp -= 9;
$sum += $tmp;
}
return ($sum % 10 === 0);
}
public function toString() {
return sprintf('is a valid %s number', $this->getName());
}
abstract protected function getLength();
abstract protected function getName();
}

View File

@ -0,0 +1,16 @@
<?php
namespace Faker\PHPUnit\Framework\Constraint;
class IsValidSiret extends IsValidSirenSiret
{
protected function getLength() {
return 14;
}
protected function getName() {
return 'SIRET';
}
}

View File

@ -3,20 +3,37 @@
namespace Faker\Test\Provider\fr_FR;
use Faker\Provider\fr_FR\Company;
use Faker\PHPUnit\Framework\Constraint as Constraint;
require_once __DIR__ . '/../../PHPUnit/Framework/Constraint/IsValidSirenSiret.php';
require_once __DIR__ . '/../../PHPUnit/Framework/Constraint/IsValidSiret.php';
require_once __DIR__ . '/../../PHPUnit/Framework/Constraint/IsValidSiren.php';
class CompanyTest extends \PHPUnit_Framework_TestCase
{
private static function isValidSiret()
{
return new Constraint\IsValidSiret();
}
private static function isValidSiren()
{
return new Constraint\IsValidSiren();
}
public function testParagraphWithNegativeNbDigitsReturnsAWellFormattedSiret()
{
$siret = Company::siret(-1);
$this->assertThat($siret, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} 00[\d]{3}/", $siret);
}
public function testParagraphWithInvalidNbDigitsReturnsAWellFormattedSiret()
{
$siret = Company::siret(6);
$this->assertThat($siret, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} 00[\d]{3}/", $siret);
}
@ -26,12 +43,24 @@ class CompanyTest extends \PHPUnit_Framework_TestCase
$siret2 = Company::siret(2);
$siret3 = Company::siret(3);
$siret4 = Company::siret(4);
$this->assertThat($siret1, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} 000[\d]{2}/", $siret1);
$this->assertThat($siret2, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} 00[\d]{3}/", $siret2);
$this->assertThat($siret3, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} 0[\d]{4}/", $siret3);
$this->assertThat($siret4, self :: isValidSiret());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3} [\d]{5}/", $siret4);
}
public function testSirenReturnsAValidAndWellFormattedSiren()
{
$siret = Company::siren();
$this->assertThat($siret, self :: isValidSiren());
$this->assertRegExp("/[\d]{3} [\d]{3} [\d]{3}/", $siret);
}
public function testCatchPhraseValidationReturnsFalse()
{