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

Add SARIF support to all PHP linters #3515

Closed
llaville opened this issue Apr 27, 2024 · 15 comments · Fixed by #3551
Closed

Add SARIF support to all PHP linters #3515

llaville opened this issue Apr 27, 2024 · 15 comments · Fixed by #3551
Assignees
Labels
enhancement New feature or request

Comments

@llaville
Copy link
Collaborator

Describe the solution you'd like
Add SARIF output on PHP_PHPCS linter.
@nvuillam Ask for it in December 2021 on official PHP_CodeSniffer project (see squizlabs/PHP_CodeSniffer#3496)

Describe alternatives you've considered
Continue without SARIF support for PHP_PHPCS linter

Additional context
MegaLinter automation contains test files and reports : see https://github.com/oxsecurity/megalinter/tree/main/.automation/test/

E.g : With https://github.com/oxsecurity/megalinter/tree/main/.automation/test/php source files

Current version of PHP_CodeSniffer (3.9.2) gave following results on these previous source files :

--report=summary

PHP CODE SNIFFER REPORT SUMMARY
-----------------------------------------------------------------------------------------
FILE                                                                     ERRORS  WARNINGS
-----------------------------------------------------------------------------------------
/shared/backups/github/megalinter/.automation/test/php/php_bad_1.php     7       0
/shared/backups/github/megalinter/.automation/test/php/php_bad_2.php     4       1
-----------------------------------------------------------------------------------------
A TOTAL OF 11 ERRORS AND 1 WARNING WERE FOUND IN 2 FILES
-----------------------------------------------------------------------------------------
PHPCBF CAN FIX 11 OF THESE SNIFF VIOLATIONS AUTOMATICALLY
-----------------------------------------------------------------------------------------

Time: 66ms; Memory: 6MB

--report=source

PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
-------------------------------------------------------------------------------------------
    STANDARD  CATEGORY            SNIFF                                               COUNT
-------------------------------------------------------------------------------------------
[x] PSR12     Operators           Operator spacing no space before                    4
[x] PSR12     Operators           Operator spacing no space after                     3
[ ] PSR1      Files               Side effects found with symbols                     1
[x] PSR2      Methods             Function call signature space before open bracket   1
[x] Squiz     Control structures  Control signature newline after open brace          1
[x] Squiz     Functions           Multi line function declaration brace on same line  1
[x] Squiz     White space         Scope closing brace content before                  1
-------------------------------------------------------------------------------------------
A TOTAL OF 12 SNIFF VIOLATIONS WERE FOUND IN 7 SOURCES
-------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 6 MARKED SOURCES AUTOMATICALLY (11 VIOLATIONS IN TOTAL)
-------------------------------------------------------------------------------------------

Time: 75ms; Memory: 6MB

Following recommendation of @jrfnl (squizlabs/PHP_CodeSniffer#3496 (comment)), I've started to write a POC today, that is able to produce following SARIF output on same previous source files

--report-file=phpcs.sarif

With, e.g, such kind of config file .phpcs.xml.dist

<?xml version="1.0"?>
<ruleset
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="phpcs.xsd"
    name="MyStandard">
    <description>My custom coding standard.</description>

    <autoload>./vendor/autoload.php</autoload>

    <file>/shared/backups/github/megalinter/.automation/test/php/</file>

    <arg name="report" value="/shared/backups/bartlett/phpcs-sarif-report/src/Sarif.php" />

    <rule ref="PSR12">
        <exclude name="PSR12.Files.FileHeader" />
        <exclude name="PSR12.Files.OpenTag" />
        <exclude name="Squiz.Classes.ValidClassName" />
    </rule>
</ruleset>
{
    "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
    "version": "2.1.0",
    "runs": [
        {
            "tool": {
                "driver": {
                    "name": "PHP_CodeSniffer",
                    "semanticVersion": "3.9.2.0",
                    "informationUri": "https://github.com/squizlabs/PHP_CodeSniffer",
                    "rules": [
                        {
                            "id": "PSR12.Operators.OperatorSpacing.NoSpaceBefore",
                            "properties": {
                                "frequency": 4
                            }
                        },
                        {
                            "id": "PSR12.Operators.OperatorSpacing.NoSpaceAfter",
                            "properties": {
                                "frequency": 3
                            }
                        },
                        {
                            "id": "PSR1.Files.SideEffects.FoundWithSymbols",
                            "properties": {
                                "frequency": 1
                            }
                        },
                        {
                            "id": "Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine",
                            "properties": {
                                "frequency": 1
                            }
                        },
                        {
                            "id": "PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket",
                            "properties": {
                                "frequency": 1
                            }
                        },
                        {
                            "id": "Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace",
                            "properties": {
                                "frequency": 1
                            }
                        },
                        {
                            "id": "Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore",
                            "properties": {
                                "frequency": 1
                            }
                        }
                    ]
                }
            },
            "invocations": [
                {
                    "executionSuccessful": true,
                    "workingDirectory": {
                        "uri": "file:///shared/backups/bartlett/phpcs-sarif-report/"
                    },
                    "properties": {
                        "totals.files": 2,
                        "totals.errors": 11,
                        "totals.warnings": 1,
                        "totals.fixable": 11
                    }
                }
            ],
            "results": [
                {
                    "message": {
                        "text": "Expected at least 1 space before \"-\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceBefore",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 9
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space after \"-\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceAfter",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 9
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space before \"=\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceBefore",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 17
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space after \"=\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceAfter",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 17
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space before \"-\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceBefore",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 26
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space before \"-\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceBefore",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 58
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Expected at least 1 space after \"-\"; 0 found"
                    },
                    "ruleId": "PSR12.Operators.OperatorSpacing.NoSpaceAfter",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_1.php"
                                },
                                "region": {
                                    "startLine": 3,
                                    "startColumn": 58
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line 6 and the first side effect is on line 10."
                    },
                    "ruleId": "PSR1.Files.SideEffects.FoundWithSymbols",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_2.php"
                                },
                                "region": {
                                    "startLine": 1,
                                    "startColumn": 1
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "warning",
                        "fixable": false
                    }
                },
                {
                    "message": {
                        "text": "Opening brace should be on a new line"
                    },
                    "ruleId": "Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_2.php"
                                },
                                "region": {
                                    "startLine": 6,
                                    "startColumn": 29
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Space before opening parenthesis of function call prohibited"
                    },
                    "ruleId": "PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_2.php"
                                },
                                "region": {
                                    "startLine": 14,
                                    "startColumn": 1
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Newline required after opening brace"
                    },
                    "ruleId": "Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_2.php"
                                },
                                "region": {
                                    "startLine": 15,
                                    "startColumn": 23
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                },
                {
                    "message": {
                        "text": "Closing brace must be on a line by itself"
                    },
                    "ruleId": "Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore",
                    "locations": [
                        {
                            "physicalLocation": {
                                "artifactLocation": {
                                    "uri": "file:///shared/backups/github/megalinter/.automation/test/php/php_bad_2.php"
                                },
                                "region": {
                                    "startLine": 15,
                                    "startColumn": 24
                                }
                            }
                        }
                    ],
                    "properties": {
                        "severity": "error",
                        "fixable": true
                    }
                }
            ]
        }
    ]
}

