Files
kitchen/docs/api/response.php
Developer 13fdbdc431 瀑布流
2026-04-13 03:39:29 +08:00

342 lines
10 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标准
*
* @file response.php
* @author AI Assistant
* @date 2026-04-12
* @version 2.1.0
* @desc API响应格式处理包含评分格式化工具
* @lastUpdate 2026-04-12 新增评分格式化函数,处理边缘情况
*/
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';
}
}
/**
* 格式化评分显示
* 处理边缘情况:暂无评分、评分数量少、评分异常
*
* @param float $score 平均评分
* @param int $nums 评分次数
* @return array 格式化后的评分信息
*/
public static function formatRating($score, $nums) {
$result = array(
'score' => 0.00,
'nums' => 0,
'display' => '暂无评分',
'status' => 'none',
'level' => '',
'star' => 0
);
if ($nums <= 0) {
return $result;
}
$score = (float) $score;
$nums = (int) $nums;
if ($score < 0 || $score > 5) {
$result['status'] = 'abnormal';
$result['display'] = '评分异常';
return $result;
}
$result['score'] = round($score, 2);
$result['nums'] = $nums;
$result['star'] = round($score);
if ($nums < 3) {
$result['status'] = 'few';
$result['display'] = sprintf('%.1f分 (%d人评分样本较少)', $score, $nums);
} elseif ($nums < 10) {
$result['status'] = 'normal';
$result['display'] = sprintf('%.1f分 (%d人评分)', $score, $nums);
} else {
$result['status'] = 'sufficient';
$result['display'] = sprintf('%.1f分 (%d人评分)', $score, $nums);
}
if ($score >= 4.5) {
$result['level'] = '优秀';
} elseif ($score >= 4.0) {
$result['level'] = '推荐';
} elseif ($score >= 3.0) {
$result['level'] = '一般';
} elseif ($score >= 2.0) {
$result['level'] = '较差';
} else {
$result['level'] = '不推荐';
}
return $result;
}
/**
* 获取评分统计信息(用于列表显示)
*
* @param float $score 平均评分
* @param int $nums 评分次数
* @return array 简化的评分信息
*/
public static function getRatingSummary($score, $nums) {
if ($nums <= 0) {
return array(
'score' => 0.00,
'nums' => 0,
'display' => '暂无评分',
'star' => 0
);
}
$score = (float) $score;
if ($score < 0 || $score > 5) {
return array(
'score' => 0.00,
'nums' => $nums,
'display' => '评分异常',
'star' => 0
);
}
return array(
'score' => round($score, 2),
'nums' => (int) $nums,
'display' => sprintf('%.1f分', $score),
'star' => (int) round($score)
);
}
}
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";
}