Stream::isWritable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Aidphp\Http;
6
7
use Psr\Http\Message\StreamInterface;
8
use InvalidArgumentException;
9
use RuntimeException;
10
11
class Stream implements StreamInterface
12
{
13
    private $stream;
14
    private $seekable;
15
    private $readable;
16
    private $writable;
17
    private $uri;
18
    private $size;
19
20
    private static $readWriteHash = [
21
        'read' => [
22
            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
23
            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
24
            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
25
            'x+t' => true, 'c+t' => true, 'a+' => true,
26
        ],
27
        'write' => [
28
            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
29
            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
30
            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
31
            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
32
        ],
33
    ];
34
35 83
    public function __construct($resource)
36
    {
37 83
        if (! is_resource($resource))
38
        {
39 1
            throw new InvalidArgumentException('Stream must be a resource');
40
        }
41
42 82
        $this->stream = $resource;
43
44 82
        $meta = $this->getMetadata();
45 82
        $this->seekable = $meta['seekable'];
46 82
        $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
47 82
        $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
48 82
        $this->uri = $meta['uri'] ?? null;
49 82
    }
50
51 4
    public function __toString(): string
52
    {
53
        try
54
        {
55 4
            if ($this->seekable)
56
            {
57 3
                $this->rewind();
58
            }
59
60 4
            return $this->getContents();
61
        }
62 1
        catch (RuntimeException $e)
63
        {
64 1
            return '';
65
        }
66
    }
67
68 17
    public function close(): void
69
    {
70 17
        if (null !== $this->stream)
71
        {
72 17
            fclose($this->stream);
73 17
            $this->detach();
74
        }
75 17
    }
76
77 17
    public function detach()
78
    {
79 17
        $result = $this->stream;
80
81 17
        $this->stream =
82 17
        $this->size   =
83 17
        $this->uri    = null;
84
85 17
        $this->readable =
86 17
        $this->writable =
87 17
        $this->seekable = false;
88
89 17
        return $result;
90
    }
91
92 6
    public function getSize(): ?int
93
    {
94 6
        if (null === $this->size && $this->stream)
95
        {
96 5
            if ($this->uri)
97
            {
98 5
                clearstatcache(true, $this->uri);
99
            }
100
101 5
            $stats = fstat($this->stream);
102
103 5
            if (isset($stats['size']))
104
            {
105 5
                $this->size = $stats['size'];
106
            }
107
        }
108
109 6
        return $this->size;
110
    }
111
112 2
    public function tell(): int
113
    {
114 2
        if (null === $this->stream || false === ($result = ftell($this->stream)))
115
        {
116 1
            throw new RuntimeException('Unable to determine stream position');
117
        }
118
119 1
        return $result;
120
    }
121
122 4
    public function eof(): bool
123
    {
124 4
        return null === $this->stream || feof($this->stream);
125
    }
126
127 4
    public function isSeekable(): bool
128
    {
129 4
        return $this->seekable;
130
    }
131
132 7
    public function seek($offset, $whence = SEEK_SET): void
133
    {
134 7
        if (false === $this->seekable || -1 === fseek($this->stream, $offset, $whence))
135
        {
136 1
            throw new RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . var_export($whence, true));
137
        }
138 6
    }
139
140 4
    public function rewind(): void
141
    {
142 4
        $this->seek(0);
143 4
    }
144
145 2
    public function isWritable(): bool
146
    {
147 2
        return $this->writable;
148
    }
149
150 5
    public function write($string): int
151
    {
152 5
        $this->size = null;
153
154 5
        if (false === $this->writable || false === ($result = fwrite($this->stream, $string)))
155
        {
156 1
            throw new RuntimeException('Unable to write to stream');
157
        }
158
159 4
        return $result;
160
    }
161
162 2
    public function isReadable(): bool
163
    {
164 2
        return $this->readable;
165
    }
166
167 4
    public function read($length): string
168
    {
169 4
        if (false === $this->readable)
170
        {
171 1
            throw new RuntimeException('Cannot read from non-readable stream');
172
        }
173
174 3
        return fread($this->stream, $length);
175
    }
176
177 6
    public function getContents(): string
178
    {
179 6
        if (false === $this->readable || false === ($result = stream_get_contents($this->stream)))
180
        {
181 2
            throw new RuntimeException('Unable to get stream contents');
182
        }
183
184 4
        return $result;
185
    }
186
187 82
    public function getMetadata($key = null)
188
    {
189 82
        if (null === $this->stream)
190
        {
191 1
            return $key ? null : [];
192
        }
193
194 82
        $meta = stream_get_meta_data($this->stream);
195
196 82
        return null === $key ? $meta : (isset($meta[$key]) ? $meta[$key] : null);
197
    }
198
}