Repository with source code of this new CodeSniffer Report will be soon available.

@llaville llaville added the enhancement New feature or request label Apr 27, 2024
@llaville llaville self-assigned this Apr 27, 2024
@llaville
Copy link
Collaborator Author

I've a good news for all audience !

Rather than just create a SARIF support for PHP_CodeSnifffer, I've today continue my work to produce support to :

A little teaser : I'll soon (next days) release a version 1.2.0 of bartlett/sarif-php-sdk (source: https://github.com/llaville/sarif-php-sdk) that will provide these 3 converters.
I've not yet have a look on PSALM (that have already SARIF support), but I think it could also be possible to add a converter for this linter too.

As soon as the new version of bartlett/sarif-php-sdk will be published, I'll provide a patch to MegaLinter to be able to use it !

More explains will come on https://github.com/llaville/sarif-php-sdk when branch 1.2 will be available.

@nvuillam
Copy link
Member

@llaville I'm sad to not work on PHP to benefit from your great updates, but happy that the MegaLinter community will be able to enjoy them 🥳

@llaville llaville changed the title Add PHP_CodeSniffer SARIF support Add SARIF support to all PHP linters Apr 30, 2024
@llaville
Copy link
Collaborator Author

After analysis of Psalm source code, I confirm that latest version (currently 5.23.1), does not support additional report that the ones defined in Core. See https://github.com/vimeo/psalm/blob/5.23.1/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php#L328-L366

So my package bartlett/sarif-php-sdk (future 1.2.0) could not add a converter for Psalm. That means MegaLinter should continue to use the native solution.

@llaville
Copy link
Collaborator Author

