API 開發需統一回應格式,便於前端消費、錯誤處理、文件生成。本文設計 Response 類別,支援 JSON/XML 雙格式輸出、標準化碼/訊息/資料結構、巢狀資料 XML 序列化,並提供測試範例。
<?php
class Response {
/**
* 統一輸出入口
* @param int $code 狀態碼 (200=成功, 4xx=客戶端錯誤, 5xx=伺服器錯誤)
* @param string $message 提示訊息
* @param mixed $data 回傳資料 (陣列/物件/標量)
* @param string $format 輸出格式: 'json' | 'xml' | '' (自動判斷)
* @return string 格式化後字串
*/
public static function show($code, $message, $data = [], $format = '') {
if (!is_numeric($code)) return '';
$result = [
'code' => (int)$code,
'message' => (string)$message,
'data' => $data,
];
$format = strtolower($format);
if ($format === 'json') {
return self::toJson($result);
} elseif ($format === 'xml') {
return self::toXml($result);
}
// 自動判斷:依 Accept 標頭或預設 JSON
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
return (strpos($accept, 'xml') !== false) ? self::toXml($result) : self::toJson($result);
}
/**
* JSON 輸出
*/
public static function toJson($result) {
// 設定標頭
if (!headers_sent()) {
header('Content-Type: application/json; charset=utf-8');
}
// JSON_UNESCAPED_UNICODE: 中文不轉 \uXXXX
// JSON_UNESCAPED_SLASHES: / 不轉 \/
return json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
/**
* XML 輸出
*/
public static function toXml($result) {
if (!headers_sent()) {
header('Content-Type: application/xml; charset=utf-8');
}
$xml = "\n";
$xml .= "\n";
$xml .= self::arrayToXml($result);
$xml .= " ";
return $xml;
}
/**
* 陣列遞迴轉 XML
* @param array $data
* @param string $root 根節點名
* @return string
*/
protected static function arrayToXml($data, $root = 'item') {
$xml = '';
foreach ($data as $key => $value) {
// 數字鍵轉為 - ...
$attr = '';
$nodeName = $key;
if (is_numeric($key)) {
$attr = " id=\"$key\"";
$nodeName = $root;
}
// 節點名安全化:只保留字母數字底線
$nodeName = preg_replace('/[^a-zA-Z0-9_]/', '_', $nodeName);
if ($nodeName === '') $nodeName = $root;
$xml .= "<$nodeName$attr>";
if (is_array($value) || is_object($value)) {
$xml .= "\n" . self::arrayToXml((array)$value, $nodeName) . "\n";
} else {
// CDATA 保護特殊字元
$xml .= "";
}
$xml .= "$nodeName>\n";
}
return $xml;
}
}
?>
<?php
require_once 'Response.php';
// 模擬資料
$grade = [
"score" => [70, 95, 70.0, 60, "70"],
"name" => ["張三", "李四", "王五", "趙六", "天齊"],
"meta" => [
"total" => 5,
"avg" => 73.0,
"pass" => true
]
];
// 1. JSON 輸出
echo Response::show(200, 'success', $grade, 'json');
/* 輸出:
{
"code": 200,
"message": "success",
"data": {
"score": [70, 95, 70, 60, "70"],
"name": ["張三", "李四", "王五", "趙六", "天齊"],
"meta": {"total": 5, "avg": 73, "pass": true}
}
}
*/
// 2. XML 輸出
echo Response::show(200, 'success', $grade, 'xml');
/* 輸出:
200
...
...
*/
// 3. 錯誤回應
echo Response::show(404, 'User not found', null, 'json');
// {"code":404,"message":"User not found","data":null}
// 4. 直接輸出到瀏覽器(API 入口)
// Response::show(200, 'OK', $data); // 自動判斷格式
?>
<?php
class ApiResponse extends Response {
// 常用狀態碼常數
const OK = 200;
const CREATED = 201;
const BAD_REQUEST = 400;
const UNAUTHORIZED = 401;
const FORBIDDEN = 403;
const NOT_FOUND = 404;
const VALIDATION_ERROR = 422;
const SERVER_ERROR = 500;
// 成功快捷方法
public static function success($data = [], $message = 'OK', $format = '') {
return self::show(self::OK, $message, $data, $format);
}
// 驗證失敗
public static function validationError($errors, $message = 'Validation failed', $format = '') {
return self::show(self::VALIDATION_ERROR, $message, ['errors' => $errors], $format);
}
// 伺服器錯誤
public static function serverError($message = 'Internal server error', $format = '') {
return self::show(self::SERVER_ERROR, $message, null, $format);
}
// 直接輸出並結束腳本(API 入口常用)
public static function emit($code, $message, $data = [], $format = '') {
echo self::show($code, $message, $data, $format);
exit;
}
}
// 使用
// ApiResponse::emit(ApiResponse::success($userData));
// ApiResponse::emit(ApiResponse::validationError($validator->errors()));
?>
| 資料型別 | XML 表示 | 說明 |
|---|---|---|
| 純量 (string/int/float/bool) | <key><![CDATA[value]]></key> | CDATA 保護特殊字元 |
| 索引陣列 | <item id="0">...</item> | 保留順序與索引 |
| 關聯陣列/物件 | <key>...</key> | 鍵名作為節點名 |
| null | <key></key> 或略過 | 可選擇輸出空節點 |
json_encode 結果(key = md5(serialize($data)))。data: {list: [...], pagination: {page, per_page, total}} 結構,避免單次回傳過大。JSON_UNESCAPED_UNICODE 旗標。arrayToXml 中用正則清理。true/false → JSON true/false,XML 1/0 (CDATA 內),前端解析時注意型別轉換。Response 類別提供 統一結構 (code/message/data)、雙格式 (JSON/XML)、自動格式協商、巢狀資料正確序列化。延伸 ApiResponse 加入語意化狀態碼與快捷方法,可直接作為 API 專案基礎元件。生產環境建議搭配 OpenAPI/Swagger 註解 自動生成文件,並加入 回應快取、壓縮 (gzip)、CORS 標頭 完善 API 體驗。