PHP DateTime: 'previous week' or 'last week'? - php

I want to get the date of the first day of the last week:
$date = new DateTime(NULL, new DateTimeZone('Pacific/Wake'));
$date = $date->modify('previous week');
$date = $date->format('Y-m-d');
and
$date = new DateTime(NULL, new DateTimeZone('Pacific/Wake'));
$date = $date->modify('last week');
$date = $date->format('Y-m-d');
Both work.
But is there any difference?

They are the same, you can confirm this for yourself if you want, the PHP source code is available on github.
https://github.com/php/php-src/blob/master/ext/date/php_date.c#L1443
PHP_FUNCTION(strtotime)
{
...
t = timelib_strtotime(initial_ts, strlen(initial_ts), NULL, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); /* we ignore the error here, as this should never fail */
So it calls timelib_strtotime, where can this be found? Well luckily that's online too
https://github.com/php/php-src/blob/master/ext/date/lib/parse_date.c#L24743
timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
{
...
do {
t = scan(&in, tz_get_wrapper);
#ifdef DEBUG_PARSER
printf("%d\n", t);
#endif
} while(t != EOI);
...
Which relies on scan:
https://github.com/php/php-src/blob/master/ext/date/lib/parse_date.c#L835
static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
{
...
while(*ptr) {
i = timelib_get_relative_text((char **) &ptr, &behavior);
Which makes a call to timelib_get_relative_text
https://github.com/php/php-src/blob/master/ext/date/lib/parse_date.c#L561
static timelib_sll timelib_get_relative_text(char **ptr, int *behavior)
{
while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') {
++*ptr;
}
return timelib_lookup_relative_text(ptr, behavior);
}
Which makes a call to timelib_lookup_relative_text:
https://github.com/php/php-src/blob/master/ext/date/lib/parse_date.c#L536
static timelib_sll timelib_lookup_relative_text(char **ptr, int *behavior)
{
...
for (tp = timelib_reltext_lookup; tp->name; tp++) {
if (strcasecmp(word, tp->name) == 0) {
value = tp->value;
*behavior = tp->type;
}
}
...
}
Which in turn uses a structure set called timelib_reltext_lookup defined at the top of the file:
https://github.com/php/php-src/blob/master/ext/date/lib/parse_date.c#L248
static timelib_lookup_table const timelib_reltext_lookup[] = {
{ "first", 0, 1 },
{ "next", 0, 1 },
{ "second", 0, 2 },
{ "third", 0, 3 },
{ "fourth", 0, 4 },
{ "fifth", 0, 5 },
{ "sixth", 0, 6 },
{ "seventh", 0, 7 },
{ "eight", 0, 8 },
{ "eighth", 0, 8 },
{ "ninth", 0, 9 },
{ "tenth", 0, 10 },
{ "eleventh", 0, 11 },
{ "twelfth", 0, 12 },
{ "last", 0, -1 },
{ "previous", 0, -1 },
{ "this", 1, 0 },
{ NULL, 1, 0 }
};
Hopefully this is sufficient proof that they are, in every way, the same.

The word "previous" is supported since PHP5, "last" since PHP4. The result of both is the same and both are still valid.

Related

DateTime Failed to Parse Time String

I am passing 2 date and time strings per item in the array which is brought over from JSON.
These dates are successfully stored in the array but the DateTime function doesn't like them for some reason.
I have tried using different formats, just the date, just the time but nothing worked.
I have provided the JSON file and my PHP Tests file I am using.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$revokes = jsonDecode(file_get_contents("../revokes.json"), true);
$certificates = $revokes['certificates'];
// Prints the revokes array
// print_r($revokes);
$dates = array();
foreach ($certificates as $certificate_key => $certificate) {
$signed = $certificate['signed'];
$revoked = $certificate['revoked'];
$dates[] = array(
"signed" => $signed,
"revoked" => $revoked
);
}
// Prints the dates
// print_r($dates);
$intervals = array();
foreach ($dates as $key) {
$newTimeAdd = new DateTime($key["signed"]);
$newTimeRead = new DateTime($key["revoked"]);
$interval = $newTimeAdd->diff($newTimeRead);
// returns 0 on all elements of the interval array.
// var_dump($interval);
$intervals[] = $interval->days;//get days
}
if(!empty($intervals)) {
$average = average($intervals);
}
// Prints nothing
// print_r($intervals);
function average($arr) {
return array_sum($arr)/count($arr);
}
function jsonDecode($json, $assoc = false)
{
$ret = json_decode($json, $assoc);
if ($error = json_last_error())
{
$errorReference = [
JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded.',
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON.',
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded.',
JSON_ERROR_SYNTAX => 'Syntax error.',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded.',
JSON_ERROR_RECURSION => 'One or more recursive references in the value to be encoded.',
JSON_ERROR_INF_OR_NAN => 'One or more NAN or INF values in the value to be encoded.',
JSON_ERROR_UNSUPPORTED_TYPE => 'A value of a type that cannot be encoded was given.',
];
$errStr = isset($errorReference[$error]) ? $errorReference[$error] : "Unknown error ($error)";
throw new \Exception("JSON decode error ($error): $errStr");
}
return $ret;
}
?>
{
"lifeExp": "2 Days",
"certificates": [
{
"name": "CCS Group Pte Ltd",
"signed": "22/05/2020 10:31:00",
"revoked": "23/05/2020 5:40:00",
"files": {
"p12": "certificates/:id/certificate.p12",
"pem": "certificates/:id/certificate.pem",
"key": "certificates/:id/certificate.key",
"password": "certificates/:id/certificate.password"
}
},
{
"name": "Hoola Inc",
"signed": "16/05/2020 12:40:00",
"revoked": "19/05/2020 04:00:00",
"files": {
"p12": "certificates/:id/certificate.p12",
"pem": "certificates/:id/certificate.pem",
"key": "certificates/:id/certificate.key",
"password": "certificates/:id/certificate.password"
}
}
]
}
Your date formats are in European format (DD/MM/YYYY) which means you'll need to use DateTime::createFromFormat() to specify the correct format to have DateTime handle it correctly. This is due to PHP assuming US date format when it sees the NN/NN/NNNN date format.
<?php
$json = json_decode('{
"lifeExp": "2 Days",
"certificates": [
{
"name": "CCS Group Pte Ltd",
"signed": "22/05/2020 10:31:00",
"revoked": "23/05/2020 5:40:00",
"files": {
"p12": "certificates/:id/certificate.p12",
"pem": "certificates/:id/certificate.pem",
"key": "certificates/:id/certificate.key",
"password": "certificates/:id/certificate.password"
}
},
{
"name": "Hoola Inc",
"signed": "16/05/2020 12:40:00",
"revoked": "19/05/2020 04:00:00",
"files": {
"p12": "certificates/:id/certificate.p12",
"pem": "certificates/:id/certificate.pem",
"key": "certificates/:id/certificate.key",
"password": "certificates/:id/certificate.password"
}
}
]
}', true);
$signed = $json['certificates'][1]['signed'];
$revoked = $json['certificates'][1]['revoked'];
$newTimeAdd = DateTime::createFromFormat('d/m/Y H:i:s', $signed);
$newTimeRead = DateTime::createFromFormat('d/m/Y H:i:s', $revoked);
$interval = $newTimeAdd->diff($newTimeRead);
echo $interval->days;
Output
2
Demo

Adding 2 hexadecimals as strings

Description
I'm trying to get the data for a decall image for a Team Fortress 2 item from steam
http://forums.steampowered.com/forums/showthread.php?t=2695676
Data used
steam inventory item attributes
"attributes": [
{
"defindex": 152,
"value": 160020469,
"float_value": 3.3154542658454145e-033
},
{
"defindex": 227,
"value": 142668139,
"float_value": 7.7604768692394667e-034
},
{
"defindex": 746,
"value": 1065353216,
"float_value": 1
},
{
"defindex": 292,
"value": 1115684864,
"float_value": 64
},
{
"defindex": 388,
"value": 1115684864,
"float_value": 64
}
]
My script
`
function Function_BigDecimal_to_Hexadecimal_Converter($dec) {
$hex = '';
do {
$last = bcmod($dec, 16);
$hex = dechex($last).$hex;
$dec = bcdiv(bcsub($dec, $last), 16);
} while($dec>0);
return $hex;
}
function Function_BigHexadecimal_to_Decimal_Converter($hex) {
$dec = 0; $len = strlen($hex);
for ($i = 1; $i <= $len; $i++) { $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); }
return $dec;
}
$hex1 = Function_BigDecimal_to_Hexadecimal_Converter('142668139' ); // data from defindex 227
$hex2 = Function_BigDecimal_to_Hexadecimal_Converter('160020469'); // data from defindex 152
echo 'hex1 ',$hex1,'<br>'; // = 880f16b
echo 'hex2 ',$hex2,'<br>'; // = 989b7f5
$hex_append = $hex1.$hex2;
echo 'hex_append ',$hex_append,'<br>'; // = 880f16b989b7f5
$ugcid = Function_BigHexadecimal_to_Decimal_Converter($hex_append);
echo 'ugcid ',$ugcid,'<br>'; // = 38297187109156853
`
My problem
expected ucid value shoud be 612754991346202613 and I got 38297187109156853
Here i'm reversing this code to get hex_append from the expected answer
echo 'Reversed hex_append using expected answer '.Function_BigDecimal_to_Hexadecimal_Converter('612754991346202613');// = 880f16b0989b7f5
Conclusion
The difference in hex_append calculated 880f16b989b7f5 and the reversed answer 880f16b0989b7f5 is a 0
How can I fix this ?
I guess the a 0 needs to be added sometimes when adding 2 hexes as strings.
This problem was detected only for this item.
All this php code con be copyed in a php file and executed
Answer: The string must have 18 characters

How to Sort array string by different point in the string

I have this as result in and HTML page created from script.
thumbnail[++nr] = new makeIt(nr, "slides/IMG_3924.html", "thumbs/IMG_3924.jpg", 150, 100, "IMG_3924.jpg", "slides/IMG_3924.jpg", 150, 100, "", "sorting01", 0)
thumbnail[++nr] = new makeIt(nr, "slides/IMG_3909.html", "thumbs/IMG_3909.jpg", 100, 150, "IMG_3909.jpg", "slides/IMG_3909.jpg", 100, 150, "", "sorting02", 0)
thumbnail[++nr] = new makeIt(nr, "slides/IMG_3914.html", "thumbs/IMG_3914.jpg", 150, 100, "IMG_3914.jpg", "slides/IMG_3914.jpg", 150, 100, "", "sorting02", 0)
thumbnail[++nr] = new makeIt(nr, "slides/IMG_3904.html", "thumbs/IMG_3904.jpg", 100, 150, "IMG_3904.jpg", "slides/IMG_3904.jpg", 100, 150, "", "sorting01", 0)
And here's the code that produced it
if ($file2 != "." && $file2 != ".." && strpos($file2,'.')!==0 ) {
list($widthT2, $heightT2, $type2, $attr2) = getimagesize($pathToThumbs."/$name2.jpg");
$thumbW2 = $widthT2;
$thumbH2 = $heightT2;
$out[]="thumbnail[++nr] = new makeIt(nr, \"slides/$name2.html\", \"thumbs/$name2.jpg\", $thumbW2, $thumbH2, \"$name2.jpg\", \"slides/$name2.jpg\", $widthT2, $heightT2, \"\", \"$commentaire\", 0)\n";
}
}
//usort($out, function ($a, $b){
// return substr($b, -8) - substr($a, -8);
//});
sort($out);
foreach($out as $key => $value){
print $value;
}
How can I sort the results so they are sorted by sorting01, and then by sorting02 text at the end of each line? I want to sort the array from what the last 2 fields of each line contains.
Searching last "some text in quotas" in strings and compare:
function compare($a,$b)
{
// searching commentaire (searching last " and second from end ")
$end_commentaire = strrpos($a, '"');
$substr_a = substr($a, 0, $end_commentaire);
$begin_commentaire = strrpos($substr_a, '"');
$substr_a = substr($substr_a, $begin_commentaire+1);
$end_commentaire = strrpos($b, '"');
$substr_b = substr($b, 0, $end_commentaire);
$begin_commentaire = strrpos($substr_b, '"');
$substr_b = substr($substr_b, $begin_commentaire+1);
//echo "|$substr_a|$substr_b|<br>";
// compare
$result = strcmp($substr_a, $substr_b);
// $substr_a and $substr_b are different,
// don't compare rest of string
if( $result != 0 )
return $result;
// $substr_a and $substr_b are the same,
// compare rest of string
return strcmp($a, $b);
}
usort($out, 'compare');
Put this in place of sort($out)
Edit:
Using RegEx:
function compare($a,$b)
{
// searching commentaire (searching last " and second from end ")
preg_match('/"([^"]*)"[^"]*$/', $a, $result_a);
preg_match('/"([^"]*)"[^"]*$/', $b, $result_b);
//echo "|$result_a[1]|$result_b[1]|$result_a[0]|$result_b[0]|<br>";
// compare
$result = strcmp($result_a[1], $result_b[1]);
// $result_a[1] and $result_b[1] are different,
// don't compare rest of string
if( $result != 0 )
return $result;
// $result_a[1] and $result_b[1] are the same,
// compare rest of string
return strcmp($a, $b);
}
usort($out, 'compare');

How can I convert frm php to c# or json? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am using the browser-layout example for my application
I'm trying to add a two trees to it.
I'm trying to define the twotrees in a separate file, my main file is the layout-browser.js and i need to add this (and others) in the tabs I have in it.
The problem is that I'm using .net and the example is using php
How can I make this work in .net?
Here is my code:
Ext.require(['*']);
var store = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
url: 'get-nodes.php'
},
root: {
text: 'Ext JS',
id: 'src',
expanded: true
},
folderSort: true,
sorters: [{
property: 'text',
direction: 'ASC'
}]
});
var tree = Ext.create('Ext.tree.Panel', {
id: 'tree',
store: store,
width: 250,
height: 300,
columnWidth: 0.5,
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
appendOnly: true
}
}
// ,renderTo: document.body
});
var store2 = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
url: 'get-nodes.php'
},
root: {
text: 'Custom Ext JS',
id: 'src',
expanded: true,
children: []
},
folderSort: true,
sorters: [{
property: 'text',
direction: 'ASC'
}]
});
var tree2 = Ext.create('Ext.tree.Panel', {
id: 'tree2',
width: 250,
height: 300,
columnWidth: 0.5,
store: store2,
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
appendOnly: true
}
}
// ,renderTo: document.body
});
Ext.define("Ext.app.myTwoTrees",{
extend:"Ext.panel.Panel",
width : 600,
height: 350,
layout: 'column',
items: [
tree , tree2
]
});
I call it in my tab like this:
Ext.create('Ext.app.myTwotrees')
here is the get-nodes.php
<?php
// from php manual page
function formatBytes($val, $digits = 3, $mode = 'SI', $bB = 'B'){ //$mode == 'SI'|'IEC', $bB == 'b'|'B'
$si = array('', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y');
$iec = array('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi');
switch(strtoupper($mode)) {
case 'SI' : $factor = 1000; $symbols = $si; break;
case 'IEC' : $factor = 1024; $symbols = $iec; break;
default : $factor = 1000; $symbols = $si; break;
}
switch($bB) {
case 'b' : $val *= 8; break;
default : $bB = 'B'; break;
}
for($i=0;$i<count($symbols)-1 && $val>=$factor;$i++)
$val /= $factor;
$p = strpos($val, '.');
if($p !== false && $p > $digits) $val = round($val);
elseif($p !== false) $val = round($val, $digits-$p);
return round($val, $digits) . ' ' . $symbols[$i] . $bB;
}
// grab the custom params
$path = isset($_REQUEST['path'])&&$_REQUEST['path'] == 'extjs' ? '../../../' : '../../';
$node = isset($_REQUEST['node']) ? $_REQUEST['node'] : '';
$isXml = isset($_REQUEST['isXml']);
if(strpos($node, '..') !== false){
die('Nice try buddy.');
}
$nodes = array();
$directory = $path.$node;
if (is_dir($directory)){
$d = dir($directory);
while($f = $d->read()){
if($f == '.' || $f == '..' || substr($f, 0, 1) == '.') continue;
$filename = $directory . '/' . $f;
date_default_timezone_set('America/Los_Angeles');
$lastmod = date('M j, Y, g:i a', filemtime($filename));
if(is_dir($directory.'/'.$f)){
$qtip = 'Type: Folder<br />Last Modified: '.$lastmod;
$nodes[] = array(
'text' => $f,
'id' => $node.'/'.$f,
'cls' => 'folder'
);
} else {
$size = formatBytes(filesize($filename), 2);
$qtip = 'Type: JavaScript File<br />Last Modified: '.$lastmod.'<br />Size: '.$size;
$nodes[] = array(
'text' => $f,
'id' => $node.'/'.$f,
'leaf' => true,
'cls' => 'file'
);
}
}
$d->close();
}
if ($isXml) {
$xmlDoc = new DOMDocument();
$root = $xmlDoc->appendChild($xmlDoc->createElement("nodes"));
foreach ($nodes as $node) {
$xmlNode = $root->appendChild($xmlDoc->createElement("node"));
$xmlNode->appendChild($xmlDoc->createElement("text", $node['text']));
$xmlNode->appendChild($xmlDoc->createElement("id", $node['id']));
$xmlNode->appendChild($xmlDoc->createElement("cls", $node['cls']));
$xmlNode->appendChild($xmlDoc->createElement("leaf", isset($node['leaf'])));
}
header("Content-Type: text/xml");
$xmlDoc->formatOutput = true;
echo $xmlDoc->saveXml();
} else {
echo json_encode($nodes);
}
I would recommend you break the problem into two pieces:
1) change url: 'get-nodes.php' to url: 'my-dotnet-url and make the .NET page return static JSON or XML (hardcode the values to the tree)
That will confirm all your javascript, resources, etc. is working properly and that you are only asking a .NET question about how to output certain data.
2) Then find a .NET example that will let you create JSON or XML from wherever you are getting data (I'm guessing probably a database). You just need the output to look like your static data that worked correctly. If you don't know much PHP or .NET learning .NET to get your output correct would be easier than trying to port over that example. If you get stuck, I'd repost a different question and ask how to output the data that results from that static file dynamically and don't have the extjs complication involved!
Hope this helps.
Try Dextop. It's an application framework for connection between ASP.NET and Ext JS.

How to validate a VIN number?

How can I validate a Vehicle Identification Number with PHP? I just need to check if the entered VIN number is correct or not.
Here's something I wrote up real quick using the example in the wikipedia article.
Not guaranteed perfect or bug free or super efficient, but should provide you with a solid starting point:
Note: I included the edits provided by Confluence below, making the procedure slightly more succinct.
function validate_vin($vin) {
$vin = strtolower($vin);
if (!preg_match('/^[^\Wioq]{17}$/', $vin)) {
return false;
}
$weights = array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);
$transliterations = array(
"a" => 1, "b" => 2, "c" => 3, "d" => 4,
"e" => 5, "f" => 6, "g" => 7, "h" => 8,
"j" => 1, "k" => 2, "l" => 3, "m" => 4,
"n" => 5, "p" => 7, "r" => 9, "s" => 2,
"t" => 3, "u" => 4, "v" => 5, "w" => 6,
"x" => 7, "y" => 8, "z" => 9
);
$sum = 0;
for($i = 0 ; $i < strlen($vin) ; $i++ ) { // loop through characters of VIN
// add transliterations * weight of their positions to get the sum
if(!is_numeric($vin{$i})) {
$sum += $transliterations[$vin{$i}] * $weights[$i];
} else {
$sum += $vin{$i} * $weights[$i];
}
}
// find checkdigit by taking the mod of the sum
$checkdigit = $sum % 11;
if($checkdigit == 10) { // checkdigit of 10 is represented by "X"
$checkdigit = "x";
}
return ($checkdigit == $vin{8});
}
Note: there is a small percent error with verifying VINs because of the nature of the checksum:
...a match does not prove the VIN is correct, because there is still a 1 in 11 chance of any two distinct VINs having a matching check digit.
Also note: 11111111111111111 will validate against the procedure above. Whether or not you want to check for that is up to you:
Straight-ones (seventeen consecutive '1's) will suffice the check-digit. This is because a value of one, multiplied against 89 (sum of weights), is still 89. And 89 % 11 is 1, the check digit. This is an easy way to test a VIN-check algorithm.
reference: http://en.wikipedia.org/wiki/Vehicle_identification_number#Check_digit_calculation
Here's a version of the code by jordan ported to Javascript, hope it's helpful to someone...
function validate_vin(vin)
{
function isnumeric(mixed_var) {
return (typeof(mixed_var) === 'number' || typeof(mixed_var) === 'string') && mixed_var !== '' && !isNaN(mixed_var);
}
var pattern = /^[^\Wioq]{17}$/;
var weights = Array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);
var transliterations = {
"a" : 1, "b" : 2, "c" : 3, "d" : 4,
"e" : 5, "f" : 6, "g" : 7, "h" : 8,
"j" : 1, "k" : 2, "l" : 3, "m" : 4,
"n" : 5, "p" : 7, "r" : 9, "s" : 2,
"t" : 3, "u" : 4, "v" : 5, "w" : 6,
"x" : 7, "y" : 8, "z" : 9
};
vin = vin.toLowerCase();
if(!vin.match(pattern)) { return false; }
var sum = 0;
for(var i=0; i<vin.length; i++) {
if(!isnumeric(vin.charAt(i))) {
sum += transliterations[vin.charAt(i)] * weights[i];
} else {
sum += parseInt(vin.charAt(i)) * weights[i];
}
}
var checkdigit = sum % 11;
if(checkdigit == 10) { // check digit of 10 represented by X
checkdigit = 'x';
}
return (checkdigit == vin.charAt(8));
}
It's "VIN." "VIN Number" = "Vehicle Identification Number Number," which doesn't make sense.
You can see a definition of the structure of VINs here:
http://en.wikipedia.org/wiki/Vehicle_identification_number
And you can work from there, or you can grab this script here:
http://www.geekpedia.com/code29_Check-if-VIN-number-is-valid.html
Here is an improved version of the function posted by jordan:
$vin = "1M8GDM9AXKP042788";
function validate_vin($vin) {
$vin = strtolower($vin);
if (!preg_match('/^[^\Wioq]{17}$/', $vin)) {
return false;
}
$weights = array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);
$transliterations = array(
"a" => 1, "b" => 2, "c" => 3, "d" => 4,
"e" => 5, "f" => 6, "g" => 7, "h" => 8,
"j" => 1, "k" => 2, "l" => 3, "m" => 4,
"n" => 5, "p" => 7, "r" => 9, "s" => 2,
"t" => 3, "u" => 4, "v" => 5, "w" => 6,
"x" => 7, "y" => 8, "z" => 9
);
$sum = 0;
for($i = 0 ; $i < strlen($vin) ; $i++ ) { // loop through characters of VIN
// add transliterations * weight of their positions to get the sum
if(!is_numeric($vin{$i})) {
$sum += $transliterations[$vin{$i}] * $weights[$i];
} else {
$sum += $vin{$i} * $weights[$i];
}
}
// find checkdigit by taking the mod of the sum
$checkdigit = $sum % 11;
if($checkdigit == 10) { // checkdigit of 10 is represented by "X"
$checkdigit = "x";
}
return ($checkdigit == $vin{8});
}
I recently had to write a VIN validation class with PHP. I posted my class for everyone to use at:
http://dev.strategystar.net/2012/05/validate-vin-checksum-with-php/
class VIN
{
public static $transliteration = array(
'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8,
'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'P'=>7, 'R'=>9,
'S'=>2, 'T'=>3, 'U'=>4, 'V'=>5, 'W'=>6, 'X'=>7, 'Y'=>8, 'Z'=>9,
);
public static $weights = array(8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2);
/***
* The checksum method is used to validate whether or not a VIN is valid
* It will return an array with two keys: status and message
* The "status" will either be boolean TRUE or FALSE
* The "message" will be a string describing the status
*/
public static function checksum($vin)
{
$vin = strtoupper($vin);
$length = strlen($vin);
$sum = 0;
if($length != 17)
{
return array('status'=>false, 'message'=>'VIN is not the right length');
}
for($x=0; $x<$length; $x++)
{
$char = substr($vin, $x, 1);
if(is_numeric($char))
{
$sum += $char * self::$weights[$x];
}
else
{
if(!isset(self::$transliteration[$char]))
{
return array('status'=>false, 'message'=>'VIN contains an invalid character.');
}
$sum += self::$transliteration[$char] * self::$weights[$x];
}
}
$remainder = $sum % 11;
$checkdigit = $remainder == 10 ? 'X' : $remainder;
if(substr($vin, 8, 1) != $checkdigit)
{
return array('status'=>false, 'message'=>'The VIN is not valid.');
}
return array('status'=>true, 'message'=>'The VIN is valid.');
}
}
Note that the wiki says:
"A check-digit validation is used for all road vehicles sold in the United States and Canada."
So if you're working with other countries you might want to loosen the check-digit validation
https://www.olschimke.eu/2012/08/02/dealing-with-vehicle-identification-numbers-vin-data-quality/
had some good tips.
Thanks to all for the algorithm etc. which I see is on Wikipedia. This is the version I put together based on the comments above. Note, there are problems with the versions above, for ex this 00000000000354888 returns OK for a vin. I took what was above and added an option to check year first if <1980, assume it isn't a 17digit real vin (have to), and also if there are sequences of a single character, assume invalid. This is good enough for my needs as I am comparing against values that are filled with 0s if not 17 in length. Also I know the year value so if I check that I can speed up the code by skipping the vin check (yes I could have put that in before the function) lol bye!.
<?php
/*
=======================================================================================
PURPOSE: VIN Validation (Check-digit validation is compulsory for all road vehicles sold in North America.)
DETAILS: Validates 17 digit VINs by checking their formatting
USAGE: returns boolean or returns an array with a detailed message
COMMENTS: This could be made more robust by checking the country codes etc..
MORE INFO: https://en.wikipedia.org/wiki/Vehicle_identification_number
=======================================================================================
*/
class vinValidation {
public static $transliteration = array(
'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8,
'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'P'=>7, 'R'=>9,
'S'=>2, 'T'=>3, 'U'=>4, 'V'=>5, 'W'=>6, 'X'=>7, 'Y'=>8, 'Z'=>9,
);
public static $weights = array(8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2);
public function validateVIN($vin, $ret_array_status = false, $year = null) {
//validates US/NA 1980>= VINs, if before 1980, no vin standards, this returns false
if (!empty($year) && preg_match("/^[0-9]{4}/", $year)) {
if ($year < 1980) return ($ret_array_status ? array('status' => false, 'message' => 'Unable to check VIN, pre-dates 1980.') : false);
}
$vin_length = 17; // US vin requirements >= 1980
$vin = strtoupper(trim($vin));
$sum = 0;
//if (!preg_match('/^[^\Wioq]{17}$/', $vin))
//return ($ret_array_status ? array('status'=>false, 'message'=>'VIN is not valid, not the right length.') : false);
if (!preg_match('/^[A-HJ-NPR-Z0-9]{17}$/', $vin))
return ($ret_array_status ? array('status'=>false, 'message'=>'VIN is not valid, VIN formatting is incorrect.') : false);
if (preg_match('/(\w)\1{5,}/', $vin))
return ($ret_array_status ? array('status'=>false, 'message'=>'VIN contains invalid repeating character sequence.') : false);
for($x=0; $x < $vin_length; $x++) {
$char = substr($vin, $x, 1);
if(is_numeric($char)) {
$sum += $char * self::$weights[$x];
} else {
if(!isset(self::$transliteration[$char]))
return ($ret_array_status ? array('status'=>false, 'message'=>'VIN contains an invalid character.') : false);
$sum += self::$transliteration[$char] * self::$weights[$x];
}
}
$remainder = $sum % 11;
$checkdigit = $remainder == 10 ? 'X' : $remainder;
//echo " sum:".$sum." remain:".$remainder." check dig:".$checkdigit."\n";
if(substr($vin, 8, 1) != $checkdigit)
return ($ret_array_status ? array('status'=>false, 'message'=>'The VIN is not valid, failed checksum.') : false);
// all is good return true or a value and status.
return ($ret_array_status ? array('status'=>true, 'message'=>'The VIN is valid, passed checksum.') : true);
}
}
TESTING :
$vinClass = new vinValidation();
// not long enough not val
var_dump($vinClass->validateVIN('1I345678123456789', false, 2000));
var_dump($vinClass->validateVIN('1I345678123456789'));
echo "-----------------------------------------------------------\n";
// not valid
var_dump($vinClass->validateVIN('00000000012870842', true));
var_dump($vinClass->validateVIN('00000000012870842', 1968)); //assumes faulty by year
var_dump($vinClass->validateVIN('00000000012870842'));
echo "-----------------------------------------------------------\n";
// not valid
var_dump($vinClass->validateVIN('00000000000354888', true));
var_dump($vinClass->validateVIN('00000000000354888'));
echo "-----------------------------------------------------------\n";
// Fails Checksum test
var_dump($vinClass->validateVIN('368TU79MXH4763452',false,2000));
var_dump($vinClass->validateVIN('368TU79MXH4763452'));
echo "-----------------------------------------------------------\n";
// yachtzee, (returns true or array) !
var_dump($vinClass->validateVIN('WP1AF2A56GLB91679',true));
var_dump($vinClass->validateVIN('WP1AF2A56GLB91679'));
Here is the JavaScript class version as posted by Mike Q:
class VIN {
static transliteration = {
'A':1, 'B':2, 'C':3, 'D':4, 'E':5, 'F':6, 'G':7, 'H':8, 'J':1, 'K':2, 'L':3, 'M':4, 'N':5, 'P':7, 'R':9, 'S':2, 'T':3, 'U':4, 'V':5, 'W':6, 'X':7, 'Y':8, 'Z':9
}
static weights = [8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2];
validateVIN(vin, retArrayStatus = false, year = null) {
if (year != null && year.match(/^[0-9]{4}/)) {
if (year < 1980)
return retArrayStatus ? {'status': false, 'message': 'Unable to check VIN, pre-dates 1980.'} : false;
}
let vinLength = 17;
vin = vin.trim();
let sum = 0;
if (!vin.match(/^[A-HJ-NPR-Z0-9]{17}$/))
return retArrayStatus ? {'status': false, 'message': 'VIN is not valid, VIN formatting is incorrect [i, o, q].'} : false;
//if (!vin.match(/(\w)\1{5,}/))
// return retArrayStatus ? {'status': false, 'message': 'VIN contains invalid repeating character sequence.'} : false;
for (let x = 0; x < vinLength; x++) {
let char = vin.substr(x, 1);
if (!isNaN(char)) {
sum += char * VIN.weights[x];
}
else {
if (VIN.transliteration[char] == '')
return retArrayStatus ? {'status': false, 'message': 'VIN contains an invalid character.'} : false;
sum += VIN.transliteration[char] * VIN.weights[x];
}
}
let reminder = sum % 11;
let checkdigit = reminder == 10 ? 'X' : reminder;
if (vin.substr(8, 1) != checkdigit)
return retArrayStatus ? {'status': false, 'message': 'The VIN is not valid, failed checksum.'} : false;
return retArrayStatus ? {'status': true, 'message': 'The VIN is valid, passed checksum.'} : true;
}
}

Categories