Intro to RectorPHP

php[tek] 2025 əwesome

by cabbey (he/him)
20 May 2025

Mastodon cabbey@phpc.social

github: cabbey

intro cabbey

Intro to cabbey

cabbey Photo by Chris MacAskill

Disclaimers

I'm not a member of the RectorPHP project.

I've submitted 1 patch to the project. (so far)

intro awesome

Intro to Awesome

Building a better world through
the power of photography.

awesome.co
awesome
SmugMug
Flickr

SmugMug Films

Flickr Foundation
TWiP

More to come...

intro rector

!(Intro to RectorPHP)

What isn't it?

  • A Framework
  • AI/ML/LLM!

What IS it?

A tool for the automated
transformation of source code
via configurable rules.

it's a tool

What IS it?

A tool for the automated
transformation of source code
via configurable rules.

Getting Rector

Websites

  1. GetRector.com
    • looking up rules/sets
    • general docs
  2. github.com/rectorphp/rector
    • install docs
    • detailed tool usage

Install

composer require rector/rector --dev

Install (alternate approach)

Make a separate repo just to
keep all your Rector files in.

Composer install in that
repo just like normal.

Usage

  1. Install Rector
  2. Create a config file
  3. vendor/bin/rector process path/to/code --dry-run
  4. vendor/bin/rector process path/to/code
  5. Commit the changes

Config file

							
use Rector\Config\RectorConfig;
use Rector\Php83\Rector\FuncCall\RemoveGetClassGetParentClassNoArgsRector;

return RectorConfig::configure()
  ->withPaths([
    __DIR__ . '/src',
    __DIR__ . '/tests',
  ])
  ->withRules([
    RemoveGetClassGetParentClassNoArgsRector::class,
  ]);
              
						

Configurable options

							
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  ->withIndent(indentChar: ' ', indentSize: 4);
							
						

Configurable options

							
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  ->withPaths([
    __DIR__ . '/vendor/something/package',
  ])
  ->withDowngradeSets(php80: true);
							
						

Composer Version Detection

							
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  ->withComposerBased(
    twig: true,
    doctrine: true,
    phpunit: true,
    symfony: true
  );
							
						

Multiple Configurations

							
vendor/bin/rector --config pathTo/YourConfig.php ...
							
						

Example Run

							
          $ vendor/bin/rector process \
                --config example/uselogging-config.php \
                --dry-run
          1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
          1 file with changes
          ===================

          1) example/UsesLogging.php:3

          ---------- begin diff ----------
          @@ @@
          declare(strict_types=1);

          class UsesLogging {
          -    use \YeOldeLib\Log\LogAware;
          +    use \Shiny\NewThing\Logging\LoggerAwareTrait;
          }
          ----------- end diff -----------

          Applied rules:
          * MigrateToModernLogging

          [OK] 1 file would have been changed (dry-run) by Rector

							
						

What IS it?

A tool for the automated
transformation of source code
via configurable rules.

AST Talk

What IS it?

A tool for the automated
transformation of source code

via configurable rules.

Intro AST

Abstract Symbol Trees

ASTs have

  • nodes
  • links
									  graph TD
										A[Constant: 42]
									
									
										42
									
								
									  graph TD
										A[Variable: $item]
									
									
										$item
									
								
									  graph TD
										A(Operator: return) -- value --> B[Variable: $results]
									
									
										return $results;
									
								
									  graph TD
										A(Operator: +) -- Left --> B[Variable: $item]
										A(Operator: +) -- Right --> C[Constant: 42]
									
									
										$item + 42
									
								
									  graph TD
										A(Operator: Assignment) -- Left --> E[Variable: $item]
										A(Operator: Assignment) -- Right --> B(Operator: +)
										B(Operator: +) -- Left --> C[Variable: $item]
										B(Operator: +) -- Right --> D[Constant: 42]
									
									
										$item = $item + 42
									
								
									  graph TD
										A(Branch) -- Conditional --> B((Comp: ≥))
										B((Comp: ≥)) -- Left --> C[Variable:
										$count]
										B((Comp: ≥)) -- Right --> D[Constant:
										10]
										A(Branch) -- True --> E(MethodCall)
										A(Branch) -- False --> F(StaticCall)
										E(MethodCall) -- Variable --> G[Variable:
										$this]
										E(MethodCall) -- Method --> H[Identifier:
										doSomething]
										E(MethodCall) -- Argument --> I[Variable:
										$count]
										F(StaticCall) -- Class --> J[Class:
										Upsell]
										F(StaticCall) -- Method --> K[Identifier:
										begForMore]
										F(StaticCall) -- Argument --> L[empty]
									
									
										if ($count >= 10) {
											$this->doSomething($count);
										} else {
											Upsell::begForMore();
										}
									
								
							
								PhpParser\Node\Stmt\If_(
									cond: PhpParser\Node\Expr\BinaryOp\GreaterOrEqual(
										left: PhpParser\Node\Expr\Variable( name: "count" )
										right: PhpParser\Node\Scalar\Int_( value: 10 )
									)
									stmts: [
										0: PhpParser\Node\Stmt\Expression(
											expr: PhpParser\Node\Expr\MethodCall(
												var: PhpParser\Node\Expr\Variable( name: "this" )
												name: PhpParser\Node\Identifier( name: "doSomething" )
												args: [
													0: PhpParser\Node\Arg(
														name: null
														value: PhpParser\Node\Expr\Variable( name: "count" )
														byRef: false
														unpack: false
													)
												]
											)
										)
									]
									elseifs: []
									else: PhpParser\Node\Stmt\Else_(
										stmts: [
											0: PhpParser\Node\Stmt\Expression(
												expr: PhpParser\Node\Expr\StaticCall(
													class: PhpParser\Node\Name\FullyQualified( parts: ["Upsell"] )
													name: PhpParser\Node\Identifier( name: "begForMore" )
													args: []
												)
											)
										]
									)
								)
							
						
That seems like a lot of code to maintain just for this project, surely it's going to be buggy!

Nope! They're leveraging the same library that PHPStan uses: PhpParser by nikic!

There are 128 OTHER contributors in the GH repo, with the #2 spot being one of the leads of RectorPHP, #3 being a php-src contributor, #4 being a lead from PHPStan, etc...

Using an AST
to change code

(a hypothetical example)

Order of Operations

  1. Parse the code into an AST
  2. Find the nodes you want to change
  3. Change the nodes
  4. Convert the AST back into code
									
										if ($count >= 10) {
											$this->doSomething($count);
										} else {
											Upsell::begForMore();
										}
									
								
									
										if ($count >= 10) {
											$this->doSomething($count);
										} else {
											$this->handleSmallCarts();
										}
									
								
								  graph TD
									A(Branch) -- Conditional --> B((Comp: ≥))
									B((Comp: ≥)) -- Left --> C[Variable:
									$count]
									B((Comp: ≥)) -- Right --> D[Constant:
									10]
									A(Branch) -- True --> E(MethodCall)
									A(Branch) -- False --> F(StaticCall)
									E(MethodCall) -- Variable --> G[Variable:
									$this]
									E(MethodCall) -- Method --> H[Identifier:
									doSomething]
									E(MethodCall) -- Argument --> I[Variable:
									$count]
									F(StaticCall) -- Class --> J[Class:
									Upsell]
									F(StaticCall) -- Method --> K[Identifier:
									begForMore]
									F(StaticCall) -- Argument --> L[empty]
								
								  graph TD
									A(Branch) -- Conditional --> B((Comp: ≥))
									B((Comp: ≥)) -- Left --> C[Variable:
									$count]
									B((Comp: ≥)) -- Right --> D[Constant:
									10]
									A(Branch) -- True --> E(MethodCall)
									A(Branch) -- False --> F(StaticCall)
									E(MethodCall) -- Variable --> G[Variable:
									$this]
									E(MethodCall) -- Method --> H[Identifier:
									doSomething]
									E(MethodCall) -- Argument --> I[Variable:
									$count]
									F(StaticCall) -- Class --> J[Class:
									Upsell]
									F(StaticCall) -- Method --> K[Identifier:
									begForMore]
									F(StaticCall) -- Argument --> L[empty]
									classDef ignored fill:#eee8d5,stroke:#eee8d5,edgeLabelBackground:#eee8d5;
									class A,B,C,D,E,G,H,I ignored;
								
								  graph TD
									F(StaticCall) -- Class --> J[Class:
									Upsell]
									F(StaticCall) -- Method --> K[Identifier:
									begForMore]
									F(StaticCall) -- Argument --> L[empty]
								
								  graph TD
									F(MethodCall) -- Class --> J[Class:
									Upsell]
									F(MethodCall) -- Method --> K[Identifier:
									begForMore]
									F(MethodCall) -- Argument --> L[empty]
									classDef error fill:#dc322f
									class J error
								
								  graph TD
									F(MethodCall) -- Variable --> J[Variable:
									$this]
									F(MethodCall) -- Method --> K[Identifier:
									handleSmallCarts]
									F(MethodCall) -- Argument --> L[empty]
								
								  graph TD
									A(Branch) -- Conditional --> B((Comp: ≥))
									B((Comp: ≥)) -- Left --> C[Variable:
									$count]
									B((Comp: ≥)) -- Right --> D[Constant:
									10]
									A(Branch) -- True --> E(MethodCall)
									A(Branch) -- False --> F(MethodCall)
									E(MethodCall) -- Variable --> G[Variable:
									$this]
									E(MethodCall) -- Method --> H[Identifier:
									doSomething]
									E(MethodCall) -- Argument --> I[Variable:
									$count]
									F(MethodCall) -- Variable --> J[Variable:
									$this]
									F(MethodCall) -- Method --> K[Identifier:
									handleSmallCarts]
									F(MethodCall) -- Argument --> L[empty]
									classDef ignored fill:#eee8d5,stroke:#eee8d5,edgeLabelBackground:#eee8d5;
									class A,B,C,D,E,G,H,I ignored;
								
								  graph TD
									A(Branch) -- Conditional --> B((Comp: ≥))
									B((Comp: ≥)) -- Left --> C[Variable:
									$count]
									B((Comp: ≥)) -- Right --> D[Constant:
									10]
									A(Branch) -- True --> E(MethodCall)
									A(Branch) -- False --> F(MethodCall)
									E(MethodCall) -- Variable --> G[Variable:
									$this]
									E(MethodCall) -- Method --> H[Identifier:
									doSomething]
									E(MethodCall) -- Argument --> I[Variable:
									$count]
									F(MethodCall) -- Variable --> J[Variable:
									$this]
									F(MethodCall) -- Method --> K[Identifier:
									handleSmallCarts]
									F(MethodCall) -- Argument --> L[empty]
								
								
									if ($count >= 10) {
										$this->doSomething($count);
									} else {
										$this->handleSmallCarts();
									}
								
							
