I\'m coding an url shortener function for a project in which I\'m learning php, here is the code (btw I suppose that global
here is not a good thing to do :P):<
In case you're looking for the opposite function to take a base64 number and convert to base10, here's some PHP based off of the JavaScript in this answer: How to convert base64 to base10 in PHP?
function lengthen($id) {
$alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
$number=0;
foreach(str_split($id) as $letter) {
$number=($number*64) + strpos($alphabet,$letter);
}
return $number;
}
This is a variation of Nathans code to handle large integers greater than PHP_INT_MAX.
This uses the BC Maths Functions that should be built-in on Windows servers, but this needs to be enabled as an optional extension on Unix servers. This solution also requires a couple of custom BC functions to handle floor and round functions that I copied from the post by Alix Axel.
function shorten($value, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') {
$base = strlen($alphabet);
$result = '';
while ($value) {
$mod = bcmod($value, $base);
$value = bcfloor(bcdiv($value, $base));
$result = $alphabet[$mod] . $result;
}
return $result;
}
function lengthen($value, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') {
$base= strlen($alphabet);
$result = '';
for($i = 0, $limit = strlen($value); $i < $limit; $i++) {
$result = bcadd(bcmul($base, $result), strpos($alphabet, $value[$i]));
}
return $result;
}
function bcceil($number) {
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 1, 0);
return bcsub($number, 0, 0);
}
return $number;
}
function bcfloor($number) {
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 0, 0);
return bcsub($number, 1, 0);
}
return $number;
}
function bcround($number, $precision = 0) {
if (strpos($number, '.') !== false) {
if ($number[0] != '-') return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}
return $number;
}
Examples running PHP 5.6 on Windows (32 bit)
foreach ([0, 1, 9, 10, 115617, bcsub(PHP_INT_MAX, 1), PHP_INT_MAX, bcadd(PHP_INT_MAX, 1234567890)] as $value) {
$short = shorten($value);
$reversed = lengthen($short);
print shorten($value) . " ($value)<br>";
if ("$value" !== $reversed) {
print 'ERROR REVERSING VALUE<br>';
}
}
Outputs
0 (0)
1 (1)
9 (9)
a (10)
sex (115617)
1----_ (2147483646)
1----- (2147483647)
39Bwbh (3382051537)
If the ID is public, avoid using vowels in the string (115617 is shortened to sex for example). This would be the base 54 version that should provide safe words.
$alphabet = '0123456789bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ_-';
By the way, check out the base_convert() function (http://php.net/manual/en/function.base-convert.php):
echo base_convert(1000000000, 10, 36);
36 is the longest base it can convert to, though. But in the comments section I found this:
function dec2any( $num, $base, $index=false ) {
if (! $base ) {
$base = strlen( $index );
} else if (! $index ) {
$index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ,0 ,$base );
}
$out = "";
for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
$a = floor( $num / pow( $base, $t ) );
$out = $out . substr( $index, $a, 1 );
$num = $num - ( $a * pow( $base, $t ) );
}
return $out;
}
echo dec2any(1000000000, 64, "_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
Maybe it will help?
These two functions are very convenient, thanks to @malhal:
function shorten_int($id)
{
$id=dechex($id);
$id=strlen($id)%2===0?hex2bin($id):hex2bin('0'.$id);
$id=base64_encode($id);
$id=strtr($id, array('/'=>'_', '+'=>'-', '='=>''));
return $id;
}
function unshorten_int($id)
{
$id=strtr($id, array('-'=>'+', '_'=>'/'));
$id=base64_decode($id);
$id=bin2hex($id);
return base_convert($id, 16, 10);
}
echo shorten_int(43121111)."\n";
echo unshorten_int(shorten_int(43121111))."\n";
How about this:
function shorten_int($id){
$hex = base_convert(id, 10, 16);
$base64 = base64_encode(pack('H*', $hex));
//$base64 = str_replace("/", "_", $base64); // remove unsafe url chars
//$base64 = str_replace("+", "-", $base64);
//$base64 = rtrim($base64, '='); // Remove the padding "=="
$replacePairs = array('/' => '_',
'+' => '-',
'=' => '');
$base64 = strtr($base64, $replacePairs); // optimisation
return $base64;
}
Paul Greg created some PHP code that converts from Base-10 to another base. This can be tested and the code downloaded here:
http://www.pgregg.com/projects/php/base_conversion/base_conversion.php
I'm using this approach to convert the database row id's to Base-64. Once these numbers have been shortened they can be used in the URL. [details]