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