在開發的時候,可能會因為code髒像是變數命名不好、部分程式碼可以重複使用想要進行重構,往往這個時候我都會找時間直接改掉,沒有使用到 phpstorm 提供的功能,想說來認識一下phpstorm 提供的重構功能
在 phpstorm 工具列有提供 Refactor 或者滑鼠右鍵也會顯示這個功能裡面就包含 Refactor 的功能

圖1. 工具列 Refactor 功能

圖2. 滑鼠右鍵 Refactor 功能
# Rename
重新命名
## 變數
會把方法內的變數都選取起來可以一次做改名並不會影響到其它方法的同名變數
```php=
// before
public function exchangeCoupon()
{
$a = true;
if ($a) {
...
}
}
// after
public function exchangeCoupon()
{
$isExpire = true;
if ($isExpire) {
...
}
}
```
如果是針對 $a 右鍵 refactor => rename 那麼第3行和第5行的 $a 都會一起更動
## 屬性
會把檔案內相關的屬性都改掉名字
```php=
// before
class UploadController extends Controller
{
protected $a = true;
public function uploadImage()
{
$this->a = true;
}
public function downloadImage()
{
$this->a = false;
}
}
// after
class UploadController extends Controller
{
protected $isUpload = true;
public function uploadImage()
{
$this->isUpload = true;
}
public function downloadImage()
{
$this->isUpload = false;
}
}
```
## 方法
會把檔案裡同名的方法都進行更動
```php=
// before
public function a()
{
// 取得優惠券
}
public function exchangeCoupon()
{
$coupon = $this->a();
}
public function deprecateCoupon()
{
$coupon = $this->a();
}
// after
public function getCoupon()
{
// 取得優惠券
}
public function exchangeCoupon()
{
$coupon = $this->getCoupon();
}
public function deprecateCoupon()
{
$coupon = $this->getCoupon();
}
```
如果用 refactor => rename 那麼檔案內相同名稱的 method 會一起被更改名字
## 檔名
會連檔案內的 class 名稱與相關 reference 與註解或字串都會進行更動,如果不想都更動可以選擇要的範圍

圖3. rename 檔名

圖4. 重構檔案可以選擇重構的部分與範圍
# change signature
調整 method
- 名字與回傳型態
- 新增參數/刪除參數/重新排序參數
- 新增參數帶入預設值

圖5. change signature 調整 visibility、return type、name、parameter 畫面
# Extract/Introduce

圖6. 將選取到的部分提煉成 Variable、Parameter、Field、Constant、Method、Class、Interface
## Extract Parameter
新增參數到method定義且更新呼叫方式
```php=
// before
class Class1 {
public function Calculate($i){
while ( $i < 10 ) {
$i = $i + 1;
};
return $i;
}
public function DisplaySum(){
$a = 1;
$result = $this -> Calculate($a);
echo "The final result is " . $result;
}
}
// after
class Class1 {
// 多增加 $c parameter
public function Calculate($i,$c){
while ( $i < $c ) {
$i = $i + 1;
};
return $i;
}
public function DisplaySum(){
$a = 1;
$result = $this -> Calculate($a, 10);
echo "The final result is " . $result;
}
}
```
## Extract Field
把選取到的 expression 變成 field
```php=
// before
public function find($params)
{
return execute($params['param_query']);
}
public function findAll($params)
{
return executeAll($params['param_query']);
}
//after
public $query = 'param_query';
public function find($params)
{
return execute($params[self::$query]);
}
public function findAll($params)
{
return executeAll($params[self::$query]);
}
```
## Extract Method
讓選取到的區塊變成 method
```php=
// before
if ('POST' != $_SERVER['REQUEST_METHOD']) {
header('Allow: POST');
header('HTTP/1.1 405 Method Not Allowed');
header('Content-Type: text/plain');
exit;
}
// after
function printEmptyHeader()
{
header('Allow: POST');
header('HTTP/1.1 405 Method Not Allowed');
header('Content-Type: text/plain');
}
if ('POST' != $_SERVER['REQUEST_METHOD'])
{
printEmptyHeader();
exit;
}
```
## Extract Class
讓選取到的 field 與 method 移動到目標 class 並且調整 reference
```php=
// before
class Source {
private $foo;
public function bar() {
echo $this->foo;
}
}
(new Source())->bar();
// after
class Source {
private $foo;
/** @var \Target */
private $target;
public function __construct() {
$this->target = new \Target($this);
}
public function bar() {
$this->target->bar();
}
public function get_foo() {
return $this->foo;
}
}
class Target {
private $source;
public function __construct(Source $source) {
$this->source = $source;
}
public function bar() {
echo $this->source->get_foo();
}
}
(new Source())->bar();
```
## Extract Interface
選擇 className 後可以選擇相關 method 抽成介面

## Extract Variable
讓選取到的 expression 變成變數
```php=
// before
public function getFeedObject($title, $description)
{
global $wgSitename, $wgContLanguageCode, $wgFeedClasses, $wgTitle;
if (!isset($wgFeedClasses[$this->format]))
return false;
return new $wgFeedClasses[$this->format]
// 重構這一個 expression 變成變數 $feedTitle
("$wgSitename - {$title} [$wgContLanguageCode]", htmlspecialchars());
}
// after
public function getFeedObject($title, $description)
{
global $wgSitename, $wgContLanguageCode, $wgFeedClasses, $wgTitle;
$feedTitle = "$wgSitename - {$title} [$wgContLanguageCode]";
if (!isset($wgFeedClasses[$this->format]))
return false;
return new $wgFeedClasses[$this->format]
($feedTitle, htmlspecialchars());
}
```
# Move
移動 class 和 class 相關成員(methods、fields、constants), function, constant, file, constant 且會自動修正所有 reference
## Move Class
移動 class 位置

## Move Method
移動 method 到目標 class 且會調整 reference 成目標 class
```php=
// before
class UploadController extends Controller
{
public function getFile()
{
// 取得檔案
}
public function downloadImage()
{
$this->getFile();
}
}
// after
class UploadController extends Controller
{
public function downloadImage()
{
// 下載檔案
if (self::IS_FILE) {
FileController::getFile();
}
}
}
class FileController extends Controller
{
public function getCoupon()
{
// 取得優惠券
}
}
```
## Move Constant
會把移動 constant 還有調整相關 reference 成目標 class
```php=
// before
class UploadController extends Controller
{
public const IS_FILE = true;
public function uploadImage()
{
// 上傳檔案
if (self::IS_FILE) {
}
}
}
// after
class UploadController extends Controller
{
public function uploadImage()
{
// 上傳檔案
if (CommonConstant::IS_FILE) {
}
}
}
class CommonConstant
{
public const IS_FILE = true;
}
```
# Inline
與 Extract 相反,將 constant、variable、method 變回去
## Inline Constant
```php=
// before
const CONSTANT = 5;
function showConstant() {
echo CONSTANT . "\n";
}
// after
function showConstant() {
echo 5 . "\n";
}
```
## Inline Variable
```php=
// before
function sum($a, $b) {
$c = $a + $b;
return $c;
}
// after
function sum($a, $b) {
return $a + $b;
}
```
## Inline Method
```php=
// before
function log($message) {
echo $message;
}
log('Message');
// after
echo 'Message';
```
# 參考資源
https://www.jetbrains.com/help/phpstorm/refactoring-source-code.html