Completed
Pull Request — master (#28)
by Tobias
16:23 queued 07:56
created

Stream::isSeekable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Http\Client\Socket;
4
5
use Http\Client\Socket\Exception\StreamException;
6
use Http\Client\Socket\Exception\TimeoutException;
7
use Psr\Http\Message\StreamInterface;
8
9
/**
10
 * Stream implementation for Socket Client.
11
 *
12
 * This implementation is used to have a Stream which react better to the Socket Client behavior.
13
 *
14
 * The main advantage is you can get the response of a request even if it's not finish, the response is available
15
 * as soon as all headers are received, this stream will have the remaining socket used for the request / response
16
 * call.
17
 *
18
 * It is only readable once, if you want to read the content multiple times, you can store contents of this
19
 * stream into a variable or encapsulate it in a buffered stream.
20
 *
21
 * Writing and seeking is disable to avoid weird behaviors.
22
 *
23
 * @author Joel Wurtz <[email protected]>
24
 */
25
class Stream implements StreamInterface
26
{
27
    /** @var resource Underlying socket */
28
    private $socket;
29
30
    /**
31
     * @var bool Is stream detached
32
     */
33
    private $isDetached = false;
34
35
    /**
36
     * @var int|null Size of the stream, so we know what we must read, null if not available (i.e. a chunked stream)
37
     */
38
    private $size;
39
40
    /**
41
     * @var int Size of the stream readed, to avoid reading more than available and have the user blocked
42
     */
43
    private $readed = 0;
44
45
    /**
46
     * Create the stream.
47
     *
48
     * @param resource $socket
49
     * @param int      $size
50
     */
51 71
    public function __construct($socket, $size = null)
52
    {
53 71
        $this->socket = $socket;
54 71
        $this->size = $size;
55 71
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 1
    public function __toString()
61
    {
62
        try {
63 1
            if ($this->isSeekable()) {
64
                $this->seek(0);
65
            }
66
67 1
            return $this->getContents();
68
        } catch (\Exception $e) {
69
            return '';
70
        }
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 5
    public function close()
77
    {
78 5
        fclose($this->socket);
79 5
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 1
    public function detach()
85
    {
86 1
        $this->isDetached = true;
87 1
        $socket = $this->socket;
88 1
        $this->socket = null;
89
90 1
        return $socket;
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 58
    public function getSize()
97
    {
98 58
        return $this->size;
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 1
    public function tell()
105
    {
106 1
        return ftell($this->socket);
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 1
    public function eof()
113
    {
114 1
        return feof($this->socket);
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 2
    public function isSeekable()
121
    {
122 2
        return false;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 1
    public function seek($offset, $whence = SEEK_SET)
129
    {
130 1
        throw new StreamException('This stream is not seekable');
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 1
    public function rewind()
137
    {
138 1
        throw new StreamException('This stream is not seekable');
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 1
    public function isWritable()
145
    {
146 1
        return false;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 1
    public function write($string)
153
    {
154 1
        throw new StreamException('This stream is not writable');
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 1
    public function isReadable()
161
    {
162 1
        return true;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 4
    public function read($length)
169
    {
170 4
        if (null === $this->getSize()) {
171
            return fread($this->socket, $length);
172
        }
173
174 4
        if ($this->getSize() === $this->readed) {
175 1
            return '';
176
        }
177
178
        // Even if we request a length a non blocking stream can return less data than asked
179 4
        $read = fread($this->socket, $length);
180
181 4
        if ($this->getMetadata('timed_out')) {
182
            throw new TimeoutException('Stream timed out while reading data');
183
        }
184
185 4
        $this->readed += strlen($read);
186
187 4
        return $read;
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193 57
    public function getContents()
194
    {
195 57
        if (null === $this->getSize()) {
196 54
            return stream_get_contents($this->socket);
197
        }
198
199 3
        $contents = '';
200
201
        do {
202 3
            $contents .= $this->read($this->getSize() - $this->readed);
203 3
        } while ($this->readed < $this->getSize());
204
205 3
        return $contents;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 5
    public function getMetadata($key = null)
212
    {
213 5
        $meta = stream_get_meta_data($this->socket);
214
215 5
        if (null === $key) {
216
            return $meta;
217
        }
218
219 5
        return $meta[$key];
220
    }
221
}
222