@@ -7,187 +7,152 @@ class ProcessManager
77{
88 private string $ binaryPath ;
99 private $ currentProcess = null ;
10- private $ isWindows ;
10+ private bool $ debug ;
1111
12- public function __construct (string $ binaryPath )
12+ public function __construct (string $ binaryPath, bool $ debug = true )
1313 {
1414 $ this ->binaryPath = $ binaryPath ;
15- $ this ->isWindows = PHP_OS_FAMILY === 'Windows ' ;
16-
17- // Only register signal handlers on non-Windows systems
18- if (!$ this ->isWindows && function_exists ('pcntl_async_signals ' )) {
19- pcntl_async_signals (true );
20- pcntl_signal (SIGINT , [$ this , 'handleSignal ' ]);
21- pcntl_signal (SIGTERM , [$ this , 'handleSignal ' ]);
22- }
15+ $ this ->debug = $ debug ;
16+ $ this ->debugLog ("ProcessManager initialized with binary: $ binaryPath " );
2317 }
2418
25- private function handleSignal ( int $ signal ): void
19+ private function debugLog ( string $ message ): void
2620 {
27- if ($ this ->currentProcess && is_resource ( $ this -> currentProcess ) ) {
28- proc_terminate ( $ this -> currentProcess ) ;
29- proc_close ( $ this -> currentProcess );
21+ if ($ this ->debug ) {
22+ echo " [DEBUG] " . date ( ' Y-m-d H:i:s ' ) . " - $ message \n" ;
23+ flush ( );
3024 }
31- exit (0 );
3225 }
3326
3427 public function execute (array $ config , bool $ streamOutput ): string
3528 {
36- [$ success , $ process , $ pipes ] = $ this ->openProcess ();
37- $ this ->currentProcess = $ process ;
29+ $ this ->debugLog ("Starting execution " );
3830
39- if (!$ success || !is_array ($ pipes )) {
40- throw new RuntimeException ('Failed to start process of volt test ' );
41- }
31+ // For Windows, ensure the path is properly quoted
32+ $ cmd = PHP_OS_FAMILY === 'Windows '
33+ ? '" ' . str_replace ('/ ' , '\\' , $ this ->binaryPath ) . '" '
34+ : $ this ->binaryPath ;
4235
43- try {
44- $ this ->writeInput ($ pipes [0 ], json_encode ($ config , JSON_PRETTY_PRINT ));
45- fclose ($ pipes [0 ]);
46- return $ this ->handleProcess ($ pipes , $ streamOutput );
47- } finally {
48- // Clean up pipes
49- foreach ($ pipes as $ pipe ) {
50- if (is_resource ($ pipe )) {
51- fclose ($ pipe );
52- }
53- }
36+ $ this ->debugLog ("Command to execute: $ cmd " );
5437
55- if (is_resource ($ process )) {
56- $ exitCode = $ this ->closeProcess ($ process );
57- $ this ->currentProcess = null ;
58- if ($ exitCode !== 0 ) {
59- throw new RuntimeException ('Process failed with exit code ' . $ exitCode );
60- }
61- }
62- }
63- }
38+ // Create temporary file for input
39+ $ tmpfname = tempnam (sys_get_temp_dir (), 'volt_ ' );
40+ $ this ->debugLog ("Created temporary file: $ tmpfname " );
41+
42+ file_put_contents ($ tmpfname , json_encode ($ config , JSON_PRETTY_PRINT ));
43+ $ this ->debugLog ("Written config to temporary file " );
6444
65- protected function openProcess (): array
66- {
6745 $ descriptorspec = [
68- 0 => ['pipe ' , 'r ' ], // stdin
69- 1 => ['pipe ' , 'w ' ], // stdout
70- 2 => ['pipe ' , 'w ' ] // stderr
46+ 0 => ['pipe ' , 'r ' ],
47+ 1 => ['pipe ' , 'w ' ],
48+ 2 => ['pipe ' , 'w ' ]
7149 ];
7250
73- // Windows-specific process options
74- $ options = $ this ->isWindows ? [
75- 'bypass_shell ' => true ,
76- 'create_process_group ' => true // Important for Windows process management
77- ] : [
78- 'bypass_shell ' => true
79- ];
51+ $ this ->debugLog ("Opening process " );
8052
81- $ process = proc_open (
82- $ this ->binaryPath ,
83- $ descriptorspec ,
84- $ pipes ,
85- null ,
86- null ,
87- $ options
88- );
53+ $ process = proc_open ($ cmd , $ descriptorspec , $ pipes , null , null , [
54+ 'bypass_shell ' => true ,
55+ 'create_process_group ' => true
56+ ]);
8957
9058 if (!is_resource ($ process )) {
91- return [false , null , []];
59+ unlink ($ tmpfname );
60+ throw new RuntimeException ('Failed to start process ' );
9261 }
9362
94- return [ true , $ process, $ pipes ] ;
95- }
63+ $ this -> currentProcess = $ process ;
64+ $ this -> debugLog ( " Process started successfully " );
9665
97- private function handleProcess (array $ pipes , bool $ streamOutput ): string
98- {
99- $ output = '' ;
100- $ running = true ;
66+ try {
67+ // Write config to stdin
68+ $ this ->debugLog ("Writing config to process " );
69+ fwrite ($ pipes [0 ], file_get_contents ($ tmpfname ));
70+ fclose ($ pipes [0 ]);
71+ unlink ($ tmpfname );
10172
102- // Set streams to non-blocking mode
103- foreach ($ pipes as $ pipe ) {
104- if (is_resource ($ pipe )) {
105- stream_set_blocking ($ pipe , false );
106- }
107- }
73+ $ this ->debugLog ("Starting to read output " );
74+ $ output = '' ;
10875
109- while ($ running ) {
110- $ read = array_filter ($ pipes , 'is_resource ' );
111- if (empty ($ read )) {
112- break ;
113- }
76+ // Set streams to non-blocking
77+ stream_set_blocking ($ pipes [1 ], false );
78+ stream_set_blocking ($ pipes [2 ], false );
79+
80+ while (true ) {
81+ $ status = proc_get_status ($ process );
11482
115- // Windows-specific: Check process status
116- if ($ this ->isWindows ) {
117- $ status = proc_get_status ($ this ->currentProcess );
11883 if (!$ status ['running ' ]) {
119- $ running = false ;
84+ $ this ->debugLog ("Process has finished running " );
85+ break ;
12086 }
121- }
122-
123- $ write = null ;
124- $ except = null ;
12587
126- // Use a shorter timeout on Windows
127- $ timeout = $ this ->isWindows ? 0.1 : 1 ;
88+ $ read = [$ pipes [1 ], $ pipes [2 ]];
89+ $ write = null ;
90+ $ except = null ;
12891
129- if (stream_select ($ read , $ write , $ except , 0 , $ timeout * 1000000 ) === false ) {
130- break ;
131- }
132-
133- foreach ($ read as $ pipe ) {
134- $ type = array_search ($ pipe , $ pipes , true );
135- $ data = fread ($ pipe , 4096 );
92+ if (stream_select ($ read , $ write , $ except , 0 , 100000 )) {
93+ foreach ($ read as $ pipe ) {
94+ $ data = fread ($ pipe , 4096 );
95+ if ($ data === false || $ data === '' ) {
96+ continue ;
97+ }
13698
137- if ($ data === false || $ data === '' ) {
138- if (feof ($ pipe )) {
139- fclose ($ pipe );
140- unset($ pipes [$ type ]);
141- continue ;
99+ if ($ pipe === $ pipes [1 ]) {
100+ $ output .= $ data ;
101+ if ($ streamOutput ) {
102+ echo $ data ;
103+ flush ();
104+ }
105+ } else {
106+ fwrite (STDERR , $ data );
107+ flush ();
108+ }
142109 }
143110 }
111+ }
144112
145- if ($ type === 1 ) { // stdout
146- $ output .= $ data ;
147- if ($ streamOutput ) {
148- echo $ data ;
149- if ($ this ->isWindows ) {
150- flush (); // Ensure output is displayed immediately on Windows
151- }
152- }
153- } elseif ($ type === 2 && $ streamOutput ) { // stderr
154- fwrite (STDERR , $ data );
155- if ($ this ->isWindows ) {
156- flush ();
157- }
113+ $ this ->debugLog ("Finished reading output " );
114+
115+ // Close remaining pipes
116+ foreach ($ pipes as $ pipe ) {
117+ if (is_resource ($ pipe )) {
118+ fclose ($ pipe );
158119 }
159120 }
160- }
161121
162- return $ output ;
163- }
122+ // Get exit code
123+ $ exitCode = proc_close ($ process );
124+ $ this ->debugLog ("Process closed with exit code: $ exitCode " );
164125
165- protected function writeInput ($ pipe , string $ input ): void
166- {
167- if (is_resource ($ pipe )) {
168- fwrite ($ pipe , $ input );
169- if ($ this ->isWindows ) {
170- fflush ($ pipe ); // Ensure data is written immediately on Windows
126+ if ($ exitCode !== 0 ) {
127+ throw new RuntimeException ("Process failed with exit code $ exitCode " );
171128 }
172- }
173- }
174129
175- protected function closeProcess ($ process ): int
176- {
177- if (!is_resource ($ process )) {
178- return -1 ;
179- }
130+ return $ output ;
131+
132+ } catch (\Exception $ e ) {
133+ $ this ->debugLog ("Error occurred: " . $ e ->getMessage ());
134+
135+ // Clean up
136+ foreach ($ pipes as $ pipe ) {
137+ if (is_resource ($ pipe )) {
138+ fclose ($ pipe );
139+ }
140+ }
180141
181- $ status = proc_get_status ($ process );
182- if ($ status ['running ' ]) {
183- // Windows-specific process termination
184- if ($ this ->isWindows ) {
185- exec ('taskkill /F /T /PID ' . $ status ['pid ' ]);
186- } else {
187- proc_terminate ($ process );
142+ if (is_resource ($ process )) {
143+ $ status = proc_get_status ($ process );
144+ 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+ }
151+ }
152+ proc_close ($ process );
188153 }
189- }
190154
191- return proc_close ($ process );
155+ throw $ e ;
156+ }
192157 }
193158}
0 commit comments