7 min read

Tempest 3.0: The PHP Framework That Gets Out of Your Way

Tempest 3.0 is the forward-thinking PHP framework requiring PHP 8.5, with a new exception handler, faster ORM, and a discovery system that removes boilerplate.

Featured image for "Tempest 3.0: The PHP Framework That Gets Out of Your Way"

If you spend most of your PHP time in Laravel or Symfony, you may have noticed a framework quietly picking up momentum over the past couple of years. Tempest describes itself as “the PHP framework that gets out of your way,” and after spending time with version 3.0 — released February 12, 2026 — that description feels earned. It is opinionated in the best sense: it makes choices so you do not have to, surfaces errors clearly, and does not ask you to write boilerplate to tell it things it can figure out on its own.

Tempest 3.0 is not a minor release. It requires PHP 8.5, ships a rewritten exception handler, a faster ORM, modern CSRF protection, closure-based validation, and an automated upgrade path using Rector. If you have been watching from the sidelines, this release is a good reason to give it a serious look.

The Discovery System: The Core Idea

Before digging into what is new in 3.0, it is worth understanding what makes Tempest structurally different from frameworks you already know. Tempest’s central concept is discovery: the framework scans your codebase and automatically registers routes, console commands, view components, event listeners, and middleware without requiring any manual registration.

In Laravel, you register a route in routes/web.php. In Symfony, you configure route attributes and then the router still needs to know where to scan. In Tempest, you put a route attribute on a controller method and it is discovered automatically. The same applies to console commands, event handlers, and middleware. There is no service provider, no boot() method, no register() call. The framework finds the things.

use Tempest\Router\Get;
use Tempest\Router\Response;
use Tempest\Router\Responses\Ok;

final class BookController
{
    #[Get('/books/{id}')]
    public function show(int $id): Response
    {
        $book = Book::find($id);
        return new Ok($book);
    }
}

That is the entire route definition. No route file. No service provider. Drop the file in your project and Tempest picks it up. This is the philosophical heart of the framework, and everything else builds on it.

PHP 8.5 Only: A Deliberate Statement

Tempest 3.0 drops support for all PHP versions below 8.5. This is a pattern the project has followed since the start: each major release tracks the latest PHP version and drops everything older. The maintainer, Brent Roose, has written extensively about why he believes OSS maintainers have a responsibility to push the PHP community forward rather than maintain broad compatibility that incentivizes running outdated runtimes.

In practice, this means Tempest gets to use the newest language features without polyfills or conditional compatibility shims. Closure-based validation (see below) is possible precisely because Tempest did not have to support PHP 8.4. If you are running PHP 8.5 in production already, this is a net positive.

New Exception Handler

Tempest has relied on Whoops since its earliest days. Whoops is solid, but it was always a stand-in for something more native to the framework. Version 3.0 ships a ground-up replacement: a custom exception renderer built to fit Tempest’s visual identity and designed to be extended over time.

The new handler shows the exception class name, the message, a code snippet of the throw site with highlighted line, the full stack trace with source context, and HTTP context for web exceptions. The visual presentation is cleaner and better integrated with the Tempest development environment than Whoops ever was. The maintainers have explicitly stated this is a starting point, not a final product, and they plan to extend it with deeper debugging capabilities in the 3.x cycle.

Modernized CSRF Protection

This one is worth calling out because it represents a genuine design shift. Tempest 3.0 removes the classic token-based CSRF approach (and the <x-csrf /> template tag that came with it) and replaces it with header-based verification using Sec-Fetch-Site and Sec-Fetch-Mode.

These HTTP headers are automatically set by modern browsers and indicate the origin of a request. A same-origin fetch has Sec-Fetch-Site: same-origin. A cross-origin form submission from a malicious page has Sec-Fetch-Site: cross-site. The framework validates these headers server-side instead of requiring a hidden token in every form.

The practical effect: you remove <x-csrf /> from your forms. Tempest handles the protection without client-side state. The approach is well-supported across modern browsers (Chromium, Firefox, Safari) and sidesteps the token synchronization issues that crop up in single-page apps and multi-tab sessions.

Faster ORM and Database Improvements

The Tempest ORM received targeted performance work in 3.0. Benchmarks from the release notes show significant improvement in query hydration for large result sets — the kind of difference that shows up in practice when loading collections of models.

