设为首页 收藏本站
查看: 614|回复: 0

[经验分享] php创建短ID Create short IDs with PHP

[复制链接]

尚未签到

发表于 2017-4-5 08:53:09 | 显示全部楼层 |阅读模式
More is Less - the 'math'

The alphabet has 26 characters. That's a lot more than 10 digits. If we also distinguish upper- and lowercase, and add digits to the bunch or the heck of it, we already have (26 x 2 + 10) 62 options we can use per position in the ID.
Now of course we can also add additional funny characters to 'the bunch' like - / * & # but those may cause problems in URLs and that's our target audience for now.
OK so because there are roughly 6x more characters we will use per position, IDs will get much shorter. We can just fit a lot more data in each position.
This is basically what url shortening services do like tinyurl, is.gd, or bit.ly. But similar IDs can also be found at youtube: http://www.youtube.com/watch?v=yzNjIBEdyww

Convert your IDs

Now unlike Database servers: webservers are easy to scale so you can let them do a bit of converting to ease the life of your users, while keeping your database fast with numbers (MySQL really likes them plain numbers ; ).
To do the conversion I've written a PHP function that can translate big numbers to short strings and vice versa. I call it: alphaID.
The resulting string is not hard to decipher, but it can be a very nice feature to make URLs or directorie structures more compact and significant.
So basically:


  • when someone requests rLHWfKd
  • alphaID() converts it to 999999999999
  • you lookup the record for id 999999999999 in your database

Source

<?php
/**
* Translates a number to a short alhanumeric version
*
* Translated any number up to 9007199254740992
* to a shorter version in letters e.g.:
* 9007199254740989 --> PpQXn7COf
*
* specifiying the second argument true, it will
* translate back e.g.:
* PpQXn7COf --> 9007199254740989
*
* this function is based on any2dec && dec2any by
* fragmer[at]mail[dot]ru
* see: http://nl3.php.net/manual/en/function.base-convert.php#52450
*
* If you want the alphaID to be at least 3 letter long, use the
* $pad_up = 3 argument
*
* In most cases this is better than totally random ID generators
* because this can easily avoid duplicate ID's.
* For example if you correlate the alpha ID to an auto incrementing ID
* in your database, you're done.
*
* The reverse is done because it makes it slightly more cryptic,
* but it also makes it easier to spread lots of IDs in different
* directories on your filesystem. Example:
* $part1 = substr($alpha_id,0,1);
* $part2 = substr($alpha_id,1,1);
* $part3 = substr($alpha_id,2,strlen($alpha_id));
* $destindir = "/".$part1."/".$part2."/".$part3;
* // by reversing, directories are more evenly spread out. The
* // first 26 directories already occupy 26 main levels
*
* more info on limitation:
* - http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165372
*
* if you really need this for bigger numbers you probably have to look
* at things like: http://theserverpages.com/php/manual/en/ref.bc.php
* or: http://theserverpages.com/php/manual/en/ref.gmp.php
* but I haven't really dugg into this. If you have more info on those
* matters feel free to leave a comment.
*
* The following code block can be utilized by PEAR's Testing_DocTest
* <code>
* // Input //
* $number_in = 2188847690240;
* $alpha_in  = "SpQXn7Cb";
*
* // Execute //
* $alpha_out  = alphaID($number_in, false, 8);
* $number_out = alphaID($alpha_in, true, 8);
*
* if ($number_in != $number_out) {
* echo "Conversion failure, ".$alpha_in." returns ".$number_out." instead of the ";
* echo "desired: ".$number_in."\n";
* }
* if ($alpha_in != $alpha_out) {
* echo "Conversion failure, ".$number_in." returns ".$alpha_out." instead of the ";
* echo "desired: ".$alpha_in."\n";
* }
*
* // Show //
* echo $number_out." => ".$alpha_out."\n";
* echo $alpha_in." => ".$number_out."\n";
* echo alphaID(238328, false)." => ".alphaID(alphaID(238328, false), true)."\n";
*
* // expects:
* // 2188847690240 => SpQXn7Cb
* // SpQXn7Cb => 2188847690240
* // aaab => 238328
*
* </code>
*
* @authorKevin van Zonneveld <kevin@vanzonneveld.net>
* @authorSimon Franz
* @authorDeadfish
* @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
* @license   http://www.opensource.org/licenses/bsd-license.php New BSD Licence
* @version   SVN: Release: $Id: alphaID.inc.php 344 2009-06-10 17:43:59Z kevin $
* @link  http://kevin.vanzonneveld.net/
*
* @param mixed   $in  String or long input to translate
* @param boolean $to_num  Reverses translation when true
* @param mixed   $pad_up  Number or boolean padds the result up to a specified length
* @param string  $passKey Supplying a password makes it harder to calculate the original ID
*
* @return mixed string or long
*/
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
$index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($passKey !== null) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
for ($n = 0; $n<strlen($index); $n++) $i[] = substr( $index,$n ,1);
$passhash = hash('sha256',$passKey);
$passhash = (strlen($passhash) < strlen($index))
? hash('sha512',$passKey)
: $passhash;
for ($n=0; $n < strlen($index); $n++) $p[] =  substr($passhash, $n ,1);
array_multisort($p,  SORT_DESC, $i);
$index = implode($i);
}
$base  = strlen($index);
if ($to_num) {
// Digital number  <<--  alphabet letter code
$in  = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++) {
$bcpow = bcpow($base, $len - $t);
$out   = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) $out -= pow($base, $pad_up);
}
$out = sprintf('%F', $out);
$out = substr($out, 0, strpos($out, '.'));
}
else
{
// Digital number  -->>  alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) $in += pow($base, $pad_up);
}
$out = "";
for ($t = floor(log($in, $base)); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a   = floor($in / $bcp) % $base;
$out = $out . substr($index, $a, 1);
$in  = $in - ($a * $bcp);
}
$out = strrev($out); // reverse
}
return $out;
}


