Vigenere in PHP - php
could anyone help me fix this Vigenere cypher in PHP?
Sorry for the ripped up code, that's from where I have been dissecting it for hours - trying to fix!
Anyhow, the code outputs 'Ace' when it should output 'Abc'.
There is some weird double offset which I don't have the maths brain to fix! Thanks for reading.
The code originates from here in AutoHotkey script - I have attempted to transcribe it. There are PHP Vigenere examples on the web (although not on Rosetta Code, weirdly!).. but anyhow, this one is modified to accept lower case as well as the standard capitals. Thanks.
$key = "AAA";
$keyLength = 3;
$keyIndex = 0;
$messageAsArray[0] = "A";
$messageAsArray[1] = "b";
$messageAsArray[2] = "c";
foreach ($messageAsArray as $value) //Loop through input string array
{
$thisValueASCII = ord($value);
if ($thisValueASCII >= 65 && $thisValueASCII <= 90) //if is uppercase
{
$thisValueASCIIOffset = 65;
}
else //if is lowercase
{
$thisValueASCIIOffset = 97;
}
$thisA = $thisValueASCII - $thisValueASCIIOffset;
$thisB = fmod($keyIndex,$keyLength);
$thisC = substr($key, $thisB, 1);
$thisD = ord($thisC) - 65;
$thisE = $thisA + $thisD;
$thisF = fmod($thisE,26);
$thisG = $thisF + $thisValueASCII ;
$thisOutput = chr($thisG);
$output = $output . $thisOutput ;
$keyIndex++;
}
echo $output
Ok, I read your code.
You're encoding, and your error is quite simple :
$thisG = $thisF + $thisValueASCII ;
In this step, $thisF is your encrypted letter, which value is between 0 and 25. You want to print it as an ascii char and, instead of adding the offset, you're adding the uncrypted ascii value, which makes no sense.
You should have :
$thisG = $thisF + $thisValueASCIIOffset;
A few tips.
You don't need to have your text or key as an array, you can use it as if it was one.
You can use the % operator instead of fmod. Makes the code easier to read, but it is just a personnal preference.
For instance :
$key = "AAA";
$keyLength = strlen($key);
$keyIndex = 0;
$message = str_split("Abc");
$output = '';
foreach($message as $value) // Loop through input string array
{
$thisValueASCII = ord($value);
if($thisValueASCII >= 65 && $thisValueASCII <= 90) // if is uppercase
{
$thisValueASCIIOffset = 65;
} else // if is lowercase
{
$thisValueASCIIOffset = 97;
}
$letter_value_corrected = $thisValueASCII - $thisValueASCIIOffset;
$key_index_corrected = $keyIndex % $keyLength; // This is the same as fmod but I prefer this notation.
$key_ascii_value = ord($key[$key_index_corrected]);
if($key_ascii_value >= 65 && $key_ascii_value <= 90) // if is uppercase
{
$key_offset = 65;
} else // if is lowercase
{
$key_offset = 97;
}
$final_key = $key_ascii_value - $key_offset;
$letter_value_encrypted = ($letter_value_corrected + $final_key)%26;
$output = $output . chr($letter_value_encrypted + $thisValueASCIIOffset);
$keyIndex++;
}
echo $output;
Have fun and good luck for your implementation !
Related
Convert VB6 code to PHP
I want to convert Visual Basic 6 Code to PHP Code. I am new to PHP please help me to convert my VB6 Code to PHP. So far I tried to convert this into php code when I tried the code there is an error in the " CryptRC4 = CryptRC4 & Chr$((pvCryptXor(baS((CLng(baS(li)) + baS(lJ)) Mod 256), Asc(Mid$(sText, lIdx, 1))))); part and also I don't know how to proceed to the sub functions. Please see the codes below. The vb code is used to encrypt strings. I want to convert it to php format. PHP Code <?php function CryptRC4($sText,$sKey){ $baS = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255); $baK = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255); $bytSwap = 0; $li = 0; $lJ = 0; $lIdx = 0; for( $lIdx = 0; $lIdx < 256; $lIdx++){ $baS[$lIdx] = $lIdx; $baK[$lIdx] = ord(substr($sKey, 1 + ($lIdx % strlen($sKey)), 1)); } for($li = 0; $li < 256; $li++){ $lJ = ($baS[$li] + $baK[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; } $li = 0; $lJ = 0; $data_str = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $li = ($li + 1) % 256; $lJ = ($lJ + $baS[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; #echo chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); $data_str .= chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); } echo $data_str; } function pvCryptXor($li, $lJ){ if($li = $lJ){ $pcx = $lJ; } else { $pcx = $li Xor $lJ; } return $pcx; } unction ToHexDump($sText) { $lIdx; for($lIdx = 1; $lIdx < strlen($sText); $lIdx++){ $thd .= Right$("0" & Hex(Asc(Mid(sText, lIdx, 1))), 2) echo $thd; } return $thd; } FromHexDump("events"); function FromHexDump($sText) { $fhd = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $fhd .= chr(CLng("&H" & Mid(sText, lIdx, 2))); } return $fhd; } ?> VB Code: Public Function CryptRC4(sText As String, sKey As String) As String On Error Resume Next Dim baS(0 To 255) As Byte Dim baK(0 To 255) As Byte Dim bytSwap As Byte Dim li As Long Dim lJ As Long Dim lIdx As Long For lIdx = 0 To 255 baS(lIdx) = lIdx baK(lIdx) = Asc(Mid$(sKey, 1 + (lIdx Mod Len(sKey)), 1)) Next For li = 0 To 255 lJ = (lJ + baS(li) + baK(li)) Mod 256 bytSwap = baS(li) baS(li) = baS(lJ) baS(lJ) = bytSwap Next li = 0 lJ = 0 For lIdx = 1 To Len(sText) li = (li + 1) Mod 256 lJ = (lJ + baS(li)) Mod 256 bytSwap = baS(li) baS(li) = baS(lJ) baS(lJ) = bytSwap CryptRC4 = CryptRC4 & Chr$((pvCryptXor(baS((CLng(baS(li)) + baS(lJ)) Mod 256), Asc(Mid$(sText, lIdx, 1))))) Next End Function Private Function pvCryptXor(ByVal li As Long, ByVal lJ As Long) As Long On Error Resume Next If li = lJ Then pvCryptXor = lJ Else pvCryptXor = li Xor lJ End If End Function Public Function ToHexDump(sText As String) As String On Error Resume Next Dim lIdx As Long For lIdx = 1 To Len(sText) ToHexDump = ToHexDump & Right$("0" & Hex(Asc(Mid(sText, lIdx, 1))), 2) Next End Function Public Function FromHexDump(sText As String) As String On Error Resume Next Dim lIdx As Long For lIdx = 1 To Len(sText) Step 2 FromHexDump = FromHexDump & Chr$(CLng("&H" & Mid(sText, lIdx, 2))) Next End Function
I revised your updated code and it seems you only had a few minor errors in it, look and my changes: I guess you can use the build in PHP function hex2bin and bin2hex instead fo you own hex conversion. function CryptRC4($sText,$sKey){ $baS = range(0, 255); // you can use range instead of your manual arrays $baK = range(0, 255); $bytSwap = 0; $li = 0; $lJ = 0; $lIdx = 0; for( $lIdx = 0; $lIdx < 256; $lIdx++){ $baS[$lIdx] = $lIdx; $baK[$lIdx] = ord(substr($sKey, 1 + ($lIdx % strlen($sKey)), 1)); } for($li = 0; $li < 256; $li++){ $lJ = ($baS[$li] + $baK[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; } $li = 0; $lJ = 0; $data_str = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $li = ($li + 1) % 256; $lJ = ($lJ + $baS[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; #echo chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); $data_str .= chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); } return $data_str; // changed from echo to return } function pvCryptXor($li, $lJ){ if($li == $lJ){ // you had an error here, use == to compare instead of a single = $pcx = $lJ; } else { $pcx = $li ^ $lJ; // XOR function in PHP is the ^ operator } return $pcx; } $str_hex = bin2hex("events"); $str_enc = CryptRC4($str_hex,"password"); $str_dec = hex2bin(CryptRC4($str_enc,"password")); echo $str_hex . PHP_EOL . $str_enc . PHP_EOL . $str_dec; OUTPUT: 6576656e7473 '�����~i�� events So it seems to me as it's actually encoding and decoding correctly!?
It seems the original VB6 implementation of CryptRC4 function is from my answer to "VB6 encrypt text using password" question on SO. So let me try answering your Q with this short php implementation of all public functions in the VB6 snippet: function CryptRC4($text, $key) { return openssl_encrypt($text, "RC4-40", $key, 1 | 2); } function ToHexDump($text) { return strtoupper(bin2hex($text)); } function FromHexDump($text) { return hex2bin($text); } You can excercise these one-liners with something like this: $text = "a message here"; $password = "password"; $encr = ToHexDump(CryptRC4($text, $password)); $decr = CryptRC4(FromHexDump($encr), $password); echo $text . PHP_EOL . $encr . PHP_EOL . $decr;
As it's intended for passwords, you can save yourself a lot of hassle. PHP has got built in functions (version 5.5 and newer) that are designed for dealing with the hashing of passwords and for verifying hashed passwords against the password submitted by a user. Have a read through the PHP relevant PHP manual pages http://php.net/manual/en/book.password.php
PHP - latex format of function with regex
Is it possible to write a regex which would take input like 'sqrt(2 * (2+2)) + sin(pi/6)' and transform it into '\sqrt{2 \cdot (2+2)} + \sin(\pi/6)'? The problem is the 'sqrt' and parentheses in it. It is obvious I can't simply use something like this: /sqrt\((.?)\)/ -> \\sqrt{$1} because this code would create something like this '\sqrt{2 \cdot (2+2)) + \sin(\pi/6}'.
My solution: it simply go throw the string converted to char array and tests if a current substring starts with $latex, if it does second for-cycle go from this point in different direction and by parentheses decides where the function starts and ends. (startsWith function) Code: public static function formatFunction($function, $latex, $input) { $input = preg_replace("/" . $function . "\(/", $latex . "{", $input); $arr = str_split($input); $inGap = false; $gap = 0; for ($i = count($arr) - 1; $i >= 0; $i--) { if (startsWith(substr($input, $i), $latex)) { for ($x = $i; $x < count($arr); $x++) { if ($arr[$x] == "(" || $arr[$x] == "{") { $gap++; $inGap = true; } else if ($arr[$x] == ")" || $arr[$x] == "}") { $gap--; } if ($inGap && $gap == 0) { $arr[$x] = "}"; $inGap = false; break; } } } $gap = 0; } return implode($arr); } Use: self::formatFunction("sqrt", "\\sqrt", "sqrt(25 + sqrt(16 - sqrt(49)) + (7 + 1)) + sin(pi/2)"); Output: \sqrt{25+\sqrt{16-\sqrt{49}}+(7+1)}+\sin (\pi/2) Note: sin and pi aren't formated by this code, it's only str_replace function...
In general, no regular expression can effectively handle nested parentheses. Sorry to be the bearer of bad news! The MathJAX parser library can interpret LaTeX equations and you could probably add a custom output routine to do what you want. For TeX questions, you can also try http://tex.stackexchange.com .
Some time ago i soved a similar problem in such way. Maybe it will be helpful for you $str = 'sqrt((2 * (2+2)) + sin(pi/(6+7)))'; $from = []; // parentheses content $to = []; // patterns for replace #<number> $brackets = [['(', ')'], ['{', '}'], ['[', ']']]; // new parentheses for every level $level = 0; $count = 1; // count or replace made while($count) { $str = preg_replace_callback('(\(([^()]+)\))', function ($m) use (&$to, &$from, $brackets, $level) { array_unshift($to, $brackets[$level][0] . $m[1] . $brackets[$level][1]); $i = '#' . (count($to)-1); // pattern for future replace. // here it '#1', '#2'. // Make it so they will be unique array_unshift($from, $i); return $i; }, $str, -1, $count); $level++; } echo str_replace($from, $to, $str); // return content back // sqrt[{2 * (2+2)} + sin{pi/(6+7)}] I forgot all details, but it, seems, works
Validate IBAN PHP
As designing a new platform we tried to integrate the IBAN numbers. We have to make sure that the IBAN is validated and the IBAN stored to the database is always correct. So what would be a proper way to validate the number?
As the logic was explained in my other question, I've created a function myself. Based on the logic explained in the Wikipedia article find a proper function below. Country specific validation. Algorithm and character lengths per country at https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN. function checkIBAN($iban) { if(strlen($iban) < 5) return false; $iban = strtolower(str_replace(' ','',$iban)); $Countries = array('al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24,'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28,'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19,'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29,'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24); $Chars = array('a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22,'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35); if(array_key_exists(substr($iban,0,2), $Countries) && strlen($iban) == $Countries[substr($iban,0,2)]){ $MovedChar = substr($iban, 4).substr($iban,0,4); $MovedCharArray = str_split($MovedChar); $NewString = ""; foreach($MovedCharArray AS $key => $value){ if(!is_numeric($MovedCharArray[$key])){ if(!isset($Chars[$MovedCharArray[$key]])) return false; $MovedCharArray[$key] = $Chars[$MovedCharArray[$key]]; } $NewString .= $MovedCharArray[$key]; } if(bcmod($NewString, '97') == 1) { return true; } } return false; }
Slight modification of #PeterFox answer including support for bcmod() when bcmath is not available, <?php function isValidIBAN ($iban) { $iban = strtolower($iban); $Countries = array( 'al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24, 'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28, 'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19, 'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29, 'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24 ); $Chars = array( 'a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22, 'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35 ); if (strlen($iban) != $Countries[ substr($iban,0,2) ]) { return false; } $MovedChar = substr($iban, 4) . substr($iban,0,4); $MovedCharArray = str_split($MovedChar); $NewString = ""; foreach ($MovedCharArray as $k => $v) { if ( !is_numeric($MovedCharArray[$k]) ) { $MovedCharArray[$k] = $Chars[$MovedCharArray[$k]]; } $NewString .= $MovedCharArray[$k]; } if (function_exists("bcmod")) { return bcmod($NewString, '97') == 1; } // http://au2.php.net/manual/en/function.bcmod.php#38474 $x = $NewString; $y = "97"; $take = 5; $mod = ""; do { $a = (int)$mod . substr($x, 0, $take); $x = substr($x, $take); $mod = $a % $y; } while (strlen($x)); return (int)$mod == 1; }
The accepted answer is not the preferred way of validation. The specification dictates the following: Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid Replace the two check digits by 00 (e.g. GB00 for the UK) Move the four initial characters to the end of the string Replace the letters in the string with digits, expanding the string as necessary, such that A or a = 10, B or b = 11, and Z or z = 35. Each alphabetic character is therefore replaced by 2 digits Convert the string to an integer (i.e. ignore leading zeroes) Calculate mod-97 of the new number, which results in the remainder Subtract the remainder from 98, and use the result for the two check digits. If the result is a single digit number, pad it with a leading 0 to make a two-digit number I've written a class that validates, formats and parses strings according to the spec. Hope this helps some save the time required to roll their own. The code can be found on GitHub here.
top rated function does NOT work. Just try a string with '%' in it... I'm using this one : function checkIBAN($iban) { // Normalize input (remove spaces and make upcase) $iban = strtoupper(str_replace(' ', '', $iban)); if (preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $iban)) { $country = substr($iban, 0, 2); $check = intval(substr($iban, 2, 2)); $account = substr($iban, 4); // To numeric representation $search = range('A','Z'); foreach (range(10,35) as $tmp) $replace[]=strval($tmp); $numstr=str_replace($search, $replace, $account.$country.'00'); // Calculate checksum $checksum = intval(substr($numstr, 0, 1)); for ($pos = 1; $pos < strlen($numstr); $pos++) { $checksum *= 10; $checksum += intval(substr($numstr, $pos,1)); $checksum %= 97; } return ((98-$checksum) == $check); } else return false; }
I found this solution in cakephp 3.7 validation class. Plain beautiful php realization. /** * Check that the input value has a valid International Bank Account Number IBAN syntax * Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots, * body matches against checksum via Mod97-10 algorithm * * #param string $check The value to check * * #return bool Success */ public static function iban($check) { if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check)) { return false; } $country = substr($check, 0, 2); $checkInt = intval(substr($check, 2, 2)); $account = substr($check, 4); $search = range('A', 'Z'); $replace = []; foreach (range(10, 35) as $tmp) { $replace[] = strval($tmp); } $numStr = str_replace($search, $replace, $account . $country . '00'); $checksum = intval(substr($numStr, 0, 1)); $numStrLength = strlen($numStr); for ($pos = 1; $pos < $numStrLength; $pos++) { $checksum *= 10; $checksum += intval(substr($numStr, $pos, 1)); $checksum %= 97; } return ((98 - $checksum) === $checkInt); }
This function check the IBAN and need GMP activate http://php.net/manual/en/book.gmp.php. function checkIban($string){ $to_check = substr($string, 4).substr($string, 0,4); $converted = ''; for ($i = 0; $i < strlen($to_check); $i++){ $char = strtoupper($to_check[$i]); if(preg_match('/[0-9A-Z]/',$char)){ if(!preg_match('/\d/',$char)){ $char = ord($char)-55; } $converted .= $char; } } // prevent: "gmp_mod() $num1 is not an integer string" error $converted = ltrim($converted, '0'); return strlen($converted) && gmp_strval(gmp_mod($converted, '97')) == 1; } enjoy !
PHP implementation for an URL shortening algorithm
I found Marcel Jackwerth's response to How to code a URL shortener? to be a good answer for the problem, however my question is how it'll look in PHP? Here's Marcel's answer: You need a Bijective Function f (there must be no x1 != x2, that will make f(x1) = f(x2); and for every y you will find a x so that f(x)=y). This is necessary so that you can find a inverse function g('abc') = 123 for your f(123)='abc' function. I would continue your "convert number to string" approach (however you will realize that your proposed algorithm fails if your id is a prime and greater than 52). How to convert the id to a shortened url: Think of an alphabet you want to use. In your case that's [a-zA-Z0-9]. It contains 62 letters. Take the auto-generated unique numerical key (auto-incremented id): for example 125 (a decimal number) Now you have to convert the 125 (base 10) to X (base 62). This will then be {2}{1} (2×62+1=125). Now map the symbols {2} and {1} to your alphabet. Say {0} = 'a', {25} = 'z' and so on. We will have {2} = 'c' and {1} = 'b'. So '/cb' will be your shortened url. How to resolve a shortened url abc to the initial id: If you want to do this in reverse, it's not quite diffcult. 'e9a' will be resolved to "4th,61st,0th letter in alphabet" = {4}{61}{0}, which is 4×62×62 + 61×62 + 0 = 19158. You will then just have to find your database-record with id 19158.
function convert($src, $srcAlphabet, $dstAlphabet) { $srcBase = strlen($srcAlphabet); $dstBase = strlen($dstAlphabet); $wet = $src; $val = 0; $mlt = 1; while ($l = strlen($wet)) { $digit = $wet[$l - 1]; $val += $mlt * strpos($srcAlphabet, $digit); $wet = substr($wet, 0, $l - 1); $mlt *= $srcBase; } $wet = $val; $dst = ''; while ($wet >= $dstBase) { $digitVal = $wet % $dstBase; $digit = $dstAlphabet[$digitVal]; $dst = $digit . $dst; $wet /= $dstBase; } $digit = $dstAlphabet[$wet]; $dst = $digit . $dst; return $dst; } // prints cb print convert('125', '0123456789', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'); // prints 19158 print convert('e9a', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', '0123456789');
I like this PHP function which allows you to customise the alphabet (and remove confusing 0/O's etc.) // From http://snipplr.com/view/22246/base62-encode--decode/ private function base_encode($val, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') { $str = ''; do { $i = fmod($val, $base); $str = $chars[$i] . $str; $val = ($val - $i) / $base; } while($val > 0); return $str; } Follow the URL to find the reverse 'decode' function too.
The main problem with Marcel's solution is that it uses a zero digit as a placeholder. By converting between bases, inevitably the numeral chosen to represent 0 can't appear at the front of the converted number. For example, if you convert base 10 integers to base 4 using "ABCD" using the provided mechanism, there is no way to obtain output that starts with the letter "A", since that represents a zero in the new base and won't prefix the number. You might expect 5 to be "AA", but instead, it is "BA". There is no way to coerce that algorithm into producing "AA", because it would be like writing "00" in decimal, which has the same value as "0". Here's an alternate solution in PHP that uses the entire gamut: function encode($n, $alphabet = 'ABCD') { $output = ''; if($n == 0) { $output = $alphabet[0]; } else { $digits = floor(log($n, strlen($alphabet))) + 1; for($z = 0; $z < $digits; $z++) { $digit = $n % 4; $output = $alphabet[$digit] . $output; $n = floor($n / 4) - 1; } } return $output; } function decode($code, $alphabet = 'ABCD') { $n = 0; $code = str_split($code); $unit = 1; while($letter = array_pop($code)) { $n += (strpos($alphabet, $letter) + 1) * $unit; $unit = $unit * strlen($alphabet); } return $n - 1; } echo encode(25); // should output "ABB" echo decode('ABB'); // should output 25 Change/pass the second parameter to a list of characters to use instead of the short 4-character dictionary of "ABCD".
all you need to do is convert between different base systems base 10 to base 62 https://github.com/infinitas/infinitas/blob/dev/core/short_urls/models/short_url.php
IMEI validation function
Does anybody know a PHP function for IMEI validation?
Short solution You can use this (witchcraft!) solution, and simply check the string length: function is_luhn($n) { $str = ''; foreach (str_split(strrev((string) $n)) as $i => $d) { $str .= $i %2 !== 0 ? $d * 2 : $d; } return array_sum(str_split($str)) % 10 === 0; } function is_imei($n){ return is_luhn($n) && strlen($n) == 15; } Detailed solution Here's my original function that explains each step: function is_imei($imei){ // Should be 15 digits if(strlen($imei) != 15 || !ctype_digit($imei)) return false; // Get digits $digits = str_split($imei); // Remove last digit, and store it $imei_last = array_pop($digits); // Create log $log = array(); // Loop through digits foreach($digits as $key => $n){ // If key is odd, then count is even if($key & 1){ // Get double digits $double = str_split($n * 2); // Sum double digits $n = array_sum($double); } // Append log $log[] = $n; } // Sum log & multiply by 9 $sum = array_sum($log) * 9; // Compare the last digit with $imei_last return substr($sum, -1) == $imei_last; }
Maybe can help you : This IMEI number is something like this: ABCDEF-GH-IJKLMNO-X (without “-” characters) For example: 350077523237513 In our example ABCDEF-GH-IJKLMNO-X: AB is Reporting Body Identifier such as 35 = “British Approvals Board of Telecommunications (BABT)” ABCDEF is Type Approval Code GH is Final Assembly Code IJKLMNO is Serial Number X is Check Digit Also this can help you : http://en.wikipedia.org/wiki/IMEI#Check_digit_computation If i don't misunderstood, IMEI numbers using Luhn algorithm . So you can google this :) Or you can search IMEI algorithm
Maybe your good with the imei validator in the comments here: http://www.php.net/manual/en/function.ctype-digit.php#77718 But I haven't tested it
Check this solution <?php function validate_imei($imei) { if (!preg_match('/^[0-9]{15}$/', $imei)) return false; $sum = 0; for ($i = 0; $i < 14; $i++) { $num = $imei[$i]; if (($i % 2) != 0) { $num = $imei[$i] * 2; if ($num > 9) { $num = (string) $num; $num = $num[0] + $num[1]; } } $sum += $num; } if ((($sum + $imei[14]) % 10) != 0) return false; return true; } $imei = '868932036356090'; var_dump(validate_imei($imei)); ?>
IMEI validation uses Luhn check algorithm. I found a link to a page where you can validate your IMEI. Furthermore, at the bottom of this page is a piece of code written in JavaScript to show how to calculate the 15th digit of IMEI and to valid IMEI. I might give you some ideas. You can check it out here http://imei.sms.eu.sk/index.html
Here is a jQuery solution which may be of use: https://github.com/madeinstefano/imei-validator
good fun from kasperhartwich function validateImei($imei, $use_checksum = true) { if (is_string($imei)) { if (ereg('^[0-9]{15}$', $imei)) { if (!$use_checksum) return true; for ($i = 0, $sum = 0; $i < 14; $i++) { $tmp = $imei[$i] * (($i%2) + 1 ); $sum += ($tmp%10) + intval($tmp/10); } return (((10 - ($sum%10)) %10) == $imei[14]); } } return false; }