This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace nyx\connect\streams; |
||
2 | |||
3 | // External dependencies |
||
4 | use nyx\core; |
||
5 | |||
6 | /** |
||
7 | * Stream |
||
8 | * |
||
9 | * @package Nyx\Connect |
||
10 | * @version 0.1.0 |
||
11 | * @author Michal Chojnacki <[email protected]> |
||
12 | * @copyright 2012-2016 Nyx Dev Team |
||
13 | * @link http://docs.muyo.io/nyx/connect/index.html |
||
14 | */ |
||
15 | class Stream implements interfaces\Stream |
||
16 | { |
||
17 | /** |
||
18 | * @var array Hash table of readable and writable stream modes for faster look-ups. |
||
19 | */ |
||
20 | private static $rwh = [ |
||
21 | 'read' => [ |
||
22 | 'r' => true, 'w+' => true, 'r+' => true, 'rw' => true, 'x+' => true, |
||
23 | 'c+' => true, 'a+' => true, 'a+b' => true, 'rb' => true, 'w+b' => true, |
||
24 | 'r+b' => true, 'x+b' => true, 'c+b' => true, 'rt' => true, 'w+t' => true, |
||
25 | 'r+t' => true, 'x+t' => true, 'c+t' => true |
||
26 | ], |
||
27 | 'write' => [ |
||
28 | 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'c' => true, |
||
29 | 'c+' => true, 'cb' => true, 'x' => true, 'xb' => true, 'x+' => true, |
||
30 | 'x+b' => true, 'a' => true, 'a+' => true, 'a+b' => true, 'ab' => true, |
||
31 | 'wb' => true, 'w+b' => true, 'r+b' => true, 'c+b' => true, 'w+t' => true, |
||
32 | 'r+t' => true, 'x+t' => true, 'c+t' => true |
||
33 | ] |
||
34 | ]; |
||
35 | |||
36 | /** |
||
37 | * @var resource The underlying stream resource. |
||
38 | */ |
||
39 | private $resource; |
||
40 | |||
41 | /** |
||
42 | * @var resource The stream context resource in use. |
||
43 | */ |
||
44 | private $context; |
||
45 | |||
46 | /** |
||
47 | * @var array Cached data about the stream. |
||
48 | */ |
||
49 | private $metadata; |
||
50 | |||
51 | /** |
||
52 | * @var core\Mask Status/mode mask of the Stream. |
||
53 | */ |
||
54 | private $status; |
||
55 | |||
56 | /** |
||
57 | * @var int The size of the stream's contents in bytes. |
||
58 | */ |
||
59 | private $size; |
||
60 | |||
61 | /** |
||
62 | * Constructs a new Stream instance. |
||
63 | * |
||
64 | * @param string|resource $stream The URI of the stream resource that should be opened or an already |
||
65 | * created stream resource. |
||
66 | * @param string $mode The mode in which the stream should be opened. |
||
67 | * @param array $context Stream context options. Will be ignored if an already created stream |
||
68 | * is passed to the constructor. |
||
69 | * @param int $size The size of the stream in bytes. Should only be passed if it cannot be |
||
70 | * obtained by directly analyzing the stream. |
||
71 | * @throws \InvalidArgumentException When a resource was given but was not a valid stream resource or when |
||
72 | * it was to be created but no mode was given. |
||
73 | * @throws \RuntimeException When a stream was to be created but couldn't be opened. |
||
74 | */ |
||
75 | public function __construct($stream, string $mode = null, array $context = null, int $size = null) |
||
76 | { |
||
77 | // Are we dealing with an already existing stream? |
||
78 | if (is_resource($stream)) { |
||
79 | // Ensure the resource is a stream. |
||
80 | if (get_resource_type($stream) !== 'stream') { |
||
81 | throw new \InvalidArgumentException('A resource was given but it was not a valid stream.'); |
||
82 | } |
||
83 | |||
84 | $this->resource = $stream; |
||
85 | } |
||
86 | // Otherwise we should create our own. |
||
87 | else { |
||
88 | // We need a mode to work with. |
||
89 | if (null === $mode) { |
||
90 | throw new \InvalidArgumentException('A valid stream mode must be given to open a stream resource.'); |
||
91 | } |
||
92 | |||
93 | // Let's prepare a stream context if asked to. |
||
94 | if (null !== $context) { |
||
95 | $this->context = stream_context_create($context); |
||
96 | } |
||
97 | |||
98 | // Open it either with a specific context or leave the default one. |
||
99 | if (!$this->resource = $this->context ? fopen($stream, $mode, $this->context) : fopen($stream, $mode)) { |
||
100 | throw new \RuntimeException("Failed to open a stream [$stream, mode: $mode]."); |
||
101 | } |
||
102 | } |
||
103 | |||
104 | $this->size = $size; |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * {@inheritDoc} |
||
109 | */ |
||
110 | View Code Duplication | public function getContents() : string |
|
0 ignored issues
–
show
|
|||
111 | { |
||
112 | if (!$this->resource) { |
||
113 | throw new \RuntimeException('No stream resource available - cannot get contents.'); |
||
114 | } |
||
115 | |||
116 | if (!$this->is(interfaces\Stream::READABLE)) { |
||
117 | throw new \RuntimeException('Cannot get stream contents - the stream is not readable.'); |
||
118 | } |
||
119 | |||
120 | // As long as we've got a resource, we can try reading. If it fails, we can diagnose afterwards. |
||
121 | if (!false === $data = stream_get_contents($this->resource)) { |
||
122 | throw new \RuntimeException('Failed to read stream contents.'); |
||
123 | } |
||
124 | |||
125 | return $data; |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * {@inheritdoc} |
||
130 | */ |
||
131 | View Code Duplication | public function read($length) : string |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
132 | { |
||
133 | if (!$this->resource) { |
||
134 | throw new \RuntimeException('No stream resource available - cannot read.'); |
||
135 | } |
||
136 | |||
137 | if (!$this->is(interfaces\Stream::READABLE)) { |
||
138 | throw new \RuntimeException('Cannot read from non-readable stream.'); |
||
139 | } |
||
140 | |||
141 | if (false === $data = fread($this->resource, $length)) { |
||
142 | throw new \RuntimeException('Failed to read from the stream.'); |
||
143 | } |
||
144 | |||
145 | return $data; |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * {@inheritdoc} |
||
150 | */ |
||
151 | View Code Duplication | public function line(int $length = null) : string |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
152 | { |
||
153 | if (!$this->resource) { |
||
154 | throw new \RuntimeException('No stream resource available - cannot read.'); |
||
155 | } |
||
156 | |||
157 | if (!$this->is(interfaces\Stream::READABLE)) { |
||
158 | throw new \RuntimeException('Cannot read from non-readable stream.'); |
||
159 | } |
||
160 | |||
161 | if (false === $data = fgets($this->resource, $length)) { |
||
162 | throw new \RuntimeException('Failed to read from the stream.'); |
||
163 | } |
||
164 | |||
165 | return $data; |
||
166 | } |
||
167 | |||
168 | |||
169 | /** |
||
170 | * {@inheritdoc} |
||
171 | */ |
||
172 | View Code Duplication | public function write($string) : int |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
173 | { |
||
174 | if (!$this->resource) { |
||
175 | throw new \RuntimeException('No stream resource available - cannot write.'); |
||
176 | } |
||
177 | |||
178 | if (!$this->is(interfaces\Stream::WRITABLE)) { |
||
179 | throw new \RuntimeException('Cannot write to non-writable stream.'); |
||
180 | } |
||
181 | if (false === $result = fwrite($this->resource, $string)) { |
||
182 | throw new \RuntimeException('Failed to write to the stream.'); |
||
183 | } |
||
184 | |||
185 | // The size has changed so make sure we get a fresh value for the next getSize() call. |
||
186 | $this->size = null; |
||
187 | |||
188 | return $result; |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * {@inheritdoc} |
||
193 | */ |
||
194 | public function isReadable() : bool |
||
195 | { |
||
196 | if (!$this->resource) { |
||
197 | return false; |
||
198 | } |
||
199 | |||
200 | return $this->is(interfaces\Stream::READABLE); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * {@inheritdoc} |
||
205 | */ |
||
206 | public function isWritable() : bool |
||
207 | { |
||
208 | if (!$this->resource) { |
||
209 | return false; |
||
210 | } |
||
211 | |||
212 | return $this->is(interfaces\Stream::WRITABLE); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * {@inheritdoc} |
||
217 | */ |
||
218 | public function isSeekable() : bool |
||
219 | { |
||
220 | if (!$this->resource) { |
||
221 | return false; |
||
222 | } |
||
223 | |||
224 | return $this->is(interfaces\Stream::SEEKABLE); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * {@inheritDoc} |
||
229 | */ |
||
230 | public function is($status) : bool |
||
231 | { |
||
232 | if (!$this->resource) { |
||
233 | return false; |
||
234 | } |
||
235 | |||
236 | if (empty($this->metadata) || $this->status) { |
||
237 | $this->refresh(); |
||
238 | } |
||
239 | |||
240 | return $this->status->is($status); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * {@inheritdoc} |
||
245 | */ |
||
246 | public function tell() |
||
247 | { |
||
248 | if (!$this->resource) { |
||
249 | throw new \RuntimeException('No stream resource available - cannot tell position.'); |
||
250 | } |
||
251 | |||
252 | if (false === $result = ftell($this->resource)) { |
||
253 | throw new \RuntimeException('Unable to determine stream position.'); |
||
254 | } |
||
255 | |||
256 | return $result; |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * {@inheritdoc} |
||
261 | */ |
||
262 | public function eof() |
||
263 | { |
||
264 | return !$this->resource || feof($this->resource); |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * {@inheritdoc} |
||
269 | */ |
||
270 | public function seek($offset, $whence = SEEK_SET) |
||
271 | { |
||
272 | if (!$this->resource) { |
||
273 | throw new \RuntimeException('No stream resource available - cannot seek.'); |
||
274 | } |
||
275 | |||
276 | if (!$this->is(interfaces\Stream::SEEKABLE)) { |
||
277 | throw new \RuntimeException('The stream is not seekable.'); |
||
278 | } |
||
279 | |||
280 | // As long as we've got a resource, we can try seeking. If it fails, we can diagnose afterwards. |
||
281 | if (-1 === fseek($this->resource, $offset, $whence)) { |
||
282 | throw new \RuntimeException("Failed to seek to stream position [$offset] with whence [".var_export($whence, true)."]."); |
||
283 | } |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * {@inheritdoc} |
||
288 | */ |
||
289 | public function rewind() |
||
290 | { |
||
291 | $this->seek(0); |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * {@inheritdoc} |
||
296 | */ |
||
297 | public function getSize() |
||
298 | { |
||
299 | if (!$this->resource) { |
||
300 | return null; |
||
301 | } |
||
302 | |||
303 | if ($this->size !== null) { |
||
304 | return $this->size; |
||
305 | } |
||
306 | |||
307 | // We're gonna clear the cache for local streams with an URI to ensure we get |
||
308 | // an updated size each time, in case we or something else is currently writing |
||
309 | // to that resource. |
||
310 | if ($this->is(interfaces\Stream::LOCAL) && $uri = $this->getMetadata('uri')) { |
||
311 | clearstatcache(true, $uri); |
||
312 | } |
||
313 | |||
314 | $stats = fstat($this->resource); |
||
315 | |||
316 | if (isset($stats['size'])) { |
||
317 | return $this->size = $stats['size']; |
||
318 | } |
||
319 | |||
320 | return null; |
||
321 | } |
||
322 | |||
323 | /** |
||
324 | * {@inheritdoc} |
||
325 | */ |
||
326 | public function getMetadata($key = null) |
||
327 | { |
||
328 | // If we've got no underlying stream (anymore?), we are going to return null for each requested |
||
329 | // key and empty arrays if the whole metadata array was requested. |
||
330 | if (!$this->resource) { |
||
331 | return $key ? null : []; |
||
332 | } |
||
333 | |||
334 | // Certain data doesn't change in a given stream, so we might just as well return cached values |
||
335 | // if we've got them. |
||
336 | if (null !== $key && !empty($this->metadata)) { |
||
337 | switch ($key) { |
||
338 | case 'mode': |
||
339 | case 'stream_type': |
||
340 | case 'wrapper_type': |
||
341 | case 'wrapper_data': |
||
342 | case 'uri': |
||
343 | return $this->metadata[$key]; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | // Grab the metadata and cache it. |
||
348 | $this->metadata = stream_get_meta_data($this->resource); |
||
349 | |||
350 | if (null === $key) { |
||
351 | return $this->metadata; |
||
352 | } |
||
353 | |||
354 | if (!array_key_exists($key, $this->metadata)) { |
||
355 | return null; |
||
356 | } |
||
357 | |||
358 | return $this->metadata[$key]; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * {@inheritdoc} |
||
363 | */ |
||
364 | public function close() |
||
365 | { |
||
366 | if (!$this->resource || !is_resource($this->resource)) { |
||
367 | return; |
||
368 | } |
||
369 | |||
370 | // Close first, detach afterwards. |
||
371 | fclose($this->resource); |
||
372 | |||
373 | $this->detach(); |
||
374 | } |
||
375 | |||
376 | /** |
||
377 | * {@inheritdoc} |
||
378 | */ |
||
379 | public function detach() |
||
380 | { |
||
381 | $resource = $this->resource; |
||
382 | |||
383 | $this->resource = null; |
||
384 | $this->context = null; |
||
385 | $this->metadata = []; |
||
386 | $this->status = null; |
||
387 | $this->size = null; |
||
388 | |||
389 | return $resource; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * {@inheritDoc} |
||
394 | */ |
||
395 | public function toString() : string |
||
396 | { |
||
397 | // Trying to get the position first - we'll seek back to it after we're done. |
||
398 | // This will also take care of all necessary checks. |
||
399 | $position = $this->tell(); |
||
400 | |||
401 | // Grab the contents into memory. This might potentially use up a lot of memory |
||
402 | // so be advised about the pitfalls. |
||
403 | $body = stream_get_contents($this->resource, -1, 0); |
||
404 | |||
405 | // Seek back to where we were. |
||
406 | $this->seek($position); |
||
407 | |||
408 | return (string) $body; |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * {@inheritDoc} |
||
413 | */ |
||
414 | public function __toString() |
||
415 | { |
||
416 | try { |
||
417 | return $this->toString(); |
||
418 | } catch (\Exception $e) { |
||
419 | return ''; |
||
420 | } |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Ensure the stream resource gets closed when the Stream instance gets destructed. |
||
425 | */ |
||
426 | public function __destruct() |
||
427 | { |
||
428 | $this->close(); |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * Refreshes the status mask based on the current metadata of the stream. |
||
433 | * |
||
434 | * @return bool True if we successfully refreshed all relevant data, false otherwise. |
||
435 | */ |
||
436 | protected function refresh() : bool |
||
437 | { |
||
438 | // Without a resource to grab the metadata for, let invokers know there's no data |
||
439 | // to work with presently. |
||
440 | if (!$this->resource) { |
||
441 | return false; |
||
442 | } |
||
443 | |||
444 | // Prepare the status mask if necessary. Might as well give it a value to begin with. |
||
445 | if (!$this->status) { |
||
446 | $this->status = new core\Mask(stream_is_local($this->resource) ? interfaces\Stream::LOCAL : 0); |
||
447 | } |
||
448 | |||
449 | // The call results of metadata() are cached so we can just use the class property. |
||
450 | $this->getMetadata(); |
||
451 | |||
452 | View Code Duplication | if (isset(self::$rwh['read'][$this->metadata['mode']])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
453 | $this->status->set(interfaces\Stream::READABLE); |
||
454 | } |
||
455 | |||
456 | View Code Duplication | if (isset(self::$rwh['write'][$this->metadata['mode']])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
457 | $this->status->set(interfaces\Stream::WRITABLE); |
||
458 | } |
||
459 | |||
460 | // Those may change, so... besides - fancy syntax, eh chaps? |
||
461 | $this->status->{((isset($this->metadata['seekable']) && $this->metadata['seekable']) ? 'set' : 'remove')}(interfaces\Stream::SEEKABLE); |
||
462 | $this->status->{((isset($this->metadata['blocked']) && $this->metadata['blocked']) ? 'set' : 'remove')}(interfaces\Stream::BLOCKED); |
||
463 | |||
464 | return true; |
||
465 | } |
||
466 | } |
||
467 |
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.