Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
engines:
fixme:
enabled: true
enabled: false
phpmd:
enabled: true
phpcodesniffer:
enabled: true
config:
- file_extensions: "php"
- standard: ["PSR1", "PSR2"]

exclude_paths:
- .git/**/*
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ WORKDIR /usr/src/app
COPY composer.json /usr/src/app/
COPY composer.lock /usr/src/app/

RUN apk --update add git php-common php-xml php-dom php-ctype php-iconv php-json php-phar php-openssl curl && \
RUN apk --update add git php-common php-xml php-dom php-ctype php-iconv php-json php-pcntl php-phar php-openssl php-opcache php-sockets curl && \
curl -sS https://getcomposer.org/installer | php && \
/usr/src/app/composer.phar install && \
apk del build-base && rm -fr /usr/share/ri
Expand Down
46 changes: 23 additions & 23 deletions JSONRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,33 +49,33 @@ public function renderReport(Report $report)
$writer = $this->getWriter();

foreach ($report->getRuleViolations() as $violation) {
$rule = $violation->getRule();
$checkName = preg_replace("/^PHPMD\/Rule\//", "", str_replace("\\", "/", get_class($rule)));
$rule = $violation->getRule();
$checkName = preg_replace("/^PHPMD\/Rule\//", "", str_replace("\\", "/", get_class($rule)));

$path = preg_replace("/^\/code\//", "", $violation->getFileName());
$category = $this->ruleCategories[$checkName];
$path = preg_replace("/^\/code\//", "", $violation->getFileName());
$category = $this->ruleCategories[$checkName];

if ($category == null) {
$category = "Style";
}
if ($category == null) {
$category = "Style";
}

$issue = array(
"type" => "issue",
"check_name" => $checkName,
"description" => $violation->getDescription(),
"categories" => array($category),
"location" => array(
"path" => $path,
"lines" => array(
"begin" => $violation->getBeginLine(),
"end" => $violation->getEndLine()
)
)
);
$issue = array(
"type" => "issue",
"check_name" => $checkName,
"description" => $violation->getDescription(),
"categories" => array($category),
"location" => array(
"path" => $path,
"lines" => array(
"begin" => $violation->getBeginLine(),
"end" => $violation->getEndLine()
)
)
);

$json = json_encode($issue, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE);
$writer->write($json);
$writer->write(chr(0));
$json = json_encode($issue, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE);
$writer->write($json);
$writer->write(chr(0));
}
}
}
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
# Code Climate PHP Mess Detector (PHPMD) Engine

`codeclimate-phpmd` is a Code Climate Engine that wraps the PHP Mess
Detector (PHPMD) static analysis tool.
`codeclimate-phpmd` is a Code Climate Engine that wraps the PHP Mess Detector (PHPMD) static analysis tool.

### Installation

1. If you haven't already, [install the Code Climate CLI](https://github.com/codeclimate/codeclimate).
2. Run `codeclimate engines:enable phpmd`. This command both installs the engine and enables it in your `.codeclimate.yml` file.
3. You're ready to analyze! Browse into your project's folder and run `codeclimate analyze`.

###Config Options

Format the values for these config options per the [PHPMD documentation](http://phpmd.org/documentation/index.html).

* file_extensions - This is where you can configure the file extensions for the files that you want PHPMD to analyze.
* rulesets - This is the list of rulesets that you want PHPMD to use while analyzing your files.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some references to PHP_CodeSniffer here that probably shouldn't be here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good old copy and paste, good catch, will fix.


###Sample Config

exclude_paths:
- "/examples/**/*"
engines:
phpmd:
enabled: true
config:
file_extensions: "php"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably set up the file_extensions key so that it can work with a single value like this, or an array:

file_extensions:
  - php
  - php5

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe I just copied this from the codeclimate-phpcodesniffer repo. Maybe we should double check and then address it separately?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, separate sounds good!

rulesets: "unusedcode"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, my gut is we should allow either a single ruleset like this (ruleset: unusedcode) as a one-liner, or:

rulesets:
  - unusedcode
  - design
  - naming

The engine can implode the array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe I just copied this from the codeclimate-phpcodesniffer repo. Maybe we should double check and then address it separately?

ratings:
paths:
- "**.php"

### Need help?

For help with PHPMD, [check out their documentation](http://phpmd.org/documentation/index.html).

If you're running into a Code Climate issue, first look over this project's [GitHub Issues](https://github.com/phpmd/phpmd/issues), as your question may have already been covered. If not, [go ahead and open a support ticket with us](https://codeclimate.com/help).
73 changes: 73 additions & 0 deletions Runner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace CodeClimate\PHPMD;

use PHPMD\PHPMD;
use PHPMD\RuleSetFactory;
use PHPMD\Writer\StreamWriter;
use PHPMD\Renderer\JSONRenderer;

class Runner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice extraction here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

{
private $config;
private $server;

public function __construct($config, $server)
{
$this->config = $config;
$this->server = $server;
}

public function queueDirectory($dir, $prefix = '')
{
$dir = rtrim($dir, '\\/');

foreach (scandir($dir) as $f) {
if (in_array("$prefix$f", $this->config["exclude_paths"])) {
continue;
}

if ($f !== '.' and $f !== '..') {
if (is_dir("$dir/$f")) {
$this->queueDirectory("$dir/$f", "$prefix$f/");
continue;
}

$this->server->addwork(array("/code/$prefix$f"));
}
}

$this->server->process_work(false);
}

public function run($files)
{
$resultFile = tempnam(sys_get_temp_dir(), 'phpmd');

$renderer = new JSONRenderer();
$renderer->setWriter(new StreamWriter($resultFile));

$ruleSetFactory = new RuleSetFactory();

$phpmd = new PHPMD();

if (isset($this->config['config']['file_extensions'])) {
$phpmd->setFileExtensions(explode(',', $this->config['config']['file_extensions']));
}

$rulesets = "cleancode,codesize,controversial,design,naming,unusedcode";

if (isset($this->config['config']['rulesets'])) {
$rulesets = $this->config['config']['rulesets'];
}

$phpmd->processFiles(
implode(",", $files),
$rulesets,
array($renderer),
$ruleSetFactory
);

return $resultFile;
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"require": {
"phpmd/phpmd": "2.2.3"
"phpmd/phpmd": "2.2.3",
"barracudanetworks/forkdaemon-php": "1.0.*"
}
}
40 changes: 38 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 18 additions & 48 deletions engine.php
Original file line number Diff line number Diff line change
@@ -1,70 +1,40 @@
<?php

declare(ticks=1);

namespace PHPMD\TextUI;

use CodeClimate\PHPMD\Runner;

error_reporting(E_ERROR | E_PARSE | E_NOTICE);
date_default_timezone_set('UTC');
ini_set('memory_limit', -1);

require_once __DIR__.'/vendor/autoload.php';
require_once "JSONRenderer.php";
require_once "Runner.php";

use PHPMD\PHPMD;
use PHPMD\RuleSetFactory;
use PHPMD\Writer\StreamWriter;
use PHPMD\Renderer\JSONRenderer;

// obtain the config
$config = json_decode(file_get_contents('/config.json'), true);

$renderer = new JSONRenderer();
$renderer->setWriter(new StreamWriter(STDOUT));

$ruleSetFactory = new RuleSetFactory();

$all_files = scandir_recursive("/code");

if ($config["exclude_paths"]) {
$files = array();

foreach ($all_files as $file) {
if (!in_array($file, $config["exclude_paths"])) {
$files[] = "/code/".$file;
}
}
} else {
foreach ($all_files as $file) {
if (!in_array($file, $ignorePatterns)) {
$files[] = "/code/".$file;
}
}
}

$phpmd = new PHPMD();

// if ($extensions !== null) {
// $phpmd->setFileExtensions(explode(',', $extensions));
// }

$phpmd->processFiles(
implode(",", $files),
"cleancode,codesize,controversial,design,naming,unusedcode",
array($renderer),
$ruleSetFactory
);
// setup forking daemon
$server = new \fork_daemon();
$server->max_children_set(3);
$server->max_work_per_child_set(50);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please help me understand what these two values do? (20 and 50)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, max_children_set with the value of 20 means that the script with create up to 20 forks / processes, each processing up to 50 pieces of work (max_work_per_child_set). The pieces of work in this case are the PHP files to scan.

$server->store_result_set(true);
$runner = new Runner($config, $server);
$server->register_child_run(array($runner, "run"));

function scandir_recursive($dir, $prefix = '') {
$dir = rtrim($dir, '\\/');
$result = array();
$runner->queueDirectory("/code");

foreach (scandir($dir) as $f) {
if ($f !== '.' and $f !== '..') {
if (is_dir("$dir/$f")) {
$result = array_merge($result, scandir_recursive("$dir/$f", "$prefix$f/"));
} else {
$result[] = $prefix.$f;
}
}
}
$server->process_work(true);

return $result;
foreach ($server->get_all_results() as $result_file) {
echo file_get_contents($result_file);
unlink($result_file);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do result files get stored? (We'll need to ensure it's within /tmp)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just saw the reference to sys_get_temp_dir, so this should be OK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You got it.

}