Completed
Push — 2.1 ( 37580d...257604 )
by
unknown
12:15
created

ResourceStream   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 70.5%

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 2
dl 0
loc 207
ccs 55
cts 78
cp 0.705
rs 9.2
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 4 1
A __toString() 0 12 2
A close() 0 7 3
A detach() 0 10 2
A getSize() 0 14 3
A eof() 0 4 1
A isSeekable() 0 4 1
A seek() 0 6 2
A rewind() 0 4 1
A isWritable() 0 10 3
A write() 0 8 2
A isReadable() 0 10 3
A read() 0 8 2
A getContents() 0 8 2
A getMetadata() 0 12 4
A tell() 0 8 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\http;
9
10
use Psr\Http\Message\StreamInterface;
11
use yii\base\BaseObject;
12
use yii\base\ErrorHandler;
13
14
/**
15
 * ResourceStream wraps existing PHP stream resource, e.g. one opened by `fopen()`.
16
 *
17
 * Example:
18
 *
19
 * ```php
20
 * $stream = new ResourceStream([
21
 *     'resource' => tmpfile(),
22
 * ]);
23
 *
24
 * $stream->write('some content...');
25
 * $stream->close();
26
 * ```
27
 *
28
 * Usage of this class make sense in case you already have an opened PHP stream from elsewhere and wish to wrap it into `StreamInterface`.
29
 *
30
 * > Note: closing this stream will close the resource associated with it, so it becomes invalid for usage elsewhere.
31
 *
32
 * @author Paul Klimov <[email protected]>
33
 * @since 2.1.0
34
 */
35
class ResourceStream extends BaseObject implements StreamInterface
36
{
37
    /**
38
     * @var resource stream resource.
39
     */
40
    public $resource;
41
42
    /**
43
     * @var array a resource metadata.
44
     */
45
    private $_metadata;
46
47
48
    /**
49
     * Destructor.
50
     * Closes the stream resource when destroyed.
51
     */
52 47
    public function __destruct()
53
    {
54 47
        $this->close();
55 47
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 1
    public function __toString()
61
    {
62
        // __toString cannot throw exception
63
        // use trigger_error to bypass this limitation
64
        try {
65 1
            $this->seek(0);
66 1
            return $this->getContents();
67
        } catch (\Exception $e) {
68
            ErrorHandler::convertExceptionToError($e);
69
            return '';
70
        }
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 47
    public function close()
77
    {
78 47
        if ($this->resource !== null && is_resource($this->resource)) {
79 7
            fclose($this->resource);
80 7
            $this->_metadata = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $_metadata.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
81
        }
82 47
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function detach()
88
    {
89
        if ($this->resource === null) {
90
            return null;
91
        }
92
        $result = $this->resource;
93
        $this->resource = null;
94
        $this->_metadata = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $_metadata.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
95
        return $result;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 1
    public function getSize()
102
    {
103 1
        $uri = $this->getMetadata('uri');
104 1
        if (!empty($uri)) {
105
            // clear the stat cache in case stream has a URI
106 1
            clearstatcache(true, $uri);
107
        }
108
109 1
        $stats = fstat($this->resource);
110 1
        if (isset($stats['size'])) {
111 1
            return $stats['size'];
112
        }
113
        return null;
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function tell()
120
    {
121
        $result = ftell($this->resource);
122
        if ($result === false) {
123
            throw new \RuntimeException('Unable to determine stream position');
124
        }
125
        return $result;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 1
    public function eof()
132
    {
133 1
        return feof($this->resource);
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 1
    public function isSeekable()
140
    {
141 1
        return (bool)$this->getMetadata('seekable');
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 3
    public function seek($offset, $whence = SEEK_SET)
148
    {
149 3
        if (fseek($this->resource, $offset, $whence) === -1) {
150
            throw new \RuntimeException("Unable to seek to stream position '{$offset}' with whence '{$whence}'");
151
        }
152 3
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function rewind()
158
    {
159
        $this->seek(0);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165 22
    public function isWritable()
166
    {
167 22
        $mode = $this->getMetadata('mode');
168 22
        foreach (['w', 'c', 'a', 'x', 'r+'] as $key) {
169 22
            if (strpos($mode, $key) !== false) {
170 22
                return true;
171
            }
172
        }
173 4
        return false;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 1
    public function write($string)
180
    {
181 1
        $result = fwrite($this->resource, $string);
182 1
        if ($result === false) {
183
            throw new \RuntimeException('Unable to write to stream');
184
        }
185 1
        return $result;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 21
    public function isReadable()
192
    {
193 21
        $mode = $this->getMetadata('mode');
194 21
        foreach (['r', 'w+', 'a+', 'c+', 'x+'] as $key) {
195 21
            if (strpos($mode, $key) !== false) {
196 21
                return true;
197
            }
198
        }
199 5
        return false;
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205 2
    public function read($length)
206
    {
207 2
        $string = fread($this->resource, $length);
208 2
        if ($string === false) {
209
            throw new \RuntimeException('Unable to read from stream');
210
        }
211 2
        return $string;
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217 2
    public function getContents()
218
    {
219 2
        $contents = stream_get_contents($this->resource);
220 2
        if ($contents === false) {
221
            throw new \RuntimeException('Unable to read stream contents');
222
        }
223 2
        return $contents;
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229 4
    public function getMetadata($key = null)
230
    {
231 4
        if ($this->_metadata === null) {
232 4
            $this->_metadata = stream_get_meta_data($this->resource);
233
        }
234
235 4
        if ($key === null) {
236 1
            return $this->_metadata;
237
        }
238
239 4
        return isset($this->_metadata[$key]) ? $this->_metadata[$key] : null;
240
    }
241
}