-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
691 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<!-- markdownlint-disable MD013 --> | ||
# Rector Converter | ||
|
||
[![rectorphp/rector - GitHub](https://gh-card.dev/repos/rectorphp/rector.svg?fullname=)](https://github.com/rectorphp/rector) | ||
|
||
> [!NOTE] | ||
> | ||
> Available since version 1.0.0 | ||
## Table Of Contents | ||
|
||
1. [Requirements](#requirements) | ||
2. [Installation](#installation) | ||
3. [Usage](#usage) | ||
4. [Learn more](#learn-more) | ||
5. [IDE Integration](#ide-integration) | ||
6. [Web SARIF viewer](#web-sarif-viewer) | ||
|
||
![rector converter](../assets/images/converter-rector.graphviz.svg) | ||
|
||
## Requirements | ||
|
||
* [Rector][rector] requires PHP version 7.2.0 or greater, with `phpstan` 1.11 or greater | ||
* This SARIF converter requires at least Rector version 1.0 | ||
|
||
## Installation | ||
|
||
```shell | ||
composer require --dev rector/rector bartlett/sarif-php-converters | ||
``` | ||
|
||
## Usage | ||
|
||
**Step 1:** Update your `rector.php` configuration file | ||
|
||
Register at least the `RectorFormatter` service to be able to specify `--output-format sarif` with rector command. | ||
|
||
```php | ||
<?php | ||
use Bartlett\Sarif\Converter\Reporter\RectorFormatter; | ||
|
||
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface; | ||
use Rector\Config\RectorConfig; | ||
|
||
return RectorConfig::configure() | ||
->withPaths([ | ||
__DIR__ . '/src', | ||
]) | ||
->withPreparedSets(true) | ||
->withRealPathReporting() | ||
->withBootstrapFiles([__DIR__ . '/../../vendor/autoload.php']) // loader for Sarif PHP Converters classes | ||
->registerService(RectorFormatter::class, null, OutputFormatterInterface::class) | ||
; | ||
``` | ||
|
||
**Step 2:** Then print the SARIF report | ||
|
||
```shell | ||
vendor/bin/rector process --dry-run --output-format sarif --config /path/to/rector.php > .sarif.json | ||
``` | ||
|
||
> [!WARNING] | ||
> | ||
> Be sure to specify `withRealPathReporting`, otherwise the Console Tool `convert` command | ||
> will raise some warnings about file names. | ||
> Requires at least [feature](https://github.com/rectorphp/rector/issues/8757) is implemented in a future Rector release. | ||
## Learn more | ||
|
||
* See demo [`examples/rector/`][example-folder] directory into this repository. | ||
|
||
## IDE Integration | ||
|
||
The SARIF report file `[*].sarif.json` is automagically recognized and interpreted by PhpStorm (2024). | ||
|
||
![PHPStorm integration](../assets/images/phpstorm-rector.png) | ||
|
||
## Web SARIF viewer | ||
|
||
With the [React based component][sarif-web-component], you are able to explore a sarif report file previously generated. | ||
|
||
For example: | ||
|
||
![sarif-web-rector](../assets/images/sarif-web-rector.png) | ||
|
||
[example-folder]: https://github.com/llaville/sarif-php-converters/blob/1.0/examples/rector/ | ||
[rector]: https://github.com/rectorphp/rector | ||
[sarif-web-component]: https://github.com/Microsoft/sarif-web-component |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"Rector","shortDescription":{"text":"PHP Refactoring code"},"fullDescription":{"text":"Instant Upgrade and Automated Refactoring of any PHP code"},"semanticVersion":"dev-main","informationUri":"https://getrector.com","rules":[{"id":"RECTOR/DeadCode/ClassMethod","name":"RemoveUnusedPrivateMethodParameterRector","shortDescription":{"text":"Remove unused parameter, if not required by interface or parent class"},"helpUri":"https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md#removeunusedprivatemethodparameterrector","properties":{"configurable":false}},{"id":"RECTOR/DeadCode/Assign","name":"RemoveUnusedVariableAssignRector","shortDescription":{"text":"Remove unused assigns to variables"},"helpUri":"https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md#removeunusedvariableassignrector","properties":{"configurable":false}},{"id":"RECTOR/CodeQuality/FunctionLike","name":"SimplifyUselessVariableRector","shortDescription":{"text":"Removes useless variable assigns"},"helpUri":"https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md#simplifyuselessvariablerector","properties":{"configurable":true}},{"id":"RECTOR/SystemError"}]},"extensions":[{"name":"bartlett/sarif-php-converters","shortDescription":{"text":"Rector SARIF Converter"},"version":"1.0.9999999.9999999-dev"}]},"invocations":[{"executionSuccessful":true,"commandLine":"/shared/backups/forks/rector-src/bin/rector","arguments":["process","--dry-run","--output-format","sarif","--config","examples/rector/rector.php"],"workingDirectory":{"uri":"file:///shared/backups/bartlett/sarif-php-converters/"},"properties":{"changed_files":6}}],"originalUriBaseIds":{"WORKINGDIR":{"uri":"file:///shared/backups/bartlett/sarif-php-converters/"}},"results":[{"message":{"text":"Applied rule RemoveUselessReturnTagRector"},"ruleId":"RECTOR/DeadCode/ClassMethod","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Contract/ConverterInterface.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/ClassMethod":"3326e444522cdd8facde887fa1960a74bd5dc7503a5fc3a1317116774ef6af3c"}},{"message":{"text":"Applied rule RemoveUnusedVariableAssignRector"},"ruleId":"RECTOR/DeadCode/Assign","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/EcsNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/Assign":"6800590f849f76621b065eacea6cbb0dc87e2a1556d051d8af92a12fd862ae97"}},{"message":{"text":"Applied rule RemoveUnusedPrivateMethodParameterRector"},"ruleId":"RECTOR/DeadCode/ClassMethod","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/EcsNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/ClassMethod":"6800590f849f76621b065eacea6cbb0dc87e2a1556d051d8af92a12fd862ae97"}},{"message":{"text":"Applied rule RemoveUnusedVariableAssignRector"},"ruleId":"RECTOR/DeadCode/Assign","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/PhanNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/Assign":"a99dcdffc7d119bace1aeeb609c6607c633663cdbcbaf9f70bd01d0aef0db104"}},{"message":{"text":"Applied rule RemoveUnusedPrivateMethodParameterRector"},"ruleId":"RECTOR/DeadCode/ClassMethod","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/PhanNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/ClassMethod":"a99dcdffc7d119bace1aeeb609c6607c633663cdbcbaf9f70bd01d0aef0db104"}},{"message":{"text":"Applied rule RemoveUnusedVariableAssignRector"},"ruleId":"RECTOR/DeadCode/Assign","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/PhpCsFixerNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/Assign":"09ce8c52a5fe769d80c7fadb702589215b3041ac6abba478c243e5d85ec2ecbc"}},{"message":{"text":"Applied rule RemoveUnusedPrivateMethodParameterRector"},"ruleId":"RECTOR/DeadCode/ClassMethod","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/PhpCsFixerNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/ClassMethod":"09ce8c52a5fe769d80c7fadb702589215b3041ac6abba478c243e5d85ec2ecbc"}},{"message":{"text":"Applied rule SimplifyUselessVariableRector"},"ruleId":"RECTOR/CodeQuality/FunctionLike","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/RectorNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/CodeQuality/FunctionLike":"d734c199b053768bfe3c79459f5b0f3484b114c2dbf5c3cde5ebcef75dcdfbf1"}},{"message":{"text":"Applied rule RemoveUnusedPrivateMethodParameterRector"},"ruleId":"RECTOR/DeadCode/ClassMethod","locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Converter/Normalizer/TwigCsFixerNormalizer.php","uriBaseId":"WORKINGDIR"}}}],"partialFingerprints":{"RECTOR/DeadCode/ClassMethod":"89b6fb20d5952af114ce920d1e24262af8d7da2aa7077345b9f40b88c62d5281"}},{"message":{"text":"Syntax error, unexpected '}'"},"ruleId":"RECTOR/SystemError","locations":[{"physicalLocation":{"artifactLocation":{"uri":"examples/fixtures/php_bad_2.php","uriBaseId":"WORKINGDIR"},"region":{"startLine":15,"snippet":{"rendered":{"text":" > 15| } elseif ($condition) {}"}}},"contextRegion":{"startLine":13,"endLine":17,"snippet":{"rendered":{"text":" > 13| $condition = rand(0, 5);\n 14| iff ($condition) {\n 15| } elseif ($condition) {}\n 16| "}}}}}],"partialFingerprints":{"RECTOR/SystemError":"2e508d59ec5ba941128ccdf751869d555c2c618018b0979124ee4530b68fbc28"}},{"message":{"text":"Syntax error, unexpected T_STRING, Syntax error, unexpected '=', Invalid numeric literal, Syntax error, unexpected T_LNUMBER, Invalid numeric literal, Syntax error, unexpected T_STRING"},"ruleId":"RECTOR/SystemError","locations":[{"physicalLocation":{"artifactLocation":{"uri":"examples/fixtures/php_bad_1.php","uriBaseId":"WORKINGDIR"},"region":{"startLine":3,"snippet":{"rendered":{"text":" > 3| 2pe98y r-n0u823n=r 092u3- r08u2q098ry 09nq2yr09n2yr9 y2n-93yr 298yr3 29"}}},"contextRegion":{"startLine":1,"endLine":5,"snippet":{"rendered":{"text":" > 1| <?php\n 2| \n 3| 2pe98y r-n0u823n=r 092u3- r08u2q098ry 09nq2yr09n2yr9 y2n-93yr 298yr3 29\n 4| "}}}}}],"partialFingerprints":{"RECTOR/SystemError":"b72968e694d9f576c2adc3cdc3f479a0834ab688b7dc8a6fe2657d59e034ae1f"}}],"automationDetails":{"id":"Daily run 2024-07-27T08:05:50+00:00"}}]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<!-- markdownlint-disable MD013 --> | ||
# Rector Converter Demo | ||
|
||
Refer to specialized [Converter Guide](../../docs/converter/rector.md) for explains. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the Sarif-PHP-Converters package. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
* | ||
* @author Laurent Laville | ||
* @since Release 1.0.0 | ||
*/ | ||
|
||
use Bartlett\Sarif\Converter\Reporter\RectorFormatter; | ||
|
||
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface; | ||
use Rector\Config\RectorConfig; | ||
|
||
return RectorConfig::configure() | ||
->withPaths([ | ||
__DIR__ . '/../../examples', | ||
__DIR__ . '/../../src', | ||
]) | ||
->withPreparedSets(true) | ||
->withRealPathReporting() | ||
->withBootstrapFiles([__DIR__ . '/../../vendor/autoload.php']) | ||
->registerService(RectorFormatter::class, null, OutputFormatterInterface::class) | ||
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the Sarif-PHP-Converters package. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
* | ||
* @author Laurent Laville | ||
* @since Release 1.0.0 | ||
*/ | ||
|
||
return function (): Generator { | ||
$classes = [ | ||
\Bartlett\Sarif\Converter\RectorConverter::class, | ||
\Bartlett\Sarif\Converter\Source\RectorSource::class, | ||
\Bartlett\Sarif\Converter\Normalizer\RectorNormalizer::class, | ||
]; | ||
foreach ($classes as $class) { | ||
yield $class; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the Sarif-PHP-Converters package. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
* | ||
* @author Laurent Laville | ||
* @since Release 1.0.0 | ||
*/ | ||
|
||
return [ | ||
// @link https://graphviz.gitlab.io/docs/attrs/rankdir/ | ||
'graph.rankdir' => 'TB', | ||
// @link https://plantuml.com/en/color | ||
'cluster.Bartlett\Sarif\Converter.graph.bgcolor' => 'BurlyWood', | ||
'cluster.Bartlett\Sarif\Contract.graph.bgcolor' => 'LightSkyBlue', | ||
'cluster.Bartlett\Sarif\Converter\Source.graph.bgcolor' => 'Bisque', | ||
'cluster.Bartlett\Sarif\Converter\Normalizer.graph.bgcolor' => 'Bisque', | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the Sarif-PHP-Converters package. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
namespace Bartlett\Sarif\Converter\Normalizer; | ||
|
||
use Bartlett\Sarif\Contract\NormalizerInterface; | ||
|
||
use Rector\Contract\Rector\ConfigurableRectorInterface; | ||
use Rector\Contract\Rector\RectorInterface; | ||
use Rector\ValueObject\ProcessResult; | ||
|
||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
use ArrayObject; | ||
use ReflectionClass; | ||
use ReflectionException; | ||
use function array_unique; | ||
use function count; | ||
use function explode; | ||
use function in_array; | ||
use function is_a; | ||
use function sprintf; | ||
use function strtolower; | ||
|
||
/** | ||
* @author Laurent Laville | ||
* @since Release 1.0.0 | ||
*/ | ||
final class RectorNormalizer implements NormalizerInterface | ||
{ | ||
public const URI_PATTERN = 'https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md#%s'; | ||
|
||
public function getSupportedFormats(): array | ||
{ | ||
return [ | ||
NormalizerInterface::FORMAT_INTERNAL, | ||
]; | ||
} | ||
|
||
public function normalize($data, string $format, array $context): ?ArrayObject | ||
{ | ||
if (!in_array($format, $this->getSupportedFormats())) { | ||
return null; | ||
} | ||
|
||
if (!$data instanceof ProcessResult) { | ||
return null; | ||
} | ||
|
||
// internal format (legacy) | ||
return new ArrayObject($this->fromInternal($data, $context)); | ||
} | ||
|
||
/** | ||
* @return array{files: mixed, errors: mixed, rules: mixed} | ||
*/ | ||
private function fromInternal(ProcessResult $processResult, array $context): array | ||
{ | ||
$files = []; | ||
$errors = []; | ||
$rules = []; | ||
|
||
$fileDiffs = $processResult->getFileDiffs(); | ||
|
||
foreach ($fileDiffs as $fileDiff) { | ||
$filePath = $fileDiff->getAbsoluteFilePath(); | ||
$files[] = $filePath; | ||
|
||
foreach($fileDiff->getRectorClasses() as $rectorClass) { | ||
// e.g: Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector | ||
$nameParts = explode('\\', $rectorClass); | ||
|
||
$standard = $nameParts[1]; // e.g: DeadCode | ||
$group = $nameParts[3]; // e.g: ClassMethod | ||
$rectorShortClass = $nameParts[count($nameParts) - 1]; // e.g: RemoveUnusedPrivateMethodParameterRector | ||
$helpUri = sprintf(self::URI_PATTERN, strtolower($rectorShortClass)); | ||
|
||
$ruleId = sprintf('%s/%s/%s', $context['rulePrefix'], $standard, $group); | ||
|
||
$rules[$ruleId] = [ | ||
'name' => $rectorShortClass, | ||
'helpUri' => $helpUri, | ||
]; | ||
|
||
$ruleDefinition = $this->resolveFromClassName($rectorClass); | ||
if (null !== $ruleDefinition) { | ||
$rules[$ruleId]['shortDescription'] = $ruleDefinition->getDescription(); | ||
} | ||
$isRuleConfigurable = is_a($rectorClass, ConfigurableRectorInterface::class, true); | ||
$rules[$ruleId]['properties']['configurable'] = $isRuleConfigurable; | ||
|
||
$attributes = [ | ||
'ReportingDescriptor.id' => $ruleId, | ||
'Result.message' => sprintf('Applied rule %s', $rectorShortClass), | ||
]; | ||
$errors[$filePath][] = $attributes; | ||
}; | ||
} | ||
|
||
$systemErrors = $processResult->getSystemErrors(); | ||
foreach ($systemErrors as $error) { | ||
$filePath = $error->getAbsoluteFilePath(); | ||
$files[] = $filePath; | ||
|
||
$nameParts = explode('\\', \get_class($error)); | ||
$ruleId = sprintf('%s/%s', $context['rulePrefix'], end($nameParts)); | ||
|
||
$rules[$ruleId] = [ | ||
'name' => $error->getRectorClass(), | ||
]; | ||
|
||
$attributes = [ | ||
'ReportingDescriptor.id' => $ruleId, | ||
'Result.message' => $error->getMessage(), | ||
'Region.startLine' => $error->getLine() ?? null, | ||
]; | ||
$errors[$filePath][] = $attributes; | ||
} | ||
|
||
return [ | ||
'files' => array_unique($files), | ||
'errors' => $errors, | ||
'rules' => $rules, | ||
]; | ||
} | ||
|
||
private function resolveFromClassName(string $className): ?RuleDefinition | ||
{ | ||
try { | ||
$reflectionClass = new ReflectionClass($className); | ||
$documentedRule = $reflectionClass->newInstanceWithoutConstructor(); | ||
} catch (ReflectionException $e) { | ||
return null; | ||
} | ||
|
||
if ($documentedRule instanceof RectorInterface) { | ||
return $documentedRule->getRuleDefinition(); | ||
} | ||
|
||
return null; | ||
} | ||
} |
Oops, something went wrong.