Whois (RFC 3912) 為簡單 TCP 文字協定,客戶端連接伺服器 43 埠,發送查詢字串 (域名/IP/AS 號) + CRLF,伺服器回傳註冊資訊後斷線。無認證、無加密、無複雜指令。
<?php
/**
* Whois 查詢函式
* @param string $server Whois 伺服器 (如 whois.verisign-grs.com, whois.cnnic.cn)
* @param string $query 查詢字串 (域名、IP、AS號)
* @param int $timeout 連線逾時秒數
* @return string 原始回應文字
*/
function whois_query($server, $query, $timeout = 10) {
$fp = @fsockopen($server, 43, $errno, $errstr, $timeout);
if (!$fp) {
return "連線失敗 [$errno]: $errstr";
}
// 發送查詢 (必須以 \r\n 結尾)
fwrite($fp, $query . "\r\n");
// 讀取完整回應
$response = '';
stream_set_timeout($fp, $timeout);
while (!feof($fp)) {
$chunk = fread($fp, 4096);
if ($chunk === false) break;
$response .= $chunk;
// 檢查逾時
$info = stream_get_meta_data($fp);
if ($info['timed_out']) {
$response .= "\n[讀取逾時]";
break;
}
}
fclose($fp);
return $response;
}
/**
* 依域名後綴自動選擇 Whois 伺服器
*/
function get_whois_server($domain) {
$tld = strtolower(substr(strrchr($domain, '.'), 1));
$servers = [
'com' => 'whois.verisign-grs.com',
'net' => 'whois.verisign-grs.com',
'org' => 'whois.pir.org',
'info' => 'whois.afilias.net',
'biz' => 'whois.biz',
'cn' => 'whois.cnnic.cn',
'tw' => 'whois.twnic.net.tw',
'jp' => 'whois.jprs.jp',
'de' => 'whois.denic.de',
'eu' => 'whois.eu',
'io' => 'whois.nic.io',
'ai' => 'whois.nic.ai',
'app' => 'whois.nic.google',
'dev' => 'whois.nic.google',
// 更多參考: https://www.iana.org/whois
];
return $servers[$tld] ?? 'whois.iana.org'; // 根伺服器會轉介
}
// ===== 使用範例 =====
$domain = 'example.com';
$server = get_whois_server($domain);
echo "查詢 $domain @ $server ...\n\n";
$result = whois_query($server, $domain);
echo $result;
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>Whois 查詢</title>
<style>
body {font-family: monospace; max-width: 900px; margin: 2rem auto; padding: 0 1rem;}
form {margin-bottom: 1rem;} pre {background:#f5f5f5; padding:1rem; overflow:auto; white-space:pre-wrap;}
.server-select {margin-right: 1rem;}
</style></head><body>
<h1>🔍 Whois 域名查詢</h1>
<form method="GET">
<label class="server-select">
伺服器:
<select name="server">
<option value="auto" selected>自動判斷</option>
<option value="whois.verisign-grs.com">.com/.net (Verisign)</option>
<option value="whois.pir.org">.org (PIR)</option>
<option value="whois.cnnic.cn">.cn (CNNIC)</option>
<option value="whois.twnic.net.tw">.tw (TWNIC)</option>
<option value="whois.iana.org">IANA 根伺服器</option>
</select>
</label>
<label>
域名/IP: <input type="text" name="query" placeholder="example.com" required style="width:300px;">
</label>
<button type="submit">查詢</button>
</form>
<?php
if (isset($_GET['query']) && $_GET['query'] !== '') {
$query = trim($_GET['query']);
$server = $_GET['server'] === 'auto' ? get_whois_server($query) : $_GET['server'];
echo "<h2>結果: $query @ $server</h2>";
echo "<pre>";
echo htmlspecialchars(whois_query($server, $query), ENT_QUOTES, 'UTF-8');
echo "</pre>";
}
?>
</body></html>
<?php
function parse_whois($raw_text) {
$data = [];
$lines = explode("\n", $raw_text);
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '%' || $line[0] === '#') continue; // 註解/空行
if (preg_match('/^([^:]+):\s*(.+)$/', $line, $m)) {
$key = strtolower(trim($m[1]));
$val = trim($m[2]);
// 合併多行同欄位
if (isset($data[$key])) {
$data[$key] = is_array($data[$key]) ? $data[$key] : [$data[$key]];
$data[$key][] = $val;
} else {
$data[$key] = $val;
}
}
}
return $data;
}
$parsed = parse_whois($result);
echo "註冊商: " . ($parsed['registrar'] ?? 'N/A') . "\n";
echo "建立日期: " . ($parsed['creation date'] ?? $parsed['created'] ?? 'N/A') . "\n";
echo "到期日期: " . ($parsed['expiration date'] ?? $parsed['expires'] ?? 'N/A') . "\n";
echo "名稱伺服器: " . implode(', ', (array)($parsed['name server'] ?? [])) . "\n";
| 問題 | 原因 | 解決方案 |
|---|---|---|
| 連線逾時/拒絕 | 伺服器限制頻率、IP 被封、防火牆阻擋 43 埠 | 加入延遲、使用 Proxy、輪換多個 Whois 伺服器、快取結果 |
| 回應編碼亂碼 | 部分國別伺服器回傳非 UTF-8 (如 Big5、EUC-KR) | mb_detect_encoding() + mb_convert_encoding() |
| 資料不完整/格式異常 | 各註冊商/註冊局格式差異極大,無統一標準 | 針對主要 TLD 寫專用解析器,或使用成熟庫 |
| 隱私保護/代理註冊 | GDPR、隱私保護服務遮蔽持有者資訊 | 僅能取得註冊商、NS、建立/到期日等公開欄位 |
# Composer 安裝
composer require phpwhois/phpwhois
# 使用
use PhpWhois\Whois;
$whois = new Whois();
$result = $whois->lookup('example.com', false); // false = 不快取
print_r($result); // 結構化陣列,含 parsed 欄位
phpwhois/phpwhois:支援 300+ TLD、自動選擇伺服器、結構化解析、快取。webmozart/whois:現代 PSR 標準、型別安全、支援 RDAP (JSON 格式新標準)。Whois 協定極簡單:TCP 43 埠 → 送查詢 + CRLF → 讀回應 → 關閉。PHP 用 fsockopen() 幾行即可實作。但實務上伺服器分散、格式混亂、頻率限制、隱私遮蔽使得自行維護解析器成本極高。建議:簡單需求用現成庫 (phpwhois/webmozart/whois),複雜需求接入註冊商 API 或 RDAP 服務。