Symfony, маршрутизация, контроллеры, Twig, формы и валидация
Лабораторная работа 3. PHP. Основы Symfony: проект, маршруты, контроллеры и HTTP
Работа выполняется с ИИ и(или) ИИ-агентом. Здесь уже важно освоить PHP на минимально необходимом уровне для дальнейшей работы с Symfony. PHP похож на другие Си-подобные языки, типа Java, и осваивается по аналогии. Также важно, понимать для чего и как используются технологии из этой ЛР.
Часть 1.
Вводный практикум: основы языка PHP перед Symfony
Перед началом работы с Symfony выполните короткий практикум по PHP. Его задача — познакомиться с теми конструкциями языка, которые далее будут постоянно использоваться во фреймворке: переменными, массивами, условиями, циклами, функциями, классами, объектами, интерфейсами, пространствами имен, типами и PHP Attributes.
Symfony 7.4 требует PHP 8.2 или новее. В рамках курса используйте современный синтаксис PHP 8.
0.1. Подготовка песочницы PHP
До создания Symfony-проекта создайте в корне учебного репозитория отдельный каталог:
sandbox/ └── php-basics/
Этот каталог является учебной песочницей. Код из него не является частью Symfony-приложения и нужен только для знакомства с PHP.
Проверьте установленную версию PHP:
php -v
Проверьте возможность выполнения PHP из командной строки:
php -r "echo 'PHP ', PHP_VERSION, PHP_EOL;"
PHP-файл начинается с:
<?php
В учебных примерах далее используйте:
<?php declare(strict_types=1);
PHP является динамически типизированным языком, но позволяет указывать типы параметров, возвращаемых значений и свойств. strict_types=1 включает строгую проверку скалярных типов для соответствующих вызовов из данного файла.
0.2. Процедурный PHP: переменные, массивы, условия, циклы и функции
Создайте файл:
sandbox/php-basics/01-procedural.php
Добавьте код:
<?php declare(strict_types=1); $appName = 'BizFlow'; $maxCustomers = 100; $isDebug = true; echo $appName . PHP_EOL; $customers = [ [ 'id' => 1, 'name' => 'Иван Иванов', 'email' => 'ivan@example.com', ], [ 'id' => 2, 'name' => 'Анна Смирнова', 'email' => 'anna@example.com', ], ]; foreach ($customers as $customer) { echo $customer['id'] . ': ' . $customer['name'] . PHP_EOL; }
Здесь используются основные конструкции PHP:
$name = 'BizFlow'; // string $count = 10; // int $price = 125.50; // float $active = true; // bool $value = null; // null
Переменные PHP начинаются со знака $.
Массив может содержать набор значений:
$statuses = ['new', 'paid', 'cancelled'];
или пары ключ => значение:
$customer = [ 'id' => 1, 'name' => 'Иван Иванов', 'email' => 'ivan@example.com', ]; echo $customer['email'];
Условие:
if ($customer['id'] > 0) { echo 'Корректный id'; } else { echo 'Некорректный id'; }
Цикл:
foreach ($customers as $customer) { echo $customer['name'] . PHP_EOL; }
Функция:
function findCustomerById(array $customers, int $id): ?array { foreach ($customers as $customer) { if ($customer['id'] === $id) { return $customer; } } return null; }
Используйте функцию:
$customer = findCustomerById($customers, 2); if ($customer === null) { echo 'Клиент не найден' . PHP_EOL; } else { echo $customer['name'] . PHP_EOL; }
Обратите внимание на типы:
array int ?array
?array означает: функция возвращает либо array, либо null.
Запустите файл:
php sandbox/php-basics/01-procedural.php
Проверьте синтаксис отдельно:
php -l sandbox/php-basics/01-procedural.php
0.3. PHP в веб-приложении: HTTP-запрос и HTML-ответ
Создайте файл:
sandbox/php-basics/02-web.php
<?php declare(strict_types=1); $name = trim((string) ($_GET['name'] ?? 'Гость')); ?> <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>BizFlow PHP Sandbox</title> </head> <body> <h1>BizFlow</h1> <p> Здравствуйте, <?= htmlspecialchars( $name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8' ) ?> </p> </body> </html>
Запустите встроенный сервер PHP:
php -S 127.0.0.1:8080 -t sandbox/php-basics
Откройте в браузере:
http://127.0.0.1:8080/02-web.php?name=Anna
Измените значение параметра name и посмотрите результат.
В данном примере:
$_GET['name']
получает параметр из HTTP-запроса, а:
htmlspecialchars(...)
подготавливает строку для безопасного вывода как текст внутри HTML.
Встроенный сервер php -S предназначен для разработки и учебных экспериментов и не должен использоваться как production-сервер.
0.4. Объектно-ориентированный PHP: класс и объект
Symfony-приложение состоит преимущественно из PHP-классов. Поэтому перед переходом к Symfony необходимо понимать базовую объектную модель PHP.
Создайте:
sandbox/php-basics/03-oop.php
<?php declare(strict_types=1); final class Customer { private static int $createdCount = 0; public function __construct( private int $id, private string $name, private string $email, ) { self::$createdCount++; } public function getId(): int { return $this->id; } public function getName(): string { return $this->name; } public function getEmail(): string { return $this->email; } public static function getCreatedCount(): int { return self::$createdCount; } } $customer = new Customer( 1, 'Иван Иванов', 'ivan@example.com' ); echo $customer->getName() . PHP_EOL; echo $customer->getEmail() . PHP_EOL; echo 'Создано объектов: ' . Customer::getCreatedCount() . PHP_EOL;
Здесь:
class Customer
объявляет класс;
new Customer(...)
создает объект;
__construct(...)
является конструктором;
private
ограничивает прямой доступ к свойству;
public
делает метод доступным извне;
$this
обозначает текущий объект;
static
обозначает член класса, принадлежащий самому классу, а не отдельному объекту.
Синтаксис объявления свойств непосредственно в параметрах конструктора называется constructor property promotion и поддерживается PHP начиная с PHP 8.
Запустите:
php sandbox/php-basics/03-oop.php
0.5. Интерфейс и зависимость от контракта
Интерфейс определяет, какие методы должен предоставлять объект, не определяя их реализацию.
Добавьте в учебный файл:
interface CustomerFormatterInterface { public function format(Customer $customer): string; } final class CustomerFormatter implements CustomerFormatterInterface { public function format(Customer $customer): string { return sprintf( '%d: %s <%s>', $customer->getId(), $customer->getName(), $customer->getEmail(), ); } } $formatter = new CustomerFormatter(); echo $formatter->format($customer) . PHP_EOL;
Ключевые слова:
interface implements
позволяют отделить контракт от конкретной реализации.
Этот принцип далее будет использоваться при работе с сервисами и Dependency Injection в Symfony.
0.6. Пространства имен и автозагрузка
В реальном приложении классы не размещаются все вместе в одном файле. Они разделяются по файлам и пространствам имен.
Например:
<?php declare(strict_types=1); namespace App\Service; final class CustomerService { }
Другой класс может импортировать его:
use App\Service\CustomerService;
После создания Symfony-проекта вы увидите классы вида:
namespace App\Controller;
и импорты:
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;
В современных PHP-проектах загрузкой классов обычно занимается Composer. Для этого используются правила автозагрузки, в частности PSR-4: пространство имен сопоставляется с каталогом исходного кода.
Поэтому вручную подключать каждый класс конструкциями вида:
require 'Customer.php'; require 'CustomerService.php'; require 'CustomerController.php';
в Symfony-приложении не требуется.
0.7. PHP Attributes: подготовка к маршрутам Symfony
PHP поддерживает attributes — метаданные, которые записываются в конструкции:
#[...]
Например, далее в Symfony вы будете использовать:
#[Route('/customers', methods: ['GET'])]
и:
#[Route( '/customers/{id}', methods: ['GET'], requirements: ['id' => '\d+'] )]
Это синтаксис PHP Attributes, а Route — класс Symfony, который интерпретирует эти метаданные как описание маршрута.
Не запускайте эти два фрагмента отдельно в песочнице: класс Route станет доступен после установки Symfony.
0.8. Как «чистый PHP» превращается в Symfony
При переходе к Symfony знакомые операции PHP получают объектные абстракции:
| Чистый PHP | В Symfony |
|---|---|
$_GET
|
объект Request
|
$_SERVER
|
объект Request
|
| анализ URL вручную | Routing |
header()
|
Response
|
http_response_code()
|
статус объекта Response
|
json_encode() + header()
|
JsonResponse
|
| PHP-функция обработки запроса | метод контроллера |
require / include классов
|
Composer autoloading |
создание объектов через new повсюду
|
Dependency Injection |
| условная проверка URL |
#[Route(...)]
|
Контроллер Symfony является PHP callable, который получает данные запроса и формирует объект Response. Symfony рекомендует PHP Attributes как удобный способ объявления маршрутов.
Например, после установки Symfony обычный PHP-класс контроллера будет выглядеть так:
<?php declare(strict_types=1); namespace App\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; final class HealthController { #[Route('/health', name: 'health', methods: ['GET'])] public function health(): Response { return new Response('OK'); } }
То есть Symfony не заменяет язык PHP: Symfony-приложение остается PHP-программой, а фреймворк предоставляет готовые классы и механизмы для маршрутизации, HTTP, конфигурации, сервисов и других задач веб-приложения.
0.9. Минимальная самопроверка перед Symfony
Перед переходом к основной части лабораторной работы студент должен уметь:
-
создать и запустить
.php-файл; - объявить переменную и массив;
-
использовать
ifиforeach; - написать функцию с типизированными параметрами и возвращаемым значением;
- получить GET-параметр;
- создать класс, конструктор, свойства и методы;
-
создать объект оператором
new; -
различать
public,privateиstatic; -
объяснить назначение
interface,namespace,useи Composer autoloading; -
распознать синтаксис PHP Attribute
#[...].
Выполните:
php -l sandbox/php-basics/01-procedural.php php -l sandbox/php-basics/02-web.php php -l sandbox/php-basics/03-oop.php php sandbox/php-basics/01-procedural.php php sandbox/php-basics/03-oop.php
После успешного выполнения переходите к созданию проекта Symfony.
Часть 2.
Цель: создать базовое приложение BizFlow на Symfony.
Symfony 7.4 LTS используется как зафиксированная версия курса. Symfony поддерживает создание проекта через Composer или Symfony CLI; маршруты могут описываться атрибутами и конфигурацией, а контроллер получает запрос и возвращает Response. [10]
Задание
-
В корне репозитория создайте:
composer create-project symfony/skeleton:"7.4.*" bizflow
cd bizflow
composer require webapp
-
Проверьте:
php bin/console about
-
Запустите приложение.
-
Реализуйте маршруты:
GET /
GET /health
GET /dashboard
GET /customers
GET /customers/{id}
GET /orders
GET /orders/{id}
-
Для маршрутов используйте PHP attributes:
#[Route(...)]
-
Один дополнительный маршрут реализуйте через YAML.
-
Один дополнительный маршрут реализуйте через PHP-конфигурацию маршрутов.
-
Для:
/customers/{id}
задайте требование:
id — положительное целое число
-
/health должен возвращать JSON.
-
/customers/{id} для отсутствующего клиента должен возвращать:
404
-
Для некорректного запроса используйте соответствующий HTTP-код.
-
Создайте контроллеры:
HomeController
HealthController
CustomerController
OrderController
-
Не размещайте бизнес-логику в контроллерах.
-
Выполните:
php bin/console debug:router
-
Сохраните результат в:
docs/lab03-routes.txt
-
Изучите объект:
Request
-
Создайте маршрут:
GET /request-info
возвращающий:
{
"method": "...",
"path": "...",
"query": {},
"userAgent": "..."
}
-
Не возвращайте чувствительные HTTP-заголовки.
-
Установите тестовый пакет:
composer require --dev symfony/test-pack
-
Напишите functional tests для:
/
/health
/customers/1
/unknown-url
Symfony рекомендует symfony/test-pack для интеграции PHPUnit и функционального тестирования приложений. [4]
Обязательный запрос ИИ-агенту
Ты — senior Symfony developer.
Проанализируй текущий чистый Symfony-проект.
Нужно реализовать:
/
/health
/dashboard
/customers
/customers/{id}
/orders
/orders/{id}
Требования:
контроллеры должны быть тонкими;
маршруты — преимущественно PHP attributes;
для id использовать route requirement;
HTTP status codes должны быть корректными.
Сначала составь план файлов.
Затем реализуй по шагам.
После каждой группы изменений запускай подходящую Symfony-команду проверки.
Самопроверка
php bin/console about
php bin/console debug:router
php bin/console lint:yaml config
php bin/phpunit
Критерии приемки
[ ] Symfony 7.4 запускается
[ ] реализованы требуемые маршруты
[ ] используются route attributes
[ ] имеется YAML route
[ ] имеется PHP-configured route
[ ] route parameters валидируются
[ ] контроллеры возвращают корректные Response
[ ] работают 404
[ ] functional tests проходят
[ ] имеется AI log
Symfony Routing предоставляет сопоставление URL с контроллерами и команду debug:router; контроллер в Symfony является PHP callable, принимающим данные запроса и формирующим объект ответа. [11]
