Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a5d522a

Browse files
committed
Add option to capture stderr and stdout separately.
1 parent 5d8067f commit a5d522a

File tree

3 files changed

+95
-44
lines changed

3 files changed

+95
-44
lines changed

src/Symfony/Component/Console/Output/ConsoleOutput.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@
1414
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
1515

1616
/**
17-
* ConsoleOutput is the default class for all CLI output. It uses STDOUT.
17+
* ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR.
1818
*
19-
* This class is a convenient wrapper around `StreamOutput`.
19+
* This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR.
2020
*
2121
* $output = new ConsoleOutput();
2222
*
2323
* This is equivalent to:
2424
*
2525
* $output = new StreamOutput(fopen('php://stdout', 'w'));
26+
* $stdErr = new StreamOutput(fopen('php://stderr', 'w'));
2627
*
2728
* @author Fabien Potencier <[email protected]>
2829
*/
@@ -139,18 +140,18 @@ function_exists('php_uname') ? php_uname('s') : '',
139140
*/
140141
private function openOutputStream()
141142
{
142-
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
143+
if (!$this->hasStdoutSupport()) {
144+
return fopen('php://output', 'w');
145+
}
143146

144-
return @fopen($outputStream, 'w') ?: fopen('php://output', 'w');
147+
return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
145148
}
146149

147150
/**
148151
* @return resource
149152
*/
150153
private function openErrorStream()
151154
{
152-
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
153-
154-
return fopen($errorStream, 'w');
155+
return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
155156
}
156157
}

src/Symfony/Component/Console/Tester/ApplicationTester.php

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Console\Application;
1515
use Symfony\Component\Console\Input\ArrayInput;
1616
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Output\ConsoleOutput;
1718
use Symfony\Component\Console\Output\OutputInterface;
1819
use Symfony\Component\Console\Output\StreamOutput;
1920

