Getting Closer with RectorPHP

Created by Abdul Malik Ikhsan / @samsonasik

Abdul Malik Ikhsan

Contributor.

RectorPHP Maintainer.

Technical Steering Committee.

Core Team.

Test Framework Maintainer.

What is RectorPHP?

RectorPHP is a console application utilize nikic/PHP-Parser and PHPStan for automated refactoring of PHP applications that helps with:

  • PHP upgrades
  • Framework upgrades
  • Code quality improvements
  • Type coverages

PHP upgrades example: change Closure to ArrowFunction for PHP 7.4+

Before

						
function () {
	return 1;
};
						
					

After

						
fn() => 1;
						
					
Ref https://getrector.com/demo/d7709b87-39d2-4e89-91e3-244ad0139ff5

Framework Upgrades example: Change setExpectedException() to expectException() and expectExceptionMessage for PHPUnit 6.0+ TestCase

Before

						
final class MyTest extends \PHPUnit\Framework\TestCase
{
    public function test()
    {
        $this->setExpectedException('InvalidArgumentException', 'some message');
    }
}
						
					

After

						
final class MyTest extends \PHPUnit\Framework\TestCase
{
    public function test()
    {
        $this->expectException('InvalidArgumentException');
        $this->expectExceptionMessage('some message');
    }
}
						
					
Ref https://getrector.com/demo/1e8fe256-7985-447b-84bd-3cc5d7b50476

Code quality improvements example: Change floatval() to (float)

Before

						
function run($parameter)
{
	echo floatval($parameter);
}
						
					

After

						
function run($parameter)
{
	echo (float) $parameter;
}
						
					
Ref https://getrector.com/demo/46c8a393-e970-4e0d-ac59-fe07d55ee1cf

Type coverages example: Add return type for bool return

Before

						
function run($parameter)
{
	return $parameter === 1;
}
						
					

After

						
function run($parameter): bool
{
	return $parameter === 1;
}
						
					
Ref https://getrector.com/demo/720c19a2-81ae-4a90-93fa-a114494227ce

nikic/PHP-Parser overview

A PHP parser written in PHP, generating AST(Abstract Syntax Tree), hierarchical data structure that represent structure of program.

Fundamental unit within tree is named .

Node tree Structure


Common usage of node can be seen at https://github.com/rectorphp/php-parser-nodes-docs

PHPStan overview

Static analysis tool for PHP codebases.

PHPStan uses the AST generated by nikic/PHP-Parser to traverse through the code's structure, inspecting various nodes (such as function calls, variable assignments, etc.) and their associated types.

How RectorPHP works

  • Finds all files and Load Configured Rectors
  • Parse and Reconstruct 1 File
  • Report

Ref https://getrector.com/documentation/how-rector-works

RectorPHP example usage in a PHP framework

Let's try in Laravel framework
							
composer create-project laravel/laravel example-app-laravel
cd example-app-laravel && git init && git add . && git commit -m 'First commit' -a
composer require --dev rector/rector
							
						

Start RectorPHP configuration

							
➜  vendor/bin/rector

No "rector.php" config found. Should we generate it for you? [yes]:
> yes


[OK] The config is added now. Re-run command to make Rector do the work!
							
						

Get Generated rector.php config

							
<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;

return static function (RectorConfig $rectorConfig): void {
	$rectorConfig->paths([
		__DIR__ . '/app',
		__DIR__ . '/bootstrap',
		__DIR__ . '/config',
		__DIR__ . '/public',
		__DIR__ . '/resources',
		__DIR__ . '/routes',
		__DIR__ . '/tests',
	]);

	// register a single rule
	$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);

	// define sets of rules
	//    $rectorConfig->sets([
	//        LevelSetList::UP_TO_PHP_81
	//    ]);
};
							
						

Register Laravel Bootstrap

							
// rector.php
$rectorConfig->bootstrapFiles([__DIR__ . '/bootstrap/app.php']);

							
						

Uncomment Generated RectorPHP Configuration

							
// rector.php
$rectorConfig->sets([
	LevelSetList::UP_TO_PHP_81
]);
							
						

Skip cache path

							
// rector.php
$rectorConfig->skip([
	__DIR__ . '/bootstrap/cache',
]);

							
						

Run vendor/bin/rector --dry-run: ensure change valid

Run vendor/bin/rector: apply change

Commit, add more set lists, verify, repeat!

References