Skip to content

Symfony

Symfony 是一个成熟、模块化的 PHP 框架,由 SensioLabs(现 Symfony SAS)开发维护。它不仅是众多 PHP 框架的底层基础(包括 Laravel),也是一个可以直接使用的全栈框架。Symfony 的组件化设计使得开发者可以选择单独使用任何 Symfony 组件,而无需引入整个框架。本节将全面介绍 Symfony 框架的核心概念和使用方法。

前置知识

阅读本节前,建议先了解:

基础概念

Symfony 的特点

特性说明
组件化40+ 可独立使用的组件
灵活性不强制使用特定组件
企业级长期支持版本(LTS)
性能高度优化,支持 HTTP/2 和 HTTP/3
社区大型活跃社区
文档业界最佳文档之一
测试完善的测试工具链
标准化PHP-FIG 规范的主要推动者

版本对应关系

SymfonyPHP 最低版本支持状态
Symfony 7PHP 8.2当前版本
Symfony 6.4PHP 8.1LTS(长期支持)
Symfony 5.4PHP 8.0维护中

详细说明

项目创建

bash
# 方式一:创建完整 Web 应用
composer create-project symfony/website-skeleton myapp

# 方式二:创建微服务(推荐 API 项目)
composer create-project symfony/skeleton myapp

# 方式三:使用 Symfony CLI
symfony new myapp --full    # 完整版
symfony new myapp --webapp   # Web 应用
symfony new myapp            # 微服务

目录结构

symfony-project/
├── config/            # 配置文件(YAML)
│   ├── packages/
│   ├── routes/
│   └── services.yaml
├── src/               # PHP 源代码
│   ├── Controller/
│   ├── Entity/
│   ├── Repository/
│   ├── Service/
│   └── Kernel.php
├── templates/         # Twig 模板
├── public/             # Web 根目录
│   └── index.php
├── bin/                # CLI 命令
│   └── console
├── migrations/        # Doctrine 迁移
├── tests/              # 测试文件
└── composer.json

路由与控制器

php
<?php
// src/Controller/UserController.php
declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class UserController extends AbstractController
{
    #[Route('/api/users', methods: ['GET'])]
    public function index(UserRepository $repository): JsonResponse
    {
        $users = $repository->findAll();

        return $this->json([
            'data' => $users,
        ]);
    }

    #[Route('/api/users/{id}', methods: ['GET'])]
    public function show(int $id, UserRepository $repository): JsonResponse
    {
        $user = $repository->find($id);

        if ($user === null) {
            throw $this->createNotFoundException('User not found');
        }

        return $this->json($user);
    }

    #[Route('/api/users', methods: ['POST'])]
    public function store(
        Request $request,
        EntityManagerInterface $em
    ): JsonResponse {
        $data = json_decode($request->getContent(), true);

        $user = new User();
        $user->setName($data['name'] ?? '');
        $user->setEmail($data['email'] ?? '');

        $em->persist($user);
        $em->flush();

        return $this->json($user, 201);
    }
}

服务容器与依赖注入

yaml
# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    App\Controller\:
        resource: '../src/Controller/'
        tags: ['controller.service_arguments']
php
<?php
declare(strict_types=1);

namespace App\Service;

use Psr\Log\LoggerInterface;
use App\Repository\UserRepository;

class UserService
{
    // 自动注入(通过 autowire)
    public function __construct(
        private readonly UserRepository $userRepository,
        private readonly LoggerInterface $logger
    ) {}

    public function findActiveUsers(): array
    {
        $this->logger->info('Finding active users');
        return $this->userRepository->findBy(['isActive' => true]);
    }
}

Doctrine ORM

php
<?php
declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'users')]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private string $name = '';

    #[ORM\Column(length: 255, unique: true)]
    private string $email = '';

    #[ORM\Column]
    private bool $isActive = true;

    #[ORM\Column]
    private ?\DateTimeImmutable $createdAt = null;

    // Getter 和 Setter
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;
        return $this;
    }

    public function isActive(): bool
    {
        return $this->isActive;
    }
}

Symfony 控制台命令

