33
2
mirror of https://github.com/joomla-extensions/jedchecker.git synced 2024-11-16 10:05:14 +00:00

clean PHP code (by removing comments, html, and strings) in the framework rules to avoid false-positives

This commit is contained in:
Denis Ryabov 2021-02-23 23:23:45 +03:00
parent 21faa210dc
commit fb16f918d3

View File

@ -104,12 +104,14 @@ class JedcheckerRulesFramework extends JEDcheckerRule
*/ */
protected function find($file) protected function find($file)
{ {
$content = (array) file($file); $origContent = (array) file($file);
$cleanContent = preg_split("/(?:\r\n|\n|\r)(?!$)/", $this->cleanNonCode($file));
$result = false; $result = false;
foreach ($this->getTests() as $testObject) foreach ($this->getTests() as $testObject)
{ {
if ($this->runTest($file, $content, $testObject)) if ($this->runTest($file, $origContent, $cleanContent, $testObject))
{ {
$result = true; $result = true;
} }
@ -118,35 +120,146 @@ class JedcheckerRulesFramework extends JEDcheckerRule
return $result; return $result;
} }
/**
* @param string $file
*
* @return string
*/
protected function cleanNonCode($file)
{
$content = file_get_contents($file);
if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE))
{
// No PHP code found
return '';
}
$pos = $match[0][1];
$cleanContent = $this->removeContent(substr($content, 0, $pos));
while (preg_match('/(?:[\'"]|\/\*|\/\/|\?>)/', $content, $match, PREG_OFFSET_CAPTURE, $pos))
{
$foundPos = $match[0][1];
$cleanContent .= substr($content, $pos, $foundPos - $pos);
$pos = $foundPos;
switch ($match[0][0])
{
case '"':
case "'":
$q = $match[0][0];
if (!preg_match("/$q(?>[^$q\\\\]+|\\\\.)*$q/As", $content, $match, 0, $pos))
{
return $cleanContent . $q;
}
$cleanContent .= $q . $this->removeContent($match[0]) . $q;
$pos += strlen($match[0]);
break;
case '/*':
$cleanContent .= '/*';
$pos += 2;
$endPos = strpos($content, '*/', $pos);
if ($endPos === false)
{
return $cleanContent;
}
$cleanContent .= $this->removeContent(substr($content, $pos, $endPos - $pos)) . '*/';
$pos = $endPos + 2;
break;
case '//':
$pos += strcspn($content, "\r\n", $pos);
break;
case '?>':
$cleanContent .= '?>';
$pos += 2;
if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE, $pos))
{
// No PHP code found (up to the end of the file)
return $cleanContent;
}
$foundPos = $match[0][1];
$cleanContent .= $this->removeContent(substr($content, $pos, $foundPos - $pos)) . $match[0][0];
$pos = $foundPos + strlen($match[0][0]);
break;
}
}
return $cleanContent;
}
/**
* Remove all text content by keeping newline characters only
*
* @param string $content
*
* @return string
*/
protected function removeContent($content)
{
return str_repeat("\n", substr_count($content, "\n"));
}
/** /**
* runs tests and reports to the appropriate function if strings match. * runs tests and reports to the appropriate function if strings match.
* *
* @param string $file The file name * @param string $file The file name
* @param array $content The file content * @param array $origContent The file content
* @param array $cleanContent The file content w/o non-code elements
* @param object $testObject The test object generated by getTests() * @param object $testObject The test object generated by getTests()
* *
* @return boolean * @return boolean
*/ */
private function runTest($file, $content, $testObject) private function runTest($file, $origContent, $cleanContent, $testObject)
{ {
// @todo remove as unused?
$error_count = 0; $error_count = 0;
foreach ($content as $line_number => $line) foreach ($cleanContent as $line_number => $line)
{ {
$origLine = $origContent[$line_number];
foreach ($testObject->tests AS $singleTest) foreach ($testObject->tests AS $singleTest)
{ {
if (stripos($line, $singleTest) !== false) $regex = preg_quote($singleTest, '/');
if (ctype_alpha($singleTest[0]))
{ {
$line = str_ireplace($singleTest, '<b>' . $singleTest . '</b>', $line); $regex = '(?<=\W|^)' . $regex;
$error_message = JText::_('COM_JEDCHECKER_ERROR_FRAMEWORK_' . strtoupper($testObject->group)) . ':<pre>' . $line . '</pre>'; }
if (ctype_alpha($singleTest[strlen($singleTest) - 1]))
{
$regex .= '(?=\W|$)';
}
if (preg_match('/' . $regex . '/i', $line))
{
$origLine = str_ireplace($singleTest, '<b>' . $singleTest . '</b>', htmlspecialchars($origLine));
$error_message = JText::_('COM_JEDCHECKER_ERROR_FRAMEWORK_' . strtoupper($testObject->group)) . ':<pre>' . $origLine . '</pre>';
switch ($testObject->kind) switch ($testObject->kind)
{ {
case 'error':$this->report->addError($file, $error_message, $line_number); case 'error':
$this->report->addError($file, $error_message, $line_number);
break; break;
case 'warning':$this->report->addWarning($file, $error_message, $line_number); case 'warning':
$this->report->addWarning($file, $error_message, $line_number);
break; break;
case 'compatibility':$this->report->addCompat($file, $error_message, $line_number); case 'compatibility':
$this->report->addCompat($file, $error_message, $line_number);
break; break;
default: default:
// Case 'notice': // Case 'notice':
@ -154,6 +267,7 @@ class JedcheckerRulesFramework extends JEDcheckerRule
break; break;
} }
} }
// If you scored 10 errors on a single file, that's enough for now. // If you scored 10 errors on a single file, that's enough for now.
if ($error_count > 10) if ($error_count > 10)
{ {