• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

valkyrjaio / valkyrja / 16185661246

10 Jul 2025 03:48AM UTC coverage: 43.558% (-0.2%) from 43.747%
16185661246

push

github

MelechMizrachi
Http Routing: Updating ListCommand.

0 of 21 new or added lines in 1 file covered. (0.0%)

2913 existing lines in 212 files now uncovered.

3925 of 9011 relevant lines covered (43.56%)

11.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.5
/src/Valkyrja/Http/Message/Stream/Stream.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <melechmizrachi@gmail.com>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace Valkyrja\Http\Message\Stream;
15

16
use Override;
17
use Throwable;
18
use Valkyrja\Http\Message\Stream\Contract\Stream as Contract;
19
use Valkyrja\Http\Message\Stream\Enum\Mode;
20
use Valkyrja\Http\Message\Stream\Enum\ModeTranslation;
21
use Valkyrja\Http\Message\Stream\Enum\PhpWrapper;
22
use Valkyrja\Http\Message\Stream\Exception\InvalidLengthException;
23
use Valkyrja\Http\Message\Stream\Exception\InvalidStreamException;
24

25
use function fclose;
26
use function feof;
27
use function fread;
28
use function fseek;
29
use function fstat;
30
use function ftell;
31
use function fwrite;
32
use function stream_get_contents;
33
use function stream_get_meta_data;
34

35
use const SEEK_SET;
36

37
/**
38
 * Class Stream.
39
 *
40
 * @author Melech Mizrachi
41
 */
