Warning: Closing resources locally, e.g. with fclose, might not invoke the callback. Be sure to cancel the
watcher when closing the resource locally. Drivers MAY choose to notify the user if there are watchers on invalid
resources, but are not required to, due to the high performance impact. Watchers on closed resources are
therefore undefined behavior.
Multiple watchers on the same stream MAY be executed in any order.
public static onReadable ( resource $stream, callable $callback, mixed $data = null ) : string | ||
$stream | resource | The stream to monitor. |
$callback | callable | |
$data | mixed | Arbitrary data given to the callback function as the `$data` parameter. |
return | string | An unique identifier that can be used to cancel, enable or disable the watcher. |
public function read(int $length = 8192) : Awaitable { if (!$this->readerEnabled) { $len = \strlen($this->readBuffer); try { $chunk = $this->fillReadBuffer($len, $length); } catch (\Throwable $e) { return new Failure($e); } if ($chunk === null) { return new Success(null); } if ($chunk !== $this) { if ($len > $length) { $this->readBuffer = \substr($chunk, $length); return new Success(\substr($chunk, 0, $length)); } $this->readBuffer = ''; return new Success($chunk); } $this->readerEnabled = true; if ($this->pendingReads === null) { $this->pendingReads = new \SplQueue(); } if ($this->readWatcher === null) { $this->readWatcher = Loop::onReadable($this->socket, $this->createReadWatcher()); } else { try { Loop::enable($this->readWatcher); } catch (InvalidWatcherException $e) { $this->readWatcher = Loop::onReadable($this->socket, $this->createReadWatcher()); } } } $this->pendingReads->enqueue($read = new PendingRead($length, function () { foreach ($this->pendingReads as $read) { if (!$read->disabled) { return; } } $this->readerEnabled = false; Loop::disable($this->readWatcher); })); return $read; }
public function __construct($stream, float $timeout = 0) { if (!\is_resource($stream)) { throw new \InvalidArgumentException(\sprintf('Expecting stream resource, given %s', \is_object($stream) ? \get_class($stream) : \gettype($stream))); } $this->watcher = Loop::onReadable($stream, function () use($stream) { if ($this->state === self::PENDING) { Loop::cancel($this->watcher); if ($this->timer !== null) { Loop::cancel($this->timer); } $this->resolve($stream); } }); if ($timeout > 0) { $this->timer = Loop::delay($timeout * 1000, function () use($timeout) { if ($this->state === self::PENDING) { Loop::cancel($this->watcher); $this->fail(new TimeoutException(\sprintf('Await Read timed out after %.3f seconds', $timeout))); } }); } }
public function handleCallback(string $func, ...$args) : Awaitable { $request = null; $defer = new Deferred(function () use(&$request) { if (\is_resource($request)) { \eio_cancel($request); } }); $defer->when(function () { $this->pending--; if (!$this->pending) { Loop::disable($this->watcherId); } }); for ($len = \count($args), $i = 0; $i < $len; $i++) { if ($args[$i] instanceof \Closure) { $callback = $args[$i]; $args[$i] = function (...$args) use($defer, $callback) { try { $result = $callback(...$args); } catch (\Throwable $e) { return $defer->fail($e); } if ($result instanceof \Generator) { $defer->resolve(new Coroutine($result)); } else { $defer->resolve($result); } }; break; } } if (!isset($callback)) { throw new \InvalidArgumentException('Missing callback argument'); } if (!$this->pending) { if ($this->watcherId === null) { $this->watcherId = Loop::onReadable(self::$eio, self::$callback); } else { Loop::enable($this->watcherId); } } $this->pending++; try { $request = $func(...$args); } catch (\Throwable $e) { $this->pending--; if (!$this->pending) { Loop::disable($this->watcherId); } return new Failure($e); } return $defer; }