作为程序员一定要保持良好的睡眠,才能好编程

生成短链接代码详细分解

发布时间:2018-10-15

应用场景:页面分享,使用短链接



已部署线上:http://shorturl.xiaosongit.com/



把一些长长的url经过一系列的计算,得到一个短链接,下面看一下是如何进行生成的。


基础知识:


通过字母的Ascii码进行转换


a-z 的Ascii码对应着是: 97-122

A-Z的Ascii码对应着是: 65-90

0-9 的Ascii码对应着是: 0-9

通过计算 共计 62个字符 


在下面函数中就除以 62 求余 计算。


/**
 * 转换短链接
 *
 * a-z 97-122
 * A-Z 65-90
 *
 * while 循环是把 ascii 码 进行了字母转换
 *
 * 除以62 求余,证明 余数是不会大于等于 62的
 *
 * 则 $s 的取值范围是 1-65
 *
 *
 * 通过if条件判断,把数字转换成字母表示
 * 如果 余数大于35 就转换成小写字母 +61 。 则 $s(35)+61  到 $s(61)+61  97-122
 * 如果 余数小于35且大于9 则转换成大写字母 +55 取值范围 $s(10)+55  $s(35)+55=90 65-90
 *
 * 计算一次 把$x 做一下计算 ,最后 $x==0 停止
 *
 *
 * 最后这里把 字符返回。
 *
 * 为什么 一会 加61   一会加 55 呢?
 *
 * 其实就是计算出一个结果 65-90 97-122之间的数。
 *
 * 91-96这之间是特殊字符,不要使用
 *
 * 如果$s 小于 10 那么就直接拼接上即可。
 *
 * @param $x
 * @return string
 */
function code62($x) {
  $show = '';
  while ($x > 0) {
    $s = $x % 62;
    if ($s > 35) {
      $s = chr($s + 61);
    } elseif ($s > 9 && $s <= 35) {
      $s = chr($s + 55);
    }
    $show .= $s;
    $x = floor($x / 62);
  }
  return $show;
}


优化后代码:

function code62_optimize($code) {
  $shortUrl = '';
  while (TRUE) {
    $s = $code % 62; //为什么需要除以62?  26个英文字母 大小写 + 10个数字 = 62
    $shortUrl .= $s > 9 ? $s < 35 ? chr($s + 55) : chr($s + 61) : $s; // 
    if (($code = floor($code / 62)) == 0) break;
  }
  return $shortUrl;
}



* 通过if条件判断,把数字转换成字母表示* 如果 余数大于35 就转换成小写字母 +61 。 则 $s(35)+61  到 $s(61)+61  97-122* 如果 余数小于35且大于9 则转换成大写字母 +55 取值范围 $s(10)+55  $s(35)+55=90 65-90



使用方法:

$url = "http://www.xiaosongit.com/index.php/index/detail/id/144.html";
$urlCode = sprintf("%u", crc32($url));

输出结果是:

Pj5ti


有了短链接后,拼接上 域名  http://www.xiaosongit.com/Pj5ti          通过这个地址,就可以访问到  http://www.xiaosongit.com/index.php/index/detail/id/144.html


这里需要做一个对应关系 ,保存在数据库中,就是一个查询,有个对应关系,然后直接跳转。



已部署线上:http://shorturl.xiaosongit.com/


代码如下:


index.php

/**
 * 操作步骤
 *
 * 1、index.php 这是隐藏起来
 * 2、通过pathinfo的形式获取到短链接
 *    需要连接数据库
 * 3、通过查找数据库的形式做跳转
 */

session_start();
$code = '';
$method = strtolower($_SERVER['REQUEST_METHOD']);
if ($method == 'post') {

  if ($_POST['sessioncode'] != $_SESSION['code']) {
    header('Location: /');
    exit;
  }
  $_SESSION['code'] = $code = mt_rand(time(), time() + 10000);
  $url = $_POST['url'];
  if (!isset($_POST['url']) || $_POST['url'] == '') header('location: /');
  $shorturl = code62_optimize(sprintf("%u", crc32($url)));
  $db = PdoMysql::getInstance('127.0.0.1', "root", "song", "test");
  $db->add('shorturl', ['shorturl' => $shorturl, 'url' => $url]);
  include_once 'shorturl.html';
} else if ($method == 'get') {
  $_SESSION['code'] = $code = mt_rand(time(), time() + 10000);
  if (isset($_SERVER['PATH_INFO']) && $pathinfo = $_SERVER['PATH_INFO']) {
    $shorturlCode = trim($_SERVER['PATH_INFO'], '/');
    if ($findone = $db->find('shorturl', ['shorturl' => $shorturlCode])) {
      header('Location:' . $findone[0]->url);
    } else {
      header('Location: /');
    }
  } else {
    include_once 'shorturl.html';
  }
}


/*$db = PdoMysql::getInstance('127.0.0.1', "root", "song", "test");

//var_dump($db->add('shorturl', ['shorturl' => 'Pj5ti', 'url' => 'http://www.xiaosongit.com/index.php/index/detail/id/144.html']));

if ($findone = $db->find('shorturl', ['shorturl' => 'qrwvE2'])) {
  echo $findone[0]->shorturl;
} else {
  echo 'w';
}*/


function code62_optimize($code) {
  $shortUrl = '';
  while (TRUE) {
    $s = $code % 62; //为什么需要除以62?  26个英文字母 大小写 + 10个数字 = 62
    $shortUrl .= $s > 9 ? $s < 35 ? chr($s + 55) : chr($s + 61) : $s;
    if (($code = floor($code / 62)) == 0) break;
  }
  return $shortUrl;
}


