Skip to content

Commit ce1234f

Browse files
committed
WIP
1 parent 4d600d0 commit ce1234f

File tree

1 file changed

+53
-37
lines changed

1 file changed

+53
-37
lines changed

src/ProcessManager.php

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,26 @@ class ProcessManager
1111

1212
public function __construct(string $binaryPath, bool $debug = true)
1313
{
14-
$this->binaryPath = $binaryPath;
14+
// Normalize path for Windows
15+
$this->binaryPath = str_replace('/', '\\', $binaryPath);
1516
$this->debug = $debug;
16-
$this->debugLog("ProcessManager initialized with binary: $binaryPath");
17+
18+
// Verify binary exists and is executable
19+
if (!file_exists($this->binaryPath)) {
20+
throw new RuntimeException("Binary not found at: {$this->binaryPath}");
21+
}
22+
23+
if (!is_executable($this->binaryPath)) {
24+
throw new RuntimeException("Binary is not executable: {$this->binaryPath}");
25+
}
26+
27+
$this->debugLog("ProcessManager initialized with verified binary: {$this->binaryPath}");
1728
}
1829

1930
private function debugLog(string $message): void
2031
{
2132
if ($this->debug) {
22-
echo "[DEBUG] " . date('Y-m-d H:i:s') . " - $message\n";
33+
fwrite(STDERR, "[DEBUG] " . date('Y-m-d H:i:s') . " - $message\n");
2334
flush();
2435
}
2536
}
@@ -28,78 +39,83 @@ public function execute(array $config, bool $streamOutput): string
2839
{
2940
$this->debugLog("Starting execution");
3041

31-
// For Windows, ensure the path is properly quoted
32-
$cmd = PHP_OS_FAMILY === 'Windows'
33-
? '"' . str_replace('/', '\\', $this->binaryPath) . '"'
34-
: $this->binaryPath;
35-
36-
$this->debugLog("Command to execute: $cmd");
37-
3842
// Create temporary file for input
3943
$tmpfname = tempnam(sys_get_temp_dir(), 'volt_');
4044
$this->debugLog("Created temporary file: $tmpfname");
4145

46+
// Write config to temp file
4247
file_put_contents($tmpfname, json_encode($config, JSON_PRETTY_PRINT));
43-
$this->debugLog("Written config to temporary file");
4448

49+
// Build command with proper escaping
50+
$cmd = escapeshellarg($this->binaryPath);
51+
$this->debugLog("Executing command: $cmd");
52+
53+
// Setup process
4554
$descriptorspec = [
46-
0 => ['pipe', 'r'],
47-
1 => ['pipe', 'w'],
48-
2 => ['pipe', 'w']
55+
0 => ['pipe', 'r'], // stdin
56+
1 => ['pipe', 'w'], // stdout
57+
2 => ['pipe', 'w'] // stderr
4958
];
5059

51-
$this->debugLog("Opening process");
60+
$cwd = dirname($this->binaryPath);
61+
$env = ['VOLT_TEST_DEBUG' => '1'];
5262

53-
$process = proc_open($cmd, $descriptorspec, $pipes, null, null, [
63+
$this->debugLog("Opening process in directory: $cwd");
64+
65+
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env, [
5466
'bypass_shell' => true,
5567
'create_process_group' => true
5668
]);
5769

5870
if (!is_resource($process)) {
5971
unlink($tmpfname);
60-
throw new RuntimeException('Failed to start process');
72+
throw new RuntimeException("Failed to start process: $cmd");
6173
}
6274

6375
$this->currentProcess = $process;
6476
$this->debugLog("Process started successfully");
6577

6678
try {
67-
// Write config to stdin
79+
// Write config to process
6880
$this->debugLog("Writing config to process");
69-
fwrite($pipes[0], file_get_contents($tmpfname));
81+
$configContent = file_get_contents($tmpfname);
82+
fwrite($pipes[0], $configContent);
7083
fclose($pipes[0]);
7184
unlink($tmpfname);
7285

73-
$this->debugLog("Starting to read output");
74-
$output = '';
75-
76-
// Set streams to non-blocking
86+
// Set up non-blocking reads
7787
stream_set_blocking($pipes[1], false);
7888
stream_set_blocking($pipes[2], false);
7989

80-
while (true) {
90+
$output = '';
91+
$processRunning = true;
92+
93+
$this->debugLog("Starting output reading loop");
94+
95+
while ($processRunning) {
8196
$status = proc_get_status($process);
97+
$processRunning = $status['running'];
8298

83-
if (!$status['running']) {
84-
$this->debugLog("Process has finished running");
99+
$read = array_filter([$pipes[1], $pipes[2]], 'is_resource');
100+
if (empty($read)) {
85101
break;
86102
}
87103

88-
$read = [$pipes[1], $pipes[2]];
89104
$write = null;
90105
$except = null;
91106

92107
if (stream_select($read, $write, $except, 0, 100000)) {
93108
foreach ($read as $pipe) {
94109
$data = fread($pipe, 4096);
110+
95111
if ($data === false || $data === '') {
96112
continue;
97113
}
98114

99115
if ($pipe === $pipes[1]) {
100116
$output .= $data;
101117
if ($streamOutput) {
102-
echo $data;
118+
fwrite(STDOUT, $data);
103119
flush();
104120
}
105121
} else {
@@ -108,9 +124,13 @@ public function execute(array $config, bool $streamOutput): string
108124
}
109125
}
110126
}
111-
}
112127

113-
$this->debugLog("Finished reading output");
128+
// Check if process has exited
129+
if (!$processRunning) {
130+
$this->debugLog("Process has finished");
131+
break;
132+
}
133+
}
114134

115135
// Close remaining pipes
116136
foreach ($pipes as $pipe) {
@@ -119,7 +139,7 @@ public function execute(array $config, bool $streamOutput): string
119139
}
120140
}
121141

122-
// Get exit code
142+
// Get exit code and close process
123143
$exitCode = proc_close($process);
124144
$this->debugLog("Process closed with exit code: $exitCode");
125145

@@ -142,12 +162,8 @@ public function execute(array $config, bool $streamOutput): string
142162
if (is_resource($process)) {
143163
$status = proc_get_status($process);
144164
if ($status['running']) {
145-
// Force kill on Windows
146-
if (PHP_OS_FAMILY === 'Windows') {
147-
exec('taskkill /F /T /PID ' . $status['pid']);
148-
} else {
149-
proc_terminate($process);
150-
}
165+
exec("taskkill /F /T /PID {$status['pid']} 2>&1", $output, $resultCode);
166+
$this->debugLog("Taskkill result: " . implode("\n", $output) . " (code: $resultCode)");
151167
}
152168
proc_close($process);
153169
}

0 commit comments

Comments
 (0)