Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Surviving a Symfony Upgrade

Surviving a Symfony Upgrade

Symfony is evolving and you may ask yourself whether your application can keep up. This presentation aims to show coding techniques and design patterns that will make future Symfony upgrades much easier, even if we don't know what the future holds!

Avatar for Anna Filina

Anna Filina

November 28, 2025
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. I Stand With Ukraine @[email protected] Anna Filina • Coding since

    1997 • PHP, C#, Java … • Symfony since v1 in 2007 • Legacy archaeology • Project rescue • Mentorship • Open for work
  2. I Stand With Ukraine @[email protected] Depends On… • Symfony version:

    minor or major, big gap • Features used: experimental, deprecated • Coupling with Symfony • Automation: tests, tools to fix • Runtime environment neglect (including PHP) ⟡
  3. I Stand With Ukraine @[email protected] Breaks in 5.0 • Removed

    support for PHP templates1 • Need to use Twig for those templates ⟡ 1 https://symfony.com/blog/new-in-symfony-4-3-deprecated-the-templating- component-integration
  4. I Stand With Ukraine @[email protected] Warning Signs • Feature deprecated

    in 4.3 • Can see in deprecation log: Using the "templating" service is deprecated since version 4.3 and will be removed in 5.0; use Twig instead. • 6 months until 5.0 to start migrating to Twig ⟡
  5. I Stand With Ukraine @[email protected] PHP Templates Feature • Supported

    and documented • First clear upcoming removal signal in a blog1 • A month before 4.3 release ⟡ 1 https://symfony.com/blog/new-in-symfony-4-3-deprecated-the-templating- component-integration
  6. I Stand With Ukraine @[email protected] Coupling • Embedded control syntax

    = tightly coupled • Meaning of {% block ... %} or {% extends … %} aren’t universal ⟡
  7. I Stand With Ukraine @[email protected] Automation • Deprecations show up

    when you execute code • Your own automated tests will surface deprecations • Tests: need integration & application, not just unit ⟡
  8. I Stand With Ukraine @[email protected] Types of Tests • Different

    goals • Unit: internal logic correctness, execution paths • Integration: wiring stability, component interaction, database • Application: observable behavior, request lifecycle ⟡
  9. I Stand With Ukraine @[email protected] Integration Tests • Test interaction

    between components • Interaction can break when you upgrade • Symfony provides testing tools ⟡
  10. @[email protected] // Symfony 7.3 + Twig // MyIntegrationTest extends KernelTestCase

    self::bootKernel(); $container = static::getContainer(); $twig = $container->get(TwigEnvironment::class); $renderedHtml = $twig->render('pages/index.html.twig'); $this->assertStringContainsString('SymfonyCon', $renderedHtml);
  11. @[email protected] // Symfony 7.3 // MyApplicationTest extends WebTestCase $client =

    static::createClient(); $client->request('GET', '/'); $this->assertResponseIsSuccessful(); $this->assertSelectorTextContains('h1', 'SymfonyCon');
  12. @[email protected] // MyCommand extends Command // Symfony 6.4 protected function

    execute(InputInterface $input, OutputInterface $output) // Symfony 7.0 protected function execute(InputInterface $input, OutputInterface $output): int
  13. I Stand With Ukraine @[email protected] Breaks in 7.0 • Added

    native return type declaration • Native types introduced over many years and versions ⟡
  14. I Stand With Ukraine @[email protected] Warning Signs • Released as

    @return int|null • Stabilized as @return int since 4.4 • Tool to scan and patch return types since 4.41 Method " [...]\Command::execute()" will return "int" as of its next major version. • 4 years until 7.0 to fix code ⟡ 1 https://github.com/symfony/symfony/pull/33283
  15. I Stand With Ukraine @[email protected] Coupling • Commands are tightly

    coupled with Symfony • But don’t have to extend since 7.3 (in standalone Console component since 7.4 – yesterday) ⟡
  16. @[email protected] public function __invoke(): int public function __invoke(OutputInterface $output): int

    public function __invoke(#[Argument('User e-mail.')] string $email): int
  17. I Stand With Ukraine @[email protected] Interface Guarantee • Safe to

    use interfaces1 • Symfony won’t break them • Unless marked @experimental or @internal • Unless security fix ⟡ 1 https://symfony.com/doc/current/contributing/code/bc.html
  18. I Stand With Ukraine @[email protected] Automation • Scan and patch

    type declarations1 ./vendor/bin/patch-type-declarations • Fix body if needed (manual) • Unit tests for internal logic • Integration tests with CommandTester2 ⟡ 1 https://wouterj.nl/2021/09/symfony-6-native-typing 2 https://symfony.com/doc/current/console.html
  19. @[email protected] // MyTest extends KernelTestCase self::bootKernel(); $application = new Application(self::$kernel);

    $command = $application->find('app:my-command'); $commandTester = new CommandTester($command); $commandTester->execute(); $commandTester->assertCommandIsSuccessful();
  20. I Stand With Ukraine @[email protected] Rector • Refactors PHP code

    • Rules written in PHP • Existing rules for Symfony1 • Write custom rules ⟡ 1 https://getrector.com/find-rule?query=symfony
  21. I Stand With Ukraine @[email protected] Structural Changes • Renames •

    Relocations • Config format • Signature changes with same behavior • Detectable with static analysis • Fixable with automation ⟡
  22. @[email protected] Expected a scalar value as a 2nd argument to

    "Symfony\Component\HttpFoundation\InputBag::get()", "array" given. InvalidArgumentException
  23. I Stand With Ukraine @[email protected] Deprecation • trigger_deprecation since 5.1

    • Shows up in Symfony profile bar • Also @param mismatch → highlight in PHPStorm • Also reported if you use PHPStan or similar • Also shows up in error log • Also shows up in tests ⟡
  24. I Stand With Ukraine @[email protected] Semantic Changes • Type enforcement

    • Different behavior • New default config values • Event reorder • Detectable mostly at runtime (tests!) • Hard to infer intent for automated fixes ⟡
  25. I Stand With Ukraine @[email protected] Winning Combination • Automate tests

    • Use static analysis • Keep up with deprecations • Limit inheritance (implement, compose, inject interface) • Upgrade dependencies & infrastructure often ⟡