📖 درس هشتم: API Versioning و مدیریت چرخه حیات
🎯 هدف این درس: آشنایی با استراتژیهای نسخهبندی API، مدیریت Deprecation، حفظ Backward Compatibility و نحوه مهاجرت کاربران از نسخههای قدیمی به جدید.
مهمان عزیز! 👋
API شما در حال رشد است و روز به روز کاربران بیشتری از آن استفاده میکنند. اما سوال اینجاست: وقتی میخواهید تغییری در API ایجاد کنید که با نسخه قبلی سازگار نیست، چه باید کرد؟
اگر تغییرات را اعمال کنید، کاربران قدیمی شما ممکن است دچار مشکل شوند. اگر تغییرات را اعمال نکنید، API شما از رقبا عقب میماند. راه حل این است: نسخهبندی (Versioning).
💡 چرا نسخهبندی API مهم است؟
- ✅ امکان تغییر و بهبود API بدون شکستن کد کاربران.
- ✅ ارائه قابلیتهای جدید به صورت تدریجی.
- ✅ مدیریت چرخه حیات API و حذف تدریجی نسخههای قدیمی.
- ✅ ایجاد اعتماد در کاربران که API شما پایدار است.
- ✅ امکان تست نسخههای جدید با کاربران محدود (Beta Testing).
🗺️ استراتژیهای نسخهبندی API
سه استراتژی اصلی برای نسخهبندی API وجود دارد:
| استراتژی | مثال | مزایا | معایب |
|---|---|---|---|
| URI Path | /v1/users |
ساده، واضح، قابل کش | تغییر URL، نیاز به هدایت |
| Query Parameter | /users?version=1 |
ساده، بدون تغییر URL | کمتر واضح، مشکلات کش |
| Header | API-Version: 1 |
URL تمیز، RESTful | کمتر قابل مشاهده، تست سختتر |
⚠️ توصیه مهم:
استراتژی URI Path (مانند /v1/users) محبوبترین و توصیهشدهترین روش است، زیرا:
- ✅ واضح و قابل فهم است.
- ✅ با CDN و پروکسیها به خوبی کار میکند.
- ✅ به راحتی در مستندات قابل نمایش است.
- ✅ امکان هدایت و ریدایرکت ساده دارد.
🔄 پیادهسازی URI Versioning
بیایید نسخهبندی URI را در مسیریاب پیادهسازی کنیم:
Router.php (با نسخهبندی)
PHP
8.2
<?php
class Router
{
private $routes = [];
private $version = 'v1';
private $supportedVersions = ['v1', 'v2'];
private $deprecatedVersions = ['v1'];
private $defaultVersion = 'v2';
// اضافه کردن مسیر با نسخه مشخص
public function add($method, $path, $controller, $action, $version = 'v1')
{
$this->routes[] = [
'method' => $method,
'path' => '/' . $version . $path,
'controller' => $controller,
'action' => $action,
'version' => $version
];
}
// پردازش درخواست با تشخیص نسخه
public function dispatch($request)
{
$method = $request->getMethod();
$uri = $request->getUri();
// تشخیص نسخه از URI
$version = $this->extractVersion($uri);
// بررسی پشتیبانی از نسخه
if (!in_array($version, $this->supportedVersions)) {
return new Response(400, [
'error' => 'Unsupported API version',
'supported_versions' => $this->supportedVersions
]);
}
// هشدار برای نسخههای منسوخ شده
if (in_array($version, $this->deprecatedVersions)) {
header('Warning: 299 - "This API version is deprecated. Please upgrade to v2"');
}
// حذف نسخه از URI برای تطبیق
$uriWithoutVersion = preg_replace('#^/' . $version . '#i', '', $uri);
// جستجوی مسیر
foreach ($this->routes as $route) {
$pattern = preg_replace('/\{([a-zA-Z]+)\}/', '([^/]+)', $route['path']);
$pattern = '#^' . $pattern . '$#';
// تطبیق متد و مسیر
if ($route['method'] === $method &&
$route['version'] === $version &&
preg_match($pattern, $uriWithoutVersion, $matches)) {
array_shift($matches);
$params = $matches;
$controllerName = $route['controller'];
$actionName = $route['action'];
// انتخاب کنترلر بر اساس نسخه
$controllerClass = $controllerName . $version;
if (!class_exists($controllerClass)) {
$controllerClass = $controllerName;
}
$controller = new $controllerClass();
return $controller->$actionName($request, $params);
}
}
return new Response(404, ['error' => 'Endpoint not found']);
}
// استخراج نسخه از URI
private function extractVersion($uri)
{
if (preg_match('#^/(v[0-9]+)/#i', $uri, $matches)) {
return strtolower($matches[1]);
}
// اگر نسخه مشخص نشده، از نسخه پیشفرض استفاده کن
return $this->defaultVersion;
}
// تنظیم نسخههای پشتیبانی شده
public function setSupportedVersions($versions)
{
$this->supportedVersions = $versions;
}
// تنظیم نسخههای منسوخ شده
public function setDeprecatedVersions($versions)
{
$this->deprecatedVersions = $versions;
}
}
🧪 نسخههای مختلف کنترلر
برای هر نسخه، یک کنترلر مجزا ایجاد میکنیم تا تغییرات را به صورت مستقل مدیریت کنیم:
UserControllerv1.php
PHP
8.2
<?php
// ========== نسخه ۱ (قدیمی) ==========
class UserControllerv1
{
private $userModel;
public function __construct()
{
require_once __DIR__ . '/../Models/User.php';
$this->userModel = new User();
}
// GET /v1/users - دریافت کاربران (نسخه ۱: فقط نام و ایمیل)
public function index($request)
{
$users = $this->userModel->getAll();
// نسخه ۱: فقط فیلدهای پایه
$result = array_map(function($user) {
return [
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email']
];
}, $users);
return Response::success($result);
}
// GET /v1/users/{id} - دریافت یک کاربر
public function show($request, $params)
{
$user = $this->userModel->find($params[0]);
if (!$user) {
return Response::notFound();
}
// نسخه ۱: فقط فیلدهای پایه
return Response::success([
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email']
]);
}
// POST /v1/users - ایجاد کاربر
public function store($request)
{
$data = $request->getBody();
// اعتبارسنجی ساده (نسخه ۱)
if (empty($data['name']) || empty($data['email'])) {
return Response::error('Name and email are required', 422);
}
$user = $this->userModel->create($data);
return Response::created($user);
}
}
UserControllerv2.php
PHP
8.2
<?php
// ========== نسخه ۲ (جدید و بهبود یافته) ==========
class UserControllerv2
{
private $userModel;
public function __construct()
{
require_once __DIR__ . '/../Models/User.php';
$this->userModel = new User();
}
// GET /v2/users - دریافت کاربران با اطلاعات کامل
public function index($request)
{
$page = $_GET['page'] ?? 1;
$limit = $_GET['limit'] ?? 10;
$users = $this->userModel->getAll($page, $limit);
$total = $this->userModel->getTotalCount();
// نسخه ۲: اطلاعات کامل + صفحهبندی
return Response::success([
'data' => $users, // تمام فیلدها
'pagination' => [
'current_page' => $page,
'per_page' => $limit,
'total' => $total,
'total_pages' => ceil($total / $limit)
]
]);
}
// GET /v2/users/{id} - دریافت کاربر با اطلاعات کامل
public function show($request, $params)
{
$user = $this->userModel->find($params[0]);
if (!$user) {
return Response::notFound();
}
// نسخه ۲: تمام فیلدها + اطلاعات اضافی
return Response::success([
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email'],
'role' => $user['role'] ?? 'user',
'created_at' => $user['created_at'],
'updated_at' => $user['updated_at'],
'profile_complete' => $this->isProfileComplete($user)
]);
}
// POST /v2/users - ایجاد کاربر با اعتبارسنجی پیشرفته
public function store($request)
{
$data = $request->getBody();
// اعتبارسنجی پیشرفته (نسخه ۲)
$validation = $request->validate([
'name' => 'required|min:3|max:100',
'email' => 'required|email',
'password' => 'required|min:8|strong',
'role' => 'in:admin,user,guest'
]);
if ($validation !== true) {
return Response::error('Validation failed', 422, $validation);
}
$user = $this->userModel->create($data);
return Response::created($user);
}
private function isProfileComplete($user)
{
// بررسی تکمیل بودن پروفایل
$requiredFields = ['name', 'email', 'phone', 'address'];
foreach ($requiredFields as $field) {
if (empty($user[$field])) {
return false;
}
}
return true;
}
}
🗺️ ثبت مسیرها با نسخهبندی
نحوه ثبت مسیرها در index.php با نسخهبندی:
index.php (نسخهبندی)
PHP
8.2
<?php
// ========== بارگذاری فایلها ==========
require_once __DIR__ . '/../src/Core/Router.php';
require_once __DIR__ . '/../src/Core/Request.php';
require_once __DIR__ . '/../src/Core/Response.php';
// بارگذاری کنترلرهای نسخههای مختلف
require_once __DIR__ . '/../src/Controllers/UserControllerv1.php';
require_once __DIR__ . '/../src/Controllers/UserControllerv2.php';
require_once __DIR__ . '/../src/Controllers/AuthController.php';
// ========== تنظیم مسیریاب ==========
$router = new Router();
// تنظیم نسخههای پشتیبانی شده
$router->setSupportedVersions(['v1', 'v2']);
$router->setDeprecatedVersions(['v1']);
// ========== مسیرهای نسخه ۱ (قدیمی) ==========
$router->add('GET', '/users', 'UserControllerv1', 'index', 'v1');
$router->add('GET', '/users/{id}', 'UserControllerv1', 'show', 'v1');
$router->add('POST', '/users', 'UserControllerv1', 'store', 'v1');
// ========== مسیرهای نسخه ۲ (جدید) ==========
$router->add('GET', '/users', 'UserControllerv2', 'index', 'v2');
$router->add('GET', '/users/{id}', 'UserControllerv2', 'show', 'v2');
$router->add('POST', '/users', 'UserControllerv2', 'store', 'v2');
$router->add('PUT', '/users/{id}', 'UserControllerv2', 'update', 'v2');
$router->add('DELETE', '/users/{id}', 'UserControllerv2', 'delete', 'v2');
// ========== مسیرهای احراز هویت (بدون نسخه) ==========
// این مسیرها مستقل از نسخه هستند و برای همه نسخهها یکسانند
$router->add('POST', '/auth/register', 'AuthController', 'register', 'v1');
$router->add('POST', '/auth/login', 'AuthController', 'login', 'v1');
$router->add('POST', '/auth/register', 'AuthController', 'register', 'v2');
$router->add('POST', '/auth/login', 'AuthController', 'login', 'v2');
// ========== پردازش درخواست ==========
$request = new Request();
$response = $router->dispatch($request);
http_response_code($response->statusCode);
echo json_encode($response->body, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
📋 مدیریت Deprecation و اعلان به کاربران
برای اطلاعرسانی به کاربران درباره منسوخ شدن نسخهها، از هدرهای Warning و مستندات استفاده میکنیم:
Deprecation Manager
PHP
8.2
<?php
class DeprecationManager
{
private static $deprecations = [
'v1' => [
'status' => 'deprecated',
'deprecated_since' => '2026-01-01',
'sunset_date' => '2026-12-31',
'deprecation_message' => 'API v1 is deprecated. Please upgrade to v2.',
'migration_guide' => 'https://rajestary.com/docs/api-migration-v1-to-v2'
]
];
public static function addDeprecationHeaders($version)
{
if (isset(self::$deprecations[$version])) {
$deprecation = self::$deprecations[$version];
// هشدار در هدر
header('Warning: 299 - "' . $deprecation['deprecation_message'] . '"');
// هدر Sunset (طبق استاندارد RFC 8594)
header('Sunset: ' . $deprecation['sunset_date']);
// لینک راهنمای مهاجرت
header('Link: <' . $deprecation['migration_guide'] . '>; rel="deprecation"; type="text/html"');
}
}
public static function getDeprecationInfo($version)
{
return self::$deprecations[$version] ?? null;
}
public static function isDeprecated($version)
{
return isset(self::$deprecations[$version]) &&
self::$deprecations[$version]['status'] === 'deprecated';
}
}
// استفاده در مسیریاب
if (DeprecationManager::isDeprecated($version)) {
DeprecationManager::addDeprecationHeaders($version);
}
🔄 استراتژی Backward Compatibility
Backward Compatibility به معنای حفظ سازگاری با نسخههای قبلی است. در اینجا چند استراتژی برای حفظ سازگاری:
| استراتژی | توضیح | مثال |
|---|---|---|
| افزودن فیلدهای جدید | فیلدهای جدید را به پاسخ اضافه کن بدون حذف فیلدهای قدیمی | { "id": 1, "name": "یونس", "role": "admin" } |
| پارامترهای اختیاری | پارامترهای جدید را اختیاری کن تا کاربران قدیمی مشکلی نداشته باشند | ?include=profile |
| مقادیر پیشفرض | برای فیلدهای جدید مقدار پیشفرض در نظر بگیر | "role": "user" |
| نسخهبندی URI | نسخههای مختلف را با مسیرهای جداگانه مدیریت کن | /v1/users و /v2/users |
| Redirect | درخواستهای نسخه قدیمی را به نسخه جدید هدایت کن | Redirect 301 از v1 به v2 |
🔴 خطاهای رایج در نسخهبندی
- ❌ اشتباه: حذف ناگهانی یک نسخه بدون اعلان قبلی.
- ✅ درست: حداقل ۶ ماه قبل از حذف، اعلان Deprecation بده.
- ❌ اشتباه: پشتیبانی از نسخههای بسیار زیاد (بیش از ۳ نسخه).
- ✅ درست: حداکثر ۲-۳ نسخه فعال نگه دار.
- ❌ اشتباه: تغییر رفتار یک Endpoint بدون تغییر نسخه.
- ✅ درست: هر تغییر غیرسازگار باید با یک نسخه جدید همراه باشد.
- ❌ اشتباه: عدم مستندسازی تغییرات بین نسخهها.
- ✅ درست: یک Changelog واضح برای هر نسخه منتشر کن.
💎 نکات کلیدی درس
- ✅ نسخهبندی به شما امکان تغییر API بدون شکستن کد کاربران را میدهد.
- ✅ استراتژی URI Path (مانند
/v1/users) محبوبترین روش است. - ✅ از هدر
Warningبرای اطلاعرسانی Deprecation استفاده کن. - ✅ هر نسخه باید کنترلر مخصوص خود را داشته باشد.
- ✅ Backward Compatibility برای حفظ کاربران قدیمی ضروری است.
- ✅ برای هر نسخه، یک برنامه Sunset (زمان حذف) مشخص کن.
- ✅ تغییرات بین نسخهها را در مستندات Changelog ثبت کن.
🛠️ پروژه عملی درس هشتم
مهمان عزیز، حالا سیستم نسخهبندی را به API محصولات اضافه کن!
مسئله:
یک سیستم نسخهبندی کامل برای API محصولات پیادهسازی کن:
- ✅ دو نسخه v1 و v2 برای API محصولات ایجاد کن.
- ✅ نسخه v1 فقط اطلاعات پایه محصول را برگرداند.
- ✅ نسخه v2 شامل اطلاعات کامل و اضافی باشد.
- ✅ نسخه v1 را به عنوان Deprecated علامتگذاری کن.
- ✅ هدرهای Warning و Sunset را به پاسخها اضافه کن.
- ✅ یک مستند Changelog برای تغییرات بین نسخهها بنویس.
🧪 راهحل پروژه (پاسخ)
مراحل پیادهسازی:
- 1️⃣ دو کنترلر
ProductControllerv1وProductControllerv2ایجاد کن. - 2️⃣ مسیرهای هر نسخه را در
index.phpثبت کن. - 3️⃣ کلاس
DeprecationManagerرا برای مدیریت هدرها اضافه کن. - 4️⃣ نسخه v1 را Deprecated کن و هدرهای مربوطه را اضافه کن.
- 5️⃣ تغییرات بین نسخهها را در مستندات OpenAPI ثبت کن.
📝 تمرینهای عملی
🧪 تمرین ۱ (ساده):
دو نسخه از یک Endpoint ساده ایجاد کن. نسخه v1 فقط id و name و نسخه v2 id، name، email و created_at را برگرداند.
🧪 تمرین ۲ (متوسط):
یک سیستم Redirect پیادهسازی کن که درخواستهای نسخه v1 را به v2 هدایت کند و یک هشدار Deprecation نمایش دهد.
🧪 تمرین ۳ (چالشی):
یک سیستم A/B Testing با استفاده از نسخهبندی پیادهسازی کن. ۵۰٪ از کاربران به نسخه v1 و ۵۰٪ به نسخه v2 هدایت شوند. نتایج را در لاگ ثبت کن.
🏁 جمعبندی درس
مهمان عزیز، در این درس یاد گرفتی:
- ✅ چرا نسخهبندی API مهم است و چه مزایایی دارد.
- ✅ سه استراتژی اصلی نسخهبندی (URI، Query، Header).
- ✅ چگونه نسخهبندی URI را در مسیریاب پیادهسازی کنی.
- ✅ چگونه کنترلرهای مختلف برای نسخههای مختلف ایجاد کنی.
- ✅ چگونه Deprecation را با هدرهای Warning و Sunset مدیریت کنی.
- ✅ چگونه Backward Compatibility را حفظ کنی.
حالا API شما قابلیت تکامل و تغییر بدون شکستن کد کاربران را دارد. در درس بعدی، تست و دیباگ API حرفهای را یاد میگیریم. آمادهای؟ 🚀
🗺️ نقشه راه دوره: شما اینجا هستید!
برای اینکه بدانی دقیقاً کجای مسیر هستی و چه درسهایی در انتظار توست، به جدول زیر نگاه کن. درس فعلی با رنگ متفاوت مشخص شده است.
| درس | عنوان درس | آنچه یاد میگیرید |
|---|---|---|
| ۱ | مفاهیم پایه API | HTTP، REST، معماری Client-Server |
| ۲ | پیادهسازی RESTful API با PHP خام | مسیریابی، متدها، پارامترها، پاسخهای JSON |
| ۳ | مدیریت دادهها و پایگاهداده | اتصال PDO، CRUD، تراکنشها، Pagination |
| ۴ | اعتبارسنجی و احراز هویت | JWT، Roles/Permissions، Middleware |
| ۵ | امنیت API پیشرفته | OWASP Top 10، SQLi، XSS، CSRF، Rate Limiting |
| ۶ | مستندسازی API با OpenAPI | Swagger، OpenAPI Specification، مستندات تعاملی |
| ۷ | Caching و افزایش عملکرد | Redis، ETag، Cache-Control، Query Optimization |
| ۸ | API Versioning و مدیریت چرخه حیات | استراتژیهای نسخهبندی، Deprecation، Backward Compatibility |
| ۹ | تست و دیباگ API حرفهای | PHPUnit، Mocking، Logging، Postman Collection |
| ۱۰ | پروژه نهایی: API فروشگاهی | پروژه کامل فروشگاه اینترنتی با تمام قابلیتها |




نظر خود را بنویسید
با ثبت نظر، به بهبود محتوای ما کمک کنید