Gabriel Mustiere
Gabriel Mustiere
Portrait of Gabriel Mustiere
Freelance
CTO

Tech · Business · AI. Nantes — remote.

Background
№ 0216 min
PHP & Symfony in 2026: a CTO perspective on the stack

PHP & Symfony in 2026: the stack worth a CTO’s attention

In 2026, PHP 8.5 and Symfony 7 deliver a complete stack — API Platform, Sylius, EasyAdmin, Mercure, Live Components, FrankenPHP. Ready for CTO decisions on APIs, SaaS, e‑commerce, mobile and AI.

§ contents
  1. 01What I’ve watched transform over fourteen years
  2. 02The language has caught up — and gone past — what was expected of it
  3. 03Symfony: an ecosystem that covers everything
  4. 04The tooling that makes quality industrial
  5. Honourable mentions
  6. 05FrankenPHP — the rupture that changes the infrastructure equation
  7. 06Front-end, mobile and real‑time without stacking a JS pipeline
  8. 07AI on the product side: what the Symfony ecosystem changes
  9. When PHP/Symfony is the right choice in 2026

Fourteen years of PHP. Around fifty projects shipped since 2012 — from a small management application to a complex SaaS covering the entire business processes of a factory, taking in a from‑scratch e‑commerce project and a hexagonal architecture for a neobank along the way. One constant thread: Symfony.

This article isn’t meant to compare or oppose ecosystems. It offers a current, 2026 view from the inside: a language that has become strict and performant, a set of frameworks capable of covering every need — APIs, SaaS, e‑commerce, back‑office, real‑time, mobile, AI —, mature tooling, and infrastructure that has, quietly but definitively, made the performance debate disappear.

I’m writing here for CTOs and tech leads looking for a clear, up-to-date view of the Symfony ecosystem, as well as for PHP developers who want to reassess the actual potential of their stack. What follows is not advocacy, but field feedback grounded in real-world projects.

What I’ve watched transform over fourteen years

I’ve been working in PHP/Symfony since 2012. It’s a deliberate specialisation: stacking project after project in the same ecosystem builds an intuition that makes every new scoping faster, every delivery more predictable, every maintenance simpler.

It isn’t an opportunistic choice — it’s what allows me, on most of the projects I take on, to deliver in a few days what, without that mastery, would often take several weeks.

Over those fourteen years, the ecosystem has gone through several transformations that need to be looked at together rather than separately:

  • The language moved from a permissive playground to a typed, strict and now fast environment. PHP 8.5 has very little in common with the PHP 5.3 of my early career.
  • Symfony established itself as the foundation, then structured a complete ecosystem — API Platform, Sylius, UX Initiative, Mercure — covering most needs without having to assemble disparate pieces.
  • Tooling has become systemic: PHPStan, Rector, PHP CS Fixer, PHPUnit, Composer, Symfony CLI. Quality no longer relies solely on individual discipline, it is integrated into the workflow.
  • Infrastructure crossed a threshold with FrankenPHP in worker mode. Performance, long perceived as a structural weakness, is no longer an issue.
  • More recently, initiatives like Symfony UX, Symfony AI or the Hotwire Native bridge extend the ecosystem into territory where it was less expected: reactive front‑end, AI‑first apps, mobile.

The sections that follow detail these transformations as I actually use them in production: the language, the frameworks, the tooling, the infrastructure, front‑end and mobile, then AI.

In conclusion, a five-criteria reading grid to precisely situate the contexts where the PHP ecosystem is most relevant.

The language has caught up — and gone past — what was expected of it

The PHP of 2026 looks nothing like the PHP of 2012. The transformation came in two ruptures, then a phase of accumulation. PHP 7.0 in late 2015 rebuilt the engine (Zend Engine 3) and locked in a roughly ×2 performance jump on typical workloads. PHP 8.0 in late 2020 laid down the modern type system and introduced the JIT. Everything added since, from 8.1 to 8.5, is depth rather than rupture.