42
class Stream implements Contract
43
{
44
    use StreamHelpers;
45

46
    /**
47
     * StreamImpl constructor.
48
     *
49
     * @param PhpWrapper|string $stream          The stream
50
     * @param Mode              $mode            [optional] The mode
51
     * @param ModeTranslation   $modeTranslation [optional] The mode translation
52
     *
53
     * @throws InvalidStreamException
54
     */
55
    public function __construct(
56
        protected PhpWrapper|string $stream = PhpWrapper::temp,
57
        protected Mode $mode = Mode::WRITE_READ,
58
        protected ModeTranslation $modeTranslation = ModeTranslation::BINARY_SAFE
59
    ) {
60
        $this->setStream($stream, $mode, $modeTranslation);
412✔
61
    }
62

63
    /**
64
     * @inheritDoc
65
     */
66
    #[Override]
67
    public function isSeekable(): bool
68
    {
69
        // If there is no stream
70
        if ($this->isInvalidStream()) {
124✔
71
            // Don't do anything
72
            return false;
8✔
73
        }
74

75
        return (bool) $this->getMetadata('seekable');
122✔
76
    }
77

78
    /**
79
     * @inheritDoc
80
     */
81
    #[Override]
82
    public function seek(int $offset, int $whence = SEEK_SET): void
83
    {
84
        $this->verifyStream();
118✔
85
        $this->verifySeekable();
118✔
86

87
        /** @var resource $stream */
88
        $stream = $this->resource;
116✔
89

90
        // Get the results of the seek attempt
91
        $result = $this->seekStream($stream, $offset, $whence);
116✔
92

93
        $this->verifySeekResult($result);
116✔
94
    }
95

96
    /**
97
     * @inheritDoc
98
     */
99
    #[Override]
100
    public function rewind(): void
101
    {
102
        $this->seek(0);
112✔
103
    }
104

105
    /**
106
     * @inheritDoc
107
     */
108
    #[Override]
109
    public function isReadable(): bool
110
    {
111
        // If there is no stream
112
        if ($this->isInvalidStream()) {
104✔
113
            // It's not readable
114
            return false;
10✔
115
        }
116

117
        // Get the stream's mode
118
        /** @var string|null $mode */
119
        $mode = $this->getMetadata('mode');
102✔
120

121
        return $this->isModeReadable((string) $mode);
102✔
122
    }
123

124
    /**
125
     * @inheritDoc
126
     */
127
    #[Override]
128
    public function read(int $length): string
129
    {
130
        if ($length < 0) {
20✔
UNCOV
131
            throw new InvalidLengthException("Invalid length of $length provided. Length must be greater than 0");
×
132
        }
133

134
        /** @var int<1, max> $length */
135
        $this->verifyStream();
20✔
136
        $this->verifyReadable();
18✔
137

138
        /** @var resource $stream */
139
        $stream = $this->resource;
16✔
140

141
        // Read the stream
142
        $result = $this->readFromStream($stream, $length);
16✔
143

144
        $this->verifyReadResult($result);
16✔
145

146
        /** @var string $result */
147

148
        return $result;
14✔
149
    }
150

151
    /**
152
     * @inheritDoc
153
     */
154
    #[Override]
155
    public function isWritable(): bool
156
    {
157
        // If there is no stream
158
        if ($this->isInvalidStream()) {
138✔
159
            // The stream is definitely not writable
160
            return false;
6✔
161
        }
162

163
        // Get the stream's mode
164
        /** @var string|null $mode */
165
        $mode = $this->getMetadata('mode');
138✔
166

167
        return $this->isModeWriteable((string) $mode);
138✔
168
    }
169

170
    /**
171
     * @inheritDoc
172
     */
173
    #[Override]
174
    public function write(string $string): int
175
    {
176
        $this->verifyStream();
138✔
177
        $this->verifyWritable();
136✔
178

179
        /** @var resource $stream */
180
        $stream = $this->resource;
134✔
181

182
        // Attempt to write to the stream
183
        $result = $this->writeToStream($stream, $string);
134✔
184

185
        $this->verifyWriteResult($result);
134✔
186

187
        /** @var int $result */
188

189
        return $result;
132✔
190
    }
191

192
    /**
193
     * @inheritDoc
194
     */
195
    public function __toString(): string
196
    {
197
        // If the stream is not readable
198
        if (! $this->isReadable()) {
18✔
199
            // Return an empty string
200
            return '';
2✔
201
        }
202

203
        try {
204
            // Rewind the stream to the start
205
            $this->rewind();
18✔
206

207
            // Get the stream's contents
208
            return $this->getContents();
18✔
209
        } // On a runtime exception
210
        catch (Throwable) {
2✔
211
            // Return a string
212
            return '';
2✔
213
        }
214
    }
215

216
    /**
217
     * @inheritDoc
218
     */
219
    #[Override]
220
    public function close(): void
221
    {
222
        // If there is no stream
223
        if ($this->isInvalidStream()) {
16✔
224
            // Don't do anything
225
            return;
2✔
226
        }
227

228
        // Detach the stream
229
        /** @var resource $resource */
230
        $resource = $this->detach();
14✔
231

232
        // Close the stream
233
        $this->closeStream($resource);
14✔
234
    }
235

236
    /**
237
     * @inheritDoc
238
     */
239
    #[Override]
240
    public function detach()
241
    {
242
        $resource = $this->resource ?? null;
40✔
243

244
        $this->resource = null;
40✔
245

246
        return $resource;
40✔
247
    }
248

249
    /**
250
     * @inheritDoc
251
     */
252
    #[Override]
253
    public function getSize(): int|null
254
    {
255
        // If the stream isn't set
256
        if ($this->isInvalidStream()) {
4✔
257
            // Return without attempting to get the fstat
258
            return null;
2✔
259
        }
260

261
        /** @var resource $stream */
262
        $stream = $this->resource;
4✔
263

264
        // Get the stream's fstat
265
        $fstat = fstat($stream);
4✔
266

267
        if ($fstat === false) {
4✔
UNCOV
268
            return null;
×
269
        }
270

271
        return $fstat['size'];
4✔
272
    }
273

274
    /**
275
     * @inheritDoc
276
     */
277
    #[Override]
278
    public function tell(): int
279
    {
280
        $this->verifyStream();
8✔
281

282
        /** @var resource $stream */
283
        $stream = $this->resource;
6✔
284

285
        // Get the tell for the stream
286
        $result = $this->tellStream($stream);
6✔
287

288
        $this->verifyTellResult($result);
6✔
289

290
        /** @var int $result */
291

292
        return $result;
4✔
293
    }
294

295
    /**
296
     * @inheritDoc
297
     */
298
    #[Override]
299
    public function eof(): bool
300
    {
301
        // If there is no stream
302
        if ($this->isInvalidStream()) {
16✔
303
            // Don't do anything
304
            return true;
2✔
305
        }
306

307
        /** @var resource $stream */
308
        $stream = $this->resource;
16✔
309

310
        return feof($stream);
16✔
311
    }
312

313
    /**
314
     * @inheritDoc
315
     */
316
    #[Override]
317
    public function getContents(): string
318
    {
319
        $this->verifyReadable();
86✔
320

321
        /** @var resource $stream */
322
        $stream = $this->resource;
84✔
323

324
        // Get the stream contents
325
        $result = $this->getStreamContents($stream);
84✔
326

327
        $this->verifyReadResult($result);
84✔
328

329
        /** @var string $result */
330

331
        return $result;
80✔
332
    }
333

334
    /**
335
     * @inheritDoc
336
     */
337
    #[Override]
338
    public function getMetadata(string|null $key = null): mixed
339
    {
340
        // Ensure the stream is valid
341
        if ($this->isInvalidStream()) {
176✔
342
            return null;
6✔
343
        }
344

345
        /** @var resource $stream */
346
        $stream = $this->resource;
176✔
347

348
        // If no key was specified
349
        if ($key === null) {
176✔
350
            // Return all the meta data
351
            return $this->getStreamMetadata($stream);
4✔
352
        }
353

354
        // Get the meta data
355
        $metadata = $this->getStreamMetadata($stream);
176✔
356

357
        return $metadata[$key] ?? null;
176✔
358
    }
359

360
    // public function __clone()
361
    // {
362
    //     $this->rewind();
363
    //
364
    //     $contents = $this->getContents();
365
    //
366
    //     $this->setStream($this->stream, $this->mode, $this->modeTranslation);
367
    //
368
    //     if ($this->isWritable()) {
369
    //         $this->write($contents);
370
    //         $this->rewind();
371
    //     }
372
    // }
373

374
    /**
375
     * Seek the stream resource.
376
     *
377
     * @param resource $stream
378
     * @param int      $offset
379
     * @param int      $whence
380
     *
381
     * @return int
382
     */
383
    protected function seekStream($stream, int $offset, int $whence = SEEK_SET): int
384
    {
385
        // Get the results of the seek attempt
386
        return fseek($stream, $offset, $whence);
114✔
387
    }
388

389
    /**
390
     * Tell the stream resource.
391
     *
392
     * @param resource $stream
393
     *
394
     * @return int|false
395
     */
396
    protected function tellStream($stream): int|false
397
    {
398
        // Get the tell for the stream
399
        return ftell($stream);
4✔
400
    }
401

402
    /**
403
     * Write to a stream.
404
     *
405
     * @param resource $stream The stream
406
     */
407
    protected function writeToStream($stream, string $data): int|false
408
    {
409
        return fwrite($stream, $data);
132✔
410
    }
411

412
    /**
413
     * Read from stream.
414
     *
415
     * @param resource    $stream The stream
416
     * @param int<1, max> $length The length
417
     */
418
    protected function readFromStream($stream, int $length): string|false
419
    {
420
        return fread($stream, $length);
14✔
421
    }
422

423
    /**
424
     * Get a stream's metadata.
425
     *
426
     * @param resource $stream The stream
427
     *
428
     * @return array{blocked: bool, crypto?: array{cipher_bits: int, cipher_name: string, cipher_version: string, protocol: string}, eof: bool, mediatype?: string, mode: string, seekable: bool, stream_type: string, timed_out: bool, unread_bytes: int, uri: string, wrapper_data: mixed, wrapper_type: string}
429
     */
430
    protected function getStreamMetadata($stream): array
431
    {
432
        return stream_get_meta_data($stream);
176✔
433
    }
434

435
    /**
436
     * Get a stream's contents.
437
     *
438
     * @param resource $stream The stream
439
     *
440
     * @return string|false
441
     */
442
    protected function getStreamContents($stream): string|false
443
    {
444
        return stream_get_contents($stream);
80✔
445
    }
446

447
    /**
448
     * Close a stream.
449
     *
450
     * @param resource $stream The stream
451
     *
452
     * @return bool
453
     */
454
    protected function closeStream($stream): bool
455
    {
456
        return fclose($stream);
14✔
457
    }
458
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc