@@ -603,17 +603,21 @@ public static function normalizePath($path, $stripTrailingSlash = true, $isAbsol
603603 */
604604 $ path = (string )$ path ;
605605
606- if ($ path === '' ) {
606+ if ($ path === '' || $ path === ' / ' ) {
607607 return '/ ' ;
608608 }
609609
610610 if (is_null (self ::$ normalizedPathCache )) {
611611 self ::$ normalizedPathCache = new CappedMemoryCache (2048 );
612612 }
613613
614- $ cacheKey = json_encode ([$ path , $ stripTrailingSlash , $ isAbsolutePath , $ keepUnicode ]);
614+ $ cacheKey =
615+ $ path . "\0" .
616+ (int )$ stripTrailingSlash . "\0" .
617+ (int )$ isAbsolutePath . "\0" .
618+ (int )$ keepUnicode ;
615619
616- if ($ cacheKey && isset (self ::$ normalizedPathCache [$ cacheKey ])) {
620+ if (isset (self ::$ normalizedPathCache [$ cacheKey ])) {
617621 return self ::$ normalizedPathCache [$ cacheKey ];
618622 }
619623
@@ -622,26 +626,53 @@ public static function normalizePath($path, $stripTrailingSlash = true, $isAbsol
622626 $ path = \OC_Util::normalizeUnicode ($ path );
623627 }
624628
625- //add leading slash, if it is already there we strip it anyway
626- $ path = '/ ' . $ path ;
629+ $ len = strlen ($ path );
630+ $ out = [];
631+ $ outLen = 0 ;
627632
628- $ patterns = [
629- '# \\\\#s ' , // no windows style '\\' slashes
630- '#/\.(/\.)*/#s ' , // remove '/./'
631- '#\//+#s ' , // remove sequence of slashes
632- '#/\.$#s ' , // remove trailing '/.'
633- ];
633+ // Force leading slash
634+ $ out [$ outLen ++] = '/ ' ;
634635
635- do {
636- $ count = 0 ;
637- $ path = preg_replace ($ patterns , '/ ' , $ path , -1 , $ count );
638- } while ($ count > 0 );
636+ $ prev = '/ ' ;
637+ $ i = 0 ;
639638
640- //remove trailing slash
641- if ($ stripTrailingSlash && strlen ($ path ) > 1 ) {
642- $ path = rtrim ($ path , '/ ' );
639+ while ($ i < $ len ) {
640+ $ c = $ path [$ i ++];
641+
642+ // Normalize Windows slashes
643+ if ($ c === '\\' ) {
644+ $ c = '/ ' ;
645+ }
646+
647+ // Collapse multiple slashes
648+ if ($ c === '/ ' && $ prev === '/ ' ) {
649+ continue ;
650+ }
651+
652+ // Handle "/./" and trailing "/."
653+ if ($ c === '. ' && $ prev === '/ ' ) {
654+ $ next = $ i < $ len ? $ path [$ i ] : null ;
655+
656+ if ($ next === '/ ' || $ next === null ) {
657+ // Skip "./"
658+ if ($ next === '/ ' ) {
659+ $ i ++;
660+ }
661+ continue ;
662+ }
663+ }
664+
665+ $ out [$ outLen ++] = $ c ;
666+ $ prev = $ c ;
643667 }
644668
669+ // Remove trailing slash
670+ if ($ stripTrailingSlash && $ outLen > 1 && $ out [$ outLen - 1 ] === '/ ' ) {
671+ array_pop ($ out );
672+ }
673+
674+ $ path = implode ('' , $ out );
675+
645676 self ::$ normalizedPathCache [$ cacheKey ] = $ path ;
646677
647678 return $ path ;
0 commit comments