Pourquoi Symfony ?

Le framework PHP le plus utilisé en entreprise.

Symfony 7 propulse Laravel, Drupal, API Platform. Ses composants réutilisables sont la base de l'écosystème PHP moderne. PHP 8.3 + Symfony 7 = stack enterprise performante.

🏗️
Composants

HttpFoundation, Routing, DI, Console, Security… utilisés par tout l'écosystème

Performance

OPcache + compilateur Symfony = démarrage < 2ms en production

🔧
Flex & Recettes

Installation automatisée via Symfony Flex — zéro configuration manuelle

Symfony 7.x PHP 8.3 PSR-4 Composer 2

POO PHP 8 — les bases indispensables

Symfony exploite à fond PHP 8 : attributs natifs, types stricts, readonly, énumérations.

Déclaration de classe moderne

<?php
declare(strict_types=1);

namespace App\Entity;

use App\Enum\StatusEnum;

class Product
{
    // Constructor Promotion (PHP 8.0)
    public function __construct(
        private readonly int    $id,
        private string          $name,
        private float           $price,
        private StatusEnum      $status = StatusEnum::Active,
    ) {}

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

    public function setName(string $name): static
    {
        $this->name = $name;
        return $this; // fluent interface
    }

    // Named arguments (PHP 8.0)
    public static function create(string $name, float $price): static
    {
        return new static(id: 0, name: $name, price: $price);
    }
}

Enums (PHP 8.1)

<?php
namespace App\Enum;

enum StatusEnum: string
{
    case Active   = 'active';
    case Inactive = 'inactive';
    case Pending  = 'pending';

    public function label(): string
    {
        return match($this) {
            self::Active   => 'Actif',
            self::Inactive => 'Inactif',
            self::Pending  => 'En attente',
        };
    }
}

// Usage
$status = StatusEnum::Active;
echo $status->value;   // 'active'
echo $status->label(); // 'Actif'

Attributs PHP 8 (Annotations remplacées)

<?php
// PHP 8+ — attributs natifs, remplacent les annotations Doctrine/Symfony
#[Attribute(Attribute::TARGET_CLASS)]
class MyCustomAttribute
{
    public function __construct(public readonly string $name) {}
}

#[MyCustomAttribute(name: 'product')]
class Product { /* ... */ }

// Symfony et Doctrine utilisent massivement les attributs :
use Symfony\Component\Routing\Attribute\Route;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'products')]
class ProductEntity
{
    #[ORM\Id, ORM\GeneratedValue, ORM\Column]
    private ?int $id = null;

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

Interfaces & classes abstraites

Symfony est basé sur l'injection de dépendances — tout repose sur des interfaces. Comprendre la différence interface / abstract est fondamental.
<?php
// Interface : contrat pur (méthodes sans implémentation)
interface PaymentProcessorInterface
{
    public function charge(float $amount, string $currency): bool;
    public function refund(string $transactionId): bool;
}

// Classe abstraite : implémentation partielle
abstract class AbstractNotifier
{
    abstract protected function send(string $message, string $recipient): void;

    public function notify(string $message, array $recipients): void
    {
        foreach ($recipients as $recipient) {
            $this->send($message, $recipient);
        }
    }
}

// Implémentation concrète
class StripeProcessor implements PaymentProcessorInterface
{
    public function charge(float $amount, string $currency): bool
    {
        // appel API Stripe
        return true;
    }

    public function refund(string $transactionId): bool
    {
        return true;
    }
}

// Service Symfony : inject l'interface, pas la classe concrète
class OrderService
{
    public function __construct(
        private readonly PaymentProcessorInterface $paymentProcessor
    ) {}

    public function checkout(float $total): bool
    {
        return $this->paymentProcessor->charge($total, 'EUR');
    }
}

Traits

Les traits permettent la réutilisation horizontale de code. Symfony les utilise (ex: ServiceSubscriberTrait).

<?php
trait TimestampableTrait
{
    private \DateTimeImmutable $createdAt;
    private \DateTimeImmutable $updatedAt;

    public function getCreatedAt(): \DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function initTimestamps(): void
    {
        $this->createdAt = new \DateTimeImmutable();
        $this->updatedAt = new \DateTimeImmutable();
    }

