StreamTest.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. <?php
  2. namespace GuzzleHttp\Tests\Psr7;
  3. use GuzzleHttp\Psr7\NoSeekStream;
  4. use GuzzleHttp\Psr7\Stream;
  5. /**
  6. * @covers GuzzleHttp\Psr7\Stream
  7. */
  8. class StreamTest extends BaseTest
  9. {
  10. public static $isFReadError = false;
  11. /**
  12. * @expectedException \InvalidArgumentException
  13. */
  14. public function testConstructorThrowsExceptionOnInvalidArgument()
  15. {
  16. new Stream(true);
  17. }
  18. public function testConstructorInitializesProperties()
  19. {
  20. $handle = fopen('php://temp', 'r+');
  21. fwrite($handle, 'data');
  22. $stream = new Stream($handle);
  23. $this->assertTrue($stream->isReadable());
  24. $this->assertTrue($stream->isWritable());
  25. $this->assertTrue($stream->isSeekable());
  26. $this->assertEquals('php://temp', $stream->getMetadata('uri'));
  27. $this->assertInternalType('array', $stream->getMetadata());
  28. $this->assertEquals(4, $stream->getSize());
  29. $this->assertFalse($stream->eof());
  30. $stream->close();
  31. }
  32. public function testConstructorInitializesPropertiesWithRbPlus()
  33. {
  34. $handle = fopen('php://temp', 'rb+');
  35. fwrite($handle, 'data');
  36. $stream = new Stream($handle);
  37. $this->assertTrue($stream->isReadable());
  38. $this->assertTrue($stream->isWritable());
  39. $this->assertTrue($stream->isSeekable());
  40. $this->assertEquals('php://temp', $stream->getMetadata('uri'));
  41. $this->assertInternalType('array', $stream->getMetadata());
  42. $this->assertEquals(4, $stream->getSize());
  43. $this->assertFalse($stream->eof());
  44. $stream->close();
  45. }
  46. public function testStreamClosesHandleOnDestruct()
  47. {
  48. $handle = fopen('php://temp', 'r');
  49. $stream = new Stream($handle);
  50. unset($stream);
  51. $this->assertFalse(is_resource($handle));
  52. }
  53. public function testConvertsToString()
  54. {
  55. $handle = fopen('php://temp', 'w+');
  56. fwrite($handle, 'data');
  57. $stream = new Stream($handle);
  58. $this->assertEquals('data', (string) $stream);
  59. $this->assertEquals('data', (string) $stream);
  60. $stream->close();
  61. }
  62. public function testGetsContents()
  63. {
  64. $handle = fopen('php://temp', 'w+');
  65. fwrite($handle, 'data');
  66. $stream = new Stream($handle);
  67. $this->assertEquals('', $stream->getContents());
  68. $stream->seek(0);
  69. $this->assertEquals('data', $stream->getContents());
  70. $this->assertEquals('', $stream->getContents());
  71. $stream->close();
  72. }
  73. public function testChecksEof()
  74. {
  75. $handle = fopen('php://temp', 'w+');
  76. fwrite($handle, 'data');
  77. $stream = new Stream($handle);
  78. $this->assertSame(4, $stream->tell(), 'Stream cursor already at the end');
  79. $this->assertFalse($stream->eof(), 'Stream still not eof');
  80. $this->assertSame('', $stream->read(1), 'Need to read one more byte to reach eof');
  81. $this->assertTrue($stream->eof());
  82. $stream->close();
  83. }
  84. public function testGetSize()
  85. {
  86. $size = filesize(__FILE__);
  87. $handle = fopen(__FILE__, 'r');
  88. $stream = new Stream($handle);
  89. $this->assertEquals($size, $stream->getSize());
  90. // Load from cache
  91. $this->assertEquals($size, $stream->getSize());
  92. $stream->close();
  93. }
  94. public function testEnsuresSizeIsConsistent()
  95. {
  96. $h = fopen('php://temp', 'w+');
  97. $this->assertEquals(3, fwrite($h, 'foo'));
  98. $stream = new Stream($h);
  99. $this->assertEquals(3, $stream->getSize());
  100. $this->assertEquals(4, $stream->write('test'));
  101. $this->assertEquals(7, $stream->getSize());
  102. $this->assertEquals(7, $stream->getSize());
  103. $stream->close();
  104. }
  105. public function testProvidesStreamPosition()
  106. {
  107. $handle = fopen('php://temp', 'w+');
  108. $stream = new Stream($handle);
  109. $this->assertEquals(0, $stream->tell());
  110. $stream->write('foo');
  111. $this->assertEquals(3, $stream->tell());
  112. $stream->seek(1);
  113. $this->assertEquals(1, $stream->tell());
  114. $this->assertSame(ftell($handle), $stream->tell());
  115. $stream->close();
  116. }
  117. public function testDetachStreamAndClearProperties()
  118. {
  119. $handle = fopen('php://temp', 'r');
  120. $stream = new Stream($handle);
  121. $this->assertSame($handle, $stream->detach());
  122. $this->assertInternalType('resource', $handle, 'Stream is not closed');
  123. $this->assertNull($stream->detach());
  124. $this->assertStreamStateAfterClosedOrDetached($stream);
  125. $stream->close();
  126. }
  127. public function testCloseResourceAndClearProperties()
  128. {
  129. $handle = fopen('php://temp', 'r');
  130. $stream = new Stream($handle);
  131. $stream->close();
  132. $this->assertFalse(is_resource($handle));
  133. $this->assertStreamStateAfterClosedOrDetached($stream);
  134. }
  135. private function assertStreamStateAfterClosedOrDetached(Stream $stream)
  136. {
  137. $this->assertFalse($stream->isReadable());
  138. $this->assertFalse($stream->isWritable());
  139. $this->assertFalse($stream->isSeekable());
  140. $this->assertNull($stream->getSize());
  141. $this->assertSame([], $stream->getMetadata());
  142. $this->assertNull($stream->getMetadata('foo'));
  143. $throws = function (callable $fn) {
  144. try {
  145. $fn();
  146. } catch (\Exception $e) {
  147. $this->assertContains('Stream is detached', $e->getMessage());
  148. return;
  149. }
  150. $this->fail('Exception should be thrown after the stream is detached.');
  151. };
  152. $throws(function () use ($stream) { $stream->read(10); });
  153. $throws(function () use ($stream) { $stream->write('bar'); });
  154. $throws(function () use ($stream) { $stream->seek(10); });
  155. $throws(function () use ($stream) { $stream->tell(); });
  156. $throws(function () use ($stream) { $stream->eof(); });
  157. $throws(function () use ($stream) { $stream->getContents(); });
  158. $this->assertSame('', (string) $stream);
  159. }
  160. public function testStreamReadingWithZeroLength()
  161. {
  162. $r = fopen('php://temp', 'r');
  163. $stream = new Stream($r);
  164. $this->assertSame('', $stream->read(0));
  165. $stream->close();
  166. }
  167. /**
  168. * @expectedException \RuntimeException
  169. * @expectedExceptionMessage Length parameter cannot be negative
  170. */
  171. public function testStreamReadingWithNegativeLength()
  172. {
  173. $r = fopen('php://temp', 'r');
  174. $stream = new Stream($r);
  175. try {
  176. $stream->read(-1);
  177. } catch (\Exception $e) {
  178. $stream->close();
  179. throw $e;
  180. }
  181. $stream->close();
  182. }
  183. /**
  184. * @expectedException \RuntimeException
  185. * @expectedExceptionMessage Unable to read from stream
  186. */
  187. public function testStreamReadingFreadError()
  188. {
  189. self::$isFReadError = true;
  190. $r = fopen('php://temp', 'r');
  191. $stream = new Stream($r);
  192. try {
  193. $stream->read(1);
  194. } catch (\Exception $e) {
  195. self::$isFReadError = false;
  196. $stream->close();
  197. throw $e;
  198. }
  199. self::$isFReadError = false;
  200. $stream->close();
  201. }
  202. /**
  203. * @dataProvider gzipModeProvider
  204. *
  205. * @param string $mode
  206. * @param bool $readable
  207. * @param bool $writable
  208. */
  209. public function testGzipStreamModes($mode, $readable, $writable)
  210. {
  211. if (defined('HHVM_VERSION')) {
  212. $this->markTestSkipped('This does not work on HHVM.');
  213. }
  214. $r = gzopen('php://temp', $mode);
  215. $stream = new Stream($r);
  216. $this->assertSame($readable, $stream->isReadable());
  217. $this->assertSame($writable, $stream->isWritable());
  218. $stream->close();
  219. }
  220. public function gzipModeProvider()
  221. {
  222. return [
  223. ['mode' => 'rb9', 'readable' => true, 'writable' => false],
  224. ['mode' => 'wb2', 'readable' => false, 'writable' => true],
  225. ];
  226. }
  227. /**
  228. * @dataProvider readableModeProvider
  229. *
  230. * @param string $mode
  231. */
  232. public function testReadableStream($mode)
  233. {
  234. $r = fopen('php://temp', $mode);
  235. $stream = new Stream($r);
  236. $this->assertTrue($stream->isReadable());
  237. $stream->close();
  238. }
  239. public function readableModeProvider()
  240. {
  241. return [
  242. ['r'],
  243. ['w+'],
  244. ['r+'],
  245. ['x+'],
  246. ['c+'],
  247. ['rb'],
  248. ['w+b'],
  249. ['r+b'],
  250. ['x+b'],
  251. ['c+b'],
  252. ['rt'],
  253. ['w+t'],
  254. ['r+t'],
  255. ['x+t'],
  256. ['c+t'],
  257. ['a+'],
  258. ['rb+'],
  259. ];
  260. }
  261. public function testWriteOnlyStreamIsNotReadable()
  262. {
  263. $r = fopen('php://output', 'w');
  264. $stream = new Stream($r);
  265. $this->assertFalse($stream->isReadable());
  266. $stream->close();
  267. }
  268. /**
  269. * @dataProvider writableModeProvider
  270. *
  271. * @param string $mode
  272. */
  273. public function testWritableStream($mode)
  274. {
  275. $r = fopen('php://temp', $mode);
  276. $stream = new Stream($r);
  277. $this->assertTrue($stream->isWritable());
  278. $stream->close();
  279. }
  280. public function writableModeProvider()
  281. {
  282. return [
  283. ['w'],
  284. ['w+'],
  285. ['rw'],
  286. ['r+'],
  287. ['x+'],
  288. ['c+'],
  289. ['wb'],
  290. ['w+b'],
  291. ['r+b'],
  292. ['rb+'],
  293. ['x+b'],
  294. ['c+b'],
  295. ['w+t'],
  296. ['r+t'],
  297. ['x+t'],
  298. ['c+t'],
  299. ['a'],
  300. ['a+'],
  301. ];
  302. }
  303. public function testReadOnlyStreamIsNotWritable()
  304. {
  305. $r = fopen('php://input', 'r');
  306. $stream = new Stream($r);
  307. $this->assertFalse($stream->isWritable());
  308. $stream->close();
  309. }
  310. }
  311. namespace GuzzleHttp\Psr7;
  312. use GuzzleHttp\Tests\Psr7\StreamTest;
  313. function fread($handle, $length)
  314. {
  315. return StreamTest::$isFReadError ? false : \fread($handle, $length);
  316. }