On the typing side, seven versions have turned a permissive language into a strict one: scalar types (7.0), typed properties (7.4), unions and mixed (8.0), enums and intersections (8.1), readonly classes and DNF (8.2), typed class constants (8.3), property hooks and asymmetric visibility (8.4). Concretely, today, a domain entity can be written like this:

final readonly class Order
{
    public function __construct(
        public OrderId $id,
        public Status $status,
        public private(set) array $lines = [],
    ) {}

    public string $reference {
        get => sprintf('ORD-%s-%s', $this->status->value, $this->id);
    }
}

readonly on the class forbids property mutation. private(set) (8.4) restricts writes to the class while keeping reads public. The get => ... block (property hook, 8.4) replaces the trivial getter. The constructor declares and assigns properties at once (constructor promotion, 8.0). This expression density is what a modern Kotlin or C# developer expects, and it’s now native on the PHP side.

On the expressiveness side, the syntactic additions of the 8.x branch are less talked about but immediately visible when reading code. match (8.0) replaces switch with strict comparison and a returned value. The nullsafe operator ?-> chains without intermediate null checks. Native attributes #[Attribute] (8.0) buried docblock annotations. The pipe operator |> (8.5) flows a value left to right without intermediate variables:

$slug = $title
    |> trim(...)
    |> mb_strtolower(...)
    |> fn(string $s) => preg_replace('/[^a-z0-9]+/', '-', $s);

In isolation, each of these additions is minor. Stacked, they transform the reading of a 2026 PHP file compared to a 2018 one.

On the performance side, the PHP 8.x baseline is already roughly three times faster than the PHP 7.4 most people have in mind when they talk about “PHP slowness”. The JIT IR introduced in 8.4 and refined in 8.5 adds 5 to 15 % on arithmetic workloads while using less memory than the previous backend. On typical IO-bound web applications, the JIT alone doesn’t change much; it’s the whole picture — 7.0 engine, 8.0 JIT, 8.4/8.5 IR, OPcache now compiled statically into 8.5 — that has erased the structural slowness argument.

On the async side, Fibers introduced in 8.1 have been stable for four major versions and are usable in production via AmPHP or ReactPHP. The ergonomics aren’t those of async/await in JavaScript, but the functional outcome is the same: handling concurrent I/O without resorting to threads.

The language I introduce to a junior today is closer to a modern C# or Kotlin than to the PHP 5 they imagine. That completely changes the nature of the debate: we’re no longer asking whether PHP is robust, but in which cases it’s the best choice.

Symfony: an ecosystem that covers everything

Symfony has been my working foundation since Symfony 2 — modular, strongly typed, every component usable independently. The real argument in 2026 is what has been built around it.

These building blocks directly address the most common product needs:

NeedMain componentWhat you get out of the box
REST / GraphQL APIAPI PlatformGenerated CRUD, auto OpenAPI, filters, pagination, serialisation, native GraphQL
E-commerceSyliusCatalogue, cart, checkout, taxes, promotions, multi-channel, multi-currency, headless
Admin back‑officeEasyAdminFull CRUD on Doctrine entities in a few lines, dashboard, filters, exports
Real-timeMercureServer-sent events, native API Platform integration
Reactive UI without JSSymfony UX Live ComponentTwig components with server state, automatic AJAX updates
Mobile (iOS/Android)Turbo + Hotwire NativeNative app reusing server-side HTML, no React Native or Flutter

And behind them, the framework itself rests on more than fifty decoupled components, each installable independently in any PHP project. The panorama, by domain, shows the breadth of coverage:

DomainComponents
HTTP & routingHttpFoundation, HttpKernel, HttpClient (sync/async/HTTP/2), Routing, Mime, WebLink
Console & runtimeConsole, Process, Runtime, Dotenv
Configuration & DIConfig, DependencyInjection, OptionsResolver, Yaml, ExpressionLanguage
SecuritySecurity (Core/Bundle/Csrf/Http), PasswordHasher, RateLimiter, Lock, Semaphore, HtmlSanitizer, Ldap
Data & typesValidator, Serializer, Form, PropertyAccess, PropertyInfo, TypeInfo, ObjectMapper, JsonPath
Async & eventsMessenger, Scheduler, EventDispatcher, RemoteEvent, Webhook, Workflow (state machines)
CommunicationMailer (Amazon/SendGrid/Mailgun/Postmark/Brevo…), Notifier (SMS, push, Slack, Telegram, Discord…)
Cache & perfCache (PSR-6/16 + tags), Clock, Stopwatch
Filesystem & assetsFilesystem, Finder, Asset, AssetMapper (no bundler, no Node)
I18n & identifiersTranslation, Intl, Emoji, Uid (UUID v1–v8, ULID)
Test & debugBrowserKit, DomCrawler, CssSelector, Panther (Chrome/Firefox), VarDumper, VarExporter, ErrorHandler
Frontend (Symfony UX)Turbo, LiveComponent, TwigComponent, Stimulus, Autocomplete, Icons, Chart.js, Map, React/Vue/Svelte
AI (since 2025)AI Platform, AI Agent, AI Chat/Store, OpenAI/Anthropic/Gemini/Mistral/Cohere bridges, MCP Bundle

Everything that elsewhere requires assembling several third-party libraries and external services — queue + worker, retry + DLQ, signed URL + storage, OAuth + 2FA + rate limit, OpenTelemetry + injected clock for tests — exists here under a homogeneous API, maintained by the same team, documented in the same place.

On most of the products I see come through — B2B SaaS, marketplace, vertical platform — each of these blocks is used without glue code.

API Platform in particular deserves a stop: declare a Doctrine entity, annotate it, and you get a documented REST + GraphQL + OpenAPI API, Mercure-pushable, without writing a controller. In some contexts, the productivity gain is measured in factors, not in percentages.

I haven’t used Laravel in depth over the long run, but it has to be mentioned: the other major PHP framework, massive community, very polished DX, coherent product-oriented ecosystem (Forge, Vapor, Livewire, Reverb). For a small team that values convention over configuration, it’s a perfectly defensible choice.

WordPress, which I don’t work with day-to-day either, remains an ecosystem staple — about 40 % of the web runs on it, the commercial plugin market is mature, and it now exposes a real REST/GraphQL API via WPGraphQL for anyone who wants to use it as a headless CMS.

The PHP ecosystem isn’t reducible to Symfony, and its various branches cover the full spectrum of modern web products.

The tooling that makes quality industrial

This is probably the segment where the gap between perception and reality is widest. Over fourteen years, I’ve watched PHP tooling go from an assembly of homemade scripts to an industrial backbone — to the point of being today, in my view, more stable than many modern front‑end ecosystems where tools move very fast.

PHPStan has become the spine of static analysis. Strict mode (level 9) catches the vast majority of typed errors before runtime; level 10, shipped with version 2.0, even chases unresolved mixed types and has become a quality badge in PHP OSS. A serious Symfony codebase passes at minimum level 8 in CI. The phpstan/phpstan-symfony extension adds fine-grained knowledge of the container, services, routing and parameters — concretely, PHPStan knows that $container->get('doctrine') returns a Registry and that this parameter is an int. That’s valuable.

Rector is the tool that took several codebases of mine from PHP 7.4 + Symfony 4 to PHP 8.5 + Symfony 7 without lost weeks. It knows the migration rules of PHP, Symfony and Doctrine, and applies them across hundreds of files in a single command. The SymfonySetList::SYMFONY_70 set chains in minutes transformations that would take weeks by hand. In particular, it covers the move from annotations to PHP 8 attributes, the migration of services and the update of controller signatures.

PHPUnit remains the reference test framework in the Symfony ecosystem, and version 13 released in February 2026 confirms that it keeps evolving with great rigour — PHP 8 attribute metadata, stricter expectations, isolation by default. Combined with the WebTestCase and KernelTestCase provided by Symfony, it covers the whole spectrum, from unit test to functional HTTP test. For API tests, API Platform provides its own ApiTestCase that integrates natively with PHPUnit. And for those who want to validate the relevance of their tests, Infection rounds out the arsenal via mutation testing.

