If you have used Spring Boot, you already know most of Winter Boot.
In an earlier job I was writing Spring Boot and PHP side by side. Spring Boot hooked me. You could start implementing right away, dependency injection just worked, and convention over configuration meant the code I wrote was my app logic and little else. Microservices came together fast. So I kept asking myself: why can't we do this in PHP? I made the first commit in February 2021 to find out.
Winter Boot is an open-source framework for writing microservices in PHP 8.5. It takes the Spring Boot programming model (beans, dependency injection, declarative transactions, caching, scheduling) and maps it onto PHP attributes. There is no XML and no code generation step.
This post is a short tour, not a manual. The full documentation covers everything else, and the code is on GitHub.
A small service
Here is a service that reads a product from Postgres and caches it:
<?php
namespace com\example\shop;
use dev\winterframework\cache\stereotype\CacheEvict;
use dev\winterframework\cache\stereotype\Cacheable;
use dev\winterframework\pdbc\PdbcTemplate;
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\stereotype\Service;
use dev\winterframework\txn\stereotype\Transactional;
#[Service]
class ProductService {
#[Autowired]
private PdbcTemplate $db;
#[Cacheable(cacheNames: 'products')]
public function find(int $id): array {
return $this->db->queryForMap(
'SELECT * FROM products WHERE id = ?',
[$id]
);
}
#[Transactional]
#[CacheEvict(cacheNames: 'products', allEntries: true)]
public function rename(int $id, string $name): void {
$this->db->update(
'UPDATE products SET name = ? WHERE id = ?',
[$name, $id]
);
}
}
And a controller that exposes it (imports trimmed from here on):
#[RestController]
class ProductController {
#[Autowired]
private ProductService $products;
#[GetMapping(path: '/products/{id}')]
public function get(#[PathVariable] int $id): ResponseEntity {
return ResponseEntity::ok()->withJson($this->products->find($id));
}
}
The entry point is one class:
require_once __DIR__ . '/vendor/autoload.php';
#[EnableCaching]
#[EnableTransactionManagement]
#[WinterBootApplication(
configDirectory: [__DIR__ . '/config'],
scanNamespaces: [['com\\example\\shop', __DIR__ . '/src']]
)]
class ShopApplication {
public static function main(): void {
(new WinterWebSwooleApplication())->run(self::class);
}
}
ShopApplication::main();
Plus a few lines of application.yml:
server:
port: 8080
datasource:
- name: shopdb
isPrimary: true
url: "pgsql:host=localhost;dbname=shop"
username: shop
password: "*****"
Run php ShopApplication.php, then curl localhost:8080/products/1. That's the whole app.
How it runs
At boot the framework scans your namespaces, builds the bean graph and maps routes. This happens once per worker process, not per request. The server underneath is Swoole, so the process stays alive and the context stays in memory between requests. You pay the reflection and wiring cost up front, and request handling is just method calls.
#[Transactional] and #[Cacheable] are aspects. They wrap the method call without a generated proxy class. A small native PHP extension (winter_boot) handles the interception. That extension is required, which is one more thing to install. I think it's worth it. There are no proxy files to regenerate after a deploy, and final classes still work. The official Docker image ships with it already built, so in practice most people never compile it.
The rest of the box
The parts I end up using in almost every service:
-
PdbcTemplatefor plain SQL - PPA (PHP Persistence API), a lightweight ORM: mark a class with
#[Table], addPpaEntityTrait, and the framework writes theINSERT,UPDATEandDELETEfor you. The Doctrine module is there if you need a full ORM. -
#[Transactional]with propagation, rollback rules and multiple datasources -
#[Scheduled]and#[Async]for background work, no separate cron or queue runner - A built-in migrator for plain
.sqlfiles, easy to run as a pre-deploy step - Actuator endpoints for health and info, plus Prometheus metrics and OpenTelemetry
- Interceptors, CORS, coroutine-safe sessions
Optional modules cover Redis, Kafka, SQS, S3, OpenSearch and service discovery. You add a module through Composer and enable it in application.yml. Most of them live in winter-modules, and Doctrine ORM and DBAL support is in winter-doctrine.
One newer piece I like: put #[McpTool] on a controller method or a service method and it is served as a Model Context Protocol tool on /mcp. The input schema comes from the PHP signature and PHPDoc. So the same endpoint your frontend calls can be called by Claude or any other MCP client, through the same interceptors and auth.
Does it hold up?
The most demanding thing running on it right now is Snowprint, a self-hosted, cookie-less web analytics tool. In its benchmark it stores about 9,900 events per second on 2 CPUs and about 19,800 on 4 CPUs, with every accepted event persisted. It uses the parts shown above: REST controllers, PdbcTemplate, scheduled rollups, migrations and the actuator. It also tracks traffic on the Winter Boot docs site.
Rough edges
Winter Boot needs PHP 8.5, Swoole and the winter_boot extension. That is a narrower setup than a typical PHP-FPM host, and shared hosting is out. It is built for containers. If you deploy with Docker, use suvera/winter-boot as the base image and the extension problem goes away.
It is also a smaller project than Laravel or Symfony. The ecosystem is the framework plus its own modules, not thousands of third-party packages. Plain Composer libraries work fine, though, since it's still PHP.
Try it
composer require suvera/winter-boot
The easiest way to run it is the official Docker image, suvera/winter-boot. It's built on php:8.5-cli-alpine and comes with every extension the framework needs, plus Redis, Kafka and PostgreSQL drivers, already installed. There is also suvera/winter-boot-mysql if you're on MySQL. The installation guide shows a ready-to-use Dockerfile.
- Docs: https://suvera.github.io/winter-boot/
- Source: https://github.com/suvera/winter-boot
- Docker image: https://hub.docker.com/r/suvera/winter-boot
- Example service: https://github.com/suvera/winter-example-service
Issues and pull requests are welcome. If you build something with it, I'd like to hear about it.

Top comments (0)