How to Update to PHP 7.4 Typed Property Without BC Break with Rector
In Rector 0.12.9, `TypedPropertyRector` is not configurable, it will only change:
– private property
– protected property on final class without extends
In Rector 0.12.16, `TypedPropertyRector` has configurable to allow change protected and public modifier as well as far when possible with new configurable:
$services->set(TypedPropertyRector::class) ->configure([ TypedPropertyRector::INLINE_PUBLIC => true, ]);
This Article is valid for Rector <= 0.12.8
Typed Property is one of the PHP 7.4 feature that allow to write that previously like this:
namespace Lib;
class SomeClass
{
/** @var int */
public $a;
/** @var string */
protected $b;
/** @var bool */
private $c;
}
to this:
namespace Lib;
class SomeClass
{
public int $a;
protected string $b;
private bool $c;
}
If you follow Semver for versioning, and you don’t want to update to major change, eg: version 1 to version 2, changing this will make Break Backward Compatibility, for example:
namespace Lib;
class SomeClass
{
protected string $b;
}
has child in application consumer:
namespace App;
use Lib\SomeClass;
class AChild extends SomeClass
{
protected $b;
}
will result a fatal error:
Fatal error: Type of AChild::$b must be string (as in class SomeClass)
see https://3v4l.org/X9Yvd . To avoid that, you should only change to private modifier only, so, the change will only to private property:
namespace Lib;
class SomeClass
{
/** @var int */
public $a;
/** @var string */
protected $b;
- /** @var bool */
- private $c;
+ private bool $c;
}
Want to automate that? You can use Rector for it. First, let say, we have a re-usable package that can be consumed in our applications, with the following package structure:
lib ├── composer.json ├── composer.lock ├── src │ └── SomeClass.php
with composer.json config like this:
{
"require": {
"php": "^7.4"
},
"autoload": {
"psr-4": {
"Lib\\": "src/"
}
}
}
Your package will be hosted in packagist or your own server.
Now, what you need is require the rector as dev dependency by go to lib directory:
cd lib/ composer require --dev rector/rector
Rector has rule named TypedPropertyRector, that part of SetList::PHP_74 constant.
It default will update all modifiers:
- public
- protected
- private
If you are using on projects that not re-usable project, you can just use SetList::PHP_74 constant as is.
For our use case, you can override it by configure it to only apply to private property only.
You can create a rector.php configuration inside the root of lib directory as follow:
<?php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Php74\Rector\Property\TypedPropertyRector;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PATHS, [
__DIR__ . '/src'
]);
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74);
// import php 7.4 set list for php 7.4 features
$containerConfigurator->import(SetList::PHP_74);
// set Typed Property only for private property
$services = $containerConfigurator->services();
$services->set(TypedPropertyRector::class)
->call('configure', [[
TypedPropertyRector::PRIVATE_PROPERTY_ONLY => true,
]]);
};
Above, we import php 7.4 set list, with configured TypedPropertyRector for update to typed property to only change private property only.
Now, let’s run rector to see the diff and verify:
cd lib vendor/bin/rector --dry-run

Everything seems correct! Let’s apply the change:
cd lib vendor/bin/rector

