130 lines
4.8 KiB
PHP
Executable File
130 lines
4.8 KiB
PHP
Executable File
#!/usr/bin/env php
|
||
<?php
|
||
/**
|
||
* 广告来源限制 Guard 单元验证
|
||
*
|
||
* 用法:php tools/verify_ad_source_guard.php
|
||
*/
|
||
if (PHP_SAPI !== 'cli') {
|
||
fwrite(STDERR, "请在 CLI 下运行\n");
|
||
exit(1);
|
||
}
|
||
|
||
require_once dirname(__DIR__) . '/lib/CloakAdSourceGuard.php';
|
||
|
||
$passed = 0;
|
||
$failed = 0;
|
||
|
||
function vassert($name, $cond, $detail = '')
|
||
{
|
||
global $passed, $failed;
|
||
if ($cond) {
|
||
$passed++;
|
||
return;
|
||
}
|
||
$failed++;
|
||
echo "FAIL: {$name}\n";
|
||
if ($detail !== '') {
|
||
echo " {$detail}\n";
|
||
}
|
||
}
|
||
|
||
$spmId = 'Ab12Cd34Ef56';
|
||
$baseCfg = [
|
||
'ad_fb' => true,
|
||
'ad_google' => false,
|
||
'ad_tiktok' => false,
|
||
'ad_strict' => false,
|
||
'ad_spm_id' => $spmId,
|
||
];
|
||
|
||
// 优先级 1:ad_spm_id 匹配
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => ['ad_spm_id' => $spmId], 'user_agent' => '',
|
||
]);
|
||
vassert('spm_id 匹配放行', $r['passed'] && $r['matched_by'] === 'ad_spm_id');
|
||
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => ['ad_spm_id' => 'wrongId12345'], 'user_agent' => '',
|
||
]);
|
||
vassert('spm_id 不匹配不单独放行', !$r['passed'] || $r['matched_by'] !== 'ad_spm_id');
|
||
|
||
// 优先级 2:clid
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => ['fbclid' => 'IwAR1234567890123456789012'], 'user_agent' => '',
|
||
]);
|
||
vassert('fbclid 放行', $r['passed'] && strpos($r['matched_by'], 'clid:') === 0);
|
||
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => ['utm_campaign' => '__CAMPAIGN_ID__'], 'user_agent' => '',
|
||
]);
|
||
vassert('TikTok 宏放行', $r['passed'] && strpos($r['matched_by'], 'tiktok_macro:') === 0);
|
||
|
||
// 优先级 3:referer
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => 'https://www.facebook.com/ads', 'query' => [], 'user_agent' => '',
|
||
]);
|
||
vassert('referer facebook 放行', $r['passed'] && strpos($r['matched_by'], 'referer:') === 0);
|
||
|
||
// 优先级 4:UA
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 TikTok 26.0',
|
||
]);
|
||
vassert('UA TikTok 放行', $r['passed'] && strpos($r['matched_by'], 'ua:') === 0);
|
||
|
||
// 严格模式拦截
|
||
$strictCfg = array_merge($baseCfg, ['ad_strict' => true]);
|
||
$r = CloakAdSourceGuard::evaluate($strictCfg, [
|
||
'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
|
||
]);
|
||
vassert('严格模式未命中拦截', !$r['passed'] && $r['reason'] === '来源限制:未命中广告来源验证');
|
||
|
||
// 非严格模式
|
||
$r = CloakAdSourceGuard::evaluate($baseCfg, [
|
||
'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
|
||
]);
|
||
vassert('非严格未命中', !$r['passed'] && $r['reason'] === '未确定流量来源');
|
||
|
||
// guard 未启用
|
||
$r = CloakAdSourceGuard::evaluate([
|
||
'ad_fb' => false, 'ad_google' => false, 'ad_tiktok' => false,
|
||
'ad_strict' => true, 'ad_spm_id' => $spmId,
|
||
], ['referer' => '', 'query' => [], 'user_agent' => '']);
|
||
vassert('无平台开启跳过', $r['passed'] && $r['matched_by'] === 'guard_disabled');
|
||
|
||
// 链接拼装:单平台
|
||
$q = CloakAdSourceGuard::buildAdLinkQuery([CloakAdSourceGuard::PLATFORM_FB], $spmId);
|
||
vassert('单平台 FB 含完整 UTM', strpos($q, 'utm_source=facebook') !== false && strpos($q, 'campaign_id={{campaign.id}}') !== false);
|
||
vassert('单平台含 ad_spm_id', strpos($q, 'ad_spm_id=' . rawurlencode($spmId)) !== false);
|
||
|
||
// 链接拼装:多平台
|
||
$q = CloakAdSourceGuard::buildAdLinkQuery([
|
||
CloakAdSourceGuard::PLATFORM_FB,
|
||
CloakAdSourceGuard::PLATFORM_GOOGLE,
|
||
CloakAdSourceGuard::PLATFORM_TIKTOK,
|
||
], $spmId);
|
||
vassert('多平台含 FB 代表参数', strpos($q, 'campaign_id={{campaign.id}}') !== false);
|
||
vassert('多平台含 Google 代表参数', strpos($q, 'utm_campaign={campaignid}') !== false);
|
||
vassert('多平台含 TikTok 代表参数', strpos($q, 'adgroup_id=__CID__') !== false);
|
||
vassert('多平台不含完整 FB UTM', strpos($q, 'utm_medium=paid_social') === false);
|
||
|
||
// spm 生成
|
||
$id = CloakAdSourceGuard::generateSpmId();
|
||
vassert('generateSpmId 合法', CloakAdSourceGuard::isValidSpmId($id), $id);
|
||
|
||
// 临时链接计时触发
|
||
vassert('计时:referer 命中广告来源', CloakAdSourceGuard::shouldStartUrlArgsTimer($baseCfg, [
|
||
'referer' => 'https://www.facebook.com/', 'query' => [], 'user_agent' => '',
|
||
]));
|
||
vassert('计时:未命中不启动', !CloakAdSourceGuard::shouldStartUrlArgsTimer($baseCfg, [
|
||
'referer' => '', 'query' => [], 'user_agent' => 'Mozilla/5.0 Chrome',
|
||
]));
|
||
vassert('计时:guard 关闭时 clid 仍可触发', CloakAdSourceGuard::shouldStartUrlArgsTimer([
|
||
'ad_fb' => false, 'ad_google' => false, 'ad_tiktok' => false,
|
||
'ad_strict' => false, 'ad_spm_id' => '',
|
||
], ['referer' => '', 'query' => ['gclid' => 'abc'], 'user_agent' => '']));
|
||
|
||
$result = ['ok' => $failed === 0, 'passed' => $passed, 'failed' => $failed];
|
||
echo json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
|
||
exit($failed > 0 ? 1 : 0);
|