ResourceInputStream::__construct()   A
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 7
c 3
b 0
f 0
dl 0
loc 13
rs 9.6111
cc 5
nc 3
nop 1
1
<?php
2
3
namespace PhpSchool\Terminal\IO;
4
5
use function is_resource;
6
use function get_resource_type;
7
use function stream_get_meta_data;
8
use function strpos;
9
10
/**
11
 * @author Aydin Hassan <[email protected]>
12
 */
13
class ResourceInputStream implements InputStream
14
{
15
    /**
16
     * @var resource
17
     */
18
    private $stream;
19
20
    /**
21
     * @var bool Original blocking state.
22
     */
23
    private $blocking;
24
25
    public function __construct($stream = \STDIN)
26
    {
27
        if (!is_resource($stream) || get_resource_type($stream) !== 'stream') {
28
            throw new \InvalidArgumentException('Expected a valid stream');
29
        }
30
31
        $meta = stream_get_meta_data($stream);
32
        if (strpos($meta['mode'], 'r') === false && strpos($meta['mode'], '+') === false) {
33
            throw new \InvalidArgumentException('Expected a readable stream');
34
        }
35
36
        $this->blocking = $meta['blocked'];
37
        $this->stream = $stream;
38
    }
39
40
    /**
41
     * Restore the blocking state.
42
     */
43
    public function __destruct() {
44
        stream_set_blocking($this->stream, $this->blocking);
45
    }
46
47
    public function read(int $numBytes, callable $callback) : void
48
    {
49
        $buffer = fread($this->stream, $numBytes);
50
        if (!empty($buffer)) {
51
            // Prevent blocking to handle pasted input.
52
            stream_set_blocking($this->stream, false);
53
        } else {
54
            // Re-enable blocking when input has been handled.
55
            stream_set_blocking($this->stream, true);
56
        }
57
58
        $callback($buffer);
59
    }
60
61
    /**
62
     * Whether the stream is connected to an interactive terminal
63
     */
64
    public function isInteractive() : bool
65
    {
66
        return stream_isatty($this->stream);
67
    }
68
}
69