hyperf/dispatcher
Hyperf Dispatcher is a PSR-14 compatible event dispatcher for the Hyperf framework, offering coroutine-friendly event handling, flexible listener registration, and seamless integration with Hyperf's dependency injection container for scalable PHP services.
Framing and architecture fit
The release in question is Hyperf v3.2.0, not a Laravel package. Hyperf is a PHP-CLI coroutine framework built on Swoole/Swow with PSR-11/PSR-15 conventions, its own DI container, its own hyperf/* component suite, and a Laravel-shaped surface (config/autoload, Eloquent-derived hyperf/database, collections, async-queue). That shape similarity is the main trap for a TPM: it reads like "Laravel with coroutines," but it is a different runtime, different container, and different config loader. Nothing in this release makes Hyperf a drop-in dependency for a Laravel application.
Three viable postures, in order of realism:
| Posture | What it means | Verdict |
|---|---|---|
| A. Component-level reuse | Pull in isolated hyperf/* utilities (e.g. hyperf/collection, hyperf/support, hyperf/pipeline) alongside Laravel |
Low value; Laravel already covers most of it, and you inherit version/dependency coupling for no architectural gain |
| B. Separate Hyperf service | Hyperf runs as its own deployable (async-queue workers, gRPC, high-fanout I/O) behind the existing Laravel monolith | Recommended — clean boundary, no container conflict, absorbs this release's breakage inside one service |
| C. Full framework replacement | Migrate Laravel app to Hyperf | Multi-quarter rewrite; not justified by a point release |
Architecture fit for A and C is poor for a Laravel codebase. Fit for B is good — this is the only posture where v3.2.0 is a routine upgrade rather than a platform event.
Integration feasibility and technical risk
The release is heavy on breaking changes for anyone already running Hyperf, and moderate on dependency risk for anyone consuming it next to Laravel.
Carbon::diffInDays() no longer returns an absolute value, and createFromTimestamp() no longer falls back to date_default_timezone_get(). Both are silent semantic changes — no exception, no deprecation, just different numbers. Any reporting, SLA, billing, or retention logic that relied on absolute day deltas or implicit local timezones is now quietly wrong. Note also that the release notes contain a documentation defect: the "break usage" and "correct usage" snippets for createFromTimestamp() are byte-identical, so the guidance is unusable as written and must be resolved by reading the upstream PR.JobInterface::getQueueName() → getPoolName(), plus a $queue → $pool property rename on Consumer. Every custom Job class in the estate breaks at load time. This is loud (fatel error), so it is low-severity but high-touch — it is a mechanical codebase-wide edit and a mandatory release blocker.default pointer plus channels/stores maps. Published config files that are not republished will fail at container resolution — a startup-time failure, which is preferable to a runtime one, but it means config drift is a deploy-blocking condition and every environment overlay must be audited, not just config/autoload/*.php defaults.^6||^7, Guzzle ^7, PHPUnit ^11, nikic/php-parser 5.6, Elasticsearch ^8||^9, protobuf ^3.6.1||^4.2. Symfony 6/7 in particular is where coexistence with a Laravel app gets sticky — Laravel 11/12 already pins Symfony 7, and Composer will be the arbiter of whether a shared vendor tree is even possible. Assume it is not, and plan for separate vendor trees.CacheableSupportsMethodInterface, async-queue JobMessage BC shim, collection BC layer, ProxyTrait parameter mapping, SocketIO flag, WebSocket typo alias, laminas/laminas-mime. Static analysis (PHPStan at a meaningful level) is the cheap way to enumerate blast radius before touching production.Builder::limit() now throws on negative values — code that passed -1 as a "no limit" idiom will now hard-fail. deleted_at defaults to datetime — soft-delete column semantics and timezone handling need a migration review.ResponseEmitterInterface explicitly; omitting it is a runtime failure in the response path, not a startup one.fgetcsv/fputcsv require an explicit escape. Low effort, but it is a scattered search-and-replace across import/export code.Key questions for the maintainers / owning team
diffInDays() and createFromTimestamp() call sites, or is this a search-and-pray exercise?createFromTimestamp() example upstream before we publish internal migration guidance?Stack fit
Target Laravel remains on PHP 8.2+ with Symfony 7 — which happens to align with this release's Symfony floor, so the constraint is not the blocker. The blocker is the container and runtime model, not the version numbers. Do not attempt a shared Composer vendor tree between Laravel and Hyperf; isolate the Hyperf service into its own repository or a monorepo package with an independent composer.json, and communicate over HTTP/gRPC/queue rather than shared code.
For the Laravel side, this release is effectively a non-event except for one item: if the same estate independently upgrades nesbot/carbon to 3.x, the diffInDays() and createFromTimestamp() semantics change identically. Treat that as a shared cross-cutting risk and audit it once, estate-wide, rather than twice.
Migration path (Hyperf service, v3.1 → v3.2)
upgrade/v3.2 off the current release tag. Do not interleave feature work.composer.json constraints, run composer update --with-all-dependencies in isolation, and record the resolved lock as the new baseline. Expect to unpin anything that disagrees with Symfony 7 / PHPUnit 11.ProxyTrait/collection BC removals in one pass rather than deploy by deploy.getQueueName() → getPoolName(), $queue → $pool, fgetcsv/fputcsv escape arguments, laminas-mime replacement, InfluxDB client swap (influxdb/influxdb-php → influxdata/influxdb-client-php, which is a genuine API rewrite, not a rename — budget it separately).config/autoload/logger.php and cache.php to the default + channels/stores shape. Do this for every environment overlay, then diff against the framework's published defaults. Add dependencies.php binding for ResponseEmitterInterface if on Swow.diffInDays() call for whether absolute value was load-bearing, and every createFromTimestamp() call for timezone assumptions. Review ->limit(-1) idioms and soft-delete column types (deleted_at now datetime). This is the only step that cannot be automated reliably.Sequencing note: steps 1, 3, and 4 are hard prerequisites for everything downstream. Steps 5 and 6 are parallelizable across owners. Step 7 is the critical path and should be staffed first, not last.
Compatibility caveats
HandeShakeException typo alias removal will surface only in code that referenced the misspelling — grep for it explicitly rather than trusting static analysis.Jsonable::toJson(), Htmlable, Pipeline::finally(), Context macroability, and the async-queue auto-registration mode are all safe to adopt opportunistically; none are prerequisites.Maintenance
The recurring cost is configuration-surface churn. Two schema restructures in one release (logger, cache) is a signal that Hyperf's config contracts are still moving; budget for a config audit on every minor release, not just majors. Pin the framework exactly and upgrade deliberately — this is not a dependency that tolerates floating constraints.
The Carbon 3 semantics are a durable maintenance hazard, not a one-time migration. Once diffInDays() is non-absolute by default, every future "days between" calculation carries an implicit ordering assumption that reviewers must consciously check. Encode that as a lint rule or a code-review checklist item, or it will regress.
Support
Hyperf's community is materially smaller than Laravel's. For a TPM this translates to: fewer StackOverflow answers, longer mean-time-to-answer on upstream issues, and a real chance that the migration guidance you need (e.g. the broken createFromTimestamp() snippet) is only obtainable by reading PR diffs. Staff the upgrade with someone who can read framework internals, not just consume documentation. If the codebase's only Hyperf exposure is a single isolated service, this is manageable; if Hyperf is load-bearing infrastructure, it is a key-person dependency worth naming explicitly as a risk.
Scaling
Unchanged by this release — the coroutine model's scaling characteristics are the same before and after. The change that matters operationally is async-queue: the getPoolName() rename plus the new config-driven consumer registration mode means queue topology is now expressible in configuration. That is a genuine operational win (fewer manual processes.php entries, less drift) but only if the config-driven mode is adopted deliberately and the pool naming is consistent with whatever the queue backend calls the same concept. Rename half the codebase and you get jobs silently landing in the wrong pool — a failure that looks like throughput loss, not an error.
Failure modes to instrument
| Failure | Signature | Detection |
|---|---|---|
| Stale logger/cache config | Container resolution failure at boot | Startup probe; deploy fails fast, which is the good case |
Timezone drift in createFromTimestamp() |
Timestamps off by the app's UTC offset | Golden-output diff in shadow-run; alert on reporting totals deviating from baseline |
Non-absolute diffInDays() |
Negative or inverted day counts in reports | Assertion on sign of day deltas; schema-level CHECK where feasible |
| Job pool misrouting after rename | Queue depth grows on one pool, drains on another | Per-pool depth alerting, not aggregate |
Missing Swow ResponseEmitter binding |
Response path failure at runtime, post-boot | Smoke test hitting a real HTTP response, not just a health check |
limit(-1) now throwing |
Sporadic 500s on list endpoints | Error-rate alert scoped to paginated routes |
deleted_at type change |
Soft-deleted rows reappearing or vanishing | Row-count reconciliation on soft-delete tables |
Ramp-up
Estimate by posture: B (isolated service) is a 1–2 engineer-week upgrade for a team already fluent in Hyperf, gated almost entirely on the semantic audit in step 7. A (component reuse) is days, and probably not worth doing. C (full migration) is a multi-quarter program that this release does not change the calculus on.
For a team new to Hyperf but experienced in Laravel, add meaningful ramp-up specifically on the coroutine mental model — shared state across requests, blocking calls, and connection-pool lifetimes behave differently from FPM, and the config similarities actively mislead. The upgrade itself is not the onboarding exercise; running the service in production is.
Recommendation: upgrade posture B on a deliberate schedule, treat the Carbon semantics as an estate-wide audit that also covers the Laravel side, block release on the async-queue rename and config republish, and request a corrected createFromTimestamp() example upstream before publishing internal guidance.
How can I help you explore Laravel packages today?