/** * [COROUTINE] Write data to this stream. * * Execution of the current strand is suspended until the data is sent. * * Write operations must be exclusive. If concurrent writes are attempted a * StreamLockedException is thrown. * * @param string $buffer The data to write to the stream. * @param integer|null $length The maximum number of bytes to write. * * @return integer The number of bytes written. * @throws StreamClosedException if the stream is already closed. * @throws StreamLockedException if concurrent writes are unsupported. * @throws StreamWriteException if an error occurs while writing to the stream. */ public function write($buffer, $length = null) { if ($this->strand) { throw new StreamLockedException(); } elseif ($this->isClosed()) { throw new StreamClosedException(); } (yield Recoil::suspend(function ($strand) { $this->strand = $strand; $strand->kernel()->eventLoop()->addWriteStream($this->stream, function ($stream, $eventLoop) use($strand) { $eventLoop->removeWriteStream($this->stream); $this->strand->resumeWithValue(null); }); })); $this->strand = null; $exception = null; set_error_handler(function ($code, $message, $file, $line) use(&$exception) { $exception = new ErrorException($message, 0, $code, $file, $line); }); $bytesWritten = fwrite($this->stream, $buffer, $length ?: strlen($buffer)); restore_error_handler(); if (false === $bytesWritten) { throw new StreamWriteException($exception); } (yield Recoil::return_($bytesWritten)); // @codeCoverageIgnoreStart }
/** * [COROUTINE] Read data from the stream. * * Execution of the current strand is suspended until data is available or * the end of the data stream is reached. * * Read operations must be exclusive. If concurrent reads are attempted a * StreamLockedException is thrown. * * @param integer $length The maximum number of bytes to read. * * @return string The data read from the stream. * @throws StreamClosedException if the stream is already closed. * @throws StreamLockedException if concurrent reads are unsupported. * @throws StreamReadException if an error occurs while reading from the stream. */ public function read($length) { if ($this->strand) { throw new StreamLockedException(); } elseif ($this->isClosed()) { throw new StreamClosedException(); } (yield Recoil::suspend(function ($strand) { $this->strand = $strand; $strand->kernel()->eventLoop()->addReadStream($this->stream, function ($stream, $eventLoop) { $eventLoop->removeReadStream($stream); $this->strand->resumeWithValue(null); }); })); $this->strand = null; $exception = null; set_error_handler(function ($code, $message, $file, $line) use(&$exception) { $exception = new ErrorException($message, 0, $code, $file, $line); }); $buffer = fread($this->stream, $length); restore_error_handler(); if (is_resource($this->stream) && feof($this->stream)) { fclose($this->stream); } if (false === $buffer) { throw new StreamReadException($exception); } (yield Recoil::return_($buffer)); // @codeCoverageIgnoreStart }
/** * [COROUTINE] Read a value from this channel. * * Execution of the current strand is suspended until a value is available. * * If the channel is already closed, or is closed while a read operation is * pending a ChannelClosedException is thrown. * * @return mixed The value read from the channel. * @throws ChannelClosedException if the channel has been closed. */ public function read() { if ($this->isClosed()) { throw new ChannelClosedException(); } $value = (yield Recoil::suspend(function ($strand) { $this->readStrands->push($strand); if (!$this->writeStrands->isEmpty()) { $writeStrand = $this->writeStrands->dequeue(); $writeStrand->resumeWithValue(null); } })); (yield Recoil::return_($value)); // @codeCoverageIgnoreStart }
/** * [COROUTINE] Read a value from this channel. * * Execution of the current strand is suspended until a value is available. * * If the channel is already closed, or is closed while a read operation is * pending a ChannelClosedException is thrown. * * Read operations must be exclusive only if the underlying stream requires * exclusive reads. * * @return mixed The value read from the channel. * @throws ChannelClosedException if the channel has been closed. * @throws ChannelLockedException if concurrent reads are unsupported. */ public function read() { while (!$this->unserializer->hasValue()) { try { $buffer = (yield $this->stream->read($this->bufferSize)); } catch (StreamClosedException $e) { throw new ChannelClosedException($e); } catch (StreamLockedException $e) { throw new ChannelLockedException($e); } $this->unserializer->feed($buffer); if ($this->stream->isClosed()) { $this->unserializer->finalize(); } } (yield Recoil::return_($this->unserializer->unserialize())); // @codeCoverageIgnoreStart }
/** * [COROUTINE] Write data to this stream. * * Execution of the current strand is suspended until the underlying stream * is drained. * * Write operations must be exclusive. If concurrent writes are attempted a * StreamLockedException is thrown. * * @param string $buffer The data to write to the stream. * @param integer|null $length The maximum number of bytes to write. * * @return integer The number of bytes written. * @throws StreamClosedException if the stream is already closed. * @throws StreamLockedException if concurrent writes are unsupported. * @throws StreamWriteException if an error occurs while writing to the stream. */ public function write($buffer, $length = null) { if ($this->strand) { throw new StreamLockedException(); } elseif ($this->isClosed()) { throw new StreamClosedException(); } if (null === $length) { $length = strlen($buffer); } else { $buffer = substr($buffer, 0, $length); } (yield Recoil::suspend(function ($strand) use($buffer) { $this->strand = $strand; if ($this->stream->write($buffer)) { $this->onStreamDrain(); } })); $this->strand = null; (yield Recoil::return_($length)); // @codeCoverageIgnoreStart }
/** * [COROUTINE] Read data from the stream. * * Execution of the current strand is suspended until data is available or * the end of the data stream is reached. * * Read operations must be exclusive. If concurrent reads are attempted a * StreamLockedException is thrown. * * @param integer $length The maximum number of bytes to read. * * @return string The data read from the stream. * @throws StreamClosedException if the stream is already closed. * @throws StreamLockedException if concurrent reads are unsupported. * @throws StreamReadException if an error occurs while reading from the stream. */ public function read($length) { if ($this->strand) { throw new StreamLockedException(); } elseif ($this->isClosed()) { throw new StreamClosedException(); } if (!$this->buffer) { (yield Recoil::suspend(function ($strand) { $this->strand = $strand; $this->stream->resume(); })); $this->strand = null; } if (strlen($this->buffer) > $length) { $buffer = substr($this->buffer, 0, $length); $this->buffer = substr($this->buffer, $length); } else { $buffer = $this->buffer; $this->buffer = ''; } (yield Recoil::return_($buffer)); // @codeCoverageIgnoreStart }