Разработка PHP приложения: методы, примеры, рекомендации

Раздел: Программирование на 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.

Создание PHP-приложения - comments

En
создание приложения php (php)