@@ -8,23 +8,18 @@ class ProcessManager
88 private string $ binaryPath ;
99 private $ currentProcess = null ;
1010 private bool $ debug ;
11+ private int $ timeout = 30 ; // timeout in seconds
1112
12- public function __construct (string $ binaryPath , bool $ debug = true )
13+ public function __construct (string $ binaryPath , bool $ debug = false )
1314 {
14- // Normalize path for Windows
1515 $ this ->binaryPath = str_replace ('/ ' , '\\' , $ binaryPath );
1616 $ this ->debug = $ debug ;
1717
18- // Verify binary exists and is executable
1918 if (!file_exists ($ this ->binaryPath )) {
2019 throw new RuntimeException ("Binary not found at: {$ this ->binaryPath }" );
2120 }
2221
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 }" );
22+ $ this ->debugLog ("ProcessManager initialized with binary: {$ this ->binaryPath }" );
2823 }
2924
3025 private function debugLog (string $ message ): void
@@ -39,107 +34,110 @@ public function execute(array $config, bool $streamOutput): string
3934 {
4035 $ this ->debugLog ("Starting execution " );
4136
42- // Create temporary file for input
43- $ tmpfname = tempnam (sys_get_temp_dir (), 'volt_ ' );
44- $ this ->debugLog ("Created temporary file: $ tmpfname " );
45-
46- // Write config to temp file
47- file_put_contents ($ tmpfname , json_encode ($ config , JSON_PRETTY_PRINT ));
48-
49- // Build command with proper escaping
37+ // Prepare command
5038 $ cmd = escapeshellarg ($ this ->binaryPath );
51- $ this ->debugLog ("Executing command : $ cmd " );
39+ $ this ->debugLog ("Command : $ cmd " );
5240
53- // Setup process
41+ // Prepare config
42+ $ configJson = json_encode ($ config , JSON_PRETTY_PRINT );
43+ $ this ->debugLog ("Config prepared: " . substr ($ configJson , 0 , 100 ) . "... " );
44+
45+ // Start process
5446 $ descriptorspec = [
55- 0 => ['pipe ' , 'r ' ], // stdin
56- 1 => ['pipe ' , 'w ' ], // stdout
57- 2 => ['pipe ' , 'w ' ] // stderr
47+ 0 => ['pipe ' , 'r ' ],
48+ 1 => ['pipe ' , 'w ' ],
49+ 2 => ['pipe ' , 'w ' ]
5850 ];
5951
60- $ cwd = dirname ($ this ->binaryPath );
61- $ env = ['VOLT_TEST_DEBUG ' => '1 ' ];
62-
63- $ this ->debugLog ("Opening process in directory: $ cwd " );
64-
65- $ process = proc_open ($ cmd , $ descriptorspec , $ pipes , $ cwd , $ env , [
52+ $ this ->debugLog ("Opening process " );
53+ $ process = proc_open ($ cmd , $ descriptorspec , $ pipes , null , null , [
6654 'bypass_shell ' => true ,
6755 'create_process_group ' => true
6856 ]);
6957
7058 if (!is_resource ($ process )) {
71- unlink ($ tmpfname );
72- throw new RuntimeException ("Failed to start process: $ cmd " );
59+ throw new RuntimeException ("Failed to start process " );
7360 }
7461
7562 $ this ->currentProcess = $ process ;
76- $ this ->debugLog ("Process started successfully " );
63+ $ this ->debugLog ("Process started " );
64+
65+ // Set streams to non-blocking mode
66+ foreach ($ pipes as $ pipe ) {
67+ stream_set_blocking ($ pipe , false );
68+ }
7769
7870 try {
79- // Write config to process
71+ // Write config to stdin
8072 $ this ->debugLog ("Writing config to process " );
81- $ configContent = file_get_contents ($ tmpfname );
82- fwrite ($ pipes [0 ], $ configContent );
83- fclose ($ pipes [0 ]);
84- unlink ($ tmpfname );
73+ $ written = fwrite ($ pipes [0 ], $ configJson );
74+ if ($ written === false ) {
75+ throw new RuntimeException ("Failed to write config to process " );
76+ }
77+ $ this ->debugLog ("Wrote $ written bytes to process " );
8578
86- // Set up non-blocking reads
87- stream_set_blocking ($ pipes [1 ], false );
88- stream_set_blocking ($ pipes [2 ], false );
79+ // Important: flush and close stdin
80+ fflush ($ pipes [0 ]);
81+ fclose ($ pipes [0 ]);
82+ $ this ->debugLog ("Closed stdin pipe " );
8983
9084 $ output = '' ;
91- $ processRunning = true ;
92-
93- $ this ->debugLog ("Starting output reading loop " );
85+ $ startTime = time ();
86+ $ lastDataTime = time ();
9487
95- while ($ processRunning ) {
88+ while (true ) {
9689 $ status = proc_get_status ($ process );
97- $ processRunning = $ status ['running ' ];
98-
99- $ read = array_filter ([$ pipes [1 ], $ pipes [2 ]], 'is_resource ' );
100- if (empty ($ read )) {
90+ if (!$ status ['running ' ]) {
91+ $ this ->debugLog ("Process has finished " );
10192 break ;
10293 }
10394
95+ // Check timeout
96+ if (time () - $ startTime > $ this ->timeout ) {
97+ throw new RuntimeException ("Process timed out after {$ this ->timeout } seconds " );
98+ }
99+
100+ // Check for data timeout (no data received)
101+ if (time () - $ lastDataTime > 5 ) {
102+ $ this ->debugLog ("No data received for 5 seconds, checking process status " );
103+ $ lastDataTime = time (); // Reset timer
104+ }
105+
106+ $ read = [$ pipes [1 ], $ pipes [2 ]];
104107 $ write = null ;
105108 $ except = null ;
106109
107- if (stream_select ($ read , $ write , $ except , 0 , 100000 )) {
110+ // Short timeout for select
111+ if (stream_select ($ read , $ write , $ except , 0 , 200000 )) {
108112 foreach ($ read as $ pipe ) {
109- $ data = fread ($ pipe , 4096 );
110-
111- if ($ data === false || $ data === '' ) {
113+ $ data = fread ($ pipe , 8192 );
114+ if ($ data === false ) {
112115 continue ;
113116 }
114-
115- if ($ pipe === $ pipes [1 ]) {
116- $ output .= $ data ;
117- if ($ streamOutput ) {
118- fwrite (STDOUT , $ data );
117+ if ($ data !== '' ) {
118+ $ lastDataTime = time ();
119+ if ($ pipe === $ pipes [1 ]) {
120+ $ output .= $ data ;
121+ if ($ streamOutput ) {
122+ fwrite (STDOUT , $ data );
123+ flush ();
124+ }
125+ } else {
126+ fwrite (STDERR , $ data );
119127 flush ();
120128 }
121- } else {
122- fwrite (STDERR , $ data );
123- flush ();
124129 }
125130 }
126131 }
127-
128- // Check if process has exited
129- if (!$ processRunning ) {
130- $ this ->debugLog ("Process has finished " );
131- break ;
132- }
133132 }
134133
135134 // Close remaining pipes
136- foreach ($ pipes as $ pipe ) {
137- if (is_resource ($ pipe )) {
138- fclose ($ pipe );
135+ foreach ([ 1 , 2 ] as $ i ) {
136+ if (isset ( $ pipes [ $ i ]) && is_resource ($ pipes [ $ i ] )) {
137+ fclose ($ pipes [ $ i ] );
139138 }
140139 }
141140
142- // Get exit code and close process
143141 $ exitCode = proc_close ($ process );
144142 $ this ->debugLog ("Process closed with exit code: $ exitCode " );
145143
@@ -152,18 +150,20 @@ public function execute(array $config, bool $streamOutput): string
152150 } catch (\Exception $ e ) {
153151 $ this ->debugLog ("Error occurred: " . $ e ->getMessage ());
154152
155- // Clean up
153+ // Clean up pipes
156154 foreach ($ pipes as $ pipe ) {
157155 if (is_resource ($ pipe )) {
158156 fclose ($ pipe );
159157 }
160158 }
161159
160+ // Terminate process if still running
162161 if (is_resource ($ process )) {
163162 $ status = proc_get_status ($ process );
164163 if ($ status ['running ' ]) {
164+ // Force kill on Windows
165165 exec ("taskkill /F /T /PID {$ status ['pid ' ]} 2>&1 " , $ output , $ resultCode );
166- $ this ->debugLog ("Taskkill result: " . implode ( "\n" , $ output ) . " ( code: $ resultCode) " );
166+ $ this ->debugLog ("Taskkill result code: $ resultCode " );
167167 }
168168 proc_close ($ process );
169169 }
0 commit comments