-
Notifications
You must be signed in to change notification settings - Fork 587
Expand file tree
/
Copy pathFileExistenceChecker.php
More file actions
103 lines (89 loc) · 2.86 KB
/
Copy pathFileExistenceChecker.php
File metadata and controls
103 lines (89 loc) · 2.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php declare(strict_types = 1);
namespace PHPStan\File;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use function array_merge;
use function explode;
use function get_include_path;
use function is_dir;
use function is_file;
use const PATH_SEPARATOR;
/**
* Resolves a possibly-relative path the same way PHP would at runtime and checks whether it exists.
*
* We cannot use stream_resolve_include_path() because it works based on the calling script.
* This mirrors its behavior but for an arbitrary script directory. The priority order is:
* 1. The base directories (e.g. from an attribute argument).
* 2. The current working directory.
* 3. The include path.
* 4. The directory of the script that is being analysed.
*/
#[AutowiredService]
final class FileExistenceChecker
{
/**
* Functions whose whole purpose is to test whether a path exists, so passing
* them a non-existent path is expected and must not be reported.
*
* Relevant once phpstorm-stubs annotate these with #[JetBrains\PhpStorm\FileReference]:
* @see https://youtrack.jetbrains.com/issue/WI-85516/FileReference-and-builtin-functions-like-isfile
*/
public const FILE_EXISTENCE_FUNCTIONS = [
'file_exists',
'is_file',
'is_readable',
'is_writable',
'is_writeable',
'is_executable',
];
public function __construct(
#[AutowiredParameter]
private string $currentWorkingDirectory,
)
{
}
/**
* @param list<string> $baseDirectories
*/
public function fileExists(string $path, string $scriptDirectory, array $baseDirectories = []): bool
{
return $this->exists($path, $scriptDirectory, $baseDirectories, false);
}
/**
* Like fileExists(), but a directory at the resolved path also counts as existing.
*
* @param list<string> $baseDirectories
*/
public function pathExists(string $path, string $scriptDirectory, array $baseDirectories = []): bool
{
return $this->exists($path, $scriptDirectory, $baseDirectories, true);
}
/**
* @param list<string> $baseDirectories
*/
private function exists(string $path, string $scriptDirectory, array $baseDirectories, bool $allowDirectory): bool
{
$scriptHelper = new FileHelper($scriptDirectory);
$resolvedBaseDirectories = [];
foreach ($baseDirectories as $baseDirectory) {
// a relative base directory is resolved against the directory of the analysed script
$resolvedBaseDirectories[] = $scriptHelper->absolutizePath($baseDirectory);
}
$directories = array_merge(
$resolvedBaseDirectories,
[$this->currentWorkingDirectory],
explode(PATH_SEPARATOR, get_include_path()),
[$scriptDirectory],
);
foreach ($directories as $directory) {
$absolutePath = (new FileHelper($directory))->absolutizePath($path);
if (is_file($absolutePath)) {
return true;
}
if ($allowDirectory && is_dir($absolutePath)) {
return true;
}
}
return false;
}
}