Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ PHP plugin ] refactoring and corrections #82

Merged
merged 6 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
92 changes: 48 additions & 44 deletions php-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,24 @@
<artifactId>sonar-plugin-api-impl</artifactId>
</dependency>

<dependency>
<groupId>org.sonarsource.analyzer-commons</groupId>
<artifactId>sonar-analyzer-commons</artifactId>
</dependency>

<!-- for security on regex patterns : https://github.com/google/re2j -->
<dependency>
<groupId>com.google.re2j</groupId>
<artifactId>re2j</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.google.re2j</groupId>-->
<!-- <artifactId>re2j</artifactId>-->
<!-- </dependency>-->

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand All @@ -64,43 +65,46 @@
<sonarQubeMinVersion>${sonarqube.version}</sonarQubeMinVersion>
<basePlugin>php</basePlugin>
<jreMinVersion>${java.version}</jreMinVersion>
<requirePlugins>php:${sonarphp.version}</requirePlugins>
<sonarQubeMinVersion>${sonarqube.version}</sonarQubeMinVersion>
<jreMinVersion>${java.version}</jreMinVersion>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>commons-*:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.*:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
<exclude>org/sonar/api/batch/sensor/**</exclude>
</excludes>
</filter>
<filter>
<artifact>com.*:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-shade-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>package</phase>-->
<!-- <goals>-->
<!-- <goal>shade</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <filters>-->
<!-- <filter>-->
<!-- <artifact>commons-*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- <filter>-->
<!-- <artifact>org.*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- <exclude>org/sonar/api/batch/sensor/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- <filter>-->
<!-- <artifact>com.*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- </filters>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ public class PHPPlugin implements Plugin {

@Override
public void define(Context context) {
context.addExtension(PhpRulesDefinition.class);
context.addExtension(PhpRuleRepository.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2012-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package fr.greencodeinitiative.php;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableList;
import fr.greencodeinitiative.php.checks.AvoidDoubleQuoteCheck;
import fr.greencodeinitiative.php.checks.AvoidFullSQLRequestCheck;
import fr.greencodeinitiative.php.checks.AvoidSQLRequestInLoopCheck;
import fr.greencodeinitiative.php.checks.AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements;
import fr.greencodeinitiative.php.checks.AvoidUsingGlobalVariablesCheck;
import fr.greencodeinitiative.php.checks.IncrementCheck;
import fr.greencodeinitiative.php.checks.NoFunctionCallWhenDeclaringForLoop;
import fr.greencodeinitiative.php.checks.UseOfMethodsForBasicOperations;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader;
import org.sonar.plugins.php.api.visitors.PHPCustomRuleRepository;

public class PhpRuleRepository implements RulesDefinition, PHPCustomRuleRepository {

public static final String LANGUAGE = "php";
public static final String NAME = "Green Code Initiative";
public static final String RESOURCE_BASE_PATH = "/fr/greencodeinitiative/l10n/php/rules/custom/";
public static final String REPOSITORY_KEY = "gci-php";

@Override
public void define(Context context) {
NewRepository repository = context.createRepository(repositoryKey(), LANGUAGE).setName(NAME);

new RulesDefinitionAnnotationLoader().load(repository, checkClasses().toArray(new Class[] {}));

// technical debt
Map<String, String> remediationCosts = new HashMap<>();
remediationCosts.put(AvoidSQLRequestInLoopCheck.RULE_KEY, "10min");
remediationCosts.put(AvoidFullSQLRequestCheck.RULE_KEY, "20min");
Copy link
Member

Choose a reason for hiding this comment

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

We will have to specify here if the remediation cost of a rule is different from 5 minutes? Maybe create a more modular and readable list outside the algorithm

Copy link
Member Author

@dedece35 dedece35 Mar 17, 2023

Choose a reason for hiding this comment

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

@utarwyn,
yes you are right.
indeed, I spend a lot of time to analyse why python and php plugin didn't work since upgrade libraries version in december 2022.
I wanted find a quick solution which turn on again these two plugins for the hackathon in order to have all plugins working for developers.
But the real solution, for me, must be found after the hackathon with more time to keep a solution like done in java plugin. For example, rollback JSON files which describe this kind of information.
I created an issue for that : #80

I'm waiting for @jhertout review on next monday, to merge this branch and to have all plugins working.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for the detailed answer, I understand that we have to make the plugin work for the hackaton. My comments are only open remarks and proposals, even to be applied later. I don't have yet the hindsight to validate this code, I let Julien look at it 😉

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @dedece35,

I agree with @utarwyn but as you say for the moment the important thing is to have the project to work for the hackaton. I suppose it is for the same reason you leave comments in the pom files?

The review is OK for me. I tested with the php test project and I have no problems.

Some minor things you may want to fix here (or in an other PR):

  • the rule "Prefer local variables to globals" html is in French and not well formatted
  • the rule "Use of methods for basic operations" has a compliant solution commented with // NOK

Copy link
Member Author

@dedece35 dedece35 Mar 23, 2023

Choose a reason for hiding this comment

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

Hi @jhertout
thank you for feedback.
Problems corrected on rule "Prefer local variables to globals". I corrected also the exact same problem on java plugin.
Capture d’écran 2023-03-23 à 23 20 23

On the other hand, I don't find / understand your problem on rule "Use of methods for basic operations".
See my capture : 2 lines with the problem and with "NOK" on each.
Capture d’écran 2023-03-23 à 22 55 32
If there was a problem, unit tests wouldn't pass. No ?

I merge this PR.
We can correct your second point on other PR.

repository.rules().forEach(rule -> {
String debt = remediationCosts.get(rule.key());

// TODO DDC : create support to use org.apache.commons.lang.StringUtils
// if (StringUtils.isBlank(debt)) {
if (debt == null || debt.trim().equals("")) {
// default debt to 5min for issue correction
rule.setDebtRemediationFunction(
rule.debtRemediationFunctions().constantPerIssue("5min"));
} else {
rule.setDebtRemediationFunction(
rule.debtRemediationFunctions().constantPerIssue(debt));
}
});

// HTML description
repository.rules().forEach(rule ->
rule.setHtmlDescription(loadResource(RESOURCE_BASE_PATH + rule.key() + ".html")));

repository.done();
}

@Override
public String repositoryKey() {
return REPOSITORY_KEY;
}

@Override
public List<Class<?>> checkClasses() {
return ImmutableList.of(
utarwyn marked this conversation as resolved.
Show resolved Hide resolved
AvoidDoubleQuoteCheck.class,
AvoidFullSQLRequestCheck.class,
AvoidSQLRequestInLoopCheck.class,
AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements.class,
AvoidUsingGlobalVariablesCheck.class,
IncrementCheck.class,
NoFunctionCallWhenDeclaringForLoop.class,
UseOfMethodsForBasicOperations.class
);
}

private String loadResource(String path) {
URL resource = getClass().getResource(path);
if (resource == null) {
throw new IllegalStateException("Resource not found: " + path);
}
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (InputStream in = resource.openStream()) {
byte[] buffer = new byte[1024];
for (int len = in.read(buffer); len != -1; len = in.read(buffer)) {
result.write(buffer, 0, len);
}
return new String(result.toByteArray(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Failed to read resource: " + path, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
import org.sonar.plugins.php.api.visitors.PHPSubscriptionCheck;

@Rule(
key = "S66",
name = "Developpement",
key = AvoidDoubleQuoteCheck.RULE_KEY,
name = AvoidDoubleQuoteCheck.ERROR_MESSAGE,
description = AvoidDoubleQuoteCheck.ERROR_MESSAGE,
priority = Priority.MINOR,
tags = {"bug"})

tags = {"bug", "eco-design"})
public class AvoidDoubleQuoteCheck extends PHPSubscriptionCheck {

public static final String RULE_KEY = "S66";
public static final String ERROR_MESSAGE = "Avoid using double quote (\"), prefer using simple quote (')";
private static final Map<String, Collection<Integer>> linesWithIssuesByFile = new HashMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

import com.google.re2j.Pattern;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.plugins.php.api.tree.Tree;
Expand All @@ -12,15 +12,15 @@
import org.sonar.plugins.php.api.visitors.PHPSubscriptionCheck;

@Rule(
key = "S74",
name = "Developpement",
key = AvoidFullSQLRequestCheck.RULE_KEY,
name = AvoidFullSQLRequestCheck.ERROR_MESSAGE,
description = AvoidFullSQLRequestCheck.ERROR_MESSAGE,
priority = Priority.MINOR,
tags = {"bug"}
)

tags = {"bug", "eco-design"})
public class AvoidFullSQLRequestCheck extends PHPSubscriptionCheck {

public static final String RULE_KEY = "S74";

public static final String ERROR_MESSAGE = "Don't use the query SELECT * FROM";

private static final Pattern PATTERN = Pattern.compile("(?i).*select.*\\*.*from.*");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

import com.google.re2j.Pattern;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.plugins.php.api.tree.Tree;
Expand All @@ -18,13 +18,14 @@
import org.sonar.plugins.php.api.visitors.PHPSubscriptionCheck;

@Rule(
key = "S72",
name = "Developpement", description = AvoidSQLRequestInLoopCheck.ERROR_MESSAGE,
key = AvoidSQLRequestInLoopCheck.RULE_KEY,
name = AvoidSQLRequestInLoopCheck.ERROR_MESSAGE,
description = AvoidSQLRequestInLoopCheck.ERROR_MESSAGE,
priority = Priority.MINOR,
tags = {"bug"}
)
tags = {"bug", "eco-design"})
public class AvoidSQLRequestInLoopCheck extends PHPSubscriptionCheck {

public static final String RULE_KEY = "S72";
public static final String ERROR_MESSAGE = "Avoid SQL request in loop";
private static final Pattern PATTERN = Pattern.compile("(mysql(i::|_)query\\s*\\(.*)|(oci_execute\\(.*)");

Expand All @@ -35,14 +36,26 @@ public List<Tree.Kind> nodesToVisit() {

@Override
public void visitNode(Tree tree) {
if (tree.is(Kind.FOR_STATEMENT))
visitBlockNode((BlockTree) ((ForStatementTree) tree).statements().get(0));
if (tree.is(Kind.FOR_STATEMENT)) {
StatementTree stTree = ((ForStatementTree) tree).statements().get(0);
if (stTree.is(Kind.BLOCK)) {
visitBlockNode((BlockTree) stTree);
}
}

if (tree.is(Kind.FOREACH_STATEMENT))
visitBlockNode((BlockTree) ((ForEachStatementTree) tree).statements().get(0));
if (tree.is(Kind.FOREACH_STATEMENT)) {
StatementTree stTree = ((ForEachStatementTree) tree).statements().get(0);
if (stTree.is(Kind.BLOCK)) {
visitBlockNode((BlockTree) stTree);
}
}

if (tree.is(Kind.DO_WHILE_STATEMENT))
visitBlockNode((BlockTree) ((DoWhileStatementTree) tree).statement());
if (tree.is(Kind.DO_WHILE_STATEMENT)) {
StatementTree stTree = ((DoWhileStatementTree) tree).statement();
if (stTree.is(Kind.BLOCK)) {
visitBlockNode((BlockTree) stTree);
}
}
}

private void visitBlockNode(BlockTree block) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
import org.sonar.plugins.php.api.visitors.PHPSubscriptionCheck;

@Rule(
key = "S34",
name = "Developpement",
key = AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements.RULE_KEY,
name = AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements.ERROR_MESSAGE,
description = AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements.ERROR_MESSAGE,
priority = Priority.MINOR,
tags = {"bug"})
tags = {"bug", "eco-design"})
public class AvoidTryCatchFinallyCheck_NOK_failsAllTryStatements extends PHPSubscriptionCheck {

public static final String RULE_KEY = "S34";
public static final String ERROR_MESSAGE = "Avoid using try-catch-finally";

@Override
Expand Down
Loading