Разработка PHP приложения: методы, примеры, рекомендации
Создание PHP-приложения включает выбор архитектуры, организацию кода и использование инструментов. В этой статье рассматриваются различные подходы с примерами и разбором трудностей.
Основные подходы к созданию PHP-приложений
Как организовать код в объектно-ориентированном стиле с автозагрузкой классов?
Основной рекомендуемый подход - использование ООП с автозагрузкой через Composer. Это обеспечивает чистую архитектуру, переиспользование кода и лёгкое тестирование.
Структура проекта:
project/
├── public/
│ └── index.php
├── src/
│ ├── Controllers/
│ │ └── HomeController.php
│ ├── Models/
│ │ └── User.php
│ └── Router.php
├── vendor/
├── composer.json
├── .htaccess
Файл composer.json для автозагрузки:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
После composer dump-autoload классы загружаются автоматически.
<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
use App\Router;
use App\Controllers\HomeController;
$router = new Router();
$router->get('/', [HomeController::class, 'index']);
$router->dispatch();
Проблемы и решения:
Частая ошибка - неправильное указание namespace в файле класса. Например, класс HomeController должен находиться в папке src/Controllers/ и иметь namespace App\Controllers. Иначе автозагрузка не сработает. Решение: проверять соответствие пути и namespace.
Как создать приложение в процедурном стиле?
Процедурный подход подходит для мелких скриптов. Весь код располагается в одном или нескольких файлах без классов. Пример простой маршрутизации:
<?php
// index.php
$uri = $_SERVER['REQUEST_URI'];
if ($uri === '/') {
echo 'Главная страница';
} elseif ($uri === '/about') {
echo 'О нас';
} else {
http_response_code(404);
echo '404';
}
При разрастании проекта такой код становится неудобным - дублирование, сложность поддержки.
Проблема: отсутствие структуры ведет к спагетти-коду. Решение: при увеличении объёма лучше перейти на ООП или использовать готовый микро-фреймворк.
Как использовать микро-фреймворк Slim для быстрого прототипирования?
Slim позволяет быстро настроить маршруты, middleware и внедрение зависимостей. Пример:
<?php
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function ($request, $response) {
$response->getBody()->write('Главная');
return $response;
});
$app->run();
Микро-фреймворк избавляет от написания базовой инфраструктуры.
Ошибка: забывают настроить веб-сервер для перенаправления всех запросов на index.php. В .htaccess для Apache:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
Какие преимущества даёт полноценный фреймворк Laravel?
Laravel предоставляет ORM, миграции, шаблонизатор Blade, artisan-команды. Создание контроллера:
php artisan make:controller UserController --resource
Миграция для таблицы users:
php artisan make:migration create_users_table
Laravel решает большинство рутинных задач, но требует изучения и ресурсов.
Проблема: большой размер фреймворка и сложность настройки окружения. Решение: использовать Homestead или Docker.
Расширенный пример - приложение «Список задач» (ToDo) с использованием ООП и автозагрузки. Реализованы CRUD операции через PDO.
// Структура:
// src/Controllers/TaskController.php
// src/Models/Task.php
// src/Router.php
// public/index.php
// .htaccess
// public/index.php
<?php
require __DIR__ . '/../vendor/autoload.php';
use App\Router;
use App\Controllers\TaskController;
$router = new Router();
$router->get('/tasks', [TaskController::class, 'index']);
$router->post('/tasks', [TaskController::class, 'store']);
$router->get('/tasks/create', [TaskController::class, 'create']);
$router->dispatch();
?>
// src/Router.php
<?php
namespace App;
class Router
{
private array $routes = [];
public function get(string $path, array $action): void
{
$this->routes['GET'][$path] = $action;
}
public function post(string $path, array $action): void
{
$this->routes['POST'][$path] = $action;
}
public function dispatch(): void
{
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (isset($this->routes[$method][$uri])) {
[$class, $methodAction] = $this->routes[$method][$uri];
$controller = new $class();
echo $controller->$methodAction();
} else {
http_response_code(404);
echo '404 Not Found';
}
}
}
// src/Models/Task.php
<?php
namespace App\Models;
use PDO;
class Task
{
private PDO $pdo;
public function __construct()
{
$this->pdo = new PDO('mysql:host=localhost;dbname=todo', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
}
public function getAll(): array
{
$stmt = $this->pdo->query('SELECT * FROM tasks ORDER BY id DESC');
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function create(string $title): void
{
$stmt = $this->pdo->prepare('INSERT INTO tasks (title) VALUES (:title)');
$stmt->execute(['title' => $title]);
}
}
// src/Controllers/TaskController.php
<?php
namespace App\Controllers;
use App\Models\Task;
class TaskController
{
public function index(): string
{
$task = new Task();
$tasks = $task->getAll();
ob_start();
include __DIR__ . '/../Views/tasks/index.php';
return ob_get_clean();
}
public function create(): string
{
ob_start();
include __DIR__ . '/../Views/tasks/create.php';
return ob_get_clean();
}
public function store(): void
{
$title = $_POST['title'] ?? '';
if ($title) {
$task = new Task();
$task->create(htmlspecialchars($title));
}
header('Location: /tasks');
exit;
}
}
// src/Views/tasks/index.php (фрагмент)
<!DOCTYPE html>
<html>
<head><title>Список задач</title></head>
<body>
<h2>Задачи</h2>
<ul>
<?php foreach ($tasks as $task): ?>
<li><?= $task['title'] ?></li>
<?php endforeach; ?>
</ul>
<a href="/tasks/create">Добавить задачу</a>
</body>
</html>
Результат работы приложения: при переходе на /tasks отображается список задач из БД. Форма на /tasks/create позволяет добавить новую задачу. После отправки происходит редирект обратно на список.
Страница /tasks: - Задача 1 - Задача 2 [Добавить задачу]
Типичные ошибки:
1. Ошибка соединения с БД - неверные параметры в PDO. Решение: проверять имя хоста, имя пользователя и пароль. 2. Пути в require не соответствуют реальному расположению файлов. Решение: использовать константу __DIR__. 3. Автозагрузка не работает, если забыть выполнить composer dump-autoload после добавления новых классов. 4. Отсутствие обработки исключений в PDO - приводит к фатальной ошибке. Рекомендуется оборачивать вызовы в try-catch.