Packages that ship a lot of versions can have a lot of sets to apply. For, twig/twig has 6 sets in Rector, a couple for v1 and a couple for v2. What about v3? We must always check for our locally installed version and then keep rector.php up to date.
This could lead to errors as we run sets with new features from v3 that we don't have yet.
At the moment, we have to add sets manually one by one to rector.php:
return RectorConfig::configure()
->withSets([
\Rector\Symfony\Set\TwigSetList::TWIG_112,
\Rector\Symfony\Set\TwigSetList::TWIG_127,
\Rector\Symfony\Set\TwigSetList::TWIG_134,
\Rector\Symfony\Set\TwigSetList::TWIG_20,
\Rector\Symfony\Set\TwigSetList::TWIG_24,
])
This doesn't seem right for a couple of reasons:
composer.json version of twig/twigIf we upgrade to Twig 3 later on, Rector should pick up sets for Twig 3 for us. So, we don't maintain the rector.php at all.
withComposerBased()return RectorConfig::configure()
->withComposerBased(twig: true);
You can drop all sets from above and use this single parameter. Currently, we support Twig, PHPUnit, and Doctrine. Support for Symfony and Laravel is coming soon.
Rector will go through all Twig sets and check our installed version in vendor/composer/installed.json. Then, it finds all sets relevant to our specific version.
If you're writing your custom extension for your community, you'll want to create your own SetProvider to handle complexity for your users.
Let's look at a real example for TwigSetProvider from the rector-symfony package:
namespace Rector\Symfony\Set\SetProvider;
use Rector\Set\Contract\SetInterface;
use Rector\Set\Contract\SetProviderInterface;
use Rector\Set\ValueObject\ComposerTriggeredSet;
use Rector\Set\ValueObject\Set;
final class TwigSetProvider implements SetProviderInterface
{
/**
* @return SetInterface[]
*/
public function provide(): array
{
return [
new ComposerTriggeredSet(
'twig',
'twig/twig',
'1.27',
__DIR__ . '/../../../config/sets/twig/twig127.php'
),
new ComposerTriggeredSet(
'twig',
'twig/twig',
'2.0',
__DIR__ . '/../../../config/sets/twig/twig20.php'
),
// ...
];
}
}
Setup is straightforward - define the version and path to the set:
namespace Rector\Set\ValueObject;
final class ComposerTriggeredSet
{
public function __construct(
private string $groupName,
private string $packageName,
private string $version,
private string $setFilePath
) {
}
// ...
}
$groupName is key in ->withComposerBased():$packageName is the composer package name.$version is the minimal version to trigger the set$setFilePath is the path to the Rector config with rules as we know itIn the near future, community packages like Laravel will have their own SetProvider classes. To get their latest upgrade sets, all you'll have to do is add one param:
return RectorConfig::configure()
->withComposerBased(laravel: true);
Happy coding!