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