Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

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