I plan to release version 1.2.0 of bartlett/sarif-php-sdk on May 2nd, and next days this week a patch for MegaLinter v7

@llaville
Copy link
Collaborator Author

llaville commented May 3, 2024

Yesterday, I've released https://github.com/llaville/sarif-php-sdk/releases/tag/1.2.0
Today, I've released https://github.com/overtrue/phplint/releases/tag/9.2.0

And probably tomorrow, I should be able to build a patch for MegaLinter v7

@llaville
Copy link
Collaborator Author

llaville commented May 3, 2024

Patch, part 1/3

diff --git a/megalinter/descriptors/php.megalinter-descriptor.yml b/megalinter/descriptors/php.megalinter-descriptor.yml
index 041730f09a..b89d9d4605 100644
--- a/megalinter/descriptors/php.megalinter-descriptor.yml
+++ b/megalinter/descriptors/php.megalinter-descriptor.yml
@@ -147,10 +147,13 @@ linters:
           url: https://marketplace.visualstudio.com/items?itemName=getpsalm.psalm-vscode-plugin
   # PHPLint
   - linter_name: phplint
+    can_output_sarif: true
     linter_url: https://github.com/overtrue/phplint
     linter_repo: https://github.com/overtrue/phplint
     config_file_name: .phplint.yml
     cli_lint_mode: list_of_files
+    cli_sarif_args:
+      - "--log-sarif={{SARIF_OUTPUT_FILE}}"
     examples:
       - "phplint myfile.php"
       - "phplint mydir"

Seems good ... at least for PHPLint (9.2.0)

  • Building PHP flavor with this descriptor updates
  • Running Docker image built on megalinter/.automation/test/php for tests, with following command :
docker run --rm  -v /var/run/docker.sock:/var/run/docker.sock:rw -v $(pwd):/tmp/lint:rw -e ENABLE=PHP -e SARIF_REPORTER=true -e LOG_LEVEL=debug -e PARALLEL=false -e PHP_PHPLINT_ARGUMENTS="-v" <DOCKER_IMAGE_ID>

NB:

  • -e PARALLEL=false used only for easy debugging, and retrieve results in logs group by linters
  • -e PHP_PHPLINT_ARGUMENTS="-v" used only for debugging purpose and see if in MegaLinter logs, PHPLint accept expected values
[phplint] command: ['phplint', '-v', '-c', '/action/lib/.automation/.phplint.yml', '--log-sarif=/tmp/lint/megalinter-reports/sarif/PHP_PHPLINT.sarif', 'php_bad_1.php', 'php_bad_2.php', 'php_good_1.php', 'php_good_2.php']
[phplint] CWD: /tmp/lint
Linter version command: ['/usr/local/bin/phplint', '--version']
Linter version result: 0 phplint 9.2.0

[SARIF Reporter] Generated PHP_PHPLINT report: /tmp/lint/megalinter-reports/sarif/PHP_PHPLINT.sarif
Sarif to human result: 0
warning: unexpected identifier "pe98y" in line 3

warning: unexpected token "}" in line 15

warning: 2 warnings emitted

[phplint] result: 1 phplint 9.2.0 by overtrue and contributors.

Runtime       : PHP 8.3.6
Configuration : /action/lib/.automation/.phplint.yml

┌──────────────────┬─────────────────────────────────────────────────────────────────────┬─────────┐
│ Name             │ Value                                                               │ Help    │
├──────────────────┼─────────────────────────────────────────────────────────────────────┼─────────┤
│ path             │ ["php_bad_1.php","php_bad_2.php","php_good_1.php","php_good_2.php"] │ command │
├──────────────────┼─────────────────────────────────────────────────────────────────────┼─────────┤
│ configuration    │ "/action/lib/.automation/.phplint.yml"                              │ command │
│ no-configuration │ false                                                               │ command │
│ exclude          │ []                                                                  │ command │
│ extensions       │ ["php"]                                                             │ command │
│ jobs             │ 5                                                                   │ command │
│ no-cache         │ false                                                               │ command │
│ cache            │ ".phplint.cache"                                                    │ command │
│ no-progress      │ false                                                               │ command │
│ progress         │ "printer"                                                           │ command │
│ log-json         │ false                                                               │ command │
│ log-junit        │ false                                                               │ command │
│ log-sarif        │ "/tmp/lint/megalinter-reports/sarif/PHP_PHPLINT.sarif"              │ command │
│ sarif-converter  │ "\\Bartlett\\Sarif\\Converter\\PhpLintConverter"                    │ command │
│ warning          │ false                                                               │ command │
│ memory-limit     │ "128M"                                                              │ command │
│ ignore-exit-code │ false                                                               │ command │
└──────────────────┴─────────────────────────────────────────────────────────────────────┴─────────┘

