Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
vendor/
.phpunit.result.cache
index.php
.idea/
12 changes: 0 additions & 12 deletions .travis.yml

This file was deleted.

8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# VIN Decoder
[![Build status](https://api.travis-ci.org/5baddi/vin-decoder.svg)](https://api.travis-ci.org/5baddi/vin-decoder)

[![Licence](https://img.shields.io/github/license/5baddi/vin-decoder?logo=MIT)](./LICENSE)
![PHP Version](https://img.shields.io/packagist/php-v/baddiservices/safehtml)
[![Open issues](https://img.shields.io/github/issues-raw/5baddi/vin-decoder)](https://github.com/5baddi/vin-decoder/issues?q=is%3Aissue+is%3Aopen)
[![Stars](https://img.shields.io/github/stars/5baddi/vin-decoder)](https://github.com/5baddi/vin-decoder/stargazers)
[![Downloads](https://img.shields.io/packagist/dm/baddiservices/safehtml)](https://packagist.org/packages/baddiservices/safehtml)
[![Twitter Follow](https://img.shields.io/twitter/follow/5baddi?style=social)](https://twitter.com/intent/follow?screen_name=5baddi)

PHP package provide VIN number decoding to retrieve vehicle informations

Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"name": "baddiservices/vin-decoder",
"type": "library",
"description": "Universal vin decoder to retrieve vehicle informations",
"description": "Universal vin decoder to retrieve vehicle information's",
"keywords": ["vin","vin decoder","decoder","vehicle", "dealer", "dealership"],
"homepage": "https://baddi.info",
"license": "MIT",
"authors": [
{
"name": "Mohamed Baddi",
"email": "project@baddi.info",
"homepage": "https://baddi.info",
"role": "Developer"
"homepage": "https://baddi.info"
}
],
"require": {
"php": "^7.2"
"php": ">=8.0",
"ext-ctype": "*"
},
"require-dev": {
"phpunit/phpunit": "^8"
Expand Down
4 changes: 3 additions & 1 deletion src/VINConstants.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class VINConstants
const CHECKSUM = 10;
const CHECKSUM_FACTOR = 11;
const CHECKSUM_POSITION = 8;

const TRANSLITERATION = [
'A' => 1, 'J' => 1,
'B' => 2, 'K' => 2, 'S' => 2,
Expand All @@ -31,7 +32,8 @@ class VINConstants
'H' => 8, 'Y' => 8,
'R' => 9, 'Z' => 9,
];
const WEIGHTEDFACTORS = [

const WEIGHTED_FACTORS = [
1 => 8, 10 => 9,
2 => 7, 11 => 8,
3 => 6, 12 => 7,
Expand Down
150 changes: 61 additions & 89 deletions src/VINDecoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,56 @@
*/
class VINDecoder
{
public $vin;
public string $vin;

private $checkDigit;
private int $checkDigit;

private $wmi;
private $vds;
private $vis;
private string $wmi;
private string $vds;
private string $vis;

private array $transliteration = [];

private $transliteration = [];

private $weightedProduct = [];
private array $weightedProduct = [];

private $calculatedWeightedProduct = 0;
private int $calculatedWeightedProduct = 0;

/**
* constructor
*
* @param string $vin
* @throws Exception
*/
public function __construct(string $vin)
{
try{
// Validate the VIN length
if(!$this->validate($vin))
throw new Exception("Invalid VIN characters");

// Store the vin into this instance
$this->vin = strtoupper($vin);

// Validate the VIN
if(!$this->checksum())
throw new Exception("Invalid VIN");

// Parse the VIN details identifiers
$this->wmi = substr($this->vin, 0, 3);
$this->vds = substr($this->vin, 3, 6);
$this->vis = substr($this->vin, 9, 8);
}catch(Exception $ex){
throw new Exception("Unknow error");
// Validate the VIN length
if(! $this->validate($vin)) {
throw new Exception("Invalid VIN characters");
}

// Store the vin into this instance
$this->vin = strtoupper($vin);

// Validate the VIN
if(! $this->checksum()) {
throw new Exception("Invalid VIN");
}

// Parse the VIN details identifiers
$this->wmi = substr($this->vin, 0, 3);
$this->vds = substr($this->vin, 3, 6);
$this->vis = substr($this->vin, 9, 8);
}

/**
* Check if the VIN is valid
* @return boolean
*
* @throws Exception
*/
public function isValid() : bool
{
return $this->validate($this->vin);
return @$this->validate($this->vin);
}

/**
* Check digit getter
*
* @return int
*/
public function getCheckDigit() : int
{
Expand All @@ -80,59 +74,39 @@ public function getCheckDigit() : int

/**
* Extract the manufacturer brand name
*
* @return string|null
*/
public function getManufacturer() : ?string
{
// Load manufacturers list
$manufactures = json_decode(file_get_contents(__DIR__ . "/data/manufacturers.json"), true);

// Get the manufacturers brand name
if(isset($manufactures[$this->wmi]))
return ucwords($manufactures[$this->wmi]);

return null;
return ! empty($manufactures[$this->wmi]) ? ucwords($manufactures[$this->wmi]) : null;
}

/**
* Get country by code
*
* @return string|null
*/
public function getCountry() : ?string
{
// Load countries list
$countries = json_decode(file_get_contents(__DIR__ . "/data/countries.json"), true);

// Get country name by code
if(isset($countries[$this->getCountryCode()]))
return ucwords($countries[$this->getCountryCode()]);

return null;
return ! empty($countries[$this->getCountryCode()]) ? ucwords($countries[$this->getCountryCode()]) : null;
}

/**
* Get year by code
*
* @return string|null
*/
public function getYear() : ?int
{
// Load years list
$years = json_decode(file_get_contents(__DIR__ . "/data/years.json"), true);

// Get year by code
if(isset($years[$this->vis[0]]))
return $years[$this->vis[0]];

return null;
return $years[$this->vis[0]] ?? null;
}

/**
* Extract serial numver
*
* @return string
* Extract serial number
*/
public function getSerialNumber() : string
{
Expand All @@ -141,8 +115,6 @@ public function getSerialNumber() : string

/**
* Extract security code
*
* @return char
*/
public function getSecurityCode() : string
{
Expand All @@ -152,43 +124,43 @@ public function getSecurityCode() : string
/**
* Check the vin length and regex
*
* @param string $vin
* @return boolean
* @throws Exception
*/
private function validate(string $vin) : bool
{
// Verify if the vin corresponds to a vehicle manufactured before 1981
if(strlen($vin) == 11)
if(strlen($vin) == 11) {
throw new Exception("Information on vehicles manufactured before 1981 is limited");
elseif(strlen($vin) != VINConstants::VIN_LENGTH)
}

if(strlen($vin) != VINConstants::VIN_LENGTH) {
throw new Exception("VIN number must be 17 characters");
}

return (bool)preg_match('/^[a-zA-Z0-9]+$/', $vin);
}

/**
* Find and replace illegal characters
*
* @return void
*/
private function illegalCharacters() : void
{
// Replace - and _ and whitespace
$this->vin = str_replace('-', '', $this->vin);
$this->vin = str_replace(' ', '', $this->vin);
$this->vin = str_replace(['-', ' '], '', $this->vin);
$this->vin = str_replace('_', 0, $this->vin);

// Replace the illegal characters
foreach(VINConstants::EXCLUDED_LETTERS as $letter){
if(strpos($this->vin, $letter) !== false)
$this->vin = str_replace($letter, ($letter === 'I' ? 1 : 0), $this->vin);
if(! str_contains($this->vin, $letter)) {
continue;
}

$this->vin = str_replace($letter, ($letter === 'I' ? 1 : 0), $this->vin);
}
}

/**
* Check VIN sum and digit by transliteration and weighted product
*
* @return boolean
*/
private function checksum() : bool
{
Expand All @@ -198,37 +170,37 @@ private function checksum() : bool
// Convert the VIN letters using the transliteration
foreach(str_split($this->vin) as $letter){
// Unknown transliteration
if(ctype_alpha($letter) && isset(VINConstants::TRANSLITERATION[$letter]))
if(ctype_alpha($letter) && isset(VINConstants::TRANSLITERATION[$letter])) {
$this->transliteration[] = VINConstants::TRANSLITERATION[$letter];
else
$this->transliteration[] = $letter;

continue;
}

$this->transliteration[] = $letter;
}

// Calculate the weighted product by wieghted factor
// Calculate the weighted product by weighted factor
foreach($this->transliteration as $key => $trans){
$this->weightedProduct[] = VINConstants::WEIGHTEDFACTORS[$key + 1];
$this->calculatedWeightedProduct += $trans * VINConstants::WEIGHTEDFACTORS[$key + 1];
$this->weightedProduct[] = VINConstants::WEIGHTED_FACTORS[$key + 1];
$this->calculatedWeightedProduct += $trans * VINConstants::WEIGHTED_FACTORS[$key + 1];
}

// Check digit
$check = substr($this->vin, VINConstants::CHECKSUM_POSITION, 1);
$this->checkDigit = $this->calculatedWeightedProduct % VINConstants::CHECKSUM_FACTOR;

// Verify the vin is valid
if($this->checkDigit == VINConstants::CHECKSUM && $check === VINConstants::CHECKSUM_LETTER)
return true;
elseif(isset(VINConstants::WEIGHTEDFACTORS[$check]) && ctype_alpha($check) && VINConstants::WEIGHTEDFACTORS[$check] == $mod)
return true;
elseif($this->checkDigit == $check)
return true;

return false;
return
($this->checkDigit === VINConstants::CHECKSUM && $check === VINConstants::CHECKSUM_LETTER)
|| (
! ctype_alpha($check)
&& isset(VINConstants::WEIGHTED_FACTORS[$check])
&& VINConstants::WEIGHTED_FACTORS[$check] === $this->checkDigit
)
|| ($this->checkDigit == $check);
}

/**
* Get country code from the VIN number
*
* @return string
*/
private function getCountryCode() : string
{
Expand Down
9 changes: 3 additions & 6 deletions tests/VINDecoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,13 @@
class VINDecoderTest extends TestCase
{
/**
* Test the instance creation
*
* @return bool
* @throws Exception
*/
public function testInitInstance()
{
// VIN example
$vin = "JTEHT05J542053195";

$vin = "JTEHT05J542053195"; // VIN example
$vinDecoder = new VINDecoder($vin);

$this->assertInstanceOf(VINDecoder::class, $vinDecoder);
}
}