diff --git a/readme.md b/readme.md index b94189b9..d3e8a92c 100644 --- a/readme.md +++ b/readme.md @@ -246,7 +246,7 @@ try { // you can reset the unique modifier for all providers by passing true as first argument $faker->unique($reset = true)->randomDigitNotNull; // will not throw OverflowException since unique() was reset -// tip: unique() keeps one array of values per provider +// tip: unique() keeps one array of values per provider // optional() sometimes bypasses the provider to return null instead $values = array(); @@ -743,6 +743,41 @@ echo $faker->firstKanaName; // "トモミ" echo $faker->lastKanaName; // "ナギサ" ``` +### `Faker\Provider\ro_RO\Person` + +```php +prefixMale; // "ing." +// Generates a random female name prefix/title +echo $faker->prefixFemale; // "d-na." +// Generates a random male fist name +echo $faker->firstNameMale; // "Adrian" +// Generates a random female fist name +echo $faker->firstNameFemale; // "Miruna" + +// Generates a random Personal Numerical Code (CNP) +echo $faker->cnp; // "2800523081231" +echo $faker->cnp($gender = NULL, $century = NULL, $county = NULL); + +// Valid option values: +// $gender: m, f, 1, 2 +// $century: 1800, 1900, 2000, 1, 2, 3, 4, 5, 6 +// $county: 2 letter ISO 3166-2:RO county codes and B1-B6 for Bucharest's 6 sectors +``` + +### `Faker\Provider\ro_RO\PhoneNumber` + +```php +tollFreePhoneNumber; // "0800123456" +// Generates a random premium-rate phone number +echo $faker->premiumRatePhoneNumber; // "0900123456" +``` + ## License Faker is released under the MIT Licence. See the bundled LICENSE file for details. diff --git a/src/Faker/Provider/ro_RO/Person.php b/src/Faker/Provider/ro_RO/Person.php index 242bfc6c..27723c23 100644 --- a/src/Faker/Provider/ro_RO/Person.php +++ b/src/Faker/Provider/ro_RO/Person.php @@ -85,6 +85,18 @@ class Person extends \Faker\Provider\Person protected static $prefixMale = array('dl.', 'ing.', 'dr.'); protected static $prefixFemale = array('d-na.', 'd-șoara', 'ing.', 'dr.'); + protected static $cnpCountyCodes = array( + 'AB' => '01', 'AR' => '02', 'AG' => '03', 'B' => '40', 'BC' => '04', 'BH' => '05', + 'BN' => '06', 'BT' => '07', 'BV' => '08', 'BR' => '09', 'BZ' => '10', 'CS' => '11', + 'CL' => '51', 'CJ' => '12', 'CT' => '13', 'CV' => '14', 'DB' => '15', 'DJ' => '16', + 'GL' => '17', 'GR' => '52', 'GJ' => '18', 'HR' => '19', 'HD' => '20', 'IL' => '21', + 'IS' => '22', 'IF' => '23', 'MM' => '24', 'MH' => '25', 'MS' => '26', 'NT' => '27', + 'OT' => '28', 'PH' => '29', 'SM' => '30', 'SJ' => '31', 'SB' => '32', 'SV' => '33', + 'TR' => '34', 'TM' => '35', 'TL' => '36', 'VS' => '37', 'VL' => '38', 'VN' => '39', + + 'B1' => '41', 'B2' => '42', 'B3' => '43', 'B4' => '44', 'B5' => '45', 'B6' => '46' + ); + /** * @example 'Ion Popescu' */ @@ -128,4 +140,104 @@ class Person extends \Faker\Provider\Person { return static::randomElement(static::$prefixFemale); } + + /** + * Personal Numerical Code (CNP) + * + * @link http://ro.wikipedia.org/wiki/Cod_numeric_personal + * @example 1111111111118 + * + * @param string $gender Valid values: m, f, 1, 2 + * @param integer $century Valid values: 1800, 1900, 2000, 1, 2, 3, 4, 5, 6 + * @param string $county Valid values: 2 letter ISO 3166-2:RO county codes and B1-B6 for Bucharest's 6 sectors + * @return string + * + */ + public function cnp($gender = null, $century = null, $county = null) + { + if (is_null($county) || !array_key_exists($county, static::$cnpCountyCodes)) { + $countyCode = static::randomElement(array_values(static::$cnpCountyCodes)); + } else { + $countyCode = static::$cnpCountyCodes[$county]; + } + + $cnp = (string) static::cnpFirstDigit($gender, $century) + . static::numerify('##') + . sprintf('%02d', $this->generator->month()) + . sprintf('%02d', $this->generator->dayOfMonth()) + . $countyCode + . static::numerify('##%') + ; + + $cnp = static::cnpAddChecksum($cnp); + + return $cnp; + } + + /** + * Calculates the first digit for the Personal Numerical Code (CNP) based on + * the gender and century + * + * @param string $gender Valid values: m, f, 1, 2 + * @param integer $century Valid values: 1800, 1900, 2000, 1, 2, 3, 4, 5, 6 + * @return integer + */ + protected static function cnpFirstDigit($gender = null, $century = null) + { + switch ($century) { + case 1800: + case 3: + case 4: + $centuryCode = 2; + break; + case 1900: + case 1: + case 2: + $centuryCode = 0; + break; + case 2000: + case 5: + case 6: + $centuryCode = 4; + break; + default: + $centuryCode = static::randomElement(array(0, 2, 4, 6, 9)); + } + + switch (strtolower($gender)) { + case 'm': + case 1: + $genderCode = 1; + break; + case 'f': + case 2: + $genderCode = 2; + break; + default: + $genderCode = static::randomElement(array(1, 2)); + } + + $firstDigit = $centuryCode + $genderCode; + + return ($firstDigit > 9) ? 9 : $firstDigit; + } + + /** + * Calculates a checksum for the Personal Numerical Code (CNP). + * + * @param string $cnp Randomly generated CNP + * @return string CNP with the last digit altered to a proper checksum + */ + protected static function cnpAddChecksum($cnp) + { + $checkNumber = 279146358279; + + $checksum = 0; + foreach (range(0, 11) as $digit) { + $checksum += substr($cnp, $digit, 1) * substr($checkNumber, $digit, 1); + } + $checksum = $checksum % 11; + + return substr($cnp, 0, 12) . ($checksum == 10 ? 1 : $checksum); + } } diff --git a/src/Faker/Provider/ro_RO/PhoneNumber.php b/src/Faker/Provider/ro_RO/PhoneNumber.php new file mode 100644 index 00000000..9e3c07fe --- /dev/null +++ b/src/Faker/Provider/ro_RO/PhoneNumber.php @@ -0,0 +1,67 @@ + array( + '021#######', // Bucharest + '023#######', + '024#######', + '025#######', + '026#######', + '027#######', // non-geographic + '031#######', // Bucharest + '033#######', + '034#######', + '035#######', + '036#######', + '037#######', // non-geographic + ), + 'mobile' => array( + '07########', + ) + ); + + protected static $specialFormats = array( + 'toll-free' => array( + '0800######', + '0801######', // shared-cost numbers + '0802######', // personal numbering + '0806######', // virtual cards + '0807######', // pre-paid cards + '0870######', // internet dial-up + ), + 'premium-rate' => array( + '0900######', + '0903######', // financial information + '0906######', // adult entertainment + ) + ); + + /** + * @link http://en.wikipedia.org/wiki/Telephone_numbers_in_Romania#Last_years + */ + public static function phoneNumber() + { + $type = static::randomElement(array_keys(static::$normalFormats)); + $number = static::numerify(static::randomElement(static::$normalFormats[$type])); + + return $number; + } + + public static function tollFreePhoneNumber() + { + $number = static::numerify(static::randomElement(static::$specialFormats['toll-free'])); + + return $number; + } + + public static function premiumRatePhoneNumber() + { + $number = static::numerify(static::randomElement(static::$specialFormats['premium-rate'])); + + return $number; + } +} diff --git a/test/Faker/Provider/ro_RO/PersonTest.php b/test/Faker/Provider/ro_RO/PersonTest.php new file mode 100644 index 00000000..1c8ecfc6 --- /dev/null +++ b/test/Faker/Provider/ro_RO/PersonTest.php @@ -0,0 +1,94 @@ +addProvider(new DateTime($faker)); + $faker->addProvider(new Person($faker)); + $this->faker = $faker; + } + + public function testCnpReturnsValidCnp() + { + $cnp = $this->faker->cnp; + $this->assertTrue($this->isValidCnp($cnp)); + } + + public function testCnpReturnsMaleCnp() + { + $cnp = $this->faker->cnp('m'); + $this->assertRegExp('/^[1357]\d{12}$/', $cnp); + } + + public function testCnpReturnsFemaleCnp() + { + $cnp = $this->faker->cnp('f'); + $this->assertRegExp('/^[2468]\d{12}$/', $cnp); + } + + public function testCnpReturns1800sCnp() + { + $cnp = $this->faker->cnp(null, 1800); + $this->assertRegExp('/^[34]\d{12}$/', $cnp); + } + + public function testCnpReturns1900sCnp() + { + $cnp = $this->faker->cnp(null, 1900); + $this->assertRegExp('/^[12]\d{12}$/', $cnp); + } + + public function testCnpReturns2000sCnp() + { + $cnp = $this->faker->cnp(null, 2000); + $this->assertRegExp('/^[56]\d{12}$/', $cnp); + } + + public function testCnpReturnsBrasovCnp() + { + $cnp = $this->faker->cnp(null, null, 'BV'); + $this->assertRegExp('/^\d{7}08\d{4}$/', $cnp); + } + + public function testCnpReturns2000sClujFemaleCnp() + { + $cnp = $this->faker->cnp('f', 2000, 'CJ'); + $this->assertRegExp('/^6\d{6}12\d{4}$/', $cnp); + } + + protected function isValidCnp($cnp) + { + if ( + is_string($cnp) + && (bool) preg_match(static::TEST_CNP_REGEX, $cnp) + && checkdate(substr($cnp, 3, 2), substr($cnp, 5, 2), substr($cnp, 1, 2)) + ){ + $checkNumber = 279146358279; + + $checksum = 0; + foreach (range(0, 11) as $digit) { + $checksum += substr($cnp, $digit, 1) * substr($checkNumber, $digit, 1); + } + $checksum = $checksum % 11; + + if ( + ($checksum < 10 && $checksum == substr($cnp, -1)) + || ($checksum == 10 && substr($cnp, -1) == 1) + ){ + return true; + } + } + + return false; + } +} diff --git a/test/Faker/Provider/ro_RO/PhoneNumberTest.php b/test/Faker/Provider/ro_RO/PhoneNumberTest.php new file mode 100644 index 00000000..97314d5f --- /dev/null +++ b/test/Faker/Provider/ro_RO/PhoneNumberTest.php @@ -0,0 +1,31 @@ +addProvider(new PhoneNumber($faker)); + $this->faker = $faker; + } + + public function testPhoneNumberReturnsNormalPhoneNumber() + { + $this->assertRegExp('/^0(?:[23][13-7]|7\d)\d{7}$/', $this->faker->phoneNumber()); + } + + public function testTollFreePhoneNumberReturnsTollFreePhoneNumber() + { + $this->assertRegExp('/^08(?:0[1267]|70)\d{6}$/', $this->faker->tollFreePhoneNumber()); + } + + public function testPremiumRatePhoneNumberReturnsPremiumRatePhoneNumber() + { + $this->assertRegExp('/^090[036]\d{6}$/', $this->faker->premiumRatePhoneNumber()); + } +}