Skip to content

Require Psalm 7 and declare event-loop I/O capabilities - #134

Open
danog wants to merge 3 commits into
revoltphp:mainfrom
danog:psalm-event-loop-io
Open

danog wants to merge 3 commits into
revoltphp:mainfrom
danog:psalm-event-loop-io

Conversation

@danog

@danog danog commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Psalm 7.0.0-rc10 replaces the old purity annotations with capability sets, and it requires a contract anywhere it can infer one. psalm/phar is now ^7.0.0-rc10. PHP 8.1 CI still removes that dev dependency before install, same as before.

EventLoop's scheduling methods (queue, defer, delay, repeat, onReadable, onWritable, onSignal, enable, disable, cancel, reference, unreference, isEnabled, isReferenced, getSuspension) and Suspension::resume()/suspend()/throw() are @psalm-capabilities io, the same capability Psalm gives usleep(). A suspended fiber doesn't see what the driver and the other fibers do while it waits, the same way Psalm treats Fiber::suspend(). Psalm can't infer that boundary, so those EventLoop methods suppress ImpureMethodCall and DriverSuspension suppresses ImmutableDependency. Consumers get the contract from the package itself, with no stub to load.

Driver itself stays impure. Implementations mutate callback state, read the clock and run user callbacks. Concrete methods are annotated with the narrower set Psalm infers (property reads, writes on $this, and so on). FiberLocal and InvalidCallbackError are @api, so private helpers used only by that public surface are not reported as unused.

The memory benchmark no longer instantiates a driver from a getopt string. The HTTP suspension example prints a raw response header to the CLI, so the two taint issues on that file are suppressed in psalm.xml.

Psalm 7.0.0-rc10 asks for a capability contract on every pure-looking
method and on Driver and Suspension. Driver operations stay impure:
implementations mutate callback state, touch the process-wide driver,
and run user callbacks. Concrete methods use the narrower contract
Psalm infers from the body.

stubs/EventLoop.phpstub is shipped in the package so consumers can
treat scheduling and suspension as io, the same capability as usleep().
This package's own Psalm config does not load that stub, so the real
method bodies stay checked against the wider contracts.
danog added a commit to danog/amp that referenced this pull request Oct 25, 2026
Psalm now reads vendor/revolt/event-loop/stubs/EventLoop.phpstub.
That file is added by revoltphp/event-loop#134 and is no longer
copied into this repository.
@danog danog changed the title Require Psalm 7 and publish event-loop I/O capabilities Require Psalm 7 and declare event-loop I/O capabilities Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant