diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 2aa155e753..a7af68fafa 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -295,7 +295,7 @@ class DateTypeTest extends TypeTestCase } /** - * This test is to check that the strings '0', '1', '2', '3' are no accepted + * This test is to check that the strings '0', '1', '2', '3' are not accepted * as valid IntlDateFormatter constants for FULL, LONG, MEDIUM or SHORT respectively. * * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index a1659c4fa5..6d54571f3d 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -912,6 +912,14 @@ class Request } } + if ($host = $this->headers->get('HOST')) { + if (false !== $pos = strrpos($host, ':')) { + return intval(substr($host, $pos + 1)); + } + + return 'https' === $this->getScheme() ? 443 : 80; + } + return $this->server->get('SERVER_PORT'); } @@ -1634,7 +1642,7 @@ class Request $seg = $segs[$index]; $baseUrl = '/'.$seg.$baseUrl; ++$index; - } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos)); + } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos); } // Does the baseUrl have anything in common with the request_uri? @@ -1651,7 +1659,7 @@ class Request } $truncatedRequestUri = $requestUri; - if (($pos = strpos($requestUri, '?')) !== false) { + if (false !== $pos = strpos($requestUri, '?')) { $truncatedRequestUri = substr($requestUri, 0, $pos); } @@ -1664,7 +1672,7 @@ class Request // If using mod_rewrite or ISAPI_Rewrite strip the script filename // out of baseUrl. $pos !== 0 makes sure it is not matching a value // from PATH_INFO or QUERY_STRING - if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) { + if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) { $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); } @@ -1717,7 +1725,7 @@ class Request $requestUri = substr($requestUri, 0, $pos); } - if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) { + if (null !== $baseUrl && false === $pathInfo = substr($requestUri, strlen($baseUrl))) { // If substr() returns false then PATH_INFO is set to an empty string return '/'; } elseif (null === $baseUrl) { diff --git a/src/Symfony/Component/HttpFoundation/ServerBag.php b/src/Symfony/Component/HttpFoundation/ServerBag.php index d7aadc6bb6..de6dbc9fb5 100644 --- a/src/Symfony/Component/HttpFoundation/ServerBag.php +++ b/src/Symfony/Component/HttpFoundation/ServerBag.php @@ -64,11 +64,17 @@ class ServerBag extends ParameterBag $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION']; } - // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic - if ((null !== $authorizationHeader) && (0 === stripos($authorizationHeader, 'basic'))) { - $exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); - if (count($exploded) == 2) { - list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + if (null !== $authorizationHeader) { + if (0 === stripos($authorizationHeader, 'basic')) { + // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); + if (count($exploded) == 2) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest'))) { + // In some circumstances PHP_AUTH_DIGEST needs to be set + $headers['PHP_AUTH_DIGEST'] = $authorizationHeader; + $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader; } } } @@ -76,6 +82,8 @@ class ServerBag extends ParameterBag // PHP_AUTH_USER/PHP_AUTH_PW if (isset($headers['PHP_AUTH_USER'])) { $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); + } elseif (isset($headers['PHP_AUTH_DIGEST'])) { + $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST']; } return $headers; diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 357176100b..cc14b235e1 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1566,6 +1566,18 @@ class RequestTest extends \PHPUnit_Framework_TestCase // trusted hosts $request->headers->set('host', 'trusted.com'); $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + + $request->server->set('HTTPS', true); + $request->headers->set('host', 'trusted.com'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(443, $request->getPort()); + $request->server->set('HTTPS', false); + + $request->headers->set('host', 'trusted.com:8000'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(8000, $request->getPort()); + $request->headers->set('host', 'subdomain.trusted.com'); $this->assertEquals('subdomain.trusted.com', $request->getHost()); diff --git a/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php index f8e487d7f8..89920f1fbc 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php @@ -89,6 +89,28 @@ class ServerBagTest extends \PHPUnit_Framework_TestCase ), $bag->getHeaders()); } + public function testHttpDigestAuthWithPhpCgi() + { + $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $digest, + 'PHP_AUTH_DIGEST' => $digest, + ), $bag->getHeaders()); + } + + public function testHttpDigestAuthWithPhpCgiRedirect() + { + $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; + $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $digest, + 'PHP_AUTH_DIGEST' => $digest, + ), $bag->getHeaders()); + } + public function testOAuthBearerAuth() { $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 87a978d772..7e2d118d49 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -57,15 +57,14 @@ class Process private $stderr; private $enhanceWindowsCompatibility; private $enhanceSigchildCompatibility; - private $pipes; private $process; private $status = self::STATUS_READY; private $incrementalOutputOffset; private $incrementalErrorOutputOffset; private $tty; - private $fileHandles; - private $readBytes; + private $useFileHandles = false; + private $processPipes; private static $sigchild; @@ -157,6 +156,7 @@ class Process } $this->stdin = $stdin; $this->setTimeout($timeout); + $this->useFileHandles = defined('PHP_WINDOWS_VERSION_BUILD'); $this->enhanceWindowsCompatibility = true; $this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled(); $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options); @@ -216,7 +216,7 @@ class Process * * @param callback|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR - * + * * @return Process The process itself * * @throws RuntimeException When process can't be launch or is stopped @@ -242,18 +242,15 @@ class Process } } - $this->process = proc_open($commandline, $descriptors, $this->pipes, $this->cwd, $this->env, $this->options); + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); if (!is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); } $this->status = self::STATUS_STARTED; - foreach ($this->pipes as $pipe) { - stream_set_blocking($pipe, false); - } - - $this->writePipes(); + $this->processPipes->unblock(); + $this->processPipes->write(false, $this->stdin); $this->updateStatus(false); $this->checkTimeout(); } @@ -305,24 +302,12 @@ class Process if (null !== $callback) { $this->callback = $this->buildCallback($callback); } - while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) { + while ($this->processInformation['running']) { $this->checkTimeout(); - $this->readPipes(true); + $this->updateStatus(true); } $this->updateStatus(false); - if ($this->processInformation['signaled']) { - if ($this->isSigchildEnabled()) { - throw new RuntimeException('The process has been signaled.'); - } - - throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); - } - - $time = 0; - while ($this->isRunning() && $time < 1000000) { - $time += 1000; - usleep(1000); - } + $this->processPipes->close(); if ($this->processInformation['signaled']) { if ($this->isSigchildEnabled()) { @@ -978,38 +963,10 @@ class Process */ private function getDescriptors() { - //Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. - //Workaround for this problem is to use temporary files instead of pipes on Windows platform. - //@see https://bugs.php.net/bug.php?id=51800 - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->fileHandles = array( - self::STDOUT => tmpfile(), - ); - if (false === $this->fileHandles[self::STDOUT]) { - throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); - } - $this->readBytes = array( - self::STDOUT => 0, - ); + $this->processPipes = new ProcessPipes($this->useFileHandles); + $descriptors = $this->processPipes->getDescriptors(); - return array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w')); - } - - if ($this->tty) { - $descriptors = array( - array('file', '/dev/tty', 'r'), - array('file', '/dev/tty', 'w'), - array('file', '/dev/tty', 'w'), - ); - } else { - $descriptors = array( - array('pipe', 'r'), // stdin - array('pipe', 'w'), // stdout - array('pipe', 'w'), // stderr - ); - } - - if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + if (!$this->useFileHandles && $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'))); @@ -1087,41 +1044,6 @@ class Process return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); } - /** - * Handles the windows file handles fallbacks. - * - * @param Boolean $closeEmptyHandles if true, handles that are empty will be assumed closed - */ - private function processFileHandles($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($this->callback, $type == 1 ? self::OUT : self::ERR, $data); - } - if (false === $data || ($closeEmptyHandles && '' === $data && feof($fileHandle))) { - fclose($fileHandle); - unset($this->fileHandles[$type]); - } - } - } - - /** - * Returns true if a system call has been interrupted. - * - * @return Boolean - */ - private function hasSystemCallBeenInterrupted() - { - $lastError = error_get_last(); - - // stream_select returns false when the `select` system call is interrupted by an incoming signal - return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); - } - /** * Validates and returns the filtered timeout. * @@ -1151,117 +1073,11 @@ class Process */ private function readPipes($blocking) { - if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) { - $this->processFileHandles(!$this->pipes); - } - - if ($this->pipes) { - $r = $this->pipes; - $w = null; - $e = null; - - // let's have a look if something changed in streams - if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(self::TIMEOUT_PRECISION * 1E6) : 0)) { - // if a system call has been interrupted, forget about it, let's try again - // otherwise, an error occured, let's reset pipes - if (!$this->hasSystemCallBeenInterrupted()) { - $this->pipes = array(); - } - - return; - } - - // nothing has changed - if (0 === $n) { - return; - } - - $this->processReadPipes($r); - } - } - - /** - * Writes data to pipes. - */ - private function writePipes() - { - if ($this->tty) { - $this->status = self::STATUS_TERMINATED; - - return; - } - - if (null === $this->stdin) { - fclose($this->pipes[0]); - unset($this->pipes[0]); - - return; - } - - $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(); - } - - $r = $this->pipes; - $w = $writePipes; - $e = null; - - if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(static::TIMEOUT_PRECISION * 1E6) : 0)) { - // if a system call has been interrupted, forget about it, let's try again - if ($this->hasSystemCallBeenInterrupted()) { - continue; - } - break; - } - - // nothing has changed, let's wait until the process is ready - if (0 === $n) { - continue; - } - - if ($w) { - $written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192); - if (false !== $written) { - $stdinOffset += $written; - } - if ($stdinOffset >= $stdinLen) { - fclose($writePipes[0]); - $writePipes = null; - } - } - - $this->processReadPipes($r); - } - } - - /** - * Processes read pipes, executes callback on it. - * - * @param array $pipes - */ - private function processReadPipes(array $pipes) - { - foreach ($pipes as $pipe) { - $type = array_search($pipe, $this->pipes); - $data = fread($pipe, 8192); - - if (strlen($data) > 0) { - // last exit code is output and caught to work around --enable-sigchild - if (3 == $type) { - $this->fallbackExitcode = (int) $data; - } else { - call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data); - } - } - if (false === $data || feof($pipe)) { - fclose($pipe); - unset($this->pipes[$type]); + foreach ($this->processPipes->read($blocking) as $type => $data) { + if (3 == $type) { + $this->fallbackExitcode = (int) $data; + } else { + call_user_func($this->callback, $type === self::STDOUT ? self::OUT : self::ERR, $data); } } } @@ -1284,11 +1100,7 @@ class Process */ private function close() { - foreach ($this->pipes as $pipe) { - fclose($pipe); - } - - $this->pipes = null; + $this->processPipes->close(); $exitcode = -1; if (is_resource($this->process)) { @@ -1305,13 +1117,6 @@ class Process $this->exitcode = 128 + $this->processInformation['termsig']; } - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - foreach ($this->fileHandles as $fileHandle) { - fclose($fileHandle); - } - $this->fileHandles = array(); - } - return $this->exitcode; } @@ -1327,11 +1132,8 @@ class Process $this->processInformation = null; $this->stdout = null; $this->stderr = null; - $this->pipes = null; $this->process = null; $this->status = self::STATUS_READY; - $this->fileHandles = null; - $this->readBytes = null; $this->incrementalOutputOffset = 0; $this->incrementalErrorOutputOffset = 0; } diff --git a/src/Symfony/Component/Process/ProcessPipes.php b/src/Symfony/Component/Process/ProcessPipes.php new file mode 100644 index 0000000000..aa6eb659f1 --- /dev/null +++ b/src/Symfony/Component/Process/ProcessPipes.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * ProcessPipes manages descriptors and pipes for the use of proc_open. + */ +class ProcessPipes +{ + /** @var array */ + public $pipes = array(); + /** @var array */ + private $fileHandles = array(); + /** @var array */ + private $readBytes = array(); + /** @var Boolean */ + private $useFiles; + + public function __construct($useFiles = false) + { + $this->useFiles = (Boolean) $useFiles; + + // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. + // Workaround for this problem is to use temporary files instead of pipes on Windows platform. + // + // Please note that this work around prevents hanging but + // another issue occurs : In some race conditions, some data may be + // lost or corrupted. + // + // @see https://bugs.php.net/bug.php?id=51800 + if ($this->useFiles) { + $this->fileHandles = array( + Process::STDOUT => tmpfile(), + Process::STDERR => tmpfile(), + ); + if (false === $this->fileHandles[Process::STDOUT]) { + throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); + } + if (false === $this->fileHandles[Process::STDERR]) { + throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); + } + $this->readBytes = array( + Process::STDOUT => 0, + Process::STDERR => 0, + ); + } + } + + public function __destruct() + { + $this->close(); + } + + /** + * Sets non-blocking mode on pipes. + */ + public function unblock() + { + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, 0); + } + } + + /** + * Closes file handles and pipes. + */ + public function close() + { + foreach ($this->pipes as $offset => $pipe) { + fclose($pipe); + } + + foreach ($this->fileHandles as $offset => $handle) { + fclose($handle); + } + $this->fileHandles = $this->pipes = array(); + } + + /** + * Returns an array of descriptors for the use of proc_open. + * + * @return array + */ + public function getDescriptors() + { + if ($this->useFiles) { + return array( + array('pipe', 'r'), + $this->fileHandles[Process::STDOUT], + $this->fileHandles[Process::STDERR], + ); + } + + return array( + array('pipe', 'r'), // stdin + array('pipe', 'w'), // stdout + array('pipe', 'w'), // stderr + ); + } + + /** + * Reads data in file handles and pipes. + * + * @param Boolean $blocking Whether to use blocking calls or not. + * + * @return array An array of read data indexed by their fd. + */ + public function read($blocking) + { + return array_replace($this->readStreams($blocking), $this->readFileHandles()); + } + + /** + * Writes stdin data. + * + * @param Boolean $blocking Whether to use blocking calls or not. + * @param string $stdin The data to write. + */ + public function write($blocking, $stdin) + { + if (null === $stdin) { + fclose($this->pipes[0]); + unset($this->pipes[0]); + + return; + } + + $writePipes = array($this->pipes[0]); + unset($this->pipes[0]); + $stdinLen = strlen($stdin); + $stdinOffset = 0; + + while ($writePipes) { + $r = null; + $w = $writePipes; + $e = null; + + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { + // if a system call has been interrupted, forget about it, let's try again + if ($this->hasSystemCallBeenInterrupted()) { + continue; + } + break; + } + + // nothing has changed, let's wait until the process is ready + if (0 === $n) { + continue; + } + + if ($w) { + $written = fwrite($writePipes[0], (binary) substr($stdin, $stdinOffset), 8192); + if (false !== $written) { + $stdinOffset += $written; + } + if ($stdinOffset >= $stdinLen) { + fclose($writePipes[0]); + $writePipes = null; + } + } + } + } + + /** + * Reads data in file handles. + * + * @return array An array of read data indexed by their fd. + */ + private function readFileHandles() + { + $read = array(); + + foreach ($this->fileHandles as $type => $fileHandle) { + fseek($fileHandle, $this->readBytes[$type]); + $data = ''; + while (!feof($fileHandle)) { + $data .= fread($fileHandle, 8192); + } + if (0 < $length = strlen($data)) { + $this->readBytes[$type] += $length; + $read[$type] = $data; + } + } + + return $read; + } + + /** + * Reads data in file pipes streams. + * + * @param Boolean $blocking Whether to use blocking calls or not. + * + * @return array An array of read data indexed by their fd. + */ + private function readStreams($blocking) + { + $read = array(); + + $r = $this->pipes; + $w = null; + $e = null; + + // let's have a look if something changed in streams + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { + // if a system call has been interrupted, forget about it, let's try again + // otherwise, an error occured, let's reset pipes + if (!$this->hasSystemCallBeenInterrupted()) { + $this->pipes = array(); + } + + return $read; + } + + // nothing has changed + if (0 === $n) { + return $read; + } + + foreach ($r as $pipe) { + $type = array_search($pipe, $this->pipes); + $data = fread($pipe, 8192); + + if (strlen($data) > 0) { + $read[$type] = $data; + } + } + + return $read; + } + + /** + * Returns true if a system call has been interrupted. + * + * @return Boolean + */ + private function hasSystemCallBeenInterrupted() + { + $lastError = error_get_last(); + + // stream_select returns false when the `select` system call is interrupted by an incoming signal + return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); + } +} diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index d4fda22ae3..4eab4dc820 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -70,17 +70,16 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase { $data = ''; - $process = $this->getProcess('echo "foo";sleep 1;echo "foo"'); + $process = $this->getProcess('echo foo && php -r "sleep(1);" && echo foo'); $process->start(function ($type, $buffer) use (&$data) { $data .= $buffer; }); - $start = microtime(true); while ($process->isRunning()) { usleep(10000); } - $this->assertEquals("foo\nfoo\n", $data); + $this->assertEquals(2, preg_match_all('/foo/', $data, $matches)); } /** @@ -120,6 +119,12 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase public function chainedCommandsOutputProvider() { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + return array( + array("2 \r\n2\r\n", '&&', '2') + ); + } + return array( array("1\n1\n", ';', '1'), array("2\n2\n", '&&', '2'), @@ -132,10 +137,6 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase */ public function testChainedCommandsOutput($expected, $operator, $input) { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Does it work on windows ?'); - } - $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input)); $process->run(); $this->assertEquals($expected, $process->getOutput()); @@ -174,7 +175,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase public function testGetOutput() { - $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}'))); + $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }'))); $p->run(); $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches)); @@ -309,7 +310,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase public function testIsSuccessfulOnlyAfterTerminated() { - $process = $this->getProcess('sleep 1'); + $process = $this->getProcess('php -r "sleep(1);"'); $process->start(); while ($process->isRunning()) { $this->assertFalse($process->isSuccessful()); @@ -442,7 +443,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase public function testRunProcessWithTimeout() { $timeout = 0.5; - $process = $this->getProcess('sleep 3'); + $process = $this->getProcess('php -r "sleep(3);"'); $process->setTimeout($timeout); $start = microtime(true); try { @@ -460,7 +461,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase { $timeout = 0.5; $precision = 100000; - $process = $this->getProcess('sleep 3'); + $process = $this->getProcess('php -r "sleep(3);"'); $process->setTimeout($timeout); $start = microtime(true); diff --git a/src/Symfony/Component/Process/Tests/ProcessTestHelper.php b/src/Symfony/Component/Process/Tests/ProcessTestHelper.php index 25cfb41f93..cdc75255e7 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTestHelper.php +++ b/src/Symfony/Component/Process/Tests/ProcessTestHelper.php @@ -8,9 +8,9 @@ define('ERR_WRITE_FAILED', 4); $read = array(STDIN); $write = array(STDOUT, STDERR); -stream_set_blocking(STDIN, false); -stream_set_blocking(STDOUT, false); -stream_set_blocking(STDERR, false); +stream_set_blocking(STDIN, 0); +stream_set_blocking(STDOUT, 0); +stream_set_blocking(STDERR, 0); $out = $err = ''; while ($read || $write) { @@ -26,7 +26,7 @@ while ($read || $write) { } if (in_array(STDOUT, $w) && strlen($out) > 0) { - $written = fwrite(STDOUT, (binary) $out, 1024); + $written = fwrite(STDOUT, (binary) $out, 32768); if (false === $written) { die(ERR_WRITE_FAILED); } @@ -37,7 +37,7 @@ while ($read || $write) { } if (in_array(STDERR, $w) && strlen($err) > 0) { - $written = fwrite(STDERR, (binary) $err, 1024); + $written = fwrite(STDERR, (binary) $err, 32768); if (false === $written) { die(ERR_WRITE_FAILED); } @@ -48,7 +48,7 @@ while ($read || $write) { } if ($r) { - $str = fread(STDIN, 1024); + $str = fread(STDIN, 32768); if (false !== $str) { $out .= $str; $err .= $str;