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 a few different ways of achieving the same result, we are going to use a vanilla instance as our basis for git comparison, and your current "deployed
" instance with your code installed in the filesystem.
In your vanilla 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 vanilla one.
sh-3.2$ cp -rf /path/to/current/sugar/* /path/to/vanila/sugar/
* 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 copied over, execute git status
commands to get your customization inventory:
sh-3.2$ git status On branch main Untracked files: (use "git add <file>..." to include in what will be committed) modules/FD_SignupFraudDetection/ modules/FD_TransactionFraudDetect/ nothing added to commit but untracked files present (use "git add" to track)
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__ . '/modules/FD_SignupFraudDetection/', __DIR__ . '/modules/FD_TransactionFraudDetect/', ]);
* 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):
Remove/Comment
// register a single rule $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
Specify PHP 8.2 as your phpVersion:
$rectorConfig->phpVersion(PhpVersion::PHP_82);
The following rules should be skipped to prevent known issues with Sugar (it will skip those but will run all the other rules available in your rector version).
$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', ]);
If you'd like to have only the incompatible rules between PHP 7.4 to PHP 8.2, you can use the following:
$rectorConfig->rules([ AddParamBasedOnParentClassMethodRector::class, SetStateToStaticRector::class, Php8ResourceReturnToObjectRector::class, Php81ResourceReturnToObjectRector::class, ArrayKeyExistsOnPropertyRector::class, ExportToReflectionFunctionRector::class, FilterVarToAddSlashesRector::class, MbStrrposEncodingArgumentPositionRector::class, MoneyFormatToNumberFormatRector::class, RealToFloatTypeCastRector::class, RestoreDefaultNullToNullableTypePropertyRector::class, StringifyStrNeedlesRector::class, GetClassOnNullRector::class, ListEachRector::class, ReplaceEachAssignmentWithKeyCurrentRector::class, ParseStrWithResultArgumentRector::class, StringifyDefineRector::class, WhileEachToForeachRector::class, ConsistentImplodeRector::class, CurlyToSquareBracketArrayStringRector::class, ]);
Add safeCount
rule to your config if you are in a supported Sugar version
<?php ... use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; ... return static function (RectorConfig $rectorConfig): void { ... $rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [ 'count' => 'safeCount', ]); ...
Full config file example
By following those steps, you should have a rector.php similar to this to run all Rector rules against your code:
<?php declare(strict_types=1); use Rector\Config\RectorConfig; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/modules/FD_SignupFraudDetection/', __DIR__ . '/modules/FD_TransactionFraudDetect/', ]); $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', ]); $rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [ 'count' => 'safeCount', ]); };
Full config file example (incompatibilities only)
By following those steps, you should have a rector.php similar to this to run incompatible rules between PHP 7.4 to PHP 8.2:
<?php declare(strict_types=1); use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; use Rector\Php72\Rector\Assign\ListEachRector; use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector; use Rector\Php72\Rector\FuncCall\GetClassOnNullRector; use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector; use Rector\Php72\Rector\FuncCall\StringifyDefineRector; use Rector\Php72\Rector\While_\WhileEachToForeachRector; use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector; use Rector\Php74\Rector\Double\RealToFloatTypeCastRector; use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector; use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector; use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector; use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector; use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector; use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector; use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector; use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector; use Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector; use Rector\Config\RectorConfig; use Rector\Core\ValueObject\PhpVersion; use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector; use Rector\Caching\ValueObject\Storage\MemoryCacheStorage; use Rector\Php81\Rector\FuncCall\Php81ResourceReturnToObjectRector; use Rector\Renaming\Rector\FuncCall\RenameFunctionRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/modules/FD_SignupFraudDetection/', __DIR__ . '/modules/FD_TransactionFraudDetect/', ]); $rectorConfig->cacheClass(MemoryCacheStorage::class); $rectorConfig->disableParallel(); $rectorConfig->phpVersion(PhpVersion::PHP_82); $rectorConfig->rules([ AddParamBasedOnParentClassMethodRector::class, SetStateToStaticRector::class, Php8ResourceReturnToObjectRector::class, Php81ResourceReturnToObjectRector::class, ArrayKeyExistsOnPropertyRector::class, ExportToReflectionFunctionRector::class, FilterVarToAddSlashesRector::class, MbStrrposEncodingArgumentPositionRector::class, MoneyFormatToNumberFormatRector::class, RealToFloatTypeCastRector::class, RestoreDefaultNullToNullableTypePropertyRector::class, StringifyStrNeedlesRector::class, GetClassOnNullRector::class, ListEachRector::class, ReplaceEachAssignmentWithKeyCurrentRector::class, ParseStrWithResultArgumentRector::class, StringifyDefineRector::class, WhileEachToForeachRector::class, ConsistentImplodeRector::class, CurlyToSquareBracketArrayStringRector::class, ]); $rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [ 'count' => 'safeCount', ]); };