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

2025: Performance Milestone for the Symfony eco...

Avatar for Antoine Bluchet Antoine Bluchet
November 27, 2025
980

2025: Performance Milestone for the SymfonyΒ ecosystem

This presentation, "2025: Performance Milestone for the Symfony Ecosystem," dives into the evolution of Symfony, its commitment to performance, and the components driving its future.

Key Topics Include:

20 Years of Symfony: Tracing the framework's journey from a rigid full-stack solution (v1/v2) to a flexible, performance-focused component system (v4+).
Modern Performance Components: A deep look at new components like the JsonStreamer for minimal memory usage and 10x faster serialization of large JSON volumes, and the ObjectMapper (7.4/8.0) for improved handling of nested classes and lazy loading.
API Platform Integration: Showing how these new components are used within API Platform.
The PHP Revolution: Celebrating 30 years of PHP and exploring modern performance gains, including the use of FrankenPHP in worker mode to achieve significant gains over traditional NGINX + PHP-FPM setups.
Future Optimizations: Highlighting ongoing work in the Object Mapper and Serializer components that promise up to 50% and 87% performance improvements, respectively.

This talk is essential for developers interested in high-performance PHP, modern Symfony development, and the future of API creation with API Platform.

Avatar for Antoine Bluchet

Antoine Bluchet

November 27, 2025

Transcript

  1. 20 years of Symfony βœ” From Symfony 1 to Symfony

    4+ βœ” Innovation and Backward compatibility βœ” Flex βœ” Adaptable and performant
  2. Dependency Injection use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; $container = new ContainerBuilder();

    // ... register services and parameters ... $container->compile(); $dumper = new PhpDumper($container); file_put_contents('Container.php', $dumper->dump());
  3. βœ” streaming JSON interface βœ” Achieves minimal memory usage for

    large data volumes βœ” 10x faster serialization and 50% less memory usage JsonStreamer Component symfony.com/blog/new-in-symfony-7-3-jsonstreamer-component
  4. JsonStreamer Caveats βœ” Error handling during streaming βœ” Only public

    properties βœ” Harder to customize than normalizers
  5. JSON-LD { "@context": "/contexts/Book", "@id": "/books/25", "@type": "https://schema.org/Book", "title": "1984",

    "condition": "https://schema.org/UsedCondition", "author": "/authors/20" } https:/ /edge-side-apis.rocks
  6. use Symfony\Component\JsonStreamer\Mapping\PropertyMetadata; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; use Symfony\Component\TypeInfo\Type; final class WritePropertyMetadataLoader implements

    PropertyMetadataLoaderInterface { public function load(string $className, array $options = [], array $context = []): array { $properties = $this->loader->load($className, $options, $context); $properties['@id'] = PropertyMetadata::createSynthetic( Type::string(), ['api_platform.jsonld.json_streamer.write.value_transformer.iri'] ); // ... return $properties; } }
  7. use ApiPlatform\Metadata\CollectionOperationInterface; use ApiPlatform\Metadata\IriConverterInterface; use Symfony\Component\JsonStreamer\ValueTransformer\ValueTransformerInterface; final class IriValueTransformer implements

    ValueTransformerInterface { public function __construct(private readonly IriConverterInterface $iriConverter) {} public function transform(mixed $value, array $options = []): mixed { return $this->iriConverter->getIriFromResource( $options['_current_object'], UrlGeneratorInterface::ABS_PATH, $options['operation'] instanceof CollectionOperationInterface ? null : $options['operation'], ); } }
  8. βœ“ Mapping of nested classes with promoted read-only properties βœ“

    Map to embedded object with property access βœ“ Bypass lazy ghost with class transform βœ“ Lazy loading βœ“ Cache attributes in memory (soyuka) ObjectMapper (7.4/8.0)
  9. Future optimizations ? βœ” Object Mapper (symfony/pull/61515) 50% perf improvement

    βœ” Serializer (symfony/pull/52905) 87% perf improvement (by @Nyholm Tobias Nyholm) Help reviewing!
  10. βœ” API Platform release manager βœ” Developer, biker, builder βœ”

    Father βœ” Open source advocate βœ” CTO at Les-Tilleuls.coop Antoine Bluchet aka soyuka https:/ /github.com/soyuka
  11. 10 years of API Platform βœ” API Platform 4.2 βœ”

    JsonStreamer component βœ” ObjectMapper component
  12. 30 years of PHP βœ” PHP 8 revolution βœ” PHP

    Foundation (2021) βœ” JIT βœ” Lazy proxies / ghost JetBrains PHPverse 2025
  13. FrankenPHP with Docker FROM dunglas/frankenphp:php8.5 # add additional extensions here:

    RUN install-php-extensions \ pdo_mysql \ gd \ intl \ exif \ zip \ opcache ENV FRANKENPHP_CONFIG="worker ./public/index.php" RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini COPY php-tweaks.ini /usr/local/etc/php/conf.d/php-tweaks.ini COPY . /app Symfony 7.4 detects the worker mode see https:/ /github.com/symfony/symfony/pull/60503
  14. Tips βœ“ Cloud ready: environment variables for every configuration βœ“

    Log to stderr is a good practice with containers βœ“ Single container really helps with pragmatic configuration βœ“ Watch mode to restart the worker in dev mode symfony.com/doc/current/performance.html
  15. NGINX vs FrankenPHP Scaleway DEV1-S (2 vCPUs, 2 GB RAM)

    Database Server: 2 vCPUs, 2 GB RAM, MySQL Benchmark Tool: wrk (on a separate DEV1-S instance) Nginx + PHP-FPM: pm.max_children = 15 FrankenPHP (Worker Mode): worker.num = 36 FrankenPHP (Thread Mode): num_threads = 36 (Worker vs. Thread) Performance Benchmark on a Sylius Application
  16. Conclusion βœ” Use worker mode βœ” Find worker { num

    } sweet spot βœ” Must read: github.com/php/frankenphp/discussio ns/1981
  17. PHP Extensions in Go βœ” Go ecosystem is awesome βœ”

    frankenphp extension-init Scaffolder βœ” Declare your php functions in go comments frankenphp.dev/docs/extensions/