本文目录导读:

我来详细说明如何用PHP项目实现多变量测试(Multivariate Testing,MVT)。
多变量测试的基本概念
多变量测试是同时测试多个页面元素的多个变体,以找到最佳组合的测试方法。
基础实现方案
1 定义测试变量和变体
<?php
// config/mvt-config.php
// 定义测试配置
$mvtConfig = [
'homepage_hero' => [
'enabled' => true,
'name' => '首页横幅测试',
'metrics' => ['click_rate', 'conversion_rate'],
'variables' => [
'headline' => [
'variants' => [
'A' => 'Welcome to Our Platform',
'B' => 'Transform Your Business Today',
'C' => 'Get Started for Free'
],
'weight' => [33, 33, 34] // 权重分配
],
'button_color' => [
'variants' => [
'A' => 'blue',
'B' => 'green',
'C' => 'red'
],
'weight' => [33, 33, 34]
],
'cta_text' => [
'variants' => [
'A' => 'Sign Up Now',
'B' => 'Get Started',
'C' => 'Try Free'
],
'weight' => [33, 33, 34]
]
]
]
];
2 核心引擎实现
<?php
// src/MVTEngine.php
class MVTEngine {
private $config;
private $db;
private $userId;
private $variants = [];
public function __construct($config, $db) {
$this->config = $config;
$this->db = $db;
$this->userId = $this->getUserId();
}
/**
* 获取用户分配的实验版本
*/
public function assignVariants($testName) {
if (!isset($this->config[$testName]) || !$this->config[$testName]['enabled']) {
return $this->getDefaultVariants($testName);
}
// 检查用户是否已有分配
$existing = $this->getExistingAssignment($testName);
if ($existing) {
return json_decode($existing['variants'], true);
}
// 为每个变量分配变体
$test = $this->config[$testName];
$assignedVariants = [];
foreach ($test['variables'] as $varName => $variable) {
$assignedVariants[$varName] = $this->getWeightedVariant(
$variable['variants'],
$variable['weight'],
$testName . '_' . $varName
);
}
// 保存分配
$this->saveAssignment($testName, $assignedVariants);
return $assignedVariants;
}
/**
* 加权随机选择变体
*/
private function getWeightedVariant($variants, $weights, $seed) {
$variantKeys = array_keys($variants);
$totalWeight = array_sum($weights);
$random = mt_rand(1, $totalWeight);
$cumulativeWeight = 0;
foreach ($variantKeys as $index => $key) {
$cumulativeWeight += $weights[$index];
if ($random <= $cumulativeWeight) {
return $key;
}
}
return $variantKeys[0];
}
/**
* 获取用户ID
*/
private function getUserId() {
// 使用session或cookie中的用户标识
if (!isset($_SESSION['user_id'])) {
$_SESSION['user_id'] = uniqid('user_', true);
}
return $_SESSION['user_id'];
}
/**
* 保存测试分配
*/
private function saveAssignment($testName, $variants) {
$sql = "INSERT INTO mvt_assignments (user_id, test_name, variants, assigned_at)
VALUES (?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
$stmt->execute([
$this->userId,
$testName,
json_encode($variants)
]);
}
/**
* 获取已存在的分配
*/
private function getExistingAssignment($testName) {
$sql = "SELECT variants FROM mvt_assignments
WHERE user_id = ? AND test_name = ?
ORDER BY assigned_at DESC LIMIT 1";
$stmt = $this->db->prepare($sql);
$stmt->execute([$this->userId, $testName]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* 获取默认变体
*/
private function getDefaultVariants($testName) {
$defaults = [];
foreach ($this->config[$testName]['variables'] as $varName => $variable) {
$defaults[$varName] = array_key_first($variable['variants']);
}
return $defaults;
}
}
3 数据追踪类
<?php
// src/MVTTracker.php
class MVTTracker {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 记录事件
*/
public function trackEvent($testName, $eventName, $userId, $variants) {
$sql = "INSERT INTO mvt_events (test_name, event_name, user_id, variants, created_at)
VALUES (?, ?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
$stmt->execute([
$testName,
$eventName,
$userId,
json_encode($variants)
]);
}
/**
* 记录转化
*/
public function trackConversion($testName, $userId, $variants, $conversionValue = null) {
$sql = "INSERT INTO mvt_conversions (test_name, user_id, variants, conversion_value, created_at)
VALUES (?, ?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
$stmt->execute([
$testName,
$userId,
json_encode($variants),
$conversionValue
]);
}
}
4 前端集成示例
<?php
// templates/homepage.php
// 初始化MVT引擎
$mvtEngine = new MVTEngine($mvtConfig, $db);
$variants = $mvtEngine->assignVariants('homepage_hero');
// 获取当前页面的变体内容
$headline = $mvtConfig['homepage_hero']['variables']['headline']['variants'][$variants['headline']];
$buttonColor = $mvtConfig['homepage_hero']['variables']['button_color']['variants'][$variants['button_color']];
$ctaText = $mvtConfig['homepage_hero']['variables']['cta_text']['variants'][$variants['cta_text']];
?>
<!DOCTYPE html>
<html>
<head>MVT Test Page</title>
<style>
.cta-button {
background-color: <?php echo htmlspecialchars($buttonColor); ?>;
color: white;
}
</style>
</head>
<body>
<div class="hero-section">
<h1><?php echo htmlspecialchars($headline); ?></h1>
<button class="cta-button" onclick="trackEvent('click')">
<?php echo htmlspecialchars($ctaText); ?>
</button>
</div>
<script>
// 追踪数据
const testData = {
testName: 'homepage_hero',
userId: '<?php echo $userId; ?>',
variants: <?php echo json_encode($variants); ?>
};
// 追踪页面浏览
fetch('/api/track.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
...testData,
event: 'page_view'
})
});
// 追踪按钮点击
function trackEvent(eventName) {
fetch('/api/track.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
...testData,
event: eventName
})
}).then(() => {
// 可以添加转化追踪逻辑
if (eventName === 'signup_complete') {
trackConversion();
}
});
}
function trackConversion() {
fetch('/api/conversion.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
...testData,
conversionValue: 100 // 转化价值
})
});
}
</script>
</body>
</html>
数据库表结构
-- 分配记录表
CREATE TABLE mvt_assignments (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
test_name VARCHAR(100) NOT NULL,
variants JSON NOT NULL,
assigned_at DATETIME NOT NULL,
INDEX idx_user_test (user_id, test_name)
);
-- 事件追踪表
CREATE TABLE mvt_events (
id INT AUTO_INCREMENT PRIMARY KEY,
test_name VARCHAR(100) NOT NULL,
event_name VARCHAR(100) NOT NULL,
user_id VARCHAR(255) NOT NULL,
variants JSON NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_test_event (test_name, event_name, created_at)
);
-- 转化记录表
CREATE TABLE mvt_conversions (
id INT AUTO_INCREMENT PRIMARY KEY,
test_name VARCHAR(100) NOT NULL,
user_id VARCHAR(255) NOT NULL,
variants JSON NOT NULL,
conversion_value DECIMAL(10,2) DEFAULT NULL,
created_at DATETIME NOT NULL,
INDEX idx_test_user (test_name, user_id)
);
结果分析类
<?php
// src/MVTResultsAnalyzer.php
class MVTResultsAnalyzer {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 获取测试结果统计
*/
public function getTestResults($testName) {
// 获取所有变体组合的统计数据
$sql = "SELECT
e.variants,
COUNT(DISTINCT e.user_id) as visitors,
COUNT(DISTINCT c.user_id) as conversions,
COUNT(c.user_id) / COUNT(DISTINCT e.user_id) * 100 as conversion_rate
FROM mvt_events e
LEFT JOIN mvt_conversions c
ON e.test_name = c.test_name
AND e.user_id = c.user_id
WHERE e.test_name = ?
AND e.event_name = 'page_view'
GROUP BY e.variants
ORDER BY conversion_rate DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute([$testName]);
$results = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$variants = json_decode($row['variants'], true);
$results[] = [
'variants' => $variants,
'visitors' => $row['visitors'],
'conversions' => $row['conversions'],
'conversion_rate' => round($row['conversion_rate'], 2)
];
}
return $results;
}
/**
* 获取每个变量的表现
*/
public function getVariablePerformance($testName) {
$config = $this->getTestConfig($testName);
$variablePerformance = [];
foreach ($config['variables'] as $varName => $variable) {
$performance = [];
foreach ($variable['variants'] as $variantKey => $variantValue) {
$sql = "SELECT
COUNT(DISTINCT e.user_id) as visitors,
COUNT(DISTINCT c.user_id) as conversions
FROM mvt_events e
LEFT JOIN mvt_conversions c
ON e.test_name = c.test_name
AND e.user_id = c.user_id
WHERE e.test_name = ?
AND JSON_EXTRACT(e.variants, '$.\"$varName\"') = ?
AND e.event_name = 'page_view'";
$stmt = $this->db->prepare($sql);
$stmt->execute([$testName, $variantKey]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
$performance[$variantKey] = [
'value' => $variantValue,
'visitors' => $data['visitors'],
'conversions' => $data['conversions'],
'conversion_rate' => $data['visitors'] > 0 ?
round($data['conversions'] / $data['visitors'] * 100, 2) : 0
];
}
$variablePerformance[$varName] = $performance;
}
return $variablePerformance;
}
/**
* 统计显著性检查(简化版)
*/
public function calculateSignificance($testName) {
$results = $this->getTestResults($testName);
if (count($results) < 2) {
return null;
}
// 以第一个结果为基准
$baseline = $results[0];
$significance = [];
for ($i = 1; $i < count($results); $i++) {
$variant = $results[$i];
$zScore = $this->calculateZScore(
$baseline['conversion_rate'] / 100,
$variant['conversion_rate'] / 100,
$baseline['visitors'],
$variant['visitors']
);
$significance[$i] = [
'variants' => $variant['variants'],
'z_score' => round($zScore, 2),
'p_value' => $this->calculatePValue($zScore)
];
}
return $significance;
}
private function calculateZScore($p1, $p2, $n1, $n2) {
$p = ($p1 * $n1 + $p2 * $n2) / ($n1 + $n2);
$se = sqrt($p * (1 - $p) * (1/$n1 + 1/$n2));
return ($p1 - $p2) / $se;
}
private function calculatePValue($zScore) {
// 简化实现,实际应用应使用更精确的算法
return 2 * (1 - 0.5 * (1 + erf($zScore / sqrt(2))));
}
}
高级优化建议
1 缓存机制
<?php
// 使用Redis缓存用户分配
class MVTEngineWithCache extends MVTEngine {
private $redis;
public function assignVariants($testName) {
$cacheKey = "mvt:{$this->userId}:{$testName}";
// 检查缓存
$cached = $this->redis->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
// 调用父类方法
$variants = parent::assignVariants($testName);
// 设置缓存(24小时过期)
$this->redis->setex($cacheKey, 86400, json_encode($variants));
return $variants;
}
}
2 流量分配控制
<?php
// 使用MurmurHash实现一致性分配
function consistentAssignment($userId, $testName, $totalVariants) {
$hash = hash('murmur3f', $userId . '_' . $testName);
// 将hash转换为整数
$intHash = hexdec(substr($hash, 0, 8));
return $intHash % $totalVariants;
}
3 A/B/n 测试支持
<?php
// 扩展以支持A/B/n测试(多变量测试的特例)
class ABnTestEngine extends MVTEngine {
public function assignVariant($testName) {
if ($this->getExistingAssignment($testName)) {
return parent::assignVariants($testName);
}
// 简化为单一变量测试
return parent::assignVariants($testName);
}
}
最佳实践
- 性能优化:使用缓存减少数据库查询
- 防止污染:确保用户始终看到相同的变体组合
- 数据质量:过滤机器人流量和无效数据
- 实时监控:监控测试效果,及时发现异常
- 渐进式部署:先小流量测试,逐步扩大
这个实现方案提供了完整的多变量测试功能,可以根据实际需求进行调整和扩展。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播