winter / wn-octane-plugin
Serves Winter CMS from a persistent application server (FrankenPHP, Swoole or RoadRunner) via Laravel Octane.
Package info
github.com/wintercms/wn-octane-plugin
Type:winter-plugin
pkg:composer/winter/wn-octane-plugin
Requires
- php: >=8.1
- composer/installers: ~1.0 || ~2.0
- laravel/octane: ^2.5
- winter/storm: ^1.3
This package is auto-updated.
Last update: 2026-08-15 09:39:42 UTC
README
Adds Laravel Octane support to Winter CMS, so a site can be served by a persistent application server (FrankenPHP, Swoole or RoadRunner) instead of PHP-FPM.
With Octane, the application boots once and the same process serves many requests. Anything cached in memory during one request, such as the authenticated user, permission-filtered navigation menus, settings, view data or an open database transaction, is still present when the next request arrives, and has to be cleared so it is not served to the wrong user. Winter was written for PHP-FPM, where the whole process is discarded after every request, so it does not do this on its own.
This plugin:
- Registers Octane's service provider. Winter disables Laravel's package discovery, so installing
laravel/octaneby itself has no effect. - Clears request state at the start of every Octane operation, before Octane's own listeners run.
- Calls
resetWorkerState()on any plugin that defines it, so other plugins can clear their own request state.
Requirements
- Winter CMS with the worker-safety changes (currently the
feat/octane-worker-supportbranches ofwintercms/winterandwintercms/storm) - PHP 8.1 or later
- One of the application servers supported by Octane: FrankenPHP, Swoole or RoadRunner
Installation
This plugin is available for installation via Composer.
composer require winter/wn-octane-plugin
After installing the plugin you will need to run the migrations and (if you are using a public folder) republish your public directory.
php artisan migrate php artisan octane:install
Then start a worker:
php artisan octane:start
Refer to the Octane documentation for server configuration, deployment and tuning.
FrankenPHP and Winter's public path
Winter serves from the project root by default, but the frankenphp-worker.php file Octane generates assumes a public/ directory one level below the project. If the worker fails to start with worker frankenphp-worker.php has not reached frankenphp_handle_request(), replace the generated file in your project root with:
<?php $_SERVER['APP_BASE_PATH'] = $_ENV['APP_BASE_PATH'] ?? $_SERVER['APP_BASE_PATH'] ?? __DIR__; $_SERVER['APP_PUBLIC_PATH'] = $_ENV['APP_PUBLIC_PATH'] ?? $_SERVER['APP_PUBLIC_PATH'] ?? __DIR__; require $_SERVER['APP_BASE_PATH'].'/vendor/laravel/octane/bin/frankenphp-worker.php';
Octane only generates the file when it is missing, so the replacement persists. Sites using a public folder are not affected.
Disabling the plugin
Do not disable this plugin while the site is being served by Octane. A disabled plugin is never loaded, so the request state reset stops running while the workers continue serving requests, and request state (including the authenticated user) will be shared between users. To stop using Octane, switch the site back to PHP-FPM first. Once no worker is running, the plugin has no effect and can be disabled or removed safely.
The plugin is registered as elevated so that it is still loaded on the protected routes and console commands where Winter skips normal plugin initialization.
Plugin developers
If your plugin caches request data on the plugin class, a singleton or a static property, define a resetWorkerState() method to clear it. It is called at the start of every Octane operation:
public function resetWorkerState(): void { $this->somePerRequestCache = []; }
Notes:
- The method may be called more than once per request (for example, after a request that threw an exception partway through), so it must be safe to run repeatedly.
- Only clear data created during a request. Event listeners, navigation items and other registrations are built once per worker and are not rebuilt if discarded.
- Do not read the current request from this method. It runs before the incoming request has been bound, and its job is only to clear the previous one.
Plugins that only support Winter versions shipping the contract can declare implements \Winter\Storm\Contracts\ResetsWorkerState for the compile-time check. PHP will not load a class that implements a missing interface, so plugins supporting older Winter versions should define the method without implementing the interface.
Tests
The test suite boots a single application and dispatches requests through Octane's ApplicationGateway, the same code path a worker uses. Run it from the plugin directory inside a Winter installation:
../../../vendor/bin/phpunit
License
This plugin is licensed under the MIT license.