EE
Time: < 1 sec, Memory: 10.4 MiB, Cache: 2 hits, 2 misses, Process: 1

 [ERROR] 4 files, 2 errors                                                      


There was 2 errors:
1. /tmp/lint/php_bad_1.php:3
    1| <?php
    2| 
  > 3| 2pe98y r-n0u823n=r  092u3- r08u2q098ry 09nq2yr09n2yr9 y2n-93yr  298yr3  29

 unexpected identifier "pe98y" in line 3
2. /tmp/lint/php_bad_2.php:15
    12| 
    13| $condition = rand(0, 5);
    14| iff ($condition) {
  > 15| } elseif ($condition) {}

 unexpected token "}" in line 15

[Text Reporter] Generated TEXT report: /tmp/lint/megalinter-reports/linters_logs/ERROR-PHP_PHPLINT.log


[SARIF Reporter] Generated SARIF report: /tmp/lint/megalinter-reports/megalinter-report.sarif

I don't copy/paste SARIF log file but all is OK !!!

@llaville
Copy link
Collaborator Author

llaville commented May 3, 2024

Have initialized new branch features/php-linters-with-sarif with at least the patch part 1/3

Next, will come later ...

@llaville
Copy link
Collaborator Author

llaville commented May 4, 2024

Patch, part 2/3

Now, it's turn to do it for PHP_CodeSniffer. I run in trouble with many issues; I'll give you then my experience feedback here !

