Passed
Push — main ( d33ea0...558cf2 )
by James Ekow Abaka
09:33
created

StringStream::stream_seek()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 16
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 23
ccs 14
cts 15
cp 0.9333
crap 6.0106
rs 9.1111
1
<?php
2
3
/*
4
 * Adapted for use in ntentan\utils from dvdoug\stringstream package.
5
 */
6
7
namespace ntentan\utils;
8
9
/**
10
 * Stream wrapper for strings which allows you to read strings as though they
11
 * were I/O streams.
12
 * @author Doug Wright, James Ainooson
13
 * @package StringStream
14
 */
15
class StringStream
16
{
17
18
    /**
19
     * Content of stream
20
     * @var array
21
     */
22
    private static $string = [];
23
24
    /**
25
     * Whether this stream can be read
26
     * @var boolean
27
     */
28
    private $read;
29
30
    /**
31
     * Whether this stream can be written
32
     * @var boolean
33
     */
34
    private $write;
35
36
    /**
37
     * Options
38
     * @var int
39
     */
40
    private $options;
41
42
    /**
43
     * Current position within stream
44
     * @var int
45
     */
46
    private $position;
47
    private $path;
48
    private static $registered = false;
49
50 9
    private function setFlags($read, $write, $position)
51
    {
52 9
        $this->read = $read;
53 9
        $this->write = $write;
54 9
        $this->position = $position;
55 9
    }
56
57
    /**
58
     * Open a stream
59
     *
60
     * @param string $aPath
61
     * @param string $aMode
62
     * @param int $aOptions
63
     * @param string $aOpenedPath
64
     * @return boolean
65
     * @throws exceptions\StringStreamException
66
     */
67 10
    public function stream_open($aPath, $aMode, $aOptions, &$aOpenedPath)
68
    {
69 10
        $this->path = substr($aPath, 9);
70 10
        if (!isset(self::$string[$this->path])) {
71 10
            self::$string[$this->path] = '';
72
        }
73 10
        $this->options = $aOptions;
74 10
        $aOpenedPath = $this->path;
75 10
        $lenght = strlen(self::$string[$this->path]);
76 10
        $flags = [
77 10
            'r' => [true, false, 0],
78
            'rb' => [true, false, 0],
79
            'r+' => [true, true, 0],
80
            'c+' => [true, true, 0],
81
            'w' => [false, true, 0],
82
            'wb' => [false, true, 0],
83
            'w+' => [true, true, 0],
84 10
            'a' => [false, true, $lenght],
85 10
            'a+' => [true, true, $lenght],
86
            'c' => [false, true, 0]
87
        ];
88
89 10
        if (isset($flags[$aMode])) {
90 9
            $flag = $flags[$aMode];
91 9
            $this->setFlags($flag[0], $flag[1], $flag[2]);
92
        } else {
93 1
            throw new exceptions\StringStreamException("Unknown stream mode '{$aMode}'");
94
        }
95
96 9
        if ($aMode === 'w+') {
97 1
            $this->stream_truncate(0);
98
        }
99
100 9
        return true;
101
    }
102
103
    /**
104
     * Read from stream
105
     * @param int $aBytes number of bytes to return
106
     * @return string|bool
107
     */
108 7
    public function stream_read($aBytes)
109
    {
110 7
        if ($this->read) {
111 6
            $read = substr(self::$string[$this->path], $this->position, $aBytes);
112 6
            $this->position += strlen($read);
113 6
            return $read;
114
        } else {
115 1
            return false;
116
        }
117
    }
118
119
    /**
120
     * Write to stream
121
     * @param string $aData data to write
122
     * @return int|bool
123
     */
124 9
    public function stream_write($aData)
125
    {
126 9
        if ($this->write) {
127 9
            $left = substr(self::$string[$this->path], 0, $this->position);
128 9
            $right = substr(self::$string[$this->path], $this->position + strlen($aData));
129 9
            self::$string[$this->path] = $left . $aData . $right;
130 9
            $this->position += strlen($aData);
131 9
            return strlen($aData);
132
        } else {
133 1
            return false;
134
        }
135
    }
136
137
    /**
138
     * Return current position
139
     * @return int
140
     */
141 6
    public function stream_tell()
142
    {
143 6
        return $this->position;
144
    }
145
146
    /**
147
     * Return if EOF
148
     * @return boolean
149
     */
150 6
    public function stream_eof()
151
    {
152 6
        return $this->position >= strlen(self::$string[$this->path]);
153
    }
154
155
    /**
156
     * Seek to new position
157
     * @param int $aOffset
158
     * @param int $aWhence
159
     * @return boolean
160
     */
161 6
    public function stream_seek($aOffset, $aWhence)
162
    {
163
        switch ($aWhence) {
164 6
            case SEEK_SET:
165 3
                $this->position = $aOffset;
166 3
                if ($aOffset > strlen(self::$string[$this->path])) {
167 1
                    $this->stream_truncate($aOffset);
168
                }
169 3
                return true;
170
171 4
            case SEEK_CUR:
172 4
                $this->position += $aOffset;
173 4
                return true;
174
175 2
            case SEEK_END:
176 2
                $this->position = strlen(self::$string[$this->path]) + $aOffset;
177 2
                if (($this->position + $aOffset) > strlen(self::$string[$this->path])) {
178 1
                    $this->stream_truncate(strlen(self::$string[$this->path]) + $aOffset);
179
                }
180 2
                return true;
181
182
            default:
183
                return false;
184
        }
185
    }
186
187
    /**
188
     * Truncate to given size
189
     * @param int $aSize
190
     * @return bool
191
     */
192 3
    public function stream_truncate($aSize)
193
    {
194 3
        if (strlen(self::$string[$this->path]) > $aSize) {
195 1
            self::$string[$this->path] = substr(self::$string[$this->path], 0, $aSize);
196 2
        } else if (strlen(self::$string[$this->path]) < $aSize) {
197 2
            self::$string[$this->path] = str_pad(self::$string[$this->path], $aSize, "\0", STR_PAD_RIGHT);
198
        }
199 3
        return true;
200
    }
201
202
    /**
203
     * Return info about stream
204
     * @return array
205
     */
206 1
    public function stream_stat()
207
    {
208 1
        return array('dev' => 0,
209 1
            'ino' => 0,
210 1
            'mode' => 0,
211 1
            'nlink' => 0,
212 1
            'uid' => 0,
213 1
            'gid' => 0,
214 1
            'rdev' => 0,
215 1
            'size' => strlen(self::$string[$this->path]),
216 1
            'atime' => 0,
217 1
            'mtime' => 0,
218 1
            'ctime' => 0,
219
            'blksize' => -1,
220
            'blocks' => -1);
221
    }
222
223
    /**
224
     * Return info about stream
225
     * @param string $aPath
226
     * @return array
227
     */
228 1
    public function url_stat($aPath)
229
    {
230 1
        $resource = fopen($aPath, 'r');
231 1
        return fstat($resource);
232
    }
233
234
    /**
235
     * @param string $protocol
236
     * @throws exceptions\StringStreamException
237
     */
238 10
    public static function register($protocol = 'string')
239
    {
240 10
        if (!self::$registered) {
241 10
            if (in_array($protocol, stream_get_wrappers())) {
242
                throw new exceptions\StringStreamException("An existing wrapper already exists for '$protocol'");
243
            }
244 10
            stream_wrapper_register("string", __CLASS__);
245 10
            self::$registered = true;
246
        }
247 10
    }
248
249 10
    public static function unregister()
250
    {
251 10
        if (self::$registered) {
252 10
            stream_wrapper_unregister('string');
253 10
            self::$registered = false;
254 10
            self::$string = [];
255
        }
256 10
    }
257
258
}
259