酒店预订平台
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

142 行
4.2 KiB

  1. <?php
  2. namespace GuzzleHttp\Psr7;
  3. use Psr\Http\Message\StreamInterface;
  4. /**
  5. * Stream decorator that can cache previously read bytes from a sequentially
  6. * read stream.
  7. *
  8. * @final
  9. */
  10. class CachingStream implements StreamInterface
  11. {
  12. use StreamDecoratorTrait;
  13. /** @var StreamInterface Stream being wrapped */
  14. private $remoteStream;
  15. /** @var int Number of bytes to skip reading due to a write on the buffer */
  16. private $skipReadBytes = 0;
  17. /**
  18. * We will treat the buffer object as the body of the stream
  19. *
  20. * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
  21. * @param StreamInterface $target Optionally specify where data is cached
  22. */
  23. public function __construct(
  24. StreamInterface $stream,
  25. StreamInterface $target = null
  26. ) {
  27. $this->remoteStream = $stream;
  28. $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
  29. }
  30. public function getSize()
  31. {
  32. return max($this->stream->getSize(), $this->remoteStream->getSize());
  33. }
  34. public function rewind()
  35. {
  36. $this->seek(0);
  37. }
  38. public function seek($offset, $whence = SEEK_SET)
  39. {
  40. if ($whence == SEEK_SET) {
  41. $byte = $offset;
  42. } elseif ($whence == SEEK_CUR) {
  43. $byte = $offset + $this->tell();
  44. } elseif ($whence == SEEK_END) {
  45. $size = $this->remoteStream->getSize();
  46. if ($size === null) {
  47. $size = $this->cacheEntireStream();
  48. }
  49. $byte = $size + $offset;
  50. } else {
  51. throw new \InvalidArgumentException('Invalid whence');
  52. }
  53. $diff = $byte - $this->stream->getSize();
  54. if ($diff > 0) {
  55. // Read the remoteStream until we have read in at least the amount
  56. // of bytes requested, or we reach the end of the file.
  57. while ($diff > 0 && !$this->remoteStream->eof()) {
  58. $this->read($diff);
  59. $diff = $byte - $this->stream->getSize();
  60. }
  61. } else {
  62. // We can just do a normal seek since we've already seen this byte.
  63. $this->stream->seek($byte);
  64. }
  65. }
  66. public function read($length)
  67. {
  68. // Perform a regular read on any previously read data from the buffer
  69. $data = $this->stream->read($length);
  70. $remaining = $length - strlen($data);
  71. // More data was requested so read from the remote stream
  72. if ($remaining) {
  73. // If data was written to the buffer in a position that would have
  74. // been filled from the remote stream, then we must skip bytes on
  75. // the remote stream to emulate overwriting bytes from that
  76. // position. This mimics the behavior of other PHP stream wrappers.
  77. $remoteData = $this->remoteStream->read(
  78. $remaining + $this->skipReadBytes
  79. );
  80. if ($this->skipReadBytes) {
  81. $len = strlen($remoteData);
  82. $remoteData = substr($remoteData, $this->skipReadBytes);
  83. $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
  84. }
  85. $data .= $remoteData;
  86. $this->stream->write($remoteData);
  87. }
  88. return $data;
  89. }
  90. public function write($string)
  91. {
  92. // When appending to the end of the currently read stream, you'll want
  93. // to skip bytes from being read from the remote stream to emulate
  94. // other stream wrappers. Basically replacing bytes of data of a fixed
  95. // length.
  96. $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
  97. if ($overflow > 0) {
  98. $this->skipReadBytes += $overflow;
  99. }
  100. return $this->stream->write($string);
  101. }
  102. public function eof()
  103. {
  104. return $this->stream->eof() && $this->remoteStream->eof();
  105. }
  106. /**
  107. * Close both the remote stream and buffer stream
  108. */
  109. public function close()
  110. {
  111. $this->remoteStream->close() && $this->stream->close();
  112. }
  113. private function cacheEntireStream()
  114. {
  115. $target = new FnStream(['write' => 'strlen']);
  116. Utils::copyToStream($this, $target);
  117. return $this->tell();
  118. }
  119. }