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

valkyrjaio / valkyrja / 13047041070

30 Jan 2025 06:43AM UTC coverage: 47.621% (+0.2%) from 47.422%
13047041070

push

github

MelechMizrachi
PHPStan level 7 and 8.

168 of 1038 new or added lines in 111 files covered. (16.18%)

444 existing lines in 45 files now uncovered.

5195 of 10909 relevant lines covered (47.62%)

18.83 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 Throwable;
17
use Valkyrja\Http\Message\Stream\Contract\Stream as Contract;
18
use Valkyrja\Http\Message\Stream\Enum\Mode;
19
use Valkyrja\Http\Message\Stream\Enum\ModeTranslation;
20
use Valkyrja\Http\Message\Stream\Enum\PhpWrapper;
21
use Valkyrja\Http\Message\Stream\Exception\InvalidLengthException;
22
use Valkyrja\Http\Message\Stream\Exception\InvalidStreamException;
23

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

34
use const SEEK_SET;
35

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

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

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

73
        return (bool) $this->getMetadata('seekable');
130✔
74
    }
75

76
    /**
77
     * @inheritDoc
78
     */
79
    public function seek(int $offset, int $whence = SEEK_SET): void
80
    {
81
        $this->verifyStream();
126✔
82
        $this->verifySeekable();
126✔
83

84
        /** @var resource $stream */
85
        $stream = $this->resource;
124✔
86

87
        // Get the results of the seek attempt
88
        $result = $this->seekStream($stream, $offset, $whence);
124✔
89

90
        $this->verifySeekResult($result);
124✔
91
    }
92

93
    /**
94
     * @inheritDoc
95
     */
96
    public function rewind(): void
97
    {
98
        $this->seek(0);
120✔
99
    }
100

101
    /**
102
     * @inheritDoc
103
     */
104
    public function isReadable(): bool
105
    {
106
        // If there is no stream
107
        if ($this->isInvalidStream()) {
104✔
108
            // It's not readable
109
            return false;
10✔
110
        }
111

112
        // Get the stream's mode
113
        /** @var string|null $mode */
114
        $mode = $this->getMetadata('mode');
102✔
115

116
        return $this->isModeReadable((string) $mode);
102✔
117
    }
118

119
    /**
120
     * @inheritDoc
121
     */
122
    public function read(int $length): string
123
    {
124
        if ($length < 0) {
20✔
NEW
125
            throw new InvalidLengthException("Invalid length of $length provided. Length must be greater than 0");
×
126
        }
127

128
        $this->verifyStream();
20✔
129
        $this->verifyReadable();
18✔
130

131
        /** @var resource $stream */
132
        $stream = $this->resource;
16✔
133

134
        // Read the stream
135
        $result = $this->readFromStream($stream, $length);
16✔
136

137
        $this->verifyReadResult($result);
16✔
138

139
        /** @var string $result */
140

141
        return $result;
14✔
142
    }
143

144
    /**
145
     * @inheritDoc
146
     */
147
    public function isWritable(): bool
148
    {
149
        // If there is no stream
150
        if ($this->isInvalidStream()) {
146✔
151
            // The stream is definitely not writable
152
            return false;
6✔
153
        }
154

155
        // Get the stream's mode
156
        /** @var string|null $mode */
157
        $mode = $this->getMetadata('mode');
146✔
158

159
        return $this->isModeWriteable((string) $mode);
146✔
160
    }
161

162
    /**
163
     * @inheritDoc
164
     */
165
    public function write(string $string): int
166
    {
167
        $this->verifyStream();
146✔
168
        $this->verifyWritable();
144✔
169

170
        /** @var resource $stream */
171
        $stream = $this->resource;
142✔
172

173
        // Attempt to write to the stream
174
        $result = $this->writeToStream($stream, $string);
142✔
175

176
        $this->verifyWriteResult($result);
142✔
177

178
        /** @var int $result */
179

180
        return $result;
140✔
181
    }
182

183
    /**
184
     * @inheritDoc
185
     */
186
    public function __toString(): string
187
    {
188
        // If the stream is not readable
189
        if (! $this->isReadable()) {
18✔
190
            // Return an empty string
191
            return '';
2✔
192
        }
193

194
        try {
195
            // Rewind the stream to the start
196
            $this->rewind();
18✔
197

198
            // Get the stream's contents
199
            return $this->getContents();
18✔
200
        } // On a runtime exception
201
        catch (Throwable) {
2✔
202
            // Return a string
203
            return '';
2✔
204
        }
205
    }
206

207
    /**
208
     * @inheritDoc
209
     */
210
    public function close(): void
211
    {
212
        // If there is no stream
213
        if ($this->isInvalidStream()) {
16✔
214
            // Don't do anything
215
            return;
2✔
216
        }
217

218
        // Detach the stream
219
        /** @var resource $resource */
220
        $resource = $this->detach();
14✔
221

222
        // Close the stream
223
        $this->closeStream($resource);
14✔
224
    }
225

226
    /**
227
     * @inheritDoc
228
     */
229
    public function detach()
230
    {
231
        $resource = $this->resource ?? null;
40✔
232

233
        $this->resource = null;
40✔
234

235
        return $resource;
40✔
236
    }
237

238
    /**
239
     * @inheritDoc
240
     */
241
    public function getSize(): int|null
242
    {
243
        // If the stream isn't set
244
        if ($this->isInvalidStream()) {
4✔
245
            // Return without attempting to get the fstat
246
            return null;
2✔
247
        }
248

249
        /** @var resource $stream */
250
        $stream = $this->resource;
4✔
251

252
        // Get the stream's fstat
253
        $fstat = fstat($stream);
4✔
254

255
        if ($fstat === false) {
4✔
NEW
256
            return null;
×
257
        }
258

259
        return $fstat['size'];
4✔
260
    }
261

262
    /**
263
     * @inheritDoc
264
     */
265
    public function tell(): int
266
    {
267
        $this->verifyStream();
8✔
268

269
        /** @var resource $stream */
270
        $stream = $this->resource;
6✔
271

272
        // Get the tell for the stream
273
        $result = $this->tellStream($stream);
6✔
274

275
        $this->verifyTellResult($result);
6✔
276

277
        /** @var int $result */
278

279
        return $result;
4✔
280
    }
281

282
    /**
283
     * @inheritDoc
284
     */
285
    public function eof(): bool
286
    {
287
        // If there is no stream
288
        if ($this->isInvalidStream()) {
16✔
289
            // Don't do anything
290
            return true;
2✔
291
        }
292

293
        /** @var resource $stream */
294
        $stream = $this->resource;
16✔
295

296
        return feof($stream);
16✔
297
    }
298

299
    /**
300
     * @inheritDoc
301
     */
302
    public function getContents(): string
303
    {
304
        $this->verifyReadable();
86✔
305

306
        /** @var resource $stream */
307
        $stream = $this->resource;
84✔
308

309
        // Get the stream contents
310
        $result = $this->getStreamContents($stream);
84✔
311

312
        $this->verifyReadResult($result);
84✔
313

314
        /** @var string $result */
315

316
        return $result;
80✔
317
    }
318

319
    /**
320
     * @inheritDoc
321
     */
322
    public function getMetadata(string|null $key = null): mixed
323
    {
324
        // Ensure the stream is valid
325
        if ($this->isInvalidStream()) {
184✔
326
            return null;
6✔
327
        }
328

329
        /** @var resource $stream */
330
        $stream = $this->resource;
184✔
331

332
        // If no key was specified
333
        if ($key === null) {
184✔
334
            // Return all the meta data
335
            return $this->getStreamMetadata($stream);
4✔
336
        }
337

338
        // Get the meta data
339
        $metadata = $this->getStreamMetadata($stream);
184✔
340

341
        return $metadata[$key] ?? null;
184✔
342
    }
343

344
    // public function __clone()
345
    // {
346
    //     $this->rewind();
347
    //
348
    //     $contents = $this->getContents();
349
    //
350
    //     $this->setStream($this->stream, $this->mode, $this->modeTranslation);
351
    //
352
    //     if ($this->isWritable()) {
353
    //         $this->write($contents);
354
    //         $this->rewind();
355
    //     }
356
    // }
357

358
    /**
359
     * Seek the stream resource.
360
     *
361
     * @param resource $stream
362
     * @param int      $offset
363
     * @param int      $whence
364
     *
365
     * @return int
366
     */
367
    protected function seekStream($stream, int $offset, int $whence = SEEK_SET): int
368
    {
369
        // Get the results of the seek attempt
370
        return fseek($stream, $offset, $whence);
122✔
371
    }
372

373
    /**
374
     * Tell the stream resource.
375
     *
376
     * @param resource $stream
377
     *
378
     * @return int|false
379
     */
380
    protected function tellStream($stream): int|false
381
    {
382
        // Get the tell for the stream
383
        return ftell($stream);
4✔
384
    }
385

386
    /**
387
     * Write to a stream.
388
     *
389
     * @param resource $stream The stream
390
     */
391
    protected function writeToStream($stream, string $data): int|false
392
    {
393
        return fwrite($stream, $data);
140✔
394
    }
395

396
    /**
397
     * Read from stream.
398
     *
399
     * @param resource    $stream The stream
400
     * @param int<0, max> $length The length
401
     */
402
    protected function readFromStream($stream, int $length): string|false
403
    {
404
        return fread($stream, $length);
14✔
405
    }
406

407
    /**
408
     * Get a stream's metadata.
409
     *
410
     * @param resource $stream The stream
411
     *
412
     * @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}
413
     */
414
    protected function getStreamMetadata($stream): array
415
    {
416
        return stream_get_meta_data($stream);
184✔
417
    }
418

419
    /**
420
     * Get a stream's contents.
421
     *
422
     * @param resource $stream The stream
423
     *
424
     * @return string|false
425
     */
426
    protected function getStreamContents($stream): string|false
427
    {
428
        return stream_get_contents($stream);
80✔
429
    }
430

431
    /**
432
     * Close a stream.
433
     *
434
     * @param resource $stream The stream
435
     *
436
     * @return bool
437
     */
438
    protected function closeStream($stream): bool
439
    {
440
        return fclose($stream);
14✔
441
    }
442
}
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