PHP Refactoring Browser Alpha Release
First published at Monday 8 April 2013
Warning: This blog post is more then 11 years old – read and use with care.
PHP Refactoring Browser Alpha Release
Without continuous refactoring, code maintainability and extensibility will start to decrease fast, even if it has tests. Until now, only IDEs contained functionality to perform automated refactorings. And then even only PHPStorm contains the most important refactorings such as "extract method".
Today we release the PHP Refactoring Browser, a refactoring tool written completely in PHP. It is based on several outstanding open-source libraries:
PHP Parser by Nikic
PHP Token Reflection by Ondřej Nešpor
PHP Analyzer by Johannes Schmitt
The Browser currently supports the following refactorings:
Extract Method
Rename Local Variable
Convert Local to Instance Variable
The result of each refactoring is a patch, which is printed to the screen. You can review the patch and then apply it to your source code using Unixs patch
command.
Let's discuss a sample code and apply some refactorings to it:
<?php
// tests/FooServiceTest.php
class FooServiceTest extends PHPUnit_Framework_TestCase
{
public function testFoo()
{
$dependency = $this->getMock('DependencyService');
$service = new FooService($dependency);
$dependency->expects($this->once())->method('doSomething');
$value = $service->perform();
$this->assertEquals(42, $value);
}
}
For our next test, we need the same test setup, so we want to refactor the first two lines into the setUp()
method and convert the local variable into an instance variable:
$ php refactor.phar convert-local-to-instance-variable tests/FooServiceTest.php 6 dependency
Prints:
--- a/tests/FooService.php
+++ b/tests/FooService.php
@@ -2,11 +2,13 @@
// tests/FooServiceTest.php
class FooServiceTest extends PHPUnit_Framework_TestCase
{
+ private $dependency;
+
public function testFoo()
{
- $dependency = $this->getMock('DependencyService');
+ $this->dependency = $this->getMock('DependencyService');
- $service = new FooService($dependency);
+ $service = new FooService($this->dependency);
- $dependency->expects($this->once())->method('doSomething');
+ $this->dependency->expects($this->once())->method('doSomething');
$value = $service->perform();
We can apply the patch by calling the command again and pipe to |patch -p1
. Now we want to do the same to the $service
variable:
$ php refactor.phar convert-local-to-instance-variable tests/FooServiceTest.php 10 service
We get the patch:
--- a/tests/FooService.php
+++ b/tests/FooService.php
@@ -3,5 +3,7 @@
class FooServiceTest extends PHPUnit_Framework_TestCase
{
private $dependency;
+ private $service;
+
public function testFoo()
@@ -8,5 +8,5 @@
{
$this->dependency = $this->getMock('DependencyService');
- $service = new FooService($this->dependency);
+ $this->service = new FooService($this->dependency);
$this->dependency->expects($this->once())->method('doSomething');
@@ -12,5 +12,5 @@
$this->dependency->expects($this->once())->method('doSomething');
- $value = $service->perform();
+ $value = $this->service->perform();
$this->assertEquals(42, $value);
Now we extract the first two lines into the setUp()
method:
$ php refactor.phar extract-method tests/FooServiceTest.php 11-12 setUp
We get the patch:
--- a/test.php
+++ b/test.php
@@ -9,6 +9,5 @@
public function testFoo()
{
- $this->dependency = $this->getMock('DependencyService');
- $this->service = new FooService($this->dependency);
+ $this->setUp();
$this->dependency->expects($this->once())->method('doSomething');
@@ -17,5 +17,11 @@
$this->assertEquals(42, $value);
}
+
+ private function setUp()
+ {
+ $this->dependency = $this->getMock('DependencyService');
+ $this->service = new FooService($this->dependency);
+ }
}
And we can see the setUp()
method is created in the class and called in testFoo()
. When we apply the patch we know that PHPUnit calls setUp()
itself and requires it to be public, so we change this code manually to end up with:
<?php
// tests/FooServiceTest.php
class FooServiceTest extends PHPUnit_Framework_TestCase
{
private $dependency;
private $service;
public function testFoo()
{
$this->dependency->expects($this->once())->method('doSomething');
$value = $this->service->perform();
$this->assertEquals(42, $value);
}
public function setUp()
{
$this->dependency = $this->getMock('DependencyService');
$this->service = new FooService($this->dependency);
}
}
This project already has been a valuable tool for us in a customer project and we intent to make it much more useful over time:
Integration into Vim, using hot-keys to perform refactorings
Introducing more refactorings and improving the existing ones
Currently you can easily break the refactorings when coming up with weird code-scenarios. We will try to handle as much edge-cases as PHP Parser, PHP Token Reflection and PHP Analyzer allow us to.
To install the Refactoring Browser, head over to it's website on Github and download the PHAR file from there.
Of course, contributions are highly welcome via pull requests on Github.
Subscribe to updates
There are multiple ways to stay updated with new posts on my blog: