Skip to content

Runtime & Architecture / Comparison

Swoole PHP Frameworks in 2026: Laravel Octane vs Hyperf vs Semitexa

By ·

Keeping a PHP application in memory is no longer exotic. The question in 2026 is which shape of it fits your project: an existing Laravel app on an application server, a coroutine framework built for services, or a framework designed around the worker from the start.

This guide compares three ways to run PHP on Swoole: Laravel Octane, Hyperf and Semitexa. They are not three versions of the same product. Octane is a package that serves a normal Laravel application from a long-running server. Hyperf and Semitexa are frameworks written for that server. That difference matters more than any benchmark, so the comparison starts there. Facts about Octane and Hyperf come from their own documentation and repositories, checked on October 17, 2026; we build Semitexa, so read that part knowing who wrote it.

The short answer

Choose Laravel Octane if you already run Laravel. You keep the ecosystem, the packages and the team's habits, and gain a warm application between requests. You also take on the job of keeping your own code free of request state in long-lived objects.

Choose Hyperf for coroutine-heavy services. Microservices, RPC, many concurrent outbound calls, connection pools everywhere. It is the most mature Swoole-native framework of the three, with a large component set.

Choose Semitexa for a new application where the server owns the page and AI agents work on the code. Server-rendered pages with live updates over SSE, multi-tenant SaaS, and a codebase that agents query and verify instead of grep. It is the youngest of the three, with the smallest ecosystem.

Laravel Octane vs Hyperf vs Semitexa at a glance

QuestionLaravel OctaneHyperfSemitexa
What it isA package that boots a Laravel app once and serves requests from memory.A coroutine framework for Swoole or Swow.A framework with a Swoole-first runtime, designed for long-running workers.
Version and PHPOctane 2.x (v2.22.0), PHP 8.1+, Laravel 10 to 13.Hyperf 3.2, PHP 8.2+.Date-tagged releases, PHP 8.4, Swoole 6.x.
ServersFrankenPHP, Swoole, Open Swoole, RoadRunner.Swoole 5+ or Swow.Swoole, inside Docker.
State between requestsLaravel resets its own state; your singletons and statics are yours to keep clean.Container objects are singletons; per-request data goes in the coroutine Context.Services are worker-scoped; handlers and request, auth, tenant and locale context are execution-scoped.
ConcurrencyOctane::concurrently, ticks, cache and tables, on Swoole only.Coroutines throughout, pooled clients.Coroutines, Channel-based connection pools with bounded waiting.
Multi-tenancyThird-party, usually stancl/tenancy.Small community packages.Built in: ORM, cache and storage scope to the resolved tenant.
Live pagesBlade; SSE responses in Laravel 13; WebSockets through Reverb.Blade, Twig and others; a WebSocket server component.Server-rendered pages with deferred blocks and live updates over SSE.
AI coding agentsLaravel Boost: guidelines, skills and an MCP server.Early MCP packages.Project Graph for impact queries, ai:verify for every change.
EcosystemLaravel's, the largest in PHP.Large component set, a mostly Chinese-speaking community.Young and small; first-party packages only.

Versions and counts change. Octane 2.22.0 and Hyperf 3.2.0 were current on Packagist when this was written.

What a long-running worker changes

Under PHP-FPM, each request starts a clean application and throws it away at the end. A long-running worker boots once and serves many requests, often several at a time with coroutines. That removes repeated bootstrap work, and it turns object lifetimes into a design decision: anything that survives the request can carry one user's data into the next user's response.

All three frameworks answer the same three questions differently. Which objects live with the worker? Where does per-request data go? What stops one slow request from holding up the others? Long-running PHP: inside the Semitexa runtime walks through the model with a live example; here the focus is on how each framework draws the lines.

Laravel Octane: your Laravel app, kept warm

Octane's strength is that it asks very little of an existing Laravel application. Routes, controllers, form requests and middleware stay as they are, and type-hinting the Request in a controller method is safe. Service providers register and boot once per worker, and requests reuse the result.

The cost is a rule you now have to follow everywhere. The Octane documentation says Laravel resets its own state between requests but "does not always know how to reset the global state created by your application". Injecting the container, the request or the config repository into a singleton leaves it holding a stale copy; static arrays that grow per request leak memory. The documented fixes are a resolver closure instead of the object, or passing request data at call time. Workers restart after 500 requests by default as a safety net, and config/octane.php lets you list bindings to flush per request.

Some of Octane's extras depend on the server. Concurrent tasks, ticks, the Octane cache and tables are Swoole features; on FrankenPHP or RoadRunner you get the warm application without them. Multi-tenancy comes from packages such as stancl/tenancy, so check how your tenancy package handles long-lived workers before you switch.

For AI-assisted work, Laravel has the most complete official story of the three: Boost gives coding agents guidelines, skills and an MCP server that can read your schema, query the database and see the last error, and the Laravel AI SDK covers agents inside the app.

Hyperf: coroutines all the way down

Hyperf was written for Swoole, and it shows. Coroutines run throughout the framework, its database and Redis clients are coroutine-aware, and built-in components handle connection pooling so you do not acquire and release connections by hand. For services that spend most of their time waiting on other services, that model is the point.

Its rule for state is short and strict. The DI documentation says objects managed by the container are all singletons and must not hold request state. Per-request data goes into Hyperf\Context\Context, which belongs to the current coroutine and is released when it ends. Globals and statics are shared by every coroutine in the worker, and a blocking call stalls them all, so every I/O client has to be coroutine-enabled.

