forked from damsfx/valet-windows
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathCommandLine.php
More file actions
211 lines (185 loc) · 6.18 KB
/
Copy pathCommandLine.php
File metadata and controls
211 lines (185 loc) · 6.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<?php
namespace Valet;
use Valet\Packages\Gsudo;
use Symfony\Component\Process\Process;
class CommandLine {
/**
* Pass the command to the command line and display the output.
*
* @param string $command
*/
public function passthru($command) {
passthru($command);
}
/**
* Execute command via shell and return the complete output as a string
*
* @param string $command
*
* @return bool|string|null The output.
*/
public function shellExec($command) {
return shell_exec($command);
}
/**
* Stream command output in real time and optionally collect matching lines.
*
* @param string $command
* @param array $callbacks Optional callbacks:
* - onLine (callable): receives every raw line after it is written.
* - matches (callable): return true to collect line for post-run analysis.
* - isError (callable): return true to render line as an error. Defaults to matches.
*
* @return array The collected output lines or an empty array if no lines were collected.
*/
public function streamCommandOutput($command, array $callbacks = []): array {
$lineHandler = $callbacks['onLine'] ?? null;
$lineMatches = $callbacks['matches'] ?? null;
$lineIsError = $callbacks['isError'] ?? $lineMatches;
$capturedLines = [];
// Open a process to execute the command and read its output.
// 2>&1 redirects stderr to stdout so we can capture both.
$handle = popen("$command 2>&1", 'r');
while ($handle && !feof($handle)) {
$line = fgets($handle);
if ($line === false) {
break;
}
// If the line is an error, output it as an error.
if ($lineIsError && $lineIsError($line)) {
error($line, false, false, true);
}
// Otherwise, output it normally.
else {
output($line, false);
}
// Invoke the optional line handler after writing output so callers can append
// follow-up messages in display order.
if ($lineHandler) {
$lineHandler($line);
}
// If a callback is provided and the line matches the condition,
// then collect the line for post-run analysis.
if ($lineMatches && $lineMatches($line)) {
$capturedLines[] = trim($line);
}
}
// Close the process.
if ($handle) {
pclose($handle);
}
return $capturedLines;
}
/**
* Pass the given Valet command to the command line with elevated privileges using gsudo.
*
* gsudo is a sudo equivalent of the Mac `sudo` utility. It allows the user to run commands as the root user with elevated privileges with minimal amount of UAC popups, ie. only 1 UAC popup.
*
* https://github.com/gerardog/gsudo
*
* @param string $valetCommand The Valet command to run.
* @param bool $asTrustedInstaller Set to `true` to run the command as a trusted installer. Default: `false`
* @param bool $quiet Set to `true` to suppress the output. Default: `false`
*/
public function sudo($valetCommand, $asTrustedInstaller = false, $quiet = false) {
$gsudoClass = resolve(Gsudo::class);
$gsudo = $asTrustedInstaller ? $gsudoClass->runAsTrustedInstaller() : $gsudoClass->runAsSystem();
$this->passthru($gsudo . ' ' . $valetCommand . ($quiet ? ' > nul 2>&1' : ''));
}
/**
* Run the given command.
*
* Uses the Symfony Process component to run the command.
*
* @param string $command
* @param callable|null $onError
* @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
*
* @return ProcessOutput
*/
public function run($command, ?callable $onError = null, $realTimeOutput = false) {
return $this->runCommand($command, $onError, $realTimeOutput);
}
/**
* Run the given command with PowerShell.
*
* @param string $command
* @param callable|null $onError
* @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
*
* @return ProcessOutput
*/
public function powershell(string $command, ?callable $onError = null, $realTimeOutput = false) {
return $this->runCommand("powershell -command \"$command\"", $onError, $realTimeOutput);
}
/**
* Run the given command and exit if fails.
*
* @param string $command
* @param callable|null $onError
* @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
*
* @return ProcessOutput
*/
public function runOrExit($command, ?callable $onError = null, $realTimeOutput = false) {
/**
* @param int $code Error code
* @param string $output Error output
*/
return $this->run($command, function ($code, $output) use ($onError) {
if ($onError) {
$onError($code, $output);
}
exit(1);
}, $realTimeOutput);
}
/**
* Run the given command.
*
* @param string $command
* @param callable|null $onError
* @param bool $realTimeOutput Set to `true` to get the output in real time as the command is running. Default: `false`
*
* @return ProcessOutput|void Returns a ProcessOutput only if the real time is `false`, otherwise it doesn't return anything (void) as it's echoing out in real time.
*/
public function runCommand($command, ?callable $onError = null, $realTimeOutput = false) {
$onError = $onError ?: function () {
};
// Symfony's 4.x Process component has deprecated passing a command string
// to the constructor, but older versions (which Valet's Composer
// constraints allow) don't have the fromShellCommandLine method.
// For more information, see: https://github.com/laravel/valet/pull/761
if (method_exists(Process::class, 'fromShellCommandline')) {
$process = Process::fromShellCommandline($command);
}
else {
$process = new Process($command);
}
/**
* Output in real time
*/
if ($realTimeOutput) {
// Use setTimeout of 0 to allow it to run seemingly forever, until user cancels it.
$process->setTimeout(0)->run(function ($type, $line) {
if (Process::ERR === $type) {
echo 'ERROR: ' . $line;
}
else {
echo $line;
}
});
}
else {
$processOutput = '';
$process->setTimeout(60)->run(function ($type, $line) use (&$processOutput) {
$processOutput .= $line;
});
}
if ($process->getExitCode() !== 0) {
$onError($process->getExitCode(), $processOutput);
}
if (!$realTimeOutput) {
return new ProcessOutput($process);
}
}
}