/** * CREATE TABLE `shortUrl` ( * `id` int(11) NOT NULL AUTO_INCREMENT, * `key` int(11) unsigned NOT NULL, * `suffix` int(11) unsigned NOT NULL, * `url` text NOT NULL, * `num` int(11) NOT NULL, * `timeout` int(11) NOT NULL, * `createTime` int(11) NOT NULL, * `uri` char(16) GENERATED ALWAYS AS (concat(lower(hex(`key`)),hex(`suffix`))) VIRTUAL NOT NULL, * PRIMARY KEY (`id`), * UNIQUE KEY `uniq` (`key`,`suffix`) USING BTREE * ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='short url redirect'; */ class ShortUrl { private $pdo; private $tableName; private $strict = false; /** * * @param \PDO $pdo * @param string $tableName */ function __construct($pdo, $tableName) { $this->pdo = $pdo; $this->tableName = $tableName; } /** * * @param boolean $flag */ function setStrict($flag) { $this->strict = $flag; } /** * * @return boolean */ function getStrict() { return $this->strict; } /** * * @param string $url * @param number $timeout * @return string */ function add($url, $timeout = 0) { $query = parse_url($url, PHP_URL_QUERY); if (! empty($query)) { parse_str($query, $query); if (! $this->strict) { ksort($query); } $query = http_build_query($query); $classUrl = '\Http\Url'; if (function_exists('http_build_url')) { $parse = parse_url($url); $parse['query'] = $query; $url = http_build_query($parse); } elseif (class_exists($classUrl)) { $parse = new $classUrl($url); $parse->query = $query; $url = $parse->toString(); } else { $url = preg_replace('/\?(.+)(#|$)/', '?' . $query, $url); } } $data = array(); $key = crc32($url); $key = sprintf("%u", $key); $sql = 'select uri from ' . $this->tableName . ' where `key`=' . $key; $sql .= ' and url=?'; $stat = $this->pdo->prepare($sql); $stat->execute(array( $url )); $uri = $stat->fetchColumn(); if (! empty($uri)) { return $uri; } $suffix = 0; // Find min usable suffix regardless exists values.Very powerful algorithm. $batch = 1000; $loop = 0; $this->pdo->exec('lock tables ' . $this->tableName . ' write'); while (true) { $sql = 'select suffix from ' . $this->tableName; $sql .= ' where `key`=' . $key; $sql .= ' and suffix>=' . $suffix; $sql .= ' order by suffix limit ' . $batch; $stat = $this->pdo->query($sql); $list = $stat->fetchAll(\PDO::FETCH_NUM); if (empty($list)) { break; } foreach ($list as $k => $v) { $suffix = ($k + $batch * $loop); if ($suffix != $v[0]) { break 2; } } $suffix ++; if ($k + 1 < $batch) { break; } $loop ++; } $data['key'] = $key; $data['suffix'] = $suffix; $data['url'] = $url; $data['num'] = 0; $data['timeout'] = $timeout; $data['createTime'] = time(); $sql = 'insert into ' . $this->tableName; $sql .= '(`key`,suffix,url,num,timeout,createTime)'; $sql .= 'values(:key,:suffix,:url,:num,:timeout,:createTime)'; $stat = $this->pdo->prepare($sql); $res = $stat->execute($data); $this->pdo->exec('unlock tables'); if ($res) { return dechex($key) . dechex($suffix); } } /** * * @return boolean */ function clean() { $time = time(); $stat = $this->pdo->prepare( 'delete from ' . $this->tableName . ' where timeout>0 && createTime+timeout<?'); return $stat->execute(array( $time )); } /** * * @param string $key * @return null|integer 1:timeout or unavailable */ function redirect($key) { $suffix = substr($key, 8); $key = substr($key, 0, 8); $key = sprintf("%u", hexdec($key)); $sql = 'select * from ' . $this->tableName; $sql .= ' where `key`=? and suffix=?'; $stat = $this->pdo->prepare($sql); $stat->execute(array( $key, $suffix )); $row = $stat->fetchObject(); if (false == $row) { return 1; } if ($row->timeout > 0) { $expire = $row->createTime + $row->timeout; if ($expire < time()) { return 1; } } $sql = 'update ' . $this->tableName . ' set num=num+1 where `key`=?'; $sql .= ' and suffix=?'; $stat = $this->pdo->prepare($sql); $stat->execute(array( $key, $suffix )); header('Location: ' . $row->url); } }
下载:ShortUrl.php