Веб-сервисы на PHP: от простого REST к сложным интеграциям
Создание веб-сервисов на PHP
Веб-сервисы позволяют приложениям обмениваться данными через HTTP. PHP предоставляет множество инструментов для их построения – от встроенных функций до мощных фреймворков. Рассмотрим наиболее эффективные и альтернативные подходы.
Основное решение: микрофреймворк Slim для REST API
Slim – легковесный фреймворк, идеально подходящий для быстрой разработки RESTful веб-сервисов. Он предоставляет маршрутизацию, middleware и удобную работу с PSR-7 сообщениями.
Как создать простой REST API на Slim, возвращающий JSON?
composer require slim/slim:^4
composer require slim/httpApp path php (работа с путями файлов в php)
Создаём файл public/index.php:
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/api/hello/{name}', function (Request $request, Response $response, array $args) {
$name = htmlspecialchars($args['name']);
$data = ['message' => "Hello, $name"];
$response->getBody()->write(json_encode($data));
return $response->withHeader('Content-Type', 'application/json');
});
$app->run();App php domain (работа с доменами в php)
Запустите встроенный сервер: php -S localhost:8080 -t public. Теперь GET-запрос к /api/hello/Max вернёт {"message":"Hello, Max"}.
Типичные проблемы и решения:
- Ошибка 404 при запросе – убедитесь, что сервер настроен на директорию
publicи все запросы направляются на index.php (через .htaccess или nginx config). - Проблемы с CORS – добавьте middleware для заголовка Access-Control-Allow-Origin. Slim поддерживает
slim/cors. - Неправильная сериализация – используйте
json_encodeс флагамиJSON_UNESCAPED_UNICODEдля поддержки UTF-8.
Вариант 1: Веб-сервис на чистом PHP без фреймворков
Как обработать URL и вернуть JSON, используя только встроенные возможности PHP?
<?php
// index.php
header('Content-Type: application/json; charset=utf-8');
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Простейшая маршрутизация
if ($path === '/api/status' && $method === 'GET') {
echo json_encode(['status' => 'OK', 'time' => time()]);
} else {
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
}Http user agent php (получение user-agent в php)
Возможные сложности:
- Отсутствие нормализации входных данных – параметры из
$_GETи$_POSTтребуют фильтрации черезfilter_inputилиhtmlspecialchars. - Обработка тела запроса – для PUT/PATCH данные приходят в
php://input, их нужно разобрать черезfile_get_contents('php://input')и декодировать из JSON. - Сложность поддержки – при росте числа эндпоинтов код становится нечитаемым; рекомендуется хотя бы выделить маршруты в отдельный файл.
Вариант 2: Веб-сервис на Symfony (HTTP Foundation + Routing)
Как использовать компоненты Symfony для создания более структурированного API без полного фреймворка?
composer require symfony/http-foundation symfony/runtime
# composer.json
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}Config app php (конфигурация php приложения)
Создаём контроллер src/Controller/ApiController.php:
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class ApiController
{
public function status(Request $request): JsonResponse
{
return new JsonResponse(['status' => 'running']);
}
}создание скриптов php (создание скриптов php)
Ошибки конфигурации:
- Автозагрузка не работает – выполните
composer dump-autoloadи проверьте пространство имён. - Маршруты не обрабатываются – вручную подключите Router или используйте Symfony FrameworkBundle для полноценного приложения. В простом варианте придётся самостоятельно сопоставлять URI.
Вариант 3: Веб-сервис на Laravel (или Lumen)
Как построить полнофункциональный REST API с помощью Laravel, включая аутентификацию и Eloquent?
composer create-project laravel/laravel example-api
php artisan make:controller Api/UserController --resource --apiApp php route (маршрутизация в php приложении)
Пример контроллера:
<?php
namespace App\Http\Controllers\Api;
use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
public function index()
{
return User::all();
}
public function show($id)
{
$user = User::find($id);
if (!$user) return response()->json(['error' => 'User not found'], 404);
return $user;
}
}Php create html (создание html в php)
Добавьте маршрут в routes/api.php:
Route::apiResource('users', 'Api\UserController');Default php app (настройки по умолчанию в php приложении)
Распространённые трудности:
- Медленная работа при большом количестве запросов – используйте кеширование (Redis) и пагинацию.
- Неверные заголовки ответа – Laravel автоматически добавляет
Content-Type: application/jsonпри возврате массивов или коллекций черезresponse()->json(). - Проблемы с аутентификацией – в Laravel 11 используется Sanctum или Passport; для простого API достаточно токенов в заголовке Bearer.
Вариант 4: SOAP-сервис на PHP
Как реализовать классический SOAP веб-сервис с WSDL?
<?php
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
}
$options = ['uri' => 'http://example.com/soap'];
$server = new SoapServer(null, $options);
$server->setClass('Calculator');
$server->handle();Но для корректной генерации WSDL рекомендуется использовать библиотеки типа php-soap-ext или WSDL-генераторы.
Сложности SOAP:
- Громоздкая структура – требуется XML-сериализация, что медленнее JSON.
- Трудности с отладкой – сообщения об ошибках обычно скрыты; используйте
SoapServer::setPersistence()и логи. - Сложность с WSDL – создание вручную требует времени; альтернатива – фреймворки вроде
zend-soap.
Расширенные примеры создания PHP веб-сервисов
Пример 1: REST API с аутентификацией JWT на Slim
Установите пакет firebase/php-jwt и создайте middleware.
composer require firebase/php-jwt<?php
// middleware/AuthMiddleware.php
namespace App\Middleware;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Slim\Psr7\Response;
class AuthMiddleware
{
private $secretKey = 'your-secret-key-here';
public function __invoke(Request $request, Handler $handler): Response
{
$authHeader = $request->getHeaderLine('Authorization');
if (empty($authHeader) || !str_starts_with($authHeader, 'Bearer ')) {
$response = new Response();
$response->getBody()->write(json_encode(['error' => 'Unauthorized']));
return $response->withStatus(401)->withHeader('Content-Type', 'application/json');
}
$token = substr($authHeader, 7);
try {
$decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
$request = $request->withAttribute('user_id', $decoded->sub);
return $handler->handle($request);
} catch (\Exception $e) {
$response = new Response();
$response->getBody()->write(json_encode(['error' => 'Token invalid']));
return $response->withStatus(401)->withHeader('Content-Type', 'application/json');
}
}
}Примените middleware к защищённым маршрутам:
$app->get('/api/profile', function ($request, $response) {
$userId = $request->getAttribute('user_id');
// ... получение данных пользователя
$data = ['id' => $userId, 'name' => 'John'];
$response->getBody()->write(json_encode($data));
return $response->withHeader('Content-Type', 'application/json');
})->add(new \App\Middleware\AuthMiddleware());Результат GET-запроса с неправильным токеном:
{"error":"Token invalid"}Пример 2: Обработка входящих JSON-данных в чистом PHP
Создайте эндпоинт для создания пользователя:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/api/users') {
$input = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
$name = isset($input['name']) ? trim($input['name']) : '';
if (empty($name)) {
http_response_code(422);
echo json_encode(['error' => 'Name is required']);
exit;
}
// Сохранение в БД (пример с PDO)
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (:name)');
$stmt->execute(['name' => $name]);
$id = $pdo->lastInsertId();
http_response_code(201);
echo json_encode(['id' => $id, 'name' => $name]);
}Отправка запроса через curl:
curl -X POST http://localhost:8080/api/users -H "Content-Type: application/json" -d '{"name":"Alice"}'
{"id":1,"name":"Alice"}Пример 3: Веб-сервис с передачей файлов через multipart/form-data
Используем микрофреймворк Slim для загрузки изображения.
$app->post('/api/upload', function (Request $request, Response $response) {
$uploadedFiles = $request->getUploadedFiles();
if (empty($uploadedFiles['avatar'])) {
$response->getBody()->write(json_encode(['error' => 'No file uploaded']));
return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
}
$avatar = $uploadedFiles['avatar'];
if ($avatar->getError() !== UPLOAD_ERR_OK) {
$response->getBody()->write(json_encode(['error' => 'Upload failed']));
return $response->withStatus(500)->withHeader('Content-Type', 'application/json');
}
$newFilename = uniqid() . '-' . $avatar->getClientFilename();
$avatar->moveTo('uploads/' . $newFilename);
$response->getBody()->write(json_encode(['filename' => $newFilename]));
return $response->withHeader('Content-Type', 'application/json');
});Проверка через curl:
curl -X POST -F "avatar=@/path/to/photo.jpg" http://localhost:8080/api/upload
{"filename":"657f3a2b1c0d4-photo.jpg"}Пример 4: Веб-сервис с графическим интерфейсом (RAML/OpenAPI)
Генерация документации с помощью swagger-php.
composer require zircote/swagger-phpДобавьте аннотации к контроллеру:
/**
* @OA\Get(
* path="/api/status",
* @OA\Response(response="200", description="Returns server status")
* )
*/
public function status() { ... }Генерация спецификации:
vendor/bin/openapi --output public/swagger.json src/