Completed
Push — dev ( 804f9e...e7cc03 )
by James Ekow Abaka
01:19
created

StringStream   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 92.13%

Importance

Changes 0
Metric Value
wmc 27
c 0
b 0
f 0
lcom 1
cbo 1
dl 0
loc 248
ccs 82
cts 89
cp 0.9213
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A setFlags() 0 6 1
B stream_open() 0 35 4
A stream_read() 0 10 2
A stream_write() 0 12 2
A stream_tell() 0 4 1
A stream_eof() 0 4 1
B stream_seek() 0 28 6
A stream_truncate() 0 9 3
A stream_stat() 0 16 1
A url_stat() 0 5 1
A register() 0 10 3
A unregister() 0 8 2
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 string
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 7
    private function setFlags($read, $write, $position)
51
    {
52 7
        $this->read = $read;
53 7
        $this->write = $write;
54 7
        $this->position = $position;
55 7
    }
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 8
    public function stream_open($aPath, $aMode, $aOptions, &$aOpenedPath)
68
    {
69 8
        $this->path = substr($aPath, 9);
70 8
        if (!isset(self::$string[$this->path])) {
71 8
            self::$string[$this->path] = '';
72
        }
73 8
        $this->options = $aOptions;
74 8
        $aOpenedPath = $this->path;
75 8
        $lenght = strlen(self::$string[$this->path]);
76
        $flags = [
77 8
            '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 8
            'a' => [false, true, $lenght],
85 8
            'a+' => [true, true, $lenght],
86
            'c' => [false, true, 0]
87
        ];
88
89 8
        if (isset($flags[$aMode])) {
90 7
            $flag = $flags[$aMode];
91 7
            $this->setFlags($flag[0], $flag[1], $flag[2]);
92
        } else {
93 1
            throw new exceptions\StringStreamException("Unknown stream mode '{$aMode}'");
94
        }
95
96 7
        if ($aMode === 'w+') {
97 1
            $this->stream_truncate(0);
98
        }
99
100 7
        return true;
101
    }
102
103
    /**
104
     * Read from stream
105
     * @param int $aBytes number of bytes to return
106
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
107
     */
108 5
    public function stream_read($aBytes)
109
    {
110 5
        if ($this->read) {
111 4
            $read = substr(self::$string[$this->path], $this->position, $aBytes);
112 4
            $this->position += strlen($read);
113 4
            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
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
123
     */
124 7
    public function stream_write($aData)
125
    {
126 7
        if ($this->write) {
127 7
            $left = substr(self::$string[$this->path], 0, $this->position);
128 7
            $right = substr(self::$string[$this->path], $this->position + strlen($aData));
129 7
            self::$string[$this->path] = $left . $aData . $right;
130 7
            $this->position += strlen($aData);
131 7
            return strlen($aData);
132
        } else {
133 1
            return false;
134
        }
135
    }
136
137
    /**
138
     * Return current position
139
     * @return int
140
     */
141 4
    public function stream_tell()
142
    {
143 4
        return $this->position;
144
    }
145
146
    /**
147
     * Return if EOF
148
     * @return boolean
149
     */
150 5
    public function stream_eof()
151
    {
152 5
        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
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
160
     */
161 4
    public function stream_seek($aOffset, $aWhence)
162
    {
163
        switch ($aWhence) {
164 4
            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
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
171
172 2
            case SEEK_CUR:
173 2
                $this->position += $aOffset;
174 2
                return true;
175
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
176
177
            case SEEK_END:
178
                $this->position = strlen(self::$string[$this->path]) + $aOffset;
179
                if (($this->position + $aOffset) > strlen(self::$string[$this->path])) {
180
                    $this->stream_truncate(strlen(self::$string[$this->path]) + $aOffset);
181
                }
182
                return true;
183
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
184
185
            default:
186
                return false;
187
        }
188
    }
189
190
    /**
191
     * Truncate to given size
192
     * @param int $aSize
193
     * @return bool
194
     */
195 2
    public function stream_truncate($aSize)
196
    {
197 2
        if (strlen(self::$string[$this->path]) > $aSize) {
198 1
            self::$string[$this->path] = substr(self::$string[$this->path], 0, $aSize);
199 1
        } else if (strlen(self::$string[$this->path]) < $aSize) {
200 1
            self::$string[$this->path] = str_pad(self::$string[$this->path], $aSize, "\0", STR_PAD_RIGHT);
201
        }
202 2
        return true;
203
    }
204
205
    /**
206
     * Return info about stream
207
     * @return array
208
     */
209 1
    public function stream_stat()
210
    {
211 1
        return array('dev' => 0,
212 1
            'ino' => 0,
213 1
            'mode' => 0,
214 1
            'nlink' => 0,
215 1
            'uid' => 0,
216 1
            'gid' => 0,
217 1
            'rdev' => 0,
218 1
            'size' => strlen(self::$string[$this->path]),
219 1
            'atime' => 0,
220 1
            'mtime' => 0,
221 1
            'ctime' => 0,
222
            'blksize' => -1,
223
            'blocks' => -1);
224
    }
225
226
    /**
227
     * Return info about stream
228
     * @param string $aPath
229
     * @param array $aOptions
230
     * @return array
231
     */
232 1
    public function url_stat($aPath, $aOptions)
0 ignored issues
show
Unused Code introduced by
The parameter $aOptions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
233
    {
234 1
        $resource = fopen($aPath, 'r');
235 1
        return fstat($resource);
236
    }
237
238
    /**
239
     * @param string $protocol
240
     * @throws exceptions\StringStreamException
241
     */
242 9
    public static function register($protocol = 'string')
243
    {
244 9
        if (!self::$registered) {
245 9
            if (in_array($protocol, stream_get_wrappers())) {
246
                throw new exceptions\StringStreamException("An existing wrapper already exists for '$protocol'");
247
            }
248 9
            stream_wrapper_register("string", __CLASS__);
249 9
            self::$registered = true;
250
        }
251 9
    }
252
253 9
    public static function unregister()
254
    {
255 9
        if (self::$registered) {
256 9
            stream_wrapper_unregister('string');
257 9
            self::$registered = false;
258 9
            self::$string = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $string.

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...
259
        }
260 9
    }
261
262
}
263