Completed
Pull Request — master (#35)
by Tobias
09:50
created

Stream::__destruct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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