Архитектура ядра PHP-приложения с index.php
Ядро PHP-приложения: index.php и точка входа
Ядро приложения на PHP (index.php app core) это центральный точка входа, которая обрабатывает все HTTP-запросы. Оно загружает зависимости, выполняет маршрутизацию, инициализирует контроллеры и возвращает ответ. Без правильно организованного ядра приложение становится трудно поддерживаемым и небезопасным.
Как создать эффективное ядро с автоматической загрузкой и маршрутизацией?
Наиболее эффективным решением является использование фронт-контроллера на основе Composer и простого роутера. Файл index.php (или public/index.php) отвечает за:
- Подключение автозагрузчика Composer (vendor/autoload.php)
- Загрузка переменных окружения (например, с помощью vlucas/phpdotenv)
- Создание контейнера зависимостей для сервисов
- Выполнение роутинга и диспетчеризация запроса к нужному контроллеру
- Обработка исключений и возврат ответа
<?
// public/index.php
require_once __DIR__ . '/../vendor/autoload.php';
use Dotenv\Dotenv;
use App\Core\Router;
use App\Core\Container;
use App\Core\Request;
use App\Core\Response;
// Загрузка переменных окружения
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();
// Создание контейнера
$container = new Container();
$container->set(Request::class, new Request());
// Создание роутера
$router = new Router($container);
// Определение маршрутов
$router->add('GET', '/', 'HomeController@index');
$router->add('POST', '/submit', 'SubmitController@store');
// Обработка запроса
try {
$response = $router->dispatch($container->get(Request::class));
$response->send();
} catch (\Exception $e) {
http_response_code(500);
echo 'Ошибка: ' . htmlspecialchars($e->getMessage());
}Index php app core (ядро приложения на php)
Пояснение:
- Автозагрузчик Composer загружает все классы, установленные через composer install.
- Класс Router хранит маршруты и выбирает подходящий контроллер.
- Контейнер управляет зависимостями, чтобы контроллеры получали готовые сервисы (например, базу данных, логирование).
- Блок try-catch перехватывает любые исключения и возвращает ошибку 500.
Типичные ошибки:
- Пути до vendor/autoload.php указаны неверно. Решение: использовать __DIR__ и проверять существование файла.
- Классы роутера или контроллера не найдены. Решение: проверить composer.json и выполнить composer dump-autoload.
- Забыли обработать маршруты с параметрами. Решение: добавить поддержку регулярных выражений в роутер.
Как реализовать минимальный index.php без ООП (для обучения)?
Простой процедурный подход: все маршруты проверяются через $_SERVER['REQUEST_URI'] и $_SERVER['REQUEST_METHOD']. Этот вариант подходит для крошечных проектов или демонстрации.
<?
// index.php (процедурный)
require 'functions.php';
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
if ($uri === '/' && $method === 'GET') {
echo 'Главная страница';
} elseif ($uri === '/about' && $method === 'GET') {
include 'views/about.php';
} else {
http_response_code(404);
echo 'Страница не найдена';
}Цель: быстрый старт без зависимостей. Использование: только для одностраничников или прототипов.
Проблемы: отсутствие автозагрузки, дублирование кода, сложность поддержки при росте. Решение: перейти на ООП с роутером.
Как использовать микрофреймворк, чтобы не писать ядро самому?
Микрофреймворки (Slim, Flight, Lumen) предоставляют готовое ядро с роутингом, middleware и DI-контейнером. Пример на Slim 4:
<?
// public/index.php с Slim
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function ($request, $response) {
$response->getBody()->write('Hello, world!');
return $response;
});
$app->run();Цель: экономия времени на разработку ядра, если приложение средней сложности. Использование: API, простые веб-приложения.
Проблема: микрофреймворк может не подойти для очень специфических архитектурных требований. Решение: кастомизировать его через middleware или перейти на Symfony/Laravel.
Как построить ядро с использованием PSR-7 и middleware?
Для совместимости и гибкости применяют стандарт PSR-7 для сообщений HTTP и промежуточные обработчики (middleware). Ядро становится цепочкой обработчиков.
<?
// public/index.php с PSR-7 (Zend Diactoros, Relay)
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Relay\Relay;
require __DIR__ . '/../vendor/autoload.php';
$request = ServerRequestFactory::fromGlobals();
$middlewareQueue = [
new App\Middleware\ErrorHandler(),
new App\Middleware\RouterMiddleware(),
function ($request, $handler) {
return $handler->handle($request);
}
];
$relay = new Relay($middlewareQueue);
$response = $relay->handle($request);
(new SapiEmitter())->emit($response);Цель: высокая модульность, возможность подключать/отключать обработчики (логирование, авторизация, кэширование). Использование: крупные приложения с гибкой архитектурой.
Ошибка: неправильная очередь middleware (например, роутер выполняется до проверки CORS). Решение: тщательно продумывать порядок.
Расширенные примеры ядра PHP-приложения
Маршрутизация с поддержкой параметров и регулярных выражений
<?
// App\Core\Router.php
class Router
{
private array $routes = [];
public function add(string $method, string $pattern, callable|string $handler): void
{
// Преобразуем {param} в регулярное выражение
$pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '(?P<$1>[^/]+)', $pattern);
$this->routes[] = [
'method' => $method,
'pattern' => '#^' . $pattern . '$#',
'handler' => $handler
];
}
public function dispatch(string $method, string $uri): mixed
{
foreach ($this->routes as $route) {
if ($route['method'] !== $method) continue;
if (preg_match($route['pattern'], $uri, $matches)) {
$params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
if (is_callable($route['handler'])) {
return call_user_func($route['handler'], $params);
}
// Если handler строка вида 'Controller@method'
[$controller, $method] = explode('@', $route['handler']);
$instance = new $controller();
return $instance->$method($params);
}
}
http_response_code(404);
return '404 Not Found';
}
}<?
// index.php с использованием
$router->add('GET', '/user/{id}', function($params) {
return 'User ID: ' . $params['id'];
});
$router->add('GET', '/post/{slug}', 'PostController@show');
$result = $router->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
echo $result;Пример вызова /user/42: User ID: 42
Проблема: если в маршруте используются символы, не соответствующие шаблону (например, точка в slug), регулярное выражение их не захватит. Решение: расширить шаблон на [a-zA-Z0-9_\-]+ или применить более гибкую библиотеку FastRoute.
Обработка исключений и кастомные страницы ошибок
<?
// public/index.php с обработчиком ошибок
set_error_handler(function($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function($exception) {
$code = $exception->getCode() ?: 500;
http_response_code($code);
if (in_array($code, [404, 403, 500])) {
include __DIR__ . "/../views/errors/{$code}.php";
} else {
echo 'Ошибка
' . htmlspecialchars($exception->getMessage()) . '
';
}
});
// Дальше обычный код app core<?
// views/errors/500.php
?>
500 - Внутренняя ошибка
Внутренняя ошибка сервера
Пожалуйста, попробуйте позже.
Пояснение: перехватываются все PHP-ошибки и исключения, возвращается дружелюбная страница.
Реализация простого DI-контейнера для ядра
<?
// App\Core\Container.php
class Container
{
private array $services = [];
private array $definitions = [];
public function set(string $id, object|callable $definition): void
{
if (is_callable($definition)) {
$this->definitions[$id] = $definition;
} else {
$this->services[$id] = $definition;
}
}
public function get(string $id): object
{
if (isset($this->services[$id])) {
return $this->services[$id];
}
if (isset($this->definitions[$id])) {
$instance = call_user_func($this->definitions[$id], $this);
$this->services[$id] = $instance;
return $instance;
}
throw new \InvalidArgumentException("Service $id not found.");
}
}
// Использование в роутере
$container->set(DB::class, function($c) {
return new DB($_ENV['DB_HOST']);
});
$router->add('GET', '/users', function() use ($container) {
$db = $container->get(DB::class);
$users = $db->query('SELECT * FROM users');
return json_encode($users);
});Результат: JSON с данными пользователей.
Middleware для логирования времени выполнения
<?
// App\Middleware\PerformanceMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
class PerformanceMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$start = microtime(true);
$response = $handler->handle($request);
$duration = microtime(true) - $start;
$response = $response->withHeader('X-Execution-Time', round($duration, 4) . ' sec');
return $response;
}
}<?
// index.php (Slim)
$app->add(new App\Middleware\PerformanceMiddleware());
$app->run();Пример заголовка ответа: X-Execution-Time: 0.0123 sec