Beyond raw speed, three practical improvements landed:

UUID primary keys. Models can now use UUIDs as their primary column with first-class support. No custom casting or workarounds required.

use Tempest\Database\IsDatabaseModel;
use Tempest\Database\IsUuid;

final class Order
{
    use IsDatabaseModel;
    use IsUuid;

    public string $id;
    public string $status;
}

Improved Query::toRawSql(). Debugging complex queries is significantly easier now that toRawSql() correctly interpolates bound parameters. You get the full SQL string with values substituted in, suitable for dropping directly into a database client.

Enum events in the event bus. A smaller but useful change: enum cases can now be dispatched as events, which makes event definitions more compact and type-safe.

Closure-Based Validation

PHP 8.5 added support for using closures as attribute arguments, and Tempest 3.0 takes immediate advantage of it. Where you previously needed to define a named class implementing the validation rule interface, you can now use an inline closure:

use Tempest\Database\IsDatabaseModel;
use Tempest\Validation\Rules\ValidateWith;

final class Book
{
    use IsDatabaseModel;

    #[ValidateWith(static function (string $value): bool {
        return strlen($value) >= 3 && ! str_starts_with($value, ' ');
    })]
    public string $title;

    #[ValidateWith(static function (string $value): bool {
        return filter_var($value, FILTER_VALIDATE_URL) !== false;
    })]
    public string $coverUrl;
}

For simple, single-use validation rules, this eliminates the ceremony of creating a dedicated rule class. Named classes are still the right choice when a rule is reused across multiple models, but for one-off constraints the closure form is cleaner.

View Improvements: Fallthrough Attributes

Tempest’s view engine (its own template system, not Blade or Twig) added support for fallthrough attributes in 3.0. When you pass an attribute to a component that does not explicitly declare it, the attribute is merged onto the component’s root element:

<!-- x-card.view.php -->
<div class="card">
    <x-slot />
</div>

<!-- usage in a page -->
<x-card class="bg-red-100">
    <p>Content here</p>
</x-card>

<!-- rendered output -->
<div class="card bg-red-100">
    <p>Content here</p>
</div>

This pattern is familiar from Vue and Alpine component models and it makes building small, composable view components significantly less awkward. The additional class is merged, not replaced, so you retain the component’s base styles.

Whitespace handling was also corrected: compiled views now preserve whitespace as-is, which fixes edge cases where stripped whitespace broke inline elements and made debugging compiled output difficult.

Upgrading from 2.x with Rector

Tempest ships an automated upgrade path powered by Rector. For most 2.x projects, the upgrade process is:

composer require rector/rector --dev
composer require tempest/framework:^3.0 --no-scripts

# Configure rector.php:
# ->withSets([TempestSetList::TEMPEST_30])

vendor/bin/rector

The --no-scripts flag prevents errors during the update before Rector has had a chance to fix breaking changes. After Rector runs, review the diff and handle anything it could not automate — primarily the LogConfig and DatabaseConfig changes, which require manual adjustment.

The remaining manual items are small in number and well-documented in the 3.0 upgrade guide.

What Is Next in the 3.x Cycle

The Tempest roadmap for the 3.x series includes three headline items: a dedicated debugging AI integrated into the development experience, FrankenPHP worker mode support, and a ground-up overhaul of the event and command bus. The event/command bus rewrite is particularly interesting — the current implementation is functional but lightweight, and the team has signaled they intend to make it significantly more powerful for complex domain-driven designs.

Current version as of this writing is 3.9.0, so the 3.x cycle has been active since the February launch.

Should You Use Tempest?

If you are building a greenfield application and you run PHP 8.5, Tempest is worth a serious evaluation. The discovery system eliminates a real category of boilerplate. The framework’s commitment to modern PHP means you write code that looks like idiomatic PHP 8.5 — typed, readonly-friendly, attribute-driven — rather than code that has to work across several PHP versions.

It is not a replacement for Laravel in the context of “I need an ecosystem with first-class Stripe and Mailgun packages.” Laravel’s ecosystem advantage is real and Tempest does not match it yet. But if you value a lean, coherent framework that enforces modern practices and gets out of your way when you are building, Tempest 3.0 is the strongest version of that idea in the PHP world right now.

Sources