Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"ext-json": "*",
"phpunit/phpunit": "^9",
"jetbrains/phpstorm-stubs": "^2019.3",
"psalm/phar": "6.20.*"
"psalm/phar": "^7.0.0-rc10"
},
"autoload": {
"psr-4": {
Expand Down
24 changes: 15 additions & 9 deletions examples/benchmark-memory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

use Revolt\EventLoop;
use Revolt\EventLoop\Driver;
use Revolt\EventLoop\Driver\EvDriver;
use Revolt\EventLoop\Driver\EventDriver;
use Revolt\EventLoop\Driver\StreamSelectDriver;
use Revolt\EventLoop\Driver\UvDriver;

require __DIR__ . '/../vendor/autoload.php';

Expand All @@ -21,15 +25,17 @@

/** @psalm-suppress RiskyCast */
$t = (int) \round((\array_key_exists('t', $args) ? (int) $args['t'] : 0));
if (\array_key_exists('d', $args)) {
if (\is_string($args['d'])) {
$loopClass = 'Revolt\EventLoop\Driver\\' . $args['d'] . 'Driver';
if (\class_exists($loopClass)) {
$loop = new ($loopClass)();
if ($loop instanceof Driver) {
EventLoop::setDriver($loop);
}
}
if (\array_key_exists('d', $args) && \is_string($args['d'])) {
$loop = match ($args['d']) {
'Ev' => EvDriver::isSupported() ? new EvDriver() : null,
'Event' => EventDriver::isSupported() ? new EventDriver() : null,
'StreamSelect' => new StreamSelectDriver(),
'Uv' => UvDriver::isSupported() ? new UvDriver() : null,
default => null,
};

if ($loop instanceof Driver) {
EventLoop::setDriver($loop);
}
}

Expand Down
5 changes: 5 additions & 0 deletions examples/fiber-local-automatic.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@
* This might be used to log the current URL, authenticated user, or request identifier in an HTTP server.
*
* @psalm-suppress UnusedProperty
*
* @psalm-mutable
*/
final class Logger
{
private int $nextId = 1;
private FiberLocal $transactionId;

/**
* @psalm-capabilities read-props
*/
public function __construct()
{
$this->transactionId = new FiberLocal(fn () => $this->nextId++);
Expand All @@ -29,7 +34,7 @@
*/
public function log(string $message): void
{
echo $this->transactionId->get() . ': ' . $message . PHP_EOL;

Check warning on line 37 in examples/fiber-local-automatic.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedOperand

examples/fiber-local-automatic.php:37:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 37 in examples/fiber-local-automatic.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedOperand

examples/fiber-local-automatic.php:37:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 37 in examples/fiber-local-automatic.php

View workflow job for this annotation

GitHub Actions / PHP 8.5

MixedOperand

examples/fiber-local-automatic.php:37:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 37 in examples/fiber-local-automatic.php

View workflow job for this annotation

GitHub Actions / PHP 8.4

MixedOperand

examples/fiber-local-automatic.php:37:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)
}
}

Expand Down
7 changes: 5 additions & 2 deletions examples/fiber-local-manual.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
*
* @psalm-suppress UnusedProperty
*/
final class Logger
final class ManualLogger
{
private FiberLocal $transactionId;

/**
* @psalm-capabilities read-props
*/
public function __construct()
{
$this->transactionId = new FiberLocal(fn () => throw new \Exception('Unknown transaction ID'));
Expand All @@ -41,11 +44,11 @@
*/
public function log(string $message): void
{
echo $this->transactionId->get() . ': ' . $message . PHP_EOL;

Check warning on line 47 in examples/fiber-local-manual.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedOperand

examples/fiber-local-manual.php:47:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 47 in examples/fiber-local-manual.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedOperand

examples/fiber-local-manual.php:47:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 47 in examples/fiber-local-manual.php

View workflow job for this annotation

GitHub Actions / PHP 8.5

MixedOperand

examples/fiber-local-manual.php:47:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)

Check warning on line 47 in examples/fiber-local-manual.php

View workflow job for this annotation

GitHub Actions / PHP 8.4

MixedOperand

examples/fiber-local-manual.php:47:14: MixedOperand: Left operand cannot be mixed (see https://psalm.dev/059)
}
}

$logger = new Logger();
$logger = new ManualLogger();
$logger->setTransactionId(1);

EventLoop::delay(1, static function () use ($logger) {
Expand Down
6 changes: 6 additions & 0 deletions examples/http-client-suspension.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ function fetch(string $url): string
return $buffer;
}

/**
* Returns the header block of an HTTP response.
*
* @psalm-pure
*/
function extractHeader(string $httpResponse): string
{
return \explode("\r\n\r\n", $httpResponse, 2)[0];
}

// Printed on the CLI, where the header is text rather than HTML.
echo extractHeader(fetch('http://www.google.com/'));
19 changes: 13 additions & 6 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@
</projectFiles>

<issueHandlers>
<DuplicateClass>
<errorLevel type="suppress">
<directory name="examples"/>
</errorLevel>
</DuplicateClass>

<InvalidOperand>
<errorLevel type="suppress">
<directory name="src/EventLoop/Driver"/>
Expand Down Expand Up @@ -98,6 +92,19 @@
<file name="src/EventLoop/Driver/EventDriver.php"/>
</errorLevel>
</UnusedVariable>

<!-- The example prints a raw response header to the CLI. -->
<TaintedHtml>
<errorLevel type="suppress">
<file name="examples/http-client-suspension.php"/>
</errorLevel>
</TaintedHtml>

<TaintedTextWithQuotes>
<errorLevel type="suppress">
<file name="examples/http-client-suspension.php"/>
</errorLevel>
</TaintedTextWithQuotes>
</issueHandlers>

<stubs>
Expand Down
62 changes: 62 additions & 0 deletions src/EventLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
/**
* Accessor to allow global access to the event loop.
*
* Scheduling and suspension are `io` for callers, like `usleep()`: the driver is the scheduler, and
* what other fibers do while the current one is suspended is their own effect.
*
* @see Driver
*/
final class EventLoop
Expand All @@ -35,24 +38,36 @@ public static function setDriver(Driver $driver): void
try {
/** @psalm-suppress InternalClass */
self::$driver = new class () extends AbstractDriver {
/**
* @psalm-pure
*/
#[\Override]
protected function activate(array $callbacks): void
{
throw new \Error("Can't activate callback during garbage collection.");
}

/**
* @psalm-pure
*/
#[\Override]
protected function dispatch(bool $blocking): void
{
throw new \Error("Can't dispatch during garbage collection.");
}

/**
* @psalm-capabilities read-props
*/
#[\Override]
protected function deactivate(DriverCallback $callback): void
{
// do nothing
}

/**
* @psalm-pure
*/
#[\Override]
public function getHandle(): mixed
{
Expand Down Expand Up @@ -83,6 +98,9 @@ protected function now(): float
*
* @param \Closure(...):void $closure The callback to queue.
* @param mixed ...$args The callback arguments.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function queue(\Closure $closure, mixed ...$args): void
{
Expand All @@ -102,6 +120,9 @@ public static function queue(\Closure $closure, mixed ...$args): void
* invalidated before the callback invocation.
*
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function defer(\Closure $closure): string
{
Expand All @@ -122,6 +143,9 @@ public static function defer(\Closure $closure): string
* before the callback invocation.
*
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function delay(float $delay, \Closure $closure): string
{
Expand All @@ -142,6 +166,9 @@ public static function delay(float $delay, \Closure $closure): string
* @param \Closure(string):void $closure The callback to repeat.
*
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function repeat(float $interval, \Closure $closure): string
{
Expand All @@ -165,6 +192,9 @@ public static function repeat(float $interval, \Closure $closure): string
* @param \Closure(string, resource):void $closure The callback to execute.
*
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function onReadable(mixed $stream, \Closure $closure): string
{
Expand All @@ -188,6 +218,9 @@ public static function onReadable(mixed $stream, \Closure $closure): string
* @param \Closure(string, resource):void $closure The callback to execute.
*
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function onWritable(mixed $stream, \Closure $closure): string
{
Expand All @@ -212,6 +245,9 @@ public static function onWritable(mixed $stream, \Closure $closure): string
* @return string A unique identifier that can be used to cancel, enable or disable the callback.
*
* @throws UnsupportedFeatureException If signal handling is not supported.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function onSignal(int $signal, \Closure $closure): string
{
Expand All @@ -229,6 +265,9 @@ public static function onSignal(int $signal, \Closure $closure): string
* @return string The callback identifier.
*
* @throws InvalidCallbackError If the callback identifier is invalid.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function enable(string $callbackId): string
{
Expand All @@ -247,6 +286,9 @@ public static function enable(string $callbackId): string
* @param string $callbackId The callback identifier.
*
* @return string The callback identifier.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function disable(string $callbackId): string
{
Expand All @@ -260,6 +302,9 @@ public static function disable(string $callbackId): string
* callback is permanently invalid. Calling this function MUST NOT fail, even if passed an invalid identifier.
*
* @param string $callbackId The callback identifier.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function cancel(string $callbackId): void
{
Expand All @@ -277,6 +322,9 @@ public static function cancel(string $callbackId): void
* @return string The callback identifier.
*
* @throws InvalidCallbackError If the callback identifier is invalid.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function reference(string $callbackId): string
{
Expand All @@ -292,6 +340,9 @@ public static function reference(string $callbackId): string
* @param string $callbackId The callback identifier.
*
* @return string The callback identifier.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function unreference(string $callbackId): string
{
Expand Down Expand Up @@ -352,6 +403,9 @@ public static function getType(string $callbackId): CallbackType
* @param string $callbackId The callback identifier.
*
* @return bool `true` if the callback is currently enabled, otherwise `false`.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function isEnabled(string $callbackId): bool
{
Expand All @@ -364,6 +418,9 @@ public static function isEnabled(string $callbackId): bool
* @param string $callbackId The callback identifier.
*
* @return bool `true` if the callback is currently referenced, otherwise `false`.
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function isReferenced(string $callbackId): bool
{
Expand Down Expand Up @@ -391,6 +448,9 @@ public static function getDriver(): Driver
* Calls from the same fiber will return the same suspension object.
*
* @return Suspension
*
* @psalm-capabilities io
* @psalm-suppress ImpureMethodCall
*/
public static function getSuspension(): Suspension
{
Expand All @@ -415,6 +475,8 @@ public static function run(): void
* Disable construction as this is a static class.
*
* @psalm-suppress UnusedConstructor
*
* @psalm-capabilities read-props
*/
private function __construct()
{
Expand Down
Loading
Loading