Files
php-qml/framework/php/src/Controller/HealthController.php

39 lines
1.2 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace PhpQml\Bridge\Controller;
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
use PhpQml\Bridge\PublisherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
/**
* Readiness probe used by the Qt host to detect when the backend is up.
* See PLAN.md §3 (*Startup*, step 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
* `PublisherInterface` is injected purely as a deep-health canary: if the
* bridge bundle's autoload or container wiring is broken (e.g. a packaging
* build with a dangling vendor path-repo symlink), this controller can't
* even be constructed, so /healthz fails 500 instead of misleadingly
* returning 200 against a half-loaded bundle. The response includes the
* concrete class name so packagers can detect a wrong-implementation
* deployment from the canary value alone.
*/
final class HealthController
{
public function __construct(
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
private readonly PublisherInterface $publisher,
) {
}
#[Route('/healthz', name: 'php_qml_bridge_healthz', methods: ['GET'])]
public function __invoke(): JsonResponse
{
return new JsonResponse([
'status' => 'ok',
'bundle' => $this->publisher::class,
]);
}
}