@@ -71,6 +71,7 @@ class Process implements \IteratorAggregate
7171 private $ incrementalErrorOutputOffset = 0 ;
7272 private $ tty = false ;
7373 private $ pty ;
74+ private $ options = ['suppress_errors ' => true , 'bypass_shell ' => true ];
7475
7576 private $ useFileHandles = false ;
7677 /** @var PipesInterface */
@@ -196,7 +197,11 @@ public static function fromShellCommandline(string $command, string $cwd = null,
196197
197198 public function __destruct ()
198199 {
199- $ this ->stop (0 );
200+ if ($ this ->options ['create_new_console ' ] ?? false ) {
201+ $ this ->processPipes ->close ();
202+ } else {
203+ $ this ->stop (0 );
204+ }
200205 }
201206
202207 public function __clone ()
@@ -303,10 +308,7 @@ public function start(callable $callback = null, array $env = [])
303308 $ commandline = $ this ->replacePlaceholders ($ commandline , $ env );
304309 }
305310
306- $ options = ['suppress_errors ' => true ];
307-
308311 if ('\\' === \DIRECTORY_SEPARATOR ) {
309- $ options ['bypass_shell ' ] = true ;
310312 $ commandline = $ this ->prepareWindowsCommandLine ($ commandline , $ env );
311313 } elseif (!$ this ->useFileHandles && $ this ->isSigchildEnabled ()) {
312314 // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
@@ -332,7 +334,7 @@ public function start(callable $callback = null, array $env = [])
332334 throw new RuntimeException (sprintf ('The provided cwd "%s" does not exist. ' , $ this ->cwd ));
333335 }
334336
335- $ this ->process = @proc_open ($ commandline , $ descriptors , $ this ->processPipes ->pipes , $ this ->cwd , $ envPairs , $ options );
337+ $ this ->process = @proc_open ($ commandline , $ descriptors , $ this ->processPipes ->pipes , $ this ->cwd , $ envPairs , $ this -> options );
336338
337339 if (!\is_resource ($ this ->process )) {
338340 throw new RuntimeException ('Unable to launch a new process. ' );
@@ -1220,6 +1222,32 @@ public function getStartTime(): float
12201222 return $ this ->starttime ;
12211223 }
12221224
1225+ /**
1226+ * Defines options to pass to the underlying proc_open().
1227+ *
1228+ * @see https://php.net/proc_open for the options supported by PHP.
1229+ *
1230+ * Enabling the "create_new_console" option allows a subprocess to continue
1231+ * to run after the main process exited, on both Windows and *nix
1232+ */
1233+ public function setOptions (array $ options )
1234+ {
1235+ if ($ this ->isRunning ()) {
1236+ throw new RuntimeException ('Setting options while the process is running is not possible. ' );
1237+ }
1238+
1239+ $ defaultOptions = $ this ->options ;
1240+ $ existingOptions = ['blocking_pipes ' , 'create_process_group ' , 'create_new_console ' ];
1241+
1242+ foreach ($ options as $ key => $ value ) {
1243+ if (!\in_array ($ key , $ existingOptions )) {
1244+ $ this ->options = $ defaultOptions ;
1245+ throw new LogicException (sprintf ('Invalid option "%s" passed to "%s()". Supported options are "%s". ' , $ key , __METHOD__ , implode ('", " ' , $ existingOptions )));
1246+ }
1247+ $ this ->options [$ key ] = $ value ;
1248+ }
1249+ }
1250+
12231251 /**
12241252 * Returns whether TTY is supported on the current operating system.
12251253 */
0 commit comments