歴史あるPHPプロダクトで少しでも開発を楽にしたい

## はじめに

古いPHPで書かれた古いプロダクトの保守をしているといろいろと大変なことがあります。  
例えば、メソッドや変数に型情報がないためコードを読んだり書いたりするのにも以下のようなことから一苦労です。

- コード補完がきかない
- コードジャンプもきかない

少しだけ手間をかけることで、ちょっとでもコードを読み解きやすくしたいと思います。


## 環境

ご長寿

- PHP 5.3
- Symfony 1.4
- Doctrine 1.2
- PhpStorm

PhpStorm は素の状態です

## PHPDoc を書く

### PHPDoc とは

https://www.phpdoc.org/

簡単に言うと、メソッドやクラスなどに定義上明記されていない情報を特別な記法でコメントに書くことで IDE などが解析しやすくするものです。  
機械が解析できるようにすることで、ドキュメントを自動生成できるようになります。

この記事では変数の型やその説明が書ける、と理解してもらえれば良いです。

実際に書いてみましょう。

### メソッドに書く場合

メソッドにはこんなのが書けます。

```php5
  /**
   * Counts the number of items in the provided array.
   *
   * @param mixed[] $items     Array structure to count the elements of.
   * @param bool    $recursive Optional. Whether or not to recursively
   *                           count elements in nested arrays.
   *                           Defaults to `false`.
   *
   * @return int Returns the number of elements.
   */
  public function count($items, $recursive = false)
  {
    <...>
  }
```

ここで見てほしいのは `@param` と `@return` です。

#### [`@param`](https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/param.html)

引数に関する情報を書きます。

```
@param [型] [名前] [説明]
```

重要なのは `[型]` と `[説明]` です。  
`[型]` には `int` のようなプリミティブ型やクラスなどを書きます。  
型について詳しくはこちらで↓確認できます。

https://docs.phpdoc.org/3.0/guide/references/phpdoc/types.html

`[説明]` には引数にどんなものを渡してほしいのかなどを簡潔に書くと良いです。

#### [`@return`](https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/return.html)

返り値に関する情報を書きます。
名前がないこと以外は `@param` と同様です。

```
@return [型] [説明]
```

#### 先程のメソッドを呼び出してみましょう

`$this->` と打ち込んだところで候補に `count` が出てきました。  
ここまでは PHPDoc コメントがなくても出てくると思います。

![countが補完されている様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/90934c26-281f-b65b-b3bb-84f6baa1549b.png)

##### ここで `count` を選択してみます

なにか出てきました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/7a7377ff-53b4-2b5d-6191-ceb95645ce8c.png)

こんなこと↓が書いてあるようですね

- 上段 : `引数の名前: 引数の型`
- 下段 : 引数の説明

引数の説明のところを丁寧に書いておくと
呼び出すときにどんなものを渡せば良いのかが、わざわざ定義を読まなくてもわかります。  
嬉しい。

##### ちなみに PHPDoc コメントを書いていないと

こうなります

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/6a417f88-6d8e-3732-e5a9-4db694f23f72.png)

型が類推できる場合はでてくることもありますが、説明は当然ながらありません。  
`items` に何を渡すべきなのかを知るには `count()` の定義を確認する必要がありそうです。

### 直接 Doc コメントが書けないメソッドもある

すべてのメソッドに PHPDoc コメントを書きましょう
といいたいところなんですが、そうわけにいかないこともあります。  
歴史あるプロダクトには一筋縄ではいかない場合もあったりするのです。

例えば古い Symfony + Doctine なプロダクトを泳いでいるとこんなものに遭遇することがあります。

```php
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserStatus($user->getStatus());
```

どうやらこの `findOneBySomeUserStatus` というメソッドには PHPDoc コメントは書かれていないようです。
ここままだと `$segment` を使うときに補完がききません。
困った。

#### 渋々定義を確認しようとします

が、よく見ると定義がありませんとあります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/fdf28fb5-3196-9b90-de83-8cde537c640f.png)

まさかと思って確認しますが確かに `SomeSegmentsTable` クラスには書いてありません。

```php5:SomeSegmentsTable.class.php
<?php

/**
 * SomeSegmentsTable
 * 
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class SomeSegmentsTable extends Doctrine_Table
{
    /**
     * Returns an instance of this class.
     *
     * @return object SomeSegmentsTable
     */
    public static function getInstance()
    {
        return Doctrine_Core::getTable('SomeSegments');
    }
}
```

これだけ。

<!-- (自動生成されたこのクラス、1か所腹立たしいことがあるんですがその話はまたどこかで) -->

詳細は割愛しますが、 `findOneBySomeUserStatus` はマジックメソッドで、 Doctrine 本体で定義されています。  
ライブラリのコードなので、自分で Doc コメントを書き加えたりするわけにもいかないでしょう。

しかしこのままではどうしても嫌なのでなんとかしてみます。

### 変数に書く場合

先程のこちら、

```php
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserStatus($user->getStatus());
```

詳しいことは省きますが、
 `findOneBy~~` はデータベースからデータを1件取得してくれます。  
この例ではデータを1件分のクラスは `SomeSegments` です。

こんなとき、この変数 `$segment` に対してこのように PHPDoc コメントを書きます。

```php5
/** @var SomeSegments $segment This is user segment. */
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserType($user->getType());

// もしくはこう↓書く

/**
 * This is user segment.
 * 
 * @var SomeSegments $segment
 */
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserType($user->getType());
```

#### [`@var`](https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/var.html)

1行で書く場合

```
/** @var [型] [変数名] [説明] */
```

複数行で書く場合

```
/**
 * [説明]
 *
 * @var [型] [変数名]
 */
```

#### 先程の変数を使ってみます

ちゃんと補完してくれますね。  
嬉しい。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/1e8ad0ed-f906-a652-d3e7-da27da3b3a01.png)

#### ただしメソッドの正体は謎のままです

そんなメソッドはありませんよ、と言われる。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/fe75c393-8c57-e949-00d3-d3e52b2b8b8b.png)

ちょっとモヤモヤしますが、気にしなければ先へ進むことができます。  
気にしていられないことも多いのでこのまま突き進むこともよくあります。

### クラスに書く場合

上述のように呼び出す度に書いても良いんですが、よく使う場合はちょっと邪魔くさく感じるかもしれません。  
そこで、先程の定義が書いてあることを期待したけど書いてなかったクラス、
こちらに Doc コメントを書くこともできます。

`@method` を使います。

```php5:SomeSegmentsTable.class.php
/**
 * SomeSegmentsTable
 *
 * This class has been auto-generated by the Doctrine ORM Framework
 *
 * @method SomeSegments findOneBySomeUserType(string $someUserType)
 */
class SomeSegmentsTable extends Doctrine_Table
```

#### [`@method`](https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/method.html)

```
@method [[static] 返り値の型] [名前]([[引数の型] [引数]<, ...>]) [説明]
```

#### 改めて呼び出し側をみてみましょう

今度は変数の Doc コメントがなくても補完されます。  
いいですね。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/c8bded3f-e8c8-62a8-23d1-d560e0bcdba4.png)

メソッドの型定義もわかります。  
すばらしい。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58640/05cc3075-d488-3e44-4234-f00d4c67e73a.png)

## まとめ

これだけのことでも少しは息ができるようになりました。

## おまけ

VSCode の場合は Extention を入れると同じように補完してくれるみたいです。

https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client