PHP CS Fixer is the reference formatter on the Symfony side, with its @Symfony and @Symfony:risky presets that automatically apply the community’s conventions. Configuration in .php-cs-fixer.dist.php, CI/IDE integration, end of the style debate. The @PER-CS2.0 ruleset additionally aligns with the most recent PHP-FIG standard.

On the security side, composer audit has been integrated natively into Composer since 2.4, and since 2.9 it automatically blocks the installation of vulnerable dependencies. Wire it into CI, no debate. To go further, Roave Security Advisories locks at the composer.json level itself, and the Local PHP Security Checker maintained by Symfony remains a complementary option.

For architecture, Deptrac or PHPat (as a PHPStan extension) let you test contracts between layers — for example, forbidding a controller from calling a repository directly — and turn architecture discipline into rules verified in CI. On Symfony projects that structure their code as bounded contexts or hexagonal architecture, it’s a safety net that pays off immediately.

A minimal but serious CI on a Symfony project in 2026 looks like this:

# .github/workflows/ci.yml (excerpt)
- run: composer install --no-progress
- run: vendor/bin/php-cs-fixer fix --dry-run --diff # style
- run: vendor/bin/phpstan analyse # types (level 8+)
- run: vendor/bin/rector --dry-run # upcoming deprecations
- run: vendor/bin/phpunit --coverage-clover=clover.xml
- run: composer audit # CVE

Six commands, six concrete guarantees: clean style, correct types, code aligned with the next version, tested behaviour, no vulnerable dependency. That’s exactly the level I expect from a serious TypeScript project. On top of that I systematically add the Symfony CLI, which bundles a TLS dev server, Docker integration and a security checker, and Castor (built by JoliCode) as a modern task runner to orchestrate project commands — replacing Make or scattered shell scripts, with proper PHP ergonomics and the matching autocompletion.

Honourable mentions

In addition, a few structuring tools from the ecosystem deserve to be mentioned:

On testing, Pest is worth mentioning even though I stay loyal to PHPUnit: its expressive syntax and version 4 — which integrates Playwright for browser tests without a separate JavaScript stack — have made it the fastest-growing framework, particularly on the Laravel side (over 39 million installations in early 2026). For a team starting from scratch, it’s a defensible option.

On static analysis, Psalm, now maintained by Daniil Gentili, remains the alternative to PHPStan. Its distinctive feature is taint analysis — static detection of XSS, SQL injection and command injection — which has no free equivalent in the ecosystem.

On runtimes, beyond FrankenPHP covered in the next section, RoadRunner (Go-based application server with built-in queue and gRPC) and Swoole (coroutine/event-loop extension) are the two other serious choices for stepping out of the PHP-FPM model. For serverless, Bref has become the de facto standard for running PHP on AWS Lambda.

On dev environments, DDEV stands out as the most mature multi-stack Docker solution across all communities (Drupal, WordPress, TYPO3, Symfony, Laravel), while Laravel Herd offers a particularly polished native macOS/Windows experience for those who want to avoid Docker locally.

On distribution, PHIVE (Phar Installation and Verification Environment) lets you install GPG-signed QA tools without polluting the project’s composer.json, and Box remains the standard for packaging a PHP application as a standalone PHAR.

Finally, a mention for Mago, a new entrant written in Rust that aims to unify linter, formatter and static analyser in a single binary, in the spirit of what Biome does on the JavaScript side. It’s still young (pre-1.0 at the end of 2025), but typically the kind of tool to watch closely to anticipate the next evolutions of the ecosystem.

FrankenPHP — the rupture that changes the infrastructure equation

This is probably the most structuring transformation of the last two years. FrankenPHP, built by Kévin Dunglas (the creator of API Platform), is a PHP application server built on Caddy, distributed as a single binary, and offering a worker mode: the PHP process stays in memory between requests, and the framework bootstrap runs only once at worker start-up.