PHPLint 9.2 depends on bartlett/sarif-php-sdk (see https://github.com/overtrue/phplint/blob/9.2.0/composer.json#L37)
But It's not the case for PHP_CodeSniffer or PHPStan, so we need another strategy.

As previous installation of these linters are done with PHIVE (PHAR distribution), we cannot use this strategy.

So the only one solution is to install with Composer.

On Alpine 3.19, Composer (2.7.4) depends on PHP 8.2 components

See * https://pkgs.alpinelinux.org/package/v3.19/community/x86_64/composer

While upcoming Composer (2.7.5) depends on PHP 8.3 components

See * https://pkgs.alpinelinux.org/package/edge/community/x86_64/composer

So I don't install Composer with apk, but with docker image (see https://getcomposer.org/doc/00-intro.md#docker-image)

And results are pretty good :

[phpcs] command: ['phpcs', '--standard=/action/lib/.automation/phpcs.xml', '--report=\\Bartlett\\Sarif\\Converter\\PhpCsConverter', 'php_bad_1.php', 'php_bad_2.php', 'php_good_1.php', 'php_good_2.php']
[phpcs] CWD: /tmp/lint
Linter version command: ['/root/.composer/vendor/bin/phpcs', '--version']
Linter version result: 0 PHP_CodeSniffer version 3.9.2 (stable) by Squiz and PHPCSStandards

[SARIF Reporter] Generated PHP_PHPCS report: /tmp/lint/megalinter-reports/sarif/PHP_PHPCS.sarif
Sarif to human result: 0


[phpcs] result: 2 {"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"PHP_CodeSniffer","shortDescription":{"text":"Detects violations of a defined set of coding standards."},"fullDescription":{"text":"PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards."},"semanticVersion":"3.9.2.0","informationUri":"https://github.com/PHPCSStandards/PHP_CodeSniffer","rules":[{"id":"PSR12.Operators.OperatorSpacing.NoSpaceBefore","properties":{"frequency":4}},{"id":"PSR12.Operators.OperatorSpacing.NoSpaceAfter","properties":{"frequency":3}},{"id":"PSR1.Files.SideEffects.FoundWithSymbols","properties":{"frequency":1}},{"id":"Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine","properties":{"frequency":1}},{"id":"PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket","properties":{"frequency":1}},{"id":"Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace","properties":{"frequency":1}},{"id":"Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore","properties":{"frequency":1}}]}},"invocations":[{"executionSuccessful":true,"workingDirectory":{"uri":"file:///tmp/lint/"},"properties":{"totals.files":2,"totals.errors":11,"totals.warnings":1,"totals.fixable":11}}],"results":[{"message":{"text":"Expected at least 1 space before \"-\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceBefore","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":9}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space after \"-\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceAfter","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":9}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space before \"=\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceBefore","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":17}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space after \"=\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceAfter","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":17}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space before \"-\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceBefore","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":26}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space before \"-\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceBefore","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":58}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Expected at least 1 space after \"-\"; 0 found"},"ruleId":"PSR12.Operators.OperatorSpacing.NoSpaceAfter","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_1.php"},"region":{"startLine":3,"startColumn":58}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line 6 and the first side effect is on line 10."},"ruleId":"PSR1.Files.SideEffects.FoundWithSymbols","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_2.php"},"region":{"startLine":1,"startColumn":1}}}],"properties":{"severity":"warning","fixable":false}},{"message":{"text":"Opening brace should be on a new line"},"ruleId":"Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_2.php"},"region":{"startLine":6,"startColumn":29}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Space before opening parenthesis of function call prohibited"},"ruleId":"PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_2.php"},"region":{"startLine":14,"startColumn":1}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Newline required after opening brace"},"ruleId":"Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_2.php"},"region":{"startLine":15,"startColumn":23}}}],"properties":{"severity":"error","fixable":true}},{"message":{"text":"Closing brace must be on a line by itself"},"ruleId":"Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore","locations":[{"physicalLocation":{"artifactLocation":{"uri":"php_bad_2.php"},"region":{"startLine":15,"startColumn":24}}}],"properties":{"severity":"error","fixable":true}}]}]}
diff --git a/megalinter/descriptors/php.megalinter-descriptor.yml b/megalinter/descriptors/php.megalinter-descriptor.yml
index b89d9d4605..6fcc33e53f 100644
--- a/megalinter/descriptors/php.megalinter-descriptor.yml
+++ b/megalinter/descriptors/php.megalinter-descriptor.yml
@@ -16,6 +16,8 @@ install:
     - php83-ctype
     - php83-curl
     - php83-dom
+    - php83-openssl
+    - php83-common
     - php83-simplexml
     - dpkg
   dockerfile:
@@ -34,10 +36,13 @@ install:
           && mv phive.phar /usr/local/bin/phive \
           && rm phive.phar.asc \
           && update-alternatives --install /usr/bin/php php /usr/bin/php83 110
+    - COPY --from=composer/composer:2-bin /composer /usr/bin/composer
+    - ENV PATH="/root/.composer/vendor/bin:${PATH}"
 linters:
   # PHPCS
   - linter_name: phpcs
     name: PHP_PHPCS
+    can_output_sarif: true
     linter_url: https://github.com/PHPCSStandards/PHP_CodeSniffer
     linter_repo: https://github.com/PHPCSStandards/PHP_CodeSniffer
     linter_rules_configuration_url: https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Advanced-Usage#using-a-default-configuration-file
@@ -47,6 +52,8 @@ linters:
     config_file_name: phpcs.xml
     cli_lint_mode: list_of_files
     cli_config_arg_name: "--standard="
+    cli_sarif_args:
+      - "--report=\\Bartlett\\Sarif\\Converter\\PhpCsConverter"
     cli_lint_errors_count: regex_number
     cli_lint_errors_regex: "FOUND ([0-9]+) ERRORS"
     examples:
@@ -56,7 +63,7 @@ linters:
     install:
       dockerfile:
         - |
-          RUN GITHUB_AUTH_TOKEN="$(cat /run/secrets/GITHUB_TOKEN)" && export GITHUB_AUTH_TOKEN && phive --no-progress install phpcs -g --trust-gpg-keys 31C7E470E2138192,95DE904AB800754A11D80B605E6DDE998AB73B8E
+          RUN GITHUB_AUTH_TOKEN="$(cat /run/secrets/GITHUB_TOKEN)" && export GITHUB_AUTH_TOKEN && composer global require squizlabs/php_codesniffer bartlett/sarif-php-sdk
     ide:
       atom:
         - name: linter-phpcs

@llaville
Copy link
Collaborator Author

llaville commented May 4, 2024

Patch, part 3/3

And to finish, it's turn to do it for PHPStan.

I used the same strategy as for PHPCS

And results are pretty good :

[phpstan] command: ['phpstan', 'analyse', '--no-progress', '--no-ansi', '--memory-limit', '1G', '-c', '/action/lib/.automation/phpstan.neon.dist', '--error-format=sarif', 'php_bad_1.php', 'php_bad_2.php', 'php_good_1.php', 'php_good_2.php']
[phpstan] CWD: /tmp/lint
Linter version command: ['/root/.composer/vendor/bin/phpstan', '--version']
Linter version result: 0 PHPStan - PHP Static Analysis Tool 1.10.67

[SARIF Reporter] Generated PHP_PHPSTAN report: /tmp/lint/megalinter-reports/sarif/PHP_PHPSTAN.sarif
Sarif to human result: 0
diff --git a/TEMPLATES/phpstan.neon.dist b/TEMPLATES/phpstan.neon.dist
index 7229da62eb..84fca4e117 100644
--- a/TEMPLATES/phpstan.neon.dist
+++ b/TEMPLATES/phpstan.neon.dist
@@ -1,2 +1,6 @@
 parameters:
        level: 0
+
+services:
+    errorFormatter.sarif:
+        class: Bartlett\Sarif\Converter\PhpStanConverter
diff --git a/megalinter/descriptors/php.megalinter-descriptor.yml b/megalinter/descriptors/php.megalinter-descriptor.yml
index 6fcc33e53f..0316bca842 100644
--- a/megalinter/descriptors/php.megalinter-descriptor.yml
+++ b/megalinter/descriptors/php.megalinter-descriptor.yml
@@ -84,6 +84,7 @@ linters:
   # PHP Stan
   - linter_name: phpstan
     name: PHP_PHPSTAN
+    can_output_sarif: true
     linter_url: https://phpstan.org/
     linter_repo: https://github.com/phpstan/phpstan
     linter_image_url: https://i.imgur.com/WaRKPlC.png
@@ -97,6 +98,8 @@ linters:
       - "--no-ansi"
       - "--memory-limit"
       - "1G"
+    cli_sarif_args:
+      - "--error-format=sarif"
     cli_lint_errors_count: regex_number
     cli_lint_errors_regex: "Found ([0-9]+) error"
     examples:
@@ -106,9 +109,7 @@ linters:
       - "phpstan analyse --no-progress --no-ansi mydir1/ mydir2/ myfile.php"
     install:
       dockerfile:
-        - FROM ghcr.io/phpstan/phpstan:latest-php8.3 as phpstan
-        - COPY --link --from=phpstan /composer/vendor/phpstan/phpstan/phpstan.phar /usr/bin/phpstan
-        - RUN chmod +x /usr/bin/phpstan
+        - RUN GITHUB_AUTH_TOKEN="$(cat /run/secrets/GITHUB_TOKEN)" && export GITHUB_AUTH_TOKEN && composer global require phpstan/phpstan bartlett/sarif-php-sdk
       idea:
         - name: PHPStan / Psalm / Generics
           url: https://plugins.jetbrains.com/plugin/12754-phpstan--psalm--generics

@llaville
Copy link
Collaborator Author

llaville commented May 4, 2024

Spell Linter is a bit rough when new word is not in is dictionary :

trivy_secret_phpcs

@nvuillam
Copy link
Member

nvuillam commented May 4, 2024

@llaville you can get an updated cspell.json in MegaLinter artifacts, just overwrite the one in the repo and check for updated lines to confirm added exceptions are not spelling mistakes ^^

@llaville
Copy link
Collaborator Author

llaville commented May 5, 2024

@nvuillam will do !

But before, I recommend to ML team to proceed with feature request #3538 (PR #3541 is on way)

@llaville
Copy link
Collaborator Author

llaville commented May 5, 2024

Add new commit 505247d to fix cspell issue
Branch is now also re-synchronized with main

@llaville
Copy link
Collaborator Author

llaville commented May 5, 2024

I don't think what you know about this feature request status, but perharps we need more time to test if we found regression before to suggest a new PR ? Agree ?

@nvuillam
Copy link
Member

nvuillam commented May 5, 2024

@llaville CI test cases are here to confirm that there are no regressions about linters , and anyway we don't release a new version every week so we'll have the time to make additional tests with the future beta version :)

nvuillam added a commit that referenced this issue May 13, 2024
* patch part 1/3 for PHP_PHPLINT and SARIF support (see feature request #3515)

* patch part 2/3 for PHP_PHPCS and SARIF support (see feature request #3515)

* patch part 3/3 for PHP_PHPSTAN and SARIF support (see feature request #3515)

* fix cspell linter dict about unknown word 'codesniffer'

* update changelog related to feature #3515

* update after running bash build.py

* [MegaLinter] Apply linters fixes

---------

Co-authored-by: Nicolas Vuillamy <nicolas.vuillamy@gmail.com>
Co-authored-by: llaville <llaville@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants