Files
php-qml/framework/php/src/Publisher.php

36 lines
994 B
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace PhpQml\Bridge;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
/**
v0.2.0 (1/N): public API surface — interfaces + BridgeOp enum Establishes the contract layer the rest of v0.2.0 builds on. Pre-1.0 SemVer break: ModelPublisher::publishEntityChange() now takes BridgeOp instead of a raw string. Interfaces (Symfony idiom: same namespace as concrete, like HubInterface next to Hub): - PublisherInterface — publish(string, array, bool) - ModelPublisherInterface — publishEntityChange(object, BridgeOp) - CorrelationContextInterface — set/get/clear App code should typehint these instead of the concretes so swappable implementations (offline-buffer publisher, multi-hub fan-out, request- stamp correlation) remain non-breaking. Concrete classes implement them unchanged; autowire continues to inject the implementations transparently. BridgeOp: PHP 8.1 string-backed enum with cases Upsert / Delete / Replace / Event matching PLAN.md §4's envelope `op` wire format. Internal call sites updated; tests use the cases directly. Switched typehints: - ModelPublisher ctor: PublisherInterface + CorrelationContextInterface - DoctrineBridgeListener ctor: ModelPublisherInterface - HealthController ctor: PublisherInterface (still emits `Publisher` as bundle canary value — `::class` resolves to the concrete class name regardless of typehint, so bundled-supervisor.sh's grep stays green) - skeleton PingController ctor: PublisherInterface (canonical app pattern — example/todo has no Publisher consumer to update) Drive-by: removed deprecated setAccessible(true) call in ModelPublisher::extractId — PHP 8.1+ allows reflection without it. PHPStan + cs-fixer + PHPUnit (17/17) + maker snapshot all pass; dev container compiles in the example app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:50:01 +02:00
* Default implementation of {@see PublisherInterface}: a thin wrapper over
* `symfony/mercure`'s `HubInterface` that JSON-encodes the envelope and
* forwards the publish.
*
* Topic conventions and envelope shape are defined in PLAN.md §4.
v0.2.0 (1/N): public API surface — interfaces + BridgeOp enum Establishes the contract layer the rest of v0.2.0 builds on. Pre-1.0 SemVer break: ModelPublisher::publishEntityChange() now takes BridgeOp instead of a raw string. Interfaces (Symfony idiom: same namespace as concrete, like HubInterface next to Hub): - PublisherInterface — publish(string, array, bool) - ModelPublisherInterface — publishEntityChange(object, BridgeOp) - CorrelationContextInterface — set/get/clear App code should typehint these instead of the concretes so swappable implementations (offline-buffer publisher, multi-hub fan-out, request- stamp correlation) remain non-breaking. Concrete classes implement them unchanged; autowire continues to inject the implementations transparently. BridgeOp: PHP 8.1 string-backed enum with cases Upsert / Delete / Replace / Event matching PLAN.md §4's envelope `op` wire format. Internal call sites updated; tests use the cases directly. Switched typehints: - ModelPublisher ctor: PublisherInterface + CorrelationContextInterface - DoctrineBridgeListener ctor: ModelPublisherInterface - HealthController ctor: PublisherInterface (still emits `Publisher` as bundle canary value — `::class` resolves to the concrete class name regardless of typehint, so bundled-supervisor.sh's grep stays green) - skeleton PingController ctor: PublisherInterface (canonical app pattern — example/todo has no Publisher consumer to update) Drive-by: removed deprecated setAccessible(true) call in ModelPublisher::extractId — PHP 8.1+ allows reflection without it. PHPStan + cs-fixer + PHPUnit (17/17) + maker snapshot all pass; dev container compiles in the example app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:50:01 +02:00
* Application code should typehint `PublisherInterface` instead of this
* concrete class so swappable implementations (offline buffer, multi-hub
* fan-out) remain a non-breaking change.
*/
v0.2.0 (1/N): public API surface — interfaces + BridgeOp enum Establishes the contract layer the rest of v0.2.0 builds on. Pre-1.0 SemVer break: ModelPublisher::publishEntityChange() now takes BridgeOp instead of a raw string. Interfaces (Symfony idiom: same namespace as concrete, like HubInterface next to Hub): - PublisherInterface — publish(string, array, bool) - ModelPublisherInterface — publishEntityChange(object, BridgeOp) - CorrelationContextInterface — set/get/clear App code should typehint these instead of the concretes so swappable implementations (offline-buffer publisher, multi-hub fan-out, request- stamp correlation) remain non-breaking. Concrete classes implement them unchanged; autowire continues to inject the implementations transparently. BridgeOp: PHP 8.1 string-backed enum with cases Upsert / Delete / Replace / Event matching PLAN.md §4's envelope `op` wire format. Internal call sites updated; tests use the cases directly. Switched typehints: - ModelPublisher ctor: PublisherInterface + CorrelationContextInterface - DoctrineBridgeListener ctor: ModelPublisherInterface - HealthController ctor: PublisherInterface (still emits `Publisher` as bundle canary value — `::class` resolves to the concrete class name regardless of typehint, so bundled-supervisor.sh's grep stays green) - skeleton PingController ctor: PublisherInterface (canonical app pattern — example/todo has no Publisher consumer to update) Drive-by: removed deprecated setAccessible(true) call in ModelPublisher::extractId — PHP 8.1+ allows reflection without it. PHPStan + cs-fixer + PHPUnit (17/17) + maker snapshot all pass; dev container compiles in the example app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:50:01 +02:00
final readonly class Publisher implements PublisherInterface
{
public function __construct(
private HubInterface $hub,
) {
}
public function publish(string $topic, array $envelope, bool $private = false): string
{
return $this->hub->publish(new Update(
$topic,
json_encode($envelope, \JSON_THROW_ON_ERROR),
$private,
));
}
}