域名查詢是建站平台、主機商常見需求。本文示範如何構建完整流程:前端 Checkbox 多後綴選擇 → PHP 組合域名 → 萬網 API 查詢 → XML 解析 → 結果展示,並補充錯誤處理、快取、併發優化等生產級細節。
<form method="POST" action="domain_check.php">
<div class="input-group">
<span class="prefix">www.</span>
<input type="text" name="domain" id="domain" placeholder="輸入域名主體" required>
</div>
<fieldset>
<legend>選擇要查詢的後綴</legend>
<div class="tld-grid">
<label><input type="checkbox" name="tlds[]" value="com" checked> .com</label>
<label><input type="checkbox" name="tlds[]" value="cn" checked> .cn</label>
<label><input type="checkbox" name="tlds[]" value="net" checked> .net</label>
<label><input type="checkbox" name="tlds[]" value="org"> .org</label>
<label><input type="checkbox" name="tlds[]" value="com.cn"> .com.cn</label>
<label><input type="checkbox" name="tlds[]" value="net.cn"> .net.cn</label>
<label><input type="checkbox" name="tlds[]" value="org.cn"> .org.cn</label>
<label><input type="checkbox" name="tlds[]" value="xyz"> .xyz</label>
<label><input type="checkbox" name="tlds[]" value="top"> .top</label>
<label><input type="checkbox" name="tlds[]" value="shop"> .shop</label>
<!-- 更多後綴... -->
</div>
</fieldset>
<button type="submit">批量查詢</button>
</form>
<style>
.tld-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; }
.tld-grid label { display: flex; align-items: center; gap: 4px; font-size: 14px; cursor: pointer; }
</style>
<?php
declare(strict_types=1);
header('Content-Type: text/html; charset=utf-8');
// 1. 取得並驗證輸入
$domainBody = trim($_POST['domain'] ?? '');
$tlds = $_POST['tlds'] ?? [];
if ($domainBody === '') {
exit('請輸入域名主體');
}
if (empty($tlds)) {
exit('請至少選擇一個後綴');
}
// 基本格式驗證:只允許字母數字連字號,不以連字號開頭結尾
if (!preg_match('/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i', $domainBody)) {
exit('域名主體格式不正確(僅限字母、數字、連字號,長度 1-63)');
}
// 2. 萬網 API 端點(注意:此為舊版公開接口,正式專案請用阿里云/GoDaddy 等官方 API)
$apiBase = 'https://panda.www.net.cn/cgi-bin/check.cgi';
// 3. 併發查詢(使用 curl_multi 大幅提速)
$multiHandle = curl_multi_init();
$handles = [];
$results = [];
foreach ($tlds as $tld) {
$fullDomain = $domainBody . '.' . $tld;
$url = $apiBase . '?area_domain=' . urlencode($fullDomain);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false, // 舊 HTTP API 無 HTTPS
CURLOPT_USERAGENT => 'DomainChecker/1.0',
]);
curl_multi_add_handle($multiHandle, $ch);
$handles[$fullDomain] = $ch;
}
// 執行併發
$running = null;
do {
curl_multi_exec($multiHandle, $running);
curl_multi_select($multiHandle, 0.5);
} while ($running > 0);
// 4. 收集與解析結果
foreach ($handles as $domain => $ch) {
$xmlString = curl_multi_getcontent($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_multi_remove_handle($multiHandle, $ch);
curl_close($ch);
if ($httpCode !== 200 || empty($xmlString)) {
$results[$domain] = ['status' => 'error', 'msg' => "HTTP $httpCode"];
continue;
}
// 解析 XML:210 ...
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlString);
$errors = libxml_get_errors();
libxml_clear_errors();
if ($xml === false) {
$results[$domain] = ['status' => 'error', 'msg' => 'XML 解析失敗'];
continue;
}
// 取得 original 字串,前 3 碼為狀態碼
$original = (string)($xml->original ?? '');
$code = substr($original, 0, 3);
$msgMap = [
'210' => '可註冊',
'211' => '已被註冊',
'212' => '參數錯誤',
'213' => '查詢超時',
];
$results[$domain] = [
'status' => isset($msgMap[$code]) ? 'success' : 'unknown',
'code' => $code,
'msg' => $msgMap[$code] ?? "未知狀態碼: $code",
];
}
curl_multi_close($multiHandle);
// 5. 輸出結果(可改為 JSON 供前端 AJAX 渲染)
echo '<table class="result-table"><thead><tr><th>域名</th><th>狀態</th><th>代碼</th></tr></thead><tbody>';
foreach ($results as $domain => $res) {
$badge = match($res['status']) {
'success' => $res['code'] === '210' ? 'success' : 'danger',
default => 'secondary',
};
echo "<tr><td>$domain</td><td><span class='badge badge-$badge'>{$res['msg']}</span></td><td>{$res['code']}</td></tr>";
}
echo '</tbody></table>';
| 面向 | 問題 | 方案 |
|---|---|---|
| API 可靠性 | 萬網舊接口無 SLA、無 HTTPS、頻率限制不明 | 改用阿里云 Domain API、GoDaddy API、Namecheap API 等官方介面,支援 HTTPS、簽名驗證、配額管理 |
| 效能 | 循序查詢 20 個後綴需 10-20 秒 | curl_multi 併發(上例)、或前端輪詢/分批、或後端隊列異步 + WebSocket 推送結果 |
| 快取 | 重複查詢浪費配額 | Redis 快取結果 1-24 小時,Key: domain:check:{domain} |
| 限流 | 用戶瘋狂點擊導致 IP 被封 | IP + 用戶雙維度限流(如 10 req/min),rate_limit 中介軟體 |
| 安全 | 域名參數注入、XSS | 嚴格驗證 preg_match('/^[a-z0-9-]+$/i'),輸出時 htmlspecialchars() |
| 國際化 | IDN 國際化域名(中文、日文等) | 用 idn_to_ascii() 轉 Punycode (xn--...) 後再查詢 |
// 服務層介面
interface DomainCheckerInterface {
public function check(string $domain): DomainStatus;
public function checkBatch(array $domains): iterable;
}
// 阿里云實作
class AliyunDomainChecker implements DomainCheckerInterface {
private Client $client; // 阿里云 SDK Client
public function check(string $domain): DomainStatus { /* ... */ }
public function checkBatch(array $domains): iterable { /* 批量介面 */ }
}
// 控制器
class DomainController {
public function __construct(private DomainCheckerInterface $checker) {}
public function checkAction(Request $request): JsonResponse {
$body = $request->get('domain');
$tlds = $request->get('tlds', ['com','net','cn']);
$domains = array_map(fn($tld) => "$body.$tld", $tlds);
$results = iterator_to_array($this->checker->checkBatch($domains));
return new JsonResponse($results);
}
}
從簡單的 file_get_contents 到 curl_multi 併發、再到服務層抽象與官方 SDK 整合,域名查詢功能的演進反映了 PHP 開發從「湊合跑通」到「工程化、可維運」的成長路徑。