Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What is Symfony?

What is Symfony?

What is Symfony?

Avatar for Fabien Potencier

Fabien Potencier

June 12, 2025
Tweet

More Decks by Fabien Potencier

Other Decks in Technology

Transcript

  1. 2014 DX initiative 2017 Symfony Flex Symfony 4 2020 Symfony

    UX 2022 recipes:update 2018 Silex symfony/symfony Symfony DX in 5 key dates Each new version brings DX improvements "New in Symfony X.Y" posts / tag on Github Since 2014 DX 🤝
  2. use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\Attribute\Route; class AppKernel

    extends Kernel { use MicroKernelTrait; #[Route('/hello/{name}', defaults: ['name' => 'World'])] public function __invoke(string $name): Response { return new Response('Hello '.$name); } } return static function (array $context) { return new AppKernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; Classical HTTP Hello World "application" Hello
  3. Features cache config dependency-injection event-dispatcher framework-bundle http-foundation http-kernel routing runtime

    Interfaces and PHP compat layers Some PSR interfaces Some polyfills Some Symfony contracts symfony composer install --no-dev Utilities Enhance PHP core error-handler filesystem finder var-dumper var-exporter 💪 Error handling, dumper, PHP attributes, ... 💪 No third-party code Hello
  4. Not a toy framework Production ready - like caching for

    better performance Lot of useful features - without adding any deps Extensible - scale to the full Symfony capabilities Hello
  5. use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\Attribute\Route; class AppKernel

    extends Kernel { use MicroKernelTrait; #[Route('/', methods: 'GET', defaults: ['name' => 'World'])] #[Route('/hello/{name}', requirements: ['name' => '\w+'])] public function __invoke(string $name): Response { return new Response('Hello '.$name); } } return static function (array $context) { return new AppKernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; Multi-route support, full configurability Hello
  6. #[Route('/')] public function wow(): Response { return new Response('wow'); }

    #[Route('/hello/{name}', defaults: ['name' => 'World'])] public function hello(string $name): Response { return new Response('Hello '.$name); } Multi-controllers in the same file Any method name Hello
  7. #[Route('/hello')] class AppKernel extends Kernel { use MicroKernelTrait; #[Route('/')] public

    function wow(): Response { return new Response('wow'); } #[Route('/{name}', defaults: ['name' => 'World'])] public function hello(string $name): Response { return new Response('Hello '.$name); } } Mount routes Hello
  8. use Psr\Log\LoggerInterface; class AppKernel extends Kernel { use MicroKernelTrait; #[Route('/hello')]

    public function hello(Request $request, LoggerInterface $logger): Response { $logger->info('Hello'); return new Response('Hello '.$request->query->get('name')); } } Inject Services ❯ symfony server:log Following Web Server log file (/Users/fabien/.symfony5/log/95...b1.log) Following PHP-FPM log file (/Users/fabien/.symfony5/log/95...b1/53...75.log) [Web Server ] Mar 14 16:57:08 |INFO | SERVER GET (200) /hello.php/hello?name=Fabien ip="127.0.0.1" [PHP-FPM ] [14-Mar-2025 15:57:08 UTC] [info] Matched route "appkernel_hello". [PHP-FPM ] [14-Mar-2025 15:57:08 UTC] [info] Hello Hello
  9. Debug with ease return static function (array $context) { dump($context['APP_ENV'],

    $context['APP_DEBUG'], $context); return new AppKernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; With auto-navigation in the browser Hello
  10. Classical Console Hello World "application" use Symfony\Component\Console\Application; use Symfony\Component\Console\Attribute\AsCommand; use

    Symfony\Component\Console\Command\Command; $app = new Application(); $app->add(new #[AsCommand(name: 'app:hello')] class extends Command { public function __invoke(): int { echo 'Hello World'; return 0; } } ); $app->setDefaultCommand('app:hello')->run(); Hello
  11. Classical Console Hello World "application" $app = new Application(); $app->add(new

    #[AsCommand(name: 'app:hello')] class extends Command { public function __invoke( SymfonyStyle $io, #[Argument] string $name = 'World', #[Option] bool $formal = false, ): int { $io->title(sprintf('%s %s!', $formal ? 'Hello' : 'Hey', $name)); return Command::SUCCESS; } } ); $app->setDefaultCommand('app:hello')->run(); Hello ❯ php app.php app:hello --formal Fabien Hello Fabien! =============
  12. Using the Symfony Runtime #[AsCommand(name: 'app:hello')] class AppKernel extends Kernel

    { use MicroKernelTrait; public function __invoke( SymfonyStyle $io, #[Argument] string $name = 'World', #[Option] bool $formal = false, ): int { $io->title(sprintf('%s %s!', $formal ? 'Hello' : 'Hey', $name)); return Command::SUCCESS; } } return static function (array $context) { return (new Application(new AppKernel('dev', true)))->setDefaultCommand('app:hello'); }; Hello
  13. A front controller for CLI and HTTP Solo use App\Kernel;

    use Symfony\Bundle\FrameworkBundle\Console\Application; require_once __DIR__.'/../vendor/autoload_runtime.php'; return static function (array $context) { $kernel = new Kernel('dev', true); return \PHP_SAPI === 'cli' ? new Application($kernel) : $kernel; }; public/index.php
  14. Your choice of a directory structure Solo public/ index.php src/

    HelloController.php HelloCommand.php Kernel.php SomeService.php composer.json
  15. Doing some configuration Solo namespace App; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\HttpKernel\Kernel

    as BaseKernel; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; class Kernel extends BaseKernel { use MicroKernelTrait; private function configureRoutes(RoutingConfigurator $routes): void { $routes->import(dirname(__DIR__).'/src/*Controller.php', 'attribute'); } } src/Kernel.php
  16. Controllers as classes Solo namespace App; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController;

    use Symfony\Component\Routing\Attribute\Route; #[AsController] class HelloController { #[Route('/hello')] public function hello(): Response { return new Response('Hello'); } } src/HelloController.php No base class
  17. Add Twig support Solo public function registerBundles(): iterable { yield

    new FrameworkBundle(); yield new TwigBundle(); } private function configureContainer(ContainerConfigurator $container): void { $container->services() ->defaults()->autowire()->autoconfigure() ->load('App\\', dirname(__DIR__).'/src') ; } src/Kernel.php public function hello(Environment $twig): Response { return new Response($twig->render('hello.twig')); } src/HelloController.php public/ index.php src/ HelloController.php Kernel.php templates/ hello.twig Default config
  18. public function registerBundles(): iterable { yield new FrameworkBundle(); yield new

    WebProfilerBundle(); } private function configureRoutes(RoutingConfigurator $routes): void { $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml')->prefix('/_profiler'); $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml')->prefix('/_wdt'); } private function configureContainer(ContainerConfigurator $container): void { $container->extension('web_profiler', [ 'toolbar' => 'dev' === $this->getEnvironment(), ]); } src/Kernel.php Add Profiler Bundle Solo Use .php in 7.3+
  19. namespace App; #[AsCommand(name: 'app:hello')] class HelloCommand { public function __invoke(SymfonyStyle

    $io, #[Argument] string $name = 'World'): int { $io->title(sprintf('Hello %s!', $name)); return Command::SUCCESS; } } src/HelloCommand.php Commands as classes Solo
  20. Send Emails Solo private function configureContainer(ContainerConfigurator $container): void { $container->extension('framework',

    [ 'mailer' => [ 'dsn' => '%env(MAILER_DSN)%', ], ]); } src/Kernel.php #[Route('/send-email', methods: 'GET')] public function sendEmail(MailerInterface $mailer): Response { $mailer->send((new Email()) ->from('[email protected]')->to('[email protected]') ->subject('Time for Symfony Mailer!') ->text('Sending emails is fun again!') ); return new Response('<html><body><h1>Email sent</h1></body></html>'); } src/HelloController.php
  21. Send Emails Solo MAILER_DSN=smtp://localhost:1025 .env 1 services: mailer: image: schickling/mailcatcher

    ports: [1025, 1080] docker-compose.yml 2 ❯ docker compose up -d ❯ symfony server:start -d 3
  22. Solo Hello Team Single file Learning / Demo None None

    Examples / Demo Small to medium Individual Self-defined Self-defined Personal projects Single-purpose apps Quick proof of concepts Medium to large Team Standardized Documented Team collaboration Long-term maintenance Complex business logic Scale Developers Structure Conventions Best for
  23. LLMs vs Human vs Community Symfony 7.3 introduces a way

    to define and inject arguments directly. Use it. More documentation here: @https://github.com/symfony/symfony/pull/59340 Looking at the pull request, Symfony 7.3 introduces some exciting DX improvements for console commands, particularly the ability to use invokable commands with attribute-based arguments. Let's update your ColorSchemeCommand to use these new features: - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io, #[Argument(description: 'Hex color (e.g. #FFFFFF)')] string $color): int Human LLM
  24. Good title Great rationale behind the change Before/After examples Helps

    reviewers Helps writing docs Helps LLMs Community