sonar-php

repository·master·Indexed 19 days ago

https://github.com/sonarsource/sonar-php

A static code analyzer for PHP designed as an extension for the SonarQube platform. It provides code quality and security analysis, featuring 200+ built-in rules, support for PHP up to version 8.4, and the ability to implement custom rules. The project includes a Control Flow Graph (CFG) for modeling execution paths and an integration testing infrastructure to validate the scanner and its plugins.

Tokens
3.3K
Snippets
12
Records
21
Agent score
65%

What's inside sonar-php

  1. Overview of sonar-php

    master

    sonar-php is a static code analyzer for the PHP language designed as an extension for the SonarQube platform. It helps developers identify and correct bugs, vulnerabilities, and code smells.

    Key Features:

    • 200+ built-in rules.
    • Support for PHP up to version 8.4.
    • Calculation of metrics such as complexity and lines of code.
    • Import of unit test and coverage results.
    • Support for custom rules.
  2. How the CFG models Try-Catch-Finally blocks

    master

    The CFG construction for try-catch-finally uses several simplifying assumptions:

    1. Try Body: The try body is generated as a single block. Both catch and finally blocks are successors of this block. Note: While the CFG treats the body as one block, analysis must account for the fact that any statement within the body could trigger an exception and exit the block early.
    2. Missing Finally: If no finally clause is present, the analyzer assumes an empty finally clause exists to maintain graph consistency.
    3. Exception Handling: The analyzer assumes any catch clause might fail to handle an exception (even if it catches hrowable or aseException). Consequently, there is always a path from a catch block to the END block via any enclosed finally blocks.
  3. How the CFG models loops and switch statements

    master

    For Loops

    The for statement is decomposed into four distinct blocks:

    1. Initialization: Variable initialization before the body.
    2. Body: The code inside the loop.
    3. Update: The increment/decrement step following the body.
    4. Condition: The check performed before each iteration.

    Switch Statements

    switch statements are modeled as a series of if-elseif-else blocks.

    Note on PHP behavior: The CFG accounts for the PHP-specific behavior where a continue statement inside a switch block behaves identically to a break statement.

  4. Understand the Control Flow Graph (CFG) in SonarPHP

    master

    The SonarPHP analyzer uses a Control Flow Graph (CFG) to model the execution paths of PHP code. A CFG is a graph composed of basic blocks. A block is a sequence of statements executed linearly without branching.

    Block Attributes

    • successors: The set of blocks executed after the current block. The special END block has zero successors.
    • predecessors: The set of blocks executed before the current block.
    • syntactic successor: An imaginary successor used for blocks ending in unconditional jumps (break, continue, return, goto, throw). It represents the "normal" successor if the jump were omitted. It is null for non-jump blocks.
    • elements: A list of AST nodes (statements) executed sequentially within the block. If a jump (return, break, etc.) is present, it is the last element in the list.

    Branching Blocks

    Statements like if, while, for, foreach, and do-while generate Branching Blocks. These blocks have:

    • Exactly two successors (representing the true and false branches).
    • A branching tree attribute containing the AST node of the condition causing the branch.
  5. Implement custom PHP rules

    master

    You can implement custom rules to address specific needs in your codebase.

    Important API Changes for Custom Rules:

    • Version 3.32+: An additional newIssue endpoint is available in the CheckContext API interface.
    • Version 3.11+ (PHP 8 support):
      • Use ParameterTree#declaredType() instead of ParameterTree#type().
      • Use ReturnTypeClauseTree#declaredType() instead of ReturnTypeClauseTree#type().
      • Use ClassPropertyDeclarationTree#declaredType() instead of ClassPropertyDeclarationTree#typeAnnotation().
      • Use FunctionCallTree#callArguments() instead of FunctionCallTree#arguments().
      • Use AnonymousClassTree#callArguments() instead of AnonymousClassTree#arguments().
      • CatchBlockTree#variable() can now return NULL.
      • New tree types introduced: CallArgumentTree (wraps expressions passed as arguments), ThrowExpressionTree, and MatchExpressionTree.
      • ParameterTree now includes a visibility method.
  6. First time configuration for Integration Tests

    master

    Before running integration tests for the first time, follow these steps:

    1. Ensure your Developer Box is properly set up.
    2. Configure Orchestrator settings. The only mandatory options are the Artifactory API key and the GitHub token. The GitHub token must be a Personal Access Token (classic) with the repo scope permission and properly configured SSO.
    3. Build the test resources (such as custom plugins) by running the build command from the repository root.
    ./gradlew :its:build
  7. Build and test sonar-php locally

    master

    To build the project and run unit tests, ensure you have configured the build dependencies first.

    1. Configure build dependencies:

    git submodule update --init -- build-logic/common

    Tip: To always get the latest build logic during git operations, run git config submodule.recurse true.

    2. Build the project and run unit tests: Execute this from the project root:

    ./gradlew build
  8. Debug the Scanner in Integration Tests

    master

    You can debug the Scanner during an integration test by setting the SONAR_SCANNER_DEBUG_OPTS environment variable. This configuration instructs the scanner to wait for a remote debug session to start at the specified port.

    SonarScanner scanner = SonarScanner.create("projectDir")
        .setEnvironmentVariable("SONAR_SCANNER_DEBUG_OPTS", "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=<port>");
     
    ORCHESTRATOR.executeBuild(scanner);
  9. Fix code formatting issues

    master

    The project uses spotless for formatting checks during the Gradle build. If your build fails due to formatting, you can check the status manually or apply fixes automatically.

    Check formatting status:

    ./gradlew spotlessCheck

    Apply formatting fixes:

    ./gradlew spotlessApply
  10. Build the Custom Rules Plugin with Maven

    master

    To use Maven instead of Gradle, you must replace the build.gradle.kts file with the maven/pom.xml file. Note that when using Maven, you must manually build the plugin dependency by publishing it to your local Maven repository first.

    # First, publish the dependency to your local Maven repository
    ./gradlew publishToMavenLocal
    
    # Then proceed with your Maven build using maven/pom.xml
  11. Generate PHPUnit test and coverage reports

    master

    To provide the SonarPHP analyzer with the necessary data for processing, you must generate both a JUnit log and a Clover coverage report using PHPUnit. This requires Xdebug to be enabled in coverage mode.

    Requirements

    • PHP 8.*
    • Composer 2.*
    • Xdebug

    Steps to generate reports

    1. Enable Xdebug coverage mode: Set the XDEBUG_MODE environment variable to coverage.
    2. Run PHPUnit: Execute the test suite and specify the output paths for the Clover coverage and JUnit log files.

    Note: The analyzer expects these reports to contain absolute paths.

    # 1. Enable coverage generation
    export XDEBUG_MODE=coverage
    
    # 2. Run PHPUnit to create reports
    vendor/bin/phpunit tests \
    --coverage-clover=reports/phpunit.coverage.local.xml \
    --log-junit=reports/phpunit.tests.local.xml