Completed
Push — master ( 9d8248...78c4ab )
by James Ekow Abaka
01:49
created

StringStream::stream_tell()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

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
eloc 2
nc 1
nop 0
crap 1
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 4
    private function setFlags($read, $write, $position)
51
    {
52 4
        $this->read = $read;
53 4
        $this->write = $write;
54 4
        $this->position = $position;
55 4
    }
56
57
    /**
58
     * Open stream
59
     * @param string $aPath
60
     * @param string $aMode
61
     * @param int $aOptions
62
     * @param string $aOpenedPath
63
     * @return boolean
64
     */
65 4
    public function stream_open($aPath, $aMode, $aOptions, &$aOpenedPath)
66
    {
67 4
        $this->path = substr($aPath, 9);
68 4
        if (!isset(self::$string[$this->path])) {
69 4
            self::$string[$this->path] = '';
70
        }
71 4
        $this->options = $aOptions;
72 4
        $aOpenedPath = $this->path;
73 4
        $lenght = strlen(self::$string[$this->path]);
74
        $flags = [
75 4
            'r' => [true, false, 0],
76
            'rb' => [true, false, 0],
77
            'r+' => [true, true, 0],
78
            'c+' => [true, true, 0],
79
            'w' => [false, true, 0],
80
            'wb' => [false, true, 0],
81
            'w+' => [true, true, 0],
82 4
            'a' => [false, true, $lenght],
83 4
            'a+' => [true, true, $lenght],
84
            'c' => [false, true, 0]
85
        ];
86
87 4
        if (isset($flags[$aMode])) {
88 4
            $flag = $flags[$aMode];
89 4
            $this->setFlags($flag[0], $flag[1], $flag[2]);
90
        } else {
91
            throw new exceptions\StringStreamException("Unknown stream mode '{$aMode}'");
92
        }
93
94 4
        if ($aMode === 'w+') {
95
            $this->stream_truncate(0);
96
        }
97
98 4
        return true;
99
    }
100
101
    /**
102
     * Read from stream
103
     * @param int $aBytes number of bytes to return
104
     * @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...
105
     */
106 4
    public function stream_read($aBytes)
107
    {
108 4
        if ($this->read) {
109 4
            $read = substr(self::$string[$this->path], $this->position, $aBytes);
110 4
            $this->position += strlen($read);
111 4
            return $read;
112
        } else {
113
            return false;
114
        }
115
    }
116
117
    /**
118
     * Write to stream
119
     * @param string $aData data to write
120
     * @return int
121
     */
122 4
    public function stream_write($aData)
123
    {
124 4
        if ($this->write) {
125 4
            $left = substr(self::$string[$this->path], 0, $this->position);
126 4
            $right = substr(self::$string[$this->path], $this->position + strlen($aData));
127 4
            self::$string[$this->path] = $left . $aData . $right;
128 4
            $this->position += strlen($aData);
129 4
            return strlen($aData);
130
        } else {
131
            return 0;
132
        }
133
    }
134
135
    /**
136
     * Return current position
137
     * @return int
138
     */
139 3
    public function stream_tell()
140
    {
141 3
        return $this->position;
142
    }
143
144
    /**
145
     * Return if EOF
146
     * @return boolean
147
     */
148 4
    public function stream_eof()
149
    {
150 4
        return $this->position >= strlen(self::$string[$this->path]);
151
    }
152
153
    /**
154
     * Seek to new position
155
     * @param int $aOffset
156
     * @param int $aWhence
157
     * @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...
158
     */
159 3
    public function stream_seek($aOffset, $aWhence)
160
    {
161
        switch ($aWhence) {
162 3
            case SEEK_SET:
163 3
                $this->position = $aOffset;
164 3
                if ($aOffset > strlen(self::$string[$this->path])) {
165 1
                    $this->stream_truncate($aOffset);
166
                }
167 3
                return true;
168
                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...
169
170 1
            case SEEK_CUR:
171 1
                $this->position += $aOffset;
172 1
                return true;
173
                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...
174
175
            case SEEK_END:
176
                $this->position = strlen(self::$string[$this->path]) + $aOffset;
177
                if (($this->position + $aOffset) > strlen(self::$string[$this->path])) {
178
                    $this->stream_truncate(strlen(self::$string[$this->path]) + $aOffset);
179
                }
180
                return true;
181
                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...
182
183
            default:
184
                return false;
185
        }
186
    }
187
188
    /**
189
     * Truncate to given size
190
     * @param int $aSize
191
     */
192 1
    public function stream_truncate($aSize)
193
    {
194 1
        if (strlen(self::$string[$this->path]) > $aSize) {
195
            self::$string[$this->path] = substr(self::$string[$this->path], 0, $aSize);
196 1
        } else if (strlen(self::$string[$this->path]) < $aSize) {
197 1
            self::$string[$this->path] = str_pad(self::$string[$this->path], $aSize, "\0", STR_PAD_RIGHT);
198
        }
199 1
        return true;
200
    }
201
202
    /**
203
     * Return info about stream
204
     * @return array
205
     */
206
    public function stream_stat()
207
    {
208
        return array('dev' => 0,
209
            'ino' => 0,
210
            'mode' => 0,
211
            'nlink' => 0,
212
            'uid' => 0,
213
            'gid' => 0,
214
            'rdev' => 0,
215
            'size' => strlen(self::$string[$this->path]),
216
            'atime' => 0,
217
            'mtime' => 0,
218
            'ctime' => 0,
219
            'blksize' => -1,
220
            'blocks' => -1);
221
    }
222
223
    /**
224
     * Return info about stream
225
     * @param string $aPath
226
     * @param array $aOptions
227
     * @return array
228
     */
229
    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...
230
    {
231
        $resource = fopen($aPath, 'r');
232
        return fstat($resource);
233
    }
234
235 4
    public static function register($protocol = 'string')
236
    {
237 4
        if (!self::$registered) {
238 4
            if (in_array($protocol, stream_get_wrappers())) {
239
                throw new exceptions\StringStreamException("An existing wrapper already exists for '$protocol'");
240
            }
241 4
            stream_wrapper_register("string", __CLASS__);
242 4
            self::$registered = true;
243
        }
244 4
    }
245
246 4
    public static function unregister()
247
    {
248 4
        if (self::$registered) {
249 4
            stream_wrapper_unregister('string');
250 4
            self::$registered = false;
251 4
            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...
252
        }
253 4
    }
254
255
}
256