Refactoring PHP Code: A Comprehensive Guide Using VSCode and Rector

As applications grow in complexity, maintaining clean, efficient, and easy-to-understand code becomes increasingly challenging. This is where refactoring comes in—refining code to make it more readable, maintainable, and efficient without changing its external behavior. In the PHP ecosystem, refactoring can be done manually, leveraging powerful tools like Visual Studio Code (VSCode), or automatically using tools like Rector. This blog post will explore both manual refactoring techniques using VSCode and automated refactoring with Rector.

Why Refactor PHP Code?

Before diving into the tools and techniques, it’s important to understand why refactoring is essential:

  1. Improved Code Readability: Refactoring clarifies code, making it easier for developers (including your future self) to understand and maintain.
  2. Enhanced Maintainability: Clean, modular code is easier to modify and extend.
  3. Reduced Technical Debt: Regular refactoring helps prevent the accumulation of poorly written code, which can lead to bugs and slow development.
  4. Optimized Performance: Refactoring can uncover inefficiencies and lead to performance improvements.

Manual Refactoring Techniques with VSCode

Visual Studio Code (VSCode) is a popular code editor that, while lightweight, offers robust support for PHP development. Let’s explore some manual refactoring techniques you can apply using VSCode.

1. Extracting Functions

One of the simplest refactoring techniques is to extract a block of code into a separate function. This can reduce repetition and make your code more modular.

Before Refactoring:

function processOrder($order) {
    // Calculate the total
    $total = 0;
    foreach ($order->items as $item) {
        $total += $item->price * $item->quantity;
    }
    
    // Apply discount
    if ($order->hasDiscount()) {
        $total -= $order->getDiscountAmount();
    }
    
    // Log the order processing
    file_put_contents('order.log', "Processed order with total: $total\n", FILE_APPEND);
    
    return $total;
}

After Refactoring:

function calculateTotal($order) {
    $total = 0;
    foreach ($order->items as $item) {
        $total += $item->price * $item->quantity;
    }
    return $total;
}

function applyDiscount($total, $order) {
    if ($order->hasDiscount()) {
        $total -= $order->getDiscountAmount();
    }
    return $total;
}

function logOrder($total) {
    file_put_contents('order.log', "Processed order with total: $total\n", FILE_APPEND);
}

function processOrder($order) {
    $total = calculateTotal($order);
    $total = applyDiscount($total, $order);
    logOrder($total);
    return $total;
}

VSCode Tips:

  • Quickly Extract Method: Highlight the block of code you want to extract, right-click, and select “Refactor” > “Extract function.” VSCode will automatically create a new function.
  • Rename Symbol: Rename variables, functions, and classes consistently using F2 (Windows) or Fn + F2 (Mac) after selecting the symbol you want to rename.

2. Rename Variables and Methods

Renaming variables and methods to have more meaningful names is a crucial refactoring technique. It makes your code easier to understand and maintain.

Before Refactoring:

function calc($x, $y) {
    return $x * $y;
}

After Refactoring:

function calculateProduct($firstNumber, $secondNumber) {
    return $firstNumber * $secondNumber;
}

VSCode Tips:

  • Use the “Rename Symbol” feature to rename variables and functions throughout your codebase seamlessly.

3. Extracting Constants

Magic numbers and strings should be avoided. Instead, extract them into constants.

Before Refactoring:

function calculateShippingCost($weight) {
    return $weight * 0.5;
}

After Refactoring:

const SHIPPING_RATE_PER_KG = 0.5;

function calculateShippingCost($weight) {
    return $weight * SHIPPING_RATE_PER_KG;
}

VSCode Tips:

  • Highlight the magic number or string, and use “Refactor” > “Extract constant” to automatically generate a constant.

Automated Refactoring with Rector

Manual refactoring is essential, but it can be time-consuming, especially for large codebases. This is where automated tools like Rector come in handy. Rector is an automated refactoring tool that can quickly apply various refactoring rules to your PHP codebase, saving you time and reducing the risk of human error.

Getting Started with Rector

InstallationTo install Rector, you’ll need Composer. Run the following command in your project directory:
composer require rector/rector --dev

Basic ConfigurationAfter installing Rector, you’ll need to create a configuration file, typically named rector.php:

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig->rules([
        SimplifyIfReturnBoolRector::class,
        ConsistentImplodeRector::class,
    ]);
};



This configuration applies two basic refactoring rules: simplifying if statements that return booleans and ensuring consistent usage of implode().

Running Rector

Once you’ve configured Rector, you can run it to automatically refactor your code:

vendor/bin/rector process src

This command will apply the refactoring rules to the files in the src directory.

Examples of Refactoring with Rector

Rector comes with a wide range of predefined rules that you can apply to your codebase. Here are some examples:

Simplify If Statements

Rector can automatically simplify if statements that return boolean values.

Before Refactoring:

if ($value) {
    return true;
} else {
    return false;
}

After Refactoring:

return (bool) $value;

Consistent Implode Function

Rector ensures that the implode function is used consistently across your codebase.

Before Refactoring:

$result = implode($array, ',');

After Refactoring:

$result = implode(',', $array);

Convert foreach to array_map

Rector can convert simple foreach loops into array_map calls where appropriate.

Before Refactoring:

$results = [];
foreach ($items as $item) {
    $results[] = $item->process();
}

After Refactoring:

$results = array_map(fn($item) => $item->process(), $items);

Custom Rules with Rector

Rector is highly customizable. If you need to apply specific refactoring patterns unique to your codebase, you can write custom rules.

Creating a Custom Rule

Here’s a basic example of a custom rule that replaces a deprecated function with a new one:

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;

final class ReplaceDeprecatedFunctionRector extends AbstractRector
{
    public function getNodeTypes(): array
    {
        return [FuncCall::class];
    }

    public function refactor(Node $node): ?Node
    {
        if (!$this->isName($node, 'old_function')) {
            return null;
        }

        return $this->nodeFactory->createFuncCall('new_function', $node->args);
    }
}

This rule checks for calls to old_function and replaces them with new_function.

Applying the Custom Rule

Add your custom rule to your rector.php configuration file and run Rector as usual.

Conclusion

Refactoring is an essential practice in maintaining a healthy PHP codebase. While manual refactoring using tools like VSCode allows for fine-grained control and immediate application, automated tools like Rector can handle repetitive, large-scale changes with ease.

By combining manual and automated techniques, you can ensure that your PHP code remains clean, efficient, and maintainable. Whether you’re working on a small project or a large-scale application, the right tools and practices will help you keep your codebase in top shape, allowing you to focus on building new features and delivering value to your users.

Happy coding!

Leave a Reply