intro rules and sets

What IS it?

A tool for the automated
transformation of source code

via configurable rules.

Rules

PHP 8.3 deprecations

Calling get_class() and get_parent_class() without arguments is now deprecated.

							
 class Example extends StdClass {
   public function whoAreYou() {
-    return get_class() . ' daughter of ' . get_parent_class();
+    return self::class . ' daughter of ' . parent::class;
   }
 }
							
						

Rector has a rule for that!

							
use Rector\Config\RectorConfig;
use Rector\Php83\Rector\FuncCall\RemoveGetClassGetParentClassNoArgsRector;

return RectorConfig::configure()
  ->withRules([
    RemoveGetClassGetParentClassNoArgsRector::class,
  ]);
							
						

Rules need input

Occasionally you'll have a rule change that needs some input to define or control what it does.

							
-$value = SomeClass::OLD_CONSTANT;
-$value = SomeClass::OTHER_OLD_CONSTANT;
+$value = SomeClass::NEW_CONSTANT;
+$value = DifferentClass::NEW_CONSTANT;
							
						

Rector has a rule (and a system) for that!

							
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
use Rector\Renaming\ValueObject\RenameClassConstFetch;
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;

return RectorConfig::configure()
  ->withConfiguredRule(
    RenameClassConstFetchRector::class,
    [
      new RenameClassConstFetch(
        'SomeClass', 'OLD_CONSTANT', 'NEW_CONSTANT'
	    ),
	    new RenameClassAndConstFetch(
        'SomeClass', 'OTHER_OLD_CONSTANT',
        'DifferentClass', 'NEW_CONSTANT'
	    ),
    ]
  );
							
						

Rector has a rule (and a system) for that!

							
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
use Rector\Renaming\ValueObject\RenameClassConstFetch;
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;

return RectorConfig::configure()
  ->withConfiguredRule(
    RenameClassConstFetchRector::class,
    [
      new RenameClassConstFetch(
        SomeClass::class, 'OLD_CONSTANT', 'NEW_CONSTANT'
	    ),
	    new RenameClassAndConstFetch(
        SomeClass::class, 'OTHER_OLD_CONSTANT',
        DifferentClass::class, 'NEW_CONSTANT'
	    ),
    ]
  );
							
						

