Skip to content

Commit de4f40b

Browse files
Merge branch '6.4' into 7.4
* 6.4: [Console] Fix OUTPUT_RAW corrupting binary content on Windows [Mime] Use shell_exec() instead of passthru() in FileBinaryMimeTypeGuesser
2 parents 6d643a9 + 4d5318a commit de4f40b

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

Output/ConsoleOutput.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,24 +130,54 @@ private function isRunningOS400(): bool
130130
*/
131131
private function openOutputStream()
132132
{
133+
static $stdout;
134+
135+
if ($stdout) {
136+
return $stdout;
137+
}
138+
133139
if (!$this->hasStdoutSupport()) {
134-
return fopen('php://output', 'w');
140+
return $stdout = fopen('php://output', 'w');
135141
}
136142

137143
// Use STDOUT when possible to prevent from opening too many file descriptors
138-
return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
144+
if (!\defined('STDOUT')) {
145+
return $stdout = @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
146+
}
147+
148+
// On Windows, STDOUT is opened in text mode; reopen in binary mode to prevent \n to \r\n conversion
149+
if ('\\' === \DIRECTORY_SEPARATOR) {
150+
return $stdout = @fopen('php://stdout', 'w') ?: \STDOUT;
151+
}
152+
153+
return $stdout = \STDOUT;
139154
}
140155

141156
/**
142157
* @return resource
143158
*/
144159
private function openErrorStream()
145160
{
161+
static $stderr;
162+
163+
if ($stderr) {
164+
return $stderr;
165+
}
166+
146167
if (!$this->hasStderrSupport()) {
147-
return fopen('php://output', 'w');
168+
return $stderr = fopen('php://output', 'w');
148169
}
149170

150171
// Use STDERR when possible to prevent from opening too many file descriptors
151-
return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w'));
172+
if (!\defined('STDERR')) {
173+
return $stderr = @fopen('php://stderr', 'w') ?: fopen('php://output', 'w');
174+
}
175+
176+
// On Windows, STDERR is opened in text mode; reopen in binary mode to prevent \n → \r\n conversion
177+
if ('\\' === \DIRECTORY_SEPARATOR) {
178+
return $stderr = @fopen('php://stderr', 'w') ?: \STDERR;
179+
}
180+
181+
return $stderr ??= \STDERR;
152182
}
153183
}

Tests/Fixtures/binary_output.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
$vendor = __DIR__;
4+
while (!file_exists($vendor.'/vendor')) {
5+
$vendor = \dirname($vendor);
6+
}
7+
require $vendor.'/vendor/autoload.php';
8+
9+
use Symfony\Component\Console\Output\ConsoleOutput;
10+
use Symfony\Component\Console\Output\OutputInterface;
11+
12+
$output = new ConsoleOutput();
13+
$output->write("HELLO\nWORLD", false, OutputInterface::OUTPUT_RAW);

Tests/Output/StreamOutputTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Console\Output\Output;
1616
use Symfony\Component\Console\Output\StreamOutput;
17+
use Symfony\Component\Process\Process;
1718

1819
class StreamOutputTest extends TestCase
1920
{
@@ -65,4 +66,12 @@ public function testDoWriteOnFailure()
6566
rewind($output->getStream());
6667
$this->assertEquals('', stream_get_contents($output->getStream()));
6768
}
69+
70+
public function testRawOutputPreservesNewlinesInContent()
71+
{
72+
$process = new Process(['php', __DIR__.'/../Fixtures/binary_output.php']);
73+
$process->run();
74+
75+
$this->assertSame("HELLO\nWORLD", $process->getOutput(), 'Raw output must not convert LF in binary content');
76+
}
6877
}

0 commit comments

Comments
 (0)