The result isn’t a marginal improvement. On a standard Symfony application, here’s a benchmark published on Symfony 7.4 (source DEV.to, early 2026):

SetupThroughputp95 latency
Nginx + PHP-FPM1,240 RPS45 ms
FrankenPHP (worker mode)3,850 RPS8 ms

Three times more requests per second, p95 latency divided by five. On Laravel, some worker mode migrations reported by the community show very significant gains on slow endpoints (to be contextualised case by case). These results need to be validated in the context of your own load, but the order of magnitude is representative.

The infrastructure implication is direct: at constant traffic, you divide by three or four the number of workers needed. On a B2B SaaS serving a few thousand concurrent users, that typically translates into going from four-to-six application instances down to one or two. The monthly cost follows.

Deployment is also simplified: a FrankenPHP binary can embed the server, the PHP runtime and the HTTP configuration (Caddy), with automatic HTTPS, HTTP/3 and native integration with Mercure. You move away from a classic Nginx + PHP-FPM + reverse proxy stack to orchestrate.

Migration is surprisingly cheap. Most Symfony applications run in worker mode without changes, provided one simple rule is respected: don’t keep mutable state between two requests. The rare services that cache request-scoped data must implement ResetInterface, which is generally quick to fix.

The historical “PHP slowness” argument rested on the per-request process model of PHP-FPM. That model is no longer the only option — and, on the majority of applicative web projects, performance is no longer a discriminating factor.

Front-end, mobile and real‑time without stacking a JS pipeline

This is probably the segment where PHP was, for a long time, trailing. A modern web application implies client-side interactivity — and the historical PHP answer was never fully convincing. That’s what has changed.

Symfony UX Live Components is now the approach I use by default on B2B SaaS. A Twig component backed by a PHP class, updated server-side in response to user interactions, HTML rendering shipped in fragments, state preserved on the server and synchronised through signed HTML attributes. No JavaScript framework, no bundle, no business logic duplicated between front and back. Concretely:

// src/Twig/Components/UserSearch.php
#[AsLiveComponent]
final class UserSearch
{
    use DefaultActionTrait;

    #[LiveProp(writable: true)]
    public string $query = '';

    public function __construct(private UserRepository $users) {}

