238 lines
7.4 KiB
PHP
238 lines
7.4 KiB
PHP
<?php
|
||
/**
|
||
* 🚀 API响应格式处理
|
||
*
|
||
* 支持多种响应格式,提高App读取效率
|
||
* - JSON: 通用格式,兼容性好
|
||
* - MessagePack: 二进制格式,体积小30-50%,解析更快
|
||
* - Gzip: 压缩JSON,减少传输大小
|
||
* - CBOR: 二进制JSON,RFC 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";
|
||
}
|