Hyperf uses attributes for routing and injection, supports lazy proxies and offers AOP through generated proxy classes, which apply only to objects the container creates. Views are supported through several engines, but rendering runs in a task worker by default, where the request and session are not available. Multi-tenancy is left to small community packages. The project reports about 104,000 requests per second in its own README benchmark; there is no independent 2026 figure to compare it with.

Semitexa: the worker as the starting point

Semitexa starts from the same place as Hyperf, an application that lives in a Swoole worker, and adds two opinions: there is one way through a request, and the framework should be able to describe itself to the people and agents changing it.

A request is a typed payload, a handler, a resource, then rendering. The container knows two lifetimes. Services injected with #[InjectAsReadonly] are built once per worker. Handlers are cloned for each execution, and anything injected with #[InjectAsMutable], such as the request or the tenant context, belongs to that execution. Database connections come from a Channel-based pool with bounded waiting. Workers recycle after a configurable number of requests and also watch their own memory, leaving through the same drain a reload uses before PHP's limit would cut a request off.

Tenancy is part of the framework. The tenant is resolved per request by path, subdomain, header or query parameter, and the ORM scopes queries, the cache namespaces keys and storage isolates paths to it; the tenant also travels with queued work. Pages are rendered on the server, slow regions are deferred and streamed in over SSE, and server-side changes can be pushed to pages that are already open. The live data grid is one example.

For coding agents, the framework scans itself into a Project Graph that answers "who uses this class" and "what does this change affect" before an edit, and bin/semitexa ai:verify runs the checks a change touches and records what it checked. If you work in Laravel, the same idea is available as semitexa/laravel-ai-verify, without leaving the framework.

The honest limits: Semitexa requires PHP 8.4 and Swoole 6.x and is developed and deployed with Docker; it has no FPM mode. Its ecosystem is its own packages, and its community is small. It runs semitexa.com and a few production projects, not thousands.

One task, three frameworks: the current tenant

A small task shows the difference in where state lives: a service needs the current tenant. These are minimal sketches of each framework's documented approach, not complete code.

Laravel Octane

Do not hand the singleton the request or a tenant object when it is built. Give it a way to ask for the current one, as the Octane docs recommend for the container and request.

// A singleton built once per worker must not keep the first request's tenant.
$this->app->singleton(InvoiceService::class, fn ($app) =>
    new InvoiceService(fn () => $app->make(CurrentTenant::class)->id())
);

Hyperf

Keep the tenant in the coroutine context and read it where you need it.

use Hyperf\Context\Context;

// In middleware, once per request (coroutine):
Context::set('tenant_id', $tenantId);

// In a singleton service:
$tenantId = Context::get('tenant_id');

Semitexa

Declare the lifetime on the property. The handler is a fresh clone per execution, and the tenant context is injected for that execution.

#[AsPayloadHandler(payload: InvoicePayload::class, resource: InvoiceResource::class)]
final class InvoiceHandler implements TypedHandlerInterface
{
    #[InjectAsReadonly]   // built once per worker
    protected InvoiceRepository $invoices;

    #[InjectAsMutable]    // this execution's tenant
    protected TenantContextInterface $tenantContext;
}

None of the three makes the mistake impossible. They differ in where the rule is written: in your discipline with Octane, in a context API with Hyperf, and in the type and attribute of each dependency with Semitexa, where a review or an agent can see it.

How to choose

You have a Laravel application

Start with Octane. Audit your singletons and statics, load-test, and keep the worker restart limit until you trust the code. Rewriting a working Laravel app in another framework for speed alone rarely pays off.

You are building services that mostly wait on other services

Hyperf's coroutine model, pools and service components are built for that work, and it has years of production use behind it.

You are starting a product with live pages, tenants and AI agents in the workflow

That is the case Semitexa was designed for: one typed path per request, tenancy and SSE in the framework, and a codebase agents can query and verify. Weigh that against a younger ecosystem and the PHP 8.4, Docker and Swoole requirements.

Common questions

Is Laravel Octane a Swoole framework?

No. Octane is a Laravel package that serves a Laravel application from a long-running server. Swoole is one of four servers it supports, alongside Open Swoole, RoadRunner and FrankenPHP.

Is Hyperf faster than Laravel Octane?

Hyperf publishes higher numbers in its own benchmark, and a coroutine-native stack usually does less work per request than a full Laravel boot kept warm. For most applications the database and external calls decide response time, so measure your own workload before choosing on throughput.

Can I use Laravel packages in Hyperf or Semitexa?

Not as Laravel packages. Hyperf has its own components and can use framework-agnostic Composer libraries. Semitexa uses its own packages plus framework-agnostic libraries. If the Laravel ecosystem is the reason you chose Laravel, Octane keeps it.

Does Semitexa run without Swoole or Docker?

No. Semitexa targets PHP 8.4 with Swoole 6.x and ships its runtime as Docker images, so the host needs Docker rather than a local PHP install.

What is the biggest risk when moving to long-running PHP?

State that outlives the request: a singleton that captured the first user, a static cache that only grows, a connection one coroutine is still using. Each framework gives you tools for it; none removes the need to think about lifetimes.

← Back to the Semitexa Blog

Want the runtime model in more depth? Long-running PHP: inside the Semitexa runtime follows a request from worker startup to the HTML, with a live example.

Have a product idea?One free MVP every month