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.
Hyperf v3.2.0 is a substantial release with real migration work if you're coming from v3.1.x — especially around configuration shapes and dependency floors. Before writing any code:
php >= 8.2. If you're on 8.1, stop and upgrade first; nothing else will work.composer.json dependency constraints against the new floors:
symfony/* → ^6.0 || ^7.0phpunit/phpunit → ^11.0nikic/php-parser → 5.6guzzlehttp/guzzle → ^7.0elasticsearch/elasticsearch → ^8.0 || ^9.0google/protobuf → ^3.6.1 || ^4.2
Run composer why-not on each before bumping to see what's holding you back.config/autoload/logger.php and config/autoload/cache.php. These are the ones that will break boot silently if missed, so fix them first and confirm the app boots cleanly.getQueueName → getPoolName, $queue → $pool in consumers).First practical use case: upgrading an existing Hyperf app from 3.1 → 3.2. The release notes are effectively a migration checklist, and the best first step is a branch that only touches config files + composer.json, so you can isolate config errors from code errors.
The logger config changed from a flat driver => [...] map to a default pointer + channels map. This is the same shape Laravel developers will recognize, so use it as an opportunity to actually split concerns:
return [
'default' => env('LOG_CHANNEL', 'default'),
'channels' => [
'default' => [
'driver' => 'daily',
'path' => BASE_PATH . '/runtime/logs/hyperf.log',
'level' => env('LOG_LEVEL', 'debug'),
'days' => 7,
],
'async_queue' => [
'driver' => 'daily',
'path' => BASE_PATH . '/runtime/logs/async-queue.log',
'level' => 'info',
'days' => 14,
],
],
];
Day-to-day tip: now that channels exist, inject LoggerFactory and resolve per-feature loggers ($factory->get('async_queue')) instead of grepping one giant log file. This is the biggest quality-of-life win in the release.
storesSame pattern as logger:
return [
'default' => env('CACHE_DRIVER', 'default'),
'stores' => [
'default' => [
'driver' => RedisDriver::class,
'packer' => PhpSerializerPacker::class,
'prefix' => 'c:',
],
'sessions' => [
'driver' => RedisDriver::class,
'packer' => PhpSerializerPacker::class,
'prefix' => 's:',
],
],
];
Practical pattern: pass the store name as the second argument to Cache::store('sessions') in code where you need segmentation (e.g. session data, rate-limit counters, and application cache sharing one Redis instance).
getQueueName() → getPoolName()Rename your override in every custom Job:
class SendInvoice extends \Hyperf\AsyncQueue\Job
{
public function getPoolName(): string
{
return 'billing';
}
}
Also note $queue → $pool renamed on Consumer. If you extend Hyperf\AsyncQueue\Consumer, change the property name. Combined with #7621 (auto-registered consumer processes), your config/autoload/processes.php can now drop manually registered queue consumers in favor of config-driven registration — do this in the same PR as the rename so process names stay consistent.
Two Carbon changes with real day-to-day impact:
Carbon::createFromTimestamp() no longer falls back to date_default_timezone_get(). Always pass the timezone explicitly.
// Before (implicit)
Carbon::createFromTimestamp($t);
// After
Carbon::createFromTimestamp($t, date_default_timezone_get());
diffInDays() no longer returns an absolute value by default. This is the sneaky one — a sign change in a "days since" calculation can quietly flip sort order or comparisons.
// Before: always positive
$days = $created->diffInDays($now);
// After: signed
$days = $created->diffInDays($now); // signed
$days = $created->diffInDays($now, true); // positive — add true explicitly
If you run on Swow, register the response emitter in config/autoload/dependencies.php:
return [
\Hyperf\Contract\ResponseEmitterInterface::class => \Hyperf\Engine\ResponseEmitter::class,
];
Pattern: treat this as part of your swow environment's bootstrap, not as something to toggle per request.
PHP 8.4 CSV escape parameter. If you're on 8.4, fgetcsv/fputcsv now require explicit escape. Pass escape: '' — not the old default backslash and not PHP's internal default. This matches Hyperf's own str_getcsv change in #7372. Easy to miss because the deprecation only fires when the function is called.
diffInDays() sign flip is the highest-risk silent break. Grep for diffInDays, diffInWeeks, diffInMonths, etc. and decide per call site whether you want the absolute value. Tests that assert assertGreaterThan(0, ...) will catch some, but comparisons of two Carbon instances (e.g. "is this overdue?") won't fail loudly — they'll just return wrong.
getQueueName() → getPoolName() is not aliased. Unlike some Hyperf deprecations, there's no BC shim. If you miss one subclass, jobs will silently route to the default pool, not throw. Add a test or a grep -rn "getQueueName" gate in CI.
Logger and cache config change is a "boot-time" break, not a "call-time" break. If you keep the old flat structure, LoggerFactory/CacheManager will throw on first resolution. Move the old keys under channels/stores and add the default pointer in one commit.
JobMessage BC serialization removed (#7609). Queued messages produced by older releases are not guaranteed to deserialize. In a rolling deploy with mixed versions, drain the queue before upgrading or run the new version only after workers on old versions finish.
Removed interfaces will fatal, not warn. Hyperf\Serializer\Contract\CacheableSupportsMethodInterface was removed (#7573). Also check for ResumeExitCoordinatorListener, SocketIO flag, and the HandeShakeException typo alias (#7610) — these were deprecated long enough that the removal is intentional, so don't try to restore them, just update references.
Builder::limit() now throws on negative values (#7208). If you're passing dynamic limits that can go negative (e.g. $total - $pageSize), clamp with max(0, ...) before passing.
deleted_at default type change to datetime (#6760). Re-run migrations in a fresh environment and check any query that assumes a specific timestamp format from deleted_at.
Extension points for new features:
NonCoroutine attribute (#7605) — put it on test methods that must run outside coroutine context (useful for testing code that assumes synchronous I/O).Jsonable::toJson() (#6760) — implement alongside __toString() on domain objects that get serialized into HTTP responses; it lets you pass options (depth, flags) instead of casting.Htmlable contract (#6794) — implement on response value objects whose toHtml() output should bypass default escaping.Pipeline::finally() (#7247) — useful for cleanup work in queue pipelines that must run regardless of stage result.Collection/utility additions worth adopting now: partition(), reject() in Arr (#7302); multi-item shift()/pop() on Collection (#7274); shuffle() custom random engine (#7142). These are drop-in and useful for replacing array_filter / foreach boilerplate while you're already touching code for the breaking changes.
QueryException now carries the connection name (#7198). In multi-connection apps, log the connection alongside the query — this release makes that free, and it pays off when debugging "which Redis/MySQL shard is throwing."
laminas/laminas-mime removed (#7278). If you're building multipart emails with laminas-mime directly, switch to Symfony Mime (symfony/mime), which is now safely at ^6.0 || ^7.0 and the more idiomatic choice anyway.
How can I help you explore Laravel packages today?