Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.55% covered (success)
93.55%
29 / 31
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
RectorConfig
93.55% covered (success)
93.55%
29 / 31
50.00% covered (danger)
50.00%
1 / 2
6.01
0.00% covered (danger)
0.00%
0 / 1
 configure
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
2
 applySafeMigrationSet
77.78% covered (warning)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
4.18
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\Config;
21
22use Composer\InstalledVersions;
23use Ergebnis\Rector\Rules\Faker\GeneratorPropertyFetchToMethodCallRector;
24use FastForward\DevTools\Rector\AddMissingMethodPhpDocRector;
25use FastForward\DevTools\Rector\RemoveEmptyDocBlockRector;
26use FastForward\DevTools\Path\ManagedWorkspace;
27use FastForward\DevTools\Path\WorkingProjectPathResolver;
28use Rector\Config\RectorConfig as RectorConfigInterface;
29use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector;
30use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector;
31use Rector\Php\PhpVersionResolver\ComposerJsonPhpVersionResolver;
32use Rector\Configuration\PhpLevelSetResolver;
33use Rector\Set\ValueObject\SetList;
34
35use function Safe\getcwd;
36
37/**
38 * Provides the default Rector configuration.
39 *
40 * Consumers can use this as a starting point and extend it:
41 *
42 *     return \FastForward\DevTools\Config\RectorConfig::configure(
43 *         static function (\Rector\Config\RectorConfig $rectorConfig): void {
44 *             $rectorConfig->rules([
45 *                 // custom rules
46 *             ]);
47 *         }
48 *     );
49 *
50 * @see https://github.com/rectorphp/rector
51 */
52 class RectorConfig
53{
54    /**
55     * @var list<string> the default Rector sets applied to Fast Forward projects
56     */
57    public const array DEFAULT_SETS = [
58        SetList::DEAD_CODE,
59        SetList::CODE_QUALITY,
60        SetList::CODING_STYLE,
61        SetList::TYPE_DECLARATION,
62        SetList::PRIVATIZATION,
63        SetList::INSTANCEOF,
64        SetList::EARLY_RETURN,
65    ];
66
67    /**
68     * @var list<class-string> the default Rector rules applied on top of the configured sets
69     */
70    public const array DEFAULT_RULES = [
71        GeneratorPropertyFetchToMethodCallRector::class,
72        AddMissingMethodPhpDocRector::class,
73        RemoveEmptyDocBlockRector::class,
74    ];
75
76    /**
77     * @var list<class-string> the Rector rules that SHOULD be skipped by default
78     */
79    public const array DEFAULT_SKIPPED_RULES = [
80        RemoveUselessReturnTagRector::class,
81        RemoveUselessParamTagRector::class,
82    ];
83
84    /**
85     * Creates the default Rector configuration.
86     *
87     * @param callable|null $customize optional callback to customize the configuration
88     *
89     * @return callable the configuration callback
90     */
91    public static function configure(?callable $customize = null): callable
92    {
93        return static function (RectorConfigInterface $rectorConfig) use ($customize): void {
94            $workingDirectory = getcwd();
95            $skipPaths = WorkingProjectPathResolver::getToolingExcludedDirectories($workingDirectory);
96            $skipRules = self::DEFAULT_SKIPPED_RULES;
97
98            $rectorConfig->sets(self::DEFAULT_SETS);
99            $rectorConfig->paths([$workingDirectory]);
100            $rectorConfig->skip([...$skipPaths, ...$skipRules]);
101            $rectorConfig->cacheDirectory(
102                ManagedWorkspace::getCacheDirectory(ManagedWorkspace::RECTOR, $workingDirectory)
103            );
104            $rectorConfig->importNames();
105            $rectorConfig->removeUnusedImports();
106            $rectorConfig->fileExtensions(['php']);
107            $rectorConfig->parallel(600);
108            $rectorConfig->rules(self::DEFAULT_RULES);
109
110            $projectPhpVersion = ComposerJsonPhpVersionResolver::resolveFromCwdOrFail();
111            $phpLevelSets = PhpLevelSetResolver::resolveFromPhpVersion($projectPhpVersion);
112
113            $rectorConfig->sets($phpLevelSets);
114
115            self::applySafeMigrationSet($rectorConfig);
116
117            if (null !== $customize) {
118                $customize($rectorConfig);
119            }
120        };
121    }
122
123    /**
124     * Applies the optional Safe migration callback when the package is installed.
125     *
126     * @param RectorConfigInterface $rectorConfig
127     */
128    public static function applySafeMigrationSet(RectorConfigInterface $rectorConfig): void
129    {
130        if (! InstalledVersions::isInstalled('thecodingmachine/safe', false)) {
131            return;
132        }
133
134        $packageLocation = InstalledVersions::getInstallPath('thecodingmachine/safe');
135        $safeRectorMigrateFile = $packageLocation . '/rector-migrate.php';
136
137        if (! file_exists($safeRectorMigrateFile)) {
138            return;
139        }
140
141        $callback = require_once $safeRectorMigrateFile;
142
143        if (\is_callable($callback)) {
144            $callback($rectorConfig);
145        }
146    }
147}