Streams throw exceptions instead of returning false.

This commit is contained in:
PJ Dietz 2015-04-26 20:49:59 -04:00
parent dce4bdf572
commit 4f667f1dda
4 changed files with 125 additions and 29 deletions

View File

@ -14,6 +14,10 @@ class NullStream implements StreamInterface
* *
* Warning: This could attempt to load a large amount of data into memory. * Warning: This could attempt to load a large amount of data into memory.
* *
* This method MUST NOT raise an exception in order to conform with PHP's
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
* @return string * @return string
*/ */
public function __toString() public function __toString()
@ -58,7 +62,7 @@ class NullStream implements StreamInterface
*/ */
public function tell() public function tell()
{ {
return false; return 0;
} }
/** /**
@ -91,27 +95,26 @@ class NullStream implements StreamInterface
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset * offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset. * SEEK_END: Set position to end-of-stream plus offset.
* @return bool Returns TRUE on success or FALSE on failure. * @throws \RuntimeException on failure.
*/ */
public function seek($offset, $whence = SEEK_SET) public function seek($offset, $whence = SEEK_SET)
{ {
return false; throw new \RuntimeException("Unable to seek to position.");
} }
/** /**
* Seek to the beginning of the stream. * Seek to the beginning of the stream.
* *
* If the stream is not seekable, this method will return FALSE, indicating * If the stream is not seekable, this method will raise an exception;
* failure; otherwise, it will perform a seek(0), and return the status of * otherwise, it will perform a seek(0).
* that operation.
* *
* @see seek() * @see seek()
* @link http://www.php.net/manual/en/function.fseek.php * @link http://www.php.net/manual/en/function.fseek.php
* @return bool Returns TRUE on success or FALSE on failure. * @throws \RuntimeException on failure.
*/ */
public function rewind() public function rewind()
{ {
return false; throw new \RuntimeException("Unable to rewind srream.");
} }
/** /**
@ -128,12 +131,12 @@ class NullStream implements StreamInterface
* Write data to the stream. * Write data to the stream.
* *
* @param string $string The string that is to be written. * @param string $string The string that is to be written.
* @return int|bool Returns the number of bytes written to the stream on * @return int Returns the number of bytes written to the stream.
* success or FALSE on failure. * @throws \RuntimeException on failure.
*/ */
public function write($string) public function write($string)
{ {
return false; throw new \RuntimeException("Unable to write to stream.");
} }
/** /**
@ -152,8 +155,9 @@ class NullStream implements StreamInterface
* @param int $length Read up to $length bytes from the object and return * @param int $length Read up to $length bytes from the object and return
* them. Fewer than $length bytes may be returned if underlying stream * them. Fewer than $length bytes may be returned if underlying stream
* call returns fewer bytes. * call returns fewer bytes.
* @return string|false Returns the data read from the stream, false if * @return string Returns the data read from the stream, or an empty string
* unable to read or if an error occurs. * if no bytes are available.
* @throws \RuntimeException if an error occurs.
*/ */
public function read($length) public function read($length)
{ {
@ -164,6 +168,8 @@ class NullStream implements StreamInterface
* Returns the remaining contents in a string * Returns the remaining contents in a string
* *
* @return string * @return string
* @throws \RuntimeException if unable to read or an error occurs while
* reading.
*/ */
public function getContents() public function getContents()
{ {

View File

@ -41,6 +41,10 @@ class Stream implements StreamInterface
* *
* Warning: This could attempt to load a large amount of data into memory. * Warning: This could attempt to load a large amount of data into memory.
* *
* This method MUST NOT raise an exception in order to conform with PHP's
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
* @return string * @return string
*/ */
public function __toString() public function __toString()
@ -87,11 +91,18 @@ class Stream implements StreamInterface
/** /**
* Returns the current position of the file read/write pointer * Returns the current position of the file read/write pointer
* *
* @return int|bool Position of the file pointer or false on error. * @return int Position of the file pointer
* @throws \RuntimeException on error.
*/ */
public function tell() public function tell()
{ {
return ftell($this->resource); $position = ftell($this->resource);
if ($position === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Unable to retrieve current position of file pointer.");
// @codeCoverageIgnoreEnd
}
return $position;
} }
/** /**
@ -124,11 +135,19 @@ class Stream implements StreamInterface
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset * offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset. * SEEK_END: Set position to end-of-stream plus offset.
* @return bool Returns TRUE on success or FALSE on failure. * @throws \RuntimeException on failure.
*/ */
public function seek($offset, $whence = SEEK_SET) public function seek($offset, $whence = SEEK_SET)
{ {
fseek($this->resource, $offset, $whence); $result = -1;
if ($this->isSeekable()) {
$result = fseek($this->resource, $offset, $whence);
}
if ($result === -1) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Unable to seek to position.");
// @codeCoverageIgnoreEnd
}
} }
/** /**
@ -144,7 +163,15 @@ class Stream implements StreamInterface
*/ */
public function rewind() public function rewind()
{ {
rewind($this->resource); $result = false;
if ($this->isSeekable()) {
$result = rewind($this->resource);
}
if ($result === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Unable to seek to position.");
// @codeCoverageIgnoreEnd
}
} }
/** /**
@ -162,12 +189,19 @@ class Stream implements StreamInterface
* Write data to the stream. * Write data to the stream.
* *
* @param string $string The string that is to be written. * @param string $string The string that is to be written.
* @return int|bool Returns the number of bytes written to the stream on * @return int Returns the number of bytes written to the stream.
* success or FALSE on failure. * @throws \RuntimeException on failure.
*/ */
public function write($string) public function write($string)
{ {
return fwrite($this->resource, $string); $result = false;
if ($this->isWritable()) {
$result = fwrite($this->resource, $string);
}
if ($result === false) {
throw new \RuntimeException("Unable to write to stream.");
}
return $result;
} }
/** /**
@ -192,17 +226,33 @@ class Stream implements StreamInterface
*/ */
public function read($length) public function read($length)
{ {
return fread($this->resource, $length); $result = false;
if ($this->isReadable()) {
$result = fread($this->resource, $length);
}
if ($result === false) {
throw new \RuntimeException("Unable to read from stream.");
}
return $result;
} }
/** /**
* Returns the remaining contents in a string * Returns the remaining contents in a string
* *
* @return string * @return string
* @throws \RuntimeException if unable to read or an error occurs while
* reading.
*/ */
public function getContents() public function getContents()
{ {
return stream_get_contents($this->resource); $result = false;
if ($this->isReadable()) {
$result = stream_get_contents($this->resource);
}
if ($result === false) {
throw new \RuntimeException("Unable to read from stream.");
}
return $result;
} }
/** /**

View File

@ -20,6 +20,7 @@ class NullStreamTest extends \PHPUnit_Framework_TestCase
$stream = new \WellRESTed\Message\NullStream(); $stream = new \WellRESTed\Message\NullStream();
$this->assertNull($stream->close()); $this->assertNull($stream->close());
} }
/** /**
* @covers WellRESTed\Message\NullStream::detach() * @covers WellRESTed\Message\NullStream::detach()
* @uses WellRESTed\Message\Stream * @uses WellRESTed\Message\Stream
@ -43,10 +44,10 @@ class NullStreamTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers WellRESTed\Message\NullStream::tell * @covers WellRESTed\Message\NullStream::tell
*/ */
public function testTellReturnsFalse() public function testTellReturnsZero()
{ {
$stream = new \WellRESTed\Message\NullStream(); $stream = new \WellRESTed\Message\NullStream();
$this->assertFalse($stream->tell()); $this->assertEquals(0, $stream->tell());
} }
/** /**
@ -70,20 +71,22 @@ class NullStreamTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers WellRESTed\Message\NullStream::seek * @covers WellRESTed\Message\NullStream::seek
* @expectedException \RuntimeException
*/ */
public function testSeekReturnsFalse() public function testSeekReturnsFalse()
{ {
$stream = new NullStream(); $stream = new NullStream();
$this->assertFalse($stream->seek(10)); $stream->seek(10);
} }
/** /**
* @covers WellRESTed\Message\NullStream::rewind * @covers WellRESTed\Message\NullStream::rewind
* @expectedException \RuntimeException
*/ */
public function testRewindReturnsFalse() public function testRewindReturnsFalse()
{ {
$stream = new \WellRESTed\Message\NullStream(); $stream = new \WellRESTed\Message\NullStream();
$this->assertFalse($stream->rewind()); $stream->rewind();
} }
/** /**
@ -97,11 +100,12 @@ class NullStreamTest extends \PHPUnit_Framework_TestCase
/** /**
* @covers WellRESTed\Message\NullStream::write * @covers WellRESTed\Message\NullStream::write
* @expectedException \RuntimeException
*/ */
public function testWriteReturnsFalse() public function testWriteThrowsException()
{ {
$stream = new \WellRESTed\Message\NullStream(); $stream = new \WellRESTed\Message\NullStream();
$this->assertFalse($stream->write("")); $stream->write("");
} }
/** /**

View File

@ -172,6 +172,30 @@ class StreamTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($this->content . $message, (string) $stream); $this->assertEquals($this->content . $message, (string) $stream);
} }
/**
* @covers WellRESTed\Message\Stream::write
* @expectedException \RuntimeException
*/
public function testThrowsExceptionOnErrorWriting()
{
$filename = tempnam(sys_get_temp_dir(), "php");
$handle = fopen($filename, "r");
$stream = new \WellRESTed\Message\Stream($handle);
$stream->write("Hello, world!");
}
/**
* @covers WellRESTed\Message\Stream::read
* @expectedException \RuntimeException
*/
public function testThrowsExceptionOnErrorReading()
{
$filename = tempnam(sys_get_temp_dir(), "php");
$handle = fopen($filename, "w");
$stream = new \WellRESTed\Message\Stream($handle);
$stream->read(10);
}
/** /**
* @covers WellRESTed\Message\Stream::read * @covers WellRESTed\Message\Stream::read
*/ */
@ -183,6 +207,18 @@ class StreamTest extends \PHPUnit_Framework_TestCase
$this->assertEquals("world", $string); $this->assertEquals("world", $string);
} }
/**
* @covers WellRESTed\Message\Stream::getContents
* @expectedException \RuntimeException
*/
public function testThrowsExceptionOnErrorReadingToEnd()
{
$filename = tempnam(sys_get_temp_dir(), "php");
$handle = fopen($filename, "w");
$stream = new \WellRESTed\Message\Stream($handle);
$stream->getContents();
}
/** /**
* @covers WellRESTed\Message\Stream::getContents * @covers WellRESTed\Message\Stream::getContents
*/ */