<?php
/**
* Functions for helping process standards.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Config;
class Standards
{
/**
* Get a list of paths where standards are installed.
*
* Unresolvable relative paths will be excluded from the results.
*
* @return array
*/
public static function getInstalledStandardPaths()
{
$ds = DIRECTORY_SEPARATOR;
$installedPaths = [dirname(dirname(__DIR__)).$ds.'src'.$ds.'Standards'];
$configPaths = Config::getConfigData('installed_paths');
if ($configPaths !== null) {
$installedPaths = array_merge($installedPaths, explode(',', $configPaths));
}
$resolvedInstalledPaths = [];
foreach ($installedPaths as $installedPath) {
if (substr($installedPath, 0, 1) === '.') {
$installedPath = Common::realPath(__DIR__.$ds.'..'.$ds.'..'.$ds.$installedPath);
if ($installedPath === false) {
continue;
}
}
$resolvedInstalledPaths[] = $installedPath;
}
return $resolvedInstalledPaths;
}//end getInstalledStandardPaths()
/**
* Get the details of all coding standards installed.
*
* Coding standards are directories located in the
* CodeSniffer/Standards directory. Valid coding standards
* include a Sniffs subdirectory.
*
* The details returned for each standard are:
* - path: the path to the coding standard's main directory
* - name: the name of the coding standard, as sourced from the ruleset.xml file
* - namespace: the namespace used by the coding standard, as sourced from the ruleset.xml file
*
* If you only need the paths to the installed standards,
* use getInstalledStandardPaths() instead as it performs less work to
* retrieve coding standard names.
*
* @param boolean $includeGeneric If true, the special "Generic"
* coding standard will be included
* if installed.
* @param string $standardsDir A specific directory to look for standards
* in. If not specified, PHP_CodeSniffer will
* look in its default locations.
*
* @return array
* @see getInstalledStandardPaths()
*/
public static function getInstalledStandardDetails(
$includeGeneric=false,
$standardsDir=''
) {
$rulesets = [];
if ($standardsDir === '') {
$installedPaths = self::getInstalledStandardPaths();
} else {
$installedPaths = [$standardsDir];
}
foreach ($installedPaths as $standardsDir) {
// Check if the installed dir is actually a standard itself.
$csFile = $standardsDir.'/ruleset.xml';
if (is_file($csFile) === true) {
$rulesets[] = $csFile;
continue;
}
if (is_dir($standardsDir) === false) {
continue;
}
$di = new \DirectoryIterator($standardsDir);
foreach ($di as $file) {
if ($file->isDir() === true && $file->isDot() === false) {
$filename = $file->getFilename();
// Ignore the special "Generic" standard.
if ($includeGeneric === false && $filename === 'Generic') {
continue;
}
// Valid coding standard dirs include a ruleset.
$csFile = $file->getPathname().'/ruleset.xml';
if (is_file($csFile) === true) {
$rulesets[] = $csFile;
}
}
}
}//end foreach
$installedStandards = [];
foreach ($rulesets as $rulesetPath) {
$ruleset = @simplexml_load_string(file_get_contents($rulesetPath));
if ($ruleset === false) {
continue;
}
$standardName = (string) $ruleset['name'];
$dirname = basename(dirname($rulesetPath));
if (isset($ruleset['namespace']) === true) {
$namespace = (string) $ruleset['namespace'];
} else {
$namespace = $dirname;
}
$installedStandards[$dirname] = [
'path' => dirname($rulesetPath),
'name' => $standardName,
'namespace' => $namespace,
];
}//end foreach
return $installedStandards;
}//end getInstalledStandardDetails()
/**
* Get a list of all coding standards installed.
*
* Coding standards are directories located in the
* CodeSniffer/Standards directory. Valid coding standards
* include a Sniffs subdirectory.
*
* @param boolean $includeGeneric If true, the special "Generic"
* coding standard will be included
* if installed.
* @param string $standardsDir A specific directory to look for standards
* in. If not specified, PHP_CodeSniffer will
* look in its default locations.
*
* @return array
* @see isInstalledStandard()
*/
public static function getInstalledStandards(
$includeGeneric=false,
$standardsDir=''
) {
$installedStandards = [];
if ($standardsDir === '') {
$installedPaths = self::getInstalledStandardPaths();
} else {
$installedPaths = [$standardsDir];
}
foreach ($installedPaths as $standardsDir) {
// Check if the installed dir is actually a standard itself.
$csFile = $standardsDir.'/ruleset.xml';
if (is_file($csFile) === true) {
$installedStandards[] = basename($standardsDir);
continue;
}
if (is_dir($standardsDir) === false) {
// Doesn't exist.
continue;
}
$di = new \DirectoryIterator($standardsDir);
foreach ($di as $file) {
if ($file->isDir() === true && $file->isDot() === false) {
$filename = $file->getFilename();
// Ignore the special "Generic" standard.
if ($includeGeneric === false && $filename === 'Generic') {
continue;
}
// Valid coding standard dirs include a ruleset.
$csFile = $file->getPathname().'/ruleset.xml';
if (is_file($csFile) === true) {
$installedStandards[] = $filename;
}
}
}
}//end foreach
return $installedStandards;
}//end getInstalledStandards()
/**
* Determine if a standard is installed.
*
* Coding standards are directories located in the
* CodeSniffer/Standards directory. Valid coding standards
* include a ruleset.xml file.
*
* @param string $standard The name of the coding standard.
*
* @return boolean
* @see getInstalledStandards()
*/
public static function isInstalledStandard($standard)
{
$path = self::getInstalledStandardPath($standard);
if ($path !== null && strpos($path, 'ruleset.xml') !== false) {
return true;
} else {
// This could be a custom standard, installed outside our
// standards directory.
$standard = Common::realPath($standard);
if ($standard === false) {
return false;
}
// Might be an actual ruleset file itUtil.
// If it has an XML extension, let's at least try it.
if (is_file($standard) === true
&& (substr(strtolower($standard), -4) === '.xml'
|| substr(strtolower($standard), -9) === '.xml.dist')
) {
return true;
}
// If it is a directory with a ruleset.xml file in it,
// it is a standard.
$ruleset = rtrim($standard, ' /\\').DIRECTORY_SEPARATOR.'ruleset.xml';
if (is_file($ruleset) === true) {
return true;
}
}//end if
return false;
}//end isInstalledStandard()
/**
* Return the path of an installed coding standard.
*
* Coding standards are directories located in the
* CodeSniffer/Standards directory. Valid coding standards
* include a ruleset.xml file.
*
* @param string $standard The name of the coding standard.
*
* @return string|null
*/
public static function getInstalledStandardPath($standard)
{
if (strpos($standard, '.') !== false) {
return null;
}
$installedPaths = self::getInstalledStandardPaths();
foreach ($installedPaths as $installedPath) {
$standardPath = $installedPath.DIRECTORY_SEPARATOR.$standard;
if (file_exists($standardPath) === false) {
if (basename($installedPath) !== $standard) {
continue;
}
$standardPath = $installedPath;
}
$path = Common::realpath($standardPath.DIRECTORY_SEPARATOR.'ruleset.xml');
if ($path !== false && is_file($path) === true) {
return $path;
} else if (Common::isPharFile($standardPath) === true) {
$path = Common::realpath($standardPath);
if ($path !== false) {
return $path;
}
}
}//end foreach
return null;
}//end getInstalledStandardPath()
/**
* Prints out a list of installed coding standards.
*
* @return void
*/
public static function printInstalledStandards()
{
$installedStandards = self::getInstalledStandards();
$numStandards = count($installedStandards);
if ($numStandards === 0) {
echo 'No coding standards are installed.'.PHP_EOL;
} else {
$lastStandard = array_pop($installedStandards);
if ($numStandards === 1) {
echo "The only coding standard installed is $lastStandard".PHP_EOL;
} else {
$standardList = implode(', ', $installedStandards);
$standardList .= ' and '.$lastStandard;
echo 'The installed coding standards are '.$standardList.PHP_EOL;
}
}
}//end printInstalledStandards()
}//end class