    public function getResults(): array
    {
        return $this->users->searchByName($this->query, limit: 20);
    }
}
{# templates/components/UserSearch.html.twig #}
<div {{ attributes }}>
    <input type="search" data-model="query" placeholder="Search a user…">
    <ul>
        {% for user in this.results %}
            <li>{{ user.fullName }}</li>
        {% endfor %}
    </ul>
</div>

That’s all you need. The data-model binds the input to $query, rendering updates on every keystroke (with debounce). On the vast majority of admin and B2B SaaS screens, that level of interactivity is more than enough — and we wrote zero JavaScript.

For real‑time, Mercure is integrated natively into Symfony and API Platform. A publisher on the server, an SSE consumer in the browser, and you get notifications, collaborative editing or real‑time updates — without WebSockets to manage.

For mobile, the arrival of the Hotwire Native bridge on the Symfony side (via symfony/turbo combined with 37signals’ Hotwire native shell) shifts the trade-off. Instead of maintaining three codebases — React web, Swift iOS app, Kotlin Android app — you maintain one Symfony codebase that renders HTML. Two minimal native shells load that HTML inside optimised WebViews.

When you need native (camera, push notifications, sensors), you expose it via JS↔native bridges. This is notably the approach used by Basecamp for HEY, and it’s now fully usable in a Symfony stack.

On a B2B SaaS product with a companion mobile app, this is often the best compromise: single team, single codebase, near-immediate feature parity.

AI on the product side: what the Symfony ecosystem changes

The latest objection I hear, more and more, is that AI‑first apps are built in Python — that’s where the libraries are, that’s where the benchmarks are. That’s partly true for R&D and custom inference.

It is not the case for the majority of applications that consume LLMs through APIs — which covers the overwhelming majority of AI products today: vertical chatbots, RAG, agents, tool use, embedded copilots.

The Symfony AI Initiative launched by Christopher Hertel formalises what was already being patched together in several projects. Concretely, it covers:

  • LLM calls (OpenAI, Anthropic, Mistral, Ollama)
  • message and tool-call handling
  • embeddings and vector stores (Pinecone, Qdrant, pgvector)
  • RAG pipelines
  • agents with planning

It’s still young and evolving fast, but already usable on the majority of product-side applicative use cases.

On a recent project where we needed to add a RAG vertical assistant on top of a client document base, I built it in Symfony:

  • document ingestion in the background via Messenger
  • embeddings stored in pgvector
  • retrieval + generation through Symfony AI
  • Live Component UI for the chat with SSE streaming through Mercure

Everything in the same codebase.

No separate Python service to orchestrate, no double deployment, no architectural fragmentation.

When PHP/Symfony is the right choice in 2026

Here is how I position PHP/Symfony today for a new product:

CriterionPHP/Symfony verdict in 2026
Delivery velocityAPI Platform + EasyAdmin + Live Components → a complete, structured MVP in a few weeks
Infrastructure costFrankenPHP worker mode → 3–4× the throughput of PHP-FPM, costs divided accordingly
Hiring profileFluid market: wide pool, stable seniors, freelancers available
Code longevityRector + PHPStan → automated migrations, code maintainable for ten years and beyond
Functional coverageAPI, SaaS, e‑commerce, back‑office, real‑time, mobile, AI — everything is covered

There are cases where another stack is better placed. For pure AI research, scientific computing, sub-millisecond real‑time, or systems tooling, PHP isn’t the right choice — and no one will pretend otherwise.

But for the vast majority of modern products — B2B SaaS, e‑commerce, vertical back‑office, exposed APIs, AI assistants connected to data — the PHP ecosystem is today a perfectly defensible default option. Not a nostalgic compromise: a practitioner’s choice, anchored in fourteen years of direct observation.

That’s the observation I wanted to lay down, after fourteen years in the field.

§ faq

Frequently asked questions

Should you really start a new project in PHP/Symfony rather than Node or Python in 2026?
It depends on the product. For a web app that consumes LLMs through APIs and needs API + back‑office + e‑commerce + mobile coverage, Symfony is today the fastest choice to get up and running — API Platform, EasyAdmin, Sylius, Live Components and the Hotwire Native bridge all come out of the box, with no glue code. For a strongly ML-oriented product (training, custom inference, large-scale data processing), Python remains better placed. For a strongly real‑time service under one millisecond or systems tooling, Go or Rust come out ahead. The trap is reasoning by genre rather than by use case.
FrankenPHP in worker mode: what does the migration cost on an existing Symfony codebase?
Low, in most cases. The rule fits in one sentence: don't keep mutable state between two requests. The rare services that cache request-scoped data must implement `ResetInterface`, which is quickly fixed. Once that step is past, the gain is immediate: on the Symfony 7.4 benchmark published in early 2026, you go from 1,240 RPS / p95 45 ms (Nginx + PHP-FPM) to 3,850 RPS / p95 8 ms (FrankenPHP worker). Deployment is simplified too — single binary, integrated Caddy, automatic HTTPS, native HTTP/3 and Mercure. RoadRunner and Swoole remain serious alternatives for those who have already invested in them.
How long for a Node or Python team to become productive in Symfony?
On the teams I've seen migrate, a senior developer comfortable with a typed full-stack framework (Spring, Rails, NestJS, Django) reaches a decent level in two to four weeks, and full productivity in two to three months. The main brake isn't the language — PHP 8.5 is close to a modern C# or Kotlin — but getting familiar with Symfony conventions (DI, services, Doctrine, Messenger). It's a modest investment compared to the functional coverage you get back in return.
§ end