class PdoMysql extends \PDO {

  private static $_instance;
  private        $dbOptions = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING
  ];

  private $lastSqls = [];

  public static function getInstance($host, $username, $password, $db, $port = 3306, $dbOptions = []) {
    if (self::$_instance instanceof self) {
      return self::$_instance;
    } else {
      self::$_instance = new PdoMysql($host, $username, $password, $db, $port, $dbOptions);
      return self::$_instance;
    }
  }

  public function __construct($host, $username, $password, $db, $port, $dbOptions) {
    $dsn = sprintf("mysql:host=%s;dbname=%s;port=%d", $host, $db, $port);
    $dbOptions = $dbOptions ? array_merge($this->dbOptions, $dbOptions) : $this->dbOptions;
    parent::__construct($dsn, $username, $password, $dbOptions);
    return $this;
  }


  public function find($table, $where) {
    $sql = $this->parseSql($table, 'find', $where);
    $statement = $this->prepare($sql);
    $statement->execute($this->getPrepareData($where));
    $result = [];
    while (!!$row = $statement->fetchObject()) {
      $result[] = $row;
    }
    //return $statement->rowCount() ? $statement->fetch(PDO::FETCH_OBJ) : [];

    return $result;
  }


  /*public function select($table,$where=[],$page_num=1,$page_size=10){

  }

  public function delete($table,$where){

  }
  */
  public function add($table, $data) {
    $data['createtime'] = time();
    $sql = $this->parseSql($table, 'add', $data);

    $statment = $this->prepare($sql);

    $result = $statment->execute($this->getPrepareData($data));
    //array_push($this->lastSqls, $statment->debugDumpParams());
    if ($result) {
      return $this->lastInsertId();
    }
  }

  public function getLastSqls() {
    return $this->lastSqls;
  }

  private function getPrepareData($data) {
    $data = array_combine(
      array_map(function ($key) { return ':' . $key; }, array_keys($data)),
      array_map(function ($val) {
        return addslashes($val);
      }, array_values($data))
    );
    return $data;
  }

  private function parseSql($table, $action, $data, $fileds = []) {
    $sql = '';
    switch ($action) {
      case 'add':
        $keys = array_keys($data);
        $keysPrifix = array_map(function ($val) {
          return ':' . $val;
        }, $keys);

        $sql = sprintf('INSERT INTO %s(%s)VALUES(%s)', $table, implode(',', $keys), implode(',', $keysPrifix));
        break;
      case 'find':
        empty($fileds) && $fileds = ['*'];
        $where = '';
        foreach ($data as $k => $v) {
          $where .= empty($where) ? $k . '=:' . $k : ' AND ' . $k . '=:' . $k;
        }
        $sql = sprintf("SELECT %s FROM %s WHERE %s ORDER BY id DESC LIMIT 1", implode(',', $fileds), $table, $where);
        break;
    }
    return $sql;
  }

}


shorturl.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style type="text/css">
        body {
            background: #eee;
        }

        * {
            margin: 0px;
            padding: 0px;
        }

        .container {
            width: 75%;
            display: block;
            margin: 150px auto;
        }

        .container h1.text {
            color: blue;
            font-size: 40px;
            text-align: center;
            margin-bottom: 20px;
            letter-spacing: 1px;
        }

        .container form {
            width: 75%;
            margin: 20px auto;
        }

        .container form .urltext {
            padding: 5px 10px;
            background: #fff;
            border: 1px solid #ccc;
            border-radius: 3px;
            color: #333;
            font-size: 14px;
            display: block;
            width: 100%;
            height: 35px;

        }

        .container form .submitBtn {
            width: 180px;
            display: block;
            margin: 5px auto;
            border: 1px solid #ccc;
            font-size: 14px;
            font-weight: bold;
            height: 35px;
            line-height: 35px;
            border-radius: 5px;
            backgroud: #fff;
            cursor: pointer;
        }

        .createShortUrl {
            font-size: 16px;
            color: blue;
            margin-top: 40px;
        }

    </style>
    <title>短链接转换</title>
</head>
<body>

<div class="container">

    <h1 class="text">转成短链接</h1>

    <div class="form">

        <form method="post" action="/">

            <input type="text" name="url" class="urltext">


            <input type="hidden" name="sessioncode" value="<?php echo $code;?>">


            <input type="submit" class="submitBtn" value="提交">

            <?php if(isset($shorturl)){ ?>
            <div class="createShortUrl">
                生成短链接地址是:<?php echo $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/'. $shorturl;?></div>
            <?php } ?>
        </form>

    </div>

</div>


</body>
</html>


nginx配置


隐藏index.php 并支持path_info

server {
        listen       8005;
        client_max_body_size 100m;
        index index.php index.html;
        root  /usr/local/nginx/html/shorturl;
        #error_page 404 = /404/index.html;
        location / {
            root   /usr/local/nginx/html/shorturl;
            index  index.html index.php index.htm;
            
            #隐藏 index.php 
            if (!-e $request_filename) {
                rewrite  ^(.*)$  /index.php$1  last;
                break;
            }
        }
        
        location ~ .*\.(jpg|jpeg|png|gif|js|css)$ {
                expires 1d;
        }
        location ~ \.php(/|$) {
                fastcgi_pass   127.0.0.1:9007;
                fastcgi_index  index.php;
                
                #这两行是关键 支持pathinfo
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                fastcgi_param PATH_INFO $fastcgi_path_info;
                
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include        fastcgi_params;
        }
}