应用场景:页面分享,使用短链接
已部署线上: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; } }