Лабораторная работа III. PHP. Основы Symfony: проект, маршруты, контроллеры и HTTP

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

Перед переходом к основной части лабораторной работы студент должен уметь:

  1. создать и запустить .php-файл;
  2. объявить переменную и массив;
  3. использовать if и foreach;
  4. написать функцию с типизированными параметрами и возвращаемым значением;
  5. получить GET-параметр;
  6. создать класс, конструктор, свойства и методы;
  7. создать объект оператором new;
  8. различать public, private и static;
  9. объяснить назначение interface, namespace, use и Composer autoloading;
  10. распознать синтаксис 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]

Задание

  1. В корне репозитория создайте:

composer create-project symfony/skeleton:"7.4.*" bizflow
cd bizflow
composer require webapp

  1. Проверьте:

php bin/console about

  1. Запустите приложение.

  2. Реализуйте маршруты:

GET /
GET /health
GET /dashboard
GET /customers
GET /customers/{id}
GET /orders
GET /orders/{id}

  1. Для маршрутов используйте PHP attributes:

#[Route(...)]

  1. Один дополнительный маршрут реализуйте через YAML.

  2. Один дополнительный маршрут реализуйте через PHP-конфигурацию маршрутов.

  3. Для:

/customers/{id}

задайте требование:

id — положительное целое число

  1. /health должен возвращать JSON.

  2. /customers/{id} для отсутствующего клиента должен возвращать:

404

  1. Для некорректного запроса используйте соответствующий HTTP-код.

  2. Создайте контроллеры:

HomeController
HealthController
CustomerController
OrderController

  1. Не размещайте бизнес-логику в контроллерах.

  2. Выполните:

php bin/console debug:router

  1. Сохраните результат в:

docs/lab03-routes.txt

  1. Изучите объект:

Request

  1. Создайте маршрут:

GET /request-info

возвращающий:

{
"method": "...",
"path": "...",
"query": {},
"userAgent": "..."
}

  1. Не возвращайте чувствительные HTTP-заголовки.

  2. Установите тестовый пакет:

composer require --dev symfony/test-pack

  1. Напишите 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]

Автор: к.п.н., Румянцев Сергей Александрович, доцент Финансового университета при Правительстве РФ; доцент ОЧУВО Международного инновационного университета; Консалтинг, управление разработкой ПО; системный и бизнес анализ; менеджмент; аналитиз данных; управление ИТ. Телефон для связи +79269444818 (мессенджеры)   Короткая ссылка:

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

Перед переходом к основной части лабораторной работы студент должен уметь:

  1. создать и запустить .php-файл;
  2. объявить переменную и массив;
  3. использовать if и foreach;
  4. написать функцию с типизированными параметрами и возвращаемым значением;
  5. получить GET-параметр;
  6. создать класс, конструктор, свойства и методы;
  7. создать объект оператором new;
  8. различать public, private и static;
  9. объяснить назначение interface, namespace, use и Composer autoloading;
  10. распознать синтаксис 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]

Задание

  1. В корне репозитория создайте:

composer create-project symfony/skeleton:"7.4.*" bizflow
cd bizflow
composer require webapp

  1. Проверьте:

php bin/console about

  1. Запустите приложение.

  2. Реализуйте маршруты:

GET /
GET /health
GET /dashboard
GET /customers
GET /customers/{id}
GET /orders
GET /orders/{id}

  1. Для маршрутов используйте PHP attributes:

#[Route(...)]

  1. Один дополнительный маршрут реализуйте через YAML.

  2. Один дополнительный маршрут реализуйте через PHP-конфигурацию маршрутов.

  3. Для:

/customers/{id}

задайте требование:

id — положительное целое число

  1. /health должен возвращать JSON.

  2. /customers/{id} для отсутствующего клиента должен возвращать:

404

  1. Для некорректного запроса используйте соответствующий HTTP-код.

  2. Создайте контроллеры:

HomeController
HealthController
CustomerController
OrderController

  1. Не размещайте бизнес-логику в контроллерах.

  2. Выполните:

php bin/console debug:router

  1. Сохраните результат в:

docs/lab03-routes.txt

  1. Изучите объект:

Request

  1. Создайте маршрут:

GET /request-info

возвращающий:

{
"method": "...",
"path": "...",
"query": {},
"userAgent": "..."
}

  1. Не возвращайте чувствительные HTTP-заголовки.

  2. Установите тестовый пакет:

composer require --dev symfony/test-pack

  1. Напишите 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]

https://webprogr.ru/~jVukE
Короткая ссылка на новость:https://webprogr.ru/~jVukE


Последние новости

Лабораторная работа III. PHP. Основы Symfony: проект, маршруты, контроллеры и HTTP

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

Перед переходом к основной части лабораторной работы студент должен уметь:

  1. создать и запустить .php-файл;
  2. объявить переменную и массив;
  3. использовать if и foreach;
  4. написать функцию с типизированными параметрами и возвращаемым значением;
  5. получить GET-параметр;
  6. создать класс, конструктор, свойства и методы;
  7. создать объект оператором new;
  8. различать public, private и static;
  9. объяснить назначение interface, namespace, use и Composer autoloading;
  10. распознать синтаксис 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]

Задание

  1. В корне репозитория создайте:

composer create-project symfony/skeleton:"7.4.*" bizflow
cd bizflow
composer require webapp

  1. Проверьте:

php bin/console about

  1. Запустите приложение.

  2. Реализуйте маршруты:

GET /
GET /health
GET /dashboard
GET /customers
GET /customers/{id}
GET /orders
GET /orders/{id}

  1. Для маршрутов используйте PHP attributes:

#[Route(...)]

  1. Один дополнительный маршрут реализуйте через YAML.

  2. Один дополнительный маршрут реализуйте через PHP-конфигурацию маршрутов.

  3. Для:

/customers/{id}

задайте требование:

id — положительное целое число

  1. /health должен возвращать JSON.

  2. /customers/{id} для отсутствующего клиента должен возвращать:

404

  1. Для некорректного запроса используйте соответствующий HTTP-код.

  2. Создайте контроллеры:

HomeController
HealthController
CustomerController
OrderController

  1. Не размещайте бизнес-логику в контроллерах.

  2. Выполните:

php bin/console debug:router

  1. Сохраните результат в:

docs/lab03-routes.txt

  1. Изучите объект:

Request

  1. Создайте маршрут:

GET /request-info

возвращающий:

{
"method": "...",
"path": "...",
"query": {},
"userAgent": "..."
}

  1. Не возвращайте чувствительные HTTP-заголовки.

  2. Установите тестовый пакет:

composer require --dev symfony/test-pack

  1. Напишите 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]

Рейтинг@Mail.ru