2024-01-26 19:37:08 +00:00
< ? php
declare ( strict_types = 1 );
namespace Rector\Configuration ;
use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface ;
2024-02-08 04:24:11 +00:00
use Rector\Config\Level\DeadCodeLevel ;
use Rector\Config\Level\TypeDeclarationLevel ;
2024-01-26 19:37:08 +00:00
use Rector\Config\RectorConfig ;
2024-02-09 12:02:07 +00:00
use Rector\Config\RegisteredService ;
2024-02-05 08:01:59 +00:00
use Rector\Configuration\Levels\LevelRulesResolver ;
2024-01-29 21:10:17 +00:00
use Rector\Contract\Rector\ConfigurableRectorInterface ;
2024-01-26 19:37:08 +00:00
use Rector\Contract\Rector\RectorInterface ;
2024-01-27 12:22:01 +00:00
use Rector\Doctrine\Set\DoctrineSetList ;
2024-01-29 23:52:40 +00:00
use Rector\Exception\Configuration\InvalidConfigurationException ;
use Rector\Php\PhpVersionResolver\ProjectComposerJsonPhpVersionResolver ;
2024-01-27 12:22:01 +00:00
use Rector\PHPUnit\Set\PHPUnitSetList ;
2024-01-29 23:52:40 +00:00
use Rector\Set\ValueObject\LevelSetList ;
2024-01-26 21:43:08 +00:00
use Rector\Set\ValueObject\SetList ;
2024-01-27 12:22:01 +00:00
use Rector\Symfony\Set\FOSRestSetList ;
use Rector\Symfony\Set\JMSSetList ;
use Rector\Symfony\Set\SensiolabsSetList ;
use Rector\Symfony\Set\SymfonySetList ;
2024-01-26 19:37:08 +00:00
use Rector\ValueObject\PhpVersion ;
2024-03-01 20:02:28 +00:00
use RectorPrefix202403\Symfony\Component\Finder\Finder ;
use RectorPrefix202403\Webmozart\Assert\Assert ;
2024-01-26 19:37:08 +00:00
/**
* @ api
*/
final class RectorConfigBuilder
{
/**
* @ var string []
*/
private $paths = [];
/**
* @ var string []
*/
private $sets = [];
/**
* @ var array < mixed >
*/
private $skip = [];
/**
* @ var array < class - string < RectorInterface >>
*/
private $rules = [];
/**
2024-01-29 21:10:17 +00:00
* @ var array < class - string < ConfigurableRectorInterface > , mixed [] >
2024-01-26 19:37:08 +00:00
*/
2024-01-29 21:10:17 +00:00
private $rulesWithConfigurations = [];
2024-01-26 19:37:08 +00:00
/**
* @ var string []
*/
private $fileExtensions = [];
/**
* @ var null | class - string < CacheStorageInterface >
*/
private $cacheClass ;
/**
* @ var string | null
*/
private $cacheDirectory ;
/**
* @ var string | null
*/
private $containerCacheDirectory ;
/**
2024-03-03 20:46:43 +00:00
* @ var bool | null
2024-01-26 19:37:08 +00:00
*/
2024-03-03 20:46:43 +00:00
private $parallel ;
2024-01-26 19:37:08 +00:00
/**
* @ var int
*/
private $parallelTimeoutSeconds = 120 ;
/**
* @ var int
*/
private $parallelMaxNumberOfProcess = 16 ;
/**
* @ var int
*/
private $parallelJobSize = 16 ;
/**
* @ var bool
*/
private $importNames = \false ;
/**
* @ var bool
*/
private $importDocBlockNames = \false ;
/**
* @ var bool
*/
private $importShortClasses = \true ;
/**
* @ var bool
*/
private $removeUnusedImports = \false ;
/**
* @ var bool
*/
private $noDiffs = \false ;
/**
* @ var string | null
*/
private $memoryLimit ;
/**
* @ var string []
*/
private $autoloadPaths = [];
/**
* @ var string []
*/
private $bootstrapFiles = [];
/**
* @ var string
*/
private $indentChar = ' ' ;
/**
* @ var int
*/
private $indentSize = 4 ;
/**
* @ var string []
*/
private $phpstanConfigs = [];
/**
* @ var null | PhpVersion ::*
*/
private $phpVersion ;
2024-01-30 16:16:28 +00:00
/**
* @ var string | null
*/
private $symfonyContainerXmlFile ;
/**
* @ var string | null
*/
private $symfonyContainerPhpFile ;
2024-02-07 21:49:03 +00:00
/**
* To make sure type declarations set and level are not duplicated ,
* as both contain same rules
2024-03-28 16:05:42 +00:00
* @ var bool | null
2024-02-07 21:49:03 +00:00
*/
2024-03-28 16:05:42 +00:00
private $isTypeCoverageLevelUsed ;
2024-02-07 21:49:03 +00:00
/**
2024-03-28 16:05:42 +00:00
* @ var bool | null
2024-02-07 21:49:03 +00:00
*/
2024-03-28 16:05:42 +00:00
private $isDeadCodeLevelUsed ;
/**
* @ var bool | null
*/
private $isFluentNewLine ;
2024-02-09 12:02:07 +00:00
/**
* @ var RegisteredService []
*/
private $registerServices = [];
2024-01-26 19:37:08 +00:00
public function __invoke ( RectorConfig $rectorConfig ) : void
{
2024-01-30 20:43:47 +00:00
$uniqueSets = \array_unique ( $this -> sets );
2024-03-28 16:05:42 +00:00
if ( \in_array ( SetList :: TYPE_DECLARATION , $uniqueSets , \true ) && $this -> isTypeCoverageLevelUsed === \true ) {
2024-02-07 21:49:03 +00:00
throw new InvalidConfigurationException ( \sprintf ( 'Your config already enables type declarations set.%sRemove "->withTypeCoverageLevel()" as it only duplicates it, or remove type declaration set.' , \PHP_EOL ));
}
2024-03-28 16:05:42 +00:00
if ( \in_array ( SetList :: DEAD_CODE , $uniqueSets , \true ) && $this -> isDeadCodeLevelUsed === \true ) {
2024-02-07 21:49:03 +00:00
throw new InvalidConfigurationException ( \sprintf ( 'Your config already enables dead code set.%sRemove "->withDeadCodeLevel()" as it only duplicates it, or remove dead code set.' , \PHP_EOL ));
}
2024-03-17 01:39:59 +00:00
if ( $uniqueSets !== []) {
$rectorConfig -> sets ( $uniqueSets );
}
2024-02-05 10:43:38 +00:00
if ( $this -> paths !== []) {
$rectorConfig -> paths ( $this -> paths );
}
2024-02-09 12:02:07 +00:00
// must be in upper part, as these services might be used by rule registered bellow
foreach ( $this -> registerServices as $registerService ) {
$rectorConfig -> singleton ( $registerService -> getClassName ());
if ( $registerService -> getAlias ()) {
$rectorConfig -> alias ( $registerService -> getClassName (), $registerService -> getAlias ());
}
if ( $registerService -> getTag ()) {
$rectorConfig -> tag ( $registerService -> getClassName (), $registerService -> getTag ());
}
}
2024-03-17 01:39:59 +00:00
if ( $this -> skip !== []) {
$rectorConfig -> skip ( $this -> skip );
}
if ( $this -> rules !== []) {
$rectorConfig -> rules ( $this -> rules );
}
2024-01-29 21:10:17 +00:00
foreach ( $this -> rulesWithConfigurations as $rectorClass => $configurations ) {
foreach ( $configurations as $configuration ) {
$rectorConfig -> ruleWithConfiguration ( $rectorClass , $configuration );
}
2024-01-26 19:37:08 +00:00
}
if ( $this -> fileExtensions !== []) {
$rectorConfig -> fileExtensions ( $this -> fileExtensions );
}
if ( $this -> cacheClass !== null ) {
$rectorConfig -> cacheClass ( $this -> cacheClass );
}
if ( $this -> cacheDirectory !== null ) {
$rectorConfig -> cacheDirectory ( $this -> cacheDirectory );
}
if ( $this -> containerCacheDirectory !== null ) {
$rectorConfig -> containerCacheDirectory ( $this -> containerCacheDirectory );
}
if ( $this -> importNames || $this -> importDocBlockNames ) {
$rectorConfig -> importNames ( $this -> importNames , $this -> importDocBlockNames );
$rectorConfig -> importShortClasses ( $this -> importShortClasses );
}
if ( $this -> removeUnusedImports ) {
$rectorConfig -> removeUnusedImports ( $this -> removeUnusedImports );
}
if ( $this -> noDiffs ) {
$rectorConfig -> noDiffs ();
}
if ( $this -> memoryLimit !== null ) {
$rectorConfig -> memoryLimit ( $this -> memoryLimit );
}
if ( $this -> autoloadPaths !== []) {
$rectorConfig -> autoloadPaths ( $this -> autoloadPaths );
}
if ( $this -> bootstrapFiles !== []) {
$rectorConfig -> bootstrapFiles ( $this -> bootstrapFiles );
}
if ( $this -> indentChar !== ' ' || $this -> indentSize !== 4 ) {
$rectorConfig -> indent ( $this -> indentChar , $this -> indentSize );
}
if ( $this -> phpstanConfigs !== []) {
$rectorConfig -> phpstanConfigs ( $this -> phpstanConfigs );
}
if ( $this -> phpVersion !== null ) {
$rectorConfig -> phpVersion ( $this -> phpVersion );
}
2024-03-03 20:46:43 +00:00
if ( $this -> parallel !== null ) {
if ( $this -> parallel ) {
$rectorConfig -> parallel ( $this -> parallelTimeoutSeconds , $this -> parallelMaxNumberOfProcess , $this -> parallelJobSize );
} else {
$rectorConfig -> disableParallel ();
}
2024-01-26 19:37:08 +00:00
}
2024-01-30 16:16:28 +00:00
if ( $this -> symfonyContainerXmlFile !== null ) {
$rectorConfig -> symfonyContainerXml ( $this -> symfonyContainerXmlFile );
}
if ( $this -> symfonyContainerPhpFile !== null ) {
$rectorConfig -> symfonyContainerPhp ( $this -> symfonyContainerPhpFile );
}
2024-03-28 16:05:42 +00:00
if ( $this -> isFluentNewLine !== null ) {
$rectorConfig -> newLineOnFluentCall ( $this -> isFluentNewLine );
}
2024-01-26 19:37:08 +00:00
}
/**
* @ param string [] $paths
*/
public function withPaths ( array $paths ) : self
{
$this -> paths = $paths ;
return $this ;
}
/**
* @ param array < mixed > $skip
*/
public function withSkip ( array $skip ) : self
{
2024-02-16 16:38:11 +00:00
$this -> skip = \array_merge ( $this -> skip , $skip );
2024-01-26 19:37:08 +00:00
return $this ;
}
2024-02-16 19:08:11 +00:00
public function withSkipPath ( string $skipPath ) : self
{
2024-02-16 22:00:46 +00:00
if ( \strpos ( $skipPath , '*' ) === \false ) {
Assert :: fileExists ( $skipPath );
}
2024-02-16 19:08:11 +00:00
return $this -> withSkip ([ $skipPath ]);
}
2024-01-26 19:37:08 +00:00
/**
* Include PHP files from the root directory ,
* typically ecs . php , rector . php etc .
*/
public function withRootFiles () : self
{
$rootPhpFilesFinder = ( new Finder ()) -> files () -> in ( \getcwd ()) -> depth ( 0 ) -> name ( '*.php' );
foreach ( $rootPhpFilesFinder as $rootPhpFileFinder ) {
$this -> paths [] = $rootPhpFileFinder -> getRealPath ();
}
return $this ;
}
/**
* @ param string [] $sets
*/
public function withSets ( array $sets ) : self
{
2024-01-26 22:36:43 +00:00
$this -> sets = \array_merge ( $this -> sets , $sets );
2024-01-26 19:37:08 +00:00
return $this ;
}
2024-01-27 12:22:01 +00:00
/**
* Upgrade your annotations to attributes
*/
public function withAttributesSets ( bool $symfony = \false , bool $doctrine = \false , bool $mongoDb = \false , bool $gedmo = \false , bool $phpunit = \false , bool $fosRest = \false , bool $jms = \false , bool $sensiolabs = \false ) : self
{
if ( $symfony ) {
$this -> sets [] = SymfonySetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $doctrine ) {
$this -> sets [] = DoctrineSetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $mongoDb ) {
$this -> sets [] = DoctrineSetList :: MONGODB__ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $gedmo ) {
$this -> sets [] = DoctrineSetList :: GEDMO_ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $phpunit ) {
$this -> sets [] = PHPUnitSetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $fosRest ) {
$this -> sets [] = FOSRestSetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $jms ) {
$this -> sets [] = JMSSetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
if ( $sensiolabs ) {
$this -> sets [] = SensiolabsSetList :: ANNOTATIONS_TO_ATTRIBUTES ;
}
return $this ;
}
2024-01-29 23:52:40 +00:00
/**
* What PHP sets should be applied ? By default the same version
* as composer . json has is used
*/
2024-03-15 10:45:55 +00:00
public function withPhpSets ( bool $php83 = \false , bool $php82 = \false , bool $php81 = \false , bool $php80 = \false , bool $php74 = \false , bool $php73 = \false , bool $php72 = \false , bool $php71 = \false , bool $php70 = \false , bool $php56 = \false , bool $php55 = \false , bool $php54 = \false , bool $php53 = \false , bool $php84 = \false ) : self
2024-01-29 23:52:40 +00:00
{
$pickedArguments = \array_filter ( \func_get_args ());
if ( \count ( $pickedArguments ) > 1 ) {
throw new InvalidConfigurationException ( \sprintf ( 'Pick only one version target in "withPhpSets()". All rules up to this version will be used.%sTo use your composer.json PHP version, keep arguments empty.' , \PHP_EOL ));
}
if ( $pickedArguments === []) {
// use composer.json PHP version
$projectComposerJsonFilePath = \getcwd () . '/composer.json' ;
if ( \file_exists ( $projectComposerJsonFilePath )) {
$projectPhpVersion = ProjectComposerJsonPhpVersionResolver :: resolve ( $projectComposerJsonFilePath );
if ( \is_int ( $projectPhpVersion )) {
$this -> sets [] = \Rector\Configuration\PhpLevelSetResolver :: resolveFromPhpVersion ( $projectPhpVersion );
return $this ;
}
}
throw new InvalidConfigurationException ( \sprintf ( 'We could not find local "composer.json" to determine your PHP version.%sPlease, fill the PHP version set in withPhpSets() manually.' , \PHP_EOL ));
2024-01-30 05:37:48 +00:00
}
if ( $php53 ) {
2024-01-29 23:52:40 +00:00
$this -> sets [] = LevelSetList :: UP_TO_PHP_53 ;
} elseif ( $php54 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_54 ;
} elseif ( $php55 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_55 ;
} elseif ( $php56 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_56 ;
} elseif ( $php70 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_70 ;
} elseif ( $php71 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_71 ;
} elseif ( $php72 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_72 ;
} elseif ( $php73 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_73 ;
} elseif ( $php74 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_74 ;
} elseif ( $php80 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_80 ;
} elseif ( $php81 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_81 ;
} elseif ( $php82 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_82 ;
} elseif ( $php83 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_83 ;
2024-03-15 10:45:55 +00:00
} elseif ( $php84 ) {
$this -> sets [] = LevelSetList :: UP_TO_PHP_84 ;
2024-01-29 23:52:40 +00:00
}
return $this ;
}
2024-01-26 21:43:08 +00:00
public function withPreparedSets ( bool $deadCode = \false , bool $codeQuality = \false , bool $codingStyle = \false , bool $typeDeclarations = \false , bool $privatization = \false , bool $naming = \false , bool $instanceOf = \false , bool $earlyReturn = \false , bool $strictBooleans = \false ) : self
{
if ( $deadCode ) {
$this -> sets [] = SetList :: DEAD_CODE ;
}
if ( $codeQuality ) {
$this -> sets [] = SetList :: CODE_QUALITY ;
}
if ( $codingStyle ) {
$this -> sets [] = SetList :: CODING_STYLE ;
}
if ( $typeDeclarations ) {
$this -> sets [] = SetList :: TYPE_DECLARATION ;
}
if ( $privatization ) {
$this -> sets [] = SetList :: PRIVATIZATION ;
}
if ( $naming ) {
$this -> sets [] = SetList :: NAMING ;
}
if ( $instanceOf ) {
$this -> sets [] = SetList :: INSTANCEOF ;
}
if ( $earlyReturn ) {
$this -> sets [] = SetList :: EARLY_RETURN ;
}
if ( $strictBooleans ) {
$this -> sets [] = SetList :: STRICT_BOOLEANS ;
}
return $this ;
}
2024-01-26 19:37:08 +00:00
/**
* @ param array < class - string < RectorInterface >> $rules
*/
public function withRules ( array $rules ) : self
{
2024-02-16 16:38:11 +00:00
$this -> rules = \array_merge ( $this -> rules , $rules );
2024-01-26 19:37:08 +00:00
return $this ;
}
/**
* @ param string [] $fileExtensions
*/
public function withFileExtensions ( array $fileExtensions ) : self
{
$this -> fileExtensions = $fileExtensions ;
return $this ;
}
/**
2024-01-27 12:22:01 +00:00
* @ param class - string < CacheStorageInterface >| null $cacheClass
2024-01-26 19:37:08 +00:00
*/
2024-01-27 12:22:01 +00:00
public function withCache ( ? string $cacheDirectory = null , ? string $cacheClass = null , ? string $containerCacheDirectory = null ) : self
2024-01-26 19:37:08 +00:00
{
2024-01-27 12:22:01 +00:00
$this -> cacheDirectory = $cacheDirectory ;
2024-01-26 19:37:08 +00:00
$this -> cacheClass = $cacheClass ;
2024-01-27 12:22:01 +00:00
$this -> containerCacheDirectory = $containerCacheDirectory ;
2024-01-26 19:37:08 +00:00
return $this ;
}
/**
2024-01-29 21:10:17 +00:00
* @ param class - string < ConfigurableRectorInterface > $rectorClass
2024-01-26 19:37:08 +00:00
* @ param mixed [] $configuration
*/
public function withConfiguredRule ( string $rectorClass , array $configuration ) : self
{
2024-01-29 21:10:17 +00:00
$this -> rulesWithConfigurations [ $rectorClass ][] = $configuration ;
2024-01-26 19:37:08 +00:00
return $this ;
}
public function withParallel ( ? int $timeoutSeconds = null , ? int $maxNumberOfProcess = null , ? int $jobSize = null ) : self
{
$this -> parallel = \true ;
if ( \is_int ( $timeoutSeconds )) {
$this -> parallelTimeoutSeconds = $timeoutSeconds ;
}
if ( \is_int ( $maxNumberOfProcess )) {
$this -> parallelMaxNumberOfProcess = $maxNumberOfProcess ;
}
if ( \is_int ( $jobSize )) {
$this -> parallelJobSize = $jobSize ;
}
return $this ;
}
public function withoutParallel () : self
{
$this -> parallel = \false ;
return $this ;
}
2024-01-27 12:22:01 +00:00
public function withImportNames ( bool $importNames = \true , bool $importDocBlockNames = \true , bool $importShortClasses = \true , bool $removeUnusedImports = \false ) : self
2024-01-26 19:37:08 +00:00
{
$this -> importNames = $importNames ;
$this -> importDocBlockNames = $importDocBlockNames ;
$this -> importShortClasses = $importShortClasses ;
$this -> removeUnusedImports = $removeUnusedImports ;
return $this ;
}
public function withNoDiffs () : self
{
$this -> noDiffs = \true ;
return $this ;
}
public function withMemoryLimit ( string $memoryLimit ) : self
{
$this -> memoryLimit = $memoryLimit ;
return $this ;
}
public function withIndent ( string $indentChar = ' ' , int $indentSize = 4 ) : self
{
$this -> indentChar = $indentChar ;
$this -> indentSize = $indentSize ;
return $this ;
}
/**
* @ param string [] $autoloadPaths
*/
public function withAutoloadPaths ( array $autoloadPaths ) : self
{
$this -> autoloadPaths = $autoloadPaths ;
return $this ;
}
/**
* @ param string [] $bootstrapFiles
*/
public function withBootstrapFiles ( array $bootstrapFiles ) : self
{
$this -> bootstrapFiles = $bootstrapFiles ;
return $this ;
}
/**
* @ param string [] $phpstanConfigs
*/
public function withPHPStanConfigs ( array $phpstanConfigs ) : self
{
$this -> phpstanConfigs = $phpstanConfigs ;
return $this ;
}
/**
* @ param PhpVersion ::* $phpVersion
*/
public function withPhpVersion ( int $phpVersion ) : self
{
$this -> phpVersion = $phpVersion ;
return $this ;
}
2024-01-30 16:16:28 +00:00
public function withSymfonyContainerXml ( string $symfonyContainerXmlFile ) : self
{
$this -> symfonyContainerXmlFile = $symfonyContainerXmlFile ;
return $this ;
}
public function withSymfonyContainerPhp ( string $symfonyContainerPhpFile ) : self
{
$this -> symfonyContainerPhpFile = $symfonyContainerPhpFile ;
return $this ;
}
2024-02-05 08:01:59 +00:00
/**
* @ experimental since 0.19 . 7 Raise your dead - code coverage from the safest rules
* to more affecting ones , one level at a time
*/
public function withDeadCodeLevel ( int $level ) : self
{
2024-02-07 21:49:03 +00:00
$this -> isDeadCodeLevelUsed = \true ;
2024-02-08 04:24:11 +00:00
$levelRules = LevelRulesResolver :: resolve ( $level , DeadCodeLevel :: RULES , 'RectorConfig::withDeadCodeLevel()' );
2024-02-05 08:01:59 +00:00
$this -> rules = \array_merge ( $this -> rules , $levelRules );
return $this ;
}
/**
* @ experimental since 0.19 . 7 Raise your type coverage from the safest type rules
* to more affecting ones , one level at a time
*/
public function withTypeCoverageLevel ( int $level ) : self
{
2024-02-07 21:49:03 +00:00
$this -> isTypeCoverageLevelUsed = \true ;
2024-02-08 04:24:11 +00:00
$levelRules = LevelRulesResolver :: resolve ( $level , TypeDeclarationLevel :: RULES , 'RectorConfig::withTypeCoverageLevel()' );
2024-02-05 08:01:59 +00:00
$this -> rules = \array_merge ( $this -> rules , $levelRules );
return $this ;
}
2024-03-28 16:05:42 +00:00
public function withFluentCallNewLine ( bool $isFluentNewLine = \true ) : self
{
$this -> isFluentNewLine = $isFluentNewLine ;
return $this ;
}
2024-02-09 12:02:07 +00:00
public function registerService ( string $className , ? string $alias = null , ? string $tag = null ) : self
{
$this -> registerServices [] = new RegisteredService ( $className , $alias , $tag );
return $this ;
}
2024-01-26 19:37:08 +00:00
}