123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819 |
- <?php
- namespace Symfony\Component\Filesystem;
- use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
- use Symfony\Component\Filesystem\Exception\RuntimeException;
- final class Path
- {
-
- private const CLEANUP_THRESHOLD = 1250;
-
- private const CLEANUP_SIZE = 1000;
-
- private static $buffer = [];
-
- private static $bufferSize = 0;
-
- public static function canonicalize(string $path): string
- {
- if ('' === $path) {
- return '';
- }
-
-
-
- if (isset(self::$buffer[$path])) {
- return self::$buffer[$path];
- }
-
- if ('~' === $path[0]) {
- $path = self::getHomeDirectory().substr($path, 1);
- }
- $path = self::normalize($path);
- [$root, $pathWithoutRoot] = self::split($path);
- $canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot);
-
- self::$buffer[$path] = $canonicalPath = $root.implode('/', $canonicalParts);
- ++self::$bufferSize;
-
- if (self::$bufferSize > self::CLEANUP_THRESHOLD) {
- self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, true);
- self::$bufferSize = self::CLEANUP_SIZE;
- }
- return $canonicalPath;
- }
-
- public static function normalize(string $path): string
- {
- return str_replace('\\', '/', $path);
- }
-
- public static function getDirectory(string $path): string
- {
- if ('' === $path) {
- return '';
- }
- $path = self::canonicalize($path);
-
- if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
- $scheme = substr($path, 0, $schemeSeparatorPosition + 3);
- $path = substr($path, $schemeSeparatorPosition + 3);
- } else {
- $scheme = '';
- }
- if (false === $dirSeparatorPosition = strrpos($path, '/')) {
- return '';
- }
-
- if (0 === $dirSeparatorPosition) {
- return $scheme.'/';
- }
-
- if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) {
- return $scheme.substr($path, 0, 3);
- }
- return $scheme.substr($path, 0, $dirSeparatorPosition);
- }
-
- public static function getHomeDirectory(): string
- {
-
- if (getenv('HOME')) {
- return self::canonicalize(getenv('HOME'));
- }
-
- if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) {
- return self::canonicalize(getenv('HOMEDRIVE').getenv('HOMEPATH'));
- }
- throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported.");
- }
-
- public static function getRoot(string $path): string
- {
- if ('' === $path) {
- return '';
- }
-
- if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
- $scheme = substr($path, 0, $schemeSeparatorPosition + 3);
- $path = substr($path, $schemeSeparatorPosition + 3);
- } else {
- $scheme = '';
- }
- $firstCharacter = $path[0];
-
- if ('/' === $firstCharacter || '\\' === $firstCharacter) {
- return $scheme.'/';
- }
- $length = \strlen($path);
-
- if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) {
-
- if (2 === $length) {
- return $scheme.$path.'/';
- }
-
- if ('/' === $path[2] || '\\' === $path[2]) {
- return $scheme.$firstCharacter.$path[1].'/';
- }
- }
- return '';
- }
-
- public static function getFilenameWithoutExtension(string $path, string $extension = null): string
- {
- if ('' === $path) {
- return '';
- }
- if (null !== $extension) {
-
- return rtrim(basename($path, $extension), '.');
- }
- return pathinfo($path, \PATHINFO_FILENAME);
- }
-
- public static function getExtension(string $path, bool $forceLowerCase = false): string
- {
- if ('' === $path) {
- return '';
- }
- $extension = pathinfo($path, \PATHINFO_EXTENSION);
- if ($forceLowerCase) {
- $extension = self::toLower($extension);
- }
- return $extension;
- }
-
- public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool
- {
- if ('' === $path) {
- return false;
- }
- $actualExtension = self::getExtension($path, $ignoreCase);
-
- if ([] === $extensions || null === $extensions) {
- return '' !== $actualExtension;
- }
- if (\is_string($extensions)) {
- $extensions = [$extensions];
- }
- foreach ($extensions as $key => $extension) {
- if ($ignoreCase) {
- $extension = self::toLower($extension);
- }
-
- $extensions[$key] = ltrim($extension, '.');
- }
- return \in_array($actualExtension, $extensions, true);
- }
-
- public static function changeExtension(string $path, string $extension): string
- {
- if ('' === $path) {
- return '';
- }
- $actualExtension = self::getExtension($path);
- $extension = ltrim($extension, '.');
-
- if ('/' === substr($path, -1)) {
- return $path;
- }
-
- if (empty($actualExtension)) {
- return $path.('.' === substr($path, -1) ? '' : '.').$extension;
- }
- return substr($path, 0, -\strlen($actualExtension)).$extension;
- }
- public static function isAbsolute(string $path): bool
- {
- if ('' === $path) {
- return false;
- }
-
- if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
- $path = substr($path, $schemeSeparatorPosition + 3);
- }
- $firstCharacter = $path[0];
-
- if ('/' === $firstCharacter || '\\' === $firstCharacter) {
- return true;
- }
-
- if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) {
-
- if (2 === \strlen($path)) {
- return true;
- }
-
- if ('/' === $path[2] || '\\' === $path[2]) {
- return true;
- }
- }
- return false;
- }
- public static function isRelative(string $path): bool
- {
- return !self::isAbsolute($path);
- }
-
- public static function makeAbsolute(string $path, string $basePath): string
- {
- if ('' === $basePath) {
- throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath));
- }
- if (!self::isAbsolute($basePath)) {
- throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath));
- }
- if (self::isAbsolute($path)) {
- return self::canonicalize($path);
- }
- if (false !== $schemeSeparatorPosition = strpos($basePath, '://')) {
- $scheme = substr($basePath, 0, $schemeSeparatorPosition + 3);
- $basePath = substr($basePath, $schemeSeparatorPosition + 3);
- } else {
- $scheme = '';
- }
- return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path);
- }
-
- public static function makeRelative(string $path, string $basePath): string
- {
- $path = self::canonicalize($path);
- $basePath = self::canonicalize($basePath);
- [$root, $relativePath] = self::split($path);
- [$baseRoot, $relativeBasePath] = self::split($basePath);
-
-
-
- if ('' === $root && '' !== $baseRoot) {
-
- if ('' === $relativeBasePath) {
- $relativePath = ltrim($relativePath, './\\');
- }
- return $relativePath;
- }
-
-
- if ('' !== $root && '' === $baseRoot) {
- throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath));
- }
-
- if ($baseRoot && $root !== $baseRoot) {
- throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot));
- }
- if ('' === $relativeBasePath) {
- return $relativePath;
- }
-
- $parts = explode('/', $relativePath);
- $baseParts = explode('/', $relativeBasePath);
- $dotDotPrefix = '';
-
-
- $match = true;
- foreach ($baseParts as $index => $basePart) {
- if ($match && isset($parts[$index]) && $basePart === $parts[$index]) {
- unset($parts[$index]);
- continue;
- }
- $match = false;
- $dotDotPrefix .= '../';
- }
- return rtrim($dotDotPrefix.implode('/', $parts), '/');
- }
-
- public static function isLocal(string $path): bool
- {
- return '' !== $path && false === strpos($path, '://');
- }
-
- public static function getLongestCommonBasePath(string ...$paths): ?string
- {
- [$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths)));
- for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) {
- [$root, $path] = self::split(self::canonicalize(current($paths)));
-
-
- if ($root !== $bpRoot) {
- return null;
- }
-
- while (true) {
- if ('.' === $basePath) {
-
- $basePath = '';
-
- continue 2;
- }
-
-
- if (0 === strpos($path.'/', $basePath.'/')) {
-
- continue 2;
- }
- $basePath = \dirname($basePath);
- }
- }
- return $bpRoot.$basePath;
- }
-
- public static function join(string ...$paths): string
- {
- $finalPath = null;
- $wasScheme = false;
- foreach ($paths as $path) {
- if ('' === $path) {
- continue;
- }
- if (null === $finalPath) {
-
- $finalPath = $path;
- $wasScheme = (false !== strpos($path, '://'));
- continue;
- }
-
- if (!\in_array(substr($finalPath, -1), ['/', '\\'])) {
- $finalPath .= '/';
- }
-
- $finalPath .= $wasScheme ? $path : ltrim($path, '/');
- $wasScheme = false;
- }
- if (null === $finalPath) {
- return '';
- }
- return self::canonicalize($finalPath);
- }
-
- public static function isBasePath(string $basePath, string $ofPath): bool
- {
- $basePath = self::canonicalize($basePath);
- $ofPath = self::canonicalize($ofPath);
-
-
-
-
-
- return 0 === strpos($ofPath.'/', rtrim($basePath, '/').'/');
- }
-
- private static function findCanonicalParts(string $root, string $pathWithoutRoot): array
- {
- $parts = explode('/', $pathWithoutRoot);
- $canonicalParts = [];
-
- foreach ($parts as $part) {
- if ('.' === $part || '' === $part) {
- continue;
- }
-
-
- if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) {
- array_pop($canonicalParts);
- continue;
- }
-
- if ('..' !== $part || '' === $root) {
- $canonicalParts[] = $part;
- }
- }
- return $canonicalParts;
- }
-
- private static function split(string $path): array
- {
- if ('' === $path) {
- return ['', ''];
- }
-
- if (false !== $schemeSeparatorPosition = strpos($path, '://')) {
- $root = substr($path, 0, $schemeSeparatorPosition + 3);
- $path = substr($path, $schemeSeparatorPosition + 3);
- } else {
- $root = '';
- }
- $length = \strlen($path);
-
- if (0 === strpos($path, '/')) {
- $root .= '/';
- $path = $length > 1 ? substr($path, 1) : '';
- } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
- if (2 === $length) {
-
- $root .= $path.'/';
- $path = '';
- } elseif ('/' === $path[2]) {
-
- $root .= substr($path, 0, 3);
- $path = $length > 3 ? substr($path, 3) : '';
- }
- }
- return [$root, $path];
- }
- private static function toLower(string $string): string
- {
- if (false !== $encoding = mb_detect_encoding($string, null, true)) {
- return mb_strtolower($string, $encoding);
- }
- return strtolower($string);
- }
- private function __construct()
- {
- }
- }
|