Daniel Siepmann - Coding is Art

Blog Post

Auto migrate PHP code via configuration

Published: , Updated:

Tested with TYPO3: 12 LTS, 11 LTS, 10 LTS

Topics: typo3

Introduction

We need to update software regularly. E.g. because our framework released a new major version containing breaking changes to APIs. It is a cumber stone and hard and boring task, not meant to be done by humans. Luckily we have computers and software we can use to automate boring tasks.

Our example is about TYPO3 and Aimeos updates. TYPO3 is an open source CMS while Aimeos is an open source shop framework. I'll share my experience how to automate the boring tasks during one update and how to use the automation while upgrading further installations.

We will check out rector and show how you can use existing rules with custom configuration to auto mate your code migrations.

Meet rector

rector is an open source PHP framework that allows to auto migrate source code. The project is available at https://getrector.com/ and https://github.com/rectorphp/rector/. At its core it provides so-called rectors that check source code and adjust the source code. Rector itself delivers some rectors already for certain tasks, a full list is available at https://getrector.com/documentation/rules-overview. It includes rules (=rectors) to migrate based on PHP version switches, Symfony, PHPUnit, etc. It is possible to write custom rules (rectors) https://getrector.com/documentation/custom-rule covered with tests.

It is also possible to re-use existing rectors with different configuration. E.g. there is one rector to rename methods. This rector can be configured to migrate method calls for example if a framework switches from getView() to view().

Rector for TYPO3

TYPO3 developers have an easy job, there is already a community project maintaining specific rectors for upgrading TYPO3 versions. You can check the project at https://www.typo3-rector.com/ as well as https://github.com/sabbelasichon/typo3-rector. It comes with a documentation and explains how to copy the template configuration and how to execute the update. I'll not go into much detail about that.

It is possible to extend the boilerplate configuration. That way TYPO3 extensions can provide their own rectors that can be added to your project setup, in order to not only auto migrate source code related to TYPO3 CMS itself, but also to TYPO3 Extensions provided by 3rd Parties.

Rector for Aimeos

Aimeos doesn't have anything regarding rector out of the box. But one can use that as an opportunity to get used to rector. We had one project where we had to upgrade from TYPO3 v8 to v11 and Aimeos 2018 to 2022. We first started with migrations by hand until we realized how much work it is for the concrete code base in that project. So I've started to add rector rules. The result looks like this:

use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;
use Rector\Renaming\ValueObject\RenameStaticMethod;<br>
// Custom Aimeos migration
$rectorConfig->ruleWithConfiguration(RenameClassRector::class, [
    'Aimeos\MShop\Context\Item\Iface' => 'Aimeos\MShop\ContextIface',
]);
$rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
    new MethodCallRename('Aimeos\Admin\JQAdm\Base', 'getContext', 'context'),
    new MethodCallRename('Aimeos\Admin\JQAdm\Base', 'getView', 'view'),
    new MethodCallRename('Aimeos\Base\Mail\Message\Iface', 'setSubject', 'subject'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'createItem', 'create'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'createSearch', 'filter'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'deleteItems', 'delete'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'findItem', 'find'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'saveItem', 'save'),
    new MethodCallRename('Aimeos\MShop\Common\Manager\Iface', 'searchItems', 'search'),
    new MethodCallRename('Aimeos\MShop\ContextIface', 'getCache', 'cache'),
    new MethodCallRename('Aimeos\MShop\ContextIface', 'getConfig', 'config'),
    new MethodCallRename('Aimeos\MShop\ContextIface', 'getLocale', 'locale'),
    new MethodCallRename('Aimeos\MShop\ContextIface', 'getLogger', 'logger'),
]);
$rectorConfig->ruleWithConfiguration(RenameStaticMethodRector::class, [
    new RenameStaticMethod('Aimeos\MShop\Factory', 'createManager', 'Aimeos\MShop', 'create'),
]);
$rectorConfig->ruleWithConfiguration(RenameClassConstFetchRector::class, [
    new RenameClassAndConstFetch('Aimeos\MShop\Common\Item\Address\Base', 'SALUTATION_MISS', 'Aimeos\MShop\Common\Item\Address\Base', 'SALUTATION_MS'),
    new RenameClassAndConstFetch('Aimeos\MShop\Common\Item\Address\Base', 'SALUTATION_MRS', 'Aimeos\MShop\Common\Item\Address\Base', 'SALUTATION_MS'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'ALERT', 'Aimeos\Base\Logger\Iface', 'ALERT'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'CRIT', 'Aimeos\Base\Logger\Iface', 'CRIT'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'DEBUG', 'Aimeos\Base\Logger\Iface', 'DEBUG'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'EMERG', 'Aimeos\Base\Logger\Iface', 'EMERG'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'ERR', 'Aimeos\Base\Logger\Iface', 'ERR'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'INFO', 'Aimeos\Base\Logger\Iface', 'INFO'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'NOTICE', 'Aimeos\Base\Logger\Iface', 'NOTICE'),
    new RenameClassAndConstFetch('Aimeos\MW\Logger\Base', 'WARN', 'Aimeos\Base\Logger\Iface', 'WARN'),
]);

That way we don't have to write actual code but configure rector to migrate method calls, classes, constants, etc. Thanks to rector generic rules.

Acknowledgements

Thanks to werkraum_media who allowed me to do my first Aimeos upgrade in order to get used to configure generic rector rules.

Further reading

Read more about rector:

Read more about TYPO3 rector: