Completed
Push — master ( 094488...91c0a8 )
by Sebastian
02:19
created

ProcOpen::run()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5.0031

Importance

Changes 0
Metric Value
dl 0
loc 40
c 0
b 0
f 0
ccs 19
cts 20
cp 0.95
rs 8.9688
cc 5
nc 5
nop 2
crap 5.0031
1
<?php
2
/**
3
 * This file is part of SebastianFeldmann\Cli.
4
 *
5
 * (c) Sebastian Feldmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianFeldmann\Cli\Processor;
11
12
use RuntimeException;
13
use SebastianFeldmann\Cli\Command\Result;
14
use SebastianFeldmann\Cli\Output;
15
use SebastianFeldmann\Cli\Processor;
16
17
/**
18
 * Class ProcOpen
19
 *
20
 * @package SebastianFeldmann\Cli
21
 * @author  Sebastian Feldmann <[email protected]>
22
 * @link    https://github.com/sebastianfeldmann/cli
23
 * @since   Class available since Release 0.9.0
24
 */
25
class ProcOpen implements Processor
26
{
27
    /**
28
     * Handles the output of stdOut
29
     *
30
     * @var \SebastianFeldmann\Cli\Output
31
     */
32
    private $stdOutOutput;
33
34
    /**
35
     * Handles the Output of stdErr
36
     *
37
     * @var \SebastianFeldmann\Cli\Output
38
     */
39
    private $stdErrOutput;
40
41
    /**
42
     * ProcOpen constructor.
43
     *
44
     * @param \SebastianFeldmann\Cli\Output $stdOut
45
     * @param \SebastianFeldmann\Cli\Output $stdErr
46
     */
47 3
    public function __construct(Output $stdOut = null, Output $stdErr = null)
48
    {
49 3
        $this->stdOutOutput = $stdOut ?? new Output\Silent();
50 3
        $this->stdErrOutput = $stdErr ?? new Output\Silent();
51 3
    }
52
53
    /**
54
     * Execute the command.
55
     *
56
     * @param  string $cmd
57
     * @param  int[]  $acceptableExitCodes
58
     * @return \SebastianFeldmann\Cli\Command\Result
59
     */
60 3
    public function run(string $cmd, array $acceptableExitCodes = [0]) : Result
61
    {
62 3
        $old  = error_reporting(0);
63
        $spec = [
64 3
            ['pipe', 'r'],
65
            ['pipe', 'w'],
66
            ['pipe', 'w'],
67
        ];
68
69 3
        $process = proc_open($cmd, $spec, $pipes);
70 3
        if (!is_resource($process)) {
71
            throw new RuntimeException('can\'t execute \'proc_open\'');
72
        }
73
74 3
        $stdOut = '';
75 3
        $stdErr = '';
76
77
        // Loop on process until it exits normally.
78
        do {
79 3
            $status = proc_get_status($process);
80
            // If our pipes have data, grab it for later use.
81 3
            $out = !feof($pipes[1]) ? fgets($pipes[1]) : '';
82 3
            $err = !feof($pipes[2]) ? fgets($pipes[2]) : '';
83
84 3
            $this->stdOutOutput->write($out);
85 3
            $this->stdErrOutput->write($err);
86
87 3
            $stdOut .= $out;
88 3
            $stdErr .= $err;
89 3
        } while ($status['running']);
90
91
        // According to documentation, the exit code is only valid the first call
92
        // after a process is finished. We can't rely on the return value of
93
        // proc_close because proc_get_status will read the exit code first.
94 3
        $code = $status['exitcode'];
95 3
        proc_close($process);
96 3
        error_reporting($old);
97
98 3
        return new Result($cmd, $code, $stdOut, $stdErr, '', $acceptableExitCodes);
99
    }
100
}
101