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!
leave a comment