PHPMD(PHP Mess Detector)在 Eclipse Mars 中的集成
PHPMD (PHP Mess Detector) integration in Eclipse Mars
在 Mars 发布之前,可以在 Eclipse 中安装 PHPMD 支持,尽管需要 some caveats and difficulties。
现在似乎完全取消了 PTI 的支持,即使 development of PHPMD hasn't stopped and PHPMD does offer some features that other tools do not: for example, detect unused variables。
对于最后一个功能,我发现了一个 not too recent CodeSniffer plugin that does the trick. There are also some sniffs 应该可以完成的工作,但它们似乎对我不起作用,或者至少在所有情况下都不起作用:我有一个项目需要重构,CodeSniffer 发出了 11 条警告,PHPMD 发出了 2524 条警告。
我想我有一个简单而不优雅的方法来重新插入 PHPMD,但是在做 that 之前,我想知道是否有人有这个特定的 problem/need,并且s/he 是否设法以某种方式解决了它。
好的,就这样吧。
我有一个可以从命令行运行的 PHPMD 二进制文件。我计划做的是 将其输出注入到 CodeSniffer 插件的输出中 ,"enrich" 后者带有 PHPMD 消息。
为此,我破坏了 plugins/org.ppsrc.eclipse.pti.tools.codesniffer_.../php/tools
目录中的 phpcs.php
。
(因为我对 CodeSniffer 的另一个问题是它会经常重新扫描它应该知道的文件,我决定给 CodeSniffer 一个记忆。
首先,我提取调用的最后一个参数,即正在分析的文件(标有+++的行是我的additions/changes):
// Optionally use PHP_Timer to print time/memory stats for the run.
// Note that the reports are the ones who actually print the data
// as they decide if it is ok to print this data to screen.
@include_once 'PHP/Timer.php';
if (class_exists('PHP_Timer', false) === true) {
PHP_Timer::start();
}
if (is_file(dirname(__FILE__).'/../CodeSniffer/CLI.php') === true) {
include_once dirname(__FILE__).'/../CodeSniffer/CLI.php';
} else {
include_once 'PHP/CodeSniffer/CLI.php';
}
+++ $lastArgument = array_pop($_SERVER['argv']);
然后我添加一些 CS 似乎没有通过的标志,但我需要,例如忽略一些目录:
+++ $_SERVER['argv'][] = '--ignore=tests,vendor,cache';
+++ $_SERVER['argv'][] = $lastArgument;
然后 CS 调用继续进行,但现在我将其结果保存到缓冲区,而不是直接将它们发送到 Eclipse。
$phpcs = new PHP_CodeSniffer_CLI();
$phpcs->checkRequirements();
+++ ob_start();
$numErrors = $phpcs->process();
+++ $dom = new DOMDocument();
+++ $dom->loadXML(ob_get_clean());
+++ $cs = $dom->getElementsByTagName('phpcs')->item(0);
+++ $xpath = new DOMXPath($dom);
现在我已经准备好 PHPCS 输出 XML。
剩下的就是使用它自己的语法调用 PHPMD。
// Add PHPMD.
$mdCmd = "C:/PHP/composer/vendor/phpmd/phpmd/src/bin/phpmd \"{$lastArgument}\" xml \"C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/codesize.xml,C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/naming.xml,C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/unusedcode.xml\"";
...并将其加载到另一个 XML:
fprintf(STDERR, $mdCmd . "\n");
$dompmd = new DOMDocument();
$dompmd->loadXML($mdxml = shell_exec($mdCmd));
现在我从 PMD 对象中取出所有错误,并将其添加到 CS 对象中:
$files = $dompmd->getElementsByTagName('file');
foreach ($files as $file) {
$name = $file->getAttribute('name');
$list = $xpath->query("//file[@name=\"{$name}\"]");
if (null === $list) {
continue;
}
$csFile = $list->item(0);
if (null === $csFile) {
// No errors from CS.
$csFile = $dom->createElement('file');
$csFile->setAttribute('name', $name);
$csFile->setAttribute('errors', 0);
$csFile->setAttribute('warnings', 0);
$csFile->setAttribute('fixable', 0);
$cs->appendChild($csFile);
}
$errs = 0;
foreach ($file->childNodes as $violation) {
if ($violation instanceof \DOMText) {
continue;
}
$error = $dom->createElement('warning', trim($violation->nodeValue));
$error->setAttribute('line', $violation->getAttribute('beginline'));
$error->setAttribute('column', 1);
$error->setAttribute('source', 'PHPMD.' . $violation->getAttribute('ruleset'));
$error->setAttribute('severity', $violation->getAttribute('priority'));
$error->setAttribute('fixable', 0);
$csFile->appendChild($error);
$errs++;
}
$csFile->getAttributeNode('errors')->value += $errs;
}
最后,将数据传回Eclipse:
print $dom->saveXML();
exit($numErrors ? 1: 0);
缓存 CodeSniffer(还有 PHPMD)
由于我对 CodeSniffer 的另一个问题是它会经常重新扫描它应该知道的文件,我决定给 CodeSniffer 一个记忆。这很简单:我可以存储一个临时文件,其中包含已保存的 XML 和由原始文件名 和 的 MD5 构建的名称其内容:
/tmp/68ce1959ef67bcc94e05084e2e20462a.76e55e72f32156a20a183de82fe0b3b6.xml
所以当PHPCS被要求分析/path/to/file/so-and-so.php
时,它会:
- 创建文件名的 MD5。
- 创建其内容的 MD5。
- 如果/tmp/md5file.md5content.xml不存在:
- 删除任何 /tmp/md5file.*.xml 有:它们已过时。
- 运行 代码如上。
- 将结果保存到 /tmp/md5file.md5content.xml
- 输出/tmp/md5file.md5content.xml.
的内容
在 Mars 发布之前,可以在 Eclipse 中安装 PHPMD 支持,尽管需要 some caveats and difficulties。
现在似乎完全取消了 PTI 的支持,即使 development of PHPMD hasn't stopped and PHPMD does offer some features that other tools do not: for example, detect unused variables。
对于最后一个功能,我发现了一个 not too recent CodeSniffer plugin that does the trick. There are also some sniffs 应该可以完成的工作,但它们似乎对我不起作用,或者至少在所有情况下都不起作用:我有一个项目需要重构,CodeSniffer 发出了 11 条警告,PHPMD 发出了 2524 条警告。
我想我有一个简单而不优雅的方法来重新插入 PHPMD,但是在做 that 之前,我想知道是否有人有这个特定的 problem/need,并且s/he 是否设法以某种方式解决了它。
好的,就这样吧。
我有一个可以从命令行运行的 PHPMD 二进制文件。我计划做的是 将其输出注入到 CodeSniffer 插件的输出中 ,"enrich" 后者带有 PHPMD 消息。
为此,我破坏了 plugins/org.ppsrc.eclipse.pti.tools.codesniffer_.../php/tools
目录中的 phpcs.php
。
(因为我对 CodeSniffer 的另一个问题是它会经常重新扫描它应该知道的文件,我决定给 CodeSniffer 一个记忆。
首先,我提取调用的最后一个参数,即正在分析的文件(标有+++的行是我的additions/changes):
// Optionally use PHP_Timer to print time/memory stats for the run.
// Note that the reports are the ones who actually print the data
// as they decide if it is ok to print this data to screen.
@include_once 'PHP/Timer.php';
if (class_exists('PHP_Timer', false) === true) {
PHP_Timer::start();
}
if (is_file(dirname(__FILE__).'/../CodeSniffer/CLI.php') === true) {
include_once dirname(__FILE__).'/../CodeSniffer/CLI.php';
} else {
include_once 'PHP/CodeSniffer/CLI.php';
}
+++ $lastArgument = array_pop($_SERVER['argv']);
然后我添加一些 CS 似乎没有通过的标志,但我需要,例如忽略一些目录:
+++ $_SERVER['argv'][] = '--ignore=tests,vendor,cache';
+++ $_SERVER['argv'][] = $lastArgument;
然后 CS 调用继续进行,但现在我将其结果保存到缓冲区,而不是直接将它们发送到 Eclipse。
$phpcs = new PHP_CodeSniffer_CLI();
$phpcs->checkRequirements();
+++ ob_start();
$numErrors = $phpcs->process();
+++ $dom = new DOMDocument();
+++ $dom->loadXML(ob_get_clean());
+++ $cs = $dom->getElementsByTagName('phpcs')->item(0);
+++ $xpath = new DOMXPath($dom);
现在我已经准备好 PHPCS 输出 XML。
剩下的就是使用它自己的语法调用 PHPMD。
// Add PHPMD.
$mdCmd = "C:/PHP/composer/vendor/phpmd/phpmd/src/bin/phpmd \"{$lastArgument}\" xml \"C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/codesize.xml,C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/naming.xml,C:/Program Files/eclipse/plugins/org.phpsrc.eclipse.pti.library.pear_1.2.2.R20120127000000/php/library/PEAR/data/PHP_PMD/resources/rulesets/unusedcode.xml\"";
...并将其加载到另一个 XML:
fprintf(STDERR, $mdCmd . "\n");
$dompmd = new DOMDocument();
$dompmd->loadXML($mdxml = shell_exec($mdCmd));
现在我从 PMD 对象中取出所有错误,并将其添加到 CS 对象中:
$files = $dompmd->getElementsByTagName('file');
foreach ($files as $file) {
$name = $file->getAttribute('name');
$list = $xpath->query("//file[@name=\"{$name}\"]");
if (null === $list) {
continue;
}
$csFile = $list->item(0);
if (null === $csFile) {
// No errors from CS.
$csFile = $dom->createElement('file');
$csFile->setAttribute('name', $name);
$csFile->setAttribute('errors', 0);
$csFile->setAttribute('warnings', 0);
$csFile->setAttribute('fixable', 0);
$cs->appendChild($csFile);
}
$errs = 0;
foreach ($file->childNodes as $violation) {
if ($violation instanceof \DOMText) {
continue;
}
$error = $dom->createElement('warning', trim($violation->nodeValue));
$error->setAttribute('line', $violation->getAttribute('beginline'));
$error->setAttribute('column', 1);
$error->setAttribute('source', 'PHPMD.' . $violation->getAttribute('ruleset'));
$error->setAttribute('severity', $violation->getAttribute('priority'));
$error->setAttribute('fixable', 0);
$csFile->appendChild($error);
$errs++;
}
$csFile->getAttributeNode('errors')->value += $errs;
}
最后,将数据传回Eclipse:
print $dom->saveXML();
exit($numErrors ? 1: 0);
缓存 CodeSniffer(还有 PHPMD)
由于我对 CodeSniffer 的另一个问题是它会经常重新扫描它应该知道的文件,我决定给 CodeSniffer 一个记忆。这很简单:我可以存储一个临时文件,其中包含已保存的 XML 和由原始文件名 和 的 MD5 构建的名称其内容:
/tmp/68ce1959ef67bcc94e05084e2e20462a.76e55e72f32156a20a183de82fe0b3b6.xml
所以当PHPCS被要求分析/path/to/file/so-and-so.php
时,它会:
- 创建文件名的 MD5。
- 创建其内容的 MD5。
- 如果/tmp/md5file.md5content.xml不存在:
- 删除任何 /tmp/md5file.*.xml 有:它们已过时。
- 运行 代码如上。
- 将结果保存到 /tmp/md5file.md5content.xml
- 输出/tmp/md5file.md5content.xml. 的内容