本文目录导读:

在PHP项目中实现API版本控制有多种策略,下面是几种常用且实践效果好的方案:
URI路径版本控制(最常用)
实现方式
// 目录结构 api/ ├── v1/ │ ├── UserController.php │ └── ProductController.php ├── v2/ │ ├── UserController.php │ └── ProductController.php └── index.php
路由实现示例
// 使用原生PHP实现
$version = isset($_GET['version']) ? $_GET['version'] : 'v1';
$request_uri = $_SERVER['REQUEST_URI'];
// 解析URL格式: /api/v1/users
preg_match('#^/api/(v\d+)/([a-z]+)#', $request_uri, $matches);
if (!empty($matches)) {
$version = $matches[1]; // v1, v2
$resource = $matches[2]; // users, products
// 加载对应的控制器
$controller_file = __DIR__ . "/{$version}/{$resource}Controller.php";
if (file_exists($controller_file)) {
$className = ucfirst($resource) . 'Controller';
require_once $controller_file;
$controller = new $className();
$controller->handleRequest();
}
}
使用框架实现(Laravel示例)
// routes/api.php
Route::prefix('api/v1')->group(function () {
Route::get('/users', 'v1\UserController@index');
Route::post('/users', 'v1\UserController@store');
});
Route::prefix('api/v2')->group(function () {
Route::get('/users', 'v2\UserController@index');
Route::post('/users', 'v2\UserController@store');
});
HTTP Header版本控制
实现方式
// index.php
class ApiRouter {
public function handle() {
// 从请求头获取版本
$version = $_SERVER['HTTP_API_VERSION'] ?? 'v1';
// 验证版本格式
if (!preg_match('/^v\d+$/', $version)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid version format']);
exit;
}
// 动态加载版本对应的控制器
$class_file = __DIR__ . "/controller/{$version}/" . ucfirst($this->resource) . 'Controller.php';
if (!file_exists($class_file)) {
http_response_code(404);
echo json_encode(['error' => 'Version not found']);
exit;
}
require_once $class_file;
// ... 其他逻辑
}
}
可接受MIME类型版本控制
// 检查Accept头
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
$version = 'v1';
if (preg_match('/application\/vnd\.myapi\.(v\d+)\+json/', $accept, $matches)) {
$version = $matches[1];
}
// 根据版本加载不同的处理逻辑
switch ($version) {
case 'v2':
// 新版逻辑
break;
case 'v1':
default:
// 旧版逻辑
break;
}
最佳实践与完整示例
完整的版本控制类
<?php
class ApiVersionController {
private $adapters = [];
// 注册版本适配器
public function registerAdapter($version, $adapterClass) {
$this->adapters[$version] = $adapterClass;
}
// 处理请求
public function handleRequest() {
$version = $this->detectVersion();
$resource = $this->detectResource();
// 检查版本是否存在
if (!isset($this->adapters[$version])) {
throw new Exception("API version $version not supported", 404);
}
// 动态创建适配器实例
$adapterClass = $this->adapters[$version];
$adapter = new $adapterClass();
// 执行请求
return $adapter->handle($resource);
}
// 检测API版本
private function detectVersion() {
// 支持多种版本检测方式
// 1. 路径版本
if (preg_match('#/api/(v\d+)/#', $_SERVER['REQUEST_URI'], $matches)) {
return $matches[1];
}
// 2. header版本
if (isset($_SERVER['HTTP_X_API_VERSION'])) {
$version = $_SERVER['HTTP_X_API_VERSION'];
if (in_array($version, ['v1', 'v2'])) {
return $version;
}
}
// 3. 默认版本
return 'v1';
}
}
// 版本适配器基类
abstract class ApiAdapter {
abstract public function handle($resource);
protected function successResponse($data, $statusCode = 200) {
http_response_code($statusCode);
return json_encode([
'status' => 'success',
'data' => $data
]);
}
}
// V1版本实现
class V1ApiAdapter extends ApiAdapter {
public function handle($resource) {
switch ($resource) {
case 'users':
// V1特有逻辑
$data = ['version' => '1.0', 'users' => getUserDataV1()];
return $this->successResponse($data);
case 'products':
// 产品逻辑
break;
}
}
}
// V2版本实现
class V2ApiAdapter extends ApiAdapter {
public function handle($resource) {
switch ($resource) {
case 'users':
// V2特有逻辑(可能与V1不同)
$data = ['version' => '2.0', 'users' => getUserDataV2()];
return $this->successResponse($data);
case 'products':
// 产品逻辑(V2版本可能有不同的产品结构)
break;
}
}
}
// 使用示例
$api = new ApiVersionController();
$api->registerAdapter('v1', 'V1ApiAdapter');
$api->registerAdapter('v2', 'V2ApiAdapter');
try {
echo $api->handleRequest();
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()], $e->getCode());
}
版本迁移策略
向后兼容处理
<?php
class UserController {
public function getUsers($version = 'v1') {
// 基础数据
$users = DB::table('users')->get();
switch ($version) {
case 'v2':
// V2新增字段、新逻辑
$users = $users->filter(function($user) {
return !$user->is_deleted;
});
return $this->formatResponseV2($users);
case 'v1':
default:
return $this->formatResponseV1($users);
}
}
private function formatResponseV1($users) {
return ['data' => $users];
}
private function formatResponseV2($users) {
return [
'data' => $users,
'meta' => [
'version' => '2.0',
'count' => count($users)
]
];
}
}
最佳实践建议
<?php
// 配置文件 config/api.php
return [
'versions' => [
'v1' => [
'supported' => true,
'deprecated' => false, // 是否废弃
'retired_date' => null, // 退休日期
'features' => ['legacy_auth', 'basic_rate_limit']
],
'v2' => [
'supported' => true,
'deprecated' => false,
'retired_date' => null,
'features' => ['oauth2', 'advanced_rate_limit', 'pagination']
]
],
'default_version' => 'v2'
];
// 版本控制器
class VersionManager {
public function isValid($version) {
$configs = require 'config/api.php';
return isset($configs['versions'][$version]) && $configs['versions'][$version]['supported'];
}
public function isDeprecated($version) {
$configs = require 'config/api.php';
return $configs['versions'][$version]['deprecated'];
}
// 添加弃用警告头
public function handleDeprecation($version) {
if ($this->isDeprecated($version)) {
header('Warning: 299 - "API version deprecated"');
}
}
}
- 选择版本策略:根据项目需求选择URI路径(最常用)、请求头或Accept头方式
- 保持兼容:新版本发布时,旧版本至少维护6个月
- 文档更新:每个版本对应的API文档要同步更新
- 监控告警:对旧版本的使用进行监控,把握弃用时机
- 明确通信:通过header、文档等明确告知客户端版本变更信息
选择哪种方案取决于你的应用场景和团队偏好,但URI路径版本控制是最直观、最容易理解的方案,适合大多数项目使用。