bash
# 常用 Symfony 命令
php bin/console server:run          # 启动开发服务器
php bin/console make:controller     # 创建控制器
php bin/console make:entity         # 创建 Doctrine 实体
php bin/console make:migration      # 创建迁移
php bin/console doctrine:migrations:migrate  # 运行迁移
php bin/console debug:router        # 调试路由
php bin/console cache:clear          # 清除缓存
php bin/console lint:container       # 检查服务容器

Twig 模板引擎

html
{# templates/user/index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}User List{% endblock %}

{% block body %}
    <h1>Users</h1>
    
    <table>
        {% for user in users %}
            <tr>
                <td>{{ user.id }}</td>
                <td>{{ user.name }}</td>
                <td>{{ user.email }}</td>
                <td>{{ user.isActive ? 'Active' : 'Inactive' }}</td>
            </tr>
        {% else %}
            <tr><td colspan="4">No users found.</td></tr>
        {% endfor %}
    </table>
    
    {{ paginate(users) }}
{% endblock %}

验证器

php
<?php
// src/Entity/User.php
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity]
class User
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 2, max: 255)]
    #[ORM\Column(length: 255)]
    private string $name = '';

    #[Assert\NotBlank]
    #[Assert\Email]
    #[Assert\Length(max: 255)]
    #[ORM\Column(length: 255)]
    private string $email = '';

    #[Assert\NotBlank]
    #[Assert\Length(min: 8)]
    #[ORM\Column]
    private string $password = '';
}
php
<?php
// 在控制器中使用验证
use Symfony\Component\Validator\Validator\ValidatorInterface;

public function store(Request $request, ValidatorInterface $validator): JsonResponse
{
    $user = new User();
    $user->setName($request->get('name'));
    $user->setEmail($request->get('email'));

    $errors = $validator->validate($user);
    if (count($errors) > 0) {
        $errorMessages = [];
        foreach ($errors as $error) {
            $errorMessages[$error->getPropertyPath()] = $error->getMessage();
        }
        return $this->json(['errors' => $errorMessages], 422);
    }

    $em->persist($user);
    $em->flush();

    return $this->json($user, 201);
}

表单组件

php
<?php
// src/Form/UserType.php
namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'label' => 'Name',
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Length(['min' => 2, 'max' => 255]),
                ],
            ])
            ->add('email', EmailType::class)
            ->add('password', PasswordType::class);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

实战示例

场景一:独立使用 Symfony 组件

bash
# 不使用框架,仅使用 Symfony 组件
composer require symfony/http-foundation
composer require symfony/routing
composer require symfony/console
composer require symfony/var-dumper
php
<?php
declare(strict_types=1);

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Matcher\UrlMatcher;

// 使用 HttpFoundation 组件
$request = Request::createFromGlobals();
$name = $request->query->get('name', 'World');

$response = new Response("Hello, {$name}!", 200, [
    'Content-Type' => 'text/plain',
]);
$response->send();

场景二:配置 Symfony 安全

yaml
# config/packages/security.yaml
security:
    enable_authenticator_manager: true
    providers:
        app_users:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt))/
            security: false

        main:
            lazy: true
            provider: app_users
            json_login:
                check_path: /api/login
                username_path: email
                password_path: password

    access_control:
        - { path: ^/api/login, roles: PUBLIC_ACCESS }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

注意事项

1. 环境变量

bash
# .env 文件
APP_ENV=dev
APP_SECRET=your-secret-key

DATABASE_URL=mysql://root:password@127.0.0.1:3306/myapp

2. 缓存管理

bash
# 开发环境:自动更新缓存
# 生产环境:手动管理缓存
php bin/console cache:clear        # 清除所有缓存
php bin/console cache:warmup       # 预热缓存
php bin/console cache:pool:clear  # 清除指定缓存池

最佳实践

1. 使用 Flex 管理依赖

bash
# Symfony Flex 自动配置包
composer require symfony/twig-bundle    # 自动配置 Twig
composer require symfony/mailer          # 自动配置 Mailer
composer require symfony/validator       # 自动配置 Validator

Symfony vs Laravel

  • Symfony:更灵活、更底层、更适合大型企业项目
  • Laravel:更易上手、更优雅的语法、更快的开发速度
  • 两者可以互补:Laravel 使用了多个 Symfony 组件

下一节

继续学习:ThinkPHP 框架 — 了解国内最流行的 PHP 框架。

参考链接