IPC   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Test Coverage

Coverage 51.28%

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 112
ccs 20
cts 39
cp 0.5128
rs 10
c 0
b 0
f 0
wmc 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A readStdOut() 0 3 1
A buildDescriptors() 0 7 2
A __construct() 0 17 5
A close() 0 5 1
A blockingRead() 0 20 3
A send() 0 7 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\PinEntry\Process;
6
7
class IPC
8
{
9
    const STDIN = 0;
10
    const STDOUT = 1;
11
    const STDERR = 2;
12
13
    /**
14
     * Contains a default file descriptor definitions
15
     * for the STDIN, STDOUT, STDERR streams.
16
     * @var array
17
     */
18
    protected static $defaultDescriptor = [
19
        // Define the STDIN pipe the child will read from
20
        self::STDIN => ["pipe", "r"],
21
22
        // Define the STDOUT pipe the child will write to
23
        self::STDOUT => ["pipe", "w"],
24
25
        // Define the STDERR pipe the child will write to. Can also
26
        // specify a file instead of writing to this process.
27
        self::STDERR => ["pipe", "w"],
28
    ];
29
30
    /**
31
     * @var resource[]
32
     */
33
    private $fhList = [];
34
35 1
    public function __construct(array $fileHandles)
36
    {
37 1
        $missingKeys = array_diff_key(self::$defaultDescriptor, $fileHandles);
38 1
        if (count($missingKeys) > 0) {
39
            throw new \InvalidArgumentException("Missing required file handle ({$missingKeys[0]})");
40
        }
41
42 1
        foreach ($fileHandles as $fhKey => $fh) {
43 1
            if (!(is_resource($fh) && get_resource_type($fh) === "stream")) {
44 1
                throw new \InvalidArgumentException("Invalid file handle ({$fhKey})");
45
            }
46
        }
47
48 1
        stream_set_blocking($fileHandles[self::STDOUT], false);
49 1
        stream_set_blocking($fileHandles[self::STDERR], false);
50
51 1
        $this->fhList = $fileHandles;
52 1
    }
53
54 1
    public function close()
55
    {
56 1
        fclose($this->fhList[self::STDIN]);
57 1
        fclose($this->fhList[self::STDOUT]);
58 1
        fclose($this->fhList[self::STDERR]);
59 1
    }
60
61
    /**
62
     * @param string $data
63
     * @return int
64
     */
65
    public function send(string $data): int
66
    {
67
        $write = fwrite($this->fhList[self::STDIN], $data);
68
        if ($write === false) {
69
            throw new \RuntimeException("Failed to write to process stdin");
70
        }
71
        return $write;
72
    }
73
74
    public function readStdOut(): string
75
    {
76
        return $this->blockingRead($this->fhList[self::STDOUT]);
77
    }
78
79
    /**
80
     * @param resource $fh
81
     * @return string
82
     */
83
    private function blockingRead($fh)
84
    {
85
        $rx = [$fh];
86
        $wx = [];
87
        $ex = [];
88
        // This will pause execution until the stream changes state,
89
        // most likely indicating it is ready to be read.
90
        if (false === stream_select($rx, $wx, $ex, null, 0)) {
91
            throw new \RuntimeException("stream_select failed");
92
        }
93
94
        // maybe we should inspect $rx to see what the new status is before reading
95
        $buffer = stream_get_contents($fh);
96
        if ($buffer === false) {
97
            throw new \RuntimeException("Reading from stream failed");
98
        }
99
100
        assert($buffer !== "");
101
102
        return $buffer;
103
    }
104
105
    /**
106
     * Return the default descriptors values, overloaded with
107
     * the value in $overrideDescriptors if the same key is set there.
108
     *
109
     * @param array[] $overrideDescriptors
110
     * @return array[]
111
     */
112 3
    public static function buildDescriptors(array $overrideDescriptors = []): array
113
    {
114 3
        $descriptor = static::$defaultDescriptor;
115 3
        foreach (array_intersect_key($descriptor, $overrideDescriptors) as $key => $value) {
116 1
            $descriptor[$key] = $overrideDescriptors[$key];
117
        }
118 3
        return $descriptor;
119
    }
120
}
121