Страницы и действия в PHP: от простого include до front controller
Разработка сайта на PHP требует четкой организации страниц и обработки действий (action). Подходы к этому могут различаться в зависимости от сложности проекта. Рассмотрим несколько вариантов, от простых до масштабируемых, с примерами кода и разбором типичных проблем.
Варианты организации страниц в PHP
Единая точка входа (Front Controller) с роутером
Как сделать обработку всех запросов через один файл для централизованного управления?
Наиболее эффективным решением считается использование единой точки входа (обычно index.php). Через файл .htaccess (для Apache) все запросы перенаправляются на этот скрипт. Внутри него роутер анализирует URL и направляет запрос к соответствующему обработчику. Это обеспечивает единую логику авторизации, логирования, обработки ошибок и гибкую маршрутизацию.
// index.php - Front Controller
require_once 'router.php';
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$result = route($uri);
echo $result;
Index php page category (категория страницы в php)
// .htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Cat page php page (страница категории на php)
Типичные ошибки: неправильные правила RewriteRule (зацикливание), отсутствие RewriteCond для игнорирования существующих файлов, проблемы с относительными путями внутри скрипта.
Простой include по параметру
Как реализовать страницы с минимальными затратами, передавая имя страницы через GET-параметр?
Самый простой способ - взять параметр page из URL и подключить соответствующий файл. Такой подход часто используется на ранних этапах обучения.
$page = $_GET['page'] ?? 'home';
include 'pages/' . $page . '.php';
Index php главная (главная страница на php)
Основная опасность - уязвимость Local File Inclusion (LFI), если не проверять входящее значение. Например, page=../../../etc/passwd приведет к чтению системных файлов. Решение: использовать белый список допустимых страниц или валидацию на основе массива.
Маршрутизация через switch/case
Как обрабатывать разные страницы без использования внешних библиотек, оставаясь в рамках одного файла?
Метод предполагает разбор URL с последующим выбором блока кода через конструкцию switch. Подходит для небольших сайтов с фиксированным набором страниц.
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
switch ($uri) {
case '/':
$content = 'home';
break;
case '/about':
$content = 'about';
break;
case '/contact':
$content = 'contact';
break;
default:
http_response_code(404);
$content = '404';
}
include 'templates/' . $content . '.php';
Html home php content (контент home в php)
Проблема: при росте числа страниц код становится громоздким, сложно поддерживать динамические пути (например, /post/123).
Роутер с регулярными выражениями
Как создавать гибкие пути с параметрами (например, /post/123) без фреймворка?
Регулярные выражения позволяют извлекать переменные сегменты URL и передавать их в обработчик. Это расширяет возможности switch/case и подходит для средних проектов.
$routes = [
'/' => 'home',
'/about' => 'about',
'/post/(\d+)' => 'showPost',
];
foreach ($routes as $pattern => $handler) {
$regex = '#^' . $pattern . '$#';
if (preg_match($regex, $uri, $matches)) {
array_shift($matches); // удаляем полное совпадение
$content = call_user_func_array($handler, $matches);
break;
}
}
Home php product (продукт home в php)
Ошибки: неправильное экранирование в регулярных выражениях, производительность при большом количестве маршрутов. Рекомендуется кэшировать скомпилированные регулярные выражения.
Обработка действий (action) через единый обработчик POST
Как организовать обработку форм и AJAX-запросов отдельно от отображения страниц?
Выделение обработчика действий (action handler) позволяет разделить логику получения данных и их отображения. Часто используется в комбинации с Front Controller: если запрос содержит параметр action, вызывается соответствующий метод.
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
// вызов соответствующего обработчика
call_user_func('handle_' . $action);
}
Home php table (таблица home в php)
Проблемы: снижение безопасности при динамическом вызове функций (можно вызвать любую функцию, передав её имя). Решение - строгий маппинг действий.
Архитектура MVC с использованием фреймворка
Как обеспечить масштабируемость и разделение ответственности для крупных проектов?
Большинство современных PHP-фреймворков (Laravel, Symfony) реализуют паттерн MVC. Страницы и действия представлены контроллерами, которые возвращают ответы или обрабатывают запросы. Это стандарт для индустрии.
// Пример маршрута в Laravel
Route::get('/user/{id}', [UserController::class, 'show']);
Route::post('/user/store', [UserController::class, 'store']);
Недостатки: излишняя сложность для простых сайтов, необходимость изучения специфики фреймворка, увеличение времени загрузки при неправильной настройке.
Расширенные примеры и результаты
Пример 1: Front Controller с поддержкой вложенных маршрутов
Исходный код (router.php):
<?php
function route($uri) {
$routes = [
'/' => function() { return 'Главная страница'; },
'/about' => function() { return 'О сайте'; },
'/blog' => function() { return 'Блог'; },
];
# поддержка параметров /blog/123
$uri = rtrim($uri, '/');
foreach ($routes as $pattern => $handler) {
$regex = '#^' . $pattern . '$#';
if (preg_match($regex, $uri)) {
return $handler();
}
}
# проверка на /blog/{id}
if (preg_match('#^/blog/(\d+)$#', $uri, $matches)) {
return 'Статья с ID: ' . $matches[1];
}
http_response_code(404);
return 'Страница не найдена';
}
Результаты вызовов:
/ -> Главная страница /about -> О сайте /blog -> Блог /blog/42 -> Статья с ID: 42 /unknown -> Страница не найдена (404)
Пример 2: Обработка действий (action) через карту методов
Безопасная реализация обработки POST-запросов с маппингом действий:
$allowedActions = [
'login' => 'handleLogin',
'register' => 'handleRegister',
'logout' => 'handleLogout',
];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
if (array_key_exists($action, $allowedActions)) {
$handler = $allowedActions[$action];
$response = $handler($_POST);
// JSON-ответ для AJAX
header('Content-Type: application/json');
echo json_encode($response);
exit;
} else {
http_response_code(400);
echo json_encode(['error' => 'Неизвестное действие']);
exit;
}
}
function handleLogin($postData) {
// логика входа
return ['status' => 'ok', 'message' => 'Вход выполнен'];
}
function handleRegister($postData) {
// логика регистрации
return ['status' => 'ok', 'message' => 'Регистрация прошла успешно'];
}
function handleLogout($postData) {
// логика выхода
return ['status' => 'ok', 'message' => 'Вы вышли из системы'];
}
Пример 3: Роутер с именованными параметрами и middleware
Расширенный роутер с поддержкой {param} в шаблонах и предварительной проверкой прав доступа:
class Router
{
private $routes = [];
public function add($pattern, $callback, $middleware = null)
{
// преобразуем {param} в (?P<param>[^/]+)
$regex = preg_replace('/\{([a-zA-Z_]+)\}/', '(?P<$1>[^/]+)', $pattern);
$this->routes[] = [
'regex' => '#^' . $regex . '$#',
'callback' => $callback,
'middleware' => $middleware,
];
}
public function dispatch($uri)
{
foreach ($this->routes as $route) {
if (preg_match($route['regex'], $uri, $matches)) {
// выполняем middleware если есть
if ($route['middleware']) {
$pass = call_user_func($route['middleware']);
if (!$pass) {
return 'Доступ запрещен';
}
}
// извлекаем только именованные параметры
$params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
return call_user_func($route['callback'], $params);
}
}
return '404 Not Found';
}
}
// Использование
$router = new Router();
$router->add('/user/{id}', function($params) {
return 'Пользователь ID: ' . $params['id'];
}, function() {
// проверка, авторизован ли пользователь
return isset($_SESSION['user']);
});
$router->add('/post/{slug}', function($params) {
return 'Пост: ' . $params['slug'];
});
echo $router->dispatch('/user/123');
echo $router->dispatch('/post/hello-world');
Результат работы (при наличии сессии):
Пользователь ID: 123 Пост: hello-world
Пример 4: Альтернативный подход - использование классов-контроллеров
Организация страниц через методы класса, где имя метода соответствует экшену:
class PageController
{
public function index()
{
return 'Главная';
}
public function about()
{
return 'О компании';
}
public function contact()
{
return 'Контакты';
}
public function __call($name, $arguments)
{
http_response_code(404);
return 'Страница не найдена';
}
}
$controller = new PageController();
$action = isset($_GET['action']) ? $_GET['action'] : 'index';
echo $controller->$action();
Результат для ?action=about:
О компании