Skip to content

Commit

Permalink
Release 4.2.0 (#11)
Browse files Browse the repository at this point in the history
- Update CHANGELOG.md
- update composer.json - add required PHP extension
- update PESEL validation rule - add method that check if bith date is correctly encoded in number
- add support for new options in PESEL rule
- update unit tests
- add UPRGADE_GUIDE.md file
- update README.md file
  • Loading branch information
WiktorPacer authored Dec 29, 2022
1 parent fa09b45 commit 2b23041
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 26 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Changelog
## v.4.2.0
**Possible breaking changes while updating from previous version. See upgrade guide [here](UPGRADE_GUIDE.md)**

- fix PESEL validation for numbers with birth date encoded incorrectly (thanks bbprojectnet!)
- add new options for PESEL rule - born_before, born_after (see README.md)
- drop support for Laravel 6.x and 7.x
## v.4.1.0
- update post_code rule - add support for options (see README.md)
- update PESEL rule - add suport for options (see README.md)
Expand Down
39 changes: 23 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,30 @@ For customize validaiton messages run:
php artisan vendor:publish --provider "PacerIT\LaravelPolishValidationRules\Providers\LaravelPolishValidationRulesServiceProvider"

### Version compatibility
#### Laravel
#### Laravel/Lumen
Framework | Package | Note
:---------|:--------|:------
5.8.x | ^1.x.x | No longer maintained.
6.0.x | ^2.x.x | Bug fixes only.
7.x.x | ^3.x.x | Bug fixes only.
8.x.x | ^4.x.x | PHP ^8.0 Supported from 4.0.3
9.x.x | ^5.x.x |
#### Lumen
Framework | Package | Note
:---------|:--------|:------
5.8.x | ^1.x.x | No longer maintained.
6.0.x | ^2.x.x | Bug fixes only.
7.x.x | ^3.x.x | Bug fixes only.
8.x.x | ^4.x.x | PHP ^8.0 Supported from 4.0.3
6.0.x | ^2.x.x | No longer maintained.
7.x.x | ^3.x.x | No longer maintained.
8.x.x | ^4.x.x | PHP ^8.0 Supported from 4.0.3, Bug fixes only.
9.x.x | ^5.x.x |

## Rules

1. 'PESEL' - validate [PESEL](https://pl.wikipedia.org/wiki/PESEL) number. We can validate additional parameters:
* Gender - check if gender value in PESEL
* gender_male
* gender_female
* `gender_male`
* `gender_female`
* Birth date - checking if birth date decoded from PESEL number is before or after date defined in rules
* `born_before,Y-m-d` - i.e. `PESEL:born_before,2022-01-01`
* `born_after,Y-m-d` - i.e. `PESEL:born_after,2000-01-01`
2. 'REGON' - validate [REGON](https://pl.wikipedia.org/wiki/REGON) number
3. 'NIP' - validate [NIP](https://pl.wikipedia.org/wiki/NIP) number
4. 'id_card_number' - validate Polish ID Card number
5. 'post_code' - validate Polish post codes. By default accept codes in format 00-000 and 00000. You can change this with options:
* with_dash - only post codes with format 00-000 are valid
* without_dash - only post codew with format 00000 are valid
* without_dash - only post code with format 00000 are valid
6. 'PWZ' - validate PWZ (Prawo wykonywania zawodu lekarza/farmaceuty) numer (more information [HERE](https://nil.org.pl/rejestry/centralny-rejestr-lekarzy/zasady-weryfikowania-nr-prawa-wykonywania-zawodu))
7. 'passport_number' - validate Polish passport number

Expand Down Expand Up @@ -75,11 +70,23 @@ $validator = Validator::make(
);
```

Multiple options
```php
$validator = Validator::make(
$request->all(),
[
'pesel' => 'PESEL:gender_male:born_before,2022-01-01:born_after,2000-01-01',
]
);
```

## Code Authors

The algorithms used in the functions are based on existing solutions. Below are links to the sources:

* PESEL - [http://phpedia.pl/wiki/PESEL](http://phpedia.pl/wiki/PESEL)
* PESEL
* checksum checking algorithm - [http://phpedia.pl/wiki/PESEL](http://phpedia.pl/wiki/PESEL)
* extract/validate bith date - [KKSzymanowski/PESEL](https://github.com/KKSzymanowski/PESEL/blob/master/src/Pesel.php)
* REGON - [http://phpedia.pl/wiki/REGON](http://phpedia.pl/wiki/REGON)
* NIP - [http://phpedia.pl/wiki/NIP](http://phpedia.pl/wiki/NIP)
* id_card_number - [http://www.algorytm.org](http://www.algorytm.org/numery-identyfikacyjne/numer-dowodu-osobistego/do-php.html)
Expand Down
8 changes: 8 additions & 0 deletions UPGRADE_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Upgrade guide

## From any version to 4.2.x
From version 4.2.0, PESEL rule additionally check if birthdate encoded in given number is correct.
From now on, PESEL number as "00000000000", "44444444444" will no longer pass validation.
If your system accept or store numbers like this, the update may cause the application to malfunction.

See [PESEL birthdate encoding method](https://pl.wikipedia.org/wiki/PESEL#Data_urodzenia) for more information.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
],
"require": {
"php": "^7.3|^8.0",
"illuminate/support": "^8.0"
"illuminate/support": "^8.0",
"ext-calendar": "*"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
Expand Down
159 changes: 152 additions & 7 deletions src/Rules/PESELRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace PacerIT\LaravelPolishValidationRules\Rules;

use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;

/**
* Class PESELRule.
Expand All @@ -17,6 +19,12 @@ class PESELRule implements Rule
const GENDER_MALE = 0;
const GENDER_FEMALE = 1;

// Available parameters.
const PARAMETER_GENDER_MALE = 'gender_male';
const PARAMETER_GENDER_FEMALE = 'gender_female';
const PARAMETER_BORN_BEFORE = 'born_before';
const PARAMETER_BORN_AFTER = 'born_after';

/**
* Determine if the validation rule passes.
*
Expand All @@ -34,25 +42,44 @@ public function passes($attribute, $value, $parameters = [])
}

// Get parameters.
$parameters = explode(':', (string) Arr::first($parameters));
$parameters = implode(',', $parameters);
$parameters = explode(':', (string) $parameters);

$result = true;
foreach ($parameters as $mode) {
switch ($mode) {
case 'gender_male':
foreach ($parameters as $optionWithParameters) {
// Get option and parameters.
$parameterData = explode(',', $optionWithParameters);
$option = Arr::first($parameterData);

switch ($option) {
case self::PARAMETER_GENDER_MALE:
$result = $this->validateGender($value);
break;

case 'gender_female':
case self::PARAMETER_GENDER_FEMALE:
$result = $this->validateGender($value, self::GENDER_FEMALE);
break;

case self::PARAMETER_BORN_BEFORE:

$result = $this->validateBirthDate($value, (string) Arr::get($parameterData, 1), true);
break;

case self::PARAMETER_BORN_AFTER:
$result = $this->validateBirthDate($value, (string) Arr::get($parameterData, 1));
break;

default:
return true;
$result = true;
}

// Fail on first error.
if (!$result) {
return false;
}
}

return $result;
return true;
}

/**
Expand Down Expand Up @@ -84,6 +111,18 @@ private function checkPESEL(?string $string): bool
$intControlNr = ($int == 10) ? 0 : $int;

if ($intControlNr == $string[10]) {
// Check birth date.
$birthDate = $this->getBirthDate($string);
if ($birthDate === null) {
return false;
}

if ($birthDate->isBefore(Carbon::create(1800))
|| $birthDate->isAfter(Carbon::create(2300))
) {
return false;
}

return true;
}

Expand Down Expand Up @@ -114,6 +153,112 @@ private function validateGender(string $pesel, int $gender = self::GENDER_MALE):
return false;
}

/**
* Get birth date from PESEL to properly validate numbers like "00000000000", "44444444444".
*
* @param string $pesel
*
* @return Carbon|null
*
* @see https://github.com/KKSzymanowski/PESEL/blob/master/src/Pesel.php - Source of this method.
*/
private function getBirthDate(string $pesel): ?Carbon
{
// First 6 numbers of PESEL, encode birth date - year, month and day respectively.
$year = substr($pesel, 0, 2);
$month = substr($pesel, 2, 2);
$day = substr($pesel, 4, 2);

/*
* Get century number - first number of month.
* 8,9 - for 1800 - 1899
* 0,1 - for 1990 - 1999
* 2,3 - for 2000 - 2099
* 4,5 - for 2100 - 2199
* 6,7 - for 2200 - 2299
*
* See - https://pl.wikipedia.org/wiki/PESEL#Data_urodzenia
*/
$century = (int) substr($pesel, 2, 1);

/*
* Add offset of 2 - that's because the numbers 8 and 9 represent an earlier century than the other numbers.
* Offset allows you to eliminate the calculation problem.
* Numbers after add offset - 2,3,4,5,6,7,8,9,10,11
*/
$century += 2;

/*
* Get only integers from number set.
* Result numbers - 2,3,4,5,6,7,8,9,0,1
*/
$century %= 10;

/*
* Calculated value is the number that needs to be added to base century to get the correct century for the
* entered PESEL number.
*
* Results for each numbers should be:
* 8,9 - 0
* 0,1 - 1
* 2,3 - 2
* 4,5 - 3
* 6,7 - 4
*/
$century = round($century / 2, 0, PHP_ROUND_HALF_DOWN);

/*
* The number 18 is the base century value as the PESEL number is issued to people born after year 1800.
*/
$century += 18;
$year = $century.$year;

// If number after modulo is smaller than 10, add "0" on the begin on month number.
$month = str_pad($month % 20, 2, '0', STR_PAD_LEFT);

try {
if ($day < 1
|| $month < 1
|| $month > 12
|| $day > cal_days_in_month(CAL_GREGORIAN, $month, $year)
) {
return null;
}

return Carbon::parse(sprintf('%s-%s-%s', $year, $month, $day));
} catch (InvalidFormatException $exception) {
return null;
}
}

/**
* Validate if date is before or after given date.
*
* @param string $pesel
* @param string $date
* @param bool $before
*
* @return bool
*/
private function validateBirthDate(string $pesel, string $date, bool $before = false): bool
{
// Get birth date from PESEL.
$birthDate = $this->getBirthDate($pesel);

// Parse date from request.
try {
$dateToCompare = Carbon::parse($date);
} catch (InvalidFormatException $exception) {
return false;
}

if ($before) {
return $birthDate->isBefore($dateToCompare);
}

return $birthDate->isAfter($dateToCompare);
}

/**
* Get the validation error message.
*
Expand Down
Loading

0 comments on commit 2b23041

Please sign in to comment.