Skip to content

Instantly share code, notes, and snippets.

@akizor
Created May 8, 2018 12:40
Show Gist options
  • Save akizor/5a0d876cf6967aead680157d5b3fa0ed to your computer and use it in GitHub Desktop.
Save akizor/5a0d876cf6967aead680157d5b3fa0ed to your computer and use it in GitHub Desktop.
Custom validation class for CNP field in Laravel
<?php
/**
* @author Daniel Placinta
* http://artgames.ro
* Heavy inspiration from https://github.com/alceanicu/cnp/
*/
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Cnp implements Rule
{
private $_isValid = false;
private $_cnp = [];
private $_year = false;
private $_month = false;
private $_day = false;
private $_cc = false;
private $_serial = false;
private static $controlKey = [2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9];
private static $countyCode = [
'01' => 'Alba',
'02' => 'Arad',
'03' => 'Arges',
'04' => 'Bacau',
'05' => 'Bihor',
'06' => 'Bistrita-Nasaud',
'07' => 'Botosani',
'08' => 'Brasov',
'09' => 'Braila',
'10' => 'Buzau',
'11' => 'Caras-Severin',
'12' => 'Cluj',
'13' => 'Constanta',
'14' => 'Covasna',
'15' => 'Dambovita',
'16' => 'Dolj',
'17' => 'Galati',
'18' => 'Gorj',
'19' => 'Harghita',
'20' => 'Hunedoara',
'21' => 'Ialomita',
'22' => 'Iasi',
'23' => 'Ilfov',
'24' => 'Maramures',
'25' => 'Mehedinti',
'26' => 'Mures',
'27' => 'Neamt',
'28' => 'Olt',
'29' => 'Prahova',
'30' => 'Satu Mare',
'31' => 'Salaj',
'32' => 'Sibiu',
'33' => 'Suceava',
'34' => 'Teleorman',
'35' => 'Timis',
'36' => 'Tulcea',
'37' => 'Vaslui',
'38' => 'Valcea',
'39' => 'Vrancea',
'40' => 'Bucuresti',
'41' => 'Bucuresti S.1',
'42' => 'Bucuresti S.2',
'43' => 'Bucuresti S.3',
'44' => 'Bucuresti S.4',
'45' => 'Bucuresti S.5',
'46' => 'Bucuresti S.6',
'51' => 'Calarasi',
'52' => 'Giurgiu'
];
public function passes($attribute, $value)
{
$this->_cnp = str_split(trim($value));
return $this->validateCnp();
}
public function message()
{
return ':input is not a valid CNP.';
}
/**
* Validation for Romanian Social Security Number (CNP)
* @return bool
*/
private function validateCnp()
{
$cnpArray = $this->_cnp;
// CNP must have 13 characters
if (count($cnpArray) != 13) {
return false;
}
// Set and check year, month, day and county
if ($this->year() && $this->month() && $this->day() && $this->county()) {
$hashArray = self::$controlKey;
$hashSum = 0;
// All characters must be numeric
for ($i = 0; $i <= 12; $i++) {
if (!is_numeric($cnpArray[$i])) {
return false;
}
if ($i < 12) {
$hashSum += (int)$cnpArray[$i] * (int)$hashArray[$i];
}
}
$hashSum = $hashSum % 11;
if ($hashSum == 10) {
$hashSum = 1;
}
return ($cnpArray[12] == $hashSum);
}
return false;
}
private function year()
{
$cnp = $this->_cnp;
$year = ($cnp[1] * 10) + $cnp[2];
switch ($cnp[0]) {
// romanian citizen born between 1900.01.01 and 1999.12.31
case 1 :
case 2 :
$year += 1900;
break;
// romanian citizen born between 1800.01.01 and 1899.12.31
case 3 :
case 4 :
$year += 1800;
break;
// romanian citizen born between 2000.01.01 and 2099.12.31
case 5 :
case 6 :
$year += 2000;
break;
// residents && people with foreign citizenship
case 7 :
case 8 :
case 9 :
$year += 2000;
if ($year > (int)date('Y') - 14) {
$year -= 100;
}
break;
default :
$year = 0;
break;
}
$this->_year = $year;
return ($year >= 1800) && ($year <= 2099);
}
/**
* Check and set month
* @return boolean
*/
private function month()
{
$this->_month = (int)($this->_cnp[3] . $this->_cnp[4]);
return ($this->_month >= 1) && ($this->_month <= 12);
}
/**
* Check and set day
* @return boolean
*/
private function day()
{
$this->_day = (int)($this->_cnp[5] . $this->_cnp[6]);
if (($this->_day < 1) || ($this->_day > 31)) {
return false;
}
if ($this->_day > 28) {
// validate date for day of month - 28, 29, 30 si 31
if (checkdate($this->_month, $this->_day, $this->_year) === false) {
return false;
}
}
return true;
}
/**
* Check and set county code
* @return boolean
*/
private function county()
{
$this->_cc = (string)($this->_cnp[7] . $this->_cnp[8]);
return array_key_exists($this->_cc, self::$countyCode);
}
/**
* Get Birth Place from Romanian Social Security Number (CNP)
* @param string|bool $defaultReturn
* @return string|bool
*/
public function getBirthCountyFromCNP($defaultReturn = false)
{
if ($this->_isValid) {
return self::$countyCode[$this->_cc];
}
return $defaultReturn;
}
/**
* Get Birth Date from Romanian Social Security Number (CNP)
* @param string $format
* @return string|boolean
*/
public function getBirthDateFromCNP($format = 'Y-m-d')
{
if ($this->_isValid) {
return \DateTime::createFromFormat('Y-m-d', "{$this->_year}-{$this->_month}-{$this->_day}")->format($format);
}
return false;
}
/**
* Get gender from Romanian Social Security Number (CNP)
* @param string $m
* @param string $f
* @return string|boolean
*/
public function getGenderFromCNP($m = 'M', $f = 'F')
{
if ($this->_isValid) {
if (in_array($this->_cnp[0], [1, 3, 5, 7])) {
return $m;
} elseif (in_array($this->_cnp[0], [2, 4, 6, 8])) {
return $f;
}
}
return false;
}
/**
* @return bool|string
*/
public function getSerialNumberFromCNP()
{
if ($this->_isValid) {
return $this->_cnp[9] . $this->_cnp[10] . $this->_cnp[11];
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment