2013-05-03 17:02:53 +02:00
|
|
|
<?php
|
|
|
|
|
2016-07-19 20:28:11 +02:00
|
|
|
namespace PHPCensor\Plugin;
|
2013-05-03 17:02:53 +02:00
|
|
|
|
2016-07-19 20:28:11 +02:00
|
|
|
use PHPCensor;
|
|
|
|
use PHPCensor\Builder;
|
|
|
|
use PHPCensor\Model\Build;
|
2016-07-11 18:00:04 +02:00
|
|
|
use PHPCensor\Plugin;
|
2017-01-13 16:35:41 +01:00
|
|
|
use PHPCensor\ZeroConfigPluginInterface;
|
2013-10-10 02:01:06 +02:00
|
|
|
|
2013-05-16 03:30:48 +02:00
|
|
|
/**
|
2017-01-10 18:46:19 +01:00
|
|
|
* PHP Mess Detector Plugin - Allows PHP Mess Detector testing.
|
|
|
|
*
|
2017-03-04 16:39:56 +01:00
|
|
|
* @author Dan Cryer <dan@block8.co.uk>
|
2017-01-10 18:46:19 +01:00
|
|
|
*/
|
2017-01-13 16:35:41 +01:00
|
|
|
class PhpMessDetector extends Plugin implements ZeroConfigPluginInterface
|
2013-05-03 17:02:53 +02:00
|
|
|
{
|
2013-08-15 16:22:47 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $suffixes;
|
|
|
|
|
2013-09-18 10:54:23 +02:00
|
|
|
/**
|
|
|
|
* @var string, based on the assumption the root may not hold the code to be
|
2015-06-25 16:21:23 +02:00
|
|
|
* tested, extends the base path only if the provided path is relative. Absolute
|
2014-04-12 17:28:52 +02:00
|
|
|
* paths are used verbatim
|
2013-09-18 10:54:23 +02:00
|
|
|
*/
|
|
|
|
protected $path;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array - paths to ignore
|
|
|
|
*/
|
|
|
|
protected $ignore;
|
|
|
|
|
2013-08-15 16:22:47 +02:00
|
|
|
/**
|
|
|
|
* Array of PHPMD rules. Can be one of the builtins (codesize, unusedcode, naming, design, controversial)
|
2015-06-25 16:21:23 +02:00
|
|
|
* or a filename (detected by checking for a / in it), either absolute or relative to the project root.
|
2013-07-19 15:40:16 +02:00
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $rules;
|
2016-07-11 18:00:04 +02:00
|
|
|
protected $allowed_warnings;
|
2013-05-03 17:02:53 +02:00
|
|
|
|
2017-01-11 16:15:54 +01:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function pluginName()
|
|
|
|
{
|
|
|
|
return 'php_mess_detector';
|
|
|
|
}
|
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
2016-07-11 18:00:04 +02:00
|
|
|
* {@inheritdoc}
|
2014-12-08 12:25:33 +01:00
|
|
|
*/
|
2016-07-22 09:05:34 +02:00
|
|
|
public function __construct(Builder $builder, Build $build, array $options = [])
|
2013-05-16 16:50:19 +02:00
|
|
|
{
|
2016-07-22 09:05:34 +02:00
|
|
|
parent::__construct($builder, $build, $options);
|
2016-07-11 18:00:04 +02:00
|
|
|
|
|
|
|
$this->suffixes = ['php'];
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->ignore = $this->builder->ignore;
|
2016-07-11 18:00:04 +02:00
|
|
|
$this->path = '';
|
|
|
|
$this->rules = ['codesize', 'unusedcode', 'naming'];
|
2014-04-30 15:18:58 +02:00
|
|
|
$this->allowed_warnings = 0;
|
|
|
|
|
|
|
|
if (isset($options['zero_config']) && $options['zero_config']) {
|
|
|
|
$this->allowed_warnings = -1;
|
|
|
|
}
|
2013-10-10 02:01:06 +02:00
|
|
|
|
|
|
|
if (!empty($options['path'])) {
|
|
|
|
$this->path = $options['path'];
|
|
|
|
}
|
2013-08-15 16:22:47 +02:00
|
|
|
|
2014-04-25 11:17:39 +02:00
|
|
|
if (array_key_exists('allowed_warnings', $options)) {
|
|
|
|
$this->allowed_warnings = (int)$options['allowed_warnings'];
|
|
|
|
}
|
|
|
|
|
2016-04-21 06:58:09 +02:00
|
|
|
foreach (['rules', 'ignore', 'suffixes'] as $key) {
|
2013-10-10 02:01:06 +02:00
|
|
|
$this->overrideSetting($options, $key);
|
2013-08-15 16:22:47 +02:00
|
|
|
}
|
2016-07-11 18:00:04 +02:00
|
|
|
}
|
2016-07-10 10:05:10 +02:00
|
|
|
|
2016-07-11 18:00:04 +02:00
|
|
|
/**
|
|
|
|
* Check if this plugin can be executed.
|
|
|
|
* @param $stage
|
|
|
|
* @param Builder $builder
|
|
|
|
* @param Build $build
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function canExecute($stage, Builder $builder, Build $build)
|
|
|
|
{
|
2017-03-16 15:54:25 +01:00
|
|
|
if ($stage == Build::STAGE_TEST) {
|
2016-07-11 18:00:04 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-05-16 16:50:19 +02:00
|
|
|
}
|
2013-05-03 17:02:53 +02:00
|
|
|
|
2013-05-16 18:17:29 +02:00
|
|
|
/**
|
2013-08-15 16:22:47 +02:00
|
|
|
* Runs PHP Mess Detector in a specified directory.
|
|
|
|
*/
|
2013-05-16 16:50:19 +02:00
|
|
|
public function execute()
|
|
|
|
{
|
2014-05-11 17:59:54 +02:00
|
|
|
if (!$this->tryAndProcessRules()) {
|
2014-04-16 13:30:03 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-07-22 09:05:34 +02:00
|
|
|
$phpmdBinaryPath = $this->builder->findBinary('phpmd');
|
2013-10-08 09:50:10 +02:00
|
|
|
|
2014-05-11 17:59:54 +02:00
|
|
|
$this->executePhpMd($phpmdBinaryPath);
|
2014-04-25 13:28:27 +02:00
|
|
|
|
2016-07-22 09:05:34 +02:00
|
|
|
$errorCount = $this->processReport(trim($this->builder->getLastOutput()));
|
2014-05-11 17:59:54 +02:00
|
|
|
$this->build->storeMeta('phpmd-warnings', $errorCount);
|
2013-10-08 08:21:46 +02:00
|
|
|
|
2014-05-11 17:59:54 +02:00
|
|
|
return $this->wasLastExecSuccessful($errorCount);
|
2013-05-16 16:50:19 +02:00
|
|
|
}
|
2013-10-10 02:01:06 +02:00
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
|
|
|
* Override a default setting.
|
|
|
|
* @param $options
|
|
|
|
* @param $key
|
|
|
|
*/
|
2013-10-10 02:01:06 +02:00
|
|
|
protected function overrideSetting($options, $key)
|
|
|
|
{
|
2013-11-11 03:22:05 +01:00
|
|
|
if (isset($options[$key]) && is_array($options[$key])) {
|
2013-10-10 02:01:06 +02:00
|
|
|
$this->{$key} = $options[$key];
|
|
|
|
}
|
|
|
|
}
|
2014-04-25 13:28:27 +02:00
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
|
|
|
* Process PHPMD's XML output report.
|
2016-07-22 09:05:34 +02:00
|
|
|
*
|
2014-12-08 12:25:33 +01:00
|
|
|
* @param $xmlString
|
2016-07-22 09:05:34 +02:00
|
|
|
*
|
|
|
|
* @return integer
|
|
|
|
*
|
2014-12-08 12:25:33 +01:00
|
|
|
* @throws \Exception
|
|
|
|
*/
|
2014-05-22 12:59:26 +02:00
|
|
|
protected function processReport($xmlString)
|
2014-04-25 13:28:27 +02:00
|
|
|
{
|
2014-05-22 12:59:26 +02:00
|
|
|
$xml = simplexml_load_string($xmlString);
|
2014-04-25 13:28:27 +02:00
|
|
|
|
|
|
|
if ($xml === false) {
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder->log($xmlString);
|
2014-04-25 13:28:27 +02:00
|
|
|
throw new \Exception('Could not process PHPMD report XML.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$warnings = 0;
|
|
|
|
|
|
|
|
foreach ($xml->file as $file) {
|
|
|
|
$fileName = (string)$file['name'];
|
2016-07-22 09:05:34 +02:00
|
|
|
$fileName = str_replace($this->builder->buildPath, '', $fileName);
|
2014-04-25 13:28:27 +02:00
|
|
|
|
|
|
|
foreach ($file->violation as $violation) {
|
|
|
|
$warnings++;
|
|
|
|
|
2015-10-15 11:07:54 +02:00
|
|
|
$this->build->reportError(
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder,
|
2015-10-15 11:07:54 +02:00
|
|
|
'php_mess_detector',
|
|
|
|
(string)$violation,
|
2016-07-19 20:28:11 +02:00
|
|
|
PHPCensor\Model\BuildError::SEVERITY_HIGH,
|
2015-10-15 11:07:54 +02:00
|
|
|
$fileName,
|
|
|
|
(int)$violation['beginline'],
|
|
|
|
(int)$violation['endline']
|
|
|
|
);
|
2014-04-25 13:28:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-15 11:07:54 +02:00
|
|
|
return $warnings;
|
2014-04-25 13:28:27 +02:00
|
|
|
}
|
2014-05-11 17:59:54 +02:00
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
2016-07-22 09:05:34 +02:00
|
|
|
* Try and process the rules parameter from .php-censor.yml.
|
2014-12-08 12:25:33 +01:00
|
|
|
* @return bool
|
|
|
|
*/
|
2014-05-11 17:59:54 +02:00
|
|
|
protected function tryAndProcessRules()
|
|
|
|
{
|
|
|
|
if (!empty($this->rules) && !is_array($this->rules)) {
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder->logFailure('The "rules" option must be an array.');
|
2014-05-11 17:59:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->rules as &$rule) {
|
|
|
|
if (strpos($rule, '/') !== false) {
|
2016-07-22 09:05:34 +02:00
|
|
|
$rule = $this->builder->buildPath . $rule;
|
2014-05-11 17:59:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
|
|
|
* Execute PHP Mess Detector.
|
|
|
|
* @param $binaryPath
|
|
|
|
*/
|
2014-05-11 17:59:54 +02:00
|
|
|
protected function executePhpMd($binaryPath)
|
|
|
|
{
|
|
|
|
$cmd = $binaryPath . ' "%s" xml %s %s %s';
|
|
|
|
|
|
|
|
$path = $this->getTargetPath();
|
|
|
|
|
|
|
|
$ignore = '';
|
|
|
|
if (count($this->ignore)) {
|
|
|
|
$ignore = ' --exclude ' . implode(',', $this->ignore);
|
|
|
|
}
|
|
|
|
|
|
|
|
$suffixes = '';
|
|
|
|
if (count($this->suffixes)) {
|
|
|
|
$suffixes = ' --suffixes ' . implode(',', $this->suffixes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disable exec output logging, as we don't want the XML report in the log:
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder->logExecOutput(false);
|
2014-05-11 17:59:54 +02:00
|
|
|
|
|
|
|
// Run PHPMD:
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder->executeCommand(
|
2014-05-11 17:59:54 +02:00
|
|
|
$cmd,
|
|
|
|
$path,
|
|
|
|
implode(',', $this->rules),
|
|
|
|
$ignore,
|
|
|
|
$suffixes
|
|
|
|
);
|
|
|
|
|
|
|
|
// Re-enable exec output logging:
|
2016-07-22 09:05:34 +02:00
|
|
|
$this->builder->logExecOutput(true);
|
2014-05-11 17:59:54 +02:00
|
|
|
}
|
|
|
|
|
2014-12-08 12:25:33 +01:00
|
|
|
/**
|
|
|
|
* Get the path PHPMD should be run against.
|
|
|
|
* @return string
|
|
|
|
*/
|
2014-05-11 17:59:54 +02:00
|
|
|
protected function getTargetPath()
|
|
|
|
{
|
2016-07-22 09:05:34 +02:00
|
|
|
$path = $this->builder->buildPath . $this->path;
|
2014-05-11 17:59:54 +02:00
|
|
|
if (!empty($this->path) && $this->path{0} == '/') {
|
|
|
|
$path = $this->path;
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a boolean indicating if the error count can be considered a success.
|
|
|
|
*
|
|
|
|
* @param int $errorCount
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function wasLastExecSuccessful($errorCount)
|
|
|
|
{
|
|
|
|
$success = true;
|
|
|
|
|
|
|
|
if ($this->allowed_warnings != -1 && $errorCount > $this->allowed_warnings) {
|
|
|
|
$success = false;
|
|
|
|
return $success;
|
|
|
|
}
|
|
|
|
return $success;
|
|
|
|
}
|
2013-05-16 16:50:19 +02:00
|
|
|
}
|