diff --git a/src/Symfony/Component/Console/Output/ConsoleOutput.php b/src/Symfony/Component/Console/Output/ConsoleOutput.php index f666c793e2265..007f3f01be336 100644 --- a/src/Symfony/Component/Console/Output/ConsoleOutput.php +++ b/src/Symfony/Component/Console/Output/ConsoleOutput.php @@ -14,15 +14,16 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface; /** - * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR. * - * This class is a convenient wrapper around `StreamOutput`. + * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR. * * $output = new ConsoleOutput(); * * This is equivalent to: * * $output = new StreamOutput(fopen('php://stdout', 'w')); + * $stdErr = new StreamOutput(fopen('php://stderr', 'w')); * * @author Fabien Potencier */ @@ -139,9 +140,11 @@ function_exists('php_uname') ? php_uname('s') : '', */ private function openOutputStream() { - $outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output'; + if (!$this->hasStdoutSupport()) { + return fopen('php://output', 'w'); + } - return @fopen($outputStream, 'w') ?: fopen('php://output', 'w'); + return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); } /** @@ -149,8 +152,6 @@ private function openOutputStream() */ private function openErrorStream() { - $errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output'; - - return fopen($errorStream, 'w'); + return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); } } diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php index 41af5c11f47da..54ba2f72cca55 100644 --- a/src/Symfony/Component/Console/Tester/ApplicationTester.php +++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php @@ -14,6 +14,7 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\StreamOutput; @@ -31,13 +32,13 @@ class ApplicationTester { private $application; private $input; - private $output; /** - * Constructor. - * - * @param Application $application An Application instance to test. + * @var OutputInterface */ + private $output; + private $captureStreamsIndependently = false; + public function __construct(Application $application) { $this->application = $application; @@ -48,9 +49,10 @@ public function __construct(Application $application) * * Available options: * - * * interactive: Sets the input interactive flag - * * decorated: Sets the output decorated flag - * * verbosity: Sets the output verbosity flag + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available * * @param array $input An array of arguments and options * @param array $options An array of options @@ -64,12 +66,35 @@ public function run(array $input, $options = array()) $this->input->setInteractive($options['interactive']); } - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) { - $this->output->setVerbosity($options['verbosity']); + $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, + isset($options['decorated']) ? $options['decorated'] : null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); } return $this->application->run($this->input, $this->output); @@ -95,6 +120,30 @@ public function getDisplay($normalize = false) return $display; } + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string + */ + public function getErrorOutput($normalize = false) + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + /** * Gets the input instance used by the last execution of the application. * diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 8e44b7ae17267..57460c0a4fcb3 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -411,8 +411,9 @@ public function testSetCatchExceptions() $tester = new ApplicationTester($application); $application->setCatchExceptions(true); - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag'); + $this->assertSame('', $tester->getDisplay(true)); $application->setCatchExceptions(false); try { @@ -461,22 +462,22 @@ public function testRenderException() ->will($this->returnValue(120)); $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception'); - $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true)); + $this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); - $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); $application->add(new \Foo3Command()); $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); $application->setAutoExit(false); @@ -485,8 +486,8 @@ public function testRenderException() ->will($this->returnValue(32)); $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); } /** @@ -504,11 +505,11 @@ public function testRenderExceptionWithDoubleWidthCharacters() }); $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - $tester->run(array('command' => 'foo'), array('decorated' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + $tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); $application->setAutoExit(false); @@ -519,8 +520,8 @@ public function testRenderExceptionWithDoubleWidthCharacters() throw new \Exception('コマンドの実行中にエラーが発生しました。'); }); $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); } public function testRun() @@ -622,8 +623,8 @@ public function testRunReturnsIntegerExitCode() $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); $application->setAutoExit(false); $application->expects($this->once()) - ->method('doRun') - ->will($this->throwException($exception)); + ->method('doRun') + ->will($this->throwException($exception)); $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); @@ -637,8 +638,8 @@ public function testRunReturnsExitCodeOneForExceptionCodeZero() $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); $application->setAutoExit(false); $application->expects($this->once()) - ->method('doRun') - ->will($this->throwException($exception)); + ->method('doRun') + ->will($this->throwException($exception)); $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); 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