Skip to content

Commit 4b2b64a

Browse files
committed
feature #37519 [Process] allow setting options esp. "create_new_console" to detach a subprocess (andrei0x309)
This PR was merged into the 5.2-dev branch. Discussion ---------- [Process] allow setting options esp. "create_new_console" to detach a subprocess Process.php to include support for create_new_console (Windows ONLY) | Q | A | ------------- | --- | Branch? | master <!-- see below --> | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | #36583 <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT | Doc PR | symfony/symfony-docs#... <!-- required for new features --> Based on this issue: #36583 I created a quick repo [ https://github.com/andrei0x309/tets_sym_proc]( https://github.com/andrei0x309/tets_sym_proc) to illustrate how this feature can be used, it essentially lets you run something even if your main script has terminated. It is useful if you want to create something like `new Process(['php', 'script_with_long_execution_time.php']);` This was impossible to do on windows until the **create_new_console** flag was added in PHP 7.4.4. With this feature Process can be used like this: ``` // Start a process and detach form it on Win only $process = new Process(['php', 'worker.php']); $process->setOptions(["create_new_console" => true]); // New method I added $process->disableOutput(); $process->start(); Process Class behavior will never change if the user doesn't use setWinOptions() ``` Commits ------- e9ab235 [Process] allow setting options esp. "create_new_console" to detach a subprocess
2 parents 3ba965a + e9ab235 commit 4b2b64a

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

src/Symfony/Component/Process/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
5.2.0
5+
-----
6+
7+
* added `Process::setOptions()` to set `Process` specific options
8+
* added option `create_new_console` to allow a subprocess to continue
9+
to run after the main script exited, both on Linux and on Windows
10+
411
5.1.0
512
-----
613

src/Symfony/Component/Process/Process.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Process\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Process\Process;
16+
17+
/**
18+
* @author Andrei Olteanu <andrei@flashsoft.eu>
19+
*/
20+
class CreateNewConsoleTest extends TestCase
21+
{
22+
public function testOptionCreateNewConsole()
23+
{
24+
$this->expectNotToPerformAssertions();
25+
try {
26+
$process = new Process(['php', __DIR__.'/ThreeSecondProcess.php']);
27+
$process->setOptions(['create_new_console' => true]);
28+
$process->disableOutput();
29+
$process->start();
30+
} catch (\Exception $e) {
31+
$this->fail($e);
32+
}
33+
}
34+
35+
public function testItReturnsFastAfterStart()
36+
{
37+
// The started process must run in background after the main has finished but that can't be tested with PHPUnit
38+
$startTime = microtime(true);
39+
$process = new Process(['php', __DIR__.'/ThreeSecondProcess.php']);
40+
$process->setOptions(['create_new_console' => true]);
41+
$process->disableOutput();
42+
$process->start();
43+
$this->assertLessThan(3000, $startTime - microtime(true));
44+
}
45+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
echo 'Worker started';
13+
sleep(3);
14+
echo 'Worker done';

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy