How To Refactor Code With PhpStorm

How To Refactor Code With PhpStorm

Refactoring code is an essential aspect of building and maintaining software. In this tutorial I will show you how to use PhpStorm’s refactoring functionality to make it as easy as possible.


When refactoring code by hand, whether you’re working with legacy code, or creating new code, it’s easy to make mistakes, such as forgetting one place where the code was used, when renaming a method. That’s why I love to make use of PhpStorm’s refactoring functionality on a regular basis.

If this is your first time hearing about the term, Martin Fowler describes refactoring as:

Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations, each of which “too small to be worth doing”. However the cumulative effect of each of these transformations is quite significant. By doing them in small steps you reduce the risk of introducing errors. You also avoid having the system broken while you are carrying out the restructuring - which allows you to gradually refactor a system over an extended period of time.

Refactoring covers a range of different techniques, including moving, extracting, copying, deleting, and renaming. These cover all the types of changes which you are likely to make to your code on an ongoing basis.

Gladly, PhpStorm’s refactoring functionality, which is included as part of the core package, has support for all of these. In this tutorial, I’m going to step through a couple of them; specifically:

  • Extracting code to a new method
  • Renaming a function
  • Changing a function’s signature

Extracting Code to a New Method

Extracting code to a new method is the refactor that I do more than most, perhaps any other. I can’t count the number of times that I come across long functions which could be organised better by breaking them up in to a series of smaller, more reusable, more testable functions.

Take the following function as an arbitrary example.

public function populate($data)
{
    if (is_array($data) && empty($data)) {
        throw new HydrationException();
    }

    $this->id = $data['id'];
    $this->userId = $data['userId'];
    $this->entry = $data['entry'];
    $this->created = $data['created'];
    $this->updated = $data['updated'];
}

Whilst small, it’s sufficient for this example. Let’s say that the five lines at the end of the function were needed in other parts of the class or could be better served in a separate function.

What we’re going to do is to extract them out in to a separate method. To do so, we first highlight the lines to extract, then click “Refactor -> Extract -> Method”. This will display the “Extract Method” dialog, which you can see below, pre-filled with a default set of options.

Refactoring with PhpStorm: Extract Method Refactoring Dialog Popup The minimum that we need to do is to fill out a name for the method. The remaining options can be left as is, as they’re a good set of defaults. However, I’ve done one more thing, which is to specify type-hinting for the sole parameter, $data.

This way, the function is that much clearer, and the generated PhpDoc block will contain that information as well. Clicking “Refactor” both generates the new method, and replaces the highlighted code, with a call to it, which you can see below.

public function populate($data)
{
    if (is_array($data) && empty($data)) {
        throw new HydrationException();
    }

    $this->hydrateMemberVariables($data);
}

///...intervening code
public function populate($data)
{
    if (is_array($data) && empty($data)) {
        throw new HydrationException();
    }

    $this->hydrateMemberVariables($data);
}

Renaming a Function

Now that we’ve extracted code out in to a separate method, what about renaming an existing function. Perhaps its name wasn’t so intuitive of its purpose. Perhaps it wasn’t following naming best practices. Let’s see how to do that.

This refactor operates in quite a similar way, but we’re going to access it differently. This time, we’re going to use the context-menu instead of the main “Refactor” menu. To do that, as in the screenshot below, either right-click, if you’re on Windows or Linux, or do a two-finger click if you’re on a Mac.

Refactoring with PhpStorm: Context-menu refactor of a function’s name

From there, go to “Refactor -> Rename”, which will open a small popup, where the name is highlighted, which you can see below. From here, change the name to something more suitable; in this case, I’ll change it to getJournalId.

Refactoring with PhpStorm: In-place rename function refactor

If you want more options, then click Shift+f6 (check your keyboard mappings) which opens up the “Rename” dialog, which you can see below. This let’s you be more specific in how the rename will happen, as you have the ability to have PhpStorm search in both comments and strings, as well as for text occurences.

Refactoring with PhpStorm: Rename Window

This might not seem all that necessary. But if you’ve made reference to the function in comments and annotations, for example, then it’s helpful to both yourself and future developers to keep the documentation in sync with the changes to the code.

When you click “Refactor”, PhpStorm will search for all occurences of the function, whether the definition or calls to it and display a preview window, displaying all the occurrences which it’s found. At this point, stop for a moment. Don’t automatically click “Do Refactor”.

Refactoring with PhpStorm: Refactor Preview Window

The reason why is that depending on the method name which you’ve chosen, PhpStorm might have found occurrences of it which don’t relate to your implementation, but one in another vendor’s library. If that’s the case, right or two-finger click the one that’s not relevant, and click Exclude. With that done, click “Do Refactor” to complete the function rename.

Changing a Function’s Signature

Now for one final refactor, changing a function’s signature. In the code below, I have the original populate() method, which takes one argument $data, which is an array. What I’ve not done though, is to provide type-hinting.

/**
 * @param $data
 */
public function populate($data)
{
    if (is_array($data) && empty($data)) {
        throw new HydrationException();
    }

    $this->hydrateMemberVariables($data);
}

Let’s do that now. This time, we’ll use the third method of accessing PhpStorm’s refactoring functionality, by using the keyboard shortcut cmd+F6. As before, check your keyboard mappings.

PhpStorm’s change signature refactor dialog This opens up the “Change Signature” dialog, which you can see above. It has support for changing the method’s:

  • Visibility
  • Name
  • Parameters
  • Parameter order.

By clicking on $data I can then add in array to type-hint that $data is an array. Clicking enter confirms the change, as well as updates the “Signature Preview” window. As with renaming a function, we can either preview the change or complete the refactor.

The same caution applies here as before. With that done, our method is updated, as is the PhpDoc annotation for the method as well, which we can see below.

/**
 * @param array $data
 */
public function populate(array $data)
{
    if (is_array($data) && empty($data)) {
        throw new HydrationException();
    }

    $this->hydrateMemberVariables($data);
}

In Conclusion (tl;dr)

That’s how to make use of some of PhpStorm’s in-built refactoring support for performing three types of refactoring to a PHP class. There’s lots more on offer, such as safe deletion, make static, pulling members up, and pushing members down. Definitely explore it further, if you’re not too familiar with it.


You might also be interested in...


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.


Join the discussion

comments powered by Disqus