Completed
Pull Request — master (#35)
by Tobias
04:15
created

Stream   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 93.44%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 25
c 4
b 1
f 0
lcom 1
cbo 2
dl 0
loc 203
ccs 57
cts 61
cp 0.9344
rs 10

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 8 2
A __construct() 0 5 1
A __destruct() 0 6 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\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
            return $this->getContents();
64
        } catch (\Exception $e) {
65
            return '';
66
        }
67
    }
68
69
    /**
70
     * Close the socket when the object is destructed.
71
     */
72 71
    public function __destruct()
73
    {
74 71
        if (is_resource($this->socket)) {
75 65
            $this->close();
76 65
        }
77 71
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 70
    public function close()
83
    {
84 70
        fclose($this->socket);
85 70
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 1
    public function detach()
91
    {
92 1
        $this->isDetached = true;
93 1
        $socket = $this->socket;
94 1
        $this->socket = null;
95
96 1
        return $socket;
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 58
    public function getSize()
103
    {
104 58
        return $this->size;
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 1
    public function tell()
111
    {
112 1
        return ftell($this->socket);
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1
    public function eof()
119
    {
120 1
        return feof($this->socket);
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 1
    public function isSeekable()
127
    {
128 1
        return false;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 1
    public function seek($offset, $whence = SEEK_SET)
135
    {
136 1
        throw new StreamException('This stream is not seekable');
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 1
    public function rewind()
143
    {
144 1
        throw new StreamException('This stream is not seekable');
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 1
    public function isWritable()
151
    {
152 1
        return false;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 1
    public function write($string)
159
    {
160 1
        throw new StreamException('This stream is not writable');
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166 1
    public function isReadable()
167
    {
168 1
        return true;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174 5
    public function read($length)
175
    {
176 5
        if (null === $this->getSize()) {
177
            return fread($this->socket, $length);
178
        }
179
180 5
        if ($this->getSize() === $this->readed) {
181 1
            return '';
182
        }
183
184
        // Even if we request a length a non blocking stream can return less data than asked
185 5
        $read = fread($this->socket, $length);
186
187 5
        if ($this->getMetadata('timed_out')) {
188 1
            throw new TimeoutException('Stream timed out while reading data');
189
        }
190
191 4
        $this->readed += strlen($read);
192
193 4
        return $read;
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 57
    public function getContents()
200
    {
201 57
        if (null === $this->getSize()) {
202 53
            return stream_get_contents($this->socket);
203
        }
204
205 4
        $contents = '';
206
207
        do {
208 4
            $contents .= $this->read($this->getSize() - $this->readed);
209 3
        } while ($this->readed < $this->getSize());
210
211 3
        return $contents;
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217 6
    public function getMetadata($key = null)
218
    {
219 6
        $meta = stream_get_meta_data($this->socket);
220
221 6
        if (null === $key) {
222
            return $meta;
223
        }
224
225 6
        return $meta[$key];
226
    }
227
}
228