");
foreach ($violations as $violation) {
// This is going to be used as ID in HTML (deep anchoring).
@@ -401,7 +401,7 @@ class='info-lnk blck'
// Remove unnecessary tab/space characters at the line beginnings.
$html = self::reduceWhitespace($html);
- $w->write(sprintf($html, $excerptHtml));
+ $writer->write(sprintf($html, $excerptHtml));
}
}
@@ -458,8 +458,8 @@ protected static function colorize($message)
// Compile final regex, if not done already.
if (!self::$compiledHighlightRegex) {
$prepared = self::$descHighlightRules;
- array_walk($prepared, function (&$v, $k) {
- $v = "(?<{$k}>{$v['regex']})";
+ array_walk($prepared, function (&$value, $key) {
+ $value = "(?<{$key}>{$value['regex']})";
});
self::$compiledHighlightRegex = "#(" . implode('|', $prepared) . ")#";
@@ -467,13 +467,13 @@ protected static function colorize($message)
$rules = self::$descHighlightRules;
- return preg_replace_callback(self::$compiledHighlightRegex, function ($x) use ($rules) {
+ return preg_replace_callback(self::$compiledHighlightRegex, function ($matches) use ($rules) {
// Extract currently matched specification of highlighting (Match groups
// are named and we can find out which is not empty.).
- $definition = array_keys(array_intersect_key($rules, array_filter($x)));
+ $definition = array_keys(array_intersect_key($rules, array_filter($matches)));
$definition = reset($definition);
- return "
{$x[0]}";
+ return "
{$matches[0]}";
}, $message);
}
@@ -549,8 +549,8 @@ protected static function sumUpViolations($violations)
// We use "ref" reference to make things somewhat easier to read.
// Also, using a reference to non-existing array index doesn't throw a notice.
- if ($ns = $v->getNamespaceName()) {
- $ref = &$result[self::CATEGORY_NAMESPACE][$ns];
+ if ($namespaceName = $v->getNamespaceName()) {
+ $ref = &$result[self::CATEGORY_NAMESPACE][$namespaceName];
$ref = isset($ref) ? $ref + 1 : 1;
}
diff --git a/src/main/php/PHPMD/Renderer/JSONRenderer.php b/src/main/php/PHPMD/Renderer/JSONRenderer.php
index d91b9879c..a1c34ea77 100644
--- a/src/main/php/PHPMD/Renderer/JSONRenderer.php
+++ b/src/main/php/PHPMD/Renderer/JSONRenderer.php
@@ -45,7 +45,7 @@ public function renderReport(Report $report)
*
* @return array
*/
- private function initReportData()
+ protected function initReportData()
{
$data = array(
'version' => PHPMD::VERSION,
@@ -63,7 +63,7 @@ private function initReportData()
* @param array $data The report output to add the violations to.
* @return array The report output with violations, if any.
*/
- private function addViolationsToReport(Report $report, array $data)
+ protected function addViolationsToReport(Report $report, array $data)
{
$filesList = array();
/** @var RuleViolation $violation */
@@ -97,7 +97,7 @@ private function addViolationsToReport(Report $report, array $data)
* @param array $data The report output to add the errors to.
* @return array The report output with errors, if any.
*/
- private function addErrorsToReport(Report $report, array $data)
+ protected function addErrorsToReport(Report $report, array $data)
{
$errors = $report->getErrors();
if ($errors) {
diff --git a/src/main/php/PHPMD/Renderer/RendererFactory.php b/src/main/php/PHPMD/Renderer/RendererFactory.php
new file mode 100644
index 000000000..7796fc780
--- /dev/null
+++ b/src/main/php/PHPMD/Renderer/RendererFactory.php
@@ -0,0 +1,24 @@
+getStream());
+ $renderer = new BaselineRenderer(dirname($absolutePath));
+ $renderer->setWriter($writer);
+
+ return $renderer;
+ }
+}
diff --git a/src/main/php/PHPMD/Renderer/SARIFRenderer.php b/src/main/php/PHPMD/Renderer/SARIFRenderer.php
new file mode 100644
index 000000000..8962a976e
--- /dev/null
+++ b/src/main/php/PHPMD/Renderer/SARIFRenderer.php
@@ -0,0 +1,229 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\PHPMD;
+use PHPMD\Report;
+use PHPMD\Renderer\JSONRenderer;
+
+/**
+ * This class will render a SARIF (Static Analysis
+ * Results Interchange Format) report.
+ */
+class SARIFRenderer extends JSONRenderer
+{
+ /**
+ * Create report data and add renderer meta properties
+ *
+ * @return array
+ */
+ protected function initReportData()
+ {
+ $data = array(
+ 'version' => '2.1.0',
+ '$schema' =>
+ 'https://raw.githubusercontent.com/oasis-tcs/' .
+ 'sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
+ 'runs' => array(
+ array(
+ 'tool' => array(
+ 'driver' => array(
+ 'name' => 'PHPMD',
+ 'informationUri' => 'https://phpmd.org',
+ 'version' => PHPMD::VERSION,
+ 'rules' => array(),
+ ),
+ ),
+ 'originalUriBaseIds' => array(
+ 'WORKINGDIR' => array(
+ 'uri' => static::pathToUri(getcwd()) . '/',
+ ),
+ ),
+ 'results' => array(),
+ ),
+ ),
+ );
+
+ return $data;
+ }
+
+ /**
+ * Add violations, if any, to the report data
+ *
+ * @param Report $report The report with potential violations.
+ * @param array $data The report output to add the violations to.
+ * @return array The report output with violations, if any.
+ */
+ protected function addViolationsToReport(Report $report, array $data)
+ {
+ $rules = array();
+ $results = array();
+ $ruleIndices = array();
+
+ /** @var RuleViolation $violation */
+ foreach ($report->getRuleViolations() as $violation) {
+ $rule = $violation->getRule();
+ $ruleRef = str_replace(' ', '', $rule->getRuleSetName()) . '/' . $rule->getName();
+
+ if (!isset($ruleIndices[$ruleRef])) {
+ $ruleIndices[$ruleRef] = count($rules);
+
+ $ruleData = array(
+ 'id' => $ruleRef,
+ 'name' => $rule->getName(),
+ 'shortDescription' => array(
+ 'text' => $rule->getRuleSetName() . ': ' . $rule->getName(),
+ ),
+ 'messageStrings' => array(
+ 'default' => array(
+ 'text' => trim($rule->getMessage()),
+ ),
+ ),
+ 'help' => array(
+ 'text' => trim(str_replace("\n", ' ', $rule->getDescription())),
+ ),
+ 'helpUri' => $rule->getExternalInfoUrl(),
+ 'properties' => array(
+ 'ruleSet' => $rule->getRuleSetName(),
+ 'priority' => $rule->getPriority(),
+ ),
+ );
+
+ $examples = $rule->getExamples();
+ if (!empty($examples)) {
+ $ruleData['help']['markdown'] =
+ $ruleData['help']['text'] .
+ "\n\n### Example\n\n```php\n" .
+ implode("\n```\n\n```php\n", array_map('trim', $examples)) . "\n```";
+ }
+
+ $since = $rule->getSince();
+ if ($since) {
+ $ruleData['properties']['since'] = 'PHPMD ' . $since;
+ }
+
+ $rules[] = $ruleData;
+ }
+
+ $arguments = $violation->getArgs();
+ if ($arguments === null) {
+ $arguments = array();
+ }
+
+ $results[] = array(
+ 'ruleId' => $ruleRef,
+ 'ruleIndex' => $ruleIndices[$ruleRef],
+ 'message' => array(
+ 'id' => 'default',
+ 'arguments' => array_map('strval', $arguments),
+ 'text' => $violation->getDescription(),
+ ),
+ 'locations' => array(
+ array(
+ 'physicalLocation' => array(
+ 'artifactLocation' => static::pathToArtifactLocation($violation->getFileName()),
+ 'region' => array(
+ 'startLine' => $violation->getBeginLine(),
+ 'endLine' => $violation->getEndLine(),
+ ),
+ ),
+ )
+ ),
+ );
+ }
+
+ $data['runs'][0]['tool']['driver']['rules'] = $rules;
+ $data['runs'][0]['results'] = array_merge($data['runs'][0]['results'], $results);
+
+ return $data;
+ }
+
+ /**
+ * Add errors, if any, to the report data
+ *
+ * @param Report $report The report with potential errors.
+ * @param array $data The report output to add the errors to.
+ * @return array The report output with errors, if any.
+ */
+ protected function addErrorsToReport(Report $report, array $data)
+ {
+ $errors = $report->getErrors();
+ if ($errors) {
+ foreach ($errors as $error) {
+ $data['runs'][0]['results'][] = array(
+ 'level' => 'error',
+ 'message' => array(
+ 'text' => $error->getMessage(),
+ ),
+ 'locations' => array(
+ array(
+ 'physicalLocation' => array(
+ 'artifactLocation' => static::pathToArtifactLocation($error->getFile()),
+ ),
+ )
+ ),
+ );
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Makes an absolute path relative to the working directory
+ * if possible, otherwise prepends the `file://` protocol
+ * and returns the result as a SARIF `artifactLocation`
+ *
+ * @param string $path
+ * @return array
+ */
+ protected static function pathToArtifactLocation($path)
+ {
+ $workingDir = getcwd();
+ if (substr($path, 0, strlen($workingDir)) === $workingDir) {
+ // relative path
+ return array(
+ 'uri' => substr($path, strlen($workingDir) + 1),
+ 'uriBaseId' => 'WORKINGDIR',
+ );
+ }
+
+ // absolute path with protocol
+ return array(
+ 'uri' => static::pathToUri($path),
+ );
+ }
+
+ /**
+ * Converts an absolute path to a file:// URI
+ *
+ * @param string $path
+ * @return string
+ */
+ protected static function pathToUri($path)
+ {
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
+
+ // file:///C:/... on Windows systems
+ if (substr($path, 0, 1) !== '/') {
+ $path = '/' . $path;
+ }
+
+ return 'file://' . $path;
+ }
+}
diff --git a/src/main/php/PHPMD/Renderer/XMLRenderer.php b/src/main/php/PHPMD/Renderer/XMLRenderer.php
index 2a34441c8..218e04972 100644
--- a/src/main/php/PHPMD/Renderer/XMLRenderer.php
+++ b/src/main/php/PHPMD/Renderer/XMLRenderer.php
@@ -57,6 +57,7 @@ public function renderReport(Report $report)
{
$writer = $this->getWriter();
$writer->write('write('tool="phpmd" ');
$writer->write('timestamp="' . date('c') . '">');
$writer->write(PHP_EOL);
diff --git a/src/main/php/PHPMD/Report.php b/src/main/php/PHPMD/Report.php
index 5d4617e88..d7c0fd826 100644
--- a/src/main/php/PHPMD/Report.php
+++ b/src/main/php/PHPMD/Report.php
@@ -17,6 +17,8 @@
namespace PHPMD;
+use PHPMD\Baseline\BaselineValidator;
+
/**
* The report class collects all found violations and further information about
* a PHPMD run.
@@ -52,6 +54,14 @@ class Report
*/
private $errors = array();
+ /** @var BaselineValidator|null */
+ private $baselineValidator;
+
+ public function __construct(BaselineValidator $baselineValidator = null)
+ {
+ $this->baselineValidator = $baselineValidator;
+ }
+
/**
* Adds a rule violation to this report.
*
@@ -60,6 +70,10 @@ class Report
*/
public function addRuleViolation(RuleViolation $violation)
{
+ if ($this->baselineValidator !== null && $this->baselineValidator->isBaselined($violation)) {
+ return;
+ }
+
$fileName = $violation->getFileName();
if (!isset($this->ruleViolations[$fileName])) {
$this->ruleViolations[$fileName] = array();
diff --git a/src/main/php/PHPMD/Rule/AbstractLocalVariable.php b/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
index 1544e534b..84b71b303 100644
--- a/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
+++ b/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
@@ -48,7 +48,7 @@ abstract class AbstractLocalVariable extends AbstractRule
* @var array(string=>boolean)
* @link http://php.net/manual/en/reserved.variables.php
*/
- private static $superGlobals = array(
+ protected static $superGlobals = array(
'$argc' => true,
'$argv' => true,
'$_COOKIE' => true,
@@ -211,17 +211,18 @@ protected function getVariableImage($variable)
$base = $variable;
$parent = $this->getNode($variable->getParent());
- while ($parent && $parent instanceof ASTArrayIndexExpression && $parent->getChild(0) === $base->getNode()) {
+ while ($parent instanceof ASTArrayIndexExpression &&
+ $base instanceof ASTNode &&
+ $parent->getChild(0) === $base->getNode()
+ ) {
$base = $parent;
$parent = $this->getNode($base->getParent());
}
- if ($parent && $parent instanceof ASTPropertyPostfix) {
- $parent = $parent->getParent();
+ if ($parent instanceof ASTPropertyPostfix) {
+ $previousChildImage = $this->getParentMemberPrimaryPrefixImage($image, $parent);
- if ($parent instanceof ASTMemberPrimaryPrefix &&
- in_array($parent->getChild(0)->getImage(), $this->selfReferences)
- ) {
+ if (in_array($previousChildImage, $this->selfReferences, true)) {
return "::$image";
}
}
@@ -229,6 +230,23 @@ protected function getVariableImage($variable)
return $image;
}
+ protected function getParentMemberPrimaryPrefixImage($image, ASTPropertyPostfix $postfix)
+ {
+ do {
+ $postfix = $postfix->getParent();
+ } while ($postfix && $postfix->getChild(0) && $postfix->getChild(0)->getImage() === $image);
+
+ $previousChildImage = $postfix->getChild(0)->getImage();
+
+ if ($postfix instanceof ASTMemberPrimaryPrefix &&
+ in_array($previousChildImage, $this->selfReferences)
+ ) {
+ return $previousChildImage;
+ }
+
+ return null;
+ }
+
/**
* Return the PDepend node of ASTNode PHPMD node.
*
@@ -259,18 +277,23 @@ protected function isPassedByReference($variable)
if ($parent && $parent instanceof ASTArguments) {
$argumentPosition = array_search($this->getNode($variable), $parent->getChildren());
$function = $this->getNode($parent->getParent());
+ $functionParent = $this->getNode($function->getParent());
$functionName = $function->getImage();
+ if ($functionParent instanceof ASTMemberPrimaryPrefix) {
+ // @TODO: Find a way to handle methods
+ return false;
+ }
+
try {
$reflectionFunction = new ReflectionFunction($functionName);
$parameters = $reflectionFunction->getParameters();
- if ($parameters[$argumentPosition]->isPassedByReference()) {
+ if (isset($parameters[$argumentPosition]) && $parameters[$argumentPosition]->isPassedByReference()) {
return true;
}
} catch (ReflectionException $exception) {
// @TODO: Find a way to handle user-land functions
- // @TODO: Find a way to handle methods
}
}
diff --git a/src/main/php/PHPMD/Rule/CleanCode/BooleanArgumentFlag.php b/src/main/php/PHPMD/Rule/CleanCode/BooleanArgumentFlag.php
index cc4d475c8..3bb15eb67 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/BooleanArgumentFlag.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/BooleanArgumentFlag.php
@@ -50,7 +50,7 @@ public function apply(AbstractNode $node)
}
}
- private function isBooleanValue(ASTValue $value = null)
+ protected function isBooleanValue(ASTValue $value = null)
{
return $value && $value->isValueAvailable() && ($value->getValue() === true || $value->getValue() === false);
}
diff --git a/src/main/php/PHPMD/Rule/CleanCode/DuplicatedArrayKey.php b/src/main/php/PHPMD/Rule/CleanCode/DuplicatedArrayKey.php
index ee3d26da0..ba01bb2dc 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/DuplicatedArrayKey.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/DuplicatedArrayKey.php
@@ -57,7 +57,7 @@ public function apply(AbstractNode $node)
* @param ASTNode $node Array node.
* @return void
*/
- private function checkForDuplicatedArrayKeys(ASTNode $node)
+ protected function checkForDuplicatedArrayKeys(ASTNode $node)
{
$keys = array();
/** @var ASTArrayElement $arrayElement */
@@ -90,7 +90,7 @@ private function checkForDuplicatedArrayKeys(ASTNode $node)
* @param int $index Fallback in case of non-associative arrays
* @return AbstractASTNode Key name
*/
- private function normalizeKey(AbstractASTNode $node, $index)
+ protected function normalizeKey(AbstractASTNode $node, $index)
{
$childCount = count($node->getChildren());
// Skip, if there is no array key, just an array value
@@ -120,7 +120,7 @@ private function normalizeKey(AbstractASTNode $node, $index)
* @param PDependASTNode $key
* @return string
*/
- private function castStringFromLiteral(PDependASTNode $key)
+ protected function castStringFromLiteral(PDependASTNode $key)
{
$value = $key->getImage();
switch ($value) {
diff --git a/src/main/php/PHPMD/Rule/CleanCode/ElseExpression.php b/src/main/php/PHPMD/Rule/CleanCode/ElseExpression.php
index 6ec6ac7ee..0ec65781e 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/ElseExpression.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/ElseExpression.php
@@ -62,7 +62,7 @@ public function apply(AbstractNode $node)
* @param ASTNode $parent
* @return bool
*/
- private function isElseScope(AbstractNode $scope, ASTNode $parent)
+ protected function isElseScope(AbstractNode $scope, ASTNode $parent)
{
return (
count($parent->getChildren()) === 3 &&
@@ -76,7 +76,7 @@ private function isElseScope(AbstractNode $scope, ASTNode $parent)
* @param ASTNode $parent
* @return bool
*/
- private function isIfOrElseIfStatement(ASTNode $parent)
+ protected function isIfOrElseIfStatement(ASTNode $parent)
{
return ($parent->getName() === "if" || $parent->getName() === "elseif");
}
diff --git a/src/main/php/PHPMD/Rule/CleanCode/IfStatementAssignment.php b/src/main/php/PHPMD/Rule/CleanCode/IfStatementAssignment.php
index 3b88d950a..1f0f98978 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/IfStatementAssignment.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/IfStatementAssignment.php
@@ -67,7 +67,7 @@ public function apply(AbstractNode $node)
* @param AbstractNode $node An instance of MethodNode or FunctionNode class
* @return ASTNode[]
*/
- private function getStatements(AbstractNode $node)
+ protected function getStatements(AbstractNode $node)
{
return call_user_func_array('array_merge', array_map(function ($type) use ($node) {
return $node->findChildrenOfType($type);
@@ -80,7 +80,7 @@ private function getStatements(AbstractNode $node)
* @param ASTNode[] $statements Array of if and elseif clauses
* @return ASTExpression[]
*/
- private function getExpressions(array $statements)
+ protected function getExpressions(array $statements)
{
return array_map(function (ASTNode $statement) {
return $statement->getFirstChildOfType('Expression');
@@ -93,7 +93,7 @@ private function getExpressions(array $statements)
* @param ASTExpression[] $expressions Array of expressions
* @return ASTAssignmentExpression[]
*/
- private function getAssignments(array $expressions)
+ protected function getAssignments(array $expressions)
{
$assignments = array();
/** @var ASTNode $expression */
@@ -110,7 +110,7 @@ private function getAssignments(array $expressions)
* @param AbstractNode $node An instance of MethodNode or FunctionNode class
* @param ASTAssignmentExpression[] $assignments Array of assignments
*/
- private function addViolations(AbstractNode $node, array $assignments)
+ protected function addViolations(AbstractNode $node, array $assignments)
{
$processesViolations = array();
/** @var \PDepend\Source\AST\AbstractASTNode $assignment */
diff --git a/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php b/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
index 315f06bcd..423cf9b90 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
@@ -43,6 +43,7 @@ class MissingImport extends AbstractRule implements MethodAware, FunctionAware
*/
public function apply(AbstractNode $node)
{
+ $ignoreGlobal = $this->getBooleanProperty('ignore-global');
foreach ($node->findChildrenOfType('AllocationExpression') as $allocationNode) {
if (!$allocationNode) {
continue;
@@ -54,9 +55,15 @@ public function apply(AbstractNode $node)
continue;
}
+ if ($ignoreGlobal && $this->isGlobalNamespace($classNode)) {
+ continue;
+ }
+
$classNameLength = $classNode->getEndColumn() - $classNode->getStartColumn() + 1;
- $fqcnLength = strlen($classNode->getImage());
- if ($classNameLength === $fqcnLength) {
+ $className = $classNode->getImage();
+ $fqcnLength = strlen($className);
+
+ if ($classNameLength === $fqcnLength && substr($className, 0, 1) !== '$') {
$this->addViolation($classNode, array($classNode->getBeginLine(), $classNode->getStartColumn()));
}
}
@@ -72,4 +79,15 @@ protected function isSelfReference(ASTNode $classNode)
{
return in_array($classNode->getImage(), $this->selfReferences, true);
}
+
+ /**
+ * Check whether a given class node is in the global namespace
+ *
+ * @param ASTNode $classNode A class node to check.
+ * @return bool Whether the given class node is in the global namespace.
+ */
+ protected function isGlobalNamespace(ASTNode $classNode)
+ {
+ return !strpos($classNode->getImage(), '\\', 1);
+ }
}
diff --git a/src/main/php/PHPMD/Rule/CleanCode/StaticAccess.php b/src/main/php/PHPMD/Rule/CleanCode/StaticAccess.php
index 98460f75a..154e4fe1f 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/StaticAccess.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/StaticAccess.php
@@ -64,7 +64,7 @@ protected function isExcludedFromAnalysis($className, $exceptions)
return in_array(trim($className, " \t\n\r\0\x0B\\"), $exceptions);
}
- private function isStaticMethodCall(AbstractNode $methodCall)
+ protected function isStaticMethodCall(AbstractNode $methodCall)
{
return $methodCall->getChild(0)->getNode() instanceof ASTClassOrInterfaceReference &&
$methodCall->getChild(1)->getNode() instanceof ASTMethodPostfix &&
@@ -72,12 +72,12 @@ private function isStaticMethodCall(AbstractNode $methodCall)
!$this->isCallingSelf($methodCall);
}
- private function isCallingParent(AbstractNode $methodCall)
+ protected function isCallingParent(AbstractNode $methodCall)
{
return $methodCall->getChild(0)->getNode() instanceof ASTParentReference;
}
- private function isCallingSelf(AbstractNode $methodCall)
+ protected function isCallingSelf(AbstractNode $methodCall)
{
return $methodCall->getChild(0)->getNode() instanceof ASTSelfReference;
}
@@ -87,7 +87,7 @@ private function isCallingSelf(AbstractNode $methodCall)
*
* @return array
*/
- private function getExceptionsList()
+ protected function getExceptionsList()
{
try {
$exceptions = $this->getStringProperty('exceptions');
diff --git a/src/main/php/PHPMD/Rule/CleanCode/UndefinedVariable.php b/src/main/php/PHPMD/Rule/CleanCode/UndefinedVariable.php
index c4da18d3d..091714d88 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/UndefinedVariable.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/UndefinedVariable.php
@@ -43,7 +43,7 @@ class UndefinedVariable extends AbstractLocalVariable implements FunctionAware,
*
* @var array(string)
*/
- private $images = array();
+ protected $images = array();
/**
* This method checks that all local variables within the given function or
@@ -86,7 +86,7 @@ public function apply(AbstractNode $node)
*
* @param AbstractNode $node
*/
- private function collect(AbstractNode $node)
+ protected function collect(AbstractNode $node)
{
$this->collectPropertyPostfix($node);
$this->collectClosureParameters($node);
@@ -98,7 +98,7 @@ private function collect(AbstractNode $node)
$this->collectGlobalStatements($node);
}
- private function collectProperties($node)
+ protected function collectProperties($node)
{
if (!($node instanceof ASTClass)) {
return;
@@ -117,7 +117,7 @@ private function collectProperties($node)
* @param \PHPMD\Node\AbstractNode $node
* @return void
*/
- private function collectGlobalStatements(AbstractNode $node)
+ protected function collectGlobalStatements(AbstractNode $node)
{
$globalStatements = $node->findChildrenOfType('GlobalStatement');
@@ -134,7 +134,7 @@ private function collectGlobalStatements(AbstractNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectExceptionCatches(AbstractCallableNode $node)
+ protected function collectExceptionCatches(AbstractCallableNode $node)
{
$catchStatements = $node->findChildrenOfType('CatchStatement');
@@ -153,7 +153,7 @@ private function collectExceptionCatches(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectListExpressions(AbstractCallableNode $node)
+ protected function collectListExpressions(AbstractCallableNode $node)
{
$lists = $node->findChildrenOfType('ListExpression');
@@ -170,7 +170,7 @@ private function collectListExpressions(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectForeachStatements(AbstractCallableNode $node)
+ protected function collectForeachStatements(AbstractCallableNode $node)
{
$foreachStatements = $node->findChildrenOfType('ForeachStatement');
@@ -195,7 +195,7 @@ private function collectForeachStatements(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectClosureParameters(AbstractCallableNode $node)
+ protected function collectClosureParameters(AbstractCallableNode $node)
{
$closures = $node->findChildrenOfType('Closure');
@@ -211,7 +211,7 @@ private function collectClosureParameters(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractCallableNode $parentNode
* @return bool
*/
- private function checkVariableDefined(ASTNode $variable, AbstractCallableNode $parentNode)
+ protected function checkVariableDefined(ASTNode $variable, AbstractCallableNode $parentNode)
{
$image = $this->getVariableImage($variable);
@@ -224,7 +224,7 @@ private function checkVariableDefined(ASTNode $variable, AbstractCallableNode $p
* @param \PHPMD\Node\AbstractNode $node
* @return void
*/
- private function collectParameters(AbstractNode $node)
+ protected function collectParameters(AbstractNode $node)
{
// Get formal parameter container
$parameters = $node->getFirstChildOfType('FormalParameters');
@@ -243,7 +243,7 @@ private function collectParameters(AbstractNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectAssignments(AbstractCallableNode $node)
+ protected function collectAssignments(AbstractCallableNode $node)
{
foreach ($node->findChildrenOfType('AssignmentExpression') as $assignment) {
$variable = $assignment->getChild(0);
@@ -266,7 +266,7 @@ private function collectAssignments(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractNode $node
* @return void
*/
- private function collectPropertyPostfix(AbstractNode $node)
+ protected function collectPropertyPostfix(AbstractNode $node)
{
$properties = $node->findChildrenOfType('PropertyPostfix');
@@ -285,7 +285,7 @@ private function collectPropertyPostfix(AbstractNode $node)
* @param ASTVariable|ASTPropertyPostfix|ASTVariableDeclarator $variable
* @return void
*/
- private function addVariableDefinition($variable)
+ protected function addVariableDefinition($variable)
{
$image = $this->getVariableImage($variable);
@@ -302,7 +302,7 @@ private function addVariableDefinition($variable)
*
* @return boolean
*/
- private function isNameAllowedInContext(AbstractCallableNode $node, ASTNode $variable)
+ protected function isNameAllowedInContext(AbstractCallableNode $node, ASTNode $variable)
{
return (
$node instanceof MethodNode &&
diff --git a/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php b/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
index 5ddc1ee6d..43849adf9 100644
--- a/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
+++ b/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
@@ -71,14 +71,14 @@ public function apply(AbstractNode $node)
}
}
- private function isValid($methodName)
+ protected function isValid($methodName)
{
if ($this->getBooleanProperty('allow-underscore-test') && strpos($methodName, 'test') === 0) {
- return preg_match('/^test[a-zA-Z0-9]*([_][a-z][a-zA-Z0-9]*)?$/', $methodName);
+ return preg_match('/^test[a-zA-Z0-9]*(_[a-z][a-zA-Z0-9]*)*$/', $methodName);
}
if ($this->getBooleanProperty('allow-underscore')) {
- return preg_match('/^[_]?[a-z][a-zA-Z0-9]*$/', $methodName);
+ return preg_match('/^_?[a-z][a-zA-Z0-9]*$/', $methodName);
}
return preg_match('/^[a-z][a-zA-Z0-9]*$/', $methodName);
diff --git a/src/main/php/PHPMD/Rule/Controversial/CamelCaseParameterName.php b/src/main/php/PHPMD/Rule/Controversial/CamelCaseParameterName.php
index ca9fe3a7b..20b64060f 100644
--- a/src/main/php/PHPMD/Rule/Controversial/CamelCaseParameterName.php
+++ b/src/main/php/PHPMD/Rule/Controversial/CamelCaseParameterName.php
@@ -51,7 +51,7 @@ public function apply(AbstractNode $node)
}
}
- private function isValid($parameterName)
+ protected function isValid($parameterName)
{
if ($this->getBooleanProperty('allow-underscore')) {
return preg_match('/^\$[_]?[a-z][a-zA-Z0-9]*$/', $parameterName);
diff --git a/src/main/php/PHPMD/Rule/Controversial/CamelCaseVariableName.php b/src/main/php/PHPMD/Rule/Controversial/CamelCaseVariableName.php
index e055d4a23..4dbfd87ec 100644
--- a/src/main/php/PHPMD/Rule/Controversial/CamelCaseVariableName.php
+++ b/src/main/php/PHPMD/Rule/Controversial/CamelCaseVariableName.php
@@ -33,7 +33,7 @@ class CamelCaseVariableName extends AbstractRule implements MethodAware, Functio
/**
* @var array
*/
- private $exceptions = array(
+ protected $exceptions = array(
'$php_errormsg',
'$http_response_header',
'$GLOBALS',
@@ -68,7 +68,7 @@ public function apply(AbstractNode $node)
}
}
- private function isValid($variable)
+ protected function isValid($variable)
{
$image = $variable->getImage();
diff --git a/src/main/php/PHPMD/Rule/Design/CountInLoopExpression.php b/src/main/php/PHPMD/Rule/Design/CountInLoopExpression.php
index 5a2744c6c..2c6fdc705 100644
--- a/src/main/php/PHPMD/Rule/Design/CountInLoopExpression.php
+++ b/src/main/php/PHPMD/Rule/Design/CountInLoopExpression.php
@@ -44,7 +44,7 @@ class CountInLoopExpression extends AbstractRule implements ClassAware
*
* @var array
*/
- private $unwantedFunctions = array('count', 'sizeof');
+ protected $unwantedFunctions = array('count', 'sizeof');
/**
* List of already processed functions
diff --git a/src/main/php/PHPMD/Rule/Design/DevelopmentCodeFragment.php b/src/main/php/PHPMD/Rule/Design/DevelopmentCodeFragment.php
index 0c183f4ab..74ff7cb77 100644
--- a/src/main/php/PHPMD/Rule/Design/DevelopmentCodeFragment.php
+++ b/src/main/php/PHPMD/Rule/Design/DevelopmentCodeFragment.php
@@ -49,6 +49,7 @@ public function apply(AbstractNode $node)
$fragment = str_replace("{$namespace}\\", "", $fragment);
}
$fragment = strtolower($fragment);
+ $fragment = trim($fragment, "\\");
if (false === in_array($fragment, $this->getSuspectImages())) {
continue;
}
@@ -68,7 +69,7 @@ public function apply(AbstractNode $node)
*
* @return array
*/
- private function getSuspectImages()
+ protected function getSuspectImages()
{
return array_map(
'strtolower',
diff --git a/src/main/php/PHPMD/Rule/Design/TooManyMethods.php b/src/main/php/PHPMD/Rule/Design/TooManyMethods.php
index 7a4c8d4f7..e33d32986 100644
--- a/src/main/php/PHPMD/Rule/Design/TooManyMethods.php
+++ b/src/main/php/PHPMD/Rule/Design/TooManyMethods.php
@@ -32,7 +32,7 @@ class TooManyMethods extends AbstractRule implements ClassAware
*
* @var string
*/
- private $ignoreRegexp;
+ protected $ignoreRegexp;
/**
* This method checks the number of methods with in a given class and checks
@@ -71,7 +71,7 @@ public function apply(AbstractNode $node)
* @param \PHPMD\Node\AbstractTypeNode $node
* @return integer
*/
- private function countMethods(AbstractTypeNode $node)
+ protected function countMethods(AbstractTypeNode $node)
{
$count = 0;
foreach ($node->getMethodNames() as $name) {
diff --git a/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php b/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
index 454148045..f516e8554 100644
--- a/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
+++ b/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
@@ -32,7 +32,7 @@ class TooManyPublicMethods extends AbstractRule implements ClassAware
*
* @var string
*/
- private $ignoreRegexp;
+ protected $ignoreRegexp;
/**
* This method checks the number of public methods with in a given class and checks
@@ -46,14 +46,19 @@ public function apply(AbstractNode $node)
$this->ignoreRegexp = $this->getStringProperty('ignorepattern');
$threshold = $this->getIntProperty('maxmethods');
- if ($node->getMetric('npm') <= $threshold) {
+ $publicMethodsCount = $node->getMetric('npm'); // NPM stands for Number of Public Methods
+
+ if ($publicMethodsCount !== null && $publicMethodsCount <= $threshold) {
return;
}
+
/** @var AbstractTypeNode $node */
$nom = $this->countMethods($node);
+
if ($nom <= $threshold) {
return;
}
+
$this->addViolation(
$node,
array(
@@ -71,15 +76,29 @@ public function apply(AbstractNode $node)
* @param \PHPMD\Node\AbstractTypeNode $node
* @return integer
*/
- private function countMethods(AbstractTypeNode $node)
+ protected function countMethods(AbstractTypeNode $node)
{
$count = 0;
foreach ($node->getMethods() as $method) {
- if ($method->getNode()->isPublic() && preg_match($this->ignoreRegexp, $method->getName()) === 0) {
+ if ($method->getNode()->isPublic() && !$this->isIgnoredMethodName($method->getName())) {
++$count;
}
}
return $count;
}
+
+ /**
+ * Return true if the method name is ignored by the current ignoreRegexp pattern.
+ *
+ * ignoreRegexp is given to the rule using ignorepattern option.
+ *
+ * @param string $methodName
+ * @return bool
+ */
+ private function isIgnoredMethodName($methodName)
+ {
+ return !empty($this->ignoreRegexp) &&
+ preg_match($this->ignoreRegexp, $methodName) === 1;
+ }
}
diff --git a/src/main/php/PHPMD/Rule/Naming/BooleanGetMethodName.php b/src/main/php/PHPMD/Rule/Naming/BooleanGetMethodName.php
index 85cd65ab1..67a03ebe3 100644
--- a/src/main/php/PHPMD/Rule/Naming/BooleanGetMethodName.php
+++ b/src/main/php/PHPMD/Rule/Naming/BooleanGetMethodName.php
@@ -51,7 +51,7 @@ public function apply(AbstractNode $node)
* @param \PHPMD\Node\MethodNode $node
* @return boolean
*/
- private function isBooleanGetMethod(MethodNode $node)
+ protected function isBooleanGetMethod(MethodNode $node)
{
return $this->isGetterMethodName($node)
&& $this->isReturnTypeBoolean($node)
@@ -64,7 +64,7 @@ private function isBooleanGetMethod(MethodNode $node)
* @param \PHPMD\Node\MethodNode $node
* @return boolean
*/
- private function isGetterMethodName(MethodNode $node)
+ protected function isGetterMethodName(MethodNode $node)
{
return (preg_match('(^_?get)i', $node->getImage()) > 0);
}
@@ -75,7 +75,7 @@ private function isGetterMethodName(MethodNode $node)
* @param \PHPMD\Node\MethodNode $node
* @return boolean
*/
- private function isReturnTypeBoolean(MethodNode $node)
+ protected function isReturnTypeBoolean(MethodNode $node)
{
$comment = $node->getDocComment();
@@ -89,7 +89,7 @@ private function isReturnTypeBoolean(MethodNode $node)
* @param \PHPMD\Node\MethodNode $node
* @return boolean
*/
- private function isParameterizedOrIgnored(MethodNode $node)
+ protected function isParameterizedOrIgnored(MethodNode $node)
{
if ($this->getBooleanProperty('checkParameterizedMethods')) {
return $node->getParameterCount() === 0;
diff --git a/src/main/php/PHPMD/Rule/Naming/LongClassName.php b/src/main/php/PHPMD/Rule/Naming/LongClassName.php
new file mode 100644
index 000000000..41e352d10
--- /dev/null
+++ b/src/main/php/PHPMD/Rule/Naming/LongClassName.php
@@ -0,0 +1,70 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Rule\Naming;
+
+use PHPMD\AbstractNode;
+use PHPMD\AbstractRule;
+use PHPMD\Rule\ClassAware;
+use PHPMD\Rule\InterfaceAware;
+use PHPMD\Utility\Strings;
+
+/**
+ * This rule checks if an interface or class name exceeds the configured length excluding certain configured suffixes
+ */
+class LongClassName extends AbstractRule implements ClassAware, InterfaceAware
+{
+ /**
+ * Temporary cache of configured suffixes to subtract
+ *
+ * @var string[]|null
+ */
+ protected $subtractSuffixes;
+
+ /**
+ * Check if a class name exceeds the configured maximum length and emit a rule violation
+ *
+ * @param \PHPMD\AbstractNode $node
+ * @return void
+ */
+ public function apply(AbstractNode $node)
+ {
+ $threshold = $this->getIntProperty('maximum');
+ $classOrInterfaceName = $node->getName();
+ if (Strings::lengthWithoutSuffixes($classOrInterfaceName, $this->getSubtractSuffixList()) <= $threshold) {
+ return;
+ }
+ $this->addViolation($node, array($classOrInterfaceName, $threshold));
+ }
+
+ /**
+ * Gets array of suffixes from property
+ *
+ * @return string[]
+ */
+ protected function getSubtractSuffixList()
+ {
+ if ($this->subtractSuffixes === null) {
+ $this->subtractSuffixes = Strings::splitToList(
+ $this->getStringProperty('subtract-suffixes', ''),
+ ','
+ );
+ }
+
+ return $this->subtractSuffixes;
+ }
+}
diff --git a/src/main/php/PHPMD/Rule/Naming/LongVariable.php b/src/main/php/PHPMD/Rule/Naming/LongVariable.php
index 10c39ccd3..3b26c390e 100644
--- a/src/main/php/PHPMD/Rule/Naming/LongVariable.php
+++ b/src/main/php/PHPMD/Rule/Naming/LongVariable.php
@@ -22,6 +22,7 @@
use PHPMD\Rule\ClassAware;
use PHPMD\Rule\FunctionAware;
use PHPMD\Rule\MethodAware;
+use PHPMD\Utility\Strings;
/**
* This rule class will detect variables, parameters and properties with really
@@ -34,7 +35,7 @@ class LongVariable extends AbstractRule implements ClassAware, MethodAware, Func
*
* @var string[]|null
*/
- private $subtractSuffixes;
+ protected $subtractSuffixes;
/**
* Temporary map holding variables that were already processed in the
@@ -42,7 +43,7 @@ class LongVariable extends AbstractRule implements ClassAware, MethodAware, Func
*
* @var array(string=>boolean)
*/
- private $processedVariables = array();
+ protected $processedVariables = array();
/**
* Extracts all variable and variable declarator nodes from the given node
@@ -101,17 +102,20 @@ protected function checkNodeImage(AbstractNode $node)
*
* @param \PHPMD\AbstractNode $node
* @return void
+ * @SuppressWarnings(PHPMD.LongVariable)
*/
protected function checkMaximumLength(AbstractNode $node)
{
$threshold = $this->getIntProperty('maximum');
- if ($threshold >= $this->getStringLength($node->getImage(), $this->getSubtractSuffixList()) - 1) {
+ $variableName = $node->getImage();
+ $lengthWithoutDollarSign = Strings::lengthWithoutSuffixes($variableName, $this->getSubtractSuffixList()) - 1;
+ if ($lengthWithoutDollarSign <= $threshold) {
return;
}
if ($this->isNameAllowedInContext($node)) {
return;
}
- $this->addViolation($node, array($node->getImage(), $threshold));
+ $this->addViolation($node, array($variableName, $threshold));
}
/**
@@ -121,34 +125,11 @@ protected function checkMaximumLength(AbstractNode $node)
* @param \PHPMD\AbstractNode $node
* @return boolean
*/
- private function isNameAllowedInContext(AbstractNode $node)
+ protected function isNameAllowedInContext(AbstractNode $node)
{
return $this->isChildOf($node, 'MemberPrimaryPrefix');
}
- /**
- * Returns the length of the variable name, excluding at most one suffix.
- *
- * @param string $variableName Variable name to calculate the length for.
- * @param array $subtractSuffixes Optional list of suffixes to exclude from the calculated length.
- * @return int The length of the string, without suffix, if applicable.
- */
- private function getStringLength($variableName, array $subtractSuffixes)
- {
- $variableNameLength = strlen($variableName);
-
- foreach ($subtractSuffixes as $suffix) {
- $suffixLength = strlen($suffix);
- if (substr($variableName, -$suffixLength) === $suffix) {
- $variableName = substr($variableName, 0, $variableNameLength - $suffixLength);
-
- return strlen($variableName);
- }
- }
-
- return $variableNameLength;
- }
-
/**
* Checks if the given node is a direct or indirect child of a node with
* the given type.
@@ -157,7 +138,7 @@ private function getStringLength($variableName, array $subtractSuffixes)
* @param string $type
* @return boolean
*/
- private function isChildOf(AbstractNode $node, $type)
+ protected function isChildOf(AbstractNode $node, $type)
{
$parent = $node->getParent();
while (is_object($parent)) {
@@ -207,26 +188,12 @@ protected function isNotProcessed(AbstractNode $node)
*
* @return string[]
*/
- private function getSubtractSuffixList()
+ protected function getSubtractSuffixList()
{
- if ($this->subtractSuffixes !== null) {
- return $this->subtractSuffixes;
+ if ($this->subtractSuffixes === null) {
+ $this->subtractSuffixes = Strings::splitToList($this->getStringProperty('subtract-suffixes', ''), ',');
}
- try {
- $suffixes = $this->getStringProperty('subtract-suffixes');
- } catch (\OutOfBoundsException $e) {
- return $this->subtractSuffixes = array();
- }
-
- return $this->subtractSuffixes = array_filter(
- array_map(
- 'trim',
- explode(',', $suffixes)
- ),
- function ($value) {
- return $value !== '';
- }
- );
+ return $this->subtractSuffixes;
}
}
diff --git a/src/main/php/PHPMD/Rule/Naming/ShortClassName.php b/src/main/php/PHPMD/Rule/Naming/ShortClassName.php
new file mode 100644
index 000000000..4d0ab8a55
--- /dev/null
+++ b/src/main/php/PHPMD/Rule/Naming/ShortClassName.php
@@ -0,0 +1,75 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Rule\Naming;
+
+use PHPMD\AbstractNode;
+use PHPMD\AbstractRule;
+use PHPMD\Rule\ClassAware;
+use PHPMD\Rule\InterfaceAware;
+use PHPMD\Utility\Strings;
+
+/**
+ * This rule will detect classes and interfaces with names that are too short.
+ */
+class ShortClassName extends AbstractRule implements ClassAware, InterfaceAware
+{
+ /**
+ * Temporary cache of configured exceptions. Have name as key
+ *
+ * @var array|null
+ */
+ protected $exceptions;
+
+ /**
+ * Check if a class or interface name is below the minimum configured length and emit a rule violation
+ *
+ * @param \PHPMD\AbstractNode $node
+ * @return void
+ */
+ public function apply(AbstractNode $node)
+ {
+ $threshold = $this->getIntProperty('minimum');
+ $classOrInterfaceName = $node->getName();
+ if (strlen($classOrInterfaceName) >= $threshold) {
+ return;
+ }
+
+ $exceptions = $this->getExceptionsList();
+ if (isset($exceptions[$classOrInterfaceName])) {
+ return;
+ }
+
+ $this->addViolation($node, array($classOrInterfaceName, $threshold));
+ }
+
+ /**
+ * Gets array of exceptions from property
+ *
+ * @return array
+ */
+ protected function getExceptionsList()
+ {
+ if ($this->exceptions === null) {
+ $this->exceptions = array_flip(
+ Strings::splitToList($this->getStringProperty('exceptions', ''), ',')
+ );
+ }
+
+ return $this->exceptions;
+ }
+}
diff --git a/src/main/php/PHPMD/Rule/Naming/ShortMethodName.php b/src/main/php/PHPMD/Rule/Naming/ShortMethodName.php
index 7ea95b81c..d24cb7781 100644
--- a/src/main/php/PHPMD/Rule/Naming/ShortMethodName.php
+++ b/src/main/php/PHPMD/Rule/Naming/ShortMethodName.php
@@ -63,7 +63,7 @@ public function apply(AbstractNode $node)
*
* @return array
*/
- private function getExceptionsList()
+ protected function getExceptionsList()
{
try {
$exceptions = $this->getStringProperty('exceptions');
diff --git a/src/main/php/PHPMD/Rule/Naming/ShortVariable.php b/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
index 5e78005aa..b61544882 100644
--- a/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
+++ b/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
@@ -35,7 +35,7 @@ class ShortVariable extends AbstractRule implements ClassAware, MethodAware, Fun
*
* @var array(string=>boolean)
*/
- private $processedVariables = array();
+ protected $processedVariables = array();
/**
* Extracts all variable and variable declarator nodes from the given node
@@ -68,7 +68,7 @@ public function apply(AbstractNode $node)
* @param AbstractNode $node
* @return void
*/
- private function applyClass(AbstractNode $node)
+ protected function applyClass(AbstractNode $node)
{
$fields = $node->findChildrenOfType('FieldDeclaration');
foreach ($fields as $field) {
@@ -89,7 +89,7 @@ private function applyClass(AbstractNode $node)
* @param AbstractNode $node
* @return void
*/
- private function applyNonClass(AbstractNode $node)
+ protected function applyNonClass(AbstractNode $node)
{
$declarators = $node->findChildrenOfType('VariableDeclarator');
foreach ($declarators as $declarator) {
@@ -150,7 +150,7 @@ protected function checkMinimumLength(AbstractNode $node)
*
* @return array
*/
- private function getExceptionsList()
+ protected function getExceptionsList()
{
try {
$exceptions = $this->getStringProperty('exceptions');
@@ -169,14 +169,67 @@ private function getExceptionsList()
* @param \PHPMD\AbstractNode $node
* @return boolean
*/
- private function isNameAllowedInContext(AbstractNode $node)
+ protected function isNameAllowedInContext(AbstractNode $node)
{
+ $parent = $node->getParent();
+
+ if ($parent && $parent->isInstanceOf('ForeachStatement')) {
+ return $this->isInitializedInLoop($node);
+ }
+
return $this->isChildOf($node, 'CatchStatement')
|| $this->isChildOf($node, 'ForInit')
- || $this->isChildOf($node, 'ForeachStatement')
|| $this->isChildOf($node, 'MemberPrimaryPrefix');
}
+ /**
+ * Checks if a short name is initialized within a foreach loop statement
+ *
+ * @param \PHPMD\AbstractNode $node
+ * @return boolean
+ */
+ protected function isInitializedInLoop(AbstractNode $node)
+ {
+ if (!$this->getBooleanProperty('allow-short-variables-in-loop', true)) {
+ return false;
+ }
+
+ $exceptionVariables = array();
+
+ $parentForeaches = $this->getParentsOfType($node, 'ForeachStatement');
+ foreach ($parentForeaches as $foreach) {
+ foreach ($foreach->getChildren() as $foreachChild) {
+ $exceptionVariables[] = $foreachChild->getImage();
+ }
+ }
+
+ $exceptionVariables = array_filter(array_unique($exceptionVariables));
+
+ return in_array($node->getImage(), $exceptionVariables, true);
+ }
+
+ /**
+ * Returns an array of parent nodes of the specified type
+ *
+ * @param \PHPMD\AbstractNode $node
+ * @return array
+ */
+ protected function getParentsOfType(AbstractNode $node, $type)
+ {
+ $parents = array();
+
+ $parent = $node->getParent();
+
+ while (is_object($parent)) {
+ if ($parent->isInstanceOf($type)) {
+ $parents[] = $parent;
+ }
+ $parent = $parent->getParent();
+ }
+
+ return $parents;
+ }
+
/**
* Checks if the given node is a direct or indirect child of a node with
* the given type.
@@ -185,7 +238,7 @@ private function isNameAllowedInContext(AbstractNode $node)
* @param string $type
* @return boolean
*/
- private function isChildOf(AbstractNode $node, $type)
+ protected function isChildOf(AbstractNode $node, $type)
{
$parent = $node->getParent();
while (is_object($parent)) {
diff --git a/src/main/php/PHPMD/Rule/UnusedFormalParameter.php b/src/main/php/PHPMD/Rule/UnusedFormalParameter.php
index 364da6488..33498cd9b 100644
--- a/src/main/php/PHPMD/Rule/UnusedFormalParameter.php
+++ b/src/main/php/PHPMD/Rule/UnusedFormalParameter.php
@@ -32,7 +32,7 @@ class UnusedFormalParameter extends AbstractLocalVariable implements FunctionAwa
*
* @var \PHPMD\Node\ASTNode[]
*/
- private $nodes = array();
+ protected $nodes = array();
/**
* This method checks that all parameters of a given function or method are
@@ -76,7 +76,7 @@ public function apply(AbstractNode $node)
* @param \PHPMD\AbstractNode $node
* @return boolean
*/
- private function isAbstractMethod(AbstractNode $node)
+ protected function isAbstractMethod(AbstractNode $node)
{
if ($node instanceof MethodNode) {
return $node->isAbstract();
@@ -92,7 +92,7 @@ private function isAbstractMethod(AbstractNode $node)
* @param \PHPMD\AbstractNode $node
* @return boolean
*/
- private function isInheritedSignature(AbstractNode $node)
+ protected function isInheritedSignature(AbstractNode $node)
{
if ($node instanceof MethodNode) {
return preg_match('/@inheritdoc/i', $node->getDocComment()) === 1;
@@ -107,7 +107,7 @@ private function isInheritedSignature(AbstractNode $node)
* @param AbstractNode $node
* @return boolean
*/
- private function isMagicMethod(AbstractNode $node)
+ protected function isMagicMethod(AbstractNode $node)
{
if (!($node instanceof MethodNode)) {
return false;
@@ -138,7 +138,7 @@ private function isMagicMethod(AbstractNode $node)
* @return boolean
* @since 1.2.1
*/
- private function isNotDeclaration(AbstractNode $node)
+ protected function isNotDeclaration(AbstractNode $node)
{
if ($node instanceof MethodNode) {
return !$node->isDeclaration();
@@ -154,16 +154,16 @@ private function isNotDeclaration(AbstractNode $node)
* @param \PHPMD\AbstractNode $node
* @return void
*/
- private function collectParameters(AbstractNode $node)
+ protected function collectParameters(AbstractNode $node)
{
- // First collect the formal parameters container
- $parameters = $node->getFirstChildOfType('FormalParameters');
+ // First collect the formal parameters containers
+ foreach ($node->findChildrenOfType('FormalParameters') as $parameters) {
+ // Now get all declarators in the formal parameters container
+ $declarators = $parameters->findChildrenOfType('VariableDeclarator');
- // Now get all declarators in the formal parameters container
- $declarators = $parameters->findChildrenOfType('VariableDeclarator');
-
- foreach ($declarators as $declarator) {
- $this->nodes[$declarator->getImage()] = $declarator;
+ foreach ($declarators as $declarator) {
+ $this->nodes[$declarator->getImage()] = $declarator;
+ }
}
}
@@ -175,7 +175,7 @@ private function collectParameters(AbstractNode $node)
* @param \PHPMD\AbstractNode $node
* @return void
*/
- private function removeUsedParameters(AbstractNode $node)
+ protected function removeUsedParameters(AbstractNode $node)
{
$this->removeRegularVariables($node);
$this->removeCompoundVariables($node);
@@ -188,7 +188,7 @@ private function removeUsedParameters(AbstractNode $node)
* @param \PHPMD\AbstractNode $node The node to remove the regular variables from.
* @return void
*/
- private function removeRegularVariables(AbstractNode $node)
+ protected function removeRegularVariables(AbstractNode $node)
{
$variables = $node->findChildrenOfTypeVariable();
@@ -218,7 +218,7 @@ private function removeRegularVariables(AbstractNode $node)
* @param \PHPMD\AbstractNode $node The node to remove the compound variables from.
* @return void
*/
- private function removeCompoundVariables(AbstractNode $node)
+ protected function removeCompoundVariables(AbstractNode $node)
{
$compoundVariables = $node->findChildrenOfType('CompoundVariable');
@@ -243,7 +243,7 @@ private function removeCompoundVariables(AbstractNode $node)
* @param \PHPMD\AbstractNode $node The node to remove the referneced variables from.
* @return void
*/
- private function removeVariablesUsedByFuncGetArgs(AbstractNode $node)
+ protected function removeVariablesUsedByFuncGetArgs(AbstractNode $node)
{
$functionCalls = $node->findChildrenOfType('FunctionPostfix');
diff --git a/src/main/php/PHPMD/Rule/UnusedLocalVariable.php b/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
index 6b9fb6a8d..9d66e4145 100644
--- a/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
+++ b/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
@@ -32,7 +32,7 @@ class UnusedLocalVariable extends AbstractLocalVariable implements FunctionAware
*
* @var array(string)
*/
- private $images = array();
+ protected $images = array();
/**
* This method checks that all local variables within the given function or
@@ -50,12 +50,40 @@ public function apply(AbstractNode $node)
$this->removeParameters($node);
foreach ($this->images as $nodes) {
- if (count($nodes) === 1) {
+ if (!$this->containsUsages($nodes)) {
$this->doCheckNodeImage($nodes[0]);
}
}
}
+ /**
+ * Return true if one of the passed nodes contains variables usages.
+ *
+ * @param array $nodes
+ *
+ * @return bool
+ */
+ protected function containsUsages(array $nodes)
+ {
+ if (count($nodes) === 1) {
+ return false;
+ }
+
+ foreach ($nodes as $node) {
+ $parent = $node->getParent();
+
+ if (!$parent->isInstanceOf('AssignmentExpression')) {
+ return true;
+ }
+
+ if (in_array($this->getNode($node), array_slice($parent->getChildren(), 1))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* This method removes all variables from the $_images property that
* are also found in the formal parameters of the given method or/and
@@ -64,7 +92,7 @@ public function apply(AbstractNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function removeParameters(AbstractCallableNode $node)
+ protected function removeParameters(AbstractCallableNode $node)
{
// Get formal parameter container
$parameters = $node->getFirstChildOfType('FormalParameters');
@@ -86,7 +114,7 @@ private function removeParameters(AbstractCallableNode $node)
* @param \PHPMD\Node\AbstractCallableNode $node
* @return void
*/
- private function collectVariables(AbstractCallableNode $node)
+ protected function collectVariables(AbstractCallableNode $node)
{
foreach ($node->findChildrenOfTypeVariable() as $variable) {
if ($this->isLocal($variable)) {
@@ -101,6 +129,7 @@ private function collectVariables(AbstractCallableNode $node)
foreach ($node->findChildrenOfType('VariableDeclarator') as $variable) {
$this->collectVariable($variable);
}
+
foreach ($node->findChildrenOfType('FunctionPostfix') as $func) {
if ($this->isFunctionNameEndingWith($func, 'compact')) {
foreach ($func->findChildrenOfType('Literal') as $literal) {
@@ -117,7 +146,7 @@ private function collectVariables(AbstractCallableNode $node)
* @param \PHPMD\Node\ASTNode $node
* @return void
*/
- private function collectCompoundVariableInString(ASTNode $node)
+ protected function collectCompoundVariableInString(ASTNode $node)
{
$parentNode = $node->getParent()->getNode();
$candidateParentNodes = $node->getParentsOfType('PDepend\Source\AST\ASTString');
@@ -140,7 +169,7 @@ private function collectCompoundVariableInString(ASTNode $node)
* @param \PHPMD\Node\ASTNode $node
* @return void
*/
- private function collectVariable(ASTNode $node)
+ protected function collectVariable(ASTNode $node)
{
$imageName = $node->getImage();
$this->storeImage($imageName, $node);
@@ -153,11 +182,12 @@ private function collectVariable(ASTNode $node)
* @param \PHPMD\Node\ASTNode $node the node being stored
* @return void
*/
- private function storeImage($imageName, ASTNode $node)
+ protected function storeImage($imageName, ASTNode $node)
{
if (!isset($this->images[$imageName])) {
$this->images[$imageName] = array();
}
+
$this->images[$imageName][] = $node;
}
@@ -167,12 +197,14 @@ private function storeImage($imageName, ASTNode $node)
* @param \PHPMD\Node\ASTNode $node
* @return void
*/
- private function collectLiteral(ASTNode $node)
+ protected function collectLiteral(ASTNode $node)
{
- $variable = '$' . trim($node->getImage(), '\'');
+ $variable = '$' . trim($node->getImage(), '\'\"');
+
if (!isset($this->images[$variable])) {
$this->images[$variable] = array();
}
+
$this->images[$variable][] = $node;
}
@@ -187,13 +219,22 @@ protected function doCheckNodeImage(ASTNode $node)
if ($this->isNameAllowedInContext($node)) {
return;
}
+
if ($this->isUnusedForeachVariableAllowed($node)) {
return;
}
- $exceptions = $this->getExceptionsList();
- if (in_array(substr($node->getImage(), 1), $exceptions)) {
+
+ if (in_array(substr($node->getImage(), 1), $this->getExceptionsList())) {
return;
}
+
+ $parent = $node->getParent();
+
+ // ASTFormalParameter should be handled by the UnusedFormalParameter rule
+ if ($parent && $parent->isInstanceOf('FormalParameter')) {
+ return;
+ }
+
$this->addViolation($node, array($node->getImage()));
}
@@ -205,7 +246,7 @@ protected function doCheckNodeImage(ASTNode $node)
* @param \PHPMD\AbstractNode $node
* @return boolean
*/
- private function isNameAllowedInContext(AbstractNode $node)
+ protected function isNameAllowedInContext(AbstractNode $node)
{
return $this->isChildOf($node, 'CatchStatement');
}
@@ -218,9 +259,10 @@ private function isNameAllowedInContext(AbstractNode $node)
* @param \PHPMD\Node\ASTNode $variable The variable to check.
* @return bool True if allowed, else false.
*/
- private function isUnusedForeachVariableAllowed(ASTNode $variable)
+ protected function isUnusedForeachVariableAllowed(ASTNode $variable)
{
$isForeachVariable = $this->isChildOf($variable, 'ForeachStatement');
+
if (!$isForeachVariable) {
return false;
}
@@ -236,7 +278,7 @@ private function isUnusedForeachVariableAllowed(ASTNode $variable)
* @param string $type
* @return boolean
*/
- private function isChildOf(AbstractNode $node, $type)
+ protected function isChildOf(AbstractNode $node, $type)
{
$parent = $node->getParent();
@@ -248,14 +290,8 @@ private function isChildOf(AbstractNode $node, $type)
*
* @return array
*/
- private function getExceptionsList()
+ protected function getExceptionsList()
{
- try {
- $exceptions = $this->getStringProperty('exceptions');
- } catch (\OutOfBoundsException $e) {
- $exceptions = '';
- }
-
- return explode(',', $exceptions);
+ return explode(',', $this->getStringProperty('exceptions', ''));
}
}
diff --git a/src/main/php/PHPMD/Rule/UnusedPrivateField.php b/src/main/php/PHPMD/Rule/UnusedPrivateField.php
index a530f5d18..8f591fae9 100644
--- a/src/main/php/PHPMD/Rule/UnusedPrivateField.php
+++ b/src/main/php/PHPMD/Rule/UnusedPrivateField.php
@@ -34,7 +34,7 @@ class UnusedPrivateField extends AbstractRule implements ClassAware
*
* @var \PHPMD\Node\ASTNode[]
*/
- private $fields = array();
+ protected $fields = array();
/**
* This method checks that all private class properties are at least accessed
@@ -58,7 +58,7 @@ public function apply(AbstractNode $node)
* @param \PHPMD\Node\ClassNode $class
* @return \PHPMD\AbstractNode[]
*/
- private function collectUnusedPrivateFields(ClassNode $class)
+ protected function collectUnusedPrivateFields(ClassNode $class)
{
$this->fields = array();
@@ -75,7 +75,7 @@ private function collectUnusedPrivateFields(ClassNode $class)
* @param \PHPMD\Node\ClassNode $class
* @return void
*/
- private function collectPrivateFields(ClassNode $class)
+ protected function collectPrivateFields(ClassNode $class)
{
foreach ($class->findChildrenOfType('FieldDeclaration') as $declaration) {
/** @var ASTNode $declaration */
@@ -92,7 +92,7 @@ private function collectPrivateFields(ClassNode $class)
* @param \PHPMD\Node\ASTNode $declaration
* @return void
*/
- private function collectPrivateField(ASTNode $declaration)
+ protected function collectPrivateField(ASTNode $declaration)
{
$fields = $declaration->findChildrenOfType('VariableDeclarator');
foreach ($fields as $field) {
@@ -108,7 +108,7 @@ private function collectPrivateField(ASTNode $declaration)
* @param \PHPMD\Node\ClassNode $class
* @return void
*/
- private function removeUsedFields(ClassNode $class)
+ protected function removeUsedFields(ClassNode $class)
{
foreach ($class->findChildrenOfType('PropertyPostfix') as $postfix) {
/** @var $postfix ASTNode */
@@ -125,7 +125,7 @@ private function removeUsedFields(ClassNode $class)
* @param \PHPMD\Node\ASTNode $postfix
* @return void
*/
- private function removeUsedField(ASTNode $postfix)
+ protected function removeUsedField(ASTNode $postfix)
{
$image = '$';
$child = $postfix->getFirstChildOfType('Identifier');
diff --git a/src/main/php/PHPMD/Rule/UnusedPrivateMethod.php b/src/main/php/PHPMD/Rule/UnusedPrivateMethod.php
index 673531e12..f257e8a2a 100644
--- a/src/main/php/PHPMD/Rule/UnusedPrivateMethod.php
+++ b/src/main/php/PHPMD/Rule/UnusedPrivateMethod.php
@@ -52,7 +52,7 @@ public function apply(AbstractNode $class)
* @param ClassNode $class
* @return ASTMethodPostfix[]
*/
- private function collectUnusedPrivateMethods(ClassNode $class)
+ protected function collectUnusedPrivateMethods(ClassNode $class)
{
$methods = $this->collectPrivateMethods($class);
@@ -65,7 +65,7 @@ private function collectUnusedPrivateMethods(ClassNode $class)
* @param ClassNode $class
* @return AbstractNode[]
*/
- private function collectPrivateMethods(ClassNode $class)
+ protected function collectPrivateMethods(ClassNode $class)
{
$methods = array();
@@ -86,7 +86,7 @@ private function collectPrivateMethods(ClassNode $class)
* @param MethodNode $method
* @return boolean
*/
- private function acceptMethod(ClassNode $class, MethodNode $method)
+ protected function acceptMethod(ClassNode $class, MethodNode $method)
{
return (
$method->isPrivate() &&
@@ -105,7 +105,7 @@ private function acceptMethod(ClassNode $class, MethodNode $method)
* @param MethodNode[] $methods
* @return ASTMethodPostfix[]
*/
- private function removeUsedMethods(ClassNode $class, array $methods)
+ protected function removeUsedMethods(ClassNode $class, array $methods)
{
foreach ($class->findChildrenOfType('MethodPostfix') as $postfix) {
/** @var $postfix ASTNode */
@@ -125,7 +125,7 @@ private function removeUsedMethods(ClassNode $class, array $methods)
* @param ASTNode $postfix
* @return boolean
*/
- private function isClassScope(ClassNode $class, ASTNode $postfix)
+ protected function isClassScope(ClassNode $class, ASTNode $postfix)
{
$owner = $postfix->getParent()->getChild(0);
diff --git a/src/main/php/PHPMD/RuleViolation.php b/src/main/php/PHPMD/RuleViolation.php
index 9f60cbe7e..8865f69e4 100644
--- a/src/main/php/PHPMD/RuleViolation.php
+++ b/src/main/php/PHPMD/RuleViolation.php
@@ -48,6 +48,14 @@ class RuleViolation
*/
private $description;
+ /**
+ * The arguments for the description/message text or null
+ * when the arguments are unknown.
+ *
+ * @var array|null
+ */
+ private $args = null;
+
/**
* The raw metric value which caused this rule violation.
*
@@ -66,7 +74,7 @@ class RuleViolation
* The name of a method or null when this violation has no method
* context.
*
- * @var string
+ * @var string|null
*/
private $methodName = null;
@@ -83,7 +91,7 @@ class RuleViolation
*
* @param \PHPMD\Rule $rule
* @param \PHPMD\AbstractNode $node
- * @param string $violationMessage
+ * @param string|array $violationMessage
* @param mixed $metric
*/
public function __construct(Rule $rule, AbstractNode $node, $violationMessage, $metric = null)
@@ -91,7 +99,20 @@ public function __construct(Rule $rule, AbstractNode $node, $violationMessage, $
$this->rule = $rule;
$this->node = $node;
$this->metric = $metric;
- $this->description = $violationMessage;
+
+ if (is_array($violationMessage) === true) {
+ $search = array();
+ $replace = array();
+ foreach ($violationMessage['args'] as $index => $value) {
+ $search[] = '{' . $index . '}';
+ $replace[] = $value;
+ }
+
+ $this->args = $violationMessage['args'];
+ $this->description = str_replace($search, $replace, $violationMessage['message']);
+ } else {
+ $this->description = $violationMessage;
+ }
if ($node instanceof AbstractTypeNode) {
$this->className = $node->getName();
@@ -123,6 +144,17 @@ public function getDescription()
return $this->description;
}
+ /**
+ * Returns the arguments for the description/message text or null
+ * when the arguments are unknown.
+ *
+ * @return array|null
+ */
+ public function getArgs()
+ {
+ return $this->args;
+ }
+
/**
* Returns the raw metric value which caused this rule violation.
*
@@ -136,7 +168,7 @@ public function getMetric()
/**
* Returns the file name where this rule violation was detected.
*
- * @return string
+ * @return string|null
*/
public function getFileName()
{
@@ -188,7 +220,7 @@ public function getClassName()
* Returns the name of a method or null when this violation has no
* method context.
*
- * @return string
+ * @return string|null
*/
public function getMethodName()
{
diff --git a/src/main/php/PHPMD/TextUI/Command.php b/src/main/php/PHPMD/TextUI/Command.php
index 577da7d23..5bb10367b 100644
--- a/src/main/php/PHPMD/TextUI/Command.php
+++ b/src/main/php/PHPMD/TextUI/Command.php
@@ -9,16 +9,23 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
+use PHPMD\Baseline\BaselineFileFinder;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSetFactory;
+use PHPMD\Baseline\BaselineValidator;
use PHPMD\PHPMD;
+use PHPMD\Renderer\RendererFactory;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
+use PHPMD\Utility\Paths;
use PHPMD\Writer\StreamWriter;
/**
@@ -31,7 +38,8 @@ class Command
*/
const EXIT_SUCCESS = 0,
EXIT_EXCEPTION = 1,
- EXIT_VIOLATION = 2;
+ EXIT_VIOLATION = 2,
+ EXIT_ERROR = 3;
/**
* This method creates a PHPMD instance and configures this object based
@@ -40,13 +48,14 @@ class Command
* The return value of this method can be used as an exit code. A value
* equal to EXIT_SUCCESS means that no violations or errors were
* found in the analyzed code. Otherwise this method will return a value
- * equal to EXIT_VIOLATION.
+ * equal to EXIT_VIOLATION or EXIT_ERROR respectively.
*
- * The use of flag --ignore-violations-on-exit will result to a
- * EXIT_SUCCESS even if any violation is found.
+ * The use of the flags --ignore-violations-on-exit and
+ * --ignore-errors-on-exit will result to a EXIT_SUCCESS
+ * even if any violation or error is found.
*
* @param \PHPMD\TextUI\CommandLineOptions $opts
- * @param \PHPMD\RuleSetFactory $ruleSetFactory
+ * @param \PHPMD\RuleSetFactory $ruleSetFactory
* @return integer
*/
public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
@@ -73,6 +82,26 @@ public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
$renderers[] = $reportRenderer;
}
+ // Configure baseline violations
+ $report = null;
+ $finder = new BaselineFileFinder($opts);
+ if ($opts->generateBaseline() === BaselineMode::GENERATE) {
+ // overwrite any renderer with the baseline renderer
+ $renderers = array(RendererFactory::createBaselineRenderer(new StreamWriter($finder->notNull()->find())));
+ } elseif ($opts->generateBaseline() === BaselineMode::UPDATE) {
+ $baselineFile = $finder->notNull()->existingFile()->find();
+ $baseline = BaselineSetFactory::fromFile(Paths::getRealPath($baselineFile));
+ $renderers = array(RendererFactory::createBaselineRenderer(new StreamWriter($baselineFile)));
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::UPDATE));
+ } else {
+ // try to locate a baseline file and read it
+ $baselineFile = $finder->existingFile()->find();
+ if ($baselineFile !== null) {
+ $baseline = BaselineSetFactory::fromFile(Paths::getRealPath($baselineFile));
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::NONE));
+ }
+ }
+
// Configure a rule set factory
$ruleSetFactory->setMinimumPriority($opts->getMinimumPriority());
$ruleSetFactory->setMaximumPriority($opts->getMaximumPriority());
@@ -96,17 +125,24 @@ public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
$ignore = $opts->getIgnore();
if ($ignore !== null) {
- $phpmd->setIgnorePattern(explode(',', $ignore));
+ $phpmd->addIgnorePatterns(explode(',', $ignore));
}
$phpmd->processFiles(
$opts->getInputPath(),
$opts->getRuleSets(),
$renderers,
- $ruleSetFactory
+ $ruleSetFactory,
+ $report !== null ? $report : new Report()
);
- if ($phpmd->hasViolations() && !$opts->ignoreViolationsOnExit()) {
+ if ($phpmd->hasErrors() && !$opts->ignoreErrorsOnExit()) {
+ return self::EXIT_ERROR;
+ }
+
+ if ($phpmd->hasViolations()
+ && !$opts->ignoreViolationsOnExit()
+ && $opts->generateBaseline() === BaselineMode::NONE) {
return self::EXIT_VIOLATION;
}
@@ -124,7 +160,7 @@ private function getVersion()
$version = '@package_version@';
if (file_exists($build)) {
- $data = @parse_ini_file($build);
+ $data = @parse_ini_file($build);
$version = $data['project.version'];
}
@@ -142,8 +178,8 @@ public static function main(array $args)
{
try {
$ruleSetFactory = new RuleSetFactory();
- $options = new CommandLineOptions($args, $ruleSetFactory->listAvailableRuleSets());
- $command = new Command();
+ $options = new CommandLineOptions($args, $ruleSetFactory->listAvailableRuleSets());
+ $command = new Command();
$exitCode = $command->run($options, $ruleSetFactory);
} catch (\Exception $e) {
diff --git a/src/main/php/PHPMD/TextUI/CommandLineOptions.php b/src/main/php/PHPMD/TextUI/CommandLineOptions.php
index b0b0fe515..3e5c2fe38 100644
--- a/src/main/php/PHPMD/TextUI/CommandLineOptions.php
+++ b/src/main/php/PHPMD/TextUI/CommandLineOptions.php
@@ -9,17 +9,20 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
+use PHPMD\Baseline\BaselineMode;
use PHPMD\Renderer\AnsiRenderer;
+use PHPMD\Renderer\GitHubRenderer;
use PHPMD\Renderer\HTMLRenderer;
use PHPMD\Renderer\JSONRenderer;
+use PHPMD\Renderer\SARIFRenderer;
use PHPMD\Renderer\TextRenderer;
use PHPMD\Renderer\XMLRenderer;
use PHPMD\Rule;
@@ -124,6 +127,14 @@ class CommandLineOptions
*/
protected $strict = false;
+ /**
+ * Should PHPMD exit without error code even if error is found?
+ *
+ * @var boolean
+ * @since 2.10.0
+ */
+ protected $ignoreErrorsOnExit = false;
+
/**
* Should PHPMD exit without error code even if violation is found?
*
@@ -138,6 +149,19 @@ class CommandLineOptions
*/
protected $availableRuleSets = array();
+ /**
+ * Should PHPMD baseline the existing violations and write them to the $baselineFile
+ * @var string allowed modes: NONE, GENERATE or UPDATE
+ */
+ protected $generateBaseline = BaselineMode::NONE;
+
+ /**
+ * The baseline source file to read the baseline violations from.
+ * Defaults to the path of the (first) ruleset file as phpmd.baseline.xml
+ * @var string|null
+ */
+ protected $baselineFile;
+
/**
* Constructs a new command line options instance.
*
@@ -202,6 +226,18 @@ public function __construct(array $args, array $availableRuleSets = array())
case '--not-strict':
$this->strict = false;
break;
+ case '--generate-baseline':
+ $this->generateBaseline = BaselineMode::GENERATE;
+ break;
+ case '--update-baseline':
+ $this->generateBaseline = BaselineMode::UPDATE;
+ break;
+ case '--baseline-file':
+ $this->baselineFile = array_shift($args);
+ break;
+ case '--ignore-errors-on-exit':
+ $this->ignoreErrorsOnExit = true;
+ break;
case '--ignore-violations-on-exit':
$this->ignoreViolationsOnExit = true;
break;
@@ -209,7 +245,8 @@ public function __construct(array $args, array $availableRuleSets = array())
case '--reportfile-text':
case '--reportfile-xml':
case '--reportfile-json':
- preg_match('(^\-\-reportfile\-(xml|html|text|json)$)', $arg, $match);
+ case '--reportfile-sarif':
+ preg_match('(^\-\-reportfile\-(xml|html|text|json|sarif)$)', $arg, $match);
$this->reportFiles[$match[1]] = array_shift($args);
break;
default:
@@ -222,9 +259,9 @@ public function __construct(array $args, array $availableRuleSets = array())
throw new \InvalidArgumentException($this->usage(), self::INPUT_ERROR);
}
- $this->inputPath = (string)array_shift($arguments);
+ $this->inputPath = (string)array_shift($arguments);
$this->reportFormat = (string)array_shift($arguments);
- $this->ruleSets = (string)array_shift($arguments);
+ $this->ruleSets = (string)array_shift($arguments);
}
/**
@@ -353,6 +390,37 @@ public function hasStrict()
return $this->strict;
}
+ /**
+ * Should the current violations be baselined
+ *
+ * @return string
+ */
+ public function generateBaseline()
+ {
+ return $this->generateBaseline;
+ }
+
+ /**
+ * The filepath of the baseline violations xml
+ *
+ * @return string|null
+ */
+ public function baselineFile()
+ {
+ return $this->baselineFile;
+ }
+
+ /**
+ * Was the --ignore-errors-on-exit passed to PHPMD's command line interface?
+ *
+ * @return boolean
+ * @since 2.10.0
+ */
+ public function ignoreErrorsOnExit()
+ {
+ return $this->ignoreErrorsOnExit;
+ }
+
/**
* Was the --ignore-violations-on-exit passed to PHPMD's command line interface?
*
@@ -394,6 +462,10 @@ public function createRenderer($reportFormat = null)
return $this->createJsonRenderer();
case 'ansi':
return $this->createAnsiRenderer();
+ case 'github':
+ return $this->createGitHubRenderer();
+ case 'sarif':
+ return $this->createSarifRenderer();
default:
return $this->createCustomRenderer();
}
@@ -423,6 +495,14 @@ protected function createAnsiRenderer()
return new AnsiRenderer();
}
+ /**
+ * @return \PHPMD\Renderer\GitHubRenderer
+ */
+ protected function createGitHubRenderer()
+ {
+ return new GitHubRenderer();
+ }
+
/**
* @return \PHPMD\Renderer\HTMLRenderer
*/
@@ -439,6 +519,14 @@ protected function createJsonRenderer()
return new JSONRenderer();
}
+ /**
+ * @return \PHPMD\Renderer\SARIFRenderer
+ */
+ protected function createSarifRenderer()
+ {
+ return new SARIFRenderer();
+ }
+
/**
* @return \PHPMD\AbstractRenderer
* @throws \InvalidArgumentException
@@ -507,8 +595,14 @@ public function usage()
'For example *src/foo/*.php or *src/foo/*' . \PHP_EOL .
'--strict: also report those nodes with a @SuppressWarnings ' .
'annotation' . \PHP_EOL .
+ '--ignore-errors-on-exit: will exit with a zero code, ' .
+ 'even on error' . \PHP_EOL .
'--ignore-violations-on-exit: will exit with a zero code, ' .
- 'even if any violations are found' . \PHP_EOL;
+ 'even if any violations are found' . \PHP_EOL .
+ '--generate-baseline: will generate a phpmd.baseline.xml next ' .
+ 'to the first ruleset file location' . \PHP_EOL .
+ '--update-baseline: will remove any non-existing violations from the phpmd.baseline.xml' . \PHP_EOL .
+ '--baseline-file: a custom location of the baseline file' . \PHP_EOL;
}
/**
@@ -519,7 +613,7 @@ public function usage()
protected function getListOfAvailableRenderers()
{
$renderersDirPathName = __DIR__ . '/../Renderer';
- $renderers = array();
+ $renderers = array();
foreach (scandir($renderersDirPathName) as $rendererFileName) {
if (preg_match('/^(\w+)Renderer.php$/i', $rendererFileName, $rendererName)) {
diff --git a/src/main/php/PHPMD/Utility/Paths.php b/src/main/php/PHPMD/Utility/Paths.php
new file mode 100644
index 000000000..fd7555d53
--- /dev/null
+++ b/src/main/php/PHPMD/Utility/Paths.php
@@ -0,0 +1,71 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Utility;
+
+use InvalidArgumentException;
+
+/**
+ * Utility class to provide string checks and manipulations
+ */
+class Strings
+{
+ /**
+ * Returns the length of the given string, excluding at most one suffix
+ *
+ * @param string $stringName String to calculate the length for.
+ * @param array $subtractSuffixes List of suffixes to exclude from the calculated length.
+ * @return int The length of the string, without suffix, if applicable.
+ */
+ public static function lengthWithoutSuffixes($stringName, array $subtractSuffixes)
+ {
+ $stringLength = strlen($stringName);
+
+ foreach ($subtractSuffixes as $suffix) {
+ $suffixLength = strlen($suffix);
+ if (substr($stringName, -$suffixLength) === $suffix) {
+ $stringLength -= $suffixLength;
+ break;
+ }
+ }
+
+ return $stringLength;
+ }
+
+ /**
+ * Split a string with the given separator, trim whitespaces around the parts and remove any empty strings
+ *
+ * @param string $listAsString The string to split.
+ * @param string $separator The separator to split the string with, similar to explode.
+ * @return array The list of trimmed and filtered parts of the string.
+ * @throws InvalidArgumentException When the separator is an empty string.
+ */
+ public static function splitToList($listAsString, $separator)
+ {
+ if ($separator === '') {
+ throw new InvalidArgumentException("Separator can't be empty string");
+ }
+
+ return array_filter(
+ array_map('trim', explode($separator, $listAsString)),
+ function ($value) {
+ return $value !== '';
+ }
+ );
+ }
+}
diff --git a/src/main/php/PHPMD/Writer/StreamWriter.php b/src/main/php/PHPMD/Writer/StreamWriter.php
index 2ac5b0658..310e08232 100644
--- a/src/main/php/PHPMD/Writer/StreamWriter.php
+++ b/src/main/php/PHPMD/Writer/StreamWriter.php
@@ -77,4 +77,12 @@ public function write($data)
{
fwrite($this->stream, $data);
}
+
+ /**
+ * @return resource
+ */
+ public function getStream()
+ {
+ return $this->stream;
+ }
}
diff --git a/src/main/resources/rulesets/cleancode.xml b/src/main/resources/rulesets/cleancode.xml
index 64f856b3b..ca1ebaab6 100644
--- a/src/main/resources/rulesets/cleancode.xml
+++ b/src/main/resources/rulesets/cleancode.xml
@@ -191,6 +191,9 @@ Importing all external classes in a file through use statements makes them clear
]]>
1
+
+
+
+ externalInfoUrl="https://phpmd.org/rules/cleancode.html#undefinedvariable">
Detects when a variable is used that has not been defined before.
diff --git a/src/main/resources/rulesets/naming.xml b/src/main/resources/rulesets/naming.xml
index fb4c245c2..c3c82d8a0 100644
--- a/src/main/resources/rulesets/naming.xml
+++ b/src/main/resources/rulesets/naming.xml
@@ -8,6 +8,58 @@
The Naming Ruleset contains a collection of rules about names - too long, too short, and so forth.
+
+
+ Detects when classes or interfaces are declared with excessively long names.
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+ Detects when classes or interfaces have a very short name.
+
+ 3
+
+
+
+
+
+
+
+
+
Suppress warnings
suppress-warnings.rst
+
+ CI Integration
+ ci-integration.rst
+
Writing a PHPMD Rule
writing-a-phpmd-rule.rst
diff --git a/src/site/rst/documentation/ci-integration.rst b/src/site/rst/documentation/ci-integration.rst
new file mode 100644
index 000000000..270154572
--- /dev/null
+++ b/src/site/rst/documentation/ci-integration.rst
@@ -0,0 +1,35 @@
+==============
+CI Integration
+==============
+
+PHPMD can be integrated into continuous integration (CI) pipelines to verify that each code change conforms to the configured rules.
+
+GitHub Actions
+==============
+
+GitHub Actions is supported out of the box with its own PHPMD renderer called ``github``. This renderer will add annotations directly to your commits and pull requests right in the code.
+
+A simple GitHub Actions workflow could look like this: ::
+
+ name: CI
+
+ on: push
+
+ jobs:
+ phpmd:
+ name: PHPMD
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Setup PHP environment
+ uses: shivammathur/setup-php@v2
+ with:
+ coverage: none
+ tools: phpmd
+
+ - name: Run PHPMD
+ run: phpmd . github phpmd.ruleset.xml --exclude 'tests/*,vendor/*'
+
+This assumes that you have a `custom rule set `_ in the file ``phpmd.ruleset.xml``. Alternatively, you can of course list the rule sets manually.
diff --git a/src/site/rst/documentation/index.rst b/src/site/rst/documentation/index.rst
index 3a4a6ddae..7966a98e9 100644
--- a/src/site/rst/documentation/index.rst
+++ b/src/site/rst/documentation/index.rst
@@ -59,9 +59,20 @@ Command line options
- ``--not-strict`` - Does not report those nodes with a @SuppressWarnings annotation.
+ - ``--ignore-errors-on-exit`` - will exit with a zero code, even on error.
+
- ``--ignore-violations-on-exit`` - will exit with a zero code, even if any
violations are found.
+ - ``--generate-baseline`` - will generate a ``phpmd.baseline.xml`` for existing violations
+ next to the ruleset definition file.
+
+ - ``--update-baseline`` - will remove all violations from an existing ``phpmd.baseline.xml``
+ that no longer exist. New violations will _not_ be added.
+
+ - ``--baseline-file`` - the filepath to a custom baseline xml file. The filepath
+ of all baselined files must be relative to this file location.
+
An example command line: ::
phpmd PHP/Depend/DbusUI xml codesize --reportfile phpmd.xml --suffixes php,phtml
@@ -92,18 +103,21 @@ that will check the source code.
Exit codes
==========
-PHPMD's command line tool currently defines three different exit codes.
+PHPMD's command line tool currently defines four different exit codes.
- *0*, This exit code indicates that everything worked as expected. This means
there was no error/exception and PHPMD hasn't detected any rule violation
in the code under test.
-- *1*, This exit code indicates that an error/exception occurred which has
+- *1*, This exit code indicates that an exception occurred which has
interrupted PHPMD during execution.
- *2*, This exit code means that PHPMD has processed the code under test
without the occurrence of an error/exception, but it has detected rule
violations in the analyzed source code. You can also prevent this behaviour
with the ``--ignore-violations-on-exit`` flag, which will result to a *0*
even if any violations are found.
+- *3*, This exit code means that one or multiple files under test could not
+ be processed because of an error. There may also be violations in other
+ files that could be processed correctly.
Renderers
=========
@@ -112,6 +126,28 @@ At the moment PHPMD comes with the following five renderers:
- *xml*, which formats the report as XML.
- *text*, simple textual format.
-- *ansi*, colorful, formated text for the command line.
+- *ansi*, colorful, formatted text for the command line.
- *html*, single HTML file with possible problems.
- *json*, formats JSON report.
+- *github*, a format that GitHub Actions understands (see `CI Integration `_).
+
+Baseline
+=========
+
+For existing projects a violation baseline can be generated. All violations in this baseline will be ignored in further inspections.
+
+The recommended approach would be a ``phpmd.xml`` in the root of the project. To generate the phpmd.baseline.xml next to it::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --generate-baseline
+
+To specify a custom baseline filepath for export::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --generate-baseline --baseline-file /path/to/source/phpmd.baseline.xml
+
+By default PHPMD will look next to ``phpmd.xml`` for ``phpmd.baseline.xml``. To overwrite this behaviour::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --baseline-file /path/to/source/phpmd.baseline.xml
+
+To clean up an existing baseline file and *only remove* no longer existing violations::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --update-baseline
diff --git a/src/site/rst/robots.txt b/src/site/rst/robots.txt
deleted file mode 100644
index 17a3cf411..000000000
--- a/src/site/rst/robots.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# robots.txt for http://kore-nordmann.de
-#
-# Please note: There are a lot of pages on this site, and there are
-# some misbehaved spiders out there that go _way_ too fast. If you're
-# irresponsible, your access to the site may be blocked.
-
-Sitemap: http://pdepend.org/.sitemap
-
-# advertising-related bots:
-User-agent: Mediapartners-Google*
-Disallow: /
-
-# A capture bot, downloads gazillions of pages with no public benefit
-# http://www.webreaper.net/
-User-agent: WebReaper
-Disallow: /
-
-#
-# Friendly, low-speed bots are welcome viewing article pages, but not
-# dynamically-generated pages please.
-#
-
-# Only googles bots now '$'
-# User-agent: Googlebot
-# Disallow: /relation/find$
-
-User-agent: *
-Disallow: /styles/
-Disallow: /images/
-
-#
-# *at least* 1 second please. preferably more :D
-#
-Crawl-delay: 1
diff --git a/src/site/rst/rules/cleancode.rst b/src/site/rst/rules/cleancode.rst
index 84f9233c4..498f92ca6 100644
--- a/src/site/rst/rules/cleancode.rst
+++ b/src/site/rst/rules/cleancode.rst
@@ -132,6 +132,14 @@ Example: ::
return new \stdClass();
}
+This rule has the following properties:
+
++-----------------------------------+---------------+------------------------------------------------------------+
+| Name | Default Value | Description |
++===================================+===============+============================================================+
+| ignore-global | | Ignore classes, interfaces and traits in the global namespace |
++-----------------------------------+---------------+------------------------------------------------------------+
+
UndefinedVariable
=================
@@ -152,4 +160,3 @@ Remark
This document is based on a ruleset xml-file, that was taken from the original source of the `PMD`__ project. This means that most parts of the content on this page are the intellectual work of the PMD community and its contributors and not of the PHPMD project.
__ http://pmd.sourceforge.net/
-
diff --git a/src/site/rst/rules/index.rst b/src/site/rst/rules/index.rst
index 5ee75d76e..2af522c53 100644
--- a/src/site/rst/rules/index.rst
+++ b/src/site/rst/rules/index.rst
@@ -64,6 +64,8 @@ many bugs, especially when the loop manipulates an array, as count happens on ea
Naming Rules
============
+- `LongClassName `_: Detects when classes or interfaces are declared with excessively long names.
+- `ShortClassName `_: Detects when classes or interfaces have a very short name.
- `ShortVariable `_: Detects when a field, local, or parameter has a very short name.
- `LongVariable `_: Detects when a field, formal or local variable is declared with a long name.
- `ShortMethodName `_: Detects when very short method names are used.
diff --git a/src/site/rst/rules/naming.rst b/src/site/rst/rules/naming.rst
index a870b7088..9dd05a873 100644
--- a/src/site/rst/rules/naming.rst
+++ b/src/site/rst/rules/naming.rst
@@ -4,6 +4,63 @@ Naming Rules
The Naming Ruleset contains a collection of rules about names - too long, too short, and so forth.
+LongClassName
+=============
+
+Since: PHPMD 2.9
+
+Detects when classes or interfaces are declared with excessively long names.
+
+Example: ::
+
+ class ATooLongClassNameThatHintsAtADesignProblem {
+
+ }
+
+ interface ATooLongInterfaceNameThatHintsAtADesignProblem {
+
+ }
+
+This rule has the following properties:
+
++-----------------------------------+---------------+------------------------------------------------------------+
+| Name | Default Value | Description |
++===================================+===============+============================================================+
+| maximum | 40 | The class name length reporting threshold. |
++-----------------------------------+---------------+------------------------------------------------------------+
+| subtract-suffixes | | Comma-separated list of suffixes that will not count in |
+| | | the length of the class name. Only the first matching |
+| | | suffix will be subtracted. |
++-----------------------------------+---------------+------------------------------------------------------------+
+
+ShortClassName
+==============
+
+Since: PHPMD 2.9
+
+Detects when classes or interfaces have a very short name.
+
+Example: ::
+
+ class Fo {
+
+ }
+
+ interface Fo {
+
+ }
+
+This rule has the following properties:
+
++-----------------------------------+---------------+------------------------------------------------------------+
+| Name | Default Value | Description |
++===================================+===============+============================================================+
+| minimum | 3 | The class name length reporting threshold |
++-----------------------------------+---------------+------------------------------------------------------------+
+| exceptions | | Comma-separated list of exceptions. Example: Log,URL,FTP |
++-----------------------------------+---------------+------------------------------------------------------------+
+
+
ShortVariable
=============
@@ -158,4 +215,4 @@ Remark
This document is based on a ruleset xml-file, that was taken from the original source of the `PMD`__ project. This means that most parts of the content on this page are the intellectual work of the PMD community and its contributors and not of the PHPMD project.
__ http://pmd.sourceforge.net/
-
+
diff --git a/src/test/php/PHPMD/AbstractStaticTest.php b/src/test/php/PHPMD/AbstractStaticTest.php
index 1037d736c..31991f549 100644
--- a/src/test/php/PHPMD/AbstractStaticTest.php
+++ b/src/test/php/PHPMD/AbstractStaticTest.php
@@ -17,6 +17,7 @@
namespace PHPMD;
+use Closure;
use PHPUnit_Framework_TestCase;
/**
@@ -66,6 +67,8 @@ protected static function returnToOriginalWorkingDirectory()
*/
protected static function cleanupTempFiles()
{
+ // cleanup any open resources on temp files
+ gc_collect_cycles();
foreach (self::$tempFiles as $tempFile) {
unlink($tempFile);
}
@@ -150,21 +153,27 @@ public static function assertXmlEquals($actualOutput, $expectedFileName)
*
* @param string $actualOutput Generated JSON output.
* @param string $expectedFileName File with expected JSON result.
+ * @param bool|Closure $removeDynamicValues If set to `false`, the actual output is not normalized,
+ * if set to a closure, the closure is applied on the actual output array.
*
* @return void
*/
- public static function assertJsonEquals($actualOutput, $expectedFileName)
+ public static function assertJsonEquals($actualOutput, $expectedFileName, $removeDynamicValues = true)
{
$actual = json_decode($actualOutput, true);
// Remove dynamic timestamp and duration attribute
- if (isset($actual['timestamp'])) {
- $actual['timestamp'] = '';
- }
- if (isset($actual['duration'])) {
- $actual['duration'] = '';
- }
- if (isset($actual['version'])) {
- $actual['version'] = '@package_version@';
+ if ($removeDynamicValues === true) {
+ if (isset($actual['timestamp'])) {
+ $actual['timestamp'] = '';
+ }
+ if (isset($actual['duration'])) {
+ $actual['duration'] = '';
+ }
+ if (isset($actual['version'])) {
+ $actual['version'] = '@package_version@';
+ }
+ } elseif ($removeDynamicValues instanceof Closure) {
+ $actual = $removeDynamicValues($actual);
}
$expected = str_replace(
@@ -173,6 +182,7 @@ public static function assertJsonEquals($actualOutput, $expectedFileName)
file_get_contents(self::createFileUri($expectedFileName))
);
+ $expected = str_replace('#{workingDirectory}', getcwd(), $expected);
$expected = str_replace('_DS_', DIRECTORY_SEPARATOR, $expected);
self::assertJsonStringEqualsJsonString($expected, json_encode($actual));
@@ -235,11 +245,17 @@ protected static function createFileUri($localPath = '')
/**
* Creates a file uri for a temporary test file.
*
+ * @param string|null $fileName
* @return string
*/
- protected static function createTempFileUri()
+ protected static function createTempFileUri($fileName = null)
{
- return (self::$tempFiles[] = tempnam(sys_get_temp_dir(), 'phpmd.'));
+ if ($fileName !== null) {
+ $filePath = sys_get_temp_dir() . '/' . $fileName;
+ } else {
+ $filePath = tempnam(sys_get_temp_dir(), 'phpmd.');
+ }
+ return (self::$tempFiles[] = $filePath);
}
/**
diff --git a/src/test/php/PHPMD/AbstractTest.php b/src/test/php/PHPMD/AbstractTest.php
index 014ea609a..4d503c75a 100644
--- a/src/test/php/PHPMD/AbstractTest.php
+++ b/src/test/php/PHPMD/AbstractTest.php
@@ -34,8 +34,11 @@
use PHPMD\Node\TraitNode;
use PHPMD\Rule\Design\TooManyFields;
use PHPMD\Stubs\RuleStub;
+use PHPUnit_Framework_ExpectationFailedException;
use PHPUnit_Framework_MockObject_MockBuilder;
use PHPUnit_Framework_MockObject_MockObject;
+use ReflectionProperty;
+use Traversable;
/**
* Abstract base class for PHPMD test cases.
@@ -184,36 +187,128 @@ protected function getFunction()
}
/**
- * Returns the first method as a MethodNode for a given test file.
+ * Returns the first class found for a given test file.
+ *
+ * @return ClassNode
+ */
+ protected function getClassNodeForTestFile($file)
+ {
+ return new ClassNode(
+ $this->parseSource($file)
+ ->getTypes()
+ ->current()
+ );
+ }
+
+ /**
+ * Returns the first method or function node for a given test file.
*
* @param string $file
- * @return MethodNode
+ * @return MethodNode|FunctionNode
* @since 2.8.3
*/
- protected function getMethodNodeForTestFile($file)
+ protected function getNodeForTestFile($file)
{
- return new MethodNode(
+ $source = $this->parseSource($file);
+ $class = $source
+ ->getTypes()
+ ->current();
+ $nodeClassName = 'PHPMD\\Node\\FunctionNode';
+ $getter = 'getFunctions';
+
+ if ($class) {
+ $source = $class;
+ $nodeClassName = 'PHPMD\\Node\\MethodNode';
+ $getter = 'getMethods';
+ }
+
+ return new $nodeClassName(
$this->getNodeByName(
- $this->parseSource($file)
- ->getTypes()
- ->current()
- ->getMethods(),
+ $source->$getter(),
pathinfo($file, PATHINFO_FILENAME)
)
);
}
/**
- * Test that a given file trigger N times the given rule.
+ * Assert that a given file trigger N times the given rule.
+ *
+ * Rethrows the PHPUnit ExpectationFailedException with the base name
+ * of the file for better readability.
*
* @param Rule $rule Rule to test.
* @param int $expectedInvokes Count of expected invocations.
* @param string $file Test file containing a method with the same name to be tested.
+ * @return void
+ * @throws PHPUnit_Framework_ExpectationFailedException
*/
protected function expectRuleHasViolationsForFile(Rule $rule, $expectedInvokes, $file)
{
- $rule->setReport($this->getReportMock($expectedInvokes));
- $rule->apply($this->getMethodNodeForTestFile($file));
+ $report = new Report();
+ $rule->setReport($report);
+ $rule->apply($this->getNodeForTestFile($file));
+ $violations = $report->getRuleViolations();
+ $actualInvokes = count($violations);
+ $assertion = $expectedInvokes === self::AL_LEAST_ONE_VIOLATION
+ ? $actualInvokes > 0
+ : $actualInvokes === $expectedInvokes;
+
+ if (!$assertion) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ $this->getViolationFailureMessage($file, $expectedInvokes, $actualInvokes, $violations)
+ );
+ }
+
+ $this->assertTrue($assertion);
+ }
+
+ /**
+ * Return a human-friendly failure message for a given list of violations and the actual/expected counts.
+ *
+ * @param string $file
+ * @param int $expectedInvokes
+ * @param int $actualInvokes
+ * @param array|iterable|Traversable $violations
+ *
+ * @return string
+ */
+ protected function getViolationFailureMessage($file, $expectedInvokes, $actualInvokes, $violations)
+ {
+ return basename($file)." failed:\n".
+ "Expected $expectedInvokes violation".($expectedInvokes !== 1 ? 's' : '')."\n".
+ "But $actualInvokes violation".($actualInvokes !== 1 ? 's' : '')." raised".
+ ($actualInvokes > 0
+ ? ":\n".$this->getViolationsSummary($violations)
+ : '.'
+ );
+ }
+
+ /**
+ * Return a human-friendly summary for a list of violations.
+ *
+ * @param array|iterable|Traversable $violations
+ * @return string
+ */
+ protected function getViolationsSummary($violations)
+ {
+ if (!is_array($violations)) {
+ $violations = iterator_to_array($violations);
+ }
+
+ return implode("\n", array_map(function (RuleViolation $violation) {
+ $nodeExtractor = new ReflectionProperty('PHPMD\\RuleViolation', 'node');
+ $nodeExtractor->setAccessible(true);
+ $node = $nodeExtractor->getValue($violation);
+ $node = $node ? $node->getNode() : null;
+ $message = ' - line '.$violation->getBeginLine();
+
+ if ($node) {
+ $type = preg_replace('/^PDepend\\\\Source\\\\AST\\\\AST/', '', get_class($node));
+ $message .= ' on '.$type.' '.$node->getImage();
+ }
+
+ return $message;
+ }, $violations));
}
/**
diff --git a/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php b/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php
new file mode 100644
index 000000000..97452c8d0
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php
@@ -0,0 +1,78 @@
+find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::existingFile
+ */
+ public function testShouldFindExistingFileNearRuleSet()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testA/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+
+ // ensure consistent slashes
+ $expected = str_replace("\\", "/", realpath(static::createResourceUriForTest('testA/phpmd.baseline.xml')));
+ $actual = str_replace("\\", "/", $finder->existingFile()->find());
+
+ static::assertSame($expected, $actual);
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ */
+ public function testShouldReturnNullForNonExistingRuleSet()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::existingFile
+ */
+ public function testShouldOnlyFindExistingFile()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testB/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->existingFile()->find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::notNull
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to find the baseline file
+ */
+ public function testShouldThrowExceptionWhenFileIsNull()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testB/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->existingFile()->notNull()->find());
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php b/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php
new file mode 100644
index 000000000..1db007f1e
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php
@@ -0,0 +1,81 @@
+contains('PHPMD\Rule\CleanCode\MissingImport', $baseDir . '/src/foo/bar', null));
+ static::assertTrue(
+ $set->contains('PHPMD\Rule\CleanCode\UndefinedVariable', $baseDir . '/src/foo/bar', 'myMethod')
+ );
+ }
+
+ /**
+ * @covers ::fromFile
+ */
+ public function testFromFileShouldSucceedWithBackAndForwardSlashes()
+ {
+ $filename = static::createResourceUriForTest('baseline.xml');
+ $baseDir = dirname($filename);
+ $set = BaselineSetFactory::fromFile($filename);
+
+ static::assertTrue($set->contains('PHPMD\Rule\CleanCode\MissingImport', $baseDir . '/src\\foo/bar', null));
+ static::assertTrue(
+ $set->contains('PHPMD\Rule\CleanCode\UndefinedVariable', $baseDir . '/src\\foo/bar', 'myMethod')
+ );
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to locate the baseline file at
+ */
+ public function testFromFileShouldThrowExceptionForMissingFile()
+ {
+ BaselineSetFactory::fromFile('foobar.xml');
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to read xml from
+ */
+ public function testFromFileShouldThrowExceptionForOnInvalidXML()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('invalid-baseline.xml'));
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Missing `rule` attribute in `violation`
+ */
+ public function testFromFileViolationMissingRuleShouldThrowException()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('missing-rule-baseline.xml'));
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Missing `file` attribute in `violation` in
+ */
+ public function testFromFileViolationMissingFileShouldThrowException()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('missing-file-baseline.xml'));
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineSetTest.php b/src/test/php/PHPMD/Baseline/BaselineSetTest.php
new file mode 100644
index 000000000..f3a7d8073
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineSetTest.php
@@ -0,0 +1,76 @@
+addEntry(new ViolationBaseline('rule', 'foobar', null));
+
+ static::assertTrue($set->contains('rule', 'foobar', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testSetContainsEntryWithMethodName()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foobar', 'method'));
+
+ static::assertTrue($set->contains('rule', 'foobar', 'method'));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldFindEntryForIdenticalRules()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', null));
+ $set->addEntry(new ViolationBaseline('rule', 'bar', null));
+ $set->addEntry(new ViolationBaseline('rule', 'bar', 'method'));
+
+ static::assertTrue($set->contains('rule', 'foo', null));
+ static::assertTrue($set->contains('rule', 'bar', null));
+ static::assertTrue($set->contains('rule', 'bar', 'method'));
+ static::assertFalse($set->contains('rule', 'unknown', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldNotFindEntryForNonExistingRule()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', null));
+
+ static::assertFalse($set->contains('unknown', 'foo', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldNotFindEntryForNonExistingMethod()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', 'method'));
+
+ static::assertFalse($set->contains('rule', 'foo', 'unknown'));
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php b/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php
new file mode 100644
index 000000000..12caac3b4
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php
@@ -0,0 +1,66 @@
+getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Rule')->disableOriginalConstructor()
+ );
+ $this->violation = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\RuleViolation')->disableOriginalConstructor()
+ );
+ $this->violation
+ ->method('getRule')
+ ->willReturn($rule);
+ $this->baselineSet = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Baseline\BaselineSet')->disableOriginalConstructor()
+ );
+ }
+
+ /**
+ * @covers ::isBaselined
+ * @dataProvider dataProvider
+ * @param bool $contains
+ * @param string $baselineMode
+ * @param bool $isBaselined
+ */
+ public function testIsBaselined($contains, $baselineMode, $isBaselined)
+ {
+ $this->baselineSet->method('contains')->willReturn($contains);
+ $validator = new BaselineValidator($this->baselineSet, $baselineMode);
+ static::assertSame($isBaselined, $validator->isBaselined($this->violation));
+ }
+
+ /**
+ * @return array
+ */
+ public function dataProvider()
+ {
+ return array(
+ 'contains: true, mode: none' => array(true, BaselineMode::NONE, true),
+ 'contains: false, mode: none' => array(false, BaselineMode::NONE, false),
+ 'contains: true, mode: update' => array(true, BaselineMode::UPDATE, false),
+ 'contains: false, mode: update' => array(false, BaselineMode::UPDATE, true),
+ 'contains: true, mode: generate' => array(true, BaselineMode::GENERATE, false),
+ 'contains: false, mode: generate' => array(false, BaselineMode::GENERATE, false),
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php b/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php
new file mode 100644
index 000000000..ae8b4bf04
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php
@@ -0,0 +1,35 @@
+getRuleName());
+ static::assertSame('foobar', $violation->getFileName());
+ static::assertNull($violation->getMethodName());
+ }
+
+ /**
+ * @covers ::__construct
+ * @covers ::getMethodName
+ */
+ public function testAccessorsWithMethod()
+ {
+ $violation = new ViolationBaseline('rule', 'foobar', 'method');
+ static::assertSame('method', $violation->getMethodName());
+ }
+}
diff --git a/src/test/php/PHPMD/Node/ClassNodeTest.php b/src/test/php/PHPMD/Node/ClassNodeTest.php
index a52df9778..995fbf777 100644
--- a/src/test/php/PHPMD/Node/ClassNodeTest.php
+++ b/src/test/php/PHPMD/Node/ClassNodeTest.php
@@ -21,6 +21,7 @@
use PDepend\Source\AST\ASTMethod;
use PDepend\Source\AST\ASTNamespace;
use PHPMD\AbstractTest;
+use PHPMD\Rule\Design\CouplingBetweenObjects;
/**
* Test case for the class node implementation.
@@ -62,6 +63,34 @@ public function testHasSuppressWarningsAnnotationForReturnsTrue()
$this->assertTrue($node->hasSuppressWarningsAnnotationFor($rule));
}
+ /**
+ * testHasSuppressWarningsWithRuleNameContainingSlashes
+ *
+ * @return void
+ */
+ public function testHasSuppressWarningsWithRuleNameContainingSlashes()
+ {
+ $class = new ASTClass(null);
+ $class->setComment('/** @SuppressWarnings(PMD.CouplingBetweenObjects) */');
+
+ $rule = new CouplingBetweenObjects();
+ $rule->setName('rulesets/design.xml/CouplingBetweenObjects');
+
+ $node = new ClassNode($class);
+
+ $this->assertTrue($node->hasSuppressWarningsAnnotationFor($rule));
+
+ $class = new ASTClass(null);
+ $class->setComment('/** @SuppressWarnings(PMD.TooManyFields) */');
+
+ $rule = new CouplingBetweenObjects();
+ $rule->setName('rulesets/design.xml/CouplingBetweenObjects');
+
+ $node = new ClassNode($class);
+
+ $this->assertFalse($node->hasSuppressWarningsAnnotationFor($rule));
+ }
+
/**
* testGetFullQualifiedNameReturnsExpectedValue
*
diff --git a/src/test/php/PHPMD/PHPMDTest.php b/src/test/php/PHPMD/PHPMDTest.php
index 11a2ec6d4..c9d01b1f6 100644
--- a/src/test/php/PHPMD/PHPMDTest.php
+++ b/src/test/php/PHPMD/PHPMDTest.php
@@ -9,16 +9,20 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSet;
+use PHPMD\Baseline\BaselineValidator;
use PHPMD\Renderer\XMLRenderer;
use PHPMD\Stubs\WriterStub;
+use PHPUnit_Framework_MockObject_MockObject;
/**
* Test case for the main PHPMD class.
@@ -47,7 +51,8 @@ public function testRunWithDefaultSettingsAndXmlRenderer()
self::createFileUri('source/ccn_function.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/default-xml.xml');
@@ -72,7 +77,8 @@ public function testRunWithDefaultSettingsAndXmlRendererAgainstDirectory()
self::createFileUri('source'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/single-directory.xml');
@@ -97,12 +103,24 @@ public function testRunWithDefaultSettingsAndXmlRendererAgainstSingleFile()
self::createFileUri('source/ccn_function.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/single-file.xml');
}
+ /**
+ * testHasErrorsReturnsFalseByDefault
+ *
+ * @return void
+ */
+ public function testHasErrorsReturnsFalseByDefault()
+ {
+ $phpmd = new PHPMD();
+ $this->assertFalse($phpmd->hasErrors());
+ }
+
/**
* testHasViolationsReturnsFalseByDefault
*
@@ -131,9 +149,11 @@ public function testHasViolationsReturnsFalseForSourceWithoutViolations()
self::createFileUri('source/source_without_violations.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertFalse($phpmd->hasViolations());
}
@@ -154,12 +174,67 @@ public function testHasViolationsReturnsTrueForSourceWithViolation()
self::createFileUri('source/source_with_npath_violation.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertTrue($phpmd->hasViolations());
}
+ /**
+ * @return void
+ */
+ public function testHasViolationsReturnsFalseWhenViolationIsBaselined()
+ {
+ self::changeWorkingDirectory();
+
+ /** @var BaselineSet|PHPUnit_Framework_MockObject_MockObject $baselineSet */
+ $baselineSet = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Baseline\BaselineSet')->disableOriginalConstructor()
+ );
+ $baselineSet->expects(static::exactly(2))->method('contains')->willReturn(true);
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter(new WriterStub());
+
+ $phpmd = new PHPMD();
+ $phpmd->processFiles(
+ self::createFileUri('source/source_with_npath_violation.php'),
+ 'pmd-refset1',
+ array($renderer),
+ new RuleSetFactory(),
+ new Report(new BaselineValidator($baselineSet, BaselineMode::NONE))
+ );
+
+ static::assertFalse($phpmd->hasViolations());
+ }
+
+ /**
+ * testHasErrorsReturnsTrueForSourceWithError
+ *
+ * @return void
+ */
+ public function testHasErrorsReturnsTrueForSourceWithError()
+ {
+ self::changeWorkingDirectory();
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter(new WriterStub());
+
+ $phpmd = new PHPMD();
+ $phpmd->processFiles(
+ self::createFileUri('source/source_with_parse_error.php'),
+ 'pmd-refset1',
+ array($renderer),
+ new RuleSetFactory(),
+ new Report()
+ );
+
+ $this->assertTrue($phpmd->hasErrors());
+ $this->assertFalse($phpmd->hasViolations());
+ }
+
/**
* testIgnorePattern
*
@@ -176,9 +251,11 @@ public function testIgnorePattern()
self::createFileUri('sourceExcluded/'),
'pmd-refset1',
array(),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertTrue($phpmd->hasViolations());
// Process with exclusions, should result in no violations.
@@ -186,9 +263,11 @@ public function testIgnorePattern()
self::createFileUri('sourceExcluded/'),
'exclude-pattern',
array(),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertFalse($phpmd->hasViolations());
}
}
diff --git a/src/test/php/PHPMD/ParserFactoryTest.php b/src/test/php/PHPMD/ParserFactoryTest.php
index 2de4b2ddb..2ec7a5b80 100644
--- a/src/test/php/PHPMD/ParserFactoryTest.php
+++ b/src/test/php/PHPMD/ParserFactoryTest.php
@@ -140,10 +140,10 @@ public function testFactoryConfiguresIgnorePattern()
$phpmd = $this->getMockFromBuilder(
$this->getMockBuilder('PHPMD\\PHPMD')
- ->setMethods(array('getIgnorePattern', 'getInput'))
+ ->setMethods(array('getIgnorePatterns', 'getInput'))
);
$phpmd->expects($this->exactly(2))
- ->method('getIgnorePattern')
+ ->method('getIgnorePatterns')
->will($this->returnValue(array('Test')));
$phpmd->expects($this->once())
->method('getInput')
diff --git a/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php b/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
index c8898be7a..9c1c913f1 100644
--- a/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
+++ b/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
@@ -19,6 +19,7 @@
use PHPMD\PHPMD;
use PHPMD\Renderer\XMLRenderer;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
use PHPMD\Stubs\WriterStub;
@@ -46,7 +47,8 @@ public function testCliAcceptsDirectoryAsInput()
self::createFileUri('source'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
@@ -67,7 +69,8 @@ public function testCliAcceptsSingleFileAsInput()
self::createFileUri('source/FooBar.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
}
diff --git a/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php b/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
index 30a0bb32c..5c2ccf14a 100644
--- a/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
+++ b/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
@@ -87,7 +87,8 @@ function (Report $report) use ($self) {
__DIR__ . '/Sources/ExcessivePublicCountWorksForPublicStaticMethods.php',
'codesize',
array($this->renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
@@ -127,7 +128,8 @@ function (Report $report) use ($self) {
__DIR__ . '/Sources/ExcessivePublicCountSuppressionWorksForPublicStaticMethods.php',
'codesize',
array($this->renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
}
diff --git a/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php b/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
index 7afa4d7f6..10bdc932c 100644
--- a/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
+++ b/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
@@ -19,6 +19,7 @@
use PHPMD\PHPMD;
use PHPMD\Renderer\TextRenderer;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
use PHPMD\Writer\StreamWriter;
@@ -49,6 +50,6 @@ public function testLocalVariableUsedInDoubleQuoteStringGetsNotReported()
$factory = new RuleSetFactory();
$phpmd = new PHPMD();
- $phpmd->processFiles($inputs, $rules, $renderes, $factory);
+ $phpmd->processFiles($inputs, $rules, $renderes, $factory, new Report());
}
}
diff --git a/src/test/php/PHPMD/Renderer/BaselineRendererTest.php b/src/test/php/PHPMD/Renderer/BaselineRendererTest.php
new file mode 100644
index 000000000..3d6c8fd9f
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/BaselineRendererTest.php
@@ -0,0 +1,124 @@
+getRuleViolationMock('/src/php/bar.php'),
+ $this->getRuleViolationMock('/src/php/foo.php'),
+ );
+
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator($violations));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected1.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderReportShouldWriteMethodName()
+ {
+ $writer = new WriterStub();
+ $violationMock = $this->getRuleViolationMock('/src/php/bar.php');
+ $violationMock->expects(static::once())->method('getMethodName')->willReturn('foo');
+
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array($violationMock)));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected2.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderReportShouldDeduplicateSimilarViolations()
+ {
+ $writer = new WriterStub();
+ $violationMock = $this->getRuleViolationMock('/src/php/bar.php');
+ $violationMock->expects(static::exactly(2))->method('getMethodName')->willReturn('foo');
+
+ // add the same violation twice
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array($violationMock, $violationMock)));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected2.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderEmptyReport()
+ {
+ $writer = new WriterStub();
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array()));
+
+ /** @var Report|MockObject $report */
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected3.xml'
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/GitHubRendererTest.php b/src/test/php/PHPMD/Renderer/GitHubRendererTest.php
new file mode 100644
index 000000000..4cc4ac3e2
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/GitHubRendererTest.php
@@ -0,0 +1,108 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\AbstractTest;
+use PHPMD\ProcessingError;
+use PHPMD\Stubs\WriterStub;
+
+/**
+ * Test case for the GitHub renderer implementation.
+ *
+ * @covers \PHPMD\Renderer\GitHubRenderer
+ */
+class GitHubRendererTest extends AbstractTest
+{
+ /**
+ * testRendererCreatesExpectedNumberOfTextEntries
+ *
+ * @return void
+ */
+ public function testRendererCreatesExpectedNumberOfTextEntries()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $violations = array(
+ $this->getRuleViolationMock('/bar.php', 1),
+ $this->getRuleViolationMock('/foo.php', 2),
+ $this->getRuleViolationMock('/foo.php', 3),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator($violations)));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+
+ $renderer = new GitHubRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertEquals(
+ "::warning file=/bar.php,line=1::Test description" . PHP_EOL .
+ "::warning file=/foo.php,line=2::Test description" . PHP_EOL .
+ "::warning file=/foo.php,line=3::Test description" . PHP_EOL,
+ $writer->getData()
+ );
+ }
+
+ /**
+ * testRendererAddsProcessingErrorsToTextReport
+ *
+ * @return void
+ */
+ public function testRendererAddsProcessingErrorsToTextReport()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $errors = array(
+ new ProcessingError('Failed for file "/tmp/foo.php".'),
+ new ProcessingError('Failed for file "/tmp/bar.php".'),
+ new ProcessingError('Failed for file "/tmp/baz.php".'),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator($errors)));
+
+ $renderer = new GitHubRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertEquals(
+ "::error file=/tmp/foo.php::Failed for file \"/tmp/foo.php\"." . PHP_EOL .
+ "::error file=/tmp/bar.php::Failed for file \"/tmp/bar.php\"." . PHP_EOL .
+ "::error file=/tmp/baz.php::Failed for file \"/tmp/baz.php\"." . PHP_EOL,
+ $writer->getData()
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/RendererFactoryTest.php b/src/test/php/PHPMD/Renderer/RendererFactoryTest.php
new file mode 100644
index 000000000..e2707db8f
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/RendererFactoryTest.php
@@ -0,0 +1,35 @@
+getWriter());
+ }
+
+ /**
+ * @covers ::createBaselineRenderer
+ * @expectedException RuntimeException
+ * @expectedExceptionMessage Unable to determine the realpath for
+ */
+ public function testCreateBaselineRendererThrowsExceptionForInvalidStream()
+ {
+ $writer = new StreamWriter(STDOUT);
+ RendererFactory::createBaselineRenderer($writer);
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/SARIFRendererTest.php b/src/test/php/PHPMD/Renderer/SARIFRendererTest.php
new file mode 100644
index 000000000..0d54b45fd
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/SARIFRendererTest.php
@@ -0,0 +1,122 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\AbstractTest;
+use PHPMD\ProcessingError;
+use PHPMD\Stubs\RuleStub;
+use PHPMD\Stubs\WriterStub;
+
+/**
+ * Test case for the SARIF renderer implementation.
+ *
+ * @covers \PHPMD\Renderer\SARIFRenderer
+ */
+class SARIFRendererTest extends AbstractTest
+{
+ /**
+ * testRendererCreatesExpectedNumberOfJsonElements
+ *
+ * @return void
+ */
+ public function testRendererCreatesExpectedNumberOfJsonElements()
+ {
+ $writer = new WriterStub();
+
+ $rule = new RuleStub('AnotherRuleStub');
+ $rule->addExample(" class Example\n{\n}\n ");
+ $rule->addExample("\nclass AnotherExample\n{\n public \$var;\n}\n ");
+ $rule->setSince(null);
+
+ $complexRuleViolationMock = $this->getRuleViolationMock(getcwd() . '/src/foobar.php', 23, 42, $rule);
+ $complexRuleViolationMock
+ ->method('getArgs')
+ ->willReturn(array(123, 3.2, 'awesomeFunction()'));
+
+ $violations = array(
+ $this->getRuleViolationMock('/bar.php'),
+ $this->getRuleViolationMock('/foo.php'),
+ $complexRuleViolationMock,
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator($violations)));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+
+ $renderer = new SARIFRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertJsonEquals(
+ $writer->getData(),
+ 'renderer/sarif_renderer_expected.sarif',
+ function ($actual) {
+ $actual['runs'][0]['tool']['driver']['version'] = '@package_version@';
+ return $actual;
+ }
+ );
+ }
+
+ /**
+ * testRendererAddsProcessingErrorsToJsonReport
+ *
+ * @return void
+ */
+ public function testRendererAddsProcessingErrorsToJsonReport()
+ {
+ $writer = new WriterStub();
+
+ $processingErrors = array(
+ new ProcessingError('Failed for file "/tmp/foo.php".'),
+ new ProcessingError('Failed for file "/tmp/bar.php".'),
+ new ProcessingError('Failed for file "' . static::createFileUri('foobar.php') . '".'),
+ new ProcessingError('Cannot read file "/tmp/foo.php". Permission denied.'),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator($processingErrors)));
+
+ $renderer = new SARIFRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertJsonEquals(
+ $writer->getData(),
+ 'renderer/sarif_renderer_processing_errors.sarif',
+ function ($actual) {
+ $actual['runs'][0]['tool']['driver']['version'] = '@package_version@';
+ return $actual;
+ }
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/ReportTest.php b/src/test/php/PHPMD/ReportTest.php
index 7944a1c19..2ce027427 100644
--- a/src/test/php/PHPMD/ReportTest.php
+++ b/src/test/php/PHPMD/ReportTest.php
@@ -9,14 +9,19 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSet;
+use PHPMD\Baseline\BaselineValidator;
+use PHPMD\Baseline\ViolationBaseline;
+
/**
* Test case for the report class.
*
@@ -185,4 +190,56 @@ public function testGetErrorsReturnsPreviousAddedProcessingError()
$this->assertSame(1, iterator_count($report->getErrors()));
}
+
+ /**
+ * @return void
+ */
+ public function testReportShouldIgnoreBaselineViolation()
+ {
+ /** @var RuleViolation $ruleA */
+ $ruleA = $this->getRuleViolationMock('foo.txt');
+ /** @var RuleViolation $ruleB */
+ $ruleB = $this->getRuleViolationMock('bar.txt', 1, 2);
+
+ // setup baseline
+ $violation = new ViolationBaseline(get_class($ruleA->getRule()), 'foo.txt', null);
+ $baseline = new BaselineSet();
+ $baseline->addEntry($violation);
+
+ // setup report
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::NONE));
+ $report->addRuleViolation($ruleA);
+ $report->addRuleViolation($ruleB);
+
+ // only expect ruleB
+ $violations = $report->getRuleViolations();
+ static::assertCount(1, $violations);
+ static::assertSame($ruleB, $violations[0]);
+ }
+
+ /**
+ * @return void
+ */
+ public function testReportShouldIgnoreNewViolationsOnBaselineUpdate()
+ {
+ /** @var RuleViolation $ruleA */
+ $ruleA = $this->getRuleViolationMock('foo.txt');
+ /** @var RuleViolation $ruleB */
+ $ruleB = $this->getRuleViolationMock('bar.txt', 1, 2);
+
+ // setup baseline
+ $violation = new ViolationBaseline(get_class($ruleA->getRule()), 'foo.txt', null);
+ $baseline = new BaselineSet();
+ $baseline->addEntry($violation);
+
+ // setup report
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::UPDATE));
+ $report->addRuleViolation($ruleA);
+ $report->addRuleViolation($ruleB);
+
+ // only expect ruleA, as ruleB is new and should not be in the report.
+ $violations = $report->getRuleViolations();
+ static::assertCount(1, $violations);
+ static::assertSame($ruleA, $violations[0]);
+ }
}
diff --git a/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php b/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
index ca60b7f94..c6597d1c7 100644
--- a/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
+++ b/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
@@ -27,98 +27,66 @@
class MissingImportTest extends AbstractTest
{
/**
- * Tests that it does not apply to a class without any class dependencies
+ * Get the rule under test.
*
- * @return void
- * @covers ::apply
+ * @return MissingImport
*/
- public function testRuleNotAppliesToClassWithoutAnyDependencies()
+ public function getRule()
{
$rule = new MissingImport();
- $rule->setReport($this->getReportWithNoViolation());
- $rule->apply($this->getMethod());
+ $rule->addProperty('ignore-global', false);
+ return $rule;
}
/**
- * Tests that it does not apply to a class with only imported classes
+ * Tests the rule for cases where it should apply.
*
+ * @param string $file The test file to test against.
* @return void
- * @covers ::apply
- * @covers ::isSelfReference
+ * @dataProvider getApplyingCases
*/
- public function testRuleNotAppliesToClassWithOnlyImportedDependencies()
+ public function testRuleAppliesTo($file)
{
- $rule = new MissingImport();
- $rule->setReport($this->getReportWithNoViolation());
- $rule->apply($this->getMethod());
+ $this->expectRuleHasViolationsForFile($this->getRule(), static::ONE_VIOLATION, $file);
}
/**
- * Tests that it applies to a class that has fully qualified class names
+ * Tests the rule for cases where it should not apply.
*
+ * @param string $file The test file to test against.
* @return void
- * @covers ::apply
- * @covers ::isSelfReference
+ * @dataProvider getNotApplyingCases
*/
- public function testRuleAppliesToClassWithNotImportedDependencies()
+ public function testRuleDoesNotApplyTo($file)
{
- $rule = new MissingImport();
- $rule->setReport($this->getReportMock(2));
- $rule->apply($this->getMethod());
+ $this->expectRuleHasViolationsForFile($this->getRule(), static::NO_VIOLATION, $file);
}
/**
- * Tests that it does not apply to a class that uses self references
+ * Tests that it applies to a class that has fully qualified class names
*
* @return void
* @covers ::apply
* @covers ::isSelfReference
*/
- public function testRuleNotAppliesToClassWithSelfAndStaticCalls()
+ public function testRuleAppliesTwiceToClassWithNotImportedDependencies()
{
- $rule = new MissingImport();
- $rule->setReport($this->getReportWithNoViolation());
+ $rule = $this->getRule();
+ $rule->setReport($this->getReportMock(2));
$rule->apply($this->getMethod());
}
/**
- * Tests that it does not apply to a function without any class dependencies
- *
- * @return void
- * @covers ::apply
- */
- public function testRuleNotAppliesToFunctionWithoutAnyDependencies()
- {
- $rule = new MissingImport();
- $rule->setReport($this->getReportWithNoViolation());
- $rule->apply($this->getFunction());
- }
-
- /**
- * Tests that it does not apply to a function with only imported classes
+ * Tests the rule ignores classes in global namespace with `ignore-global`.
*
+ * @param string $file The test file to test against.
* @return void
- * @covers ::apply
- * @covers ::isSelfReference
+ * @dataProvider getApplyingCases
*/
- public function testRuleNotAppliesToFunctionWithOnlyImportedDependencies()
+ public function testRuleDoesNotApplyWithIgnoreGlobalProperty($file)
{
- $rule = new MissingImport();
- $rule->setReport($this->getReportWithNoViolation());
- $rule->apply($this->getFunction());
- }
-
- /**
- * Tests that it applies to a function that has fully qualified class names
- *
- * @return void
- * @covers ::apply
- * @covers ::isSelfReference
- */
- public function testRuleAppliesToFunctionWithNotImportedDependencies()
- {
- $rule = new MissingImport();
- $rule->setReport($this->getReportWithOneViolation());
- $rule->apply($this->getFunction());
+ $rule = $this->getRule();
+ $rule->addProperty('ignore-global', true);
+ $this->expectRuleHasViolationsForFile($rule, static::NO_VIOLATION, $file);
}
}
diff --git a/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php b/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
index 87bfa8c51..eae6e2ca8 100644
--- a/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
+++ b/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
@@ -158,7 +158,7 @@ public function testRuleDoesApplyForTestMethodWithUnderscoreWhenNotAllowed()
/**
* Tests that the rule does not apply for a valid test method name
- * with an underscore when an single underscore is allowed.
+ * with an underscore when underscores are allowed.
*
* @return void
*/
@@ -174,13 +174,31 @@ public function testRuleDoesNotApplyForTestMethodWithUnderscoreWhenAllowed()
$rule->apply($method);
}
+ /**
+ * Tests that the rule does not apply for a valid test method name
+ * with multiple underscores in different positions when underscores are allowed.
+ *
+ * @return void
+ */
+ public function testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed()
+ {
+ $method = $this->getMethod();
+ $report = $this->getReportWithNoViolation();
+
+ $rule = new CamelCaseMethodName();
+ $rule->setReport($report);
+ $rule->addProperty('allow-underscore', 'false');
+ $rule->addProperty('allow-underscore-test', 'true');
+ $rule->apply($method);
+ }
+
/**
* Tests that the rule does apply for a test method name
- * with multiple underscores even when one is allowed.
+ * with consecutive underscores even when underscores are allowed.
*
* @return void
*/
- public function testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed()
+ public function testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed()
{
$method = $this->getMethod();
$report = $this->getReportWithOneViolation();
diff --git a/src/test/php/PHPMD/Rule/Design/DevelopmentCodeFragmentTest.php b/src/test/php/PHPMD/Rule/Design/DevelopmentCodeFragmentTest.php
index 4fcffdbc3..542e3e897 100644
--- a/src/test/php/PHPMD/Rule/Design/DevelopmentCodeFragmentTest.php
+++ b/src/test/php/PHPMD/Rule/Design/DevelopmentCodeFragmentTest.php
@@ -65,6 +65,30 @@ public function testRuleAppliesToMethodWithMultipleSuspectFunctionCall()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall
+ *
+ * @return void
+ */
+ public function testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall()
+ {
+ $rule = $this->getRule();
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getMethod());
+ }
+
+ /**
+ * testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall
+ *
+ * @return void
+ */
+ public function testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall()
+ {
+ $rule = $this->getRule();
+ $rule->setReport($this->getReportMock(3));
+ $rule->apply($this->getMethod());
+ }
+
/**
* testRuleNotAppliesToFunctionWithoutSuspectFunctionCall
*
@@ -101,6 +125,30 @@ public function testRuleAppliesToFunctionWithMultipleSuspectFunctionCall()
$rule->apply($this->getFunction());
}
+ /**
+ * testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall
+ *
+ * @return void
+ */
+ public function testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall()
+ {
+ $rule = $this->getRule();
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getFunction());
+ }
+
+ /**
+ * testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall
+ *
+ * @return void
+ */
+ public function testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall()
+ {
+ $rule = $this->getRule();
+ $rule->setReport($this->getReportMock(3));
+ $rule->apply($this->getFunction());
+ }
+
/**
* testRuleAppliesToMethodWithinNamespace
*
diff --git a/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php b/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
index dc0183a78..91025ea86 100644
--- a/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
+++ b/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
@@ -21,6 +21,7 @@
use PDepend\Source\AST\State;
use PHPMD\AbstractTest;
use PHPMD\Node\MethodNode;
+use PHPMD\Report;
/**
* Test case for the too many public methods rule.
@@ -161,6 +162,22 @@ public function testRuleIgnoresWithers()
$rule->apply($this->createClassMock(2, array('invoke', 'withClass')));
}
+ public function testRuleApplyToBasicClass()
+ {
+ $class = $this->getClass();
+ $rule = new TooManyPublicMethods();
+ $report = new Report();
+ $rule->setReport($report);
+ $rule->addProperty('maxmethods', '5');
+ $rule->addProperty('ignorepattern', '');
+ $rule->apply($class);
+ $violations = $report->getRuleViolations();
+
+ $this->assertCount(1, $violations);
+
+ $this->assertSame(6, $violations[0]->getBeginLine());
+ }
+
/**
* Creates a prepared class node mock
*
diff --git a/src/test/php/PHPMD/Rule/Naming/LongClassNameTest.php b/src/test/php/PHPMD/Rule/Naming/LongClassNameTest.php
new file mode 100644
index 000000000..612e6d591
--- /dev/null
+++ b/src/test/php/PHPMD/Rule/Naming/LongClassNameTest.php
@@ -0,0 +1,124 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Rule\Naming;
+
+use PHPMD\AbstractTest;
+
+/**
+ * Test cases for LongClassName.
+ *
+ * @coversDefaultClass \PHPMD\Rule\Naming\LongClassName
+ */
+class LongClassNameTest extends AbstractTest
+{
+ /**
+ * Tests that the rule does not apply to class name length (43) below threshold (44)
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToClassNameBelowThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 44);
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that the rule applies to class name length (40) below threshold (39)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClassNameAboveThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 39);
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that the rule does not apply to interface name length (47) below threshold (47)
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToInterfaceNameBelowThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 47);
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getInterface());
+ }
+
+ /**
+ * Tests that the rule applies to class name length (44) above threshold (43)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToInterfaceNameAboveThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 43);
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getInterface());
+ }
+
+ /**
+ * Tests that the rule does not apply to class name length (69) below threshold (60)
+ * with configured suffix length (9)
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToClassNameLengthWithSuffixSubtractedBelowThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 60);
+ $rule->addProperty('subtract-suffixes', 'Threshold');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that the rule applies to class name length (66) above threshold (56) with configured suffix length (9)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClassNameLengthWithSuffixSubtractedAboveThreshold()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 56);
+ $rule->addProperty('subtract-suffixes', 'Threshold');
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that the rule does not apply to class name length (55) below threshold (54)
+ * not matching configured suffix length (9)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClassNameLengthWithoutSuffixSubtracted()
+ {
+ $rule = new LongClassName();
+ $rule->addProperty('maximum', 54);
+ $rule->addProperty('subtract-suffixes', 'Threshold');
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getClass());
+ }
+}
diff --git a/src/test/php/PHPMD/Rule/Naming/LongVariableTest.php b/src/test/php/PHPMD/Rule/Naming/LongVariableTest.php
index c3b5587ce..8f2b339d8 100644
--- a/src/test/php/PHPMD/Rule/Naming/LongVariableTest.php
+++ b/src/test/php/PHPMD/Rule/Naming/LongVariableTest.php
@@ -34,7 +34,7 @@ class LongVariableTest extends AbstractTest
public function testRuleAppliesToLocalVariableInFunctionWithNameLongerThanThreshold()
{
$rule = new LongVariable();
- $rule->addProperty('maximum', 17);
+ $rule->addProperty('maximum', 21);
$rule->setReport($this->getReportWithOneViolation());
$rule->apply($this->getFunction());
}
@@ -47,7 +47,7 @@ public function testRuleAppliesToLocalVariableInFunctionWithNameLongerThanThresh
public function testRuleNotAppliesToLocalVariableInFunctionWithNameSmallerThanThreshold()
{
$rule = new LongVariable();
- $rule->addProperty('maximum', 17);
+ $rule->addProperty('maximum', 6);
$rule->setReport($this->getReportWithNoViolation());
$rule->apply($this->getFunction());
}
diff --git a/src/test/php/PHPMD/Rule/Naming/ShortClassNameTest.php b/src/test/php/PHPMD/Rule/Naming/ShortClassNameTest.php
new file mode 100644
index 000000000..67dc746f8
--- /dev/null
+++ b/src/test/php/PHPMD/Rule/Naming/ShortClassNameTest.php
@@ -0,0 +1,108 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Rule\Naming;
+
+use PHPMD\AbstractTest;
+
+/**
+ * Test cases for ShortClassName rule
+ *
+ * @coversDefaultClass \PHPMD\Rule\Naming\ShortClassName
+ */
+class ShortClassNameTest extends AbstractTest
+{
+ /**
+ * Tests that rule does not apply to class name length (43) above threshold (43)
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToClassNameAboveThreshold()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 43);
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that rule applies to class name length (40) below threshold (41)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClassNameBelowThreshold()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 41);
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that rule does not apply to interface name length (47) above threshold (47)
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToInterfaceNameAboveThreshold()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 47);
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getInterface());
+ }
+
+ /**
+ * Tests that rule applies for interface name length (44) below threshold (45)
+ *
+ * @return void
+ */
+ public function testRuleAppliesToInterfaceNameBelowThreshold()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 45);
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getInterface());
+ }
+
+ /**
+ * Tests that rule does not apply for class name length (55) below threshold (61) when set in exceptions
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToClassNameBelowThresholdInExceptions()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 61);
+ $rule->addProperty('exceptions', 'testRuleNotAppliesToClassNameBelowThresholdInExceptions');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getClass());
+ }
+
+ /**
+ * Tests that rule applies to class name length (55) below threshold (56) when not set in exceptions
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClassNameBelowThresholdNotInExceptions()
+ {
+ $rule = new ShortClassName();
+ $rule->addProperty('minimum', 56);
+ $rule->addProperty('exceptions', 'RandomClassName');
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getClass());
+ }
+}
diff --git a/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php b/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
index b57f645aa..db5f5dc7d 100644
--- a/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
+++ b/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
@@ -41,6 +41,20 @@ public function testRuleAppliesToLocalVariableInFunctionWithNameShorterThanThres
$rule->apply($this->getFunction());
}
+ /**
+ * testRuleAppliesToTryCatchBlocks
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToTryCatchBlocksInsideForeach()
+ {
+ $rule = new ShortVariable();
+ $rule->addProperty('minimum', 3);
+ $rule->addProperty('exceptions', '');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getFunction());
+ }
+
/**
* testRuleNotAppliesToLocalVariableInFunctionWithNameLongerThanThreshold
*
@@ -345,4 +359,34 @@ public function testRuleNotAppliesToVariablesFromExceptionsList()
$rule->apply($this->getClass());
}
+
+ /**
+ * testRuleAppliesToVariablesWithinForeach
+ *
+ * @dataProvider provideClassWithShortForeachVariables
+ * @return void
+ */
+ public function testRuleAppliesToVariablesWithinForeach($allowShortVarInLoop, $expectedErrorsCount)
+ {
+ $rule = new ShortVariable();
+ $rule->addProperty('minimum', 3);
+ $rule->addProperty('exceptions', '');
+ $rule->addProperty('allow-short-variables-in-loop', $allowShortVarInLoop);
+ $rule->setReport($this->getReportMock($expectedErrorsCount));
+
+ $class = $this->getClass();
+ $rule->apply($class);
+
+ foreach ($class->getMethods() as $method) {
+ $rule->apply($method);
+ }
+ }
+
+ public function provideClassWithShortForeachVariables()
+ {
+ return array(
+ array(true, 2),
+ array(false, 5),
+ );
+ }
}
diff --git a/src/test/php/PHPMD/Rule/UnusedFormalParameterTest.php b/src/test/php/PHPMD/Rule/UnusedFormalParameterTest.php
index 732ccb936..07588c433 100644
--- a/src/test/php/PHPMD/Rule/UnusedFormalParameterTest.php
+++ b/src/test/php/PHPMD/Rule/UnusedFormalParameterTest.php
@@ -63,6 +63,18 @@ public function testRuleAppliesToMethodUnusedFormalParameter()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleAppliesToClosureUnusedFormalParameter
+ *
+ * @return void
+ */
+ public function testRuleAppliesToClosureUnusedFormalParameter()
+ {
+ $rule = new UnusedFormalParameter();
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getMethod());
+ }
+
/**
* testRuleAppliesToMultipleMethodUnusedFormalParameter
*
diff --git a/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php b/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
index 576362a1e..f8fdc900e 100644
--- a/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
+++ b/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
@@ -40,6 +40,18 @@ public function testRuleAppliesToUnusedLocalVariable()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleAppliesToUnusedLocalVariable
+ *
+ * @return void
+ */
+ public function testRuleAppliesToUnusedLocalVariableDeclaredTwice()
+ {
+ $rule = new UnusedLocalVariable();
+ $rule->setReport($this->getReportWithOneViolation());
+ $rule->apply($this->getMethod());
+ }
+
/**
* testInnerFunctionParametersDoNotHideUnusedVariables
*
@@ -297,6 +309,19 @@ public function testRuleDoesNotApplyToCookieSuperGlobal()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleDoesNotApplyToClosureParameter
+ *
+ * @return void
+ */
+ public function testRuleDoesNotApplyToClosureParameter()
+ {
+ $rule = new UnusedLocalVariable();
+ $rule->addProperty('allow-unused-foreach-variables', 'false');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getMethod());
+ }
+
/**
* testRuleDoesNotApplyToEnvSuperGlobal
*
@@ -612,6 +637,28 @@ public function testRuleDoesNotApplyToCompactFunction()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleDoesNotApplyToCompactWithDoubleQuotesFunction
+ *
+ *
+ * class Foo {
+ * public function bar() {
+ * $key = "ok";
+ * return compact("key");
+ * }
+ * }
+ *
+ *
+ * @return void
+ */
+ public function testRuleDoesNotApplyToCompactWithDoubleQuotesFunction()
+ {
+ $rule = new UnusedLocalVariable();
+ $rule->addProperty('allow-unused-foreach-variables', 'false');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getMethod());
+ }
+
/**
* @test
* @return void
diff --git a/src/test/php/PHPMD/RuleSetFactoryTest.php b/src/test/php/PHPMD/RuleSetFactoryTest.php
index a05ca2b18..282cf916c 100644
--- a/src/test/php/PHPMD/RuleSetFactoryTest.php
+++ b/src/test/php/PHPMD/RuleSetFactoryTest.php
@@ -711,6 +711,39 @@ public function testIfGettingRuleFilePathExcludeUnreadablePaths()
$this->assertEquals(5, $ruleSetNotFoundExceptionCount);
}
+ /**
+ * Checks the ruleset XML files provided with PHPMD all provide externalInfoUrls
+ *
+ * @param string $file The path to the ruleset xml to test
+ * @return void
+ * @dataProvider getDefaultRuleSets
+ */
+ public function testDefaultRuleSetsProvideExternalInfoUrls($file)
+ {
+ $ruleSets = $this->createRuleSetsFromFiles($file);
+ $ruleSet = $ruleSets[0];
+ /** @var Rule $rule */
+ foreach ($ruleSet->getRules() as $rule) {
+ $message = sprintf(
+ '%s in rule set %s should provide an externalInfoUrl',
+ $rule->getName(),
+ $ruleSet->getName()
+ );
+
+ $this->assertNotEmpty($rule->getExternalInfoUrl(), $message);
+ }
+ }
+
+ /**
+ * Provides an array of the file paths to rule sets provided with PHPMD
+ *
+ * @return array
+ */
+ public function getDefaultRuleSets()
+ {
+ return static::getValuesAsArrays(glob(__DIR__ . '/../../../main/resources/rulesets/*.xml'));
+ }
+
/**
* Invokes the createRuleSets() of the {@link RuleSetFactory}
* class.
@@ -741,7 +774,7 @@ private function createRuleSetsFromFiles($file)
$args = func_get_args();
$factory = new RuleSetFactory();
-
+
return $factory->createRuleSets(implode(',', $args));
}
diff --git a/src/test/php/PHPMD/Stubs/RuleStub.php b/src/test/php/PHPMD/Stubs/RuleStub.php
index 14f35e746..87624be94 100644
--- a/src/test/php/PHPMD/Stubs/RuleStub.php
+++ b/src/test/php/PHPMD/Stubs/RuleStub.php
@@ -41,6 +41,7 @@ public function __construct($ruleName = 'RuleStub', $ruleSetName = 'TestRuleSet'
$this->setRuleSetName($ruleSetName);
$this->setSince('42.23');
$this->setDescription('Simple rule stub');
+ $this->setMessage('Test description');
}
/**
diff --git a/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php b/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
index a17cf7a9a..e0c2a5c97 100644
--- a/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
+++ b/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
@@ -18,6 +18,7 @@
namespace PHPMD\TextUI;
use PHPMD\AbstractTest;
+use PHPMD\Baseline\BaselineMode;
use PHPMD\Rule;
/**
@@ -187,6 +188,45 @@ public function testCliOptionsAcceptsVersionArgument()
self::assertTrue($opts->hasVersion());
}
+ /**
+ * Tests if ignoreErrorsOnExit returns false by default
+ *
+ * @return void
+ */
+ public function testIgnoreErrorsOnExitReturnsFalseByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'unusedcode');
+ $opts = new CommandLineOptions($args);
+
+ self::assertFalse($opts->ignoreErrorsOnExit());
+ }
+
+ /**
+ * Tests if CLI options accepts ignoreErrorsOnExit argument
+ *
+ * @return void
+ */
+ public function testCliOptionsAcceptsIgnoreErrorsOnExitArgument()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'unusedcode', '--ignore-errors-on-exit');
+ $opts = new CommandLineOptions($args);
+
+ self::assertTrue($opts->ignoreErrorsOnExit());
+ }
+
+ /**
+ * Tests if CLI usage contains ignoreErrorsOnExit option
+ *
+ * @return void
+ */
+ public function testCliUsageContainsIgnoreErrorsOnExitOption()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+
+ $this->assertContains('--ignore-errors-on-exit:', $opts->usage());
+ }
+
/**
* Tests if ignoreViolationsOnExit returns false by default
*
@@ -236,7 +276,10 @@ public function testCliUsageContainsAutoDiscoveredRenderers()
$args = array(__FILE__, __FILE__, 'text', 'codesize');
$opts = new CommandLineOptions($args);
- $this->assertContains('Available formats: ansi, html, json, text, xml.', $opts->usage());
+ $this->assertContains(
+ 'Available formats: ansi, baseline, github, html, json, sarif, text, xml.',
+ $opts->usage()
+ );
}
/**
@@ -302,6 +345,56 @@ public function testCliOptionsAcceptsMaximumpriorityArgument()
$this->assertEquals(42, $opts->getMaximumPriority());
}
+ /**
+ * @return void
+ */
+ public function testCliOptionGenerateBaselineFalseByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::NONE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionGenerateBaselineShouldBeSet()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--generate-baseline');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::GENERATE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionUpdateBaselineShouldBeSet()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--update-baseline');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::UPDATE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionBaselineFileShouldBeNullByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+ static::assertNull($opts->baselineFile());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionBaselineFileShouldBeWithFilename()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--baseline-file', 'foobar.txt');
+ $opts = new CommandLineOptions($args);
+ static::assertSame('foobar.txt', $opts->baselineFile());
+ }
+
/**
* @return void
*/
diff --git a/src/test/php/PHPMD/TextUI/CommandTest.php b/src/test/php/PHPMD/TextUI/CommandTest.php
index 1bb88c3a8..0f49adf5e 100644
--- a/src/test/php/PHPMD/TextUI/CommandTest.php
+++ b/src/test/php/PHPMD/TextUI/CommandTest.php
@@ -9,10 +9,10 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
@@ -45,8 +45,8 @@ protected function tearDown()
}
/**
- * @param $sourceFile
- * @param $expectedExitCode
+ * @param $sourceFile
+ * @param $expectedExitCode
* @param array|null $options
* @return void
* @dataProvider dataProviderTestMainWithOption
@@ -90,6 +90,44 @@ public function dataProviderTestMainWithOption()
Command::EXIT_SUCCESS,
array('--ignore-violations-on-exit'),
),
+ array(
+ 'source/source_with_npath_violation.php',
+ Command::EXIT_VIOLATION,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_ERROR,
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_ERROR,
+ array('--ignore-violations-on-exit'),
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_SUCCESS,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_ERROR,
+ ),
+ array(
+ 'source',
+ Command::EXIT_ERROR,
+ array('--ignore-violations-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_VIOLATION,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_SUCCESS,
+ array('--ignore-errors-on-exit', '--ignore-violations-on-exit'),
+ ),
array(
'source/ccn_suppress_function.php',
Command::EXIT_VIOLATION,
@@ -122,6 +160,8 @@ public function testWithMultipleReportFiles()
$text = self::createTempFileUri(),
'--reportfile-json',
$json = self::createTempFileUri(),
+ '--reportfile-sarif',
+ $sarif = self::createTempFileUri(),
);
Command::main($args);
@@ -130,6 +170,27 @@ public function testWithMultipleReportFiles()
$this->assertFileExists($html);
$this->assertFileExists($text);
$this->assertFileExists($json);
+ $this->assertFileExists($sarif);
+ }
+
+ public function testOutput()
+ {
+ $uri = realpath(self::createFileUri('source/source_with_anonymous_class.php'));
+ $temp = self::createTempFileUri();
+ $exitCode = Command::main(array(
+ __FILE__,
+ $uri,
+ 'text',
+ 'naming',
+ '--reportfile',
+ $temp,
+ ));
+
+ $this->assertSame(Command::EXIT_VIOLATION, $exitCode);
+ $this->assertSame(
+ "$uri:8 Avoid variables with short names like \$a. Configured minimum length is 3." . PHP_EOL,
+ file_get_contents($temp)
+ );
}
/**
@@ -162,10 +223,78 @@ public function dataProviderWithFilter()
{
return array(
array('--suffixes', '.class.php'),
- array('--exclude', 'ccn_,npath_'),
+ array('--exclude', 'ccn_,npath_,parse_error'),
+ );
+ }
+
+ public function testMainGenerateBaseline()
+ {
+ $uri = str_replace("\\", "/", realpath(self::createFileUri('source/source_with_anonymous_class.php')));
+ $temp = self::createTempFileUri();
+ $exitCode = Command::main(array(
+ __FILE__,
+ $uri,
+ 'text',
+ 'naming',
+ '--generate-baseline',
+ '--baseline-file',
+ $temp,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ static::assertFileExists($temp);
+ static::assertContains($uri, file_get_contents($temp));
+ }
+
+ /**
+ * Testcase:
+ * - Class has existing ShortVariable and new BooleanGetMethodName violations
+ * - Baseline has ShortVariable and LongClassName baseline violations
+ * Expect in baseline:
+ * - LongClassName violation should be removed
+ * - ShortVariable violation should still exist
+ * - BooleanGetMethodName shouldn't be added
+ */
+ public function testMainUpdateBaseline()
+ {
+ $sourceTemp = self::createTempFileUri('ClassWithMultipleViolations.php');
+ $baselineTemp = self::createTempFileUri();
+ copy(static::createResourceUriForTest('UpdateBaseline/ClassWithMultipleViolations.php'), $sourceTemp);
+ copy(static::createResourceUriForTest('UpdateBaseline/phpmd.baseline.xml'), $baselineTemp);
+
+ $exitCode = Command::main(array(
+ __FILE__,
+ $sourceTemp,
+ 'text',
+ 'naming',
+ '--update-baseline',
+ '--baseline-file',
+ $baselineTemp,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ static::assertXmlStringEqualsXmlString(
+ file_get_contents(static::createResourceUriForTest('UpdateBaseline/expected.baseline.xml')),
+ file_get_contents($baselineTemp)
);
}
+ public function testMainBaselineViolationShouldBeIgnored()
+ {
+ $sourceFile = realpath(static::createResourceUriForTest('Baseline/ClassWithShortVariable.php'));
+ $baselineFile = realpath(static::createResourceUriForTest('Baseline/phpmd.baseline.xml'));
+ $exitCode = Command::main(array(
+ __FILE__,
+ $sourceFile,
+ 'text',
+ 'naming',
+ '--baseline-file',
+ $baselineFile,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ }
+
public function testMainWritesExceptionMessageToStderr()
{
stream_filter_register('stderr_stream', 'PHPMD\\TextUI\\StreamFilter');
@@ -200,7 +329,7 @@ public function testMainPrintsVersionToStdout()
)
);
- $data = @parse_ini_file(__DIR__ . '/../../../../../build.properties');
+ $data = @parse_ini_file(__DIR__ . '/../../../../../build.properties');
$version = $data['project.version'];
$this->assertEquals('PHPMD ' . $version, trim(StreamFilter::$streamHandle));
diff --git a/src/test/php/PHPMD/Utility/PathsTest.php b/src/test/php/PHPMD/Utility/PathsTest.php
new file mode 100644
index 000000000..b7a634c40
--- /dev/null
+++ b/src/test/php/PHPMD/Utility/PathsTest.php
@@ -0,0 +1,96 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Utility;
+
+use PHPMD\AbstractTest;
+use PHPMD\Utility\Strings;
+
+/**
+ * Test cases for the Strings utility class.
+ *
+ * @coversDefaultClass \PHPMD\Utility\Strings
+ */
+class StringsTest extends AbstractTest
+{
+ /**
+ * Tests the lengthWithoutSuffixes() method with an empty string
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesEmptyString()
+ {
+ static::assertSame(0, Strings::lengthWithoutSuffixes('', array()));
+ }
+
+ /**
+ * Tests the lengthWithoutSuffixes() method with an empty string with list of suffixes
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesEmptyStringWithConfiguredSubtractSuffix()
+ {
+ static::assertSame(0, Strings::lengthWithoutSuffixes('', array('Foo', 'Bar')));
+ }
+
+ /**
+ * Tests the lengthWithoutSuffixes() method with a string not in the list of suffixes
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesStringWithoutSubtractSuffixMatch()
+ {
+ static::assertSame(8, Strings::lengthWithoutSuffixes('UnitTest', array('Foo', 'Bar')));
+ }
+
+ /**
+ * Tests the lengthWithoutSuffixes() method with a string in the list of suffixes
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesStringWithSubtractSuffixMatch()
+ {
+ static::assertSame(4, Strings::lengthWithoutSuffixes('UnitBar', array('Foo', 'Bar')));
+ }
+
+ /**
+ * Tests the lengthWithoutSuffixes() method with a string that should match only once for two potential matches
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesStringWithDoubleSuffixMatchSubtractOnce()
+ {
+ static::assertSame(7, Strings::lengthWithoutSuffixes('UnitFooBar', array('Foo', 'Bar')));
+ }
+
+ /**
+ * Tests the lengthWithoutSuffixes() method that a Prefix should not be matched
+ *
+ * @return void
+ */
+ public function testLengthWithoutSuffixesStringWithPrefixMatchShouldNotSubtract()
+ {
+ static::assertSame(11, Strings::lengthWithoutSuffixes('FooUnitTest', array('Foo', 'Bar')));
+ }
+
+ /**
+ * Tests the splitToList() method with an empty separator
+ *
+ * @expectedException \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function testSplitToListEmptySeparatorThrowsException()
+ {
+ Strings::splitToList('UnitTest', '');
+ }
+
+ /**
+ * Tests the splitToList() method with an empty string
+ *
+ * @return void
+ */
+ public function testSplitToListEmptyString()
+ {
+ static::assertSame(array(), Strings::splitToList('', ','));
+ }
+
+ /**
+ * Tests the splitToList() method with a non-matching separator
+ *
+ * @return void
+ */
+ public function testSplitToListStringWithoutMatchingSeparator()
+ {
+ static::assertSame(array('UnitTest'), Strings::splitToList('UnitTest', ','));
+ }
+
+ /**
+ * Tests the splitToList() method with a matching separator
+ *
+ * @return void
+ */
+ public function testSplitToListStringWithMatchingSeparator()
+ {
+ static::assertSame(array('Unit', 'Test'), Strings::splitToList('Unit,Test', ','));
+ }
+
+ /**
+ * Tests the splitToList() method with trailing whitespace
+ *
+ * @return void
+ */
+ public function testSplitToListStringTrimsLeadingAndTrailingWhitespace()
+ {
+ static::assertSame(array('Unit', 'Test'), Strings::splitToList('Unit , Test', ','));
+ }
+
+ /**
+ * Tests the splitToList() method that it removes empty strings from list
+ *
+ * @return void
+ */
+ public function testSplitToListStringRemoveEmptyStringValues()
+ {
+ static::assertSame(array('Foo'), Strings::splitToList('Foo,,,', ','));
+ }
+
+ /**
+ * Tests the splitToList() method that it does not remove zero values from list
+ *
+ * @return void
+ */
+ public function testSplitToListStringShouldNotRemoveAZeroValue()
+ {
+ static::assertSame(array('0', '1', '2'), Strings::splitToList('0,1,2', ','));
+ }
+}
diff --git a/src/test/php/PHPMD/Writer/StreamWriterTest.php b/src/test/php/PHPMD/Writer/StreamWriterTest.php
new file mode 100644
index 000000000..16023e2db
--- /dev/null
+++ b/src/test/php/PHPMD/Writer/StreamWriterTest.php
@@ -0,0 +1,21 @@
+getStream());
+ }
+}
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml
new file mode 100644
index 000000000..5a933716b
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml
new file mode 100644
index 000000000..c6b22a7b0
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml
new file mode 100644
index 000000000..c6b22a7b0
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml
new file mode 100644
index 000000000..7c19c20da
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml
new file mode 100644
index 000000000..663e2aee6
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml
new file mode 100644
index 000000000..b4cd528e2
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml
new file mode 100644
index 000000000..0c54c3d25
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesToClassWithNotImportedDependencies.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesTwiceToClassWithNotImportedDependencies.php
similarity index 89%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesToClassWithNotImportedDependencies.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesTwiceToClassWithNotImportedDependencies.php
index 34d2aca7a..8eef4bd5c 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesToClassWithNotImportedDependencies.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleAppliesTwiceToClassWithNotImportedDependencies.php
@@ -19,7 +19,7 @@
class Foo
{
- public function testRuleAppliesToClassWithNotImportedDependencies()
+ public function testRuleAppliesTwiceToClassWithNotImportedDependencies()
{
$object = new \stdClass();
new \DateTime('2019-02-02 00:00:00');
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithOnlyImportedDependencies.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithOnlyImportedDependencies.php
similarity index 88%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithOnlyImportedDependencies.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithOnlyImportedDependencies.php
index 68978974d..58b4db219 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithOnlyImportedDependencies.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithOnlyImportedDependencies.php
@@ -21,7 +21,7 @@
class Foo
{
- public function testRuleNotAppliesToClassWithOnlyImportedDependencies()
+ public function testRuleDoesNotApplyToClassWithOnlyImportedDependencies()
{
$object = new stdClass();
}
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithSelfAndStaticCalls.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithSelfAndStaticCalls.php
similarity index 89%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithSelfAndStaticCalls.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithSelfAndStaticCalls.php
index 07cd5a5dc..e043088e4 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithSelfAndStaticCalls.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithSelfAndStaticCalls.php
@@ -19,7 +19,7 @@
class Foo
{
- public function testRuleNotAppliesToClassWithSelfAndStaticCalls()
+ public function testRuleDoesNotApplyToClassWithSelfAndStaticCalls()
{
$self = new self();
new static();
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithoutAnyDependencies.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithoutAnyDependencies.php
similarity index 88%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithoutAnyDependencies.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithoutAnyDependencies.php
index f3c58864c..2c9a3fd5c 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToClassWithoutAnyDependencies.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToClassWithoutAnyDependencies.php
@@ -19,7 +19,7 @@
class Foo
{
- public function testRuleNotAppliesToClassWithoutAnyDependencies()
+ public function testRuleDoesNotApplyToClassWithoutAnyDependencies()
{
}
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php
new file mode 100644
index 000000000..ace0d86e0
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php
@@ -0,0 +1,28 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMDTest;
+
+class testRuleDoesNotApplyToDynamicClassName
+{
+ public function testRuleDoesNotApplyToDynamicClassName()
+ {
+ $className = 'DateTime';
+
+ return (new $className)->format('Y');
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithOnlyImportedDependencies.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithOnlyImportedDependencies.php
similarity index 89%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithOnlyImportedDependencies.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithOnlyImportedDependencies.php
index 0b67f0667..44a5d89c9 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithOnlyImportedDependencies.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithOnlyImportedDependencies.php
@@ -19,7 +19,7 @@
use stdClass;
-function testRuleNotAppliesToFunctionWithOnlyImportedDependencies()
+function testRuleDoesNotApplyToFunctionWithOnlyImportedDependencies()
{
$a = new stdClass();
}
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithoutAnyDependencies.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithoutAnyDependencies.php
similarity index 89%
rename from src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithoutAnyDependencies.php
rename to src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithoutAnyDependencies.php
index 8dc8c9873..d44b28ea0 100644
--- a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleNotAppliesToFunctionWithoutAnyDependencies.php
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToFunctionWithoutAnyDependencies.php
@@ -17,7 +17,7 @@
namespace PHPMDTest;
-function testRuleNotAppliesToFunctionWithoutAnyDependencies()
+function testRuleDoesNotApplyToFunctionWithoutAnyDependencies()
{
$a = null;
}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php
new file mode 100644
index 000000000..d4d63e28d
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php
@@ -0,0 +1,31 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToExtraParameters
+{
+ function testRuleAppliesToExtraParameters()
+ {
+ $x = 42;
+
+ $this->foo($x, $y);
+ }
+
+ function foo(&$a)
+ {
+ $a++;
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php
new file mode 100644
index 000000000..b8c8046ef
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php
@@ -0,0 +1,29 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToMethodMatchingFunctionName
+{
+ public function testRuleAppliesToMethodMatchingFunctionName()
+ {
+ $this->preg_match('a', 'b', $undefined);
+ }
+
+ public function preg_match($a, $b, $c)
+ {
+ // noop
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
index 0937f52a7..5f9ba6ef7 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableOnArray extends AbstractTest
+class testRuleAppliesToUndefinedVariableOnArray
{
function testRuleAppliesToUndefinedVariableOnArray()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
index cdba11288..12dfca0f8 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableOnArray extends AbstractTest
+class testRuleAppliesToUndefinedVariableOnArray
{
function testRuleAppliesToUndefinedVariableOnArrayWithKeys()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
index 39f2d95d4..9f088e737 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableWithDefinedVariable extends AbstractTest
+class testRuleAppliesToUndefinedVariableWithDefinedVariable
{
function testRuleAppliesToUndefinedVariableWithDefinedVariable()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php
new file mode 100644
index 000000000..4850530b2
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToUnknownArguments
+{
+ function testRuleAppliesToUnknownArguments(UnknownObject $object)
+ {
+ $object->foo($a);
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToCallsWithStaticProperties.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToCallsWithStaticProperties.php
new file mode 100644
index 000000000..8e738fd7d
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToCallsWithStaticProperties.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToCallsWithStaticProperties
+{
+ protected static $test = [];
+
+ function testRuleDoesNotApplyToCallsWithStaticProperties()
+ {
+ self::$test[0]->foo();
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToNestedArrays.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToNestedArrays.php
new file mode 100644
index 000000000..7d68f426b
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToNestedArrays.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToNestedArrays
+{
+ function testRuleDoesNotApplyToNestedArrays()
+ {
+ $foo = 'foo';
+ $arr = array();
+ $arr[$foo]['bar']['baz'] = 'yes';
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php
new file mode 100644
index 000000000..e31411476
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToUnknownMethod
+{
+ function testRuleDoesNotApplyToUnknownMethod(UnknownObject $object)
+ {
+ $this->foo($object);
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
index 81598cf6a..528f8501d 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleDoesNotApplyToUsedProperties extends AbstractTest
+class testRuleDoesNotApplyToUsedProperties
{
protected $x = 'abc';
diff --git a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php
new file mode 100644
index 000000000..588302650
--- /dev/null
+++ b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed
+{
+ public function testGivenSomeValue_expectSome__niceResult()
+ {
+
+ }
+}
diff --git a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
similarity index 79%
rename from src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php
rename to src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
index 751d73ce8..d506183de 100644
--- a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php
+++ b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
@@ -15,9 +15,9 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed
+class testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed
{
- public function testGivenSomeValue_expectSome_niceResult()
+ public function testGivenSomeValue_expect_some_result()
{
}
diff --git a/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall.php b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall.php
new file mode 100644
index 000000000..80beceeb3
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall.php
@@ -0,0 +1,23 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+function testRuleAppliesToFunctionWithMultipleSuspectFullyQualifiedFunctionCall()
+{
+ \var_dump(__FUNCTION__);
+ \debug_print_backtrace();
+ \debug_zval_dump($GLOBALS);
+}
diff --git a/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall.php b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall.php
new file mode 100644
index 000000000..082356142
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+function testRuleAppliesToFunctionWithSuspectFullyQualifiedFunctionCall()
+{
+ \print_r(__FUNCTION__);
+}
diff --git a/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall.php b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall.php
new file mode 100644
index 000000000..d58eadb99
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class class_testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall
+{
+ public function testRuleAppliesToMethodWithMultipleSuspectFullyQualifiedFunctionCall()
+ {
+ \var_dump(__FUNCTION__);
+ \print_r(__METHOD__);
+ \debug_zval_dump($this);
+ }
+}
diff --git a/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall.php b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall.php
new file mode 100644
index 000000000..54bdca995
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/DevelopmentCodeFragment/testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class class_testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall
+{
+ public function testRuleAppliesToMethodWithSuspectFullyQualifiedFunctionCall()
+ {
+ \var_dump(__METHOD__);
+ }
+}
diff --git a/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php b/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php
new file mode 100644
index 000000000..c25a71677
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php
@@ -0,0 +1,36 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 40
+ */
+class testRuleAppliesToClassNameAboveThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithSuffixSubtractedAboveThreshold.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithSuffixSubtractedAboveThreshold.php
new file mode 100644
index 000000000..0bb826ba6
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithSuffixSubtractedAboveThreshold.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 66
+ */
+class testRuleAppliesToClassNameLengthWithSuffixSubtractedAboveThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithoutSuffixSubtracted.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithoutSuffixSubtracted.php
new file mode 100644
index 000000000..76a90d584
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToClassNameLengthWithoutSuffixSubtracted.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 55
+ */
+class testRuleAppliesToClassNameLengthWithoutSuffixSubtracted
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToInterfaceNameAboveThreshold.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToInterfaceNameAboveThreshold.php
new file mode 100644
index 000000000..28f9cd0b4
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleAppliesToInterfaceNameAboveThreshold.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Interface name length: 44
+ */
+interface testRuleAppliesToInterfaceNameAboveThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameBelowThreshold.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameBelowThreshold.php
new file mode 100644
index 000000000..1c8e1ece4
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameBelowThreshold.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 43
+ */
+class testRuleNotAppliesToClassNameBelowThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameLengthWithSuffixSubtractedBelowThreshold.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameLengthWithSuffixSubtractedBelowThreshold.php
new file mode 100644
index 000000000..acb6838aa
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToClassNameLengthWithSuffixSubtractedBelowThreshold.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 69
+ */
+class testRuleNotAppliesToClassNameLengthWithSuffixSubtractedBelowThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToInterfaceNameBelowThreshold.php b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToInterfaceNameBelowThreshold.php
new file mode 100644
index 000000000..ec2d082fb
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/LongClassName/testRuleNotAppliesToInterfaceNameBelowThreshold.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+/**
+ * Class name length: 47
+ */
+interface testRuleNotAppliesToInterfaceNameBelowThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThreshold.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThreshold.php
new file mode 100644
index 000000000..8d4476941
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThreshold.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToClassNameBelowThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThresholdNotInExceptions.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThresholdNotInExceptions.php
new file mode 100644
index 000000000..d6146248b
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToClassNameBelowThresholdNotInExceptions.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToClassNameBelowThresholdNotInExceptions
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToInterfaceNameBelowThreshold.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToInterfaceNameBelowThreshold.php
new file mode 100644
index 000000000..6b4dc7b09
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleAppliesToInterfaceNameBelowThreshold.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+interface testRuleAppliesToInterfaceNameBelowThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameAboveThreshold.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameAboveThreshold.php
new file mode 100644
index 000000000..1b5634ac7
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameAboveThreshold.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleNotAppliesToClassNameAboveThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameBelowThresholdInExceptions.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameBelowThresholdInExceptions.php
new file mode 100644
index 000000000..67d2cc3f6
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToClassNameBelowThresholdInExceptions.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleNotAppliesToClassNameBelowThresholdInExceptions
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToInterfaceNameAboveThreshold.php b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToInterfaceNameAboveThreshold.php
new file mode 100644
index 000000000..8e8483f71
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortClassName/testRuleNotAppliesToInterfaceNameAboveThreshold.php
@@ -0,0 +1,21 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+interface testRuleNotAppliesToInterfaceNameAboveThreshold
+{
+
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortVariable/testRuleAppliesToVariablesWithinForeach.php b/src/test/resources/files/Rule/Naming/ShortVariable/testRuleAppliesToVariablesWithinForeach.php
new file mode 100755
index 000000000..96b31894b
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortVariable/testRuleAppliesToVariablesWithinForeach.php
@@ -0,0 +1,34 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToVariablesWithinForeach
+{
+ public function testing()
+ {
+ $abc = 1;
+
+ foreach ([1, 2, 3] as $i) {
+ $a = $i;
+ echo $a;
+ foreach ([4, 5, 6] as $k => $v) {
+ echo $k;
+ echo $v;
+ $x = (string) $k . (string) $v;
+ }
+ }
+ }
+}
diff --git a/src/test/resources/files/Rule/Naming/ShortVariable/testRuleNotAppliesToTryCatchBlocksInsideForeach.php b/src/test/resources/files/Rule/Naming/ShortVariable/testRuleNotAppliesToTryCatchBlocksInsideForeach.php
new file mode 100644
index 000000000..614da105a
--- /dev/null
+++ b/src/test/resources/files/Rule/Naming/ShortVariable/testRuleNotAppliesToTryCatchBlocksInsideForeach.php
@@ -0,0 +1,27 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+function testRuleNotAppliesToTryCatchBlocksInsideForeach()
+{
+ foreach ([1, 2] as $number) {
+ try {
+ echo $number;
+ } catch (InvalidArgumentException $e) {
+ var_dump($e);
+ }
+ }
+}
diff --git a/src/test/resources/files/Rule/UnusedFormalParameter/testRuleAppliesToClosureUnusedFormalParameter.php b/src/test/resources/files/Rule/UnusedFormalParameter/testRuleAppliesToClosureUnusedFormalParameter.php
new file mode 100644
index 000000000..05bc3e449
--- /dev/null
+++ b/src/test/resources/files/Rule/UnusedFormalParameter/testRuleAppliesToClosureUnusedFormalParameter.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToClosureUnusedFormalParameter
+{
+ public function testRuleAppliesToClosureUnusedFormalParameter()
+ {
+ return function ($blah) {
+
+ };
+ }
+}
diff --git a/src/test/resources/files/Rule/UnusedLocalVariable/testRuleAppliesToUnusedLocalVariableDeclaredTwice.php b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleAppliesToUnusedLocalVariableDeclaredTwice.php
new file mode 100644
index 000000000..2249a2d46
--- /dev/null
+++ b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleAppliesToUnusedLocalVariableDeclaredTwice.php
@@ -0,0 +1,25 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToUnusedLocalVariableDeclaredTwice
+{
+ function testRuleAppliesToUnusedLocalVariableDeclaredTwice()
+ {
+ $x = 42;
+ $x = 42;
+ }
+}
diff --git a/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToClosureParameter.php b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToClosureParameter.php
new file mode 100644
index 000000000..2a1c9e29a
--- /dev/null
+++ b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToClosureParameter.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToClosureParameter
+{
+ function testRuleDoesNotApplyToClosureParameter()
+ {
+ return function ($blah) {
+
+ };
+ }
+}
diff --git a/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php
new file mode 100755
index 000000000..f946564a2
--- /dev/null
+++ b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToCompactWithDoubleQuotesFunction
+{
+ public function testRuleDoesNotApplyToCompactWithDoubleQuotesFunction()
+ {
+ $key = 'ok';
+
+ return compact("key");
+ }
+}
diff --git a/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php b/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php
new file mode 100644
index 000000000..7b1adbcd6
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php b/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php
new file mode 100644
index 000000000..2f89ee6f4
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml b/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml
new file mode 100644
index 000000000..4385c988f
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/test/resources/files/Utility/Paths/resource.txt b/src/test/resources/files/Utility/Paths/resource.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/test/resources/files/pmd/default-xml.xml b/src/test/resources/files/pmd/default-xml.xml
index 0b05f1626..8f53dc552 100755
--- a/src/test/resources/files/pmd/default-xml.xml
+++ b/src/test/resources/files/pmd/default-xml.xml
@@ -1,5 +1,5 @@
-
+
The function ccn_function() has a Cyclomatic Complexity of 12. The configured cyclomatic complexity threshold is 10.
diff --git a/src/test/resources/files/pmd/single-directory.xml b/src/test/resources/files/pmd/single-directory.xml
index df5097924..e5656efdf 100755
--- a/src/test/resources/files/pmd/single-directory.xml
+++ b/src/test/resources/files/pmd/single-directory.xml
@@ -1,5 +1,5 @@
-
+
The function ccn_function() has a Cyclomatic Complexity of 12. The configured cyclomatic complexity threshold is 10.
@@ -18,4 +18,5 @@
The method doSomething() has an NPath complexity of 50000. The configured NPath complexity threshold is 50.
+
\ No newline at end of file
diff --git a/src/test/resources/files/pmd/single-file.xml b/src/test/resources/files/pmd/single-file.xml
index 0b05f1626..8f53dc552 100755
--- a/src/test/resources/files/pmd/single-file.xml
+++ b/src/test/resources/files/pmd/single-file.xml
@@ -1,5 +1,5 @@
-
+
The function ccn_function() has a Cyclomatic Complexity of 12. The configured cyclomatic complexity threshold is 10.
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected1.xml b/src/test/resources/files/renderer/baseline_renderer_expected1.xml
new file mode 100644
index 000000000..b1a7f932e
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected1.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected2.xml b/src/test/resources/files/renderer/baseline_renderer_expected2.xml
new file mode 100644
index 000000000..001b78a22
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected2.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected3.xml b/src/test/resources/files/renderer/baseline_renderer_expected3.xml
new file mode 100644
index 000000000..5a933716b
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected3.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/renderer/sarif_renderer_expected.sarif b/src/test/resources/files/renderer/sarif_renderer_expected.sarif
new file mode 100644
index 000000000..41cea6ca7
--- /dev/null
+++ b/src/test/resources/files/renderer/sarif_renderer_expected.sarif
@@ -0,0 +1,137 @@
+{
+ "version": "2.1.0",
+ "$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "PHPMD",
+ "informationUri": "https:\/\/phpmd.org",
+ "version": "@package_version@",
+ "rules": [
+ {
+ "id": "TestRuleSet\/RuleStub",
+ "name": "RuleStub",
+ "shortDescription": {
+ "text": "TestRuleSet: RuleStub"
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Test description"
+ }
+ },
+ "help": {
+ "text": "Simple rule stub"
+ },
+ "helpUri": "https:\/\/phpmd.org\/rules\/index.html",
+ "properties": {
+ "ruleSet": "TestRuleSet",
+ "priority": 5,
+ "since": "PHPMD 42.23"
+ }
+ },
+ {
+ "id": "TestRuleSet\/AnotherRuleStub",
+ "name": "AnotherRuleStub",
+ "shortDescription": {
+ "text": "TestRuleSet: AnotherRuleStub"
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Test description"
+ }
+ },
+ "help": {
+ "text": "Simple rule stub",
+ "markdown": "Simple rule stub\n\n### Example\n\n```php\nclass Example\n{\n}\n```\n\n```php\nclass AnotherExample\n{\n public $var;\n}\n```"
+ },
+ "helpUri": "https:\/\/phpmd.org\/rules\/index.html",
+ "properties": {
+ "ruleSet": "TestRuleSet",
+ "priority": 5
+ }
+ }
+ ]
+ }
+ },
+ "originalUriBaseIds": {
+ "WORKINGDIR": {
+ "uri": "file:\/\/#{workingDirectory}\/"
+ }
+ },
+ "results": [
+ {
+ "ruleId": "TestRuleSet\/RuleStub",
+ "ruleIndex": 0,
+ "message": {
+ "id": "default",
+ "arguments": [],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/bar.php"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ },
+ {
+ "ruleId": "TestRuleSet\/RuleStub",
+ "ruleIndex": 0,
+ "message": {
+ "id": "default",
+ "arguments": [],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/foo.php"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ },
+ {
+ "ruleId": "TestRuleSet\/AnotherRuleStub",
+ "ruleIndex": 1,
+ "message": {
+ "id": "default",
+ "arguments": [
+ "123",
+ "3.2",
+ "awesomeFunction()"
+ ],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "src\/foobar.php",
+ "uriBaseId": "WORKINGDIR"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif b/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif
new file mode 100644
index 000000000..d8d273cac
--- /dev/null
+++ b/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif
@@ -0,0 +1,84 @@
+{
+ "version": "2.1.0",
+ "$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "PHPMD",
+ "informationUri": "https:\/\/phpmd.org",
+ "version": "@package_version@",
+ "rules": []
+ }
+ },
+ "originalUriBaseIds": {
+ "WORKINGDIR": {
+ "uri": "file:\/\/#{workingDirectory}\/"
+ }
+ },
+ "results": [
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022\/tmp\/foo.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/foo.php"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022\/tmp\/bar.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/bar.php"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022#{rootDirectory}\/foobar.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "src\/test\/resources\/files\/foobar.php",
+ "uriBaseId": "WORKINGDIR"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Cannot read file \u0022\/tmp\/foo.php\u0022. Permission denied."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/foo.php"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/test/resources/files/renderer/xml_renderer_expected1.xml b/src/test/resources/files/renderer/xml_renderer_expected1.xml
index c38d43b40..eb7eaa5fe 100755
--- a/src/test/resources/files/renderer/xml_renderer_expected1.xml
+++ b/src/test/resources/files/renderer/xml_renderer_expected1.xml
@@ -1,5 +1,5 @@
-
+
Test description
diff --git a/src/test/resources/files/renderer/xml_renderer_processing_errors.xml b/src/test/resources/files/renderer/xml_renderer_processing_errors.xml
index 964cee322..0c08ef5cb 100755
--- a/src/test/resources/files/renderer/xml_renderer_processing_errors.xml
+++ b/src/test/resources/files/renderer/xml_renderer_processing_errors.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/src/test/resources/files/source/source_with_anonymous_class.php b/src/test/resources/files/source/source_with_anonymous_class.php
new file mode 100644
index 000000000..036fc9ad3
--- /dev/null
+++ b/src/test/resources/files/source/source_with_anonymous_class.php
@@ -0,0 +1,14 @@
+