Code as Config FTW!

							
$todo = [];
$reflector = new ReflectionClass(\YeOlde\Lib\Thing::class);
foreach ($reflector->getConstants() as $const) {
  $newHotness = new ReflectionClassConstant(
    \Shiny\New\Thing::class,
    $const->getName(),
  );
  $todo[] = new RenameClassAndConstFetch(
    $const->getDeclaringClass()->getName(),
    $const->getName(),
    $newHotness->getDeclaringClass()->getName(),
    $newHotness->getName()
  );
}
return RectorConfig::configure()
  ->withConfiguredRule(
    RenameClassConstFetchRector::class,
    $todo
  );
							
						

Code as Config FTW!

							
$todo = [];
$reflector = new ReflectionClass(\YeOlde\Lib\Thing::class);
$classes = [
  \Shiny\New\Thing::class,
  \Shiny\New\OtherThing::class,
  \Shiny\New\ThirdThing::class,
];
foreach ($reflector->getConstants() as $const) {
  $constString = $const->getName();
  $possibleConsts = [$constString];
  while ($start = strpos($constString, '_')) {
    $constString = substr($constString, $start + 1);
    $possibleConsts[] = $constString;
  };
  foreach ($classes as $class) {
    foreach ($possibleConsts as $possibleConst) {
      $newHotness = new ReflectionClassConstant(
        $class,
        $possibleConst
      );
      if (null !== $newHotness) {
        break 2;
      }
    }
  }
  $todo[] = new RenameClassAndConstFetch(
    $const->getDeclaringClass()->getName(),
    $const->getName(),
    $newHotness->getDeclaringClass()->getName(),
    $newHotness->getName()
  );
}
return RectorConfig::configure()
  ->withConfiguredRule(
    RenameClassConstFetchRector::class,
    $todo
  );
							
						

Sets

Rector has pre-defined sets of rules for common tasks.

For example, the PHP_83 set includes all the rules for deprecations and new features in PHP 8.3.

							
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
  ->withSets([SetList::PHP_83]);
							
						

Sets

Rector has pre-defined sets of rules for common tasks.

For example, the PHP_83 set includes all the rules for deprecations and new features in PHP 8.3.

							
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
  ->withPhpSets(php83: true);
							
						

Built-in Sets

Rector ships with rules for basic PHP version migration, and for some frameworks and tools.

For example: Twig, Symfony, Doctrine, PHPUnit.

Built-in Sets

Rector also ships with rules for code quality cleanups.

PHPUnit

							
use Rector\Config\RectorConfig;
use Rector\PHPUnit\Set\PHPUnitSetList;

return RectorConfig::configure()
  ->withSets([
    PHPUnitSetList::PHPUNIT_90,
  ]);
              
						

What is a Set list?

							
use Rector\Config\RectorConfig;
use Rector\PHPUnit\PHPUnit100\Rector\StmtsAwareInterface\WithConsecutiveRector;
use Rector\PHPUnit\PHPUnit90\Rector\Class_\TestListenerToHooksRector;
use Rector\PHPUnit\PHPUnit90\Rector\MethodCall\ExplicitPhpErrorApiRector;
use Rector\PHPUnit\PHPUnit90\Rector\MethodCall\SpecificAssertContainsWithoutIdentityRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;

return static function (RectorConfig $rectorConfig): void {
  $rectorConfig->rules([
    TestListenerToHooksRector::class,
    ExplicitPhpErrorApiRector::class,
    SpecificAssertContainsWithoutIdentityRector::class,
    WithConsecutiveRector::class,
  ]);

  $rectorConfig->ruleWithConfiguration(
    RenameMethodRector::class,
    [
      new MethodCallRename(
        PHPUnit\Framework\TestCase::class,
        'expectExceptionMessageRegExp',
        'expectExceptionMessageMatches'
      ),
    ]
  );
};
              
						
							
