PHP 怎么PHP Beacon API

wen PHP项目 1

本文目录导读:

PHP 怎么PHP Beacon API

  1. 什么是 Beacon API
  2. PHP 端接收 Beacon 请求
  3. 前端 JavaScript 发送 Beacon
  4. 常见用例与最佳实践
  5. 注意事项
  6. 完整示例

PHP 中实现 Beacon API 主要用于在页面卸载(关闭、刷新、跳转)时,向服务器发送少量的分析数据、日志或事件,且不关心服务器响应。

什么是 Beacon API

Beacon API(navigator.sendBeacon())是浏览器原生提供的异步、非阻塞请求方法,特点是:

  • 异步且不阻塞:即使在 unloadbeforeunload 事件中调用,也能确保请求发送
  • 不关心响应:自动忽略服务器返回内容
  • 通常使用 POST 方法:数据以 text/plainapplication/x-www-form-urlencodedBlob 格式发送
  • 可靠性高:即使页面关闭,浏览器仍会尽力发送请求

PHP 端接收 Beacon 请求

PHP 端处理 Beacon 请求与处理普通 POST 请求基本一致,主要根据 Content-Type 读取请求体数据。

1 简单接收示例

<?php
// 获取请求体内容
$rawData = file_get_contents('php://input');
// 根据 Content-Type 解析数据
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
$data = [];
if (strpos($contentType, 'application/json') !== false) {
    $data = json_decode($rawData, true) ?: [];
} elseif (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
    parse_str($rawData, $data);
} else {
    // text/plain 或其他格式 - 直接接收字符串
    $data = ['message' => $rawData];
}
// 处理数据(记录日志、存储统计等)
file_put_contents(
    'beacon_log.txt',
    date('Y-m-d H:i:s') . ' | ' . json_encode($data) . PHP_EOL,
    FILE_APPEND
);
// Beacon 不关心响应,但通常返回空 200 响应
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['status' => 'received']);
?>

2 专用接收脚本 (beacon.php)

更完整的示例,包含请求源验证:

<?php
header('Content-Type: application/json');
http_response_code(200);
// 获取原始请求数据
$input = file_get_contents('php://input');
if (empty($input)) {
    echo json_encode(['status' => 'empty']);
    exit;
}
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
$payload = [];
if (strpos($contentType, 'application/json') !== false) {
    $payload = json_decode($input, true) ?? [];
} elseif (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
    parse_str($input, $payload);
} else {
    // 假设是键值对或纯文本
    $payload = ['raw' => $input];
}
// 添加服务器时间戳和 IP
$payload['_received_at'] = date('Y-m-d H:i:s');
$payload['_client_ip'] = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
// 写入日志(生产环境建议使用数据库或消息队列)
$logEntry = json_encode($payload) . PHP_EOL;
file_put_contents('/var/log/beacon/events.log', $logEntry, FILE_APPEND | LOCK_EX);
// 可选:返回一些状态(通常不被使用)
echo json_encode(['status' => 'logged']);
?>

前端 JavaScript 发送 Beacon

1 基本用法

// 发送简单的键值对数据
const data = {
    action: 'page_exit',
    timeSpent: 120,
    page: '/about'
};
navigator.sendBeacon('/beacon.php', JSON.stringify(data));

2 在页面卸载时发送

// 页面卸载时可靠地发送数据
window.addEventListener('beforeunload', function() {
    navigator.sendBeacon('/beacon.php', JSON.stringify({
        event: 'page_unload',
        timestamp: Date.now()
    }));
});
// 对移动设备的页面隐藏事件也做处理
document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'hidden') {
        navigator.sendBeacon('/beacon.php', JSON.stringify({
            event: 'page_hidden',
            timestamp: Date.now()
        }));
    }
});

3 发送不同格式的数据

// Blob 格式(推荐用于复杂数据)
const blob = new Blob([JSON.stringify({key: 'value'})], {type: 'application/json'});
navigator.sendBeacon('/beacon.php', blob);
// FormData 格式
const formData = new FormData();
formData.append('action', 'click');
formData.append('element', 'button_save');
navigator.sendBeacon('/beacon.php', formData);
// URL 编码格式
const urlEncoded = new URLSearchParams({event: 'scroll', depth: '80%'});
navigator.sendBeacon('/beacon.php', urlEncoded);

常见用例与最佳实践

1 用户行为跟踪

// 发送页面停留时间
let startTime = Date.now();
window.addEventListener('beforeunload', function() {
    let timeSpent = Math.floor((Date.now() - startTime) / 1000);
    navigator.sendBeacon('/beacon.php', JSON.stringify({
        event: 'page_exit',
        page: window.location.pathname,
        duration: timeSpent,
        referrer: document.referrer
    }));
});

2 错误日志上报

window.addEventListener('error', function(e) {
    navigator.sendBeacon('/beacon.php', JSON.stringify({
        event: 'js_error',
        message: e.message,
        source: e.filename,
        line: e.lineno,
        column: e.colno
    }));
});

3 服务端优化建议

// beacon_handler.php - 优化版
// 1. 不处理大文件,设置接收限制
$maxSize = 1024 * 10; // 10KB
$input = file_get_contents('php://input', false, null, 0, $maxSize);
// 2. 使用快速写入,避免阻塞
$fd = fopen('/tmp/beacon_queue.log', 'a');
if ($fd) {
    fwrite($fd, $input . PHP_EOL);
    fclose($fd);
}
// 3. 返回 204 No Content(更快)
http_response_code(204);
exit;

注意事项

  1. 大小限制:Beacon 数据通常限制在 64KB 左右,过大数据可能失败
  2. 跨域问题:不同域需要 CORS 支持
  3. 服务器响应:客户端不关心响应,但服务器应尽快返回(推荐 204)
  4. 并发请求:少量 Beacon 请求,不适合批量大数据
  5. 请求顺序:Beacon 不保证请求顺序

完整示例

前端 (track.js)

// 页面加载时开始计时
const pageData = {
    url: window.location.href,
    referrer: document.referrer || '',
    userAgent: navigator.userAgent,
    language: navigator.language,
    screenSize: `${window.screen.width}x${window.screen.height}`,
    timestamp: Date.now()
};
// 发送页面加载事件
navigator.sendBeacon('/beacon.php', JSON.stringify({
    event: 'page_load',
    ...pageData
}));
// 页面卸载时发送
window.addEventListener('beforeunload', function() {
    navigator.sendBeacon('/beacon.php', JSON.stringify({
        event: 'page_unload',
        url: window.location.href,
        timestamp: Date.now(),
        duration: Date.now() - pageData.timestamp
    }));
});

后端 (beacon.php)

<?php
// 快速响应,不关心客户端
http_response_code(204);
$input = file_get_contents('php://input');
if (empty($input)) exit;
$data = json_decode($input, true);
if (!is_array($data)) exit;
// 简单验证
$allowedEvents = ['page_load', 'page_unload', 'click', 'scroll'];
if (!in_array($data['event'] ?? '', $allowedEvents)) {
    exit;
}
// 记录到日志
$log = date('c') . ' | ' . json_encode($data) . PHP_EOL;
file_put_contents('/path/to/tracking.log', $log, FILE_APPEND | LOCK_EX);
?>

PHP 处理 Beacon API 的核心就是:

  1. 接收 POST 请求
  2. 读取 php://input 原始数据
  3. 根据 Content-Type 解析
  4. 尽快返回响应(204/200)
  5. 异步处理数据(日志、队列、数据库)

Beacon 特别适合于页面卸载、错误上报、轻量级用户行为跟踪等场景。

抱歉,评论功能暂时关闭!