Написание серверного кода на PHP: практическое руководство для разработчиков
Основы написания серверного кода на PHP
Наиболее эффективным решением для построения серверной логики на PHP является использование объектно-ориентированного подхода с разделением ответственности. Вместо работы с суперглобальными массивами напрямую, рекомендуется создать классы-обёртки для запроса (Request) и ответа (Response), а также центральный обработчик (Router). Такой подход обеспечивает тестируемость, расширяемость и соответствие современным стандартам (PSR-7, PSR-15).
Пример базовой структуры
// Request.php
class Request {
private array $data;
public function __construct() {
$this->data = array_merge($_GET, $_POST, json_decode(file_get_contents('php://input'), true) ?? []);
}
public function getParam(string $key, $default = null) {
return $this->data[$key] ?? $default;
}
public function getMethod(): string {
return $_SERVER['REQUEST_METHOD'];
}
}
// Response.php
class Response {
private int $statusCode;
private array $headers;
private mixed $body;
public function __construct(mixed $body = '', int $status = 200, array $headers = []) {
$this->body = $body;
$this->statusCode = $status;
$this->headers = $headers;
}
public function send(): void {
http_response_code($this->statusCode);
foreach ($this->headers as $name => $value) {
header("$name: $value");
}
if (is_array($this->body) || is_object($this->body)) {
header('Content-Type: application/json');
echo json_encode($this->body);
} else {
echo $this->body;
}
}
}
// index.php (точка входа)
require 'Request.php';
require 'Response.php';
$request = new Request();
$response = new Response();
if ($request->getMethod() === 'GET' && $request->getParam('action') === 'hello') {
$response = new Response(['message' => 'Hello, ' . $request->getParam('name', 'World')]);
}
$response->send();
код сервера на php (написание кода сервера на php)
Пояснение шагов
- Создаются классы Request и Response для инкапсуляции данных входящего запроса и построения ответа.
- Точка входа index.php обрабатывает все запросы (front controller).
- Анализируется метод и параметры, формируется ответ.
- Метод send() отправляет заголовки и тело.
Типичные проблемы
- Проблема: Если не обрабатывать ошибки, приложение может выдать неинформативное сообщение. Решение: Реализовать глобальный обработчик исключений через set_exception_handler.
- Проблема: Заголовки могут быть отправлены дважды. Решение: Перед вызовом send() убедиться, что не было вывода в stdout (например, через ob_start).
Как быстро обработать GET/POST запрос без фреймворка?
Процедурный подход с использованием суперглобальных массивов $_GET, $_POST и $_SERVER подходит для очень простых скриптов, где не требуется сложная маршрутизация.
// simple_form.php
$name = $_POST['name'] ?? 'гость';
echo "Привет, $name!";
Пояснение
- Данные из формы доступны через $_POST.
- Оператор ?? задаёт значение по умолчанию.
- Вывод сразу отправляется клиенту.
Типичные ошибки
- Отсутствие проверки на наличие ключей приводит к ошибке Undefined array key.
- Неверный тип данных (например, строка вместо числа) без валидации.
- Решение: использовать filter_input() или собственную валидацию.
Как запустить встроенный сервер PHP для разработки?
Встроенный сервер (CLI-сервер) позволяет тестировать приложение без установки Apache или Nginx.
# Запуск в корне проекта
php -S localhost:8000 -t public/
Параметр -t указывает корневую директорию. Все запросы направляются в index.php, если не найден статический файл.
Типичные проблемы
- Сервер перезапускается после каждого изменения кода (не горячая замена).
- Не поддерживает многопоточность.
Как реализовать маршрутизацию с использованием микрофреймворка Slim?
Микрофреймворки, такие как Slim, предоставляют удобную маршрутизацию, middleware и внедрение зависимостей.
// index.php с Slim 4
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('/hello/{name}', function (Request $request, Response $response, array $args) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
$app->run();
Пояснение
- Маршруты определяются с помощью методов get(), post() и т.д.
- Параметры из URI передаются в замыкание через $args.
- Ответ формируется через объект $response.
Типичные ошибки
- Отсутствие автозагрузки Composer: классы не будут найдены.
- Неверная версия PHP (требуется PHP 8.0+ для Slim 4).
Каждый из этих вариантов подходит для разных сценариев: процедурный код – для быстрых прототипов, встроенный сервер – для локальной отладки, микрофреймворк – для серьёзных приложений с чёткой архитектурой. Выбор зависит от масштабов проекта и требований к сопровождаемости.
Расширенные примеры серверного кода на PHP
1. Обработка JSON-запросов и формирование RESTful ответа
// api.php - универсальный обработчик REST
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Извлечение тела запроса в JSON
$body = json_decode(file_get_contents('php://input'), true);
// Простой роутинг
if ($uri === '/users' && $method === 'GET') {
header('Content-Type: application/json');
// Имитация выборки из БД
$users = [
['id' => 1, 'name' => 'Анна'],
['id' => 2, 'name' => 'Борис']
];
echo json_encode($users);
} elseif ($uri === '/users' && $method === 'POST') {
if ($body === null) {
http_response_code(400);
echo json_encode(['error' => 'Неверный JSON']);
exit;
}
// Сохранение пользователя (имитация)
http_response_code(201);
echo json_encode(['id' => 3, 'name' => $body['name'] ?? 'Неизвестный']);
} else {
http_response_code(404);
echo json_encode(['error' => 'Маршрут не найден']);
}
Пример вызова:
curl -X POST -H "Content-Type: application/json" -d '{"name":"Виктор"}' http://localhost:8000/api.php/users
Ответ: {"id":3,"name":"Виктор"}
2. Работа с базой данных через PDO (подготовленные запросы)
// db.php - подключение к MySQL и безопасный запрос
$dsn = 'mysql:host=localhost;dbname=myapp;charset=utf8mb4';
$user = 'root';
$pass = '';
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Ошибка БД: ' . $e->getMessage()]);
exit;
}
// Пример вставки с подготовленным выражением
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute([
':name' => $_POST['name'] ?? '',
':email' => filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)
]);
if ($stmt->rowCount() > 0) {
echo json_encode(['success' => true, 'last_id' => $pdo->lastInsertId()]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Не удалось вставить запись']);
}
Пример запроса:
curl -d "name=Мария&email=maria@example.com" http://localhost:8000/db.php
Результат: {"success":true,"last_id":10}
3. Обработка загрузки файлов
// upload.php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Метод не разрешён');
}
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$file = $_FILES['file'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
exit('Ошибка загрузки файла');
}
$allowedTypes = ['image/jpeg', 'image/png'];
if (!in_array($file['type'], $allowedTypes)) {
http_response_code(400);
exit('Недопустимый тип файла');
}
$filename = uniqid() . '_' . basename($file['name']);
if (move_uploaded_file($file['tmp_name'], $uploadDir . $filename)) {
echo json_encode(['file' => $filename, 'size' => $file['size']]);
} else {
http_response_code(500);
exit('Не удалось сохранить файл');
}
Вызов:
curl -F "file=@photo.png" http://localhost:8000/upload.php
Ответ: {"file":"64f3a2b1c9d7e_photo.png","size":12345}
4. Использование сессий для аутентификации
// login.php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Имитация проверки (в реальности - хеши паролей и БД)
if ($username === 'admin' && $password === 'secret') {
$_SESSION['user'] = $username;
session_regenerate_id(true);
header('Location: dashboard.php');
exit;
} else {
$error = 'Неверное имя пользователя или пароль';
}
}
// dashboard.php (проверка аутентификации)
session_start();
if (!isset($_SESSION['user'])) {
header('Location: login.php');
exit;
}
echo 'Добро пожаловать, ' . htmlspecialchars($_SESSION['user']);
Примечание
Хранение паролей в открытом виде недопустимо. Для хеширования используется password_hash() и password_verify().
5. Простой middleware для логирования запросов (на чистом PHP)
// middleware.php - обёртка обработчика
function withLogging(callable $handler): callable {
return function () use ($handler) {
$start = microtime(true);
$result = $handler();
$elapsed = microtime(true) - $start;
error_log("[" . date('Y-m-d H:i:s') . "] " .
$_SERVER['REQUEST_METHOD'] . " " .
$_SERVER['REQUEST_URI'] . " - " .
round($elapsed * 1000, 2) . "ms");
return $result;
};
}
// Использование
$handler = function () {
echo 'Hello';
};
withLogging($handler)();
В логах: [2025-04-10 12:00:00] GET /index.php - 2.34 ms
Эти примеры демонстрируют реальные сценарии, с которыми сталкивается разработчик серверной части на PHP: работа с запросами, БД, файлами, сессиями и middleware. Каждый блок можно адаптировать под конкретный проект.