    public function touch(): void
    {
        $this->updatedAt = new \DateTimeImmutable();
    }
}

trait SoftDeleteTrait
{
    private ?\DateTimeImmutable $deletedAt = null;

    public function softDelete(): void
    {
        $this->deletedAt = new \DateTimeImmutable();
    }

    public function isDeleted(): bool
    {
        return $this->deletedAt !== null;
    }
}

// Utilisation
class Article
{
    use TimestampableTrait, SoftDeleteTrait;

    public function __construct(private string $title)
    {
        $this->initTimestamps();
    }
}

Composer & PSR-4

// composer.json (extrait Symfony)
{
    "require": {
        "php": ">=8.2",
        "symfony/framework-bundle": "7.2.*",
        "symfony/console": "7.2.*",
        "doctrine/orm": "^3.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
# Commandes essentielles Composer
composer install           # installe les dépendances (depuis composer.lock)
composer require symfony/mailer   # ajoute un paquet
composer require --dev phpunit/phpunit
composer update            # met à jour selon composer.json
composer dump-autoload     # régénère l'autoloader
PSR-4 : App\Entity\Productsrc/Entity/Product.php. Le namespace correspond au chemin fichier depuis src/.

Installer Symfony 7

# Via Symfony CLI (recommandé)
symfony new mon-projet --version="7.2.*" --webapp

# Via Composer
composer create-project symfony/skeleton:"7.2.*" mon-projet
cd mon-projet
composer require webapp  # meta-paquet webapp (twig, doctrine, security...)

# Lancer le serveur de développement
symfony serve            # https://127.0.0.1:8000
# ou
php -S localhost:8000 -t public/

Variables d'environnement (.env)

# .env (commité — valeurs de développement)
APP_ENV=dev
APP_SECRET=un-secret-random-32chars
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16"

# .env.local (ignoré par git — override local)
DATABASE_URL="mysql://root:@127.0.0.1:3306/mydb?serverVersion=8.0"

Structure d'un projet Symfony

mon-projet/
├── bin/
│   └── console              # CLI Symfony
├── config/
│   ├── packages/            # configuration des bundles (yaml)
│   ├── routes/              # routes supplémentaires
│   └── services.yaml        # DI container
├── migrations/              # migrations Doctrine
├── public/
│   └── index.php            # point d'entrée HTTP (front controller)
├── src/
│   ├── Controller/          # controllers HTTP
│   ├── Entity/              # entités Doctrine
│   ├── Repository/          # repositories Doctrine
│   ├── Service/             # services métier
│   ├── Form/                # Form Types
│   ├── Security/            # Voters, Authenticators
│   └── Kernel.php           # noyau Symfony
├── templates/               # templates Twig
├── tests/                   # tests PHPUnit
├── var/
│   ├── cache/               # cache compilé
│   └── log/                 # logs
├── vendor/                  # dépendances Composer
├── .env
└── composer.json
Tout le code applicatif va dans src/. Les templates dans templates/. La configuration dans config/. Jamais de logique dans public/.

bin/console — la CLI Symfony

La commande bin/console est votre outil quotidien. Listing des commandes les plus utilisées :

# Informations
php bin/console list                     # toutes les commandes
php bin/console debug:router             # routes enregistrées
php bin/console debug:container          # services DI
php bin/console debug:config framework   # config active

# Génération (MakerBundle)
php bin/console make:controller BlogController
php bin/console make:entity Product
php bin/console make:form ProductType
php bin/console make:migration
php bin/console make:voter PostVoter

# Doctrine
php bin/console doctrine:migrations:migrate
php bin/console doctrine:schema:update --force   # dev seulement
php bin/console doctrine:fixtures:load

# Cache
php bin/console cache:clear
php bin/console cache:warmup

# Sécurité
php bin/console security:hash-password

Créer sa propre commande

<?php
namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:hello', description: 'Commande de démonstration')]
class HelloCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln('<info>Bonjour depuis Symfony !</info>');
        return Command::SUCCESS;
    }
}

Prêt à coder ?

▶ Mini-projet SF01 🧠 QCM SF01 Module 02 →