Repository navigation
Add PHPStan rules for Hypervel's long-lived workers - #105
Conversation
Include optional Unsupported guidance in the main diagnostic so terminal and CI formatters retain it. Specify reflection-based argument reading and focused coverage for bare markers and supplied messages, and update the documentation requirements.
Expose resolvedShared() on the container contract so facades can distinguish worker-held instances from fresh resolutions. Retain worker caching for shared roots, keep fresh roots per coroutine, and leave scoped and uncached facades resolving through the container. Generation changes invalidate cached coroutine roots across live contexts, while copied child contexts resolve their own roots. Register the date factory during foundation bootstrap without replacing earlier bindings. Standalone Date facade fallback no longer writes a container binding. Document the supported Date subclass adaptation and cover concurrent resolution, singleton-selecting factories, copied contexts, invalidation and date binding precedence.
Add return templates to filesystem borrowing, pinned Redis connections and SQS connection callbacks without changing runtime behavior. Let filesystem callback parameter templates accept the concrete client, adapter or driver selected by the caller while preserving useful Flysystem bounds for untyped callbacks. Cover typed SDK callbacks, untyped clients and concrete or inferred Flysystem parameters in the max-level type fixtures.
Record the merged attributes prerequisite and completed runtime section. Describe callback parameter templates and their type coverage, remove stale prerequisite gates and instruction line numbers, and keep the remaining implementation at the shared analysis infrastructure.
…d facades Resolve facade accessors and exact root classes from declarations and booted bindings without executing accessors or factories. Respect overrides and unknown construction paths, and include actionable Unsupported attribute guidance in diagnostic messages. Track constants referenced by method attributes and parameter defaults through the existing source dependency helper. Cover native and nullsafe calls, facade factories and binding chains, supported overrides, and uncertain roots.
Share the collected worker-state model across rules and recognize listener, observer, and server-operation contexts. Preserve per-method receiver evidence through inherited handlers and helpers, distinguishing container-built queued listeners from restored job payloads. Report boot-only and test-only calls with their parameter conditions and proven shared receivers. Merge injection evidence across routes, jobs, and constructors; recognize singleton factories and readonly promoted dependencies; and give configuration writes one diagnostic. Add malformed-attribute checks, isolated enablement coverage, and warm-cache regressions for registrations, facade accessors, and constant-backed defaults. Preserve unknown receiver and binding paths without executing application code.
Record the implemented facade resolution, observer discovery, merged injection evidence, and source dependencies. Clarify the bounded receiver and reference-alias analysis and mark section 3.3 as the next implementation step.
Plan removal of the global mutable-date opt-out while preserving useful customization and explicit interoperability conversions. Include stronger return types and PHPStan diagnostics once runtime immutability is guaranteed.
Align resolver, manager, Capsule, testing and queue accessor return types with the Connection base class already required by Eloquent and migrations. Keep custom non-PDO drivers supported and retain ConnectionInterface at independently supplied connection boundaries. Remove redundant type guards and analysis suppressions. Update resolver-backed test doubles without changing their behavioral assertions, including non-PDO rejection coverage and both queue skip-locked outcomes. Document the custom resolver and mock requirements. Expand the native request API porting guidance and remove the ineffective HTTP status assignment during CLI environment bootstrap. Database, queue, foundation, facade metadata and affected integration tests pass; unavailable database drivers remain skipped.
Default LOG_USER facility configuration to the equivalent user string in the framework and both application skeletons. Monolog resolves named string facilities but rejects numeric strings from the environment, so the default now reflects the supported environment value. Preserve the existing LogManager fallback and facility behavior. Static analysis and formatting pass for the combined change.
Detect handles escaping lending callbacks, premature completion of managed transactions, native request APIs, discarded immutable date results and explicit mutable date creation. Match framework declarations and preserve user overrides; share connection inspection with the queue transaction rule. Add configuration environment-cast checks, typed getter guidance, observer registration conventions and application binding-cycle detection. Keep migration column policy disabled by default. Restrict configuration value inference to proven application repository reads so independent repositories retain their declared types. Reuse runtime dependency tracking and configuration source discovery, remove redundant callable guards, and document legitimate framework exceptions with local reasons. Add behavioral rule fixtures and update type expectations. The PHPStan suite, static analysis and formatting pass.
Record the implemented resolver contract, configuration ownership boundary and declaration-aware rules. Describe supported borrowed-resource and transaction identities, regression coverage and the required consumer mock updates. Mark sections 3.3 through 3.12 complete and retain section 3.13 as the next implementation step.
Move exact-catch inspection into the coroutine helper so cancellation checks can share the same verdict without duplicate reports. Resolve receivers assigned inside try blocks while discarding stale input facts and preserving argument-dependent return types through PHPStan inference. Expose source method lookup for inherited and aliased trait methods while keeping constructor ownership intact. Cover a channel assigned from a typed parameter in the existing exact-catch fixture.
Report swallowed cancellation around recognized synchronous database, Redis, HTTP and wait operations in request, job and operation handlers. Follow forwarded methods, model traits and exception causes while respecting overrides, deferred work and schema defaults that only throw. Track exception constructor files for warm result-cache invalidation. Add default-on checks for raw coroutine spawning, direct Socialite context-property access, Swoole tables allocated in provider bodies and request-dependent BindWhen conditions. Inspect condition callables in their own source namespace and retain explicit reasons for framework-owned raw coroutine use. Add focused rule fixtures for accepted alternatives, distinct dispatch paths and verified regressions. Validation includes formatting, framework analysis, targeted rule and cache tests, and the PHPStan test suite.
Record the implemented receiver and callable analysis, supported synchronous operation families, exception propagation checks and cache dependencies. Describe the verified exclusions and regression coverage, and move remaining work to documentation, final verification and approved guidance updates.
Explain the new checks with corrective examples, configuration switches and supported exceptions. Describe deferred execution and receiver ownership boundaries without promising analysis of unknown callbacks or repositories. Update the rule index, method-attribute guidance and worker-context coverage. Record the config-repository typing difference from Larastan.
Explain that PHPStan checks restricted and unsupported methods. Replace the blanket redundant-singleton example with guidance that preserves intentional binding, optional injection and rebinding behavior, and correct the alias-registration description. Record documentation completion and the remaining benchmark and final review. Hold PR creation until the owner has reviewed completion and discussed next steps.
Capture the vendor directory chosen when creating a disposable skeleton copy and expose it alongside the existing skeleton source path. Preserve its lexical spelling so consumers can observe later symlink changes, and reset it with the runtime ownership state. Extend the runtime-copy test to verify the recorded path matches the generated link and restore the field with the other isolated test state.
Track Composer metadata through the skeleton and working vendor paths instead of the disposable runtime directory. Record skeleton vendor directory presence separately because even an empty directory suppresses the working vendor fallback. Preserve ordinary file and symlink dependency handling. Cover unchanged cache reuse, working vendor symlink changes, empty skeleton vendor transitions and newly supplied skeleton metadata. Targeted cache and bootstrap tests, framework analysis, the PHPStan suite and Testbench suite pass. Complete the rules plan and final integration review. Warm analysis now reuses results across runtime copies; retain the owner discussion gate before PR creation.
Lead new diagnostic sections with recommended framework usage, shorten dense coverage explanations and remove repeated diagnostic transcripts. Preserve configuration switches, inference limits and legitimate exceptions. Clarify facade lifetimes, configuration repository ownership and native request API adaptations. Review the wording against the Processes documentation and verify links, anchors and code fences.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
PR Summary by QodoAdd PHPStan safeguards for long-lived Hypervel workers
AI Description
Diagram
High-Level Assessment
Files changed (196)
|
Code Review by Qodo
1.
|
[High impact] The PR appears safe to merge, with one non-blocking gap in borrowed-resource warnings.
|
Report gRPC receiver cleanup failures without letting carried cancellation escape the raw coroutine. Preserve the original caller cancellation when terminating a connection also fails. Contain SafeSocket logger failures so socket closure and loop cleanup still run. Cover carried cancellation, caller exception identity and native socket closure with regression tests.
Track borrowed values returned or stored through closures and arrays, captured external holders, and connection properties. Invalidate connection identity after destructuring and foreach assignments, and recognize config offset writes in expression and arrow-function scopes. Discover wildcard listeners and passive observers with their actual execution contexts. Distinguish queued listener clones from shared failure and dispatch hooks, propagating both paths to helpers without losing inherited job receivers. Recognize earlier throws that discard cancellation, negated cancellation guards and coalescing receiver assignments. Respect custom facade dispatch and class-scoped callable conditions, and clarify diagnostics and documentation. Regression fixtures cover the corrected paths and valid alternatives.
Follow top-level assignment order and closure captures when checking borrowed resources, including array additions, resets and reference captures. Keep bounded current facts instead of retaining statement history. Distinguish copied values from objects shared by listener clones, and preserve reports for shared parent and sibling receivers. Extend cancellation checks to consumed lazy queries and Redis callbacks while avoiding duplicate nullsafe reports and dead coalescing-assignment branches. Resolve self and parent binding conditions, respect custom facade forwarding, and report additional native request APIs. Update documentation and regression fixtures for unsafe patterns and valid alternatives.
The binding-rule fixtures contain PHP 8.5 attribute expressions and are analyzed as source rather than loaded. Optimized autoloading included their classes and allowed facade reflection to load a fixture, terminating PHPStan test workers with a compile error. Exclude both files explicitly so optimized development autoloading preserves their parse-only use.
Keep live reference captures separate from values frozen when callbacks are created. Explicit reference bindings invalidate borrowed-value and owner evidence consistently, including nested callbacks, while immediate external stores remain detectable. Retain synchronous observer visits with unknown operation contexts and restrict configuration-write exemptions to recognized repository receivers. Recognize destructuring, foreach and catch bindings as variable writes, and inspect every exception argument that can preserve the original cause. Respect custom Sleep constructors and nullsafe fluent calls. Use valid static closures for binding attributes, clarify cloned-object analysis limits, and explain jsonb portability in migration guidance. Add focused regressions for the corrected cases.
Carry borrowed-resource facts into nested callbacks while distinguishing shared reference bindings from by-value captures. Preserve only the applicable live names, and inspect spawned callbacks that have not already produced an escape diagnostic. Default database facade resolutions in child coroutines remain separate from explicitly captured connections. Use assigned values for directly written native properties and discard stale inputs from other properties written in the same try block. Recognize nullable receivers, bounded concurrency waits and channel-contract waits. Add regression coverage for nested captures, pinned owners and cancellation handling.
Distinguish listeners resolved from the container, cloned handlers and option instances created without their constructor. Follow the registered class for option methods, retain shared helper contexts, and check unique and debounce hooks only when the dispatcher uses them. Track inherited debounce defaults as runtime inputs, including defaults that disable debouncing. Warm collected-rule caches before dependency edits so first-warm stub path changes cannot mask missing invalidation. Cover option writes, promoted properties, bound subclasses and inherited defaults, and update the analysis documentation.
| $this->defaultSources[$class->getName()] = true; | ||
| $property = $class->getNativeReflection()->getProperty('debounceFor'); | ||
|
|
||
| return $property->isPublic() && ! $property->isStatic() && $property->getDefaultValue() !== null; |
There was a problem hiding this comment.
isDebounced() treats a null property default as proof that debounceVia() never runs. But the dispatcher calls backoff() before reading debounceFor. A listener with public ?int $debounceFor = null can set it to 10 in backoff(). Dispatch then acquires a debounce lock and calls debounceVia() on the container's listener.
The new gate removes that method's previous context, hiding warnings for static writes, restricted calls and swallowed cancellations. Keep this case unknown rather than treating it as disabled.
There was a problem hiding this comment.
Leaving this as is. The analysis reads how a listener declares debouncing: the #[DebounceFor] attribute or the debounceFor default. It does not follow one option method changing another option's property before the dispatcher reads it, so a debounceFor set inside backoff() is outside what it models.
There was a problem hiding this comment.
Understood. isDebounced() models the listener’s declared debounce configuration, not mutations introduced by another option hook such as backoff(). I treated that runtime interaction as something the analysis should follow, but given the intended scope, this is not a defect in the current model; I’ll withdraw the finding.
Should I remember this review guideline: treat cross-option property mutations as out of scope unless WorkerLifetimes explicitly models them?
Check statements before a cancellation guard together with the path that rethrows the exception. Reject early exits and replacements that discard the original exception, while preserving the stricter exact-catch check. Cover positive and negated guards after reporting, invalid prefixes, and propagation from an inner guarded catch to an outer swallowing catch.
Report borrowed resources stored through explicit global bindings and global objects, including objects captured by nested callbacks. Keep named function declarations separate from the borrowing callback. Distinguish rebinding a local name from writing through its previous reference. Preserve fixed captures and remaining live dependencies across direct detachment, and discard uncertain evidence for conditional detachment. Cover global stores, scope boundaries, nested object captures and reference invalidation.
Extend the static-state check to explicit GLOBALS writes and names bound by top-level global statements. Reuse operation contexts and join write-time value types with binding evidence, keeping trait methods and nested function scopes distinct. Cover assignments, offset reference writes, increments, deletions and destructuring, along with binding detachment, boot-time use and disabled checks. Document global storage coverage and its analysis boundaries.
A cancellation guard can fall through to a rethrow after its branch while the ordinary-error branch returns. Check the path cancellation takes through the guard and the remaining statements, stopping at a branch that already throws. Keep exception identity and early-exit checks on that path. Cover positive and negated guards that preserve cancellation, alongside a fall-through return that still swallows it.
Apply nested detachments after earlier escapes in the same statement, rebuilding only the names an unset removed. Follow detached closure references without revisiting cycles, and invalidate both sides of foreach and destructuring reference bindings while preserving earlier value copies. Recognize external storage aliases, conditional expression results, coalescing assignments and exact positional destructuring stores. Leave unknown array elements and property aliases unclassified rather than treating every value as borrowed. Extend regression coverage for return and spawn ordering, detached captures, reference source writes, external stores and corresponding local values that remain safe.
Collect foreach targets, catch bindings and writes through aliases of global storage. Distinguish reference rebinding from mutation so detached local names do not retain global ownership. Match call arguments to PHPStan write events by source position and function identity. This catches reference outputs without duplicating callee resolution, while excluding ordinary arguments and first-class callable creation. Add regression cases for global aliases, reference outputs, nullsafe calls and detached bindings. Update the analysis plan with the implemented boundaries and completed review state.
A nested unset rebuilt only its own variable, leaving later copies with stale borrowed values from the full-statement pass. Carry a separate environment through successive detachments and rebuild later destinations from it. Retain earlier detachments and removed live-reference names while preserving fixed captures and copies made before the unset. Apply each detaching node’s own write in the following segment so rebinding cannot restore an old value. Add regressions for cleared arrays, consecutive unsets, copied reference captures and reference rebinding, alongside an escaping copy after an unrelated unset. Update the analysis plan to describe the bounded traversal.
Replace timer-based ordering with channels so both readers resolve their initial roots before clearing, and neither resolves again until clearing finishes. Keep the identity assertions that verify invalidation in live coroutines. Check bounded channel waits so a failed child cannot leave the other callbacks waiting indefinitely. This removes scheduler timing from the test without changing facade behavior.
Rebuilding every later destination discarded independent facts carried between loop iterations. Limit rebuilding to unset names and later writes that read those names, following transitive copies with the existing expression visitor. Preserve other facts from the full-statement pass while retaining the ordered detachment handling for affected values. Share the expression reader with arrow-function capture analysis without changing capture boundaries. Cover the restored loop-carried escape and safe cleared-array and consecutive-unset cases. Update the plan to describe the targeted rebuild and its remaining syntactic limits.
Avoid carrying later writes backward through simple branches. Classify statements in one preorder pass, retaining repeated propagation for loops, jumps and nested assignments without inspecting nested function or class bodies. Evaluate rebuilt values from their ordered inputs and merge carried facts when repeated propagation is needed. Merge fixed captures and live reference names for each closure identity so later iterations cannot lose evidence behind an earlier empty capture. Add regression coverage for safe branch copies, nested writes and returned closures, including closures carried through arrays and loop iterations. Update the analysis plan with the transfer rules and precision boundaries.
Replace statement-wide write replay with ordered expression and statement transfers. Keep branch, loop and finally completion states separate, preserve captures at creation, and distinguish initial loop entry from later iterations. Retain bounded null and nonempty-array facts while invalidating them at calls and mutations. Detect borrowed-resource use in generator callbacks and cover ordinary escapes, resets, completion paths and the established reference limits.
A generator callback returns before its body runs, so transaction() completes before the deferred database work executes. Report literal generator callbacks while respecting transaction overrides and excluding nested generator bodies. Document generator callback guidance and reference-tracking limits, and replace the borrowed-resource plan narrative with the final control-flow design and focused verification categories.
Use one exception path for explicit throws and calls, and exclude unmatched exceptions when a Throwable catch handles them. Preserve exceptions raised by catch bodies and run finally with the resulting completion state. Visit static initializers, computed method names and destructuring or foreach targets through the existing evaluator. Preserve skipped initialization and nullsafe paths, and cover the restored escape reports and catch-all cleanup behavior.
Replace separate target evaluation and write passes with one ordered assignment routine. Destructuring applies each target before the next, foreach assigns its value before its key, and unset detaches names in order. Apply reference invalidation at the affected target without introducing alias tracking. Preserve earlier escape reports when a later target throws and cover ordering across ordinary, reference and foreach targets.
| if ($byReference) { | ||
| if ($target instanceof Variable && is_string($target->name)) { | ||
| $environment = $this->detach($environment, [$target->name], 'reference'); | ||
| } | ||
|
|
||
| return $this->bindReferences([$target, $node->expr], $environment); |
There was a problem hiding this comment.
Reference stores lose warnings
Inside withConnection(), [&$this->client] = [$connection]; saves the borrowed connection on $this, but this branch returns before checkStore(). The previous loop reported this escape. Developers now get no warning even though the saved object remains accessible after the borrow ends.
Check the leaf's known value before bindReferences(), after detaching any plain variable, and add a reference-property test.
There was a problem hiding this comment.
This one can't happen in code PHP will run. When a destructuring target is by-reference, the right-hand side has to be a variable or a call, so [&$this->client] = [$connection]; fails to compile with "Cannot assign reference to non referenceable value". The same goes for a literal holding &$connection and for nested lists. The form that does compile, [&$this->client] = $source;, reads an element out of an array, which the rule treats as unknown, the same as before this change.
There was a problem hiding this comment.
You're right—this finding is invalid. [&$this->client] = [$connection]; is rejected by PHP with a compile-time fatal error, so it cannot produce the runtime escape I described. With a variable RHS, the destructured element is unknown to this rule, as you noted. I'll withdraw the comment.
This adds a set of PHPStan rules for mistakes that are easy to make in a long-lived Swoole worker and hard to see in review: calling a boot-only method while handling a request, letting a pooled connection escape the callback that borrowed it, swallowing a coroutine cancellation, creating a Swoole table in every process, and similar. It builds on the lifetime and
Unsupportedmethod attributes, which mark the methods these rules check.Laravel code mostly runs unchanged on Hypervel, but a worker serves many requests and coroutines at once, so some patterns that are harmless under PHP-FPM leak state or misbehave here. The framework already documents these. These rules turn that guidance into diagnostics, each with a fix and, where there is one, the legitimate exception that should be ignored with a reason.
A few runtime changes came out of the work, so they're described first. They're in their own commits.
Runtime changes
bind()orTransientservice therefore kept the first request's object for every later request. The container gainsresolvedShared(), which reports whether it holds a worker-shared instance, including auto-singletons. Facades cache those for the worker as before. Other roots are kept per coroutine, so each request gets its own and concurrent requests never share one. Clearing a facade's resolved instance now makes later facade calls resolve again in every coroutine.dateis bound by the framework. TheDatefacade used to callswap()on first use whendatewasn't bound, which registered a container instance in the middle of a request.FoundationServiceProvidernow bindsdatetoDateFactorywithsingletonIf(). ADatefacade subclass'sDEFAULT_FACADEtherefore only applies whiledateis unbound; an application should binddateto its factory instead. This is listed in the Support README's differences.Connection.ConnectionResolverInterface::connection()and the resolvers implementing it now declare the driver-neutralConnection, notConnectionInterface. Every built-in andDB::extend()connection already is one, and Eloquent and the migrator already required it, so a resolver returning anything else was already broken. The change removes several ignores andinstanceofchecks. Laravel's contract documentsConnectionInterface, so this is listed in the Database README and the porting guide.withDriver(),withAdapter()andwithClient(), the RediswithPinnedConnection()methods and the SQS pool'swithConnection()are now generic over their callback's return type. The filesystem methods also accept a callback that narrows its parameter to the disk's concrete driver, adapter or client.logging.syslog.facilityreadLOG_SYSLOG_FACILITYwith the integerLOG_USERas its default. An environment variable is always a string, and Monolog only accepts facility names as strings, so the variable has to hold a name such aslocal0. The default is now'user', which Monolog maps toLOG_USER, in the framework config and both skeletons.LoadEnvironmentVariablesno longer callshttp_response_code(). It was left over from Laravel's FPM bootstrap and does nothing in Hypervel, which bootstraps from the CLI before any response exists.The rules
Each rule has its own section in the static analysis docs, with an example, the fix and its exceptions. Every rule except unsupported calls can be turned off with a parameter.
BootOnly,TestsOnlyorBootOrTestsOnlyfrom request, job or operation code, applying each marker's conditions. Also markers whose arguments would misstate the restriction.checkRestrictedCallsUnsupported, including its message.checkBorrowedResourcescommit()orrollBack()that ends a transactiontransaction()manages before its closure returns, androllBack(0).checkManagedTransactionsheader(),setcookie(),session_*(),$_GET,$_SESSIONand the other PHP functions and superglobals that act on the process instead of the request.noNativeRequestApischeckDiscardedDatesCarbon.noMutableDatesenv()read with a boolean, integer or float default that isn't cast.checkConfigEnvCastsconfig('key')orget()for a key that always holds one type, which should usestring(),integer()and so on. Only runs when analyzing an application.checkTypedConfigGettersobserve()in a service provider for a model the application owns, which should useObservedBy.checkObserverAttributesjson(),enum()andset()columns. Off by default, since existing migrations shouldn't be edited.checkMigrationColumnTypescheckBindingCyclesnoRawCoroutinescheckSocialiteProvidersnew Swoole\Tablein a service provider'sregister()orboot(), which every process runs.checkSwooleTablesBindWhencondition that reads the current request or user, which would bind that request's choice for the worker's lifetime.checkBindWhenConditionscheckCancellationHandlingThe framework's own reports are fixed or, where the code is intentional, ignored with a reason: for example the coroutine primitives themselves, Horizon's
ignore_user_abort(), and Sentry'sspotlightsetting, which accepts a boolean or a URL.Shared analysis
Several rules need to know where code runs and whether a value is the worker's shared instance.
OnReceiveandOnClose, and Eloquent observers registered withobserve()orObservedBy, are analyzed as code that runs during an operation. The existing worker-state checks use these contexts too.whenSharedrestriction is only reported when the call is known to reach the shared instance: through a facade whose service the container shares, a named application-container resolution such asapp('router'), or a dependency the container injects into a controller action, a job handler or a readonly promoted constructor property. A factory you create withnewis not reported.Configuration types only for the application repository
Configuration value types now apply only to reads from the application's repository: the
config()helper, theConfigfacade, and calls such asapp('config')->get(...). A repository received as a parameter or property, or built withnew Repository($items), may hold other values, so it keeps its declared return types. Larastan applies the application's configuration types toget()on any repository. This is listed in the PHPStan README's differences.Result cache for package analysis
Package analysis boots a Testbench copy of the skeleton that is deleted when the process exits. The result cache recorded the copy's Composer metadata by its path inside that copy, so every warm run saw it as missing and reanalyzed everything that depends on the booted application, about a third of this repository. The cache now records the metadata where Testbench takes it from: the skeleton's
vendordirectory and the working package's, by their own paths, including whether the skeleton'svendordirectory exists. Testbench'sBootstrapper::runtimeVendorPath()returns the working vendor directory for this. A warmcomposer analyseof this repository now reanalyzes nothing and takes a few seconds instead of about forty.Docs
AGENTS.mdsays PHPStan checks the lifetime attributes, rewords binding pattern 3 to say what explicit registration changes, and corrects the description of binding an alias.Validation
Formatting, PHPStan, the analyzer test suite, the Testbench suite and the Support tests pass. Each rule has tests for what it reports, the allowed alternatives and its documented exceptions, and the result cache changes have warm-cache tests. The skeleton was installed the way CI installs it and analyzed with these rules, with no reports.
Compared with
0.4, a coldcomposer analyseof this repository is a few percent slower. Turning the new rules off didn't measurably change that, and warm runs are much faster because of the result cache fix.Summary by cubic
Adds 17 PHPStan rules that catch unsafe patterns in Hypervel's long-lived workers, with runtime changes to support accurate analysis and safer behavior.
Runtime changes
dateduring bootstrap.Connection; custom resolvers and test doubles must match this contract.'user', CLI environment loading no longer sets an HTTP status, and transport coroutines contain cleanup and logging failures.Analysis
Throwablecatch handles, runfinallywith the resulting completion state, and follow resources stored in globals or objects captured by nested callbacks.GLOBALSwrites, top-levelglobalbindings, and writes through aliases and reference arguments, including foreach targets and catch bindings.Written for commit c819511. Summary will update on new commits.
Note
Add PHPStan rules for Hypervel's long-lived workers
clearResolvedInstanceinvalidates coroutine-local roots; addsContainer::resolvedShared()to the container contractConnectionInterfaceto the concreteConnectionacross resolvers, the manager, and the queue (ConnectionResolverInterface.php, DatabaseManager.php); thedateservice is now bound to aDateFactorysingleton by defaultConnectionreturn-type change is breaking for customConnectionResolverInterfaceimplementations and test doubles that return or mockConnectionInterface; per-coroutine facade caching changes when facade roots are shared across coroutines, so code relying on cross-coroutine root reuse of non-shared services may behave differentlyMacroscope summarized c819511.