螢幕解析度、視口大小、裝置像素比等資訊**只存在於瀏覽器端**,必須透過 JavaScript 取得,再以 AJAX、Cookie、Session、URL 參數等方式傳給 PHP。
<!-- 頁面 head 內嵌 -->
<script>
(function() {
function reportScreen() {
var data = {
screen_width: window.screen.width,
screen_height: window.screen.height,
avail_width: window.screen.availWidth,
avail_height: window.screen.availHeight,
device_pixel_ratio: window.devicePixelRatio || 1,
viewport_width: document.documentElement.clientWidth,
viewport_height: document.documentElement.clientHeight,
color_depth: window.screen.colorDepth,
orientation: window.screen.orientation?.type || 'unknown'
};
// 發送至後端 API
fetch('/api/screen-report', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data),
credentials: 'include', // 攜帶 Cookie/Session
keepalive: true // 頁面卸載時仍嘗試送出
}).catch(console.error);
}
// 頁面載入完成後上報
if (document.readyState === 'complete') {
reportScreen();
} else {
window.addEventListener('load', reportScreen);
}
// 視口變化時可選上報 (resize/orientationchange)
var resizeTimer;
window.addEventListener('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(reportScreen, 500);
});
})();
</script>
<?php
// /api/screen-report.php
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
if (!$input) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
// 驗證必要欄位
$required = ['screen_width', 'screen_height', 'viewport_width', 'viewport_height'];
foreach ($required as $field) {
if (!isset($input[$field]) || !is_numeric($input[$field])) {
http_response_code(400);
echo json_encode(['error' => "Missing field: $field"]);
exit;
}
}
// 關聯用戶/Session
$sessionId = session_id();
if (!$sessionId) session_start();
$userId = $_SESSION['user_id'] ?? null;
// 儲存資料庫 (範例)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '');
$stmt = $pdo->prepare('
INSERT INTO client_screen_info (session_id, user_id, screen_width, screen_height, avail_width, avail_height, dpr, viewport_width, viewport_height, color_depth, orientation, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
screen_width=VALUES(screen_width), screen_height=VALUES(screen_height),
viewport_width=VALUES(viewport_width), viewport_height=VALUES(viewport_height),
updated_at=NOW()
');
$stmt->execute([
$sessionId, $userId,
$input['screen_width'], $input['screen_height'],
$input['avail_width'] ?? 0, $input['avail_height'] ?? 0,
$input['device_pixel_ratio'] ?? 1,
$input['viewport_width'], $input['viewport_height'],
$input['color_depth'] ?? 24,
$input['orientation'] ?? 'unknown'
]);
echo json_encode(['success' => true]);
?>
<script>
// 寫入 Cookie (前端)
document.cookie = 'screen_info=' + encodeURIComponent(JSON.stringify({
w: screen.width,
h: screen.height,
vw: document.documentElement.clientWidth,
vh: document.documentElement.clientHeight,
dpr: devicePixelRatio || 1
})) + '; path=/; max-age=2592000; SameSite=Lax'; // 30 天
</script>
<?php
// 後端讀取 Cookie
if (isset($_COOKIE['screen_info'])) {
$screen = json_decode(urldecode($_COOKIE['screen_info']), true);
if ($screen) {
// 直接使用 $screen['w'], $screen['h']...
// 例:判斷是否行動裝置
$isMobile = ($screen['w'] ?? 1920) < 768;
}
}
?>
<script>
// 首頁導向帶參數 (僅示範,實際會造成重導向迴圈需加判斷)
if (!new URLSearchParams(window.location.search).has('scr')) {
var scr = [screen.width, screen.height, devicePixelRatio || 1].join('x');
window.location.href = window.location.pathname + '?scr=' + scr + '&' + window.location.search.slice(1);
}
</script>
<?php
// 後端接收
if (isset($_GET['scr'])) {
[$w, $h, $dpr] = array_pad(explode('x', $_GET['scr']), 3, [1920, 1080, 1]);
// 立即可用於伺服器端渲染決策
}
?>
CREATE TABLE client_screen_info (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
session_id VARCHAR(64) NOT NULL,
user_id BIGINT UNSIGNED NULL,
screen_width SMALLINT UNSIGNED NOT NULL,
screen_height SMALLINT UNSIGNED NOT NULL,
avail_width SMALLINT UNSIGNED DEFAULT 0,
avail_height SMALLINT UNSIGNED DEFAULT 0,
dpr DECIMAL(3,2) DEFAULT 1.00,
viewport_width SMALLINT UNSIGNED NOT NULL,
viewport_height SMALLINT UNSIGNED NOT NULL,
color_depth TINYINT UNSIGNED DEFAULT 24,
orientation ENUM('portrait-primary','landscape-primary','portrait-secondary','landscape-secondary','unknown') DEFAULT 'unknown',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_session (session_id),
KEY idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
| 場景 | 用途 | 實作建議 |
|---|---|---|
| 響應式圖片尺寸 | 依螢幕寬度輸出適當 srcset | SSR 時用 Cookie/URL 參數;CSR 用 JS 直接設定 |
| 行動版/桌面版佈局切換 | PHP 直接輸出不同模板 | 首次請求用 URL 參數或 Session 快取 |
| 廣告位尺寸決定 | 依視口大小請求對應廣告 | AJAX 上報後快取至 Session/Redis |
| 使用者體驗分析 | 統計解析度分布、DPR 分布 | 批次寫入資料庫,定期匯總 |
| Canvas/WebGL 畫布大小 | 乘以 DPR 避免模糊 | 前端直接用,無需 PHP |
Do Not Track` 或自訂選項讓用戶停用上報。PHP 取得螢幕解析度**必須仰賴前端 JS**,推薦 **AJAX 非同步上報 + 後端快取 (Session/Redis/DB)** 架構,兼顧即時性、效能與隱私合規。避免同步阻塞導向,善用 `fetch(keepalive)` 確保資料送達。