merged branch Seldaek/process-win-fix (PR #4400)

Commits
-------

bcf8cf9 [Process] Refactor the windows handling so it is always executed
0c5aa8b [Process] Add failing test for callbacks on windows

Discussion
----------

[Process] Windows callback handling fix

---------------------------------------------------------------------------

by travisbot at 2012-05-24T18:10:56Z

This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1424957) (merged bcf8cf91 into e2c36c54).
This commit is contained in:
Fabien Potencier 2012-05-25 09:28:10 +02:00
commit 48ccdaa31b
2 changed files with 74 additions and 60 deletions

View File

@ -206,6 +206,9 @@ class Process
$this->fileHandles = array(
self::STDOUT => tmpfile(),
);
$this->readBytes = array(
self::STDOUT => 0,
);
$descriptors = array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w'));
} else {
$descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
@ -236,14 +239,18 @@ class Process
unset($this->pipes[0]);
return;
} else {
$writePipes = array($this->pipes[0]);
unset($this->pipes[0]);
$stdinLen = strlen($this->stdin);
$stdinOffset = 0;
}
$writePipes = array($this->pipes[0]);
unset($this->pipes[0]);
$stdinLen = strlen($this->stdin);
$stdinOffset = 0;
while ($writePipes) {
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->processFileHandles($callback);
}
$r = $this->pipes;
$w = $writePipes;
$e = null;
@ -252,7 +259,8 @@ class Process
if (false === $n) {
break;
} elseif ($n === 0) {
}
if ($n === 0) {
proc_terminate($this->process);
throw new \RuntimeException('The process timed out.');
@ -269,22 +277,6 @@ class Process
}
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$fh = $this->fileHandles;
foreach ($fh as $type => $fileHandle) {
fseek($fileHandle, 0);
$data = fread($fileHandle, 8192);
$this->readBytes[$type] = strlen($data);
if (strlen($data) > 0) {
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
}
if (false === $data) {
fclose($fileHandle);
unset($this->fileHandles[$type]);
}
}
}
foreach ($r as $pipe) {
$type = array_search($pipe, $this->pipes);
$data = fread($pipe, 8192);
@ -319,53 +311,40 @@ class Process
$this->processInformation = proc_get_status($this->process);
$callback = $this->buildCallback($callback);
while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
$r = $this->pipes;
$w = null;
$e = null;
$n = @stream_select($r, $w, $e, $this->timeout);
if (false === $n) {
break;
}
if (0 === $n) {
proc_terminate($this->process);
throw new \RuntimeException('The process timed out.');
if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) {
$this->processFileHandles($callback, !$this->pipes);
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$fh = $this->fileHandles;
foreach ($fh as $type => $fileHandle) {
fseek($fileHandle, $this->readBytes[$type]);
$data = fread($fileHandle, 8192);
if (isset($this->readBytes)) {
$this->readBytes[$type] += strlen($data);
} else {
$this->readBytes[$type] = strlen($data);
}
if ($this->pipes) {
$r = $this->pipes;
$w = null;
$e = null;
$n = @stream_select($r, $w, $e, $this->timeout);
if (false === $n) {
$this->pipes = array();
continue;
}
if (0 === $n) {
proc_terminate($this->process);
throw new \RuntimeException('The process timed out.');
}
foreach ($r as $pipe) {
$type = array_search($pipe, $this->pipes);
$data = fread($pipe, 8192);
if (strlen($data) > 0) {
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
}
if (false === $data) {
fclose($fileHandle);
unset($this->fileHandles[$type]);
if (false === $data || feof($pipe)) {
fclose($pipe);
unset($this->pipes[$type]);
}
}
}
foreach ($r as $pipe) {
$type = array_search($pipe, $this->pipes);
$data = fread($pipe, 8192);
if (strlen($data) > 0) {
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
}
if (false === $data || feof($pipe)) {
fclose($pipe);
unset($this->pipes[$type]);
}
}
}
$this->updateStatus();
if ($this->processInformation['signaled']) {
@ -725,4 +704,27 @@ class Process
$this->addOutput(stream_get_contents($this->pipes[self::STDOUT]));
}
}
/**
* Handles the windows file handles fallbacks
*
* @param mixed $callback A valid PHP callback
* @param Boolean $closeEmptyHandles if true, handles that are empty will be assumed closed
*/
private function processFileHandles($callback, $closeEmptyHandles = false)
{
$fh = $this->fileHandles;
foreach ($fh as $type => $fileHandle) {
fseek($fileHandle, $this->readBytes[$type]);
$data = fread($fileHandle, 8192);
if (strlen($data) > 0) {
$this->readBytes[$type] += strlen($data);
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
}
if (false === $data || ($closeEmptyHandles && '' === $data && feof($fileHandle))) {
fclose($fileHandle);
unset($this->fileHandles[$type]);
}
}
}
}

View File

@ -68,6 +68,18 @@ class ProcessTest extends \PHPUnit_Framework_TestCase
$this->assertSame(0, $p->getExitCode());
}
public function testCallbackIsExecutedForOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('echo \'foo\';')));
$called = false;
$p->run(function ($type, $buffer) use (&$called) {
$called = $buffer === 'foo';
});
$this->assertTrue($called, 'The callback should be executed with the output');
}
public function testExitCodeText()
{
$process = new Process('');