Introducing coding standards validation.

This commit is contained in:
Marcos Schratzenstaller
2019-04-22 17:28:46 -03:00
committed by Nathan Rice
parent 6e40c921ab
commit fd504d41ac
1624 changed files with 190339 additions and 262 deletions
@@ -0,0 +1,99 @@
<?php
/**
* Retrieve a filtered file list.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2019 Juliette Reinders Folmer. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
/**
* Class to create a file list with filtering.
*/
class FileList
{
/**
* The path to the project root directory.
*
* @var string
*/
protected $rootPath;
/**
* Recursive directory iterator.
*
* @var \DirectoryIterator
*/
protected $fileIterator;
/**
* Base regex to use if no filter regex is provided.
*
* Matches based on:
* - File path starts with the project root (replacement done in constructor).
* - Don't match .git/ files.
* - Don't match dot files, i.e. "." or "..".
* - Don't match backup files.
* - Match everything else in a case-insensitive manner.
*
* @var string
*/
private $baseRegex = '`^%s(?!\.git/)(?!(.*/)?\.+$)(?!.*\.(bak|orig)).*$`Dix';
/**
* Constructor.
*
* @param string $directory The directory to examine.
* @param string $rootPath Path to the project root.
* @param string $filter PCRE regular expression to filter the file list with.
*/
public function __construct($directory, $rootPath='', $filter='')
{
$this->rootPath = $rootPath;
$directory = new \RecursiveDirectoryIterator(
$directory,
\RecursiveDirectoryIterator::UNIX_PATHS
);
$flattened = new \RecursiveIteratorIterator(
$directory,
\RecursiveIteratorIterator::LEAVES_ONLY,
\RecursiveIteratorIterator::CATCH_GET_CHILD
);
if ($filter === '') {
$filter = sprintf($this->baseRegex, preg_quote($this->rootPath));
}
$this->fileIterator = new \RegexIterator($flattened, $filter);
return $this;
}//end __construct()
/**
* Retrieve the filtered file list as an array.
*
* @return array
*/
public function getList()
{
$fileList = [];
foreach ($this->fileIterator as $file) {
$fileList[] = str_replace($this->rootPath, '', $file);
}
return $fileList;
}//end getList()
}//end class
@@ -0,0 +1,360 @@
<?php
/**
* Validate the PHP_CodeSniffer PEAR package.xml file.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2019 Juliette Reinders Folmer. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
/**
* Validate the PHP_CodeSniffer PEAR package.xml file.
*/
class ValidatePEARPackageXML
{
/**
* The root directory of the project.
*
* @var string
*/
protected $projectRoot = '';
/**
* The contents of the package.xml file.
*
* @var \SimpleXMLElement
*/
protected $packageXML;
/**
* List of all files in the repo.
*
* @var array
*/
protected $allFiles = [];
/**
* Valid file roles.
*
* @var array
*
* @link https://pear.php.net/manual/en/developers.packagedef.intro.php#developers.packagedef.roles
*/
private $validRoles = [
'data' => true,
'doc' => true,
'ext' => true,
'extsrc' => true,
'php' => true,
'script' => true,
'src' => true,
'test' => true,
];
/**
* Files encountered in the package.xml <contents> tag.
*
* @var array
*/
private $listedContents = [];
/**
* Constructor.
*/
public function __construct()
{
$this->projectRoot = dirname(dirname(__DIR__)).'/';
$this->packageXML = simplexml_load_file($this->projectRoot.'package.xml');
$allFiles = (new FileList($this->projectRoot, $this->projectRoot))->getList();
$this->allFiles = array_flip($allFiles);
}//end __construct()
/**
* Validate the file listings in the package.xml file.
*
* @return void
*/
public function validate()
{
$exitCode = 0;
if ($this->checkContents() !== true) {
$exitCode = 1;
}
if ($this->checkPHPRelease() !== true) {
$exitCode = 1;
}
exit($exitCode);
}//end validate()
/**
* Validate the file listings in the <contents> tag.
*
* @return bool
*/
protected function checkContents()
{
echo PHP_EOL.'Checking Contents tag'.PHP_EOL;
echo '====================='.PHP_EOL;
$valid = true;
/*
* - Check that every file that is mentioned in the `<content>` tag exists in the repo.
* - Check that the "role" value is valid.
* - Check that the "baseinstalldir" value is valid.
*/
$valid = $this->walkDirTag($this->packageXML->contents);
if ($valid === true) {
echo "Existing listings in the Contents tag are valid.".PHP_EOL;
}
/*
* Verify that all files in the `src` and the `tests` directories are listed in the `<contents>` tag.
*/
$srcFiles = (new FileList(
$this->projectRoot.'src/',
$this->projectRoot,
'`\.(css|fixed|inc|js|php|xml)$`Di'
))->getList();
$testsFiles = (new FileList(
$this->projectRoot.'tests/',
$this->projectRoot,
'`\.(css|inc|js|php|xml)$`Di'
))->getList();
$files = array_merge($srcFiles, $testsFiles);
foreach ($files as $file) {
if (isset($this->listedContents[$file]) === true) {
continue;
}
echo "- File '{$file}' is missing from Contents tag.".PHP_EOL;
$valid = false;
}
if ($valid === true) {
echo "No missing files in the Contents tag.".PHP_EOL;
}
return $valid;
}//end checkContents()
/**
* Validate all child tags within a <dir> tag.
*
* @param \SimpleXMLElement $tag The current XML tag to examine.
* @param string $currentDirectory The complete relative path to the
* directory being examined.
*
* @return bool
*/
protected function walkDirTag($tag, $currentDirectory='')
{
$valid = true;
$name = (string) $tag['name'];
if ($name !== '/' && empty($name) === false) {
$currentDirectory .= $name.'/';
}
$children = $tag->children();
foreach ($children as $key => $value) {
if ($key === 'dir') {
if ($this->walkDirTag($value, $currentDirectory) === false) {
$valid = false;
}
}
if ($key === 'file') {
if ($this->checkFileTag($value, $currentDirectory) === false) {
$valid = false;
}
}
}
return $valid;
}//end walkDirTag()
/**
* Validate the information within a <file> tag.
*
* @param \SimpleXMLElement $tag The current XML tag to examine.
* @param string $currentDirectory The complete relative path to the
* directory being examined.
*
* @return bool
*/
protected function checkFileTag($tag, $currentDirectory='')
{
$valid = true;
$attributes = $tag->attributes();
$baseinstalldir = (string) $attributes['baseinstalldir'];
$name = $currentDirectory.(string) $attributes['name'];
$role = (string) $attributes['role'];
$this->listedContents[$name] = true;
if (empty($name) === true) {
echo "- Name attribute missing.".PHP_EOL;
$valid = false;
} else {
if (isset($this->allFiles[$name]) === false) {
echo "- File '{$name}' does not exist.".PHP_EOL;
$valid = false;
}
if (empty($role) === true) {
echo "- Role attribute missing for file '{$name}'.".PHP_EOL;
$valid = false;
} else {
if (isset($this->validRoles[$role]) === false) {
echo "- Role for file '{$name}' is invalid.".PHP_EOL;
$valid = false;
} else {
// Limited validation of the "role" tags.
if (strpos($name, 'Test.') !== false && $role !== 'test') {
echo "- Test files should have the role 'test'. Found: '$role' for file '{$name}'.".PHP_EOL;
$valid = false;
} else if ((strpos($name, 'Standard.xml') !== false || strpos($name, 'Sniff.php') !== false)
&& $role !== 'php'
) {
echo "- Sniff files, including sniff documentation files should have the role 'php'. Found: '$role' for file '{$name}'.".PHP_EOL;
$valid = false;
}
}
if (empty($baseinstalldir) === true) {
if ($role !== 'script' && strpos($name, 'tests/') !== 0) {
echo "- Baseinstalldir attribute missing for file '{$name}'.".PHP_EOL;
$valid = false;
}
} else {
if ($role === 'script' || strpos($name, 'tests/') === 0) {
echo "- Baseinstalldir for file '{$name}' should be empty.".PHP_EOL;
$valid = false;
}
if ($role !== 'script' && $baseinstalldir !== 'PHP/CodeSniffer') {
echo "- Baseinstalldir for file '{$name}' is invalid.".PHP_EOL;
$valid = false;
}
}
}//end if
}//end if
return $valid;
}//end checkFileTag()
/**
* Validate the file listings in the <phprelease> tags.
*
* @return bool True if the info in the "phprelease" tags is valid. False otherwise.
*/
protected function checkPHPRelease()
{
echo PHP_EOL.'Checking PHPRelease tags'.PHP_EOL;
echo '========================'.PHP_EOL;
$valid = true;
$listedFiles = [];
$releaseTags = 1;
/*
* - Check that every file that is mentioned in the `<phprelease>` tags exists in the repo.
* - Check that the "as" value is valid.
*/
foreach ($this->packageXML->phprelease as $release) {
foreach ($release->filelist->install as $install) {
$attributes = $install->attributes();
$name = (string) $attributes['name'];
$as = (string) $attributes['as'];
$listedFiles[$releaseTags][$name] = $as;
if (empty($as) === true || empty($name) === true) {
continue;
}
if (isset($this->allFiles[$name]) === false) {
echo "- File '{$name}' does not exist.".PHP_EOL;
$valid = false;
}
// Rest of the checks only apply to the test files.
if (strpos($name, 'tests/') !== 0) {
continue;
}
// Check validity of the tags for files in the tests root directory.
if (preg_match('`^tests/([^/]+\.php)$`', $name, $matches) === 1
&& ($as === $name || $as === $matches[1])
) {
continue;
}
// Check validity of the tags for files in the tests root subdirectories.
if (preg_match('`^tests/.+\.(php|inc|js|css|xml)$`', $name) === 1
&& $as === str_replace('tests/', 'CodeSniffer/', $name)
) {
continue;
}
echo "- Invalid 'as' attribute '{$as}' for test file '{$name}'.".PHP_EOL;
$valid = false;
}//end foreach
++$releaseTags;
}//end foreach
if ($valid === true) {
echo "Existing PHPRelease tags are valid.".PHP_EOL;
}
/*
* Verify that all files in the `tests` directory are listed in both `<phprelease>` tags.
*/
$testFiles = (new FileList($this->projectRoot.'tests/', $this->projectRoot, '`\.(inc|php|js|css|xml)$`Di'))->getList();
foreach ($testFiles as $file) {
foreach ($listedFiles as $key => $listed) {
if (isset($listed[$file]) === true) {
continue;
}
echo "- File '{$file}' is missing from PHPRelease tag [{$key}] .".PHP_EOL;
$valid = false;
}
}
if ($valid === true) {
echo "No missing PHPRelease tags.".PHP_EOL;
}
return $valid;
}//end checkPHPRelease()
}//end class