Современные приложения редко живут изолированно. API — это мост между вашим бэкендом, фронтендом, мобильными приложениями и сторонними сервисами. Разбираем production-ready подходы к созданию API на PHP.
Основы RESTful API
REST — архитектурный стиль, который использует HTTP методы для операций с ресурсами.
- GET — получение ресурса (список или один элемент)
- POST — создание нового ресурса
- PUT — полное обновление ресурса
- PATCH — частичное обновление
- DELETE — удаление ресурса
Правильные URL: GET /users, GET /users/123, POST /users, PUT /users/123, DELETE /users/123.
Структура современного PHP API
├── index.php // точка входа, роутинг
├── .htaccess // перенаправление всех запросов на index.php
├── config/
│ └── database.php // подключение к БД
├── src/
│ ├── Controllers/ // обработка запросов
│ ├── Models/ // работа с БД
│ ├── Middleware/ // аутентификация, CORS, rate limiting
│ └── Helpers/ // вспомогательные функции
└── vendor/ // composer зависимости
Базовый роутер (без фреймворков)
// index.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = str_replace('/api', '', $path);
$segments = explode('/', trim($path, '/'));
$resource = $segments[0] ?? null;
$id = $segments[1] ?? null;
switch ($resource) {
case 'users':
$controller = new UserController();
if ($method === 'GET' && $id) {
$controller->show($id);
} elseif ($method === 'GET') {
$controller->index();
} elseif ($method === 'POST') {
$controller->store();
} elseif ($method === 'PUT' && $id) {
$controller->update($id);
} elseif ($method === 'DELETE' && $id) {
$controller->destroy($id);
}
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
}
JWT аутентификация
JSON Web Token — современный стандарт авторизации. Токен содержит информацию о пользователе и подпись.
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class AuthMiddleware {
private static $secret = 'your-secret-key-here';
public static function generateToken($userId, $email) {
$payload = [
'user_id' => $userId,
'email' => $email,
'iat' => time(),
'exp' => time() + 3600 // 1 час
];
return JWT::encode($payload, self::$secret, 'HS256');
}
public static function authenticate() {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
$token = str_replace('Bearer ', '', $authHeader);
try {
$decoded = JWT::decode($token, new Key(self::$secret, 'HS256'));
return (array) $decoded;
} catch (Exception $e) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
}
}
Обработка ошибок и валидация
class UserController {
public function store() {
$input = json_decode(file_get_contents('php://input'), true);
$errors = [];
if (empty($input['email'])) {
$errors['email'] = 'Email is required';
}
if (strlen($input['password'] ?? '') < 8) {
$errors['password'] = 'Password must be at least 8 characters';
}
if (!empty($errors)) {
http_response_code(422);
echo json_encode(['errors' => $errors]);
return;
}
// Сохранение в БД...
}
}
Rate Limiting (защита от DDoS и брутфорса)
function rateLimit($key, $maxRequests = 60, $timeWindow = 60) {
$ip = $_SERVER['REMOTE_ADDR'];
$cacheKey = "rate_limit_{$key}_{$ip}";
// Используем APC или Redis. Для примера — файл
$file = sys_get_temp_dir() . '/' . md5($cacheKey);
$data = file_exists($file) ? json_decode(file_get_contents($file), true) : ['count' => 0, 'reset' => time() + $timeWindow];
if (time() > $data['reset']) {
$data = ['count' => 0, 'reset' => time() + $timeWindow];
}
$data['count']++;
file_put_contents($file, json_encode($data));
if ($data['count'] > $maxRequests) {
http_response_code(429);
echo json_encode(['error' => 'Too Many Requests']);
exit;
}
}
Кэширование ответов API
function cacheGet($key, $ttl = 300, $callback) {
$cacheFile = sys_get_temp_dir() . '/api_cache_' . md5($key);
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $ttl) {
$data = file_get_contents($cacheFile);
echo $data;
return;
}
$result = $callback();
$json = json_encode($result);
file_put_contents($cacheFile, $json);
echo $json;
}
// Использование
cacheGet('users_list', 300, function() {
// Тяжёлый запрос к БД
return User::getAll();
});
CORS (Cross-Origin Resource Sharing)
Без правильных CORS-заголовков ваш API не сможет вызывать фронтенд с другого домена.
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: https://my-frontend.com');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
http_response_code(200);
exit;
}
Версионирование API
Со временем API меняется. Не ломайте старых клиентов — используйте версионирование.
- Через URL: /api/v1/users, /api/v2/users
- Через заголовок: Accept: application/vnd.myapi.v1+json
Заключение: Современный API — это больше, чем просто обработка запросов. Это безопасность, кэширование, rate limiting, документация (OpenAPI/Swagger) и мониторинг. Начните с малого: реализуйте CRUD для одной сущности, добавьте JWT-аутентификацию и rate limiting. Постепенно усложняйте. Production-ready API требует внимания к деталям, но результат стоит усилий.