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\Product → src/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 ?