前後端分離開發中,後端 API 未就緒時,前端需要可用的 Mock 資料。本文提供一個單檔案、零依賴的 PHP Mock 服務,支援 JSON/JSONP、跨域 (CORS)、字符集切換、外部 JSON 檔案資料源,適合快速接入測試。
mock.json 檔案,存在則載入作為資料源callback 參數輸出 JSONP (跨域舊瀏覽器)cross-domain=1 輸出 CORS Headercharset 參數切換輸出編碼 (utf-8/gbk/gb2312)Content-Type 輸出<?php
/**
* Mock JSON API Service
* 支援:JSON/JSONP、CORS、charset、外部 mock.json
* 使用:https://host/mock.php?callback=fn&cross-domain=1&charset=utf-8
*/
// 1. 定義常數
define('PARAM_CHARSET', 'charset');
define('PARAM_CALLBACK', 'callback');
define('PARAM_CROSS_DOMAIN', 'cross-domain');
// 2. 獲取 Mock 資料
function getMockData(): string
{
$mockFile = __DIR__ . '/mock.json';
if (file_exists($mockFile)) {
$content = file_get_contents($mockFile);
// 驗證 JSON 合法性
json_decode($content);
if (json_last_error() === JSON_ERROR_NONE) {
return $content;
}
// JSON 無效時回退預設
}
// 預設示例資料
$default = [
'code' => 200,
'message' => 'success',
'data' => [
'id' => 1,
'username' => 'demo_user',
'email' => 'demo@example.com',
'roles' => ['user', 'editor'],
'profile' => [
'avatar' => 'https://example.com/avatar.png',
'nickname' => 'Demo User'
]
],
'timestamp' => time(),
'request_id' => uniqid('req_', true)
];
return json_encode($default, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
// 3. 解析字符集參數
function getCharset(): string
{
$charset = strtolower($_GET[PARAM_CHARSET] ?? 'utf-8');
return in_array($charset, ['utf-8', 'gbk', 'gb2312'], true) ? $charset : 'utf-8';
}
// 4. 組裝輸出
function generateResponse(): string
{
$data = getMockData();
$charset = getCharset();
$callback = $_GET[PARAM_CALLBACK] ?? '';
$crossDomain = isset($_GET[PARAM_CROSS_DOMAIN]);
if ($callback !== '') {
// JSONP 輸出
// 簡單驗證 callback 合法性 (僅字母數字底線點)
if (!preg_match('/^[\w.]+$/', $callback)) {
$callback = 'callback';
}
header('Content-Type: application/javascript; charset=' . $charset);
return $callback . '(' . $data . ');';
} else {
// 標準 JSON
if ($crossDomain) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
}
header('Content-Type: application/json; charset=' . $charset);
return $data;
}
}
// 5. 處理 OPTIONS 預檢
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Max-Age: 86400');
exit(0);
}
// 6. 輸出
echo generateResponse();
在同目錄建立 mock.json,即自動載入:
{
"code": 200,
"message": "操作成功",
"data": {
"list": [
{"id": 1, "title": "文章一", "views": 1234},
{"id": 2, "title": "文章二", "views": 5678}
],
"pagination": {"page": 1, "page_size": 20, "total": 2}
}
}
| 場景 | 請求範例 | 說明 |
|---|---|---|
| 前端直連 (同域) | fetch('/mock.php').then(r=>r.json()) | 標準 JSON |
| 跨域 (現代瀏覽器) | fetch('https://api.mock/mock.php?cross-domain=1') | CORS Header |
| 跨域 (舊瀏覽器/JSONP) | <script src="https://api.mock/mock.php?callback=handleData"></script> | JSONP 回調 |
| 指定編碼 (舊系統) | /mock.php?charset=gbk | 輸出 GBK 編碼 |
| 動態資料 | 修改 mock.json 即時生效 | 無需重啟服務 |
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routes = [
'/api/user/profile' => 'mocks/user_profile.json',
'/api/articles' => 'mocks/articles.json',
'/api/comments' => 'mocks/comments.json',
];
$mockFile = __DIR__ . '/' . ($routes[$path] ?? 'mock.json');
$data = json_decode(getMockData(), true);
if (isset($_GET['page'], $_GET['page_size'])) {
$page = (int)$_GET['page'];
$size = (int)$_GET['page_size'];
if (isset($data['data']['list'])) {
$data['data']['list'] = array_slice($data['data']['list'], ($page-1)*$size, $size);
$data['data']['pagination'] = ['page'=>$page, 'page_size'=>$size, 'total'=>count($data['data']['list'])];
}
}
echo json_encode($data);
if (isset($_GET['delay'])) {
$delay = min(max((int)$_GET['delay'], 0), 5000); // 0-5000ms
usleep($delay * 1000);
}
// routes/web.php
Route::any('/mock/{any?}', function ($any = null) {
$mockFile = base_path("mocks/{$any}.json");
if (!file_exists($mockFile)) $mockFile = base_path('mocks/default.json');
// ... 同邏輯輸出
})->where('any', '.*');
| 能力 | 靜態 .json | PHP Mock 腳本 |
|---|---|---|
| JSONP 回調 | ❌ | ✅ |
| CORS Header | ❌ (需 Web 伺服器配置) | ✅ 程式控制 |
| 字符集轉換 | ❌ | ✅ |
| 動態參數過濾 | ❌ | ✅ |
| 延遲/錯誤模擬 | ❌ | ✅ |
| 請求日誌/統計 | ❌ | ✅ |
/mock/),避免衝突正式 APInginx location /mock/ { ... } 限制存取 IP這個 50 行的 PHP 腳本,解決了前端等待後端 API 的阻塞問題,支援主流跨域方案與編碼需求,是前後端並行開發的輕量級利器。