Example

Running:

alphaID(9007199254740989);
will return 'PpQXn7COf' and:

alphaID('PpQXn7COf', true);
will return '9007199254740989'
Easy right?

More features


  • There also is an optional third argument: $pad_up. This enables you to make the resulting alphaId at least X characters long.
  • You can support even more characters (making the resulting alphaID even smaller) by adding characters to the $index var at the top of the function body.

JavaScript Implementation

Thanks to Even Simon, there's a JavaScript implementation. You will also find PHP version there, that implements the encode & decode functions as separate methods in a class.

<script>
/**
*  Javascript AlphabeticID class
*  (based on a script by Kevin van Zonneveld <kevin@vanzonneveld.net>)
*
*  Author: Even Simon <even.simon@gmail.com>
*
*  Description: Translates a numeric identifier into a short string and backwords.
*
*  Usage:
*    var str = AlphabeticID.encode(9007199254740989); // str = 'fE2XnNGpF'
*    var id = AlphabeticID.decode('fE2XnNGpF'); // id = 9007199254740989;
**/
var AlphabeticID = {
index:'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
/**
*  <a href="http://twitter.com/function">@function</a> AlphabeticID.encode
*  <a href="http://twitter.com/description">@description</a> Encode a number into short string
*  <a href="http://twitter.com/param">@param</a> integer
*  <a href="http://twitter.com/return">@return</a> string
**/
encode:function(_number){
if('undefined' == typeof _number){
return null;
}
else if('number' != typeof(_number)){
throw new Error('Wrong parameter type');
}
var ret = '';
for(var i=Math.floor(Math.log(parseInt(_number))/Math.log(AlphabeticID.index.length));i>=0;i--){
ret = ret + AlphabeticID.index.substr((Math.floor(parseInt(_number) / AlphabeticID.bcpow(AlphabeticID.index.length, i)) % AlphabeticID.index.length),1);
}
return ret.reverse();
},
/**
*  <a href="http://twitter.com/function">@function</a> AlphabeticID.decode
*  <a href="http://twitter.com/description">@description</a> Decode a short string and return number
*  <a href="http://twitter.com/param">@param</a> string
*  <a href="http://twitter.com/return">@return</a> integer
**/
decode:function(_string){
if('undefined' == typeof _string){
return null;
}
else if('string' != typeof _string){
throw new Error('Wrong parameter type');
}
var str = _string.reverse();
var ret = 0;
for(var i=0;i<=(str.length - 1);i++){
ret = ret + AlphabeticID.index.indexOf(str.substr(i,1)) * (AlphabeticID.bcpow(AlphabeticID.index.length, (str.length - 1) - i));
}
return ret;
},
/**
*  <a href="http://twitter.com/function">@function</a> AlphabeticID.bcpow
*  <a href="http://twitter.com/description">@description</a> Raise _a to the power _b
*  <a href="http://twitter.com/param">@param</a> float _a
*  <a href="http://twitter.com/param">@param</a> integer _b
*  <a href="http://twitter.com/return">@return</a> string
**/
bcpow:function(_a, _b){
return Math.floor(Math.pow(parseFloat(_a), parseInt(_b)));
}
};
/**
*  <a href="http://twitter.com/function">@function</a> String.reverse
*  <a href="http://twitter.com/description">@description</a> Reverse a string
*  <a href="http://twitter.com/return">@return</a> string
**/
String.prototype.reverse = function(){
return this.split('').reverse().join('');
};
</script>