Now, you have typed property in your code!
That’s it!
How to Create Typo Variable Fixer with Rector
Rector is a code refactoring tool that can help us with major code changes (like upgrade legacy code) or daily work. There are already many rules that ready to use for us.
What if we want a custom rule, like we want a daily work can to do “Typo” check in variables? In this post, I want to show you how to create a Typo Variable Fixer with Rector, a custom Rector rule!
Preparation
First, let say, we build a new app, we use composer for it with add rector/rector to require-dev:
composer init Welcome to the Composer config generator This command will guide you through creating your composer.json config. Package name (<vendor>/<name>) [samsonasik/how-to-create-typo-variable-fixer]: samsonasik/app Description []: App Demo Author [Abdul Malik Ikhsan <[email protected]>, n to skip]: Minimum Stability []: Package Type (e.g. library, project, metapackage, composer-plugin) []: License []: MIT Define your dependencies. Would you like to define your dependencies (require) interactively [yes]? no Would you like to define your dev dependencies (require-dev) interactively [yes]? yes Search for a package: rector/rector Enter the version constraint to require (or leave blank to use the latest version): Using version ^0.8.40 for rector/rector Search for a package: { "name": "samsonasik/app", "description": "App Demo", "require-dev": { "rector/rector": "^0.8.40" }, "license": "MIT", "authors": [ { "name": "Abdul Malik Ikhsan", "email": "[email protected]" } ], "require": {} } Do you confirm generation [yes]? yes Would you like to install dependencies now [yes]? yes
After it, let say we need an app directory, we can create an app directory and write a php file inside it:
mkdir -p app && touch app/app.php
with file app/app.php content:
<?php namespace App; $previuos = 0; $begining = 1; $statment = $previuos . ' is lower than ' . $begining;
Yes, there are 3 typos in above file! For example, we will have a sample library.php file for common typos, for example, inside utils directory:
mkdir -p utils && touch utils/library.php
with file utils/library.php content:
<?php
namespace Utils;
return [
'previous' => ['previuos', 'previuous'],
'beginning' => ['begining', 'beginign'],
'statement' => ['statment'],
];
We can setup composer autoload for with add the following to our composer.json file:
"autoload": {
"psr-4": {
"App\\": "app"
}
},
"autoload-dev": {
"psr-4": {
"Utils\\": "utils"
}
}
After it, run composer dump-autoload command:
composer dump-autoload
The preparation is done!
Create the Typo Fixer Rule
We can follow the Rector‘s documentation to create new custom rule. So, for example, we create TypoVariableFixerRule under own utils/Rector directory:
mkdir -p utils/Rector && touch utils/Rector/TypoVariableFixerRule.php
Our directory will looks like the following:
. ├── app │ └── app.php ├── composer.json ├── utils │ ├── Rector │ │ └── TypoVariableFixerRule.php │ └── library.php
Now, we can start create the TypoVariableFixerRule:
<?php
declare(strict_types=1);
namespace Utils\Rector;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
final class TypoVariableFixerRule extends AbstractRector
{
public function getNodeTypes(): array
{
return [Variable::class];
}
/**
* @param Variable $node
*/
public function refactor(Node $node): ?Node
{
return $node;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Change Typo in variable', [
new CodeSample(
// code before
'$previuos',
// code after
'$previous'
),
]
);
}
}
Above, we extends AbstractRector for new Rector rule. We operate with nikic/php-parser to do refactor. The getNodeTypes returns the node that we want to refactor, at this case, we want to refactor Variable node in our refactor method.
Before we continue, let’s register our new TypoVariableFixerRule to rector config to ensure it works. We can create rector config as follow:
touch rector.php
with file rector.php content:
<?php
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Utils\Rector\TypoVariableFixerRule;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PATHS, [__DIR__ . '/app']);
$services = $containerConfigurator->services();
$services->set(TypoVariableFixerRule::class);
};
and test with run:
vendor/bin/rector process
So we see the “Green” OK:

Now, time to make refactor work! We can modify the refactor method:
public function refactor(Node $node): ?Node
{
// get the variable name
$variableName = $this->getName($node);
// get the library content
$library = include 'utils/library.php';
foreach ($library as $correctWord => $commonTypos) {
if (! in_array($variableName, $commonTypos, true)) {
continue;
}
$node->name = $correctWord;
return $node;
}
return null;
}
Above, we find if the variable name is in common typos, then we return node (as variable) with updated its name with the correct word. Now, let’s run it with --dry-run to see the diff that can be made:
vendor/bin/rector process --dry-run
and we can see:

Seems great! Let’s apply the changes:
vendor/bin/rector process

Awesome! We now already make typo fixer succesfully working! Let’s run again, and it will take no effect as already fixed:

That’s it!
leave a comment