Skip to content

Commit

Permalink
Add a Sigchild compatibility mode (set to false by default)
Browse files Browse the repository at this point in the history
This mode is required to use a hack to determine if the process finished with success when PHP has been compiled with the --enable-sigchild option
  • Loading branch information
romainneutron committed Sep 18, 2012
1 parent 5a4a73e commit 7bafc69
Show file tree
Hide file tree
Showing 6 changed files with 441 additions and 32 deletions.
88 changes: 81 additions & 7 deletions src/Symfony/Component/Process/Process.php
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Process;

use Symfony\Component\Process\Exception\RuntimeException;

/**
* Process is a thin wrapper around proc_* functions to ease
* start independent PHP processes.
Expand Down Expand Up @@ -44,13 +46,16 @@ class Process
private $stdout;
private $stderr;
private $enhanceWindowsCompatibility;
private $enhanceSigchildCompatibility;
private $pipes;
private $process;
private $status = self::STATUS_READY;

private $fileHandles;
private $readBytes;

private static $sigchild;

/**
* Exit codes translation table.
*
Expand Down Expand Up @@ -134,6 +139,7 @@ public function __construct($commandline, $cwd = null, array $env = null, $stdin
$this->stdin = $stdin;
$this->setTimeout($timeout);
$this->enhanceWindowsCompatibility = true;
$this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled();
$this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
}

Expand Down Expand Up @@ -216,9 +222,16 @@ public function start($callback = null)
array('pipe', 'r'), // stdin
array('pipe', 'w'), // stdout
array('pipe', 'w'), // stderr
array('pipe', 'w') // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
);
$this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';

if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
// last exit code is output on the fourth pipe and caught to work around --enable-sigchild
$descriptors = array_merge($descriptors, array(array('pipe', 'w')));

$this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';
} else {
$this->commandline = 'exec ' . $this->commandline;
}
}

$commandline = $this->commandline;
Expand Down Expand Up @@ -418,10 +431,16 @@ public function getErrorOutput()
*
* @return integer The exit status code
*
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
*
* @api
*/
public function getExitCode()
{
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method');
}

$this->updateStatus();

return $this->exitcode;
Expand All @@ -435,28 +454,30 @@ public function getExitCode()
*
* @return string A string representation for the exit status code
*
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
*
* @see http://tldp.org/LDP/abs/html/exitcodes.html
* @see http://en.wikipedia.org/wiki/Unix_signal
*/
public function getExitCodeText()
{
$this->updateStatus();
$exitcode = $this->getExitCode();

return isset(self::$exitCodes[$this->exitcode]) ? self::$exitCodes[$this->exitcode] : 'Unknown error';
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
}

/**
* Checks if the process ended successfully.
*
* @return Boolean true if the process ended successfully, false otherwise
*
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
*
* @api
*/
public function isSuccessful()
{
$this->updateStatus();

return 0 == $this->exitcode;
return 0 == $this->getExitCode();
}

/**
Expand All @@ -466,10 +487,16 @@ public function isSuccessful()
*
* @return Boolean
*
* @throws RuntimeException In case --enable-sigchild is activated
*
* @api
*/
public function hasBeenSignaled()
{
if ($this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
}

$this->updateStatus();

return $this->processInformation['signaled'];
Expand All @@ -482,10 +509,16 @@ public function hasBeenSignaled()
*
* @return integer
*
* @throws RuntimeException In case --enable-sigchild is activated
*
* @api
*/
public function getTermSignal()
{
if ($this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
}

$this->updateStatus();

return $this->processInformation['termsig'];
Expand Down Expand Up @@ -678,6 +711,30 @@ public function setEnhanceWindowsCompatibility($enhance)
$this->enhanceWindowsCompatibility = (Boolean) $enhance;
}

/**
* Return whether sigchild compatibility mode is activated or not
*
* @return Boolean
*/
public function getEnhanceSigchildCompatibility()
{
return $this->enhanceSigchildCompatibility;
}

/**
* Activate sigchild compatibility mode
*
* Sigchild compatibility mode is required to get the exit code and
* determine the success of a process when PHP has been compiled with
* the --enable-sigchild option
*
* @param Boolean $enhance
*/
public function setEnhanceSigchildCompatibility($enhance)
{
$this->enhanceSigchildCompatibility = (Boolean) $enhance;
}

/**
* Builds up the callback used by wait().
*
Expand Down Expand Up @@ -743,6 +800,23 @@ protected function updateOutput()
}
}

/**
* Return whether PHP has been compiled with the '--enable-sigchild' option or not
*
* @return Boolean
*/
protected function isSigchildEnabled()
{
if (null !== self::$sigchild) {
return self::$sigchild;
}

ob_start();
phpinfo(INFO_GENERAL);

return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
}

/**
* Handles the windows file handles fallbacks
*
Expand Down

0 comments on commit 7bafc69

Please sign in to comment.