@@ -31,13 +32,13 @@ class ApplicationTester
3132
{
3233
private $application;
3334
private $input;
34-
private $output;
3535

3636
/**
37-
* Constructor.
38-
*
39-
* @param Application $application An Application instance to test.
37+
* @var OutputInterface
4038
*/
39+
private $output;
40+
private $captureOutputStreamsIndependent = false;
41+
4142
public function __construct(Application $application)
4243
{
4344
$this->application = $application;
@@ -48,9 +49,10 @@ public function __construct(Application $application)
4849
*
4950
* Available options:
5051
*
51-
* * interactive: Sets the input interactive flag
52-
* * decorated: Sets the output decorated flag
53-
* * verbosity: Sets the output verbosity flag
52+
* * interactive: Sets the input interactive flag
53+
* * decorated: Sets the output decorated flag
54+
* * verbosity: Sets the output verbosity flag
55+
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
5456
*
5557
* @param array $input An array of arguments and options
5658
* @param array $options An array of options
@@ -64,12 +66,35 @@ public function run(array $input, $options = array())
6466
$this->input->setInteractive($options['interactive']);
6567
}
6668

67-
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
68-
if (isset($options['decorated'])) {
69-
$this->output->setDecorated($options['decorated']);
70-
}
71-
if (isset($options['verbosity'])) {
72-
$this->output->setVerbosity($options['verbosity']);
69+
$this->captureOutputStreamsIndependent = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
70+
if (!$this->captureOutputStreamsIndependent) {
71+
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
72+
if (isset($options['decorated'])) {
73+
$this->output->setDecorated($options['decorated']);
74+
}
75+
if (isset($options['verbosity'])) {
76+
$this->output->setVerbosity($options['verbosity']);
77+
}
78+
} else {
79+
$this->output = new ConsoleOutput(
80+
isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL,
81+
isset($options['decorated']) ? $options['decorated'] : null
82+
);
83+
84+
$errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
85+
$errorOutput->setFormatter($this->output->getFormatter());
86+
$errorOutput->setVerbosity($this->output->getVerbosity());
87+
$errorOutput->setDecorated($this->output->isDecorated());
88+
89+
$reflectedOutput = new \ReflectionObject($this->output);
90+
$strErrProperty = $reflectedOutput->getProperty('stderr');
91+
$strErrProperty->setAccessible(true);
92+
$strErrProperty->setValue($this->output, $errorOutput);
93+
94+
$reflectedParent = $reflectedOutput->getParentClass();
95+
$streamProperty = $reflectedParent->getProperty('stream');
96+
$streamProperty->setAccessible(true);
97+
$streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
7398
}
7499

75100
return $this->application->run($this->input, $this->output);
@@ -95,6 +120,30 @@ public function getDisplay($normalize = false)
95120
return $display;
96121
}
97122

123+
/**
124+
* Gets the output written to STDERR by the application.
125+
*
126+
* @param bool $normalize Whether to normalize end of lines to \n or not
127+
*
128+
* @return string
129+
*/
130+
public function getErrorOutput($normalize = false)
131+
{
132+
if (!$this->captureOutputStreamsIndependent) {
133+
throw new \LogicException('Error output not available when tester run without "capture_stderr_separately" option set.');
134+
}
135+
136+
rewind($this->output->getErrorOutput()->getStream());
137+
138+
$display = stream_get_contents($this->output->getErrorOutput()->getStream());
139+
140+
if ($normalize) {
141+
$display = str_replace(PHP_EOL, "\n", $display);
142+
}
143+
144+
return $display;
145+
}
146+
98147
/**
99148
* Gets the input instance used by the last execution of the application.
100149
*

src/Symfony/Component/Console/Tests/ApplicationTest.php

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,9 @@ public function testSetCatchExceptions()
411411
$tester = new ApplicationTester($application);
412412

413413
$application->setCatchExceptions(true);
414-
$tester->run(array('command' => 'foo'), array('decorated' => false));
415-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag');
414+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
415+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag');
416+
$this->assertSame('', $tester->getDisplay(true));
416417

417418
$application->setCatchExceptions(false);
418419
try {
@@ -461,22 +462,22 @@ public function testRenderException()
461462
->will($this->returnValue(120));
462463
$tester = new ApplicationTester($application);
463464

464-
$tester->run(array('command' => 'foo'), array('decorated' => false));
465-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception');
465+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
466+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception');
466467

467-
$tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
468-
$this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
468+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true));
469+
$this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
469470

470-
$tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false));
471-
$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');
471+
$tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true));
472+
$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');
472473

473474
$application->add(new \Foo3Command());
474475
$tester = new ApplicationTester($application);
475-
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false));
476-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
476+
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true));
477+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
477478

478-
$tester->run(array('command' => 'foo3:bar'), array('decorated' => true));
479-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
479+
$tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true));
480+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
480481

481482
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
482483
$application->setAutoExit(false);
@@ -485,8 +486,8 @@ public function testRenderException()
485486
->will($this->returnValue(32));
486487
$tester = new ApplicationTester($application);
487488

488-
$tester->run(array('command' => 'foo'), array('decorated' => false));
489-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
489+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
490+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
490491
}
491492

492493
/**
@@ -504,11 +505,11 @@ public function testRenderExceptionWithDoubleWidthCharacters()
504505
});
505506
$tester = new ApplicationTester($application);
506507

507-
$tester->run(array('command' => 'foo'), array('decorated' => false));
508-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
508+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
509+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
509510

510-
$tester->run(array('command' => 'foo'), array('decorated' => true));
511-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
511+
$tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true));
512+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
512513

513514
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
514515
$application->setAutoExit(false);
@@ -519,8 +520,8 @@ public function testRenderExceptionWithDoubleWidthCharacters()
519520
throw new \Exception('コマンドの実行中にエラーが発生しました。');
520521
});
521522
$tester = new ApplicationTester($application);
522-
$tester->run(array('command' => 'foo'), array('decorated' => false));
523-
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
523+
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
524+
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
524525
}
525526

526527
public function testRun()
@@ -622,8 +623,8 @@ public function testRunReturnsIntegerExitCode()
622623
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
623624
$application->setAutoExit(false);
624625
$application->expects($this->once())
625-
->method('doRun')
626-
->will($this->throwException($exception));
626+
->method('doRun')
627+
->will($this->throwException($exception));
627628

628629
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
629630

@@ -637,8 +638,8 @@ public function testRunReturnsExitCodeOneForExceptionCodeZero()
637638
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
638639
$application->setAutoExit(false);
639640
$application->expects($this->once())
640-
->method('doRun')
641-
->will($this->throwException($exception));
641+
->method('doRun')
642+
->will($this->throwException($exception));
642643

643644
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
644645

0 commit comments

Comments
 (0)