-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
... with some utility functions for handling file paths. The class initially contains the following utility methods: * `getName(File $phpcsFile): string` - Retrieve the normalized file name for the current file. * `isStdin(File $phpcsFile): bool` - Check whether the current file under scan comes from STDIN. * `normalizeAbsolutePath(string $path): string` - Normalize an absolute path to forward slashes and to include a trailing slash for directories. * `normalizeDirectorySeparators(string $path): string` - Normalize all directory separators to be a forward slash. * `trailingSlashIt(string $path): string` - Ensure that a directory path ends on a trailing slash. * `startsWith(string $haystack, string $needle): bool` - Check whether one file/directory path starts with another path. Includes unit tests.
- Loading branch information
Showing
7 changed files
with
758 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<?php | ||
/** | ||
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers. | ||
* | ||
* @package PHPCSUtils | ||
* @copyright 2019-2024 PHPCSUtils Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCSStandards/PHPCSUtils | ||
*/ | ||
|
||
namespace PHPCSUtils\Utils; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHPCSUtils\Utils\TextStrings; | ||
|
||
/** | ||
* Helper functions for working with arbitrary file/directory paths. | ||
* | ||
* Typically, these methods are useful for sniffs which examine the name of the file | ||
* under scan and need to act differently depending on the path in which the file | ||
* under scan is found. | ||
* | ||
* @see \PHP_CodeSniffer\Files\getFilename Retrieves the absolute path to the file under scan. | ||
* @see \PHPCSUtils\BackCompat\getCommandLineData Can be used to retrieve "basepath" setting. | ||
* | ||
* @since 1.1.0 | ||
*/ | ||
final class FilePath | ||
{ | ||
|
||
/** | ||
* Get the file name of the current file under scan. | ||
* | ||
* In contrast to the PHPCS native {@see \PHP_CodeSniffer\Files\getFilename()} method, | ||
* the name returned by this method will have been normalized. | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. | ||
* | ||
* @return string The file name without surrounding quotes and with forward slashes | ||
* as directory separators. | ||
*/ | ||
public static function getName(File $phpcsFile) | ||
{ | ||
// Usage of `stripQuotes` is to ensure `stdin_path` passed by IDEs does not include quotes. | ||
$fileName = TextStrings::stripQuotes($phpcsFile->getFileName()); | ||
if ($fileName !== 'STDIN') { | ||
$fileName = self::normalizeAbsolutePath($fileName); | ||
} | ||
|
||
return \trim($fileName); | ||
} | ||
|
||
/** | ||
* Check whether the input was received via STDIN. | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. | ||
* | ||
* @return bool | ||
*/ | ||
public static function isStdin(File $phpcsFile) | ||
{ | ||
return (self::getName($phpcsFile) === 'STDIN'); | ||
} | ||
|
||
/** | ||
* Normalize an absolute path to forward slashes and to include a trailing slash for directories. | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param string $path Absolute file or directory path. | ||
* | ||
* @return string | ||
*/ | ||
public static function normalizeAbsolutePath($path) | ||
{ | ||
return self::trailingSlashIt(self::normalizeDirectorySeparators($path)); | ||
} | ||
|
||
/** | ||
* Normalize all directory separators to be a forward slash. | ||
* | ||
* {@internal We cannot rely on the OS on which PHPCS is being run to determine the | ||
* the expected slashes, as the file name could also come from a text string in a | ||
* tokenized file or have been set by an IDE...} | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param string $path File or directory path. | ||
* | ||
* @return string | ||
*/ | ||
public static function normalizeDirectorySeparators($path) | ||
{ | ||
return \strtr((string) $path, '\\', '/'); | ||
} | ||
|
||
/** | ||
* Ensure that a directory path ends on a trailing slash. | ||
* | ||
* Includes safeguard against adding a trailing slash to path ending on a file name. | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param string $path File or directory path. | ||
* | ||
* @return string | ||
*/ | ||
public static function trailingSlashIt($path) | ||
{ | ||
if (\is_string($path) === false || $path === '') { | ||
return ''; | ||
} | ||
|
||
$extension = ''; | ||
$lastChar = \substr($path, -1); | ||
if ($lastChar !== '/' && $lastChar !== '\\') { | ||
// This may be a file, check if it has a file extension. | ||
$extension = \pathinfo($path, \PATHINFO_EXTENSION); | ||
} | ||
|
||
if ($extension !== '') { | ||
return $path; | ||
} | ||
|
||
return \rtrim((string) $path, '/\\') . '/'; | ||
} | ||
|
||
/** | ||
* Check whether one file/directory path starts with another path. | ||
* | ||
* Recommended to be used only when both paths are absolute. | ||
* | ||
* Note: this function does not normalize paths prior to comparing them. | ||
* If this is needed, normalization should be done prior to passing | ||
* the `$haystack` and `$needle` parameters to this function. | ||
* | ||
* Also note that this function does a case-sensitive comparison as most OS-es are case-sensitive. | ||
* | ||
* @since 1.1.0 | ||
* | ||
* @param string $haystack Path to examine. | ||
* @param string $needle Partial path which the haystack path should start with. | ||
* | ||
* @return bool | ||
*/ | ||
public static function startsWith($haystack, $needle) | ||
{ | ||
return (\strncmp($haystack, $needle, \strlen($needle)) === 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
<?php | ||
/** | ||
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers. | ||
* | ||
* @package PHPCSUtils | ||
* @copyright 2019-2024 PHPCSUtils Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCSStandards/PHPCSUtils | ||
*/ | ||
|
||
namespace PHPCSUtils\Tests\Utils\FilePath; | ||
|
||
use PHP_CodeSniffer\Files\DummyFile; | ||
use PHP_CodeSniffer\Ruleset; | ||
use PHPCSUtils\TestUtils\ConfigDouble; | ||
use PHPCSUtils\Utils\FilePath; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* Test class. | ||
* | ||
* @covers \PHPCSUtils\Utils\FilePath::getName | ||
* | ||
* @since 1.1.0 | ||
*/ | ||
final class GetNameTest extends TestCase | ||
{ | ||
|
||
/** | ||
* Config object for use in the tests. | ||
* | ||
* @var \PHP_CodeSniffer\Config | ||
*/ | ||
private static $config; | ||
|
||
/** | ||
* Ruleset object for use in the tests. | ||
* | ||
* @var \PHP_CodeSniffer\Ruleset | ||
*/ | ||
private static $ruleset; | ||
|
||
/** | ||
* Initialize a PHPCS config and ruleset objects. | ||
* | ||
* @beforeClass | ||
* | ||
* @return void | ||
*/ | ||
public static function setUpConfigRuleset() | ||
{ | ||
parent::setUpBeforeClass(); | ||
|
||
self::$config = new ConfigDouble(); | ||
self::$config->sniffs = ['Dummy.Dummy.Dummy']; // Limiting it to just one (dummy) sniff. | ||
self::$config->cache = false; | ||
|
||
self::$ruleset = new Ruleset(self::$config); | ||
} | ||
|
||
/** | ||
* Test retrieving the normalized file name. | ||
* | ||
* @dataProvider dataGetName | ||
* | ||
* @param string $fileName The file name to pass. | ||
* @param string $expected The expected function return value. | ||
* | ||
* @return void | ||
*/ | ||
public function testGetName($fileName, $expected) | ||
{ | ||
$content = 'phpcs_input_file: ' . $fileName . \PHP_EOL; | ||
$content .= '<?php ' . \PHP_EOL . '$var = FALSE;' . \PHP_EOL; | ||
|
||
$phpcsFile = new DummyFile($content, self::$ruleset, self::$config); | ||
|
||
$this->assertSame($expected, FilePath::getName($phpcsFile)); | ||
} | ||
|
||
/** | ||
* Data provider. | ||
* | ||
* @see testGetName() For the array format. | ||
* | ||
* @return array<string, array<string, string>> | ||
*/ | ||
public static function dataGetName() | ||
{ | ||
return [ | ||
'file path is empty string' => [ | ||
'fileName' => '', | ||
'expected' => '', | ||
], | ||
'file path is stdin' => [ | ||
'fileName' => 'STDIN', | ||
'expected' => 'STDIN', | ||
], | ||
'file path is stdin (single-quoted)' => [ | ||
'fileName' => "'STDIN'", | ||
'expected' => 'STDIN', | ||
], | ||
'file path is stdin (double-quoted)' => [ | ||
'fileName' => '"STDIN"', | ||
'expected' => 'STDIN', | ||
], | ||
'file path is dot' => [ | ||
'fileName' => '.', | ||
'expected' => './', | ||
], | ||
'file path is file name only' => [ | ||
'fileName' => 'filename.php', | ||
'expected' => 'filename.php', | ||
], | ||
'file path is file name only (single-quoted)' => [ | ||
'fileName' => "'filename.php'", | ||
'expected' => 'filename.php', | ||
], | ||
'file path is file name only (double-quoted)' => [ | ||
'fileName' => '"filename.php"', | ||
'expected' => 'filename.php', | ||
], | ||
'file path with forward slashes' => [ | ||
'fileName' => 'my/path/to/filename.php', | ||
'expected' => 'my/path/to/filename.php', | ||
], | ||
'file path with backslashes' => [ | ||
'fileName' => 'my\path\to\filename.js', | ||
'expected' => 'my/path/to/filename.js', | ||
], | ||
'file path containing a mix of forward and backslashes' => [ | ||
'fileName' => '/my\path/to\myfile.inc', | ||
'expected' => '/my/path/to/myfile.inc', | ||
], | ||
'full windows file path, backslashes only' => [ | ||
'fileName' => 'C:\my\path\to\filename.css', | ||
'expected' => 'C:/my/path/to/filename.css', | ||
], | ||
]; | ||
} | ||
} |
Oops, something went wrong.