Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.75% covered (success)
93.75%
30 / 32
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
RectorConfig
93.75% covered (success)
93.75%
30 / 32
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%
23 / 23
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            $paths = WorkingProjectPathResolver::getToolingSourcePaths();
96            $skipPaths = WorkingProjectPathResolver::getToolingExcludedDirectories();
97            $skipRules = self::DEFAULT_SKIPPED_RULES;
98
99            $rectorConfig->sets(self::DEFAULT_SETS);
100            $rectorConfig->paths($paths);
101            $rectorConfig->skip([...$skipPaths, ...$skipRules]);
102            $rectorConfig->cacheDirectory(
103                ManagedWorkspace::getCacheDirectory(ManagedWorkspace::RECTOR, $workingDirectory)
104            );
105            $rectorConfig->importNames();
106            $rectorConfig->removeUnusedImports();
107            $rectorConfig->fileExtensions(['php']);
108            $rectorConfig->parallel(600);
109            $rectorConfig->rules(self::DEFAULT_RULES);
110
111            $projectPhpVersion = ComposerJsonPhpVersionResolver::resolveFromCwdOrFail();
112            $phpLevelSets = PhpLevelSetResolver::resolveFromPhpVersion($projectPhpVersion);
113
114            $rectorConfig->sets($phpLevelSets);
115
116            self::applySafeMigrationSet($rectorConfig);
117
118            if (null !== $customize) {
119                $customize($rectorConfig);
120            }
121        };
122    }
123
124    /**
125     * Applies the optional Safe migration callback when the package is installed.
126     *
127     * @param RectorConfigInterface $rectorConfig
128     */
129    public static function applySafeMigrationSet(RectorConfigInterface $rectorConfig): void
130    {
131        if (! InstalledVersions::isInstalled('thecodingmachine/safe', false)) {
132            return;
133        }
134
135        $packageLocation = InstalledVersions::getInstallPath('thecodingmachine/safe');
136        $safeRectorMigrateFile = $packageLocation . '/rector-migrate.php';
137
138        if (! file_exists($safeRectorMigrateFile)) {
139            return;
140        }
141
142        $callback = require_once $safeRectorMigrateFile;
143
144        if (\is_callable($callback)) {
145            $callback($rectorConfig);
146        }
147    }
148}