This commit is contained in:
Javier Casares 2026-06-09 12:52:52 +00:00
commit 19ef0566df
610 changed files with 21958 additions and 3112 deletions

View file

@ -17,9 +17,10 @@ use Symfony\Component\Filesystem\Exception\RuntimeException;
/**
* Contains utility methods for handling path strings.
*
* The methods in this class are able to deal with both UNIX and Windows paths
* with both forward and backward slashes. All methods return normalized parts
* containing only forward slashes and no excess "." and ".." segments.
* The methods in this class are able to deal with both UNIX and Windows paths.
* On Windows, backward slashes are normalized to forward slashes. On UNIX,
* backward slashes are treated as valid filename characters and are not replaced.
* All methods return normalized parts with no excess "." and ".." segments.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Thomas Schulz <mail@king2500.net>
@ -49,14 +50,11 @@ final class Path
/**
* Canonicalizes the given path.
*
* During normalization, all slashes are replaced by forward slashes ("/").
* Furthermore, all "." and ".." segments are removed as far as possible.
* ".." segments at the beginning of relative paths are not removed.
* During normalization, all "." and ".." segments are removed as far as
* possible. ".." segments at the beginning of relative paths are not removed.
* On Windows, backward slashes are replaced by forward slashes ("/").
*
* ```php
* echo Path::canonicalize("\symfony\puli\..\css\style.css");
* // => /symfony/css/style.css
*
* echo Path::canonicalize("../css/./style.css");
* // => ../css/style.css
* ```
@ -103,17 +101,16 @@ final class Path
/**
* Normalizes the given path.
*
* During normalization, all slashes are replaced by forward slashes ("/").
* On Windows, backward slashes are replaced by forward slashes ("/").
* On UNIX, backward slashes are preserved as they are valid filename characters.
* Contrary to {@link canonicalize()}, this method does not remove invalid
* or dot path segments. Consequently, it is much more efficient and should
* be used whenever the given path is known to be a valid, absolute system
* path.
*
* This method is able to deal with both UNIX and Windows paths.
*/
public static function normalize(string $path): string
{
return str_replace('\\', '/', $path);
return '\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path;
}
/**
@ -225,8 +222,15 @@ final class Path
$firstCharacter = $path[0];
// UNIX root "/" or "\" (Windows style)
if ('/' === $firstCharacter || '\\' === $firstCharacter) {
if ('/' === $firstCharacter) {
return $scheme.'/';
}
if ('\\' !== \DIRECTORY_SEPARATOR) {
return '';
}
if ('\\' === $firstCharacter) {
return $scheme.'/';
}
@ -239,7 +243,7 @@ final class Path
return $scheme.$path.'/';
}
// Normal case: "C:/ or "C:\"
// Normal case: "C:/" or "C:\"
if ('/' === $path[2] || '\\' === $path[2]) {
return $scheme.$firstCharacter.$path[1].'/';
}
@ -363,13 +367,41 @@ final class Path
*/
public static function isAbsolute(string $path): bool
{
return '' !== $path && (strspn($path, '/\\', 0, 1)
|| (\strlen($path) > 3 && ctype_alpha($path[0])
&& ':' === $path[1]
&& strspn($path, '/\\', 2, 1)
)
|| null !== parse_url($path, \PHP_URL_SCHEME)
);
if ('' === $path) {
return false;
}
// URLs and stream wrappers are considered absolute
if (str_contains($path, '://') && null !== parse_url($path, \PHP_URL_SCHEME)) {
return true;
}
if ('/' === $path[0]) {
return true;
}
if ('\\' !== \DIRECTORY_SEPARATOR) {
return false;
}
if ('\\' === $path[0]) {
return true;
}
// Windows root
if (\strlen($path) > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
// Special case: "C:"
if (2 === \strlen($path)) {
return true;
}
// Normal case: "C:/" or "C:\"
if ('/' === $path[2] || '\\' === $path[2]) {
return true;
}
}
return false;
}
public static function isRelative(string $path): bool
@ -435,7 +467,7 @@ final class Path
$scheme = '';
}
return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path);
return $scheme.self::canonicalize(rtrim($basePath, '/'.\DIRECTORY_SEPARATOR).'/'.$path);
}
/**
@ -502,7 +534,7 @@ final class Path
if ('' === $root && '' !== $baseRoot) {
// If base path is already in its root
if ('' === $relativeBasePath) {
$relativePath = ltrim($relativePath, './\\');
$relativePath = ltrim($relativePath, './'.\DIRECTORY_SEPARATOR);
}
return $relativePath;
@ -647,8 +679,8 @@ final class Path
continue;
}
// Only add slash if previous part didn't end with '/' or '\'
if (!\in_array(substr($finalPath, -1), ['/', '\\'], true)) {
// Only add slash if previous part didn't end with '/' or '\' (Windows)
if ('/' !== substr($finalPath, -1) && \DIRECTORY_SEPARATOR !== substr($finalPath, -1)) {
$finalPath .= '/';
}
@ -766,7 +798,7 @@ final class Path
if (str_starts_with($path, '/')) {
$root .= '/';
$path = $length > 1 ? substr($path, 1) : '';
} elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
} elseif ('\\' === \DIRECTORY_SEPARATOR && $length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
if (2 === $length) {
// Windows special case: "C:"
$root .= $path.'/';