Python Implementation

Thanks to wessite, there's a Python implementation.

ALPHABET = "bcdfghjklmnpqrstvwxyz0123456789BCDFGHJKLMNPQRSTVWXYZ"
BASE = len(ALPHABET)
MAXLEN = 6
def encode_id(self, n):
pad = self.MAXLEN - 1
n = int(n + pow(self.BASE, pad))
s = []
t = int(math.log(n, self.BASE))
while True:
bcp = int(pow(self.BASE, t))
a = int(n / bcp) % self.BASE
s.append(self.ALPHABET[a:a+1])
n = n - (a * bcp)
t -= 1
if t < 0: break
return "".join(reversed(s))
def decode_id(self, n):
n = "".join(reversed(n))
s = 0
l = len(n) - 1
t = 0
while True:
bcpow = int(pow(self.BASE, l - t))
s = s + self.ALPHABET.index(n[t:t+1]) * bcpow
t += 1
if t > l: break
pad = self.MAXLEN - 1
s = int(s - pow(self.BASE, pad))
return int(s)

Python Implementation

Thanks to Andy Li, there's a HaXe implementation.

 
/**
*  HaXe version of AlphabeticID
*  Author: Andy Li <andy@onthewings.net>
*  ported from...
*
*  Javascript AlphabeticID class
*  Author: Even Simon <even.simon@gmail.com>
*  which is based on a script by Kevin van Zonneveld <kevin@vanzonneveld.net>)
*
*  Description: Translates a numeric identifier into a short string and backwords.
*  http://kevin.vanzonneveld.net/techblog/article/create_short_ids_with_php_like_youtube_or_tinyurl/
**/
class AlphaID {
static public var index:String = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static public function encode(_number:Int):String {        
var strBuf = new StringBuf();
var i = 0;
var end = Math.floor(Math.log(_number)/Math.log(index.length));
while(i <= end) {
strBuf.add(index.charAt((Math.floor(_number / bcpow(index.length, i++)) % index.length)));
}
return strBuf.toString();
}
static public function decode(_string:String):Int {
var str = reverseString(_string);
var ret = 0;
var i = 0;
var end = str.length - 1;
while(i <= end) {
ret += Std.int(index.indexOf(str.charAt(i)) * (bcpow(index.length, end-i)));
++i;
}
return ret;
}
inline static private function bcpow(_a:Float, _b:Float):Float {
return Math.floor(Math.pow(_a, _b));
}
inline static private function reverseString(inStr:String):String {
var ary = inStr.split("");
ary.reverse();
return ary.join("");
}
}
  来源:
http://kevin.vanzonneveld.net/techblog/article/create_short_ids_with_php_like_youtube_or_tinyurl/

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-360332-1-1.html 上篇帖子: [转] 通过 PHP 和 Sajax 使用 Ajax(三) 下篇帖子: Php设计模式(三):行为型模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表