📖 درس دوم: پیادهسازی RESTful API با PHP خام
🎯 هدف این درس: پیادهسازی عملی یک RESTful API با استفاده از PHP خام (بدون فریمورک). شامل مسیریابی دستی، مدیریت متدهای HTTP، دریافت پارامترها، ارسال پاسخهای JSON و ساختاردهی حرفهای پروژه.
مهمان عزیز! 👋
در درس قبل با مفاهیم پایه API، HTTP، REST و معماری Client-Server آشنا شدی. حالا وقت آن رسیده که دست به کد شویم و اولین API واقعی خود را با PHP خام پیادهسازی کنیم.
چرا PHP خام؟ چون درک عمیقتری از نحوه کار API به دست میآوری. وقتی بدانی پشت صحنه چه اتفاقی میافتد، بعداً استفاده از فریمورکهایی مثل Laravel یا Symfony برایت بسیار آسانتر خواهد بود.
💡 در این درس چه میسازیم؟
یک API ساده برای مدیریت کاربران (User Management API) با قابلیتهای CRUD کامل. این API از مسیریابی دستی، متدهای HTTP، دریافت و اعتبارسنجی دادهها و ارسال پاسخهای JSON استاندارد پشتیبانی میکند.
📁 ساختار پوشهبندی پروژه
قبل از شروع کدنویسی، بیایید ساختار پروژه را مشخص کنیم. یک پروژه API حرفهای باید سازماندهی شده باشد:
Project Structure
Directory
# ساختار پروژه API
api-project/
├── public/ # پوشه عمومی (دسترسی وب)
│ └── index.php # نقطه ورود (Bootstrap)
├── src/ # کدهای اصلی
│ ├── Core/ # هسته برنامه
│ │ ├── Router.php # مسیریاب
│ │ ├── Request.php # مدیریت درخواست
│ │ └── Response.php # مدیریت پاسخ
│ ├── Controllers/ # کنترلرها
│ │ └── UserController.php
│ ├── Models/ # مدلها
│ │ └── User.php
│ └── Config/ # تنظیمات
│ └── Database.php
├── .htaccess # تنظیمات Apache
└── composer.json # مدیریت وابستگیها
🚀 نقطه ورود (Bootstrap) – index.php
فایل index.php در پوشه public نقطه شروع تمام درخواستها است. این فایل مسئول بارگذاری اولیه، مسیریابی و ارسال پاسخ است.
index.php
PHP
8.2
<?php
// ۱. فعالسازی نمایش خطاها (فقط در محیط توسعه)
error_reporting(E_ALL);
ini_set('display_errors', '1');
// ۲. تنظیم هدر JSON برای تمام پاسخها
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
// ۳. مدیریت درخواست OPTIONS (Preflight)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
// ۴. بارگذاری فایلهای مورد نیاز
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/UserController.php';
// ۵. ایجاد نمونه از مسیریاب و تنظیم مسیرها
$router = new Router();
// تعریف مسیرها
$router->add('GET', '/users', 'UserController', 'index');
$router->add('GET', '/users/{id}', 'UserController', 'show');
$router->add('POST', '/users', 'UserController', 'store');
$router->add('PUT', '/users/{id}', 'UserController', 'update');
$router->add('DELETE', '/users/{id}', 'UserController', 'delete');
// ۶. پردازش درخواست
$request = new Request();
$response = $router->dispatch($request);
// ۷. ارسال پاسخ
http_response_code($response->statusCode);
echo json_encode($response->body, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
💡 نکته مهم:
مدیریت درخواست OPTIONS برای CORS بسیار حیاتی است. مرورگرها قبل از درخواستهای غیرساده (مثل POST با JSON)، یک درخواست OPTIONS ارسال میکنند تا مطمئن شوند سرور اجازه درخواست را میدهد.
🗺️ مسیریاب (Router)
مسیریاب مسئول تطبیق URL درخواستی با کنترلر و متد مناسب است. این یکی از مهمترین بخشهای هر API است.
Router.php
PHP
8.2
<?php
class Router
{
private $routes = [];
public function add($method, $path, $controller, $action)
{
$this->routes[] = [
'method' => $method,
'path' => $path,
'controller' => $controller,
'action' => $action
];
}
public function dispatch($request)
{
$method = $request->getMethod();
$uri = $request->getUri();
foreach ($this->routes as $route) {
// تبدیل مسیر به الگوی regex با پشتیبانی از {id}
$pattern = preg_replace('/\{([a-zA-Z]+)\}/', '([^/]+)', $route['path']);
$pattern = '#^' . $pattern . '$#';
if ($route['method'] === $method && preg_match($pattern, $uri, $matches)) {
// استخراج پارامترها (مثلاً id)
array_shift($matches);
$params = $matches;
// فراخوانی کنترلر
$controllerName = $route['controller'];
$actionName = $route['action'];
$controller = new $controllerName();
return $controller->$actionName($request, $params);
}
}
// مسیر پیدا نشد
return new Response(404, ['error' => 'Endpoint not found']);
}
}
📨 مدیریت درخواست (Request)
کلاس Request مسئول دریافت و پردازش دادههای ورودی از کلاینت است.
Request.php
PHP
8.2
<?php
class Request
{
private $method;
private $uri;
private $body;
public function __construct()
{
$this->method = $_SERVER['REQUEST_METHOD'];
$this->uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$this->parseBody();
}
private function parseBody()
{
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (strpos($contentType, 'application/json') !== false) {
$input = file_get_contents('php://input');
$this->body = json_decode($input, true) ?? [];
} elseif ($this->method === 'POST') {
$this->body = $_POST;
} else {
$this->body = [];
}
}
public function getMethod()
{
return $this->method;
}
public function getUri()
{
return $this->uri;
}
public function getBody()
{
return $this->body;
}
public function input($key, $default = null)
{
return $this->body[$key] ?? $default;
}
public function validate($rules)
{
$errors = [];
foreach ($rules as $field => $ruleSet) {
$rulesList = explode('|', $ruleSet);
$value = $this->input($field);
foreach ($rulesList as $rule) {
if ($rule === 'required' && empty($value)) {
$errors[$field][] = "The {$field} field is required";
}
if (strpos($rule, 'min:') === 0) {
$min = explode(':', $rule)[1];
if (strlen($value) < $min) {
$errors[$field][] = "The {$field} must be at least {$min} characters";
}
}
if ($rule === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
$errors[$field][] = "The {$field} must be a valid email address";
}
}
}
if (!empty($errors)) {
return $errors;
}
return true;
}
}
📤 مدیریت پاسخ (Response)
کلاس Response مسئول ساختاردهی پاسخهای API است.
Response.php
PHP
8.2
<?php
class Response
{
public $statusCode;
public $body;
public function __construct($statusCode = 200, $body = [])
{
$this->statusCode = $statusCode;
$this->body = $body;
}
public static function success($data = [], $message = 'Success')
{
return new self(200, [
'status' => 'success',
'message' => $message,
'data' => $data
]);
}
public static function error($message = 'Error', $statusCode = 400, $errors = [])
{
return new self($statusCode, [
'status' => 'error',
'message' => $message,
'errors' => $errors
]);
}
public static function notFound($message = 'Resource not found')
{
return new self(404, [
'status' => 'error',
'message' => $message
]);
}
public static function created($data = [], $message = 'Resource created successfully')
{
return new self(201, [
'status' => 'success',
'message' => $message,
'data' => $data
]);
}
public static function noContent()
{
return new self(204, []);
}
}
🎮 کنترلر کاربران (UserController)
کنترلر مسئول پردازش منطق کسبوکار و ارتباط با مدل داده است.
UserController.php
PHP
8.2
<?php
class UserController
{
private $userModel;
public function __construct()
{
require_once __DIR__ . '/../Models/User.php';
$this->userModel = new User();
}
// GET /users - دریافت لیست کاربران
public function index($request)
{
$users = $this->userModel->getAll();
return Response::success($users);
}
// GET /users/{id} - دریافت یک کاربر
public function show($request, $params)
{
$id = $params[0] ?? null;
if (!$id) {
return Response::error('User ID is required', 400);
}
$user = $this->userModel->find($id);
if (!$user) {
return Response::notFound('User not found');
}
return Response::success($user);
}
// POST /users - ایجاد کاربر جدید
public function store($request)
{
$data = $request->getBody();
// اعتبارسنجی
$validation = $request->validate([
'name' => 'required|min:3',
'email' => 'required|email'
]);
if ($validation !== true) {
return Response::error('Validation failed', 422, $validation);
}
$user = $this->userModel->create($data);
if (!$user) {
return Response::error('Failed to create user', 500);
}
return Response::created($user);
}
// PUT /users/{id} - بروزرسانی کامل کاربر
public function update($request, $params)
{
$id = $params[0] ?? null;
if (!$id) {
return Response::error('User ID is required', 400);
}
$existingUser = $this->userModel->find($id);
if (!$existingUser) {
return Response::notFound('User not found');
}
$data = $request->getBody();
// اعتبارسنجی
$validation = $request->validate([
'name' => 'min:3',
'email' => 'email'
]);
if ($validation !== true) {
return Response::error('Validation failed', 422, $validation);
}
$user = $this->userModel->update($id, $data);
if (!$user) {
return Response::error('Failed to update user', 500);
}
return Response::success($user, 'User updated successfully');
}
// DELETE /users/{id} - حذف کاربر
public function delete($request, $params)
{
$id = $params[0] ?? null;
if (!$id) {
return Response::error('User ID is required', 400);
}
$existingUser = $this->userModel->find($id);
if (!$existingUser) {
return Response::notFound('User not found');
}
$deleted = $this->userModel->delete($id);
if (!$deleted) {
return Response::error('Failed to delete user', 500);
}
return Response::noContent();
}
}
💾 مدل کاربران (User Model)
مدل مسئول ارتباط با پایگاه داده و انجام عملیات CRUD است. (در این درس از آرایه برای ذخیره داده استفاده میکنیم تا روی مفاهیم اصلی تمرکز کنیم. در درس بعدی به دیتابیس متصل خواهیم شد.)
User.php
PHP
8.2
<?php
class User
{
private $users = [];
private $nextId = 1;
public function __construct()
{
// دادههای نمونه
$this->users = [
['id' => 1, 'name' => 'یونس', 'email' => 'younes@example.com'],
['id' => 2, 'name' => 'مریم', 'email' => 'maryam@example.com'],
];
$this->nextId = 3;
}
public function getAll()
{
return $this->users;
}
public function find($id)
{
foreach ($this->users as $user) {
if ($user['id'] == $id) {
return $user;
}
}
return null;
}
public function create($data)
{
$user = [
'id' => $this->nextId++,
'name' => $data['name'] ?? '',
'email' => $data['email'] ?? '',
];
$this->users[] = $user;
return $user;
}
public function update($id, $data)
{
foreach ($this->users as $index => $user) {
if ($user['id'] == $id) {
$this->users[$index]['name'] = $data['name'] ?? $user['name'];
$this->users[$index]['email'] = $data['email'] ?? $user['email'];
return $this->users[$index];
}
}
return null;
}
public function delete($id)
{
foreach ($this->users as $index => $user) {
if ($user['id'] == $id) {
array_splice($this->users, $index, 1);
return true;
}
}
return false;
}
}
🔴 خطاهای رایج در پیادهسازی API با PHP خام
- ❌ اشتباه: فراموش کردن مدیریت CORS و Preflight (OPTIONS) درخواستها.
- ✅ درست: همیشه درخواستهای OPTIONS را قبل از هر چیزی پردازش کن.
- ❌ اشتباه: استفاده از
require_onceدر همه جا بدون مدیریت خطا. - ✅ درست: از Autoloader (مثلاً Composer) برای بارگذاری خودکار کلاسها استفاده کن.
- ❌ اشتباه: برگرداندن پیامهای خطای داخلی سرور به کلاینت.
- ✅ درست: فقط پیامهای کاربرپسند و کدهای وضعیت مناسب را برگردان.
- ❌ اشتباه: استفاده از
$_POSTبرای دریافت دادههای JSON. - ✅ درست: از
file_get_contents('php://input')وjson_decodeاستفاده کن.
💎 نکات کلیدی درس
- ✅ ساختار پروژه API باید سازماندهی شده باشد (Core، Controllers، Models).
- ✅ مسیریاب مسئول تطبیق URL با کنترلر مناسب است.
- ✅ کلاس Request دادههای ورودی را پردازش و اعتبارسنجی میکند.
- ✅ کلاس Response پاسخهای استاندارد JSON تولید میکند.
- ✅ کنترلر منطق کسبوکار را مدیریت میکند.
- ✅ مدل مسئول ارتباط با دیتابیس و عملیات CRUD است.
- ✅ CORS برای دسترسی از دامنههای دیگر ضروری است.
🛠️ پروژه عملی درس دوم
مهمان عزیز، حالا نوبت توست که یک API واقعی بسازی!
مسئله:
یک API برای مدیریت محصولات (Products) پیادهسازی کن. این API باید شامل موارد زیر باشد:
- ✅ دریافت لیست تمام محصولات (GET /products)
- ✅ دریافت یک محصول خاص (GET /products/{id})
- ✅ ایجاد محصول جدید (POST /products)
- ✅ بروزرسانی کامل محصول (PUT /products/{id})
- ✅ حذف محصول (DELETE /products/{id})
هر محصول باید شامل: id، name، price، category و stock باشد.
🧪 راهحل پروژه (پاسخ)
برای پیادهسازی این پروژه، کافی است الگوی کاربران را دنبال کنی:
- 1️⃣ کلاس
ProductControllerرا با متدهای مشابه ایجاد کن. - 2️⃣ کلاس
Productرا برای مدیریت دادهها بساز. - 3️⃣ مسیرهای جدید را در
index.phpثبت کن. - 4️⃣ اعتبارسنجی مناسب (مثلاً price باید عدد باشد) اضافه کن.
📝 تمرینهای عملی
🧪 تمرین ۱ (ساده):
با استفاده از Postman، تمام Endpointهای API کاربران را تست کن. برای هر Endpoint، نتیجه را یادداشت کن.
🧪 تمرین ۲ (متوسط):
یک Middleware ساده برای لاگ کردن تمام درخواستها اضافه کن. هر درخواست باید در یک فایل لاگ با تاریخ، متد و آدرس ذخیره شود.
🧪 تمرین ۳ (چالشی):
قابلیت جستجو و فیلتر به API کاربران اضافه کن. کاربران باید بتوانند با پارامترهای ?name=...&email=... جستجو کنند. همچنین صفحهبندی با ?page=1&limit=10 پیادهسازی کن.
🏁 جمعبندی درس
مهمان عزیز، در این درس یاد گرفتی:
- ✅ چگونه یک ساختار پروژه API حرفهای طراحی کنی.
- ✅ یک مسیریاب (Router) دستی برای مدیریت URLها پیادهسازی کنی.
- ✅ کلاس Request برای پردازش و اعتبارسنجی دادهها.
- ✅ کلاس Response برای تولید پاسخهای استاندارد JSON.
- ✅ کنترلرها و مدلها را برای پیادهسازی منطق کسبوکار استفاده کنی.
- ✅ تمام عملیات CRUD را در یک API پیادهسازی کنی.
- ✅ CORS را به درستی مدیریت کنی.
حالا تو یک API کاربردی با PHP خام داری! در درس بعدی، به دیتابیس متصل میشویم و دادهها را به صورت دائمی ذخیره میکنیم. آمادهای؟ 🚀
🗺️ نقشه راه دوره: شما اینجا هستید!
برای اینکه بدانی دقیقاً کجای مسیر هستی و چه درسهایی در انتظار توست، به جدول زیر نگاه کن. درس فعلی با رنگ متفاوت مشخص شده است.
| درس | عنوان درس | آنچه یاد میگیرید |
|---|---|---|
| ۱ | مفاهیم پایه 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 فروشگاهی | پروژه کامل فروشگاه اینترنتی با تمام قابلیتها |




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