|
9 | 9 |
|
10 | 10 | use OC\Files\Mount\MountPoint; |
11 | 11 | use OC\Files\Storage\StorageFactory; |
| 12 | +use OC\Files\Utils\PathHelper; |
12 | 13 | use OC\User\NoUserException; |
13 | 14 | use OCP\Cache\CappedMemoryCache; |
14 | 15 | use OCP\EventDispatcher\IEventDispatcher; |
@@ -585,66 +586,67 @@ public static function hasUpdated($path, $time) { |
585 | 586 | } |
586 | 587 |
|
587 | 588 | /** |
588 | | - * Fix common problems with a file path |
| 589 | + * Normalizes a file path for consistent use within the virtual filesystem. |
| 590 | + * |
| 591 | + * Applies Unicode normalization (optional), removes dot-segments and duplicate slashes, |
| 592 | + * enforces leading slash, and optionally strips trailing slashes. Results are cached |
| 593 | + * per unique input to improve performance. |
| 594 | + * |
| 595 | + * TODO: Better unify with Pathhelper::normalizePath() (and possibly others) |
| 596 | + * TODO: Benchmark whether the cache is still beneficial today |
589 | 597 | * |
590 | | - * @param string $path |
591 | | - * @param bool $stripTrailingSlash whether to strip the trailing slash |
592 | | - * @param bool $isAbsolutePath whether the given path is absolute |
593 | | - * @param bool $keepUnicode true to disable unicode normalization |
594 | 598 | * @psalm-taint-escape file |
595 | | - * @return string |
596 | | - */ |
597 | | - public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { |
598 | | - /** |
599 | | - * FIXME: This is a workaround for existing classes and files which call |
600 | | - * this function with another type than a valid string. This |
601 | | - * conversion should get removed as soon as all existing |
602 | | - * function calls have been fixed. |
603 | | - */ |
604 | | - $path = (string)$path; |
605 | | - |
606 | | - if ($path === '') { |
| 599 | + * |
| 600 | + * @param string $path The file path to normalize. |
| 601 | + * @param bool $stripTrailingSlash Remove the trailing slash (except root). Default: true. |
| 602 | + * @param bool $isAbsolutePath Ignored; kept for legacy compatibility. |
| 603 | + * @param bool $keepUnicode If true, skips Unicode normalization. Default: false. |
| 604 | + * @return string Normalized path. |
| 605 | + */ |
| 606 | + public static function normalizePath( |
| 607 | + string $path, |
| 608 | + bool $stripTrailingSlash = true, |
| 609 | + bool $isAbsolutePath = false, |
| 610 | + bool $keepUnicode = false |
| 611 | + ): string { |
| 612 | + // Early return for root and empty string |
| 613 | + if ($path === '' || $path === '/') { |
607 | 614 | return '/'; |
608 | 615 | } |
609 | 616 |
|
| 617 | + // Prepare cache |
610 | 618 | if (is_null(self::$normalizedPathCache)) { |
611 | 619 | self::$normalizedPathCache = new CappedMemoryCache(2048); |
612 | 620 | } |
613 | | - |
614 | 621 | $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); |
615 | | - |
616 | 622 | if ($cacheKey && isset(self::$normalizedPathCache[$cacheKey])) { |
617 | 623 | return self::$normalizedPathCache[$cacheKey]; |
618 | 624 | } |
619 | 625 |
|
620 | | - //normalize unicode if possible |
| 626 | + // Unicode normalization |
621 | 627 | if (!$keepUnicode) { |
622 | 628 | $path = \OC_Util::normalizeUnicode($path); |
623 | 629 | } |
624 | 630 |
|
625 | | - //add leading slash, if it is already there we strip it anyway |
626 | | - $path = '/' . $path; |
627 | | - |
628 | | - $patterns = [ |
629 | | - '#\\\\#s', // no windows style '\\' slashes |
630 | | - '#/\.(/\.)*/#s', // remove '/./' |
631 | | - '#\//+#s', // remove sequence of slashes |
632 | | - '#/\.$#s', // remove trailing '/.' |
633 | | - ]; |
| 631 | + // Canonical normalization via PathHelper |
| 632 | + $normalized = PathHelper::normalizePath($path); |
634 | 633 |
|
635 | | - do { |
636 | | - $count = 0; |
637 | | - $path = preg_replace($patterns, '/', $path, -1, $count); |
638 | | - } while ($count > 0); |
639 | | - |
640 | | - //remove trailing slash |
641 | | - if ($stripTrailingSlash && strlen($path) > 1) { |
642 | | - $path = rtrim($path, '/'); |
| 634 | + // TEMPORARY: Remove dot-segments here until PathHelper::normalizePath() is assessed/updated to handle them natively |
| 635 | + while (\str_contains($normalized, '/./')) { |
| 636 | + $normalized = \str_replace('/./', '/', $normalized); |
| 637 | + } |
| 638 | + // Remove trailing '/.' (unless the whole thing is '/.') |
| 639 | + if (\substr($normalized, -2) === '/.' && \strlen($normalized) > 2) { |
| 640 | + $normalized = \substr($normalized, 0, -2); |
643 | 641 | } |
644 | 642 |
|
645 | | - self::$normalizedPathCache[$cacheKey] = $path; |
| 643 | + // Optionally strip trailing slash unless root |
| 644 | + if ($stripTrailingSlash && \strlen($normalized) > 1) { |
| 645 | + $normalized = \rtrim($normalized, '/'); |
| 646 | + } |
646 | 647 |
|
647 | | - return $path; |
| 648 | + self::$normalizedPathCache[$cacheKey] = $normalized; |
| 649 | + return $normalized; |
648 | 650 | } |
649 | 651 |
|
650 | 652 | /** |
|
0 commit comments