Files
CLOAK/tools/verify_ad_source_guard.php
2026-06-27 23:58:54 +08:00

136 lines
5.4 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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,
];
// 优先级 1ad_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');
// 优先级 2clid
$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);
// 优先级 3referer
$r = CloakAdSourceGuard::evaluate($baseCfg, [
'referer' => 'https://www.facebook.com/ads', 'query' => [], 'user_agent' => '',
]);
vassert('referer facebook 放行', $r['passed'] && strpos($r['matched_by'], 'referer:') === 0);
// 优先级 4UA
$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);
vassert('单平台含 ad_spm_id', strpos($q, 'ad_spm_id=' . rawurlencode($spmId)) !== false);
$adjustFp = 'https://aindl.com/v305-8/?p0=1ynqxx65&p1=%7B%7Bcampaign.name%7D%7D&p2=%7B%7Bcampaign.id%7D%7D&p3=%7B%7Badset.name%7D%7D&p4=%7B%7Badset.id%7D%7D&p5=%7B%7Bad.name%7D%7D&p6=%7B%7Bad.id%7D%7D';
$qAdjust = CloakAdSourceGuard::buildAdLinkQuery([CloakAdSourceGuard::PLATFORM_FB], $spmId, [$adjustFp]);
vassert('Adjust DB_FP 追加 p1 宏', strpos($qAdjust, 'p1={{campaign.name}}') !== false, $qAdjust);
vassert('Adjust DB_FP 追加 p6 宏', strpos($qAdjust, 'p6={{ad.id}}') !== false, $qAdjust);
vassert('Adjust 广告链接仍含 UTM', strpos($qAdjust, 'utm_source=facebook') !== 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);