Files
kitchen/docs/api/response.php
Developer 8d27c67d3a api实现
2026-04-09 08:54:36 +08:00

238 lines
7.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 🚀 API响应格式处理
*
* 支持多种响应格式提高App读取效率
* - JSON: 通用格式,兼容性好
* - MessagePack: 二进制格式体积小30-50%,解析更快
* - Gzip: 压缩JSON减少传输大小
* - CBOR: 二进制JSONRFC 8949标准
*/
class ApiResponse {
private static $supportedFormats = array('json', 'msgpack', 'gzip', 'cbor');
public static function getFormat() {
$format = strtolower(trim($_GET['_format'] ?? 'json'));
if (!in_array($format, self::$supportedFormats)) {
$format = 'json';
}
if ($format === 'msgpack' && !function_exists('msgpack_pack')) {
$format = 'json';
}
if ($format === 'cbor' && !class_exists('CBOR\CBOREncoder')) {
$format = 'json';
}
return $format;
}
public static function output($data, $format = null) {
if ($format === null) {
$format = self::getFormat();
}
$startTime = microtime(true);
switch ($format) {
case 'msgpack':
self::outputMsgpack($data);
break;
case 'gzip':
self::outputGzip($data);
break;
case 'cbor':
self::outputCbor($data);
break;
case 'json':
default:
self::outputJson($data);
break;
}
}
private static function outputJson($data) {
header('Content-Type: application/json; charset=utf-8');
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (isset($_GET['_pretty']) && $_GET['_pretty'] === '1') {
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
$size = strlen($json);
header('X-Response-Size: ' . $size);
header('X-Response-Format: json');
echo $json;
}
private static function outputMsgpack($data) {
if (!function_exists('msgpack_pack')) {
self::outputJson($data);
return;
}
header('Content-Type: application/msgpack');
$packed = msgpack_pack($data);
$size = strlen($packed);
$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
$jsonSize = strlen($jsonData);
$saved = round((1 - $size / $jsonSize) * 100, 1);
header('X-Response-Size: ' . $size);
header('X-Json-Size: ' . $jsonSize);
header('X-Size-Saved: ' . $saved . '%');
header('X-Response-Format: msgpack');
echo $packed;
}
private static function outputGzip($data) {
if (!function_exists('gzencode')) {
self::outputJson($data);
return;
}
header('Content-Type: application/json');
header('Content-Encoding: gzip');
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$gzip = gzencode($json, 6);
$size = strlen($gzip);
$jsonSize = strlen($json);
$saved = round((1 - $size / $jsonSize) * 100, 1);
header('X-Response-Size: ' . $size);
header('X-Json-Size: ' . $jsonSize);
header('X-Size-Saved: ' . $saved . '%');
header('X-Response-Format: gzip');
echo $gzip;
}
private static function outputCbor($data) {
if (!class_exists('CBOR\CBOREncoder')) {
self::outputJson($data);
return;
}
header('Content-Type: application/cbor');
try {
$cbor = CBOR\CBOREncoder::encode($data);
$size = strlen($cbor);
$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
$jsonSize = strlen($jsonData);
$saved = round((1 - $size / $jsonSize) * 100, 1);
header('X-Response-Size: ' . $size);
header('X-Json-Size: ' . $jsonSize);
header('X-Size-Saved: ' . $saved . '%');
header('X-Response-Format: cbor');
echo $cbor;
} catch (Exception $e) {
self::outputJson($data);
}
}
public static function getAcceptFormat() {
$accept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '';
if (strpos($accept, 'application/msgpack') !== false) {
return 'msgpack';
}
if (strpos($accept, 'application/cbor') !== false) {
return 'cbor';
}
if (strpos($accept, 'application/json') !== false) {
return 'json';
}
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
return 'gzip';
}
return 'json';
}
public static function getStats($data) {
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
$jsonSize = strlen($json);
$stats = array(
'json' => array(
'size' => $jsonSize,
'size_readable' => self::formatBytes($jsonSize)
)
);
if (function_exists('gzencode')) {
$gzip = gzencode($json, 6);
$gzipSize = strlen($gzip);
$stats['gzip'] = array(
'size' => $gzipSize,
'size_readable' => self::formatBytes($gzipSize),
'saved' => round((1 - $gzipSize / $jsonSize) * 100, 1) . '%'
);
}
if (function_exists('msgpack_pack')) {
$msgpack = msgpack_pack($data);
$msgpackSize = strlen($msgpack);
$stats['msgpack'] = array(
'size' => $msgpackSize,
'size_readable' => self::formatBytes($msgpackSize),
'saved' => round((1 - $msgpackSize / $jsonSize) * 100, 1) . '%'
);
}
return $stats;
}
private static function formatBytes($bytes) {
if ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} else {
return $bytes . ' B';
}
}
}
if (php_sapi_name() === 'cli' && isset($argv[0]) && basename($argv[0]) === 'response.php') {
header('Content-Type: text/plain');
echo "支持的响应格式:\n";
echo "- json: JSON格式默认\n";
echo "- gzip: Gzip压缩JSON\n";
if (function_exists('msgpack_pack')) {
echo "- msgpack: MessagePack二进制格式 ✅\n";
} else {
echo "- msgpack: MessagePack二进制格式 ❌ (需要安装msgpack扩展)\n";
}
if (class_exists('CBOR\CBOREncoder')) {
echo "- cbor: CBOR二进制格式 ✅\n";
} else {
echo "- cbor: CBOR二进制格式 ❌ (需要安装cbor库)\n";
}
echo "\n使用方式:\n";
echo " api.php?act=list&_format=json # JSON格式\n";
echo " api.php?act=list&_format=gzip # Gzip压缩\n";
echo " api.php?act=list&_format=msgpack # MessagePack格式\n";
echo " api.php?act=list&_format=cbor # CBOR格式\n";
echo " api.php?act=list&_pretty=1 # 格式化JSON调试用\n";
}