Skip to content

Commit 20c7f33

Browse files
authored
Merge pull request #103 from clue-labs/no-sigchild
Remove legacy sigchild compatibility layer
2 parents 6fc338f + 49d7c28 commit 20c7f33

File tree

3 files changed

+11
-176
lines changed

3 files changed

+11
-176
lines changed

README.md

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ as [Streams](https://github.com/reactphp/stream).
3030
* [Command](#command)
3131
* [Termination](#termination)
3232
* [Custom pipes](#custom-pipes)
33-
* [Sigchild Compatibility](#sigchild-compatibility)
3433
* [Windows Compatibility](#windows-compatibility)
3534
* [Install](#install)
3635
* [Tests](#tests)
@@ -262,8 +261,7 @@ $process->on('exit', function ($code, $term) {
262261
```
263262

264263
Note that `$code` is `null` if the process has terminated, but the exit
265-
code could not be determined (for example
266-
[sigchild compatibility](#sigchild-compatibility) was disabled).
264+
code could not be determined.
267265
Similarly, `$term` is `null` unless the process has terminated in response to
268266
an uncaught signal sent to it.
269267
This is not a limitation of this project, but actual how exit codes and signals
@@ -387,43 +385,6 @@ number of pipes and additional file descriptors, but many common applications
387385
being run as a child process will expect that the parent process properly
388386
assigns these file descriptors.
389387

390-
### Sigchild Compatibility
391-
392-
Internally, this project uses a work-around to improve compatibility when PHP
393-
has been compiled with the `--enable-sigchild` option. This should not affect most
394-
installations as this configure option is not used by default and many
395-
distributions (such as Debian and Ubuntu) are known to not use this by default.
396-
Some installations that use [Oracle OCI8](http://php.net/manual/en/book.oci8.php)
397-
may use this configure option to circumvent `defunct` processes.
398-
399-
When PHP has been compiled with the `--enable-sigchild` option, a child process'
400-
exit code cannot be reliably determined via `proc_close()` or `proc_get_status()`.
401-
To work around this, we execute the child process with an additional pipe and
402-
use that to retrieve its exit code.
403-
404-
This work-around incurs some overhead, so we only trigger this when necessary
405-
and when we detect that PHP has been compiled with the `--enable-sigchild` option.
406-
Because PHP does not provide a way to reliably detect this option, we try to
407-
inspect output of PHP's configure options from the `phpinfo()` function.
408-
409-
The static `setSigchildEnabled(bool $sigchild): void` method can be used to
410-
explicitly enable or disable this behavior like this:
411-
412-
```php
413-
// advanced: not recommended by default
414-
Process::setSigchildEnabled(true);
415-
```
416-
417-
Note that all processes instantiated after this method call will be affected.
418-
If this work-around is disabled on an affected PHP installation, the `exit`
419-
event may receive `null` instead of the actual exit code as described above.
420-
Similarly, some distributions are known to omit the configure options from
421-
`phpinfo()`, so automatic detection may fail to enable this work-around in some
422-
cases. You may then enable this explicitly as given above.
423-
424-
**Note:** The original functionality was taken from Symfony's
425-
[Process](https://github.com/symfony/process) compoment.
426-
427388
### Windows Compatibility
428389

429390
Due to platform constraints, this library provides only limited support for
@@ -607,6 +568,14 @@ This project aims to run on any platform and thus does not require any PHP
607568
extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM.
608569
It's *highly recommended to use the latest supported PHP version* for this project.
609570

571+
Note that legacy platforms that use PHP compiled with the legacy `--enable-sigchild`
572+
option may not reliably determine the child process' exit code in some cases.
573+
This should not affect most installations as this configure option is not used
574+
by default and most distributions (such as Debian and Ubuntu) are known to not
575+
use this by default. This option may be used on some installations that use
576+
[Oracle OCI8](https://www.php.net/manual/en/book.oci8.php), see `phpinfo()` output
577+
to check if your installation might be affected.
578+
610579
See above note for limited [Windows Compatibility](#windows-compatibility).
611580

612581
## Tests

src/Process.php

Lines changed: 2 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
/**
1616
* Process component.
1717
*
18-
* This class borrows logic from Symfony's Process component for ensuring
19-
* compatibility when PHP is compiled with the --enable-sigchild option.
20-
*
2118
* This class also implements the `EventEmitterInterface`
2219
* which allows you to react to certain events:
2320
*
@@ -40,8 +37,7 @@
4037
* ```
4138
*
4239
* Note that `$code` is `null` if the process has terminated, but the exit
43-
* code could not be determined (for example
44-
* [sigchild compatibility](#sigchild-compatibility) was disabled).
40+
* code could not be determined.
4541
* Similarly, `$term` is `null` unless the process has terminated in response to
4642
* an uncaught signal sent to it.
4743
* This is not a limitation of this project, but actual how exit codes and signals
@@ -91,18 +87,13 @@ class Process extends EventEmitter
9187
private $env;
9288
private $fds;
9389

94-
private $enhanceSigchildCompatibility;
95-
private $sigchildPipe;
96-
9790
private $process;
9891
private $status;
9992
private $exitCode;
10093
private $fallbackExitCode;
10194
private $stopSignal;
10295
private $termSignal;
10396

104-
private static $sigchild;
105-
10697
/**
10798
* Constructor.
10899
*
@@ -145,7 +136,6 @@ public function __construct($cmd, $cwd = null, array $env = null, array $fds = n
145136
}
146137

147138
$this->fds = $fds;
148-
$this->enhanceSigchildCompatibility = self::isSigchildEnabled();
149139
}
150140

151141
/**
@@ -173,23 +163,6 @@ public function start(LoopInterface $loop = null, $interval = 0.1)
173163
$loop = $loop ?: Loop::get();
174164
$cmd = $this->cmd;
175165
$fdSpec = $this->fds;
176-
$sigchild = null;
177-
178-
// Read exit code through fourth pipe to work around --enable-sigchild
179-
if ($this->enhanceSigchildCompatibility) {
180-
$fdSpec[] = array('pipe', 'w');
181-
\end($fdSpec);
182-
$sigchild = \key($fdSpec);
183-
184-
// make sure this is fourth or higher (do not mess with STDIO)
185-
if ($sigchild < 3) {
186-
$fdSpec[3] = $fdSpec[$sigchild];
187-
unset($fdSpec[$sigchild]);
188-
$sigchild = 3;
189-
}
190-
191-
$cmd = \sprintf('(%s) ' . $sigchild . '>/dev/null; code=$?; echo $code >&' . $sigchild . '; exit $code', $cmd);
192-
}
193166

194167
// on Windows, we do not launch the given command line in a shell (cmd.exe) by default and omit any error dialogs
195168
// the cmd.exe shell can explicitly be given as part of the command as detailed in both documentation and tests
@@ -242,11 +215,6 @@ public function start(LoopInterface $loop = null, $interval = 0.1)
242215
});
243216
};
244217

245-
if ($sigchild !== null) {
246-
$this->sigchildPipe = $pipes[$sigchild];
247-
unset($pipes[$sigchild]);
248-
}
249-
250218
foreach ($pipes as $n => $fd) {
251219
// use open mode from stream meta data or fall back to pipe open mode for legacy HHVM
252220
$meta = \stream_get_meta_data($fd);
@@ -292,11 +260,6 @@ public function close()
292260
$pipe->close();
293261
}
294262

295-
if ($this->enhanceSigchildCompatibility) {
296-
$this->pollExitCodePipe();
297-
$this->closeExitCodePipe();
298-
}
299-
300263
$exitCode = \proc_close($this->process);
301264
$this->process = null;
302265

@@ -350,7 +313,7 @@ public function getCommand()
350313
* will be returned if the process is still running.
351314
*
352315
* Null may also be returned if the process has terminated, but the exit
353-
* code could not be determined (e.g. sigchild compatibility was disabled).
316+
* code could not be determined.
354317
*
355318
* @return int|null
356319
*/
@@ -437,85 +400,6 @@ public function isTerminated()
437400
return $status !== null ? $status['signaled'] : false;
438401
}
439402

440-
/**
441-
* Return whether PHP has been compiled with the '--enable-sigchild' option.
442-
*
443-
* @see \Symfony\Component\Process\Process::isSigchildEnabled()
444-
* @return bool
445-
*/
446-
public final static function isSigchildEnabled()
447-
{
448-
if (null !== self::$sigchild) {
449-
return self::$sigchild;
450-
}
451-
452-
if (!\function_exists('phpinfo')) {
453-
return self::$sigchild = false; // @codeCoverageIgnore
454-
}
455-
456-
\ob_start();
457-
\phpinfo(INFO_GENERAL);
458-
459-
return self::$sigchild = false !== \strpos(\ob_get_clean(), '--enable-sigchild');
460-
}
461-
462-
/**
463-
* Enable or disable sigchild compatibility mode.
464-
*
465-
* Sigchild compatibility mode is required to get the exit code and
466-
* determine the success of a process when PHP has been compiled with
467-
* the --enable-sigchild option.
468-
*
469-
* @param bool $sigchild
470-
* @return void
471-
*/
472-
public final static function setSigchildEnabled($sigchild)
473-
{
474-
self::$sigchild = (bool) $sigchild;
475-
}
476-
477-
/**
478-
* Check the fourth pipe for an exit code.
479-
*
480-
* This should only be used if --enable-sigchild compatibility was enabled.
481-
*/
482-
private function pollExitCodePipe()
483-
{
484-
if ($this->sigchildPipe === null) {
485-
return;
486-
}
487-
488-
$r = array($this->sigchildPipe);
489-
$w = $e = null;
490-
491-
$n = @\stream_select($r, $w, $e, 0);
492-
493-
if (1 !== $n) {
494-
return;
495-
}
496-
497-
$data = \fread($r[0], 8192);
498-
499-
if (\strlen($data) > 0) {
500-
$this->fallbackExitCode = (int) $data;
501-
}
502-
}
503-
504-
/**
505-
* Close the fourth pipe used to relay an exit code.
506-
*
507-
* This should only be used if --enable-sigchild compatibility was enabled.
508-
*/
509-
private function closeExitCodePipe()
510-
{
511-
if ($this->sigchildPipe === null) {
512-
return;
513-
}
514-
515-
\fclose($this->sigchildPipe);
516-
$this->sigchildPipe = null;
517-
}
518-
519403
/**
520404
* Return the cached process status.
521405
*

tests/AbstractProcessTest.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -246,24 +246,6 @@ public function testGetTermSignalWhenRunning($process)
246246
$this->assertNull($process->getTermSignal());
247247
}
248248

249-
public function testCommandWithEnhancedSigchildCompatibilityReceivesExitCode()
250-
{
251-
if (DIRECTORY_SEPARATOR === '\\') {
252-
$this->markTestSkipped('Process pipes not supported on Windows');
253-
}
254-
255-
$loop = $this->createLoop();
256-
$old = Process::isSigchildEnabled();
257-
Process::setSigchildEnabled(true);
258-
$process = new Process('echo foo');
259-
$process->start($loop);
260-
Process::setSigchildEnabled($old);
261-
262-
$loop->run();
263-
264-
$this->assertEquals(0, $process->getExitCode());
265-
}
266-
267249
public function testReceivesProcessStdoutFromEcho()
268250
{
269251
if (DIRECTORY_SEPARATOR === '\\') {

0 commit comments

Comments
 (0)