hyperf/code-parser
A lightweight PHP code parser for Hyperf that uses nikic/php-parser to inspect source as an AST. It helps analyze classes, methods, and properties, traverse nodes with visitors, and support code generation and transformation workflows.
Hyperf v3.2.0 is a major-adjacent upgrade with four config-breaking changes, so treat the first hour as a migration, not a feature tour.
php >= 8.2 is now mandatory, and the transitive stack moved: symfony/* ^6.0||^7.0, phpunit/phpunit ^11.0, nikic/php-parser 5.6, guzzlehttp/guzzle ^7.0, elasticsearch ^8.0||^9.0, google/protobuf ^3.6.1||^4.2. Run composer why-not php 8.4 / composer outdated --direct before composer update, because a stale phpunit or symfony pin will block the whole resolution.config/autoload/logger.php or cache.php typically fails at container build time, not at the call site — you get a LoggerFactory/driver resolution error that points nowhere useful.CustomJob::getQueueName() must become getPoolName(). Also rename the consumer's $queue property to $pool.config/autoload/logger.php, config/autoload/cache.php, config/autoload/dependencies.php, config/autoload/processes.php, then vendor/hyperf/*/src. Hyperf is a monorepo split into components — nearly every release-note PR number maps 1:1 to a component directory, which makes the changelog unusually greppable.First use case to validate the upgrade end to end: define one async-queue job with a custom pool, dispatch it through a consumer, and log both paths. That single flow exercises the getPoolName() rename, the consumer auto-registration, the new logger channel structure, and the cache store structure in one pass.
// config/autoload/logger.php — v3.2 shape
return [
'default' => 'default',
'channels' => [
'default' => [
'driver' => 'daily',
'path' => BASE_PATH . '/runtime/logs/hyperf.log',
'level' => env('LOG_LEVEL', 'debug'),
'days' => 7,
],
'queue' => ['driver' => 'daily', 'path' => BASE_PATH . '/runtime/logs/queue.log', 'days' => 7],
],
];
Named channels replace per-driver hacks. Because logger.default is now a string key into channels, the idiomatic pattern is one channel per subsystem (queue, sql, http) and Log::channel('queue')->info(...). Same mental model as Laravel, which makes this release a net win for anyone coming from Laravel. Do the equivalent for cache.stores — default is now an env-selected store name (env('CACHE_DRIVER', 'default')), so multi-store setups (redis for app, array for tests) become a config switch rather than a code change.
Per-job pool routing. With getPoolName() and "specify the queue name based on the job" (#6538), the recommended pattern is one Job subclass per logical pool, letting each override getPoolName(). That keeps routing declarative and avoids a dispatcher full of $pool arguments.
Process registration is now optional. #7618 adds config-driven auto-registration of async-queue consumer processes. Prefer this over hand-editing config/autoload/processes.php — it deletes a whole class of "works locally, no consumer in prod" bugs. Keep processes.php only for genuinely custom processes.
Swow deployments need an explicit binding. If you run the Swow engine, add the ResponseEmitterInterface => Hyperf\Engine\ResponseEmitter::class mapping to config/autoload/dependencies.php. Without it the response emitter falls back to the wrong engine implementation and you get subtly malformed responses rather than a clean error.
Collection/Arr ergonomics worth adopting immediately: Collection::shift($count) / pop($count) now return multiple items, Arr::partition() and Arr::reject() exist (no more [$pass, $fail] = [[],[]] loops), and Pipeline::finally() gives you a guaranteed cleanup step around middleware-style chains — ideal for releasing locks or closing spans.
Small but high-leverage additions: Context is now Macroable (custom request-scoped helpers without a wrapper class), Jsonable::toJson(), the Htmlable contract for HTTP responses, QueryException now carries the connection name (finally distinguishing which of your read/write or tenant connections blew up), and StdoutLogger prefixes timestamps.
Testing pattern. Use the new #[NonCoroutine] attribute to force a test method out of the coroutine scheduler when you need deterministic execution order — a clean escape hatch instead of restructuring fixtures around parallel().
Carbon::createFromTimestamp() are byte-identical, so the doc gives you no migration signal. The actual behavioural change is that v3.0 no longer falls back to date_default_timezone_get(). Fix: always pass the timezone explicitly — Carbon::createFromTimestamp($t, date_default_timezone_get()) — and audit every call site that omitted the second argument.diffInDays() lost its implicit absolute value. Any diff used in a comparison or sort (cache TTLs, SLA checks, "is this stale?" logic) can now silently flip sign. Pass absolute: true where the old behaviour was assumed; grep for diffInDays( as a release-blocking task.deleted_at default type changed to datetime in hyperf/database. Existing soft-delete columns and any code comparing against timestamps need a schema/migration check — this one bites silently in production, not in tests.Builder::limit() now throws on negative values. Guard pagination math that can produce -1 (often from perPage - offset); the old code quietly returned everything or nothing.fputcsv($fp, $fields, escape: '') and fgetcsv($fp, escape: ''). Note Hyperf already switched its internal str_getcsv to an empty escape — match that ('', not '\\') to stay consistent with the framework and avoid deprecation noise.Hyperf\Serializer\Contract\CacheableSupportsMethodInterface is gone, as is the AsyncQueue\JobMessage serialization BC layer (so drain old queued messages before deploying, or in-flight jobs deserialize incorrectly), plus Collection BC, ProxyTrait parameter mapping, ResumeExitCoordinatorListener, the SocketIO flag param, and the HandeShakeException typo alias. Grep for HandeShakeException — the typo'd name is a very easy one to have shipped.laminas/laminas-mime was dropped. Anything relying on it transitively (mail attachment parsing is the usual suspect) needs a replacement dependency declared explicitly.influxdata/influxdb-client-php — different client API. Metrics code will need rewriting, not just a version bump. Same category: Elasticsearch 8/9 support means the old 7.x client contract is gone from hyperf/elasticsearch.Parser::parseResponse now returns Google\Rpc\Status objects instead of Grpc\StringifyAble. Type-hint accordingly — strict typing will surface this loudly, untyped array access will not.database-sqlserver package is opt-in, but adding it pulls its own PDO constraints; only require it if you actually need it.php bin/hyperf.php with a trivial command (e.g. vendor/bin/co-phpunit --version or a no-op command) to isolate config errors from runtime errors before chasing application logic. And after the upgrade, clear the DI/event-listener caches — #7658 added caching for non-anonymous event listener resolution, so a stale annotation cache can mask a correctly fixed listener.How can I help you explore Laravel packages today?