SugarClub
SugarClub
  • User
  • Site
  • Search
  • User
  • Groups & Discussions
    Groups & Discussions
    • Product Forums
      Product-focused Q&A, discussions, best practices, fixes, and help
      Product Forums
      • Sugar Market
      • Sugar Sell & Enterprise
      • Sugar Serve
      • sales-i
    • User Groups
      Professional, Industry, Language
    • Get Involved
      Learn how to become a Raving Fan
    • Social Club
      Live, interactive, virtual meetups with other Sugar customers and Sugar’s Subject Matter experts!
    • Leadership Lounge
      Network with fellow organizational leaders, ask questions, and share insights
    • Developers
      Visit DevClub, the SugarClub group for Sugar Developers
      Developers
      • DevClub
      • Mobile Developers
      • Developer Builds
        Supplemental access level required. Inquiries: developers@sugarcrm.com
    • Additional Groups (Access Required)
      Groups that require special access will be displayed here. Contact sugarclub@sugarcrm.com for assistance. Click here to see all groups
      Additional Groups (Access Required)
      • SugarCloud Platform
  • Product Information
    Product Information
    • Release Central
      Find release-specific content to prepare for your next Sugar update
    • Documentation & Resources
      Looking to expand your Sugar knowledge? Explore our in-depth documentation and other helpful resources!
    • Product Update Blogs
      Updates about each Sugar product
    • Customer Stories »
      Case Studies by SugarCRM
  • Training & Certification
    Training & Certification
    • Training & Certification Home
      Live & On-Demand classes, Quick Videos, Sugar Certifications, and more!
    • Quick Videos
      Short videos about using Sugar
    • My SugarU Dashboard »
    • SugarU News & Updates
  • Adoption
    Adoption
    • Grow Adoption Framework
      Get started on your adoption journey and review the adoption resources from SugarCRM
  • Calendar
  • News
    News
    • Sugar News
    • SugarCRM.com News »
    • Dev Blog
    • SugarCRM Marketplace Blog
  • Help
    Help
    • Welcome to Sugar!
      New to Sugar? Get started here!
    • SugarClub Help & Instructions
      Learn more about SugarClub and find answers to questions about this site
    • New to SugarClub?
      Start your community journey here
    • Technical Support
      Sugar's support resources
      Technical Support
      • Case Portal »
        Access the SugarCRM Case Portal
      • Working with Sugar Support »
        Find out more about engaging with the SugarCRM Support team
      • SugarCloud Information
        Find information about SugarCloud service updates and site status. Contact sugarclub@sugarcrm.com to request access
  • More from Sugar
    More from Sugar
    • DevClub
    • PartnerClub
    • Support
    • SugarOutfitters Marketplace
    • sugarcrm.com
  • DevClub
  • PartnerClub
  • Support
  • Marketplace
  • sugarcrm.com
DevClub
DevClub
Dev Tutorials Prepare Rector for Customizations
Click here to join this group and curate your SugarClub experience.
  • +On-Boarding Framework
  • +Customization Guides
  • +Modern UI Technical Guide
  • -Automated PHP Compatibility Tool
    • Rector Basic Setup
    • Prepare Rector for MLP/Addon
    • Prepare Rector for Customizations
    • Executing Rector
    • Keeping Compatibility
    • Troubleshooting Rector
    • PHP 7.4 Warnings to PHP8.2 Errors
  • Did you know? Copying related records is a breeze!
  • How to write code for SugarCloud webinar Q&A
  • HOW TO: enforce ACL on Tags
  • Programatically manage Dropdown List
  • Remove custom fields created via package installation
  • Sugar Developer Tools
  • Tutorial:  How to register custom platforms in Sugar instances via Platform extension
  • Adding a google reCAPTCHA in a Web-to-Lead form
  • Sugar Developer Blog Style Guide

You are currently reviewing an older revision of this page.

  • History View current version

Prepare Rector for Customizations

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:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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")
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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):

Fullscreen
1
2
3
4
5
6
7
$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/',
]);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    $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

Fullscreen
1
2
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);

With

Fullscreen
1
$rectorConfig->phpstanConfig(__DIR__ . '/stan.neon');
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$rectorConfig->phpstanConfig(__DIR__ . '/stan.neon');

Add the following rules:

Fullscreen
1
2
3
4
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
SetList::PHP_82,
]);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$rectorConfig->sets([
    LevelSetList::UP_TO_PHP_82,
    SetList::PHP_82,
]);

The following rules should be skipped to prevent known issues with Sugar.

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$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,
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    $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:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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,
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<?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',
    ]);
};