1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Util;
6:
7: use function array_pop;
8: use function assert;
9: use function explode;
10: use function implode;
11: use function is_string;
12: use function preg_match;
13: use function sprintf;
14: use function str_replace;
15: use function strpos;
16: use function substr;
17: use function trim;
18:
19: use const DIRECTORY_SEPARATOR;
20:
21: class FileHelper
22: {
23: /**
24: * @param non-empty-string $path
25: *
26: * @return non-empty-string
27: *
28: * @psalm-pure
29: */
30: public static function normalizeWindowsPath(string $path): string
31: {
32: $path = str_replace('\\', '/', $path);
33:
34: /** @phpstan-ignore function.alreadyNarrowedType, notIdentical.alwaysTrue */
35: assert($path !== '');
36:
37: return $path;
38: }
39:
40: /**
41: * @param non-empty-string $originalPath
42: *
43: * @return non-empty-string
44: *
45: * @psalm-pure
46: */
47: public static function normalizePath(string $originalPath, string $directorySeparator = DIRECTORY_SEPARATOR): string
48: {
49: $isLocalPath = $originalPath !== '' && $originalPath[0] === '/';
50:
51: $matches = null;
52: if (! $isLocalPath) {
53: if (! preg_match('~^([a-z]+)\\:\\/\\/(.+)~', $originalPath, $matches)) {
54: $matches = null;
55: }
56: }
57:
58: if ($matches !== null) {
59: [, $scheme, $path] = $matches;
60: } else {
61: $scheme = null;
62: $path = $originalPath;
63: }
64:
65: $path = str_replace(['\\', '//', '///', '////'], '/', $path);
66:
67: $pathRoot = strpos($path, '/') === 0 ? $directorySeparator : '';
68: $pathParts = explode('/', trim($path, '/'));
69:
70: $normalizedPathParts = [];
71: foreach ($pathParts as $pathPart) {
72: if ($pathPart === '.') {
73: continue;
74: }
75:
76: if ($pathPart === '..') {
77: $removedPart = array_pop($normalizedPathParts);
78: assert(is_string($removedPart));
79: if ($scheme === 'phar' && substr($removedPart, -5) === '.phar') {
80: $scheme = null;
81: }
82: } else {
83: $normalizedPathParts[] = $pathPart;
84: }
85: }
86:
87: return ($scheme !== null ? $scheme . '://' : '') . $pathRoot . implode($directorySeparator, $normalizedPathParts);
88: }
89:
90: public static function normalizeSystemPath(string $originalPath): string
91: {
92: $path = self::normalizeWindowsPath($originalPath);
93: preg_match('~^([a-z]+)\\:\\/\\/(.+)~', $path, $matches);
94: $scheme = null;
95: if ($matches !== []) {
96: [, $scheme, $path] = $matches;
97: }
98:
99: // @infection-ignore-all Identical Needed only on Windows
100: if (DIRECTORY_SEPARATOR === '\\') {
101: // @infection-ignore-all UnwrapStrReplace Needed only on Windows
102: $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
103: }
104:
105: /** @phpstan-ignore function.alreadyNarrowedType, notIdentical.alwaysTrue */
106: assert($path !== '');
107:
108: return ($scheme !== null ? sprintf('%s://', $scheme) : '') . $path;
109: }
110: }
111: