Passed
Pull Request — master (#39)
by
unknown
01:50
created

Shell::execute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 0
dl 0
loc 18
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
    /*
4
     * Shell wrapper for package adhocore/php-cli.
5
     * @author Sushil Gupta <[email protected]>
6
     * @license MIT
7
     */
8
9
    namespace Ahc\Cli\Helper;
10
11
    use Ahc\Cli\Exception\RuntimeException;
12
13
    class Shell {
14
15
        const STDIN_DESCRIPTOR_KEY = 0;
16
        const STDOUT_DESCRIPTOR_KEY = 1;
17
        const STDERR_DESCRIPTOR_KEY = 2;
18
19
        protected $command;
20
        protected $descriptors;
21
        protected $input;
22
        protected $pipes;
23
        protected $process;
24
        protected $startTime;
25
        protected $status;
26
        protected $timeout;
27
28
        public function __construct(string $command, string $input = null, float $timeout = 10)
29
        {
30
            if (!\function_exists('proc_open')) {
31
                throw new RuntimeException('Required proc_open could not be found in your PHP setup');
32
            }
33
34
            $this->command = $command;
35
            $this->input = $input;
36
            $this->timeout = $timeout;
37
            $this->status = null;
38
        }
39
40
        private function getDescriptors()
41
        {
42
            if ('\\' === \DIRECTORY_SEPARATOR) {
43
                return array(
44
                    self::STDIN_DESCRIPTOR_KEY => array('pipe', 'r'),
45
                    self::STDOUT_DESCRIPTOR_KEY => array('file', 'NUL', 'w'),
46
                    self::STDERR_DESCRIPTOR_KEY => array('file', 'NUL', 'w'),
47
                );
48
            } else {
49
                return array(
50
                    self::STDIN_DESCRIPTOR_KEY => array('pipe', 'r'),
51
                    self::STDOUT_DESCRIPTOR_KEY => array('pipe', 'w'),
52
                    self::STDERR_DESCRIPTOR_KEY => array('pipe', 'w'),
53
                );
54
            }
55
        }
56
57
        private function setInput()
58
        {
59
            fwrite($this->pipes[self::STDIN_DESCRIPTOR_KEY], $this->input);
60
        }
61
62
        private function updateStatus()
63
        {
64
            $this->status = proc_get_status($this->process);
65
66
            return $this->status;
67
        }
68
69
        private function closePipes()
70
        {
71
            fclose($this->pipes[self::STDIN_DESCRIPTOR_KEY]);
72
            fclose($this->pipes[self::STDOUT_DESCRIPTOR_KEY]);
73
            fclose($this->pipes[self::STDERR_DESCRIPTOR_KEY]);
74
        }
75
76
        public function execute()
77
        {
78
            if ($this->isRunning()) {
79
                throw new RuntimeException('Process is already running');
80
            }
81
82
            $this->descriptors = $this->getDescriptors();
83
84
            $this->process = proc_open($this->command, $this->descriptors, $this->pipes);
85
86
            if (!\is_resource($this->process)) {
87
                throw new RuntimeException('Bad program could not be started.');
88
            }
89
90
            $this->setInput();
91
            $this->updateStatus();
92
93
            $this->startTime = microtime(true);
94
        }
95
96
        public function getOutput()
97
        {
98
            return stream_get_contents($this->pipes[self::STDOUT_DESCRIPTOR_KEY]);
99
        }
100
101
        public function getErrorOutput()
102
        {
103
            return stream_get_contents($this->pipes[self::STDERR_DESCRIPTOR_KEY]);
104
        }
105
106
        public function getExitCode()
107
        {
108
            return $this->status['exitcode'];
109
        }
110
111
        public function isRunning()
112
        {
113
            if(!$this->status) {
114
                return false;
115
            }
116
117
            $this->updateStatus();
118
119
            return $this->status['running'];
120
        }
121
122
        public function getProcessId()
123
        {
124
            return $this->isRunning() ? $this->status['pid'] : null;
125
        }
126
127
        public function stop()
128
        {
129
            if (!$this->isRunning()) {
130
                return $this->getExitCode();
131
            }
132
133
            $this->closePipes();
134
135
            if (\is_resource($this->process)) {
136
                proc_close($this->process);
137
            }
138
139
            return $this->getExitCode();
140
        }
141
142
        public function kill()
143
        {
144
            return proc_terminate($this->process);
145
        }
146
147
        public function __destruct()
148
        {
149
            $this->stop();
150
        }
151
    }