本文目录导读:

我来帮你设计一个PHP短链接与参数追踪系统,这是一个完整的解决方案,包含短链接生成、跳转和参数追踪功能。
数据库结构
-- 短链接表 CREATE TABLE `short_links` ( `id` int(11) NOT NULL AUTO_INCREMENT, `short_code` varchar(10) NOT NULL COMMENT '短码', `original_url` text NOT NULL COMMENT '原始URL', varchar(255) DEFAULT NULL COMMENT '链接标题', `user_id` int(11) DEFAULT NULL COMMENT '创建用户ID', `expire_at` datetime DEFAULT NULL COMMENT '过期时间', `is_active` tinyint(1) DEFAULT '1' COMMENT '是否启用', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_short_code` (`short_code`), KEY `idx_user_id` (`user_id`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 访问追踪表 CREATE TABLE `link_tracking` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `short_link_id` int(11) NOT NULL COMMENT '短链接ID', `ip_address` varchar(45) DEFAULT NULL COMMENT '访问IP', `user_agent` text COMMENT '用户代理', `referer` text COMMENT '来源URL', `country` varchar(100) DEFAULT NULL COMMENT '国家', `city` varchar(100) DEFAULT NULL COMMENT '城市', `device_type` varchar(20) DEFAULT NULL COMMENT '设备类型(desktop/mobile/tablet)', `os` varchar(50) DEFAULT NULL COMMENT '操作系统', `browser` varchar(50) DEFAULT NULL COMMENT '浏览器', `is_click` tinyint(1) DEFAULT '1' COMMENT '是否点击', `click_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '点击时间', `parameters` json DEFAULT NULL COMMENT '自定义参数', PRIMARY KEY (`id`), KEY `idx_short_link_id` (`short_link_id`), KEY `idx_click_time` (`click_time`), KEY `idx_country` (`country`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 链接参数配置表 CREATE TABLE `link_parameters` ( `id` int(11) NOT NULL AUTO_INCREMENT, `short_link_id` int(11) NOT NULL, `param_name` varchar(50) NOT NULL COMMENT '参数名', `param_value` varchar(255) DEFAULT NULL COMMENT '参数值', `is_required` tinyint(1) DEFAULT '0' COMMENT '是否必填', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_short_link_id` (`short_link_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
短链接核心类
<?php
// ShortLinkService.php
class ShortLinkService {
private $db;
private $config;
public function __construct($db, $config = []) {
$this->db = $db;
$this->config = array_merge([
'short_code_length' => 6,
'allowed_chars' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
'base_url' => 'https://yourdomain.com/'
], $config);
}
/**
* 生成短链接
*/
public function createShortLink($originalUrl, $title = '', $userId = null, $expireAt = null, $parameters = []) {
// 验证URL
if (!filter_var($originalUrl, FILTER_VALIDATE_URL)) {
throw new Exception('Invalid URL');
}
// 生成唯一短码
$shortCode = $this->generateUniqueCode();
// 构建原始URL参数
if (!empty($parameters)) {
$queryParams = [];
foreach ($parameters as $param => $value) {
$queryParams[] = urlencode($param) . '=' . urlencode($value);
}
$separator = (strpos($originalUrl, '?') === false) ? '?' : '&';
$originalUrl .= $separator . implode('&', $queryParams);
}
// 插入数据库
$stmt = $this->db->prepare("
INSERT INTO short_links (short_code, original_url, title, user_id, expire_at)
VALUES (?, ?, ?, ?, ?)
");
$stmt->execute([$shortCode, $originalUrl, $title, $userId, $expireAt]);
$linkId = $this->db->lastInsertId();
// 保存参数配置
if (!empty($parameters)) {
$this->saveLinkParameters($linkId, $parameters);
}
return [
'id' => $linkId,
'short_code' => $shortCode,
'short_url' => $this->config['base_url'] . $shortCode,
'original_url' => $originalUrl
];
}
/**
* 生成唯一短码
*/
private function generateUniqueCode() {
$chars = $this->config['allowed_chars'];
$maxAttempts = 10;
for ($i = 0; $i < $maxAttempts; $i++) {
$code = '';
for ($j = 0; $j < $this->config['short_code_length']; $j++) {
$code .= $chars[random_int(0, strlen($chars) - 1)];
}
// 检查是否已存在
$stmt = $this->db->prepare("SELECT id FROM short_links WHERE short_code = ?");
$stmt->execute([$code]);
if (!$stmt->fetch()) {
return $code;
}
}
throw new Exception('Failed to generate unique code');
}
/**
* 保存链接参数配置
*/
private function saveLinkParameters($linkId, $parameters) {
$stmt = $this->db->prepare("
INSERT INTO link_parameters (short_link_id, param_name, param_value)
VALUES (?, ?, ?)
");
foreach ($parameters as $name => $value) {
$stmt->execute([$linkId, $name, $value]);
}
}
/**
* 获取并重定向
*/
public function redirect($shortCode) {
// 查询短链接
$stmt = $this->db->prepare("
SELECT id, original_url, expire_at, is_active
FROM short_links
WHERE short_code = ? AND is_active = 1
");
$stmt->execute([$shortCode]);
$link = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$link) {
header("HTTP/1.0 404 Not Found");
exit('Link not found');
}
// 检查过期
if ($link['expire_at'] && strtotime($link['expire_at']) < time()) {
header("HTTP/1.0 410 Gone");
exit('Link has expired');
}
// 记录访问信息
$this->trackVisit($link['id']);
// 重定向
header("Location: " . $link['original_url'], true, 302);
exit();
}
/**
* 追踪访问信息
*/
private function trackVisit($shortLinkId) {
$ip = $this->getClientIP();
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
// 解析设备信息
$deviceInfo = $this->parseUserAgent($userAgent);
// 获取地理位置(可选,需要IP地理定位服务)
$geoData = $this->getGeoLocation($ip);
// 收集URL参数
$parameters = [];
if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $parameters);
}
try {
$stmt = $this->db->prepare("
INSERT INTO link_tracking
(short_link_id, ip_address, user_agent, referer, country, city,
device_type, os, browser, parameters)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$shortLinkId,
$ip,
$userAgent,
$referer,
$geoData['country'] ?? null,
$geoData['city'] ?? null,
$deviceInfo['device_type'],
$deviceInfo['os'],
$deviceInfo['browser'],
json_encode($parameters)
]);
} catch (Exception $e) {
// 日志记录错误但不中断重定向
error_log("Tracking failed: " . $e->getMessage());
}
}
/**
* 获取客户端IP
*/
private function getClientIP() {
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'REMOTE_ADDR'
];
foreach ($headers as $header) {
if (isset($_SERVER[$header])) {
$ips = explode(',', $_SERVER[$header]);
$ip = trim($ips[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
* 解析User-Agent
*/
private function parseUserAgent($userAgent) {
$result = [
'device_type' => 'desktop',
'os' => 'Unknown',
'browser' => 'Unknown'
];
if (empty($userAgent)) {
return $result;
}
// 设备类型
if (preg_match('/Mobile|Android|iPhone|iPad|iPod/i', $userAgent)) {
$result['device_type'] = 'mobile';
if (preg_match('/iPad|Tablet/i', $userAgent)) {
$result['device_type'] = 'tablet';
}
}
// 操作系统
$osPatterns = [
'Windows' => '/Windows NT/i',
'Mac OS' => '/Mac OS X|Macintosh/i',
'Linux' => '/Linux/i',
'Android' => '/Android/i',
'iOS' => '/iPhone|iPad|iPod/i'
];
foreach ($osPatterns as $os => $pattern) {
if (preg_match($pattern, $userAgent)) {
$result['os'] = $os;
break;
}
}
// 浏览器
$browserPatterns = [
'Chrome' => '/Chrome/i',
'Firefox' => '/Firefox/i',
'Safari' => '/Safari/i',
'Edge' => '/Edge|Edg/i',
'Opera' => '/Opera|OPR/i'
];
foreach ($browserPatterns as $browser => $pattern) {
if (preg_match($pattern, $userAgent)) {
$result['browser'] = $browser;
break;
}
}
return $result;
}
/**
* 获取地理位置(示例使用ip-api.com)
*/
private function getGeoLocation($ip) {
// 忽略内网IP
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
try {
$response = @file_get_contents("http://ip-api.com/json/{$ip}");
if ($response) {
$data = json_decode($response, true);
if ($data['status'] === 'success') {
return [
'country' => $data['country'],
'city' => $data['city']
];
}
}
} catch (Exception $e) {
// 忽略错误
}
}
return [];
}
/**
* 获取统计信息
*/
public function getStatistics($shortLinkId, $startDate = null, $endDate = null) {
$conditions = ['short_link_id = ?'];
$params = [$shortLinkId];
if ($startDate) {
$conditions[] = 'click_time >= ?';
$params[] = $startDate;
}
if ($endDate) {
$conditions[] = 'click_time <= ?';
$params[] = $endDate;
}
$where = implode(' AND ', $conditions);
$stats = [];
// 总点击量
$stmt = $this->db->prepare("SELECT COUNT(*) as total FROM link_tracking WHERE {$where}");
$stmt->execute($params);
$stats['total_clicks'] = $stmt->fetchColumn();
// 独立IP
$stmt = $this->db->prepare("SELECT COUNT(DISTINCT ip_address) as unique_ip FROM link_tracking WHERE {$where}");
$stmt->execute($params);
$stats['unique_visitors'] = $stmt->fetchColumn();
// 按国家统计
$stmt = $this->db->prepare("
SELECT country, COUNT(*) as count
FROM link_tracking
WHERE {$where} AND country IS NOT NULL
GROUP BY country
ORDER BY count DESC
LIMIT 10
");
$stmt->execute($params);
$stats['by_country'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 按设备统计
$stmt = $this->db->prepare("
SELECT device_type, COUNT(*) as count
FROM link_tracking
WHERE {$where}
GROUP BY device_type
");
$stmt->execute($params);
$stats['by_device'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 按日期统计
$stmt = $this->db->prepare("
SELECT DATE(click_time) as date, COUNT(*) as count
FROM link_tracking
WHERE {$where}
GROUP BY DATE(click_time)
ORDER BY date
");
$stmt->execute($params);
$stats['by_date'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 参数统计
$stmt = $this->db->prepare("
SELECT parameters, COUNT(*) as count
FROM link_tracking
WHERE {$where} AND parameters IS NOT NULL
GROUP BY parameters
LIMIT 20
");
$stmt->execute($params);
$stats['by_parameters'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $stats;
}
}
路由处理 (跳转逻辑)
<?php
// redirect.php - 短链接跳转处理
require_once 'database.php'; // 数据库连接
require_once 'ShortLinkService.php';
$db = new PDO('mysql:host=localhost;dbname=your_db', 'username', 'password');
$shortLinkService = new ShortLinkService($db);
// 获取短码
$shortCode = $_GET['code'] ?? '';
if (empty($shortCode)) {
header("HTTP/1.0 400 Bad Request");
exit('Missing short code');
}
// 执行跳转并追踪
$shortLinkService->redirect($shortCode);
创建短链接API
<?php
// api_create.php - 创建短链接API
require_once 'database.php';
require_once 'ShortLinkService.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$db = new PDO('mysql:host=localhost;dbname=your_db', 'username', 'password');
$shortLinkService = new ShortLinkService($db);
try {
$data = json_decode(file_get_contents('php://input'), true);
$originalUrl = $data['url'] ?? '';
$title = $data['title'] ?? '';
$expireAt = $data['expire_at'] ?? null;
$parameters = $data['parameters'] ?? [];
$result = $shortLinkService->createShortLink(
$originalUrl,
$title,
null, // user_id
$expireAt,
$parameters
);
echo json_encode([
'success' => true,
'data' => $result
]);
} catch (Exception $e) {
http_response_code(400);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
统计面板查询
<?php
// api_stats.php - 统计查询API
require_once 'database.php';
require_once 'ShortLinkService.php';
header('Content-Type: application/json');
$db = new PDO('mysql:host=localhost;dbname=your_db', 'username', 'password');
$shortLinkService = new ShortLinkService($db);
$linkId = $_GET['link_id'] ?? 0;
$startDate = $_GET['start_date'] ?? null;
$endDate = $_GET['end_date'] ?? null;
if (!$linkId) {
http_response_code(400);
echo json_encode(['error' => 'Missing link_id']);
exit;
}
try {
$stats = $shortLinkService->getStatistics($linkId, $startDate, $endDate);
echo json_encode([
'success' => true,
'data' => $stats
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
管理界面示例
<!DOCTYPE html>
<html>
<head>短链接管理系统</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: auto; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input[type="text"], input[type="url"] { width: 100%; padding: 8px; }
.btn { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
.result { margin-top: 20px; padding: 15px; background: #f0f0f0; }
.param-row { margin-bottom: 10px; }
</style>
</head>
<body>
<div class="container">
<h1>创建短链接</h1>
<form id="createLinkForm">
<div class="form-group">
<label>原始URL:</label>
<input type="url" id="originalUrl" required>
</div>
<div class="form-group">
<label>标题 (可选):</label>
<input type="text" id="title">
</div>
<div class="form-group">
<label>自定义参数:</label>
<div id="parametersContainer">
<div class="param-row">
<input type="text" placeholder="参数名" class="param-name">
<input type="text" placeholder="参数值" class="param-value">
<button type="button" onclick="addParam()">+</button>
</div>
</div>
</div>
<button type="submit" class="btn">生成短链接</button>
</form>
<div id="result" class="result" style="display:none;"></div>
</div>
<script>
function addParam() {
const container = document.getElementById('parametersContainer');
const row = document.createElement('div');
row.className = 'param-row';
row.innerHTML = `
<input type="text" placeholder="参数名" class="param-name">
<input type="text" placeholder="参数值" class="param-value">
<button type="button" onclick="this.parentElement.remove()">-</button>
`;
container.appendChild(row);
}
document.getElementById('createLinkForm').addEventListener('submit', function(e) {
e.preventDefault();
const parameters = {};
document.querySelectorAll('.param-row').forEach(row => {
const name = row.querySelector('.param-name').value;
const value = row.querySelector('.param-value').value;
if (name && value) {
parameters[name] = value;
}
});
const data = {
url: document.getElementById('originalUrl').value,
title: document.getElementById('title').value,
parameters: parameters
};
fetch('/api_create.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
const resultDiv = document.getElementById('result');
resultDiv.style.display = 'block';
if (result.success) {
resultDiv.innerHTML = `
<h3>生成成功!</h3>
<p><strong>短链接:</strong> ${result.data.short_url}</p>
<p><strong>原始URL:</strong> ${result.data.original_url}</p>
`;
} else {
resultDiv.innerHTML = `<p style="color:red;">错误: ${result.error}</p>`;
}
});
});
</script>
</body>
</html>
使用示例
创建带追踪参数的短链接
// 使用示例
$shortLinkService = new ShortLinkService($db);
// 创建带UTM参数的短链接
$result = $shortLinkService->createShortLink(
'https://example.com/product',
'产品推广链接',
null,
'2024-12-31 23:59:59',
[
'utm_source' => 'facebook',
'utm_medium' => 'social',
'utm_campaign' => 'summer_sale',
'utm_content' => 'banner_1',
'ref' => 'user123'
]
);
.htaccess 配置 (Apache)
RewriteEngine On RewriteRule ^([a-zA-Z0-9]+)$ redirect.php?code=$1 [L]
Nginx 配置
location / {
try_files $uri $uri/ /redirect.php?code=$1;
}
location ~ ^/([a-zA-Z0-9]+)$ {
rewrite ^/(.*)$ /redirect.php?code=$1 last;
}
- 短链接生成:安全随机生成,避免冲突
- 参数追踪:支持自定义参数(UTM等)
- 访问统计:IP、设备、地理位置、浏览器等
- 过期管理:支持链接过期时间
- 设备识别:自动识别桌面/移动/平板
- API接口:RESTful API便于集成
- 可视化统计:按国家、设备、日期等维度
这个系统可以轻松集成到现有的PHP项目中,支持高并发和大规模使用,你可以根据需求扩展更多功能,比如二维码生成、批量导入、A/B测试等。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播