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