SonarQubeでPHPのコードを静的解析する

2019-02-10 追記： SonarQubeではなくPHPStanで同様のことをする記事も書きました。いま個人開発ならこちらがを選ぶことも多そうです。

- [LaravelプロジェクトのコードをCircleCI上のPHPStanで静的解析してreviewdogにコメントしてもらう](https://qiita.com/kadoyau/items/a21f1fa17114797c32fd)

# SonarQubeはオープンソースの静的解析ツール
一言で言うと「コードのダメな部分を教えてくれるやつ」です。LGPLライセンス。
下のようなこんな感じでコード品質の推移が一覧できます。また、コードごとに何が悪いのか、どう直すべきなのかを見ることができます。（画像は[Drupalの解析結果](https://nemo.sonarqube.org/overview?id=drupal)）
![スクリーンショット 2016-06-06 10.57.38.png](https://qiita-image-store.s3.amazonaws.com/0/87448/3ffeef5a-1a49-a2ff-8170-ba0096f02921.png)

オンプレ環境で使えるのがメリットで、GitHubで個人のプロジェクトを解析するなら、各種静的解析SaaSを使うのが簡単なのでおすすめです。

[PHP向け静的解析SaaSの主観的比較 (Scrutinizer, SensioLabsInsight, CodeClimate) - Qiita](http://qiita.com/Hiraku/items/42631b18c64d47a15059)

公式サイト：[SonarQube™](http://www.sonarqube.org/)

# 簡単に試したい
[公式の利用例](https://nemo.sonarqube.org/governance?id=MASTER_PROJECT) を見るか、公式のサンプルを使ってみてください。自分でDBの設定をしなくても試すことができます。
[Get Started in Two Minutes - SonarQube Documentation - SonarQube](http://docs.sonarqube.org/display/SONAR/Get+Started+in+Two+Minutes)

上のサンプルを動かしてみて自分のプロジェクトに使ってみたい人だけ以下を読んで下さい。

以降では、以下の準備がされていることを仮定します。

- SonarQubeを動かす適当なサーバがある
- 手元に解析対象のPHPのソースコードがある

# SonarQubeで自分のプロジェクトを解析する
## この記事で達成すること、および手順の概要

SonarQubeは大きく2つのモジュールに分かれています。

- DBへ解析結果を書き込んだりWeb上で解析結果を閲覧するためのSonarQube Server
- 静的解析を行うSonarQube scanner(runner)

まず公式ドキュメントなどの手順に従って、これらのソフトウェアを導入します。

- [Installing the Server - SonarQube Documentation - SonarQube](http://docs.sonarqube.org/display/SONAR/Installing+the+Server)
- [【PHP】 PHPでの静的解析 SonarQubeを使ってみた - 旅するえんじにあ - Engineers to Travel -](http://deadcode.hatenablog.jp/entry/2016/04/19/114256)
- [centos6.xにsonarqubeをインストール - m_shige1979のささやかな抵抗と欲望の日々](http://m-shige1979.hatenablog.com/entry/2016/01/07/080000)
- [Installing SonarQube on Ubuntu | dev.mamikon.net](http://dev.mamikon.net/installing-sonarqube-on-ubuntu/)


その後、Jenkinsにより静的解析の実行を自動化します（例えば1日1回解析を回す、などができるようになります。ただ、これだけならcrontabで実行するほうが簡単です。次のステップをするための準備です）。
さらにGitHubと連携し、プルリクエストにフックしてJenkinsでSonarQubeを動かし、静的解析の結果をGitHub上にコメントとして表示することでコードレビューに活用できるようにします。

静的解析ツールをCIに取り入れるために、ぜひGitHub連携まで設定してみてください。ここまでやれば静的解析ツールを入れるメリットを多く享受できると思います（GitHubのレビューベースで開発を進めている場合）。
**プルリク時に自動的にコードのよくない箇所を指摘してもらうことができるようになるので、それを直すことでコード品質が改善できます**。
また、コード品質を定量的に扱う指標が得られるため、目標設定に便利です。計測できない目標を立てるのは困難ですし、計測なくして他人に納得してもらうのはもっと難しいです。

### 追記（2018-10-30）：GitHubのコメント連携は有償になりました
GitHubにコメントしてくれるためのPluginは[非推奨になりました](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin)。
もし使いたい場合有償のDeveloper Editionを使ってくださいという方針のようです。
[haya14busa/reviewdog: Automated code review tool integrated with any code analysis tools regardless of programming language](https://github.com/haya14busa/reviewdog)を使えば似たようなことができるかもしれません（未検証）

## 必要なスペックやミドルウェア
公式ドキュメントによると、RAMは効率的に動かすためには最低でも2GB、さらにOS用に1GB必要なようです。コードベースの規模も関係あるかも知れませんが、2GB程度しか割り振らなくても動きました。

SonarQubeの起動にはJavaランタイム環境が必要です。また、Webのインタフェースで解析結果を表示するためのDBも必要です。
そこで、まず公式サイトを参考にJREとMySQLを導入します。
（ySQL以外も対応しているので詳しくは公式を参照してください。）

[Requirements - SonarQube Documentation - SonarQube](http://docs.sonarqube.org/display/SONAR/Requirements)

## Javaランタイム環境の導入
最新版のSonarQube 5.6以降はJava8が必須になりました。

```bash
yum install java-1.8.0-openjdk-devel
```

## MySQLの導入
### MySQL 5.7のインストール
今回はMySql 5.7を導入する。

```bash
sudo rpm -Uvh  http://dev.mysql.com/get/mysql57-community-release-el6-8.noarch.rpm
yum install mysql-community-server
```

MySql 5.7ではセキュリティ面が向上しているが、**テストのための**簡単なパスワードははじかれてしまうので、セキュリティレベルを下げる。
/etc/my.confに以下を追記する。

```
secure-file-priv = ""
validate_password_policy=LOW # パスワードがダメダメでも許可する
```

### SonarQubeで利用するユーザを作成する
```mysql
CREATE USER 'sonar'@'localhost' IDENTIFIED BY 'YOUR_PASSWORD';
GRANT ALL PRIVILEGES ON sonar.* TO 'sonar'@'localhost';
CREATE DATABASE sonar;
FLUSH PRIVILEGES;
```

### ユーザ一覧の確認
```mysql
select Host, User, Password from mysql.user;
```
豆知識：MySQL 5.7だとPasswordカラムは存在しない。
参考： [MySQLのユーザー一覧を確認 | cloudpack技術情報サイト](https://blog.cloudpack.jp/2011/08/22/server-news-mysql-user-list/)

## SonarQube Serverの導入

```bash
wget "https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-5.5.zip"
unzip sonarqube-5.5.zip
mv sonarqube-5.5 /etc
ln -s /etc/sonarqube-5.5 /etc/sonarqube
```

`service sonar start` でSonarQubeのサーバーをスタートできるようにするためにシンボリックリンクを作成する

```bash
sudo ln -s /etc/sonarqube/bin/linux-x86-64/sonar.sh /etc/init.d/sonar
```

以後、サーバーが落ちたらこのコマンドを打って再起動します。
この時点ではSonarQubeからMySQLを利用することはできません。設定をしていきましょう。
デフォルトではH2が使われますが、DBの設定ができてない状態で起動するとエラーでプロセスが静かに死にます。
`service sonar console` で実行時のログを見ることができます。
起動時のエラーなどは `/etc/sonarqube/logs` 以下に保存されているのでなにか変だったら確認するとよいです。
初回実行時はDBのマイグレーションで数分の時間がかかりました。ログにエラーが出ていない場合は気長に待ちましょう。

## SonarQube ServerをMySQLと接続する
```bash
sudo vim /etc/sonarqube/conf/sonar.properties
```

MySQLの設定をした際のパスワード等を入力します。

```
sonar.jdbc.username=sonar
sonar.jdbc.password=YOUR_PASSWORD
#----- MySQL 5.x
# Only InnoDB storage engine is supported (not myISAM).
# Only the bundled driver is supported. It can not be changed.
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance
```

## SonarQube Scannerのインストール
下記リンクから最新版を引っ張ってくる。

```bash
wget "https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-2.6.1.zip"
unzip sonar-scanner-2.6.1.zip
mv sonar-scanner-2.6.1 /etc
ln -s /etc/sonar-scanner-2.6.1 /etc/sonar-scanner
```
[Analyzing with SonarQube Scanner - Scanners - SonarQube](http://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner)


## PHP Pluginの導入
`service sonar start`でServerを立ち上げていると、デフォルトの設定では`http://[Sonarが動いているサーバのドメイン]:9000`でWebインタフェースにアクセスすることができます。そのままではPHPは解析できないので、プラグインをインストールします。
ページトップのバーから `Administration > System > Update Center > Available` からPHPをインストールします。


## 解析対象のファイル等の設定
解析リポジトリのrootに`sonar-project.properties`というファイルを作り、解析対象のディレクトリ等を指定します。
指定/除外の方法の詳細は以下のページを参考下さい。

[Narrowing the Focus - SonarQube Documentation - SonarQube](http://docs.sonarqube.org/display/SONAR/Narrowing+the+Focus#NarrowingtheFocus-Patterns)

なお、Jenkinsで連携する場合はJenkinsのプラグイン側でsonar-project.properties内容を指定することができます。

```bash
cat sonar-project.properties
```
```text
# must be unique in a given SonarQube instance
sonar.projectKey=YOUR:PROJECT_KEY #このキーが重複するとDBの結果が上書きされるので、ユニークなものにすること
# this is the name displayed in the SonarQube UI
sonar.projectName=YOUR_PROJECT_NAME # Webインタフェースに出てくる名前
sonar.projectVersion=1.0

# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# Since SonarQube 4.2, this property is optional if sonar.modules is set.
# If not set, SonarQube starts looking for source code from the directory containing
# the sonar-project.properties file.
sonar.sources=.

# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8

# inclusions
sonar.inclusions=./hoge/** # 解析に含めたいディレクトリ
```

## 静的解析の実行
```bash
/etc/sonar-runner/bin/sonar-scanner
```
解析が終了すると、 `http://[Sonarが動いているサーバのドメイン]:9000` に `YOUR_PROJECT_NAME` という項目が生成され、コードごとに次のような情報を見ることができます（一例）。

- コードの修正すべき点
- なぜそのコードが修正するべきなのか？という解説

なお、解析のルールはデフォルトではPSR（PHPのコーディングや階層構造の規約）を含む独自のものになっています。
なので、バグじゃないっぽいのに小うるさくバグと言われたりします

# SonarQubeをJenkinsから実行できるようにする
## できるようになること
- Jenkinsのジョブとして解析を実行できるようになる

## 手順
基本的に以下の資料のとおりに進める。補足的情報のみ記す。
[Jenkins + SonarQube で PHPコードメトリクス計測！ - 長生村本郷Engineers'Blog](http://kenzo0107.hatenablog.com/entry/2016/05/21/010902)
[Installing SonarQube with Jenkins integration for a PHP project · Cvuorinen.net](http://cvuorinen.net/2013/07/installing-sonarqube-with-jenkins-integration-for-a-php-project/)

## Jenkinsのインストール
```zsh
sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
yum install jenkins
```
最新版は[Jenkins](https://jenkins.io/)で確認して下さい

## Jenkinsの起動

```bash
sudo service jenkins start
```

## Jenkinsユーザを解析対象のリポジトリにアクセスできるようにする
[解析対象のリポジトリ/settings/keys]にアクセスします。そして、Jenkinsユーザの公開鍵をdeploy-keysに登録します。

## SonarQubeへのGitHub Pluginの導入
Web UIで `Administration > System > Update Center > Available` からGitHubプラグインを導入する。
Jenkinsの設定で利用するため、`Security > User` から authentication token を取得しておく。


## JenkinsにSonarQubeのPluginを入れ、解析ジョブを作成する
下記のサイトの「Jenkins 側準備」節を参考に設定します。

[Jenkins + SonarQube で PHPコードメトリクス計測！ - 長生村本郷Engineers'Blog](http://kenzo0107.hatenablog.com/entry/2016/05/21/010902)

この際、先に作成した、解析対象のrootにある`sonar-project.properties`を削除し、設定をJenkinsに転載します。

ジョブ作成時にRepositoryのCredentialsにJenkinsユーザの鍵を登録します。
（追加 > 種類 > SSHユーザ名と秘密鍵とすると設定できる）


# GitHubのプルリクエスト時にコメントできるようにする
## できるようになること
- GitHubのプルリクエストにフックして静的解析をすることができる
- 静的解析をした結果をGitHubに自動的に書き込むことでコードレビューの手間を減らし、またコード品質を保つ
    - 公式のキャプチャはこちら：[SonarQube™ » GitHub pull request analysis helps fix the leak](http://www.sonarqube.org/github-pull-request-analysis-helps-fix-the-leak/)

### 注意点
トークンを登録したユーザのアカウントでコメントがつけられますが、SonarQubeの解析が走るたびに前のコメントは削除され、最新のコメントが付きます。
この際に登録したユーザのコメントはSonarQubeがつけたものかどうかにかかわらず全て消されてしまうため、レビューを通常行わないユーザ（Botユーザなど）のトークンで設定するようにして下さい。

## GitHub Pull Request Builderの導入
SonarQubeで解析を走らせるためには解析対象のプルリクエストの番号を指定する必要があります。
プルリクエストの番号をいちいち手で指定する運用はめんどくさすぎるので、動的に取得して指定する必要があります。GitHub Pull Request Builderを使うと簡単に取得できるので設定を頑張ります。

JenkinsのプラグインからGitHub Pull Request Builderをインストールします。
設定を以下の捕捉を参考にやっていきます。

[GitHub Pull Request Builder Plugin の多すぎる設定項目を補足してみる - Qiita](http://qiita.com/naotawool/items/e7fef0b2de7f070d891d)

### アクセストークンの自動生成がうまくいかない場合
手動でGitHubのアクセストークンを取得します。
GitHubにアクセスして、Settings（右上の歯車アイコン） > Applications > Personal access tokens でトークンを作成します。
この際 **public_repo, user, repo:status, read:org, notifications, admir:repo_hook** にチェックを入れます。
（notificationは必要ないかもしれません）
Jenkinsの管理 > システムの設定 のGitHub Pull Request Builderセクションにある「追加」ボタンをクリックして鍵を追加します。その際ドロップダウンから secret text を選択しアクセストークンを入力します。

## プルリクエストが来た際にフックするように設定ファイルに追記する
ジョブのビルド > Execute Sonarqube Scanner > Analysis propertiesの末尾に次を追加します

```
# GitHub Integration
sonar.github.repository=your_user/your_repository # 解析対象のリポジトリ
sonar.github.pullRequest=${ghprbPullId} # GitHub Pull Request Builderの変数
sonar.analysis.mode=preview
```

# その他はまったこと
## Jenkinsで見た時マークアップが崩れる
HTMLがそのまま出ているときは、Jenkins > グローバルセキュリティの設定 > マークアップ記法 で `Safe HTML` を選択すると見栄えが良くなる。

## GitHubの通知（Commit Status Context）が"default"と"sonarqube"の2つがでたことによって、もとから回していたCIの"default"の通知が消えた
SonarQube導入前の時点で、ビルド時にCommit status contexitで"default"と表示するようなツールを使っていると起こる問題。
commit statusの表示名のデフォルト値が"default"で、この表示名は複数のアプリケーションでかぶると、後に実行されたものが上書きするっぽい。

[GitHub の Deployments API を使ったデプロイのワークフローのイメージ - Sexually Knowing](http://this.aereal.org/entry/2015/01/15/214610)

ジョブの設定から、ビルドトリガ > Trigger Setup > statusContextのデフォルト値が'default'らしいのでこれを変更するとcommit statusの表示名が変更できます。これをdefault以外のユニークなキーにしておきましょう。

## Commit statusの表示が同内容のものが2つ出る
なぜ出るのかはよくわからないのですが、同じものが出ているので1つにしたいですよね。
上で設定したキーをデフォルトのものと同じ sonarqube にすると上書きされるので1つになります。

[jenkins と ghprb，github commit status を使った仕事が楽になる 10 の設定 | Aiming 開発者ブログ](http://developer.aiming-inc.com/infra/jenkins-ghprb-with-github-commit-status-1/)

## GitHub上のインラインコメントがうざいのでつけないようにしたい
http:[SonarQubeが動いているサーバ]:9000/settings?category=github から Disable issue reporting as inline comments を `true` に設定する
GitHub上のコメントは修正をpushすると自動的に消えるなどけっこう考えられているので、うざくてもつけたほうがいいと思います。

## PHPの `**` を含むコードを解析できない
PHP5.6から利用できる、べき乗の演算子ですが、これがあるとエラーを吐いて無視されているようです。
SonarQubeのPHPプラグインの使っているモジュールに起因するバグっぽい。直接的にissueにはあがっていないのであげたい。
PHP Plugin自体はPHP7にも対応しています。

追記：チケットが爆誕していました
https://jira.sonarsource.com/browse/SONARPHP-691