├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── CoordinationNumber.php ├── IdentityNumber.php ├── IdentityNumberFormatter.php ├── IdentityNumberServiceProvider.php ├── OrganizationNumber.php ├── Pin.php └── Validator.php └── tests └── IdentityNumberTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | /.phpunit.result.cache 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | dist: xenial 4 | 5 | os: linux 6 | 7 | before_script: 8 | - travis_retry composer self-update 9 | - travis_retry composer require "illuminate/support:${ILLUMINATE_VERSION}" 10 | 11 | script: 12 | - composer test 13 | 14 | jobs: 15 | include: 16 | - php: 7.2 17 | env: ILLUMINATE_VERSION=^6.0 18 | - php: 7.2 19 | env: ILLUMINATE_VERSION=^7.0 20 | - php: 7.3 21 | env: ILLUMINATE_VERSION=^6.0 22 | - php: 7.3 23 | env: ILLUMINATE_VERSION=^7.0 24 | - php: 7.3 25 | env: ILLUMINATE_VERSION=^8.0 26 | - php: 7.4 27 | env: ILLUMINATE_VERSION=^7.0 28 | - php: 7.4 29 | env: ILLUMINATE_VERSION=^8.0 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to identity-numbers will be documented in this file 4 | 5 | ### Added 6 | - Nothing 7 | 8 | ### Deprecated 9 | - Nothing 10 | 11 | ### Fixed 12 | - Nothing 13 | 14 | ### Removed 15 | - Nothing 16 | 17 | ### Security 18 | - Nothing 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Marcus Olsson 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swedish "personnummer" validator for Laravel 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE.md) 5 | [![Build Status][ico-travis]][link-travis] 6 | [![Scrutinizer Score][ico-scrutinizer]][link-scrutinizer] 7 | 8 | --- 9 | 10 | ⚠️ **Abandoned** This package has been abandoned in favor of [olssonm/swedish-entity](https://github.com/olssonm/swedish-entity). 11 | 12 | While the package will be usable for the forseeable future, it will not be updated. Please migrate to `olssonm/swedish-entity` when possible. 13 | 14 | --- 15 | 16 | Validator for Swedish "personnummer" (a.k.a. personal identity number, social security number or simply "PIN"). 17 | 18 | This validator also handles Swedish organization numbers and the temporary personal identity number known as "Samordningsnummer" (a.k.a. coordination number). 19 | 20 | For use either as a standalone package, or with Laravel. 21 | 22 | The package does not only apply the Luhn-algorithm for the last four digits, but also checks that the date of birth is a valid date. 23 | 24 | Of course you can throw pretty much any format you wish at the validator, ie. 10-digit variant (`7712112775`) or the 12-digit variant (`197712112775`) and with or without a hyphen (`771211-2775`, `19771211-2775`). 25 | 26 | ## Install 27 | 28 | Via Composer 29 | 30 | ``` bash 31 | $ composer require olssonm/identity-number 32 | ``` 33 | 34 | **Within Laravel** 35 | 36 | This package uses Package Auto-Discovery for loading the service provider. Once installed you should see the message 37 | 38 | ``` 39 | Discovered Package: olssonm/identity-number 40 | ``` 41 | 42 | Else, per standard Laravel-procedure, just register the package in your providers array: 43 | 44 | ``` php 45 | 'providers' => [ 46 | Olssonm\IdentityNumber\IdentityNumberServiceProvider::class, 47 | ] 48 | ``` 49 | 50 | ## Usage 51 | 52 | ### Standalone 53 | 54 | The package is usable straight out of the box once installed with composer: 55 | 56 | ``` php 57 | use Olssonm\IdentityNumber\Pin; 58 | ``` 59 | 60 | #### Personnummer ("personal identity number") 61 | 62 | ``` php 63 | Pin::isValid('19771211-2775'); // Defaults to identity number 64 | // true 65 | 66 | Pin::isValid('19771211-2775', 'identity'); // Identity validation specified 67 | // true 68 | ``` 69 | 70 | #### Organisationsnummer ("organization number") 71 | 72 | ``` php 73 | Pin::isValid('556016-0681', 'organization') 74 | // true 75 | ``` 76 | 77 | #### Samordningsnummer ("coordination number") 78 | 79 | ``` php 80 | Pin::isValid('19671180-2850', 'coordination'); 81 | // true 82 | ``` 83 | 84 | The coordination-number validator handles the same way as the personal identity-validator but does not run a check/validation on the date of birth. 85 | 86 | ### The IdentityNumberFormatter-class 87 | 88 | The IdentityNumberFormatter-class allows you to format a PIN/identity for your custom needs. The class is also used internally for validation, but if you want to make sure you save the same value in the database as you pass for validation – you should also apply the formatting before validating. 89 | 90 | ```php 91 | 92 | use Olssonm\IdentityNumber\IdentityNumberFormatter; 93 | 94 | // Format to a 10-character PIN without a seperator 95 | $formatter = new IdentityNumberFormatter('19860210-7313', 10, false); 96 | 97 | // Get the formatted output 98 | $formatter->getFormatted(); // 8602107313 99 | 100 | // You can also clean the number from all unwanted characters 101 | (new IdentityNumberFormatter('a19860210 - 7313', 12, true))->clean()->getFormatted(); // 19860210-7313 102 | ``` 103 | 104 | ### Laravel validators 105 | 106 | The package extends the `Illuminate\Validator` via a service provider, so all you have to do is use the `identity_number`-, `coordination_number`- and `organization_number`-rules, just as you would with any other rule. 107 | 108 | ``` php 109 | // Personal identity numbers 110 | public function store(Request $request) { 111 | $this->validate($request, [ 112 | 'number' => 'required|identity_number' 113 | ]); 114 | } 115 | 116 | // Coordination numbers 117 | public function store(Request $request) { 118 | $this->validate($request, [ 119 | 'number' => 'required|coordination_number' 120 | ]); 121 | } 122 | 123 | // Organization numbers 124 | public function store(Request $request) { 125 | $this->validate($request, [ 126 | 'number' => 'required|organization_number' 127 | ]); 128 | } 129 | ``` 130 | 131 | And of course, you can roll your own error messages: 132 | 133 | ``` php 134 | $validator = Validator::make($request->all(), [ 135 | 'number' => 'required|identity_number' 136 | ], [ 137 | 'number.identity_number' => "Hey! That's not a personnummer!" 138 | ]); 139 | 140 | if($validator->fails()) { 141 | return $this->returnWithErrorAndInput($validator); 142 | } 143 | ``` 144 | 145 | If you're using the validation throughout your application, you also might want to put the error message in your lang-files. 146 | 147 | ## Testing 148 | 149 | ``` bash 150 | $ composer test 151 | ``` 152 | 153 | or 154 | 155 | ``` bash 156 | $ phpunit 157 | ``` 158 | 159 | ## License 160 | 161 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 162 | 163 | © 2020 [Marcus Olsson](https://marcusolsson.me). 164 | 165 | [ico-version]: https://img.shields.io/packagist/v/olssonm/identity-number.svg?style=flat-square 166 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 167 | [ico-travis]: https://img.shields.io/travis/olssonm/identity-number/master.svg?style=flat-square 168 | [link-packagist]: https://packagist.org/packages/olssonm/identity-number 169 | [link-travis]: https://travis-ci.org/olssonm/identity-number 170 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/g/olssonm/identity-number.svg?style=flat-square 171 | [link-scrutinizer]: https://scrutinizer-ci.com/g/olssonm/identity-number 172 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "olssonm/identity-number", 3 | "description": "Laravel validator for Swedish personal identity numbers / social security numbers (personnummer)", 4 | "keywords": [ 5 | "Laravel", 6 | "package", 7 | "olssonm", 8 | "swedish", 9 | "personal identity number", 10 | "social security number", 11 | "personnummer" 12 | ], 13 | "homepage": "https://github.com/olssonm/identity-number", 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "Marcus Olsson", 18 | "email": "contact@marcusolsson.me", 19 | "homepage": "https://marcusolsson.me" 20 | } 21 | ], 22 | "require": { 23 | "php" : "^7.2", 24 | "illuminate/support": ">=6.0" 25 | }, 26 | "require-dev": { 27 | "orchestra/testbench": ">=4.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Olssonm\\IdentityNumber\\": "src" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Olssonm\\IdentityNumber\\Test\\": "tests" 37 | } 38 | }, 39 | "scripts": { 40 | "test": "phpunit" 41 | }, 42 | "extra": { 43 | "branch-alias": { 44 | "dev-master": "6.x-dev" 45 | }, 46 | "laravel": { 47 | "providers": [ 48 | "Olssonm\\IdentityNumber\\IdentityNumberServiceProvider" 49 | ] 50 | } 51 | }, 52 | "minimum-stability": "stable" 53 | } 54 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/CoordinationNumber.php: -------------------------------------------------------------------------------- 1 | type = Validator::COORDINATIONNUMBER; 12 | parent::__construct(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/IdentityNumber.php: -------------------------------------------------------------------------------- 1 | type = Validator::IDENTITYNUMBER; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/IdentityNumberFormatter.php: -------------------------------------------------------------------------------- 1 | value = $value; 50 | $this->characters = $characters; 51 | $this->useHyphen = $useHyphen; 52 | } 53 | 54 | /** 55 | * Clean the value-string from unwanted characters 56 | * 57 | * @return \Olssonm\IdentityNumber\IdentityNumberFormatter 58 | */ 59 | public function clean() 60 | { 61 | // Clean string from invalid values 62 | $pattern = '0123456789-+'; 63 | $this->value = preg_replace("/[^" . preg_quote($pattern, "/") . "]/", '', $this->value); 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Retrieve the formatted personal identity number 70 | * 71 | * @return string the personal identity number 72 | */ 73 | public function getFormatted() 74 | { 75 | $value = $this->value; 76 | $characters = $this->characters; 77 | $useHyphen = $this->useHyphen; 78 | 79 | // Check if "+" is present, then use it as a hyphen 80 | if (strpos($value, '+') !== false) { 81 | $this->typeHyphen = '+'; 82 | } 83 | 84 | // Remove hyphen 85 | $value = str_replace(['-', '+'], '', $value); 86 | 87 | if (strlen($value) != 12 && strlen($value) != 10) { 88 | return false; 89 | } 90 | 91 | if ($characters == 12 && strlen($value) == 10) { 92 | $value = 19 . $value; 93 | } 94 | 95 | if ($characters == 10 && strlen($value) == 12) { 96 | $newNumber = null; 97 | for ($i = 0; $i < strlen($value); $i++) { 98 | if ($i > 1) { 99 | $newNumber .= $value[$i]; 100 | } 101 | } 102 | $value = $newNumber; 103 | } 104 | 105 | // Insert hyphen if you need to 106 | if ($useHyphen == true) { 107 | $newNumber = null; 108 | for ($i = 0; $i < strlen($value); $i++) { 109 | $newNumber .= $value[$i]; 110 | if ($i == strlen($value) - 5) { 111 | $newNumber .= $this->typeHyphen; 112 | } 113 | } 114 | $value = $newNumber; 115 | } 116 | 117 | return $value; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/IdentityNumberServiceProvider.php: -------------------------------------------------------------------------------- 1 | app['validator']->extend('identity_number', function($attribute, $value, $parameters) 20 | { 21 | return Pin::isValid($value, 'identity'); 22 | }); 23 | 24 | /** 25 | * Extend the Laravel Validator with the "organization_number" rule 26 | */ 27 | $this->app['validator']->extend('organization_number', function($attribute, $value, $parameters) 28 | { 29 | return Pin::isValid($value, 'organization'); 30 | }); 31 | 32 | /** 33 | * Extend the Laravel Validator with the "coordination_number" rule 34 | */ 35 | $this->app['validator']->extend('coordination_number', function($attribute, $value, $parameters) 36 | { 37 | return Pin::isValid($value, 'coordination'); 38 | }); 39 | } 40 | 41 | /** 42 | * Register any package services. 43 | * 44 | * @return void 45 | */ 46 | public function register() 47 | { 48 | // 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/OrganizationNumber.php: -------------------------------------------------------------------------------- 1 | type = Validator::ORGANIZATIONNUMBER; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Pin.php: -------------------------------------------------------------------------------- 1 | isValid($number); 23 | break; 24 | 25 | case 'organization': 26 | $validator = new OrganizationNumber(); 27 | return $validator->isValid($number); 28 | break; 29 | 30 | case 'coordination': 31 | $validator = new CoordinationNumber(); 32 | return $validator->isValid($number); 33 | break; 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Validator.php: -------------------------------------------------------------------------------- 1 | formatNumber($number); 54 | 55 | if (!$number) { 56 | return false; 57 | } 58 | 59 | switch ($this->type) { 60 | case Validator::IDENTITYNUMBER: 61 | return $this->validate($number, true); 62 | break; 63 | 64 | case Validator::ORGANIZATIONNUMBER: 65 | return $this->validate($number, false); 66 | break; 67 | 68 | case Validator::COORDINATIONNUMBER: 69 | return $this->validate($number, false); 70 | break; 71 | } 72 | 73 | return false; 74 | } 75 | 76 | /** 77 | * Internal validator 78 | * 79 | * @param string $number the value under validation 80 | * @param boolean $checkDate if date validation is to be performed 81 | * @return boolean 82 | */ 83 | private function validate($number, $checkDate) 84 | { 85 | // Perform simple test on invalid numbers 86 | if (in_array($number, $this->invalidNumbers)) { 87 | return false; 88 | } 89 | 90 | // If checking for a date 91 | if ($checkDate == true) { 92 | $dateTest = substr($number, 0, 6); 93 | $validDate = $this->validDate($dateTest); 94 | if ($validDate == false) { 95 | return false; 96 | } 97 | } 98 | 99 | // Check luhn 100 | return $this->luhn($number); 101 | } 102 | 103 | /** 104 | * Run the IdentityNumberFormatter on the specified number 105 | * 106 | * @param string $number 107 | * @return string 108 | */ 109 | protected function formatNumber($number) 110 | { 111 | $formatter = new IdentityNumberFormatter($number, 10, false); 112 | $value = $formatter->getFormatted(); 113 | 114 | return $value; 115 | } 116 | 117 | /** 118 | * Perform luhn validation 119 | * 120 | * @param string $number 121 | * @return boolean 122 | */ 123 | private function luhn($number) 124 | { 125 | settype($number, 'string'); 126 | $number = array_reverse(str_split($number)); 127 | $sum = 0; 128 | foreach ($number as $key => $number) { 129 | if (!is_numeric($number)) { 130 | return false; 131 | } 132 | if ($key % 2) { 133 | $number = $number * 2; 134 | } 135 | $sum += ($number >= 10 ? $number - 9 : $number); 136 | } 137 | return ($sum % 10 === 0); 138 | } 139 | 140 | /** 141 | * Validate a date as a format 142 | * 143 | * @param string $dateTest the date to be tested 144 | * @param string $format the date format 145 | * @return boolean 146 | */ 147 | private function validDate($dateTest, $format = 'ymd') 148 | { 149 | $date = DateTime::createFromFormat('ymd', $dateTest); 150 | return $date && $date->format($format) == $dateTest; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/IdentityNumberTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('19860210-7313', $test1->getFormatted()); 34 | 35 | $test2 = new IdentityNumberFormatter('19860210-7313', 10, true); 36 | $this->assertEquals('860210-7313', $test2->getFormatted()); 37 | 38 | $test3 = new IdentityNumberFormatter('19860210-7313', 10, false); 39 | $this->assertEquals('8602107313', $test3->getFormatted()); 40 | 41 | $test4 = new IdentityNumberFormatter('19121012+4412', 12, true); 42 | $this->assertEquals('19121012+4412', $test4->getFormatted()); 43 | 44 | $test5 = new IdentityNumberFormatter('19121012+4412', 10, true); 45 | $this->assertEquals('121012+4412', $test5->getFormatted()); 46 | 47 | $test6 = new IdentityNumberFormatter('19121012+4412', 10, false); 48 | $this->assertEquals('1210124412', $test6->getFormatted()); 49 | 50 | $test7 = new IdentityNumberFormatter('19860210 + 1100', 10, false); 51 | $this->assertEquals('8602101100', $test7->clean()->getFormatted()); 52 | 53 | $test8 = new IdentityNumberFormatter('aa19860210-1100bb', 10, true); 54 | $this->assertEquals('860210-1100', $test8->clean()->getFormatted()); 55 | } 56 | 57 | /** @test */ 58 | public function testStandaloneCorrectIdentityNumbers() 59 | { 60 | $this->assertTrue(Pin::isValid('600411-8177')); 61 | $this->assertTrue(Pin::isValid('19860210-7313')); 62 | $this->assertTrue(Pin::isValid('600411+8177')); 63 | $this->assertTrue(Pin::isValid('19860210+7313')); 64 | $this->assertTrue(Pin::isValid('8905247188', 'identity')); 65 | $this->assertTrue(Pin::isValid('196711202850', 'identity')); 66 | } 67 | 68 | /** @test */ 69 | public function testStandaloneIncorrectIdentityNumbers() 70 | { 71 | $this->assertFalse(Pin::isValid('600412-8177')); 72 | $this->assertFalse(Pin::isValid('19860211-7313')); 73 | $this->assertFalse(Pin::isValid('8905257188')); 74 | $this->assertFalse(Pin::isValid('196711212850')); 75 | 76 | // Malformed 77 | $this->assertFalse(Pin::isValid('890')); 78 | $this->assertFalse(Pin::isValid('aaa999')); 79 | 80 | // Obviously false 81 | $this->assertFalse(Pin::isValid('00000000-0000')); 82 | $this->assertFalse(Pin::isValid('11111111-1111')); 83 | $this->assertFalse(Pin::isValid('22222222-2222')); 84 | $this->assertFalse(Pin::isValid('33333333-3333')); 85 | $this->assertFalse(Pin::isValid('44444444-4444')); 86 | $this->assertFalse(Pin::isValid('55555555-5555')); 87 | $this->assertFalse(Pin::isValid('66666666-6666')); 88 | $this->assertFalse(Pin::isValid('77777777-7777')); 89 | $this->assertFalse(Pin::isValid('88888888-8888')); 90 | $this->assertFalse(Pin::isValid('99999999-9999')); 91 | } 92 | 93 | /** @test */ 94 | public function testStandaloneCorrectOrganizationNumbers() 95 | { 96 | $this->assertTrue(Pin::isValid('556016-0680', 'organization')); // Ericsson AB 97 | $this->assertTrue(Pin::isValid('556103-4249', 'organization')); // Telia AB 98 | } 99 | 100 | /** @test */ 101 | public function testStandaloneIncorrectOrganizationNumbers() 102 | { 103 | $this->assertFalse(Pin::isValid('556016-0681', 'organization')); // Ericsson AB 104 | $this->assertFalse(Pin::isValid('556103-4240', 'organization')); // Telia AB 105 | 106 | // Malformed 107 | $this->assertFalse(Pin::isValid('55601', 'organization')); 108 | $this->assertFalse(Pin::isValid('5561035', 'organization')); 109 | $this->assertFalse(Pin::isValid('aaa888', 'organization')); 110 | } 111 | 112 | /** @test */ 113 | public function testStandaloneCorrectCoordinationNumbers() 114 | { 115 | $this->assertTrue(Pin::isValid('780161-1117', 'coordination')); 116 | $this->assertTrue(Pin::isValid('19610280-2425', 'coordination')); 117 | } 118 | 119 | /** @test */ 120 | public function testStandaloneIncorrectCoordinationNumbers() 121 | { 122 | $this->assertFalse(Pin::isValid('780161-1116', 'coordination')); 123 | $this->assertFalse(Pin::isValid('19610280-2424', 'coordination')); 124 | 125 | // Malformed 126 | $this->assertFalse(Pin::isValid('7801', 'coordination')); 127 | $this->assertFalse(Pin::isValid('19610280-242', 'coordination')); 128 | $this->assertFalse(Pin::isValid('aaa888', 'coordination')); 129 | } 130 | 131 | /** @test */ 132 | public function testStandaloneGibberishData() 133 | { 134 | $this->assertFalse(Pin::isValid(null)); 135 | $this->assertFalse(Pin::isValid(false)); 136 | $this->assertFalse(Pin::isValid(true)); 137 | $this->assertFalse(Pin::isValid(111000)); 138 | $this->assertFalse(Pin::isValid(191919191919)); 139 | $this->assertFalse(Pin::isValid(19870101)); 140 | $this->assertFalse(Pin::isValid('780161 - 1117', 'coordination')); 141 | $this->assertFalse(Pin::isValid('Firstname Lastname')); 142 | $this->assertFalse(Pin::isValid('Gibberish')); 143 | } 144 | 145 | /** @test */ 146 | public function testCoordinationWorkflow() 147 | { 148 | $this->assertFalse(Pin::isValid('19860210 - 7313', 'coordination')); 149 | $this->assertTrue(Pin::isValid((new IdentityNumberFormatter('a19860210 - 7313', 10, true))->clean()->getFormatted(), 'coordination')); 150 | } 151 | 152 | /** @test */ 153 | public function testCorrectIdentityNumbers() 154 | { 155 | $this->assertTrue($this->validate('600411-8177')); 156 | $this->assertTrue($this->validate('19860210-7313')); 157 | $this->assertTrue($this->validate('8905247188')); 158 | $this->assertTrue($this->validate('196711202850')); 159 | } 160 | 161 | /** @test */ 162 | public function testCorrectIdentityNumbersWithPlusAsHyphen() 163 | { 164 | $this->assertTrue($this->validate('600411+8177')); 165 | $this->assertTrue($this->validate('19860210+7313')); 166 | } 167 | 168 | /** @test */ 169 | public function testIncorrectIdentityNumbers() 170 | { 171 | $this->assertFalse($this->validate('600412-8177')); 172 | $this->assertFalse($this->validate('19860211-7313')); 173 | $this->assertFalse($this->validate('8905257188')); 174 | $this->assertFalse($this->validate('196711212850')); 175 | 176 | // Obviously false 177 | $this->assertFalse($this->validate('000000000000')); 178 | $this->assertFalse($this->validate('111111111111')); 179 | $this->assertFalse($this->validate('222222222222')); 180 | $this->assertFalse($this->validate('333333333333')); 181 | $this->assertFalse($this->validate('444444444444')); 182 | $this->assertFalse($this->validate('555555555555')); 183 | $this->assertFalse($this->validate('666666666666')); 184 | $this->assertFalse($this->validate('777777777777')); 185 | $this->assertFalse($this->validate('888888888888')); 186 | $this->assertFalse($this->validate('999999999999')); 187 | } 188 | 189 | /** @test **/ 190 | public function testCorrectOrganizationNumbers() 191 | { 192 | $this->assertTrue($this->validateOrgNo('556809-9963')); // IKEA AB 193 | $this->assertTrue($this->validateOrgNo('969663-7033')); // Skellefteå Energi Underhåll Handelsbolag 194 | } 195 | 196 | /** @test **/ 197 | public function testIncorrectOrganizationNumbers() 198 | { 199 | // Standalone 200 | $this->assertFalse(Pin::isValid('556016-0681', false)); // Ericsson AB 201 | $this->assertFalse(Pin::isValid('556103-4240', false)); // Telia AB 202 | 203 | // Validation 204 | $this->assertFalse($this->validateOrgNo('556809-9964')); // IKEA AB 205 | $this->assertFalse($this->validateOrgNo('969663-7034')); // Skellefteå Energi Underhåll Handelsbolag 206 | 207 | // Validate so that companies org. numbers doesn't pass as a PIN 208 | $this->assertFalse(Pin::isValid('556016-0681')); // Ericsson AB 209 | $this->assertFalse(Pin::isValid('556103-4240')); // Telia AB 210 | } 211 | 212 | /** @test **/ 213 | public function testCorrectCoordinationNumbers() 214 | { 215 | $this->assertTrue($this->validateCoordNo('6102802425')); 216 | $this->assertTrue($this->validateCoordNo('19890362-4529')); 217 | } 218 | 219 | /** @test **/ 220 | public function testIncorrectCoordinationNumbers() 221 | { 222 | $this->assertFalse($this->validateCoordNo('6102802424')); 223 | $this->assertFalse($this->validateCoordNo('6102202425')); 224 | $this->assertFalse($this->validateCoordNo('19890362-4528')); 225 | $this->assertFalse($this->validateCoordNo('19890302-4529')); 226 | } 227 | 228 | /** @test **/ 229 | public function testOrgNoAsPin() 230 | { 231 | // Validate so that companies org. numbers doesn't pass as a PIN 232 | $this->assertFalse(Pin::isValid('556016-0681', 'organization')); // Ericsson AB 233 | $this->assertFalse(Pin::isValid('556103-4240', 'organization')); // Telia AB 234 | $this->assertFalse($this->validate('556809-9964')); // IKEA AB 235 | $this->assertFalse($this->validate('969663-7034')); // Skellefteå Energi Underhåll Handelsbolag 236 | } 237 | 238 | /** @test */ 239 | public function testGibberishData() 240 | { 241 | $this->assertFalse($this->validate(null)); 242 | $this->assertFalse($this->validate(false)); 243 | $this->assertFalse($this->validate(true)); 244 | $this->assertFalse($this->validate(111000)); 245 | $this->assertFalse($this->validate(191919191919)); 246 | $this->assertFalse($this->validate(19870822)); 247 | $this->assertFalse($this->validate('Firstname Lastname')); 248 | $this->assertFalse($this->validate('Gibberish')); 249 | } 250 | 251 | /** @test */ 252 | public function testErrorMessage() 253 | { 254 | $this->assertEquals('A standard message', $this->validateWithErrorMessage('600412-8177', 'A standard message')); 255 | $this->assertEquals('validation.identity_number', $this->validateWithErrorMessage('600412-8177', null)); 256 | $this->assertEquals(true, $this->validateWithErrorMessage('600412-8177', true)); 257 | $this->assertEquals(false, $this->validateWithErrorMessage('600412-8177', false)); 258 | } 259 | 260 | /** 261 | * Validate personal identity number 262 | * 263 | * @param mixed $pin the personal identity number 264 | * @return bool whether the validation passes or not 265 | */ 266 | private function validate($pin) 267 | { 268 | $data = ['identity_no' => $pin]; 269 | $validator = Validator::make($data, [ 270 | 'identity_no' => 'identity_number|required', 271 | ]); 272 | 273 | return $validator->passes(); 274 | } 275 | 276 | /** 277 | * Validate org no 278 | * 279 | * @param mixed $pin the personal identity number 280 | * @return bool whether the validation passes or not 281 | */ 282 | private function validateOrgNo($number) 283 | { 284 | $data = ['org_no' => $number]; 285 | $validator = Validator::make($data, [ 286 | 'org_no' => 'organization_number|required', 287 | ]); 288 | 289 | return $validator->passes(); 290 | } 291 | 292 | /** 293 | * Validate coordination number 294 | * 295 | * @param mixed $pin the personal identity number 296 | * @return bool whether the validation passes or not 297 | */ 298 | private function validateCoordNo($number) 299 | { 300 | $data = ['coordination_no' => $number]; 301 | $validator = Validator::make($data, [ 302 | 'coordination_no' => 'coordination_number|required', 303 | ]); 304 | 305 | return $validator->passes(); 306 | } 307 | 308 | /** 309 | * Validate with error message 310 | * 311 | * @param mixed $pin the personal identity number 312 | * @return bool whether the validation passes or not 313 | */ 314 | private function validateWithErrorMessage($pin, $message) 315 | { 316 | $data = ['pnr' => $pin]; 317 | $validator = Validator::make($data, [ 318 | 'pnr' => 'identity_number', 319 | ], [ 320 | 'pnr.identity_number' => $message 321 | ]); 322 | 323 | $errors = $validator->errors(); 324 | 325 | return $errors->first('pnr'); 326 | } 327 | 328 | public static function tearDownAfterClass(): void 329 | { 330 | parent::tearDownAfterClass(); 331 | } 332 | } 333 | --------------------------------------------------------------------------------