return static function (RectorConfig $rectorConfig): void {
  $rectorConfig->sets([
    PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES
  ]);

  $rectorConfig->rules([
    StaticDataProviderClassMethodRector::class,
    PublicDataProviderClassMethodRector::class,
    AddProphecyTraitRector::class,
    WithConsecutiveRector::class,
    RemoveSetMethodsMethodCallRector::class,
    PropertyExistsWithoutAssertRector::class,
    ParentTestClassConstructorRector::class,
  ]);

  $rectorConfig->ruleWithConfiguration(
    RenameMethodRector::class,
    [
      // https://github.com/sebastianbergmann/phpunit/issues/4087
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertRegExp',
        'assertMatchesRegularExpression'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/5220
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertObjectHasAttribute',
        'assertObjectHasProperty'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertObjectNotHasAttribute',
        'assertObjectNotHasProperty'
      ),

      new MethodCallRename(
        'PHPUnit\Framework\MockObject\Rule\InvocationOrder',
        'getInvocationCount',
        'numberOfInvocations'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4090
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertNotRegExp',
        'assertDoesNotMatchRegularExpression'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4078
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertFileNotExists',
        'assertFileDoesNotExist'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4081
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertFileNotIsReadable',
        'assertFileIsNotReadable'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4072
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertDirectoryNotIsReadable',
        'assertDirectoryIsNotReadable'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4075
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertDirectoryNotIsWritable',
        'assertDirectoryIsNotWritable'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4069
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertDirectoryNotExists',
        'assertDirectoryDoesNotExist'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4066
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertNotIsWritable',
        'assertIsNotWritable'
      ),

      // https://github.com/sebastianbergmann/phpunit/issues/4063
      new MethodCallRename(
        'PHPUnit\Framework\Assert',
        'assertNotIsReadable',
        'assertIsNotReadable'
      ),

      // https://github.com/sebastianbergmann/phpunit/pull/3687
      new MethodCallRename(
        'PHPUnit\Framework\MockObject\MockBuilder',
        'setMethods',
        'onlyMethods'
      ),

      //https://github.com/sebastianbergmann/phpunit/issues/5062
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectDeprecationMessage',
        'expectExceptionMessage'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectDeprecationMessageMatches',
        'expectExceptionMessageMatches'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectNoticeMessage',
        'expectExceptionMessage'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectNoticeMessageMatches',
        'expectExceptionMessageMatches'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectWarningMessage',
        'expectExceptionMessage'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectWarningMessageMatches',
        'expectExceptionMessageMatches'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectErrorMessage',
        'expectExceptionMessage'
      ),
      new MethodCallRename(
        'PHPUnit\Framework\TestCase',
        'expectErrorMessageMatches',
        'expectExceptionMessageMatches'
      ),
  ]);
};
              
						

Levels

Many of the sets have hundreds of changes in them.

Levels let you gradually apply those gradually.

			  				
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  ->withPreparedSets(
    typeDeclarations: true
  );
                
              
                
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  //->withPreparedSets(typeDeclarations: true)
  ->withTypeCoverageLevel(0);
                
  						
                
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  //->withPreparedSets(typeDeclarations: true)
  ->withTypeCoverageLevel(1);
                
  						
                
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  //->withPreparedSets(typeDeclarations: true)
  ->withTypeCoverageLevel(2);
                
  						
                
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  //->withPreparedSets(typeDeclarations: true)
  ->withTypeCoverageLevel(3);
                
  						
                
use Rector\Config\RectorConfig;

return RectorConfig::configure()
  //->withPreparedSets(typeDeclarations: true)
  ->withTypeCoverageLevel(getenv('LEVEL'));
                
  						

Community Sets

Lots of community packages and projects have rector rule sets and even custom rules specifically for their users. Ex: Drupal, cakephp, CraftCMS, Laminas...

What IS it?

A tool for the automated
transformation of source code

via configurable rules.

Caveats

What's Not To Love?!

Caveat Emptor

Finding Rules

GetRector.com/find-rule

Formatting Chaos

Use a tool to auto format your code. All of it.

Do this before and after using rector.
And probably in your commit flow.

Anything with old school mix of php and
html is going to be a headache.

Highly Opinionated Imports

You can either have full names everywhere (default) or you can have everything as short names with use.

Workaround is to leave rector set to full names and use something else to refactor them more intelligently.
I suggest PHPStorm's import refactor methods.

This may not be in their control without a fair amount of work on PHP-Parser... and whatever is being used for codegen.

                
                  namespace Project\MyComponent\SomePortion;

                  use Project\OtherComponent;
                  use Project\ThirdThing;
                  ...
                  class ComponentObject {
                    private OtherComponent\ComponentObject $otherThingOne;
                    private ThirdThing\ComponentObject $otherThingTwo;
                  ...
                    public function doAllTheThings() {
                      $this->otherThingOne->doSomething();
                      $this->otherThingTwo->doSomething();
                    }
                
              
                
                    public function doAllTheThings() {
                  -    $this->otherThingOne->doSomething();
                  -    $this->otherThingTwo->doSomething();
                  +    $this->otherThingOne->doSomethingElse();
                  +    $this->otherThingTwo->doSomethingElse();
                    }
                
              
                
                  namespace Project\MyComponent\SomePortion;

                  use Project\OtherComponent;
                  use Project\ThirdThing;
                  ...
                  class ComponentObject {
                    private \Project\OtherComponent\ComponentObject $otherThingOne;
                    private \Project\ThirdThing\ComponentObject $otherThingTwo;
                  ...
                    public function doAllTheThings() {
                      $this->otherThingOne->doSomethingElse();
                      $this->otherThingTwo->doSomethingElse();
                    }
                
              
                
                  namespace Project\MyComponent\SomePortion;

                  use Project\OtherComponent\ComponentObject;
                  use Project\ThirdThing\ComponentObject;
                  ...
                  class ComponentObject {
                    private ComponentObject $otherThingOne;
                    private ComponentObject $otherThingTwo;
                  ...
                    public function doAllTheThings() {
                      $this->otherThingOne->doSomethingElse();
                      $this->otherThingTwo->doSomethingElse();
                    }
                
              
                
                  namespace Project\MyComponent\SomePortion;

                  use Project\OtherComponent\ComponentObject as ProjectOtherComponentComponentObject;
                  use Project\ThirdThing\ComponentObject as ProjectThirdThingComponentObject;
                  ...
                  class ComponentObject {
                    private ProjectOtherComponentComponentObject $otherThingOne;
                    private ProjectThirdThingComponentObject $otherThingTwo;
                  ...
                    public function doAllTheThings() {
                      $this->otherThingOne->doSomethingElse();
                      $this->otherThingTwo->doSomethingElse();
                    }
                
              
                
                  namespace Project\MyComponent\SomePortion;

                  use Project\OtherComponent;
                  use Project\ThirdThing;
                  ...
                  class ComponentObject {
                    private OtherComponent\ComponentObject $otherThingOne;
                    private ThirdThing\ComponentObject $otherThingTwo;
                  ...
                    public function doAllTheThings() {
                      $this->otherThingOne->doSomethingElse();
                      $this->otherThingTwo->doSomethingElse();
                    }
                
              

Questionable Comments

                
                  /**
                   * @var string $noSoupForYou yada yada yada
                   */
                  private string $noSoupForYou;

                  public function __construct($noSoupForYou) {
                    $this->noSoupForYou = $noSoupForYou;
                  }
                
              
                
                   /**
                    * @param string $noSoupForYou yada yada yada
                    */
                   public function __construct(
                     private string $noSoupForYou,
                   ) {
                
              
                
                    public function __construct(
                      /**
                       * yada yada yada
                       */
                      private string $noSoupForYou,
                    ) {
                
              

Heavy Sales Push

screen shot of get rector dot com screen shot of get rector dot com screen shot of get rector dot com screen shot of get rector dot com
my usage suggestions

Path to Success

  1. Have Tests First
  2. Auto-format your code
  3. Refactor in small steps
  4. Commit Frequently
  5. Use other tools to clean up
  6. Retest at ever step

Have tests first

Unit tests, Functional tests, Regression Tests, Acceptance Tests, End-to-end Tests

The more test coverage the better!

Auto-format your code

Try to separate formatting changes
from actual code logic changes.

Refactor in small steps

Just because you're not doing the grunt work of making all the changes doesn't mean you can ignore the cognitive load of understanding what the tools are doing!

You still need to be able to think about and reason around the changes.

Commit Frequently

Otherwise, there is little point to using small steps.

Use other tools to clean up

This is just ONE tool in the toolbox...
remember you still have all the others.

But to try to keep manual and
automated changes separated!

And document how to make the
change again in the commit message.

Retest at every step

The whole point of the tests is to confirm you aren't breaking anything. So periodically run them again and confirm the results are the same.

Profit!

Thanks

Thanks to:

Questions?

Thoughts?

Mastodon: cabbey@phpc.social