はじめに
APIや非同期通信で扱うJSONには、メタ情報で包む「ラッパーJSON」と、最小構造の「フラットJSON」があります。
本記事では両者の特徴・使い分けと、WordPressに依存しない素のPHP関数だけで実運用できるレスポンス実装(CORS・キャッシュ制御・エラー整形・切り替えクエリ ?flat=1 含む)を示します。
1. 定義とサンプル
ラッパーJSON(Wrapper JSON)
データ本体をメタ情報で包む形式。拡張しやすく、APIの一貫性を保ちやすい。
{
"status": "ok",
"message": "success",
"meta": { "page": 1, "per_page": 20, "total": 137 },
"data": [
{ "id": 101, "title": "記事A", "published_at": "2025-11-10" },
{ "id": 102, "title": "記事B", "published_at": "2025-11-11" }
]
}
フラットJSON(Flat JSON)
メタ情報を持たない、軽量な配列/オブジェクト。
[
{ "id": 101, "title": "記事A", "published_at": "2025-11-10" },
{ "id": 102, "title": "記事B", "published_at": "2025-11-11" }
]
2. 使い分けの要点
| 指標 | ラッパーJSON | フラットJSON |
|---|---|---|
| メタ情報(ページ/総件数/並び順) | 必要 | 不要 |
| エラー・警告の表現 | 詳細に統一しやすい | 別チャンネルが必要 |
| 将来拡張 | 強い | 弱い |
| 軽量性 | △ | ◎ |
- 公開API/外部連携/管理画面ではラッパーが安定。
- 内部用の単純な一覧はフラットで軽量化。
- 妥協案として
?flat=1で切り替え可能にしておくと現場で扱いやすい。
3. 素のPHPでJSONを返すユーティリティ(共通関数)
これだけで
wp_send_jsonなしに全APIを統一できます。
CORSやヘッダは引数で調整可能。exit;で確実に終了。
<?php
/**
* JSONを出力してスクリプト終了(素のPHP版)
*
* @param mixed $payload 返却する配列/オブジェクト
* @param int $status HTTPステータス(例: 200, 400, 422, 500)
* @param array $extra_headers 追加ヘッダ ['Header-Name' => 'Value']
* @param int $json_flags json_encode フラグ
*/
function json_output($payload, int $status = 200, array $extra_headers = [], int $json_flags = 0)
{
// 既定のエンコードフラグ(日本語/スラッシュのエスケープ抑止 + 部分失敗許容)
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
if ($json_flags) {
$flags |= $json_flags;
}
// ヘッダ未送出なら送る
if (headers_sent() === false) {
// 必要に応じてCORS(公開APIならオリジンを適切に限定推奨)
// $extra_headers['Access-Control-Allow-Origin'] = 'https://example.com';
// $extra_headers['Vary'] = 'Origin';
header('Content-Type: application/json; charset=utf-8', true, $status);
header('X-Content-Type-Options: nosniff');
// キャッシュ抑止:動的APIの既定値。静的性が高い場合は適宜変更
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
foreach ($extra_headers as $k => $v) {
header($k . ': ' . $v);
}
}
$json = json_encode($payload, $flags);
// 逆プロキシ/Chunked環境では Content-Length を省く場合あり
if ($json !== false && headers_sent() === false) {
header('Content-Length: ' . strlen($json));
}
echo $json !== false ? $json : '{"status":"error","message":"json_encode failed"}';
exit; // 確実に終了(WordPressでも使用可)
}
4. ラッパー/フラットの切り替え実装(?flat=1)
<?php
// 例: /api/posts に紐づくハンドラ内(フレームワーク不問)
// 実データはDBや外部APIから取得してください
$items = [
['id' => 101, 'title' => '記事A', 'published_at' => '2025-11-10'],
['id' => 102, 'title' => '記事B', 'published_at' => '2025-11-11'],
];
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$pp = isset($_GET['per_page']) ? max(1, (int)$_GET['per_page']) : 20;
$total = count($items); // 実際は総件数クエリ
$is_flat = isset($_GET['flat']) && (string)$_GET['flat'] === '1';
if ($is_flat) {
// フラットJSON
json_output($items, 200);
} else {
// ラッパーJSON
$response = [
'status' => 'ok',
'message' => 'success',
'meta' => ['page' => $page, 'per_page' => $pp, 'total' => $total],
'data' => $items,
];
json_output($response, 200);
}
5. エラー・バリデーションの統一形
<?php
// 入力チェック例
$errors = [];
if (empty($_GET['title'])) {
$errors[] = ['code' => 1001, 'field' => 'title', 'message' => 'タイトルは必須です。'];
}
if ($errors) {
json_output([
'status' => 'error',
'errors' => $errors,
], 422);
}
// 正常時
json_output(['status' => 'ok', 'message' => 'saved'], 200);
6. セキュリティ/運用の注意
- CORS:公開APIは
Access-Control-Allow-Originを特定オリジンに限定。認証が絡む場合は資格情報の扱いにも注意。 - キャッシュ:管理系APIは
no-store、一覧のGETは短期max-ageなど用途で分離。 - ログ:
json_output直前でリクエストIDや所要時間をログ出力すると運用が安定。 - 例外:
try/catchで例外を拾い、status:error + 5xx/4xxへ整形してからjson_outputに集約。
7. 動作確認のための cURL 例
ラッパー(通常)
curl -i "https://example.com/api/posts"
フラット(軽量)
curl -i "https://example.com/api/posts?flat=1"
バリデーションエラー
curl -i "https://example.com/api/posts/create"
# 必須欠落で 422 + {status:error,errors:[...]} を想定
8. 既存プロジェクトへの導入手順(最短)
- 共通関数
json_output()をユーティリティに追加(例:api/services/JsonResponse.phpでもfunctions.phpでも可)。 - ルーター/ハンドラの末尾を
json_output($payload, $status);へ置換。 - ラッパー/フラット切替が必要なAPIは、
?flat=1を見て分岐。 - エラー時は同一スキーマ(
{status:error, errors:[...]})で統一。 - CORSとキャッシュヘッダは用途ごとに
extra_headersで付与。
まとめ
- ラッパーJSONは拡張性・一貫性が高く、メタ情報やエラーを表現しやすい。
- フラットJSONは軽量・シンプルで内部用のリストに向く。
- 素のPHP関数
json_output()を使えば、WordPress依存なしで安定したJSONレスポンスを統一運用できる。 - 妥協案として
?flat=1切替を用意しておくと、軽量化ニーズと公開APIの設計品質を両立できる。


コメント