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