Custom PHP Rules

Hi All, we are trying to implement custom php rules as part of our development. Our goal is to build code to check for things like NullPointerExceptions, Dead/Commented code in the sample files and we came across a sample code related to our requirement in github (https://github.com/SonarSource/sonar-php/blob/master/php-checks/src/main/java/org/sonar/php/checks/CommentedOutCodeCheck.java).

However, there is no documentation/explanation on the internet of what certain methods and plugins do such as org.sonar.plugins.php.api.visitors.PhpIssue, org.sonar.plugins.php.api.visitors.PHPCheck, org.sonar.php.tree.visitors.LegacyIssue and context().newLineIssue() and new LegacyIssue().

Could anyone please provide some documentation that would explain the functionality of such plugins and methods.

Thanks,
Bhavna Somani

Hi,

Before writing custom rules, you should first check rules which are already available:

Which “plugins” are you talking about?
Did you read the documentation about writing custom rules for PHP?
Did you have a look at the example custom plugin?

Hi Pierre,

Thank you for the response. Yes, I’ve gone through the documentation you’ve mentioned in the comment. I actually wanted to get the list of methods and the details on what each method is used for in the class “PHPVisitorCheck” or the “PHPSubscriptionCheck”.
The list of methods that can be overridden for “PHPVisitorCheck” is mentioned in the below link but there is no explanation of what each method does.

Also, there is no details of methods used in “plugins” like “org.sonar.plugins.php.api.visitors.VisitorCheck”.

Is there any documentation for the above methods?

Sorry, there’s no documentation for the methods you mention.
Have a look at CheckContext and at the interfaces defined in org.sonar.plugins.php.api.tree, for example NamespaceNameTree.
Apart from that, I believe that you already found all the documentation which exists.

Thanks for the update!
Also is there any way to traverse the sample code line-by-line?
I’ve tried the visitBlock and visitScript (using the Statement Tree list), but none of them are able to traverse the code one line at a time.

If you only need to go though the code line by line, it means you don’t need an AST. The custom rule API provided for PHP is overkill in such a case.
If you still want to use it, you can get a String representing the content of the current PHP file with something like:

public class MyCheck extends PHPVisitorCheck {
  @Override
  public void visitCompilationUnit(CompilationUnitTree tree) {
    String fileContent = context().getPhpFile().contents();
    ...
  }
}

Hi Pierre,

My requirement is to search a string ( example “ABC”) in the sample file and raise an issue only if it is NOT enclosed within a try-catch block. For this, I’ve used the visitScript(ScriptTree tree) method and created a List stTree = tree.statements(). Then I’m traversing through each item in the list and matching the required pattern using regex.
This method is working fine and returning the correct line number of the non-compliant code in a sample code like:

<?php try{ $x = 4; $result = "ABC"; } catch(Exception $e){ $result = 2; } try{ $result1 = 1; } catch(Exception $e){ $result1 = 2; } $result = "ABC"; // NOK {{"ABC" must be placed inside a try-catch block.}} ?>

where “ABC” is present as an independent statement.

However, in another code like:

<?php function calcFactorial($num) // This function will calculate the factorial of variable passed in arguement. { try { if (is_null($num)) { throw new Exception('NULL Value Exception.'); } else { $result = 1; for($i = 1; $i<=$num;$i++) { $result = $result * $i; } echo "
Factorial is ".$result; } } catch(Exception $e) { echo '
Caught exception: ', $e->getMessage(); } $result = "ABC"; // Noncompliant {{"ABC" must be placed inside a try-catch block.}} } $x = 4; $y; calcFactorial($x); // Will not throw error as $x has a valid value assigned. calcFactorial($y); // Will throw error as $y has no value assigned. ?>

where “ABC” is within a function (here “calcFactorial”), it is returning the line number where the function is ending because the tree.statements() is considering the entire function as a single statement i.e., 1 list element.
My requirement is to return the exact line number where the code is non-compliant.
Is there any way to achieve this?

Thanks,
Bhavna

Can you please share the code of your custom rule?

I don’t get why you use a regular expression.

I suppose that you could override visitLiteral(LiteralTree tree) and check its value with LiteralTree.value() and recursively check its parent with Tree.getParent() to see if it’s in a try block.

Hi Pierre,

Thank you for your update.
I have shared the rule code (roql_rule_code.txt) and a sample code (roql_sample_code.txt) as well for your reference.
Actually, I have to search for the word ‘ROQL’, not string “ROQL” in my sample code. Apologies for the error I made on the previous post.
I did try to override visitLiteral(Literal tree) method as you mentioned in above comment, but as it only takes the literal values in the tree, it isn’t useful in my case.

Could you help out with this?

Thanks,
Bhavna

roql_rule_code.txt (2.9 KB)
roql_sample_code.txt (1.2 KB)

Try overriding visitVariableIdentifier or visitNameIdentifier.

Hi Pierre,

Thank you for the suggestion. The visitNameIdentifier method was helpful.

Hi Pierre,

I’m getting below error when running my custom rule in SonarQube:
java.lang.IncompatibleClassChangeError: Found interface org.sonar.plugins.php.api.symbols.Symbol, but class was expected
at org.sonar.samples.php.checks.NullValueCheckRule.checkParameters(NullValueCheckRule.java:93)

Could you help identify the reason for the same?
PFA the rule logic.[nullValueRule_30March.txt

I guess that you compiled your custom plugin against one version of sonar-php-plugin.jar and you deployed it to a SonarQube server which has another version of sonar-php-plugin.jar in its extensions/plugins directory. You wouldn’t get such an error if they were the same version.

Thanks for the update. Yes, I checked the sonar-php-plugin.jar version of my compiled custom plugin is different than the SonarQube server version of sonar-php-plugin.jar.
However, when I tried to update my pom.xml file to reflect the same version of sonar-php-plugin.jar as is in the SonarQube server, I’m getting the below error:

Missing artifact org.sonarsource.php:sonar-php-plugin:jar:3.3.0.5166

Can you tell me what am I missing here?

I’ve also attached my pom.xml file and the screenshot of the error.
php-version-error
pom.txt (2.1 KB)

Thanks & Regards,
Bhavna

It seems that, for some reason, version 3.3.0.5166 of sonar-php-plugin was not deployed to maven central repository. We’re going to have a look at that.
In the meantime, I think you should be able to use version 3.2.0.4868.