php-cs-fixer-2.0.0をためす

php-cs-fixer-2.0.0が最近リリースされていました。2系へのメジャーバージョンアップです。
https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/tag/v2.0.0

ためしに触ってみることにします。

## セットアップ

[README](https://github.com/FriendsOfPHP/PHP-CS-Fixer#installation)にあるいずれかの方法でphp-cs-fixerをダウンロード、インストールすればOK。

## 実行してみる

```bash
$ php-cs-fixer fix --dry-run --diff -v .
PHP Fatal error: Class 'Symfony\CS\FixerInterface' not found in /path/to/.php_cs
```

既存の`.php_cs`そのままでは実行できず。
[README](https://github.com/FriendsOfPHP/PHP-CS-Fixer)に`.php_cs`のサンプルがあるのでそれをまず眺めてみることに。

```php:.php_cs
<?php

$finder = PhpCsFixer\Finder::create()
    ->exclude('somedir')
    ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php')
    ->in(__DIR__)
;

return PhpCsFixer\Config::create()
    ->setRules(array(
        '@PSR2' => true,
        'strict_param' => true,
        'array_syntax' => array('syntax' => 'short'),
    ))
    ->setFinder($finder)
;
```

## 変更点

UPGRADEにわかりやすく書いてあります。
https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/v2.0.0/UPGRADE.md

以下では気づいたところを主に

### .php_cs

`.php_cs`で使うクラスが変更されています。

* `Symfony\CS\Config\Config` → `PhpCsFixer\Config`
* `Symfony\CS\Finder\DefaultFinder` → `PhpCsFixer\Finder`
* `Symfony\CS\FixerInterface::PSR[012]_LEVEL`は廃止、`PhpCsFixer\Config::setRules()`の`@PSR1`, `@PSR2`のキー指定に統合された

またプロジェクトルートの設定ファイルは`.php_cs.dist`に、ローカルの設定は`.php_cs`という位置づけになりました。
（phpunit.xmlとphpunit.xml.distの関係と同じ）

`.php_cs.dist`を使うことが推奨されているようです。

### level

1系では`Symfony\CS\Config\Config::level()`でnone, psr0, psr1, psr2を選択していましたが、前述のとおり2系では`Symfony\CS\FixerInterface::PSR[012]_LEVEL`は廃止され指定方法が変わっています。

* `PhpCsFixer\Config::setRules()`で`@PSR1`, `@PSR2`をキーにして`true`/`false`で有効無効を切り替える  

```php:.php_cs
$config = PhpCsFixer\Config::create()
    ->setRules([
        '@PSR2' => true,
    ]);
```

* psr0は単独のルールになった
* psr4が新たに追加された。これも単独のルール扱い  
`@PSR1`, `@PSR2`は複数ルールの集合、psr0, psr4は単独ルール

## .php_csを見比べる

変更点を踏まえて1系と修正した2系の.php_csを見比べてみます。

### 1系

```php:.php_cs
<?php

$level    = Symfony\CS\FixerInterface::PSR2_LEVEL;
$fixers   = ['short_array_syntax', '-braces'];
$excludes = ['path/to', 'file'];

return Symfony\CS\Config\Config::create()
    ->level($level)
    ->fixers($fixers)
    ->finder(
        Symfony\CS\Finder\DefaultFinder::create()
            ->in(__DIR__)
            ->exclude($excludes)
            ->notName('README.md')
            ->notName('*.xml')
            ->notName('*.yml')
    );
```

### 2系

```php:.php_cs
<?php

$rules = [
    '@PSR2'        => true,
    'array_syntax' => ['syntax' => 'short'],
    'braces'       => false,
];
$excludes = ['path/to', 'file'];

return PhpCsFixer\Config::create()
    ->setRules($rules)
    ->setFinder(
        PhpCsFixer\Finder::create()
            ->in(__DIR__)
            ->exclude($excludes)
            ->notName('README.md')
            ->notName('*.xml')
            ->notName('*.yml')
    );
```

いくつか変更されていますが修正量としてはそれほど多くはなさそうです。
ルールを個別で指定しているときは1系と2系でルールの名前が変わっていたりするためやや面倒かもしれません。

## 2系対応版.php_csで再実行

1系ですべてパスするコードに対して実行します。

```bash
$ php-cs-fixer fix --dry-run --diff -v --config .php_cs
Loading config default from ".php_cs".
...(省略)
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error

Checked all files in 975.268 seconds, 7.250 MB memory used
```

### 実行結果

オプションを同じにして実行すると結果は同じくすべてパスしました。
ただし実行時間は約16分という結果になっています。
1系の場合は1分程度で終わる分量なのでそれに比べるとだいぶ遅いです。

## --format junit

2.0.0で新たに追加された`--format junit`オプション。
これを指定するとjunit形式のxmlが出力されます。

```bash
$ php-cs-fixer fix --dry-run --config .php_cs --format junit
Loading config default from ".php_cs".
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite name="PHP CS Fixer" tests="1" assertions="1" failures="0" errors="0" time="6.535">
    <testcase name="All OK" assertions="1"/>
  </testsuite>
</testsuites>
```

`--format junit`と`--diff`や`-v`と組み合わせることはできないようです。

## その他

### level:none設定

1系のlevel:noneの設定をしたいときに2系ではどう設定すればよいかわからず。

```php:.php_cs
$config = PhpCsFixer\Config::create()
    ->setRules([
        '@PSR1' => false,
        '@PSR2' => false,
    ]);
```

とすれば設定できそうですが普通にデフォルト設定で実行されてしまいました。

### .php_csは指定しないといけない？

1系のように"."指定で実行するとどうも`.php_cs`の設定が反映されないようでした。

```bash
# これだと.php_csの設定が効かない
$ php-cs-fixer fix --dry-run --diff -v .
```

明示的に`--config`で`.php_cs`を指定すれば設定が効きました。

```bash
$ php-cs-fixer fix --dry-run --diff -v --config .php_cs
```

## まとめ

* php-cs-fixer-2.0.0がリリースされた
* .php_csを設定している場合はそのままでは実行できない
* .php_csの修正量はそれほど多くはないので移行は難しくなさそう
* 2系(2.0.0)は実行速度が遅い
* `--format junit`オプションは便利そう

実行速度の遅さが気になるので今後の開発に期待したいところです。