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