Download the PHP package josbeir/cakephp-mercure without Composer
On this page you can find all versions of the php package josbeir/cakephp-mercure. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package cakephp-mercure
CakePHP Mercure Plugin
Push real-time updates to clients using the Mercure protocol.
Table of Contents
- Overview
- Installation
- Installing the Plugin
- Running a Mercure Hub
- Custom DDEV Setup with Nginx Proxy
- Configuration
- Basic Usage
- Choosing Your Authorization Strategy
- Publishing Updates
- Publishing JSON Data
- Publishing Rendered Views
- Subscribing to Updates
- Authorization
- Publishing Private Updates
- Setting Authorization Cookies
- Using the Component
- Setting Default Topics
- Using the Facade classes
- Mercure Discovery
- Using the Component
- Using Middleware
- Advanced Configuration
- JWT Token Strategies
- HTTP Client Options
- Cookie Configuration
- Testing
- API Reference
- Publisher
- MercureComponent
- Authorization
- MercureHelper
- Update
- JsonUpdate
- ViewUpdate
- MercureDiscoveryMiddleware
- Cookbook: Queue Job Progress Tracking
- Creating a Progress Trait
- Using the Trait in Queue Tasks
- Frontend: Real-time Progress Display
- Controller for Triggering Jobs
- Contributing
- License
Overview
This plugin provides integration between CakePHP applications and the Mercure protocol, enabling real-time push capabilities for modern web applications.
Mercure is an open protocol built on top of Server-Sent Events (SSE) that allows you to:
- Push updates from your server to clients in real-time
- Create live-updating UIs without complex WebSocket infrastructure
- Broadcast data changes to multiple connected users
- Handle authorization for private updates
- Automatically reconnect with missed update retrieval
Common use cases include live dashboards, collaborative editing, real-time notifications, and chat applications.
Installation
Installing the Plugin
[!IMPORTANT] Minimum Requirements:
- PHP 8.2 or higher
- CakePHP 5.0.1 or higher
Install the plugin using Composer:
Load the plugin in your Application.php:
Alternatively, you can add it to config/plugins.php:
Running a Mercure Hub
Mercure requires a hub server to manage persistent SSE connections. Download the official hub from Mercure.rocks.
For development, you can run the hub using Docker:
If you're using DDEV, you can install the Mercure add-on:
For more information, see the DDEV Mercure add-on.
The hub will be available at http://localhost:3000/.well-known/mercure.
Custom DDEV Setup with Nginx Proxy
For environments with dynamic ports (like DDEV), you can set up a custom Mercure container with an nginx proxy. This allows using relative URLs that work regardless of the port.
Create .ddev/docker-compose.mercure.yaml:
Create .ddev/nginx/mercure.conf:
Then configure relative URLs in config/app_custom.php:
Run ddev restart to apply the changes. Using a relative public_url like /mercure-hub means the browser will automatically use the current origin, making it work regardless of DDEV's dynamic port assignment.
[!TIP] Using FrankenPHP? You're good to go! FrankenPHP has Mercure built in—no separate hub needed. See the FrankenPHP Mercure documentation for details.
Configuration
The plugin comes with sensible defaults and multiple configuration options.
Quick Setup (Environment Variables):
For development, the fastest way to get started is using environment variables in your .env file:
Configuration Files:
The plugin loads configuration in this order:
- Plugin defaults -
vendor/josbeir/cakephp-mercure/config/mercure.php(loaded automatically) - Your overrides -
config/app_mercure.php(optional, loaded after plugin defaults)
Create config/app_mercure.php in your project to customize any settings. Your values will override the plugin defaults.
Cross-Subdomain Setup:
[!NOTE] If your Mercure hub runs on a different subdomain than your CakePHP application (e.g.,
hub.example.comvsapp.example.com), you must configure the cookie domain:This enables the authorization cookie to be accessible by both your application and the Mercure hub. Without this setting, authorization will fail for cross-subdomain requests.
For a complete list of available environment variables, see the plugin's config/mercure.php file.
Basic Usage
The plugin provides multiple integration points depending on your use case:
- Controllers: Use the
MercureComponentto centrally manage both authorization and subscriptions as topics - Templates: Use the
MercureHelperto generate Mercure topic URLs for EventSource subscriptions in your views and templates. - Services & Manual Control: Use the
Publisherfacade to publish updates and theAuthorizationfacade for direct response manipulation when you need lower-level control (e.g., outside controllers/views, such as in background jobs or custom middleware).
[!TIP] Note: Facades (
Publisher,Authorization) can be used in any context where a CakePHP component or helper does not fit, such as in queue jobs, commands, models, or other non-HTTP or background processing code. This makes them ideal for use outside of controllers and views.
Choosing Your Authorization Strategy
Pick the approach that best fits your workflow:
| Scenario | Recommended Approach | Method to Use |
|---|---|---|
| Authorize in controller, display URL in template | MercureComponent + MercureHelper |
$this->Mercure->authorize() in controller, $this->Mercure->url($topics) in template |
| Public topics (no authorization) | MercureHelper |
$this->Mercure->url($topics) |
| Manual response control | Authorization facade |
Authorization::setCookie($response, $subscribe) |
Publishing Updates
Use the Publisher facade to send updates to the Mercure hub:
The topics parameter identifies the resource being updated. It should be a unique IRI (Internationalized Resource Identifier), typically the resource's URL.
You can publish to multiple topics simultaneously:
[!TIP] Using MercureComponent in Controllers: If you're publishing from a controller, the
MercureComponentprovides convenient methods that eliminate the need to manually create Update objects or call the Publisher facade:See the MercureComponent API Reference for all available methods.
Publishing JSON Data
For convenience when publishing JSON data, use the JsonUpdate class which automatically encodes arrays and objects to JSON:
You can customize JSON encoding options:
For private updates and event metadata:
Publishing Rendered Views
Use the ViewUpdate class to automatically render CakePHP views or elements and publish the rendered HTML.
[!NOTE] This is especially handy when using JavaScript frameworks like htmx (for instance, using the htmx-sse extension), Hotwire (with Turbo Streams), or similar reactive libraries, which can consume and swap HTML fragments received over Mercure for seamless real-time UI updates.
You can also render full templates:
For private updates with event metadata:
Subscribing to Updates
The plugin provides a View Helper to generate Mercure URLs in your templates.
First, load the helper in AppView:
Then subscribe to updates from your templates:
Subscribe to multiple topics:
If you need to access the Mercure URL from an external JavaScript file, store it in a data element:
Then retrieve it from your JavaScript:
Authorization
Publishing Private Updates
Mark updates as private to restrict access to authorized subscribers:
Private updates are only delivered to subscribers with valid JWT tokens containing matching topic selectors.
Setting Authorization Cookies
Using the Component
For centralized authorization logic, use the MercureComponent in controllers. Topics added via the component are automatically available in your views:
The component provides separation of concerns (authorization in controller, URLs in template). You can also enable automatic discovery headers:
Setting Default Topics
You can configure default topics that will be automatically merged with any topics you provide to url(). This is useful when you want certain topics (like notifications or global alerts) to be included in every subscription:
You can also set default topics using the MercureComponent in your controller:
Now every call to MercureHelper::url() will automatically include these default topics:
Using the Facade classes
For more control or when not using controllers, you can use the Authorization facade directly:
The cookie must be set before establishing the EventSource connection. The Mercure hub and your CakePHP application should share the same domain (different subdomains are allowed).
Mercure Discovery
The Mercure protocol supports automatic hub discovery via HTTP Link headers. This allows clients to discover the hub URL without hardcoding it, making your application more flexible and following the Mercure specification.
Using the Component
Add the discovery header from your controller using the MercureComponent:
This adds a Link header to the response:
Clients can then discover the hub URL from the response headers:
Discovery with Topics and Attributes
The discover() method supports optional parameters that align with the Mercure specification:
Include subscription topics in discovery:
When you want the rel="self" Link header to include the topics the user is authorized for, enable discoverWithTopics:
This generates both headers:
The rel="self" header provides a ready-to-use subscription URL that clients can connect to directly.
Using Middleware
This is an alternative approach to add the discovery header automatically to all responses by using middleware:
The middleware automatically adds the Link header with rel="mercure" to all responses, making the hub URL discoverable by any client.
[!TIP] The discovery header uses the
public_urlconfiguration (or falls back tourlif not set), ensuring clients always receive the correct publicly-accessible hub URL.
Advanced Configuration
JWT Token Strategies
The plugin supports multiple JWT generation strategies:
1. Secret-based (default):
[!IMPORTANT] When using HMAC algorithms, ensure your secret meets minimum lengths:
HS256: at least 32 bytesHS384: at least 48 bytesHS512: at least 64 bytes
2. Static token:
3. Custom provider:
Implement Mercure\Jwt\TokenProviderInterface:
4. Custom factory:
Implement Mercure\Jwt\TokenFactoryInterface:
HTTP Client Options
Configure the HTTP client used to communicate with the Mercure hub:
Cookie Configuration
The authorization cookie contains a JWT token that authenticates subscribers to private topics. JWT expiry is automatically calculated based on cookie lifetime settings.
JWT Expiry Management:
The plugin automatically sets the JWT exp claim based on cookie lifetime, following this priority:
additionalClaims['exp']- Per-request overridecookie.expires- Explicit datetime ('+1 hour', etc.)cookie.lifetime- Seconds (3600for 1 hour,0for session)ini_get('session.cookie_lifetime')- PHP session setting- Default: +1 hour
Session cookies (lifetime: 0) automatically get a 1-hour JWT expiry for security.
Security Notes:
httponly: true(default) prevents JavaScript access while still allowing EventSource connectionssamesite: 'strict'(default) provides CSRF protectionsecure: truerequires HTTPS (recommended for production)- JWT tokens always expire - no infinite authorization
For more details, see the CakePHP Cookie documentation.
Testing
For testing, mock the Publisher service to avoid actual HTTP calls:
Similarly for Authorization:
API Reference
Publisher
| Method | Returns | Description |
|---|---|---|
publish(Update $update) |
bool |
Publish an update to the hub |
setInstance(PublisherInterface $publisher) |
void |
Set custom instance (for testing) |
clear() |
void |
Clear singleton instance |
MercureComponent
Controller component for centralized authorization with separation of concerns and automatic dependency injection.
Loading the Component:
Methods:
| Method | Returns | Description |
|---|---|---|
addTopic(string $topic) |
$this |
Add a topic for the view to subscribe to |
addTopics(array $topics) |
$this |
Add multiple topics for the view |
getTopics() |
array |
Get all topics added in the component |
resetTopics() |
$this |
Reset all accumulated topics |
addSubscribe(string $topic, array $additionalClaims = []) |
$this |
Add a topic to authorize with optional JWT claims |
addSubscribes(array $topics, array $additionalClaims = []) |
$this |
Add multiple topics to authorize with optional JWT claims |
getSubscribe() |
array |
Get accumulated subscribe topics |
getAdditionalClaims() |
array |
Get accumulated JWT claims |
resetSubscribe() |
$this |
Reset accumulated subscribe topics |
resetAdditionalClaims() |
$this |
Reset accumulated JWT claims |
authorize(array $subscribe = [], array $additionalClaims = []) |
$this |
Set authorization cookie (merges with accumulated state, then resets) |
clearAuthorization() |
$this |
Clear authorization cookie |
discover(?bool $includeTopics, ?string $lastEventId, ?string $contentType, ?string $keySet) |
$this |
Add Mercure discovery Link headers with optional attributes and topics |
publish(Update $update) |
bool |
Publish an update to the Mercure hub |
publishJson(string\|array $topics, mixed $data, ...) |
bool |
Publish JSON data (auto-encodes) |
publishSimple(string\|array $topics, string $data, ...) |
bool |
Publish simple string data (no encoding) |
publishView(string\|array $topics, ?string $template, ?string $element, array $data, ...) |
bool |
Publish rendered view/element |
getCookieName() |
string |
Get the cookie name |
Topic Management:
Topics added in the controller are automatically available in MercureHelper in your views:
Authorization Builder Pattern:
Build up authorization topics and claims fluently, then call authorize():
Claims accumulate across multiple addSubscribe() calls. The authorize() method automatically resets accumulated state after setting the cookie.
Publishing convenience methods make it easy to publish updates directly from controllers:
Authorization
Static facade for direct authorization management (alternative to component).
| Method | Returns | Description |
|---|---|---|
setCookie(Response $response, array $subscribe, array $additionalClaims) |
Response |
Set authorization cookie |
clearCookie(Response $response) |
Response |
Clear authorization cookie |
addDiscoveryHeader(Response $response) |
Response |
Add Mercure discovery Link header |
getCookieName() |
string |
Get the cookie name |
MercureHelper
| Method | Returns | Description |
|---|---|---|
url(array\|string\|null $topics, array $subscribe, array $additionalClaims) |
string |
Get hub URL and optionally authorize (only sets cookie when $subscribe is provided). Merges with default topics if configured. |
addTopic(string $topic) |
$this |
Add a single topic to default topics (fluent interface) |
addTopics(array $topics) |
$this |
Add multiple topics to default topics (fluent interface) |
Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
defaultTopics |
array |
[] |
Topics to automatically merge with every subscription (read-only, not mutated by addTopic()/addTopics()) |
Update
Base class for Mercure updates. For most use cases, consider using JsonUpdate or ViewUpdate instead.
Constructor:
Constructor Parameters:
| Parameter | Type | Description |
|---|---|---|
$topics |
string\|array |
Topic IRI(s) for the update |
$data |
string |
Update content (typically JSON) |
$private |
bool |
Whether the update requires authorization |
$id |
?string |
Optional SSE event ID |
$type |
?string |
Optional SSE event type |
$retry |
?int |
Optional reconnection time in milliseconds |
Methods:
| Method | Returns | Description |
|---|---|---|
getTopics() |
array |
Get topics |
getData() |
string |
Get data |
isPrivate() |
bool |
Check if private |
getId() |
?string |
Get event ID |
getType() |
?string |
Get event type |
getRetry() |
?int |
Get retry value |
JsonUpdate
Specialized Update class that automatically encodes data to JSON. Supports both static factory method and fluent builder pattern.
Fluent Builder Pattern (Recommended):
Builder Methods:
| Method | Parameter | Returns | Description |
|---|---|---|---|
data(mixed $data) |
Data to encode | $this |
Set data to encode as JSON |
jsonOptions(int $options) |
JSON options | $this |
Set JSON encoding options |
private(bool $private = true) |
Private flag | $this |
Mark as private update |
id(string $id) |
Event ID | $this |
Set SSE event ID |
type(string $type) |
Event type | $this |
Set SSE event type |
retry(int $retry) |
Retry delay (ms) | $this |
Set retry delay |
build() |
- | Update |
Build and return Update |
Static Factory Method:
ViewUpdate
Specialized Update class that automatically renders CakePHP views or elements. Supports both static factory method and fluent builder pattern.
Fluent Builder Pattern (Recommended):
Builder Methods:
| Method | Parameter | Returns | Description |
|---|---|---|---|
template(string $template) |
Template name | $this |
Set template to render |
element(string $element) |
Element name | $this |
Set element to render |
viewVars(array $viewVars) |
View variables | $this |
Set view variables |
set(string $key, mixed $value) |
Key, value | $this |
Set single view variable |
layout(?string $layout) |
Layout name | $this |
Set layout (null to disable) |
viewOptions(array $options) |
ViewBuilder options | $this |
Set ViewBuilder options |
private(bool $private = true) |
Private flag | $this |
Mark as private update |
id(string $id) |
Event ID | $this |
Set SSE event ID |
type(string $type) |
Event type | $this |
Set SSE event type |
retry(int $retry) |
Retry delay (ms) | $this |
Set retry delay |
build() |
- | Update |
Build and return Update |
Static Factory Method:
Parameters:
| Parameter | Type | Description |
|---|---|---|
$topics |
string\|array |
Topic IRI(s) for the update |
$template |
?string |
Template to render (e.g., 'Books/view') |
$element |
?string |
Element to render (e.g., 'Books/item') |
$data |
array |
View variables to pass to the template/element |
$layout |
?string |
Layout to use (null for no layout) |
$private |
bool |
Whether this is a private update |
$id |
?string |
Optional SSE event ID |
$type |
?string |
Optional SSE event type |
$retry |
?int |
Optional reconnection time in milliseconds |
[!NOTE] Either
templateorelementmust be specified, but not both.
Example:
MercureDiscoveryMiddleware
A PSR-15 middleware that automatically adds the Mercure discovery Link header to all responses.
Usage:
The middleware adds a Link header to every response:
This allows clients to automatically discover the Mercure hub URL without hardcoding it in your application.
For more information about the Mercure protocol, visit mercure.rocks.
Cookbook: Queue Job Progress Tracking
A common use case for Mercure is providing real-time progress updates for background queue jobs. This cookbook shows how to integrate Mercure with dereuromark/cakephp-queue to display live progress bars and status updates.
Creating a Progress Trait
First, create a reusable trait for publishing progress updates from queue tasks:
Using the Trait in Queue Tasks
Use the trait in your queue task classes:
Frontend: Real-time Progress Display
Create a template that subscribes to job updates and displays progress:
Controller for Triggering Jobs
[!TIP] Throttling Updates: For jobs that process thousands of items, avoid publishing on every iteration. Instead, publish every N items (e.g., every 10 or 100) or use time-based throttling to prevent flooding the Mercure hub.
Contributing
Contributions are welcome! Please follow these guidelines:
-
Code Quality: Ensure all code passes quality checks:
-
Code Style: Follow CakePHP coding standards. Use
composer cs-fixto automatically fix style issues. -
Tests: Add tests for new features and ensure all tests pass.
-
Documentation: Update the README and inline documentation as needed.
- Pull Requests: Submit PRs against the
mainbranch with a clear description of changes.
License
MIT License. See LICENSE.md for details.