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\RemoveMixedDocblockOverruledByNativeTypeRector;
31use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector;
32use Rector\Set\ValueObject\SetList;
33use function Safe\getcwd;
34
35/**
36 * Provides the default Rector configuration.
37 *
38 * Consumers can use this as a starting point and extend it:
39 *
40 *     return \FastForward\DevTools\Config\RectorConfig::configure(
41 *         static function (\Rector\Config\RectorConfig $rectorConfig): void {
42 *             $rectorConfig->rules([
43 *                 // custom rules
44 *             ]);
45 *         }
46 *     );
47 *
48 * @see https://github.com/rectorphp/rector
49 */
50 class RectorConfig
51{
52    /**
53     * @var list<string> the default Rector sets applied to Fast Forward projects
54     */
55    public const array DEFAULT_SETS = [
56        SetList::DEAD_CODE,
57        SetList::CODE_QUALITY,
58        SetList::CODING_STYLE,
59        SetList::TYPE_DECLARATION,
60        SetList::PRIVATIZATION,
61        SetList::INSTANCEOF,
62        SetList::EARLY_RETURN,
63    ];
64
65    /**
66     * @var list<class-string> the default Rector rules applied on top of the configured sets
67     */
68    public const array DEFAULT_RULES = [
69        GeneratorPropertyFetchToMethodCallRector::class,
70        AddMissingMethodPhpDocRector::class,
71        RemoveEmptyDocBlockRector::class,
72    ];
73
74    /**
75     * @var list<class-string> the Rector rules that SHOULD be skipped by default
76     */
77    public const array DEFAULT_SKIPPED_RULES = [
78        RemoveUselessReturnTagRector::class,
79        RemoveUselessParamTagRector::class,
80        RemoveMixedDocblockOverruledByNativeTypeRector::class,
81    ];
82
83    /**
84     * Creates the default Rector configuration.
85     *
86     * @param callable|null $customize optional callback to customize the configuration
87     *
88     * @return callable the configuration callback
89     */
90    public static function configure(?callable $customize = null): callable
91    {
92        return static function (RectorConfigInterface $rectorConfig) use ($customize): void {
93            $workingDirectory = getcwd();
94            $paths = WorkingProjectPathResolver::getToolingSourcePaths();
95            $skipPaths = WorkingProjectPathResolver::getToolingExcludedDirectories();
96            $skipRules = self::DEFAULT_SKIPPED_RULES;
97
98            $rectorConfig->sets(self::DEFAULT_SETS);
99            $rectorConfig->paths($paths);
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            $phpSets = RectorConfigInterface::configure()->withPhpSets();
111            $phpSets($rectorConfig);
112
113            self::applySafeMigrationSet($rectorConfig);
114
115            if (null !== $customize) {
116                $customize($rectorConfig);
117            }
118        };
119    }
120
121    /**
122     * Applies the optional Safe migration callback when the package is installed.
123     *
124     * @param RectorConfigInterface $rectorConfig
125     */
126    public static function applySafeMigrationSet(RectorConfigInterface $rectorConfig): void
127    {
128        if (! InstalledVersions::isInstalled('thecodingmachine/safe', false)) {
129            return;
130        }
131
132        $packageLocation = InstalledVersions::getInstallPath('thecodingmachine/safe');
133        $safeRectorMigrateFile = $packageLocation . '/rector-migrate.php';
134
135        if (! file_exists($safeRectorMigrateFile)) {
136            return;
137        }
138
139        $callback = require_once $safeRectorMigrateFile;
140
141        if (\is_callable($callback)) {
142            $callback($rectorConfig);
143        }
144    }
145}