Completed
Push — master ( 2b1385...7c6a84 )
by Thomas
07:21
created

Stream::factory()   D

Complexity

Conditions 10
Paths 8

Size

Total Lines 42
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
rs 4.8197
cc 10
eloc 25
nc 8
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace GuzzleHttp\Stream;
3
4
/**
5
 * PHP stream implementation
6
 */
7
class Stream implements StreamInterface
8
{
9
    private $stream;
10
    private $size;
11
    private $seekable;
12
    private $readable;
13
    private $writable;
14
    private $uri;
15
    private $customMetadata;
16
17
    /** @var array Hash of readable and writable stream types */
18
    private static $readWriteHash = [
19
        'read' => [
20
            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
21
            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
22
            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
23
            'x+t' => true, 'c+t' => true, 'a+' => true
24
        ],
25
        'write' => [
26
            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
27
            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
28
            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
29
            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
30
        ]
31
    ];
32
33
    /**
34
     * Create a new stream based on the input type.
35
     *
36
     * This factory accepts the same associative array of options as described
37
     * in the constructor.
38
     *
39
     * @param resource|string|StreamInterface $resource Entity body data
40
     * @param array                           $options  Additional options
41
     *
42
     * @return Stream
43
     * @throws \InvalidArgumentException if the $resource arg is not valid.
44
     */
45
    public static function factory($resource = '', array $options = [])
46
    {
47
        $type = gettype($resource);
48
49
        if ($type == 'string') {
50
            $stream = fopen('php://temp', 'r+');
51
            if ($resource !== '') {
52
                fwrite($stream, $resource);
53
                fseek($stream, 0);
54
            }
55
            return new self($stream, $options);
56
        }
57
58
        if ($type == 'resource') {
59
            return new self($resource, $options);
60
        }
61
62
        if ($resource instanceof StreamInterface) {
63
            return $resource;
64
        }
65
66
        if ($type == 'object' && method_exists($resource, '__toString')) {
67
            return self::factory((string) $resource, $options);
68
        }
69
70
        if (is_callable($resource)) {
71
            return new PumpStream($resource, $options);
72
        }
73
74
        if ($resource instanceof \Iterator) {
75
            return new PumpStream(function () use ($resource) {
76
                if (!$resource->valid()) {
77
                    return false;
78
                }
79
                $result = $resource->current();
80
                $resource->next();
81
                return $result;
82
            }, $options);
83
        }
84
85
        throw new \InvalidArgumentException('Invalid resource type: ' . $type);
86
    }
87
88
    /**
89
     * This constructor accepts an associative array of options.
90
     *
91
     * - size: (int) If a read stream would otherwise have an indeterminate
92
     *   size, but the size is known due to foreknownledge, then you can
93
     *   provide that size, in bytes.
94
     * - metadata: (array) Any additional metadata to return when the metadata
95
     *   of the stream is accessed.
96
     *
97
     * @param resource $stream  Stream resource to wrap.
98
     * @param array    $options Associative array of options.
99
     *
100
     * @throws \InvalidArgumentException if the stream is not a stream resource
101
     */
102
    public function __construct($stream, $options = [])
103
    {
104
        if (!is_resource($stream)) {
105
            throw new \InvalidArgumentException('Stream must be a resource');
106
        }
107
108
        if (isset($options['size'])) {
109
            $this->size = $options['size'];
110
        }
111
112
        $this->customMetadata = isset($options['metadata'])
113
            ? $options['metadata']
114
            : [];
115
116
        $this->attach($stream);
117
    }
118
119
    /**
120
     * Closes the stream when the destructed
121
     */
122
    public function __destruct()
123
    {
124
        $this->close();
125
    }
126
127
    public function __toString()
128
    {
129
        if (!$this->stream) {
130
            return '';
131
        }
132
133
        $this->seek(0);
134
135
        return (string) stream_get_contents($this->stream);
136
    }
137
138
    public function getContents()
139
    {
140
        return $this->stream ? stream_get_contents($this->stream) : '';
141
    }
142
143
    public function close()
144
    {
145
        if (is_resource($this->stream)) {
146
            fclose($this->stream);
147
        }
148
149
        $this->detach();
150
    }
151
152
    public function detach()
153
    {
154
        $result = $this->stream;
155
        $this->stream = $this->size = $this->uri = null;
156
        $this->readable = $this->writable = $this->seekable = false;
157
158
        return $result;
159
    }
160
161
    public function attach($stream)
162
    {
163
        $this->stream = $stream;
164
        $meta = stream_get_meta_data($this->stream);
165
        $this->seekable = $meta['seekable'];
166
        $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
167
        $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
168
        $this->uri = $this->getMetadata('uri');
169
    }
170
171
    public function getSize()
172
    {
173
        if ($this->size !== null) {
174
            return $this->size;
175
        }
176
177
        if (!$this->stream) {
178
            return null;
179
        }
180
181
        // Clear the stat cache if the stream has a URI
182
        if ($this->uri) {
183
            clearstatcache(true, $this->uri);
184
        }
185
186
        $stats = fstat($this->stream);
187
        if (isset($stats['size'])) {
188
            $this->size = $stats['size'];
189
            return $this->size;
190
        }
191
192
        return null;
193
    }
194
195
    public function isReadable()
196
    {
197
        return $this->readable;
198
    }
199
200
    public function isWritable()
201
    {
202
        return $this->writable;
203
    }
204
205
    public function isSeekable()
206
    {
207
        return $this->seekable;
208
    }
209
210
    public function eof()
211
    {
212
        return !$this->stream || feof($this->stream);
213
    }
214
215
    public function tell()
216
    {
217
        return $this->stream ? ftell($this->stream) : false;
218
    }
219
220
    public function setSize($size)
221
    {
222
        $this->size = $size;
223
224
        return $this;
225
    }
226
227
    public function seek($offset, $whence = SEEK_SET)
228
    {
229
        return $this->seekable
230
            ? fseek($this->stream, $offset, $whence) === 0
231
            : false;
232
    }
233
234
    public function read($length)
235
    {
236
        return $this->readable ? fread($this->stream, $length) : false;
237
    }
238
239
    public function write($string)
240
    {
241
        // We can't know the size after writing anything
242
        $this->size = null;
243
244
        return $this->writable ? fwrite($this->stream, $string) : false;
245
    }
246
247
    public function getMetadata($key = null)
248
    {
249
        if (!$this->stream) {
250
            return $key ? null : [];
251
        } elseif (!$key) {
252
            return $this->customMetadata + stream_get_meta_data($this->stream);
253
        } elseif (isset($this->customMetadata[$key])) {
254
            return $this->customMetadata[$key];
255
        }
256
257
        $meta = stream_get_meta_data($this->stream);
258
259
        return isset($meta[$key]) ? $meta[$key] : null;
260
    }
261
}
262