Skip to content

Commit 4858ceb

Browse files
committed
Method that works like stream_resolve_include_path but for a specific directory
1 parent 95e5888 commit 4858ceb

File tree

3 files changed

+75
-15
lines changed

3 files changed

+75
-15
lines changed

src/Rules/Keywords/RequireFileExistsRule.php

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
use PhpParser\Node;
66
use PhpParser\Node\Expr\Include_;
77
use PHPStan\Analyser\Scope;
8+
use PHPStan\File\FileHelper;
89
use PHPStan\Rules\IdentifierRuleError;
910
use PHPStan\Rules\Rule;
1011
use PHPStan\Rules\RuleErrorBuilder;
1112
use PHPStan\ShouldNotHappenException;
13+
use function dirname;
14+
use function explode;
15+
use function get_include_path;
16+
use function getcwd;
17+
use function is_file;
18+
use function is_string;
1219
use function sprintf;
1320
use function str_replace;
14-
use function stream_resolve_include_path;
21+
use const PATH_SEPARATOR;
1522

1623
/**
1724
* @implements Rule<Include_>
@@ -30,7 +37,7 @@ public function processNode(Node $node, Scope $scope): array
3037
$paths = $this->resolveFilePaths($node, $scope);
3138

3239
foreach ($paths as $path) {
33-
if (stream_resolve_include_path($path) !== false) {
40+
if ($this->doesFileExist($path, $scope)) {
3441
continue;
3542
}
3643

@@ -40,6 +47,40 @@ public function processNode(Node $node, Scope $scope): array
4047
return $errors;
4148
}
4249

50+
/**
51+
* We cannot use `stream_resolve_include_path` as it works based on the calling script.
52+
* This method simulates the behavior of `stream_resolve_include_path` but for the given scope.
53+
* The priority order is the following:
54+
* 1. The current working directory.
55+
* 2. The include path.
56+
* 3. The path of the script that is being executed.
57+
*/
58+
private function doesFileExist(string $path, Scope $scope): bool
59+
{
60+
$directories = [
61+
getcwd(),
62+
...explode(PATH_SEPARATOR, get_include_path()),
63+
dirname($scope->getFile()),
64+
];
65+
66+
foreach ($directories as $directory) {
67+
if (is_string($directory) && $this->doesFileExistForDirectory($path, $directory)) {
68+
return true;
69+
}
70+
}
71+
72+
return false;
73+
}
74+
75+
private function doesFileExistForDirectory(string $path, string $workingDirectory): bool
76+
{
77+
$fileHelper = new FileHelper($workingDirectory);
78+
$normalisedPath = $fileHelper->normalizePath($path);
79+
$absolutePath = $fileHelper->absolutizePath($normalisedPath);
80+
81+
return is_file($absolutePath);
82+
}
83+
4384
private function getErrorMessage(Include_ $node, string $filePath): IdentifierRuleError
4485
{
4586
$message = 'Path in %s() "%s" is not a file or it does not exist.';

tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
7+
use function chdir;
78
use function get_include_path;
9+
use function getcwd;
810
use function implode;
911
use function realpath;
1012
use function set_include_path;
@@ -72,24 +74,24 @@ public function testFileDoesNotExistConditionally(): void
7274
]);
7375
}
7476

75-
public function testRelativePathWithoutIncludePath(): void
77+
public function testRelativePath(): void
7678
{
7779
$this->analyse([__DIR__ . '/data/require-file-relative-path.php'], [
7880
[
79-
'Path in include() "RequireFileExistsRuleTest.php" is not a file or it does not exist.',
80-
3,
81+
'Path in include() "data/include-me-to-prove-you-work.txt" is not a file or it does not exist.',
82+
8,
8183
],
8284
[
83-
'Path in include_once() "RequireFileExistsRuleTest.php" is not a file or it does not exist.',
84-
4,
85+
'Path in include_once() "data/include-me-to-prove-you-work.txt" is not a file or it does not exist.',
86+
9,
8587
],
8688
[
87-
'Path in require() "RequireFileExistsRuleTest.php" is not a file or it does not exist.',
88-
5,
89+
'Path in require() "data/include-me-to-prove-you-work.txt" is not a file or it does not exist.',
90+
10,
8991
],
9092
[
91-
'Path in require_once() "RequireFileExistsRuleTest.php" is not a file or it does not exist.',
92-
6,
93+
'Path in require_once() "data/include-me-to-prove-you-work.txt" is not a file or it does not exist.',
94+
11,
9395
],
9496
]);
9597
}
@@ -108,4 +110,16 @@ public function testRelativePathWithIncludePath(): void
108110
}
109111
}
110112

113+
public function testRelativePathWithSameWorkingDirectory(): void
114+
{
115+
$originalWorkingDirectory = getcwd();
116+
chdir(__DIR__);
117+
118+
try {
119+
$this->analyse([__DIR__ . '/data/require-file-relative-path.php'], []);
120+
} finally {
121+
chdir((string) $originalWorkingDirectory);
122+
}
123+
}
124+
111125
}
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<?php declare(strict_types=1);
22

3-
include 'RequireFileExistsRuleTest.php';
4-
include_once 'RequireFileExistsRuleTest.php';
5-
require 'RequireFileExistsRuleTest.php';
6-
require_once 'RequireFileExistsRuleTest.php';
3+
include 'include-me-to-prove-you-work.txt';
4+
include_once 'include-me-to-prove-you-work.txt';
5+
require 'include-me-to-prove-you-work.txt';
6+
require_once 'include-me-to-prove-you-work.txt';
7+
8+
include 'data/include-me-to-prove-you-work.txt';
9+
include_once 'data/include-me-to-prove-you-work.txt';
10+
require 'data/include-me-to-prove-you-work.txt';
11+
require_once 'data/include-me-to-prove-you-work.txt';

0 commit comments

Comments
 (0)