ssch/typo3-rector
Automated upgrades and refactoring for TYPO3 sites and extensions using Rector. Apply version migrations, remove deprecations, and modernize code safely in development with configurable rule sets for TYPO3 7–12+.
Want to help? Great! Join the TYPO3 Slack channel #ext-typo3-rector
Fork this project into your own account.
You can use TYPO3 Rector with at least PHP 7.4. Your contributed code MUST also be compatible with PHP 7.4!
Install the project using composer:
git clone git@github.com:your-account/typo3-rector.git
cd typo3-rector
composer install
https://github.com/sabbelasichon/typo3-rector/issues
INFO You can filter by tags where you can select, for example, only the easy ones by selecting "easy pick" or "good first issue".
Assign the issue to yourself if you are a member of the project, so others can see that you are working on it. If you are not a member of the project, make a comment to let everyone know that you are working on it.
Run the following command and answer all questions:
bin/generate-rule
To generate a code quality rule, use the following command:
bin/typo3-rector generate-code-quality-rule
This command will ask you some questions to provide a proper rector setup. Following this will lead to the creation of the overall rector structure necessary. It will create the skeleton for the rector rule with the class, test class, fixtures and directories to start coding — basically everything you need to start!
StaticCall).refactor must return a node, an array of nodes, int (when you want to remove a node) or null.getNodeTypes method is used to define the use case of the function to migrate. It helps as well acting like an early return (see example below).In this example the methods GeneralUtility::strtoupper(...) and GeneralUtility::strtolower(...) are migrated.
getNodeTypes checks for the StaticCall, preventing further rector execution if not metrefactor first checks for the ObjectType to do an early return in case the class scanned is not TYPO3\CMS\Core\Utility\GeneralUtilityfinal class GeneralUtilityToUpperAndLowerRector extends AbstractRector
{
/**
* [@return](https://github.com/return) array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [StaticCall::class];
}
/**
* [@param](https://github.com/param) StaticCall $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
$node,
new ObjectType('TYPO3\CMS\Core\Utility\GeneralUtility')
)) {
return null;
}
if (! $this->isNames($node->name, ['strtoupper', 'strtolower'])) {
return null;
}
// ...
}
}
This is a minimal example to demonstrate how to remove a node.
In this case you need to listen to an Expression and return NodeVisitor::REMOVE_NODE.
<?php
use PhpParser\Node;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeVisitor;
use Rector\Rector\AbstractRector;
final class RemoveNodeRector extends AbstractRector
{
/**
* [@return](https://github.com/return) array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Expression::class];
}
/**
* [@param](https://github.com/param) Expression $node
*/
public function refactor(Node $node): ?int
{
$expressionNode = $node->expr;
// your magic
return NodeVisitor::REMOVE_NODE;
}
}
Sometimes you need to return more than a single node.
A typical use case is if you want to create another method, for example.
In this case you need to listen to an Expression.
Do a full text search for [Expression::class] to find existing rules which can help you.
final class MyRector extends AbstractRector
{
public function getNodeTypes(): array
{
return [Expression::class];
}
/**
* [@param](https://github.com/param) Expression $node
* [@return](https://github.com/return) Node[]|null
*/
public function refactor(Node $node): ?array
{
$methodCall = $node->expr;
if (! $methodCall instanceof Node\Expr\MethodCall) {
return null;
}
// More checks
// Do your magic
$methodCall = $this->nodeFactory->createMethodCall(...);
return [new Expression($methodCall), $node];
}
}
To run this rule only when a minimum PHP version is used, add this method with a required feature:
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
final class MyRector extends AbstractRector implements MinPhpVersionInterface
{
public function provideMinPhpVersion(): int
{
return PhpVersionFeature::ENUM;
}
}
To check if a class implements an interface, first the scope needs to be fetched and then checked by using class reflection.
private function implementsInterface(ClassMethod|Class_ $node): bool
{
$scope = ScopeFetcher::fetch($node);
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return false;
}
return $classReflection->implementsInterface('Vendor\YourInterface');
}
Make sure you have a test in place for your Rector
All unit tests must pass before submitting a pull request. Additionally, the code style must be valid. Run the following commands:
composer update
composer run-script local:contribute
vendor/bin/phpunit
Overall hints for testing:
-----Great, now you can submit your changes in a pull request
How can I help you explore Laravel packages today?