facile-it/crossbar-http-publisher-bundle
Symfony bundle for publishing WAMP events to a Crossbar router over HTTP, letting PHP apps push real-time messages without maintaining a persistent WebSocket connection. Configure the publisher through Symfony services and start broadcasting quickly.
POST endpoint that accepts a JSON body (topic, args, kwargs) plus an HMAC-SHA256 signature of the raw body keyed with a shared secret. Subscribers get the event over their WAMP session. The bundle's entire job is: build that body, sign it, POST it, tell you if it worked. There is no socket, no handshake, no subscription management.ServiceProvider, no auto-discovery, no config publishing, and it depends on Symfony DI/Config components. In a Laravel app you should almost never install the bundle itself — install the underlying client (facile-it/crossbar-http-publisher, PSR-18/PSR-7 based) or vendor the ~20 lines of request-building logic. Read the bundle's DependencyInjection/Configuration.php first: it is the authoritative list of knobs (endpoint URL, key, secret, and likely HTTP client/timeout options). Then read the publisher class — that is the whole implementation.guzzlehttp/guzzle: ^6.0 pinned by another dependency) will resolve to the deprecated path — pin ^2.1 and plan the bump, or skip the package entirely and use the vendored snippet below. Note Guzzle 7 also raises the PHP floor (7.2.5+).config.json, add a web transport with a paths entry of type: publisher, a key, and a secret. Until that exists, every publish attempt is a 404/403 and you'll waste an hour debugging client code.App\Events\OrderShipped → publish to com.example.orders.shipped with the order id. Configure via env (CROSSBAR_PUBLISH_URL, CROSSBAR_KEY, CROSSBAR_SECRET), bind a small CrossbarPublisher wrapper in a service provider, and publish from a queued listener.$body = json_encode(['topic' => $topic, 'args' => [$payload]], JSON_UNESCAPED_SLASHES);
Http::withHeaders(['X-Crossbar-Signature' => hash_hmac('sha256', $body, $secret)])
->withBody($body, 'application/json')
->post($endpoint);
CrossbarPublisher class with publish(string $topic, array $args = [], array $kwargs = []). Inject it; never call Http:: from controllers or models. This is also your seam for tests.implements ShouldQueue on the listener is the standard pattern.{org}.{domain}.{entity}.{event} (acme.billing.invoice.paid). Subscribers hard-code these; renaming later is a breaking change.kwargs for named data, args for positional. Most PHP-side consumers want kwargs (associative) because it survives serialization better and reads well in JS: payload.items[0].sku.Carbon objects — pass a plain array / DTO you've explicitly ->toArray()'d. Publish identifiers, and let subscribers re-fetch if they need the full record.retry(3, 200) or a queued job's $tries/backoff. The publish is idempotent-ish for subscribers but not deduplicated by the router — a retry means subscribers may see the event twice. Design consumers tolerantly (event id in the payload, idempotent handlers).GuzzleHttp\Client (or any PSR-18 client). Build one in your container binding with timeout, connect_timeout, and http_errors => false, and pass that — it keeps retry/timeout policy in one place instead of scattered across call sites.$this->mock(CrossbarPublisher::class)), or if you inlined Http::, use Http::fake() and assert on the signature header and raw body — that's the part that actually breaks in production.topic, payload size, status code, and latency. Never log the secret or the full signature.json_encode() output and then let a client re-serialize (or you json_encode with different flags/ordering — JSON_UNESCAPED_SLASHES, JSON_PRESERVE_ZERO_FRACTION matter), the signature won't match and Crossbar returns a 403 with a body you'll misread as an auth problem. Build the string once, sign it, send that same string.200 is not a universal success signal across versions/configs. Parse and assert on the expected field, and log the body on unexpected responses.config:cache will bake the secret into a file; read it via env() at the boundary only.connect_timeout and timeout (2–5s) on the client you inject. Guzzle 7 also defaults http_errors to true, so a 4xx/5xx throws RequestException rather than returning the response — flip it off if you want to inspect Crossbar's error body.composer why guzzlehttp/guzzle before adopting, and pin ^2.1.curl -X POST $URL -H "X-Crossbar-Signature: $SIG" -H 'Content-Type: application/json' --data-binary @body.json. That isolates client bugs from config bugs instantly.How can I help you explore Laravel packages today?