Stream   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 92.98%

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 2
dl 0
loc 198
ccs 53
cts 57
cp 0.9298
rs 10
c 0
b 0
f 0

16 Methods

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