If you are looking to make your customizations, not packaged in an MLP, compatible with different versions of PHP, this guide is for you.
You will take your customization code and add to the Sugar instance where Rector is and git has been initialized.
You need to have a inventory of your assets to create your git/rector config.
Prerequisites:
- Basic Setup
- Customization Assets Inventory (file path of your assets)
- A vanila instance if you don't have an inventory
Create your Customization Inventory
If you already have your customization inventory, great! you can skip this section.
There are few different ways of achieving the same result, we are going to use a vanila instance as our basis for git comparison and your current "deployed
" instance with your code installed in the filesystem.
In your vanila instance, you will follow the steps from our Basic Setup and have git init and commit.
Once you have that in place, (git status
should return empty results), you are good to go on copying all the files from your deployed
instance to the vanila one.
* it is important to have the exact same version of Sugar, otherwise you'll have "noise" in the vendors folder and other files that aren't necessarily your custom code but Sugar release related files and those can be ignored if found.
After it has been installed, execute git status
commands to see which files were updated/added by Sugar:
sh-3.2$ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: config_override.php modified: custom/application/Ext/Include/modules.ext.php Untracked files: (use "git add <file>..." to include in what will be committed) custom/Extension/application/Ext/Include/xxxx.php custom/Extension/application/Ext/Include/orderMapping.php custom/modules/ActivityStream/ custom/modules/Forecasts/Ext/clients/ custom/modules/pmse_Project/ no changes added to commit (use "git add" and/or "git commit -a")
Prepare your Rector config
Copy the paths, and paste them into the rector.php config file that was generated in the previous step, replacing the default paths in $rectorConfig->paths([
array like this (you can ignore some of the files, like *ext.php
and *orderMapping.php
):
$rectorConfig->paths([ __DIR__ . '/custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php', __DIR__ . '/custom/clients/', __DIR__ . '/custom/modules/ActivityStream/', __DIR__ . '/custom/modules/Forecasts/Ext/clients/', __DIR__ . '/custom/modules/pmse_Project/', ]);
* Please, notice the '/' after __DIR__
constant usage. It is required as __DIR__
does not have a trailing slash.
Modify the rector.php config file to upgrade the code to the newest supported PHP version (in our case, 8.2):
Replace
// register a single rule $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
With
$rectorConfig->phpstanConfig(__DIR__ . '/stan.neon');
Add the following rules:
$rectorConfig->sets([ LevelSetList::UP_TO_PHP_82, SetList::PHP_82, ]);
The following rules should be skipped to prevent known issues with Sugar.
$rectorConfig->skip([ \Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector::class, \Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class, \Rector\Php80\Rector\FunctionLike\UnionTypesRector::class, \Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector::class, \Rector\Php80\Rector\FunctionLike\MixedTypeRector::class, \Rector\Php81\Rector\Property\ReadOnlyPropertyRector::class, \Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class, \Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector::class, \Rector\Php80\Rector\NotIdentical\StrContainsRector::class, \Rector\Php80\Rector\Identical\StrEndsWithRector::class, \Rector\Php80\Rector\Identical\StrStartsWithRector::class, \Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class, \Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class, \Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class, \Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector::class, \Rector\Php81\Rector\Array_\FirstClassCallableRector::class, \Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class, \Rector\Php81\Rector\ClassMethod\NewInInitializerRector::class, \Rector\Php80\Rector\Class_\StringableForToStringRector::class, \Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector::class, \Rector\Php54\Rector\Array_\LongArrayToShortArrayRector::class, \Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector::class, \Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector::class, \Rector\Php74\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector::class, \Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class, \Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class, \Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector::class, \Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector::class, \Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector::class, __DIR__ . '/include/SugarObjects/templates/file/views/view.edit.php', ]);
Full config file example
By following those steps, you should have a rector.php similar to this:
<?php declare(strict_types=1); use Rector\Config\RectorConfig; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php', __DIR__ . '/custom/clients/', __DIR__ . '/custom/modules/ActivityStream/', __DIR__ . '/custom/modules/Forecasts/Ext/clients/', __DIR__ . '/custom/modules/pmse_Project/', ]); $rectorConfig->phpstanConfig(__DIR__ . '/stan.neon'); $rectorConfig->sets([ LevelSetList::UP_TO_PHP_82, SetList::PHP_82, ]); $rectorConfig->skip([ \Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector::class, \Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class, \Rector\Php80\Rector\FunctionLike\UnionTypesRector::class, \Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector::class, \Rector\Php80\Rector\FunctionLike\MixedTypeRector::class, \Rector\Php81\Rector\Property\ReadOnlyPropertyRector::class, \Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class, \Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector::class, \Rector\Php80\Rector\NotIdentical\StrContainsRector::class, \Rector\Php80\Rector\Identical\StrEndsWithRector::class, \Rector\Php80\Rector\Identical\StrStartsWithRector::class, \Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class, \Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class, \Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class, \Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector::class, \Rector\Php81\Rector\Array_\FirstClassCallableRector::class, \Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class, \Rector\Php81\Rector\ClassMethod\NewInInitializerRector::class, \Rector\Php80\Rector\Class_\StringableForToStringRector::class, \Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector::class, \Rector\Php54\Rector\Array_\LongArrayToShortArrayRector::class, \Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector::class, \Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector::class, \Rector\Php74\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector::class, \Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class, \Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class, \Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector::class, \Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector::class, \Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector::class, __DIR__ . '/include/SugarObjects/templates/file/views/view.edit.php', ]); };