84bb70882cd2fcb24351cdd47888679b30bd79be
[pharext/pharext] / src / pharext / ExecCmd.php
1 <?php
2
3 namespace pharext;
4
5 /**
6 * Execute system command
7 */
8 class ExecCmd
9 {
10 /**
11 * Sudo command, if the cmd needs escalated privileges
12 * @var string
13 */
14 private $sudo;
15
16 /**
17 * Executable of the cmd
18 * @var string
19 */
20 private $command;
21
22 /**
23 * Passthrough cmd output
24 * @var bool
25 */
26 private $verbose;
27
28 /**
29 * Output of cmd run
30 * @var string
31 */
32 private $output;
33
34 /**
35 * Return code of cmd run
36 * @var int
37 */
38 private $status;
39
40 /**
41 * @param string $command
42 * @param bool verbose
43 */
44 public function __construct($command, $verbose = false) {
45 $this->command = $command;
46 $this->verbose = $verbose;
47 }
48
49 /**
50 * (Re-)set sudo command
51 * @param string $sudo
52 */
53 public function setSu($sudo = false) {
54 $this->sudo = $sudo;
55 }
56
57 /**
58 * Execute a program with escalated privileges handling interactive password prompt
59 * @param string $command
60 * @param string $output
61 * @param int $status
62 */
63 private function suExec($command, &$output, &$status) {
64 if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) {
65 $status = -1;
66 throw new Exception("Failed to run {$command}");
67 }
68 $stdout = $pipes[1];
69 $passwd = 0;
70 while (!feof($stdout)) {
71 $R = [$stdout]; $W = []; $E = [];
72 if (!stream_select($R, $W, $E, null)) {
73 continue;
74 }
75 $data = fread($stdout, 0x1000);
76 /* only check a few times */
77 if ($passwd++ < 10) {
78 if (stristr($data, "password")) {
79 printf("\n%s", $data);
80 }
81 }
82 $output .= $data;
83 }
84 $status = proc_close($proc);
85 }
86
87 /**
88 * Run the command
89 * @param array $args
90 * @return \pharext\ExecCmd self
91 * @throws \pharext\Exception
92 */
93 public function run(array $args = null) {
94 $exec = escapeshellcmd($this->command);
95 if ($args) {
96 $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args));
97 }
98
99 if ($this->sudo) {
100 $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->output, $this->status);
101 } elseif ($this->verbose) {
102 ob_start(function($s) {
103 $this->output .= $s;
104 return $s;
105 }, 1);
106 passthru($exec, $this->status);
107 ob_end_flush();
108 } else {
109 exec($exec ." 2>&1", $output, $this->status);
110 $this->output = implode("\n", $output);
111 }
112
113 if ($this->status) {
114 throw new Exception("Command {$exec} failed ({$this->status})");
115 }
116
117 return $this;
118 }
119
120 /**
121 * Retrieve exit code of cmd run
122 * @return int
123 */
124 public function getStatus() {
125 return $this->status;
126 }
127
128 /**
129 * Retrieve output of cmd run
130 * @return string
131 */
132 public function getOutput() {
133 return $this->output;
134 }
135 }