Skip to content

Commit aa236a7

Browse files
authored
stream_get_contents() does not return FALSE unless an invalid position is provided
1 parent acd2a44 commit aa236a7

File tree

10 files changed

+79
-33
lines changed

10 files changed

+79
-33
lines changed

src/Command/AnalyseCommand.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,6 @@ private function generateBaseline(string $generateBaselineFile, InceptionResult
746746
$stream = $streamOutput->getStream();
747747
rewind($stream);
748748
$baselineContents = stream_get_contents($stream);
749-
if ($baselineContents === false) {
750-
throw new ShouldNotHappenException();
751-
}
752749

753750
try {
754751
DirectoryCreator::ensureDirectoryExists($baselineFileDirectory, 0644);

src/Parallel/Process.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use React\Stream\WritableStreamInterface;
1010
use Throwable;
1111
use function fclose;
12-
use function is_string;
1312
use function rewind;
1413
use function sprintf;
1514
use function stream_get_contents;
@@ -73,16 +72,11 @@ public function start(callable $onData, callable $onError, callable $onExit): vo
7372

7473
$output = '';
7574
rewind($this->stdOut);
76-
$stdOut = stream_get_contents($this->stdOut);
77-
if (is_string($stdOut)) {
78-
$output .= $stdOut;
79-
}
75+
$output .= stream_get_contents($this->stdOut);
8076

8177
rewind($this->stdErr);
82-
$stdErr = stream_get_contents($this->stdErr);
83-
if (is_string($stdErr)) {
84-
$output .= $stdErr;
85-
}
78+
$output .= stream_get_contents($this->stdErr);
79+
8680
$onExit($exitCode, $output);
8781
fclose($this->stdOut);
8882
fclose($this->stdErr);

src/Process/ProcessPromise.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ public function run(): PromiseInterface
6969
}
7070

7171
if ($exitCode === 0) {
72-
if ($stdOut === false) {
73-
$stdOut = '';
74-
}
7572
$this->deferred->resolve($stdOut);
7673
return;
7774
}

src/Testing/ErrorFormatterTestCase.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@ protected function getOutputContent(bool $decorated = false, bool $verbose = fal
7575
rewind($this->getOutputStream($decorated, $verbose)->getStream());
7676

7777
$contents = stream_get_contents($this->getOutputStream($decorated, $verbose)->getStream());
78-
if ($contents === false) {
79-
throw new ShouldNotHappenException();
80-
}
81-
8278
return $this->rtrimMultiline($contents);
8379
}
8480

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\AutowiredService;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\ParametersAcceptorSelector;
10+
use PHPStan\Type\Constant\ConstantBooleanType;
11+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
12+
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeCombinator;
14+
use function count;
15+
16+
#[AutowiredService]
17+
final class StreamGetContentsFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
18+
{
19+
20+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
21+
{
22+
return $functionReflection->getName() === 'stream_get_contents';
23+
}
24+
25+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
26+
{
27+
if (count($functionCall->getArgs()) >= 3) {
28+
return null;
29+
}
30+
31+
// stream_get_contents() does not return FALSE unless an invalid offset is provided.
32+
$returnType = ParametersAcceptorSelector::selectFromArgs(
33+
$scope,
34+
$functionCall->getArgs(),
35+
$functionReflection->getVariants(),
36+
)->getReturnType();
37+
38+
return TypeCombinator::remove($returnType, new ConstantBooleanType(false));
39+
}
40+
41+
}

tests/PHPStan/Command/AnalyseApplicationIntegrationTest.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ private function runPath(string $path, int $expectedStatusCode): string
9393
rewind($output->getStream());
9494

9595
$contents = stream_get_contents($output->getStream());
96-
if ($contents === false) {
97-
throw new ShouldNotHappenException();
98-
}
99-
10096
$this->assertSame($expectedStatusCode, $statusCode, $contents);
10197

10298
return $contents;

tests/PHPStan/Command/CommandHelperTest.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,13 @@ public function testBegin(
136136
if (!$expectException) {
137137
rewind($output->getStream());
138138
$contents = stream_get_contents($output->getStream());
139-
if ($contents === false) {
140-
throw new ShouldNotHappenException();
141-
}
142139
$this->fail($contents);
143140
}
144141
}
145142

146143
rewind($output->getStream());
147144

148145
$contents = stream_get_contents($output->getStream());
149-
if ($contents === false) {
150-
throw new ShouldNotHappenException();
151-
}
152146
$this->assertStringContainsString($expectedOutput, $contents);
153147

154148
if (isset($result)) {

tests/PHPStan/Command/ErrorFormatter/BaselineNeonErrorFormatterTest.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,6 @@ public function testEndOfFileNewlines(
437437
rewind($outputStream->getStream());
438438

439439
$content = stream_get_contents($outputStream->getStream());
440-
if ($content === false) {
441-
throw new ShouldNotHappenException();
442-
}
443-
444440
if ($expectedNewlinesCount > 0) {
445441
Assert::assertSame(str_repeat("\n", $expectedNewlinesCount), substr($content, -$expectedNewlinesCount));
446442
}

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3572,4 +3572,19 @@ public function testBug13171(): void
35723572
]);
35733573
}
35743574

3575+
public function testBug3396(): void
3576+
{
3577+
$this->checkThisOnly = false;
3578+
$this->checkNullables = false;
3579+
$this->checkUnionTypes = true;
3580+
$this->checkExplicitMixed = false;
3581+
3582+
$this->analyse([__DIR__ . '/data/bug-3396.php'], [
3583+
[
3584+
'Parameter #1 $s of method Bug3396\HelloWorld::takesString() expects string, string|false given.',
3585+
18,
3586+
],
3587+
]);
3588+
}
3589+
35753590
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Bug3396;
4+
5+
class HelloWorld
6+
{
7+
8+
public function takesString(string $s): void
9+
{
10+
}
11+
12+
public function sayHello(): void
13+
{
14+
$stream = fopen("file.txt", "rb");
15+
if ($stream === false) throw new \Error("wtf");
16+
$this->takesString(stream_get_contents($stream));
17+
$this->takesString(stream_get_contents($stream, 1));
18+
$this->takesString(stream_get_contents($stream, 1, 1));
19+
}
20+
}

0 commit comments

Comments
 (0)