1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\File;
4:
5: use Nette\Utils\Strings;
6: use function array_pop;
7: use function explode;
8: use function implode;
9: use function ltrim;
10: use function rtrim;
11: use function str_replace;
12: use function str_starts_with;
13: use function strlen;
14: use function strpos;
15: use function substr;
16: use function trim;
17: use const DIRECTORY_SEPARATOR;
18:
19: class FileHelper
20: {
21:
22: private string $workingDirectory;
23:
24: public function __construct(string $workingDirectory)
25: {
26: $this->workingDirectory = $this->normalizePath($workingDirectory);
27: }
28:
29: public function getWorkingDirectory(): string
30: {
31: return $this->workingDirectory;
32: }
33:
34: /** @api */
35: public function absolutizePath(string $path): string
36: {
37: if (DIRECTORY_SEPARATOR === '/') {
38: if (substr($path, 0, 1) === '/') {
39: return $path;
40: }
41: } else {
42: if (substr($path, 1, 1) === ':') {
43: return $path;
44: }
45: }
46: if (str_starts_with($path, 'phar://')) {
47: return $path;
48: }
49:
50: return rtrim($this->getWorkingDirectory(), '/\\') . DIRECTORY_SEPARATOR . ltrim($path, '/\\');
51: }
52:
53: /** @api */
54: public function normalizePath(string $originalPath, string $directorySeparator = DIRECTORY_SEPARATOR): string
55: {
56: $isLocalPath = false;
57: if ($originalPath !== '') {
58: if ($originalPath[0] === '/') {
59: $isLocalPath = true;
60: } elseif (strlen($originalPath) >= 3 && $originalPath[1] === ':' && $originalPath[2] === '\\') { // e.g. C:\
61: $isLocalPath = true;
62: }
63: }
64:
65: $matches = null;
66: if (!$isLocalPath) {
67: $matches = Strings::match($originalPath, '~^([a-z]+)\\:\\/\\/(.+)~');
68: }
69:
70: if ($matches !== null) {
71: [, $scheme, $path] = $matches;
72: } else {
73: $scheme = null;
74: $path = $originalPath;
75: }
76:
77: $path = str_replace(['\\', '//', '///', '////'], '/', $path);
78:
79: $pathRoot = strpos($path, '/') === 0 ? $directorySeparator : '';
80: $pathParts = explode('/', trim($path, '/'));
81:
82: $normalizedPathParts = [];
83: foreach ($pathParts as $pathPart) {
84: if ($pathPart === '.') {
85: continue;
86: }
87: if ($pathPart === '..') {
88: /** @var string $removedPart */
89: $removedPart = array_pop($normalizedPathParts);
90: if ($scheme === 'phar' && substr($removedPart, -5) === '.phar') {
91: $scheme = null;
92: }
93:
94: } else {
95: $normalizedPathParts[] = $pathPart;
96: }
97: }
98:
99: return ($scheme !== null ? $scheme . '://' : '') . $pathRoot . implode($directorySeparator, $normalizedPathParts);
100: }
101:
102: }
103: