Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Resonate Laravel Package

webpatser/resonate

Resonate is a Laravel package for adding real-time event broadcasting and pub/sub messaging to your app. It offers a simple, expressive API for publishing and subscribing to events with minimal configuration, helping you build interactive features faster.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit — This is a plugin surface on a Pusher-protocol websocket server. v0.4.0 completes a state machine that was previously one-sided: onSubscribe had no onUnsubscribe, and PluginContext::connectionsOn() (read) had no write counterpart. The practical consequence is that the plugin layer can now express the full connection↔channel lifecycle rather than approximating it with socket-level events. The most valuable addition is PluginContext::unsubscribe(Connection, string $channel): per-channel eviction without terminating the socket. That is the primitive most real plugin use cases need — entitlement revocation, moderation, channel-level access changes — and previously the only workaround was force-disconnect plus client reconnect, which is a sledgehammer.

Integration feasibility — Straightforward but breaking. ConnectionLifecycle goes from three methods to four; any existing implementer fails to compile until it adds onUnsubscribe. Blast radius is small because the plugin system is one release old, but "small" is an assumption to verify, not a fact.

Technical risk — Three concerns, in order:

  1. Asymmetric notification semantics. PluginContext::unsubscribe() is explicitly a direct action that does not emit onUnsubscribe. This is defensible (avoids reentrancy and self-triggered loops), but it means "connection left a channel" has two notification paths with different guarantees. Any plugin that treats onUnsubscribe as a complete audit log of channel departures will silently miss plugin-initiated evictions. This will be the single most common integration bug.
  2. onClose does not fan out unsubscribes. Documented and correct — a close reports once via onClose, not N times. But a plugin maintaining a channel-membership index must treat onClose as an implicit bulk unsubscribe. Whether that is possible depends on whether Connection still exposes its channel list at onClose time; if channels are cleared before the hook fires, plugins cannot reconcile and will leak stale membership. This is the key question to resolve before committing.
  3. Ordering in EventHandler::unsubscribe(). The hook fires after removal, so the hook observes post-state. Good for consistency, but combined with (1) it means a plugin reacting to onUnsubscribe sees the same state as a plugin that called unsubscribe() itself — while only one of them gets told. Confirm the ordering is contractual, not incidental.

Exception isolation on notifyUnsubscribe() matches the existing fan-out contract, which is the right call: one bad plugin must not break the event loop. The residual risk is silent state drift rather than an outage, which is harder to detect — worth a log line per swallowed exception.

Key questions

  • Does ConnectionLifecycle ship with an abstract base class or trait providing a no-op onUnsubscribe, or must every implementer write the method? (If not, request one — it turns this from a break into a non-event.)
  • Is the channel set still readable on Connection when onClose fires?
  • Is unsubscribe() a no-op if the connection is not in the channel, or does it throw?
  • Can a plugin call PluginContext::unsubscribe() from within onSubscribe, and what happens if it targets the channel currently joining?
  • Is there any authorization concept on the plugin context, or is any plugin permitted to evict any connection from any channel? (Probably the latter — plugin code is trusted in-process — but this should be stated, not inferred.)
  • Test count rose to 317; what specifically covers the unsubscribe path, onClose-vs-onUnsubscribe exclusivity, and exception isolation?

Integration Approach

Stack fit — Laravel package, so registration is via service provider; plugins implement the lifecycle interface and receive a PluginContext. Fits naturally where the broadcasting stack already uses the pusher driver and the team wants self-hosted, in-process extension points (audit, presence enrichment, authorization, moderation) rather than a separate sidecar.

Migration path — Pin to 0.4.x explicitly; do not float on ^0.3. Audit for implements ConnectionLifecycle across the app, in-house plugins, and any vendored forks. Each implementer needs a fourth method; in the absence of a shipped no-op default, add an empty onUnsubscribe as the minimal upgrade, then adopt real behavior deliberately.

Compatibility — Interface-level break only; runtime behavior for the existing three hooks is unchanged except that unsubscribe() now emits. Critically: do not migrate per-channel cleanup out of onClose into onUnsubscribe. Because close does not fan out unsubscribes, moving that logic would silently drop cleanup on disconnect. The correct pattern is "handle both paths, idempotently." Version skew is a hard failure, not a soft one — a plugin class compiled against the 0.3 interface will fatal at load, so server and plugins must deploy together.

Sequencing

  1. Branch upgrade; run the full suite to confirm nothing else moved.
  2. Add no-op onUnsubscribe to all implementers; deploy. This is the safe, behavior-neutral step and can ship independently.
  3. Confirm the onClose channel-visibility question empirically with a throwaway plugin.
  4. Adopt PluginContext::unsubscribe() for one concrete, reversible use case (e.g. revoking a single channel on role change) and observe.
  5. Only then build onUnsubscribe consumers, and make them idempotent against onClose.

Do not build a first-class product feature on unsubscribe() until the reentrancy and authorization questions are answered.

Operational Impact

Maintenance — This is a 0.x package with an explicitly unsettled contract ("the contract is completed before it settles"). Expect further interface churn before 1.0; budget a review-and-adapt cycle per minor release. Pin exact versions and gate upgrades behind a changelog read. The good news is that the author is closing design gaps early rather than accumulating them, and is flagging the break loudly.

Support — Two documentation gaps will generate nearly all tickets: (a) "my plugin didn't get an onUnsubscribe when another plugin evicted the connection," and (b) "my per-channel state is stale after a disconnect." Both are consequences of intentional, correct design decisions that are non-obvious from the method signatures alone. Publish a one-page lifecycle contract before onboarding any plugin author.

Scaling — Net positive. Replacing "force disconnect + client resubscribe" with an in-process channel removal eliminates reconnect storms and resubscribe churn, which were the failure mode that made per-channel revocation expensive. The write path itself is a channel-membership removal, not a socket operation, so cost is negligible relative to the connection lifecycle it replaces.

Failure modes

  • Plugin throws in onUnsubscribe → isolated, but the plugin's own membership view drifts. Needs a counter/log to be observable.
  • Plugin calls unsubscribe() inside onSubscribe → possible loop or unexpected post-join state; validate before allowing.
  • Plugin maintains membership index using only onUnsubscribe → leaks entries on every disconnect.
  • Plugin uses both onUnsubscribe and onClose for the same cleanup without idempotency → double-decrement.
  • Server/plugin version skew → fatal class-load error at deploy, which at least fails loudly and immediately rather than corrupting state.
  • Silent exception swallowing across fan-out → aggregate health looks fine while plugins quietly stop tracking channels.

Ramp-up — Low to add the interface method (under an hour). Moderate to build correct membership tracking (a few days including tests), because correctness depends on the two asymmetry rules above. Recommend a written runbook for the "revoke one channel without dropping the socket" flow, since that is the capability this release exists to enable and the first team to exercise it will be defining the pattern for everyone else.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource