Github Actions を使った PHP の静的解析

Hamee Advent Calendar 2021 15日目の記事です。
https://qiita.com/advent-calendar/2021/hamee

## はじめに

Q. 綺麗にコード書きたくね？
　
A. はい。

## 静的解析とは

>静的コード解析(static code analysis)または静的プログラム解析(static program analysis)とは、コンピュータのソフトウェアの解析手法の一種であり、実行ファイルを実行することなく解析を行うこと。逆にソフトウェアを実行して行う解析を動的プログラム解析と呼ぶ[1]。静的コード解析はソースコードに対して行われることが多く、少数ながらオブジェクトコードに対して行う場合もある。

Wikipediaより引用

つまるところ、機械的にコードを分析して悪いところ見つけてくれるってことですね。
例えば、IntelliJ にある PHP の静的解析は以下の画像ような指摘をしてくれます。
![ 2021-12-14 16.30.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/347470/39099eaf-b7c9-9068-8fae-58c9552e7f3a.png)

> `$unused` が使われてなさそうだね。

みたいな感じです。
表示される変数も灰色になって使われてないよアピールをしてくれます。

## PHPにおける静的解析

IntelliJ を始め、世に出回っているIDEやエディタは静的解析できるものが多いですが、どうせなら PHP 専用の静的解析ツール使いたくないですか...？

### PHP CodeSniffer

1つ目は、PHP CodeSniffer と言ってコーディング規約のチェックをしてくれるツールです。
Laravel とか CakePHP などフレームワークに合わせた規約も用意してくれているすごいやつ。
以下の例は CakePHP の環境で実行しています。

```
> phpcs --colors -p  src/
......E..................................................... 60 / 85 (71%)
.........................                                    85 / 85 (100%)

FILE: /src/hoge.php
----------------------------------------------------------------------
FOUND 2 ERRORS AND 1 WARNING AFFECTING 3 LINES
----------------------------------------------------------------------
 216 | WARNING | [ ] Missing @return tag in function comment
 224 | ERROR   | [x] Expected 1 space(s) after IF keyword; 0 found
 226 | ERROR   | [x] Missing blank line before return statement
----------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Time: 31.67 secs; Memory: 22MB
```

### PHPStan

AutoLoader を解釈したり、PHP を一部実行したりして解析を行うツールです。

存在しないプロパティへのアクセスや、null に対するメソッドの呼び出しの可能性などをチェックしてくれます。

```
> phpstan analyse
Note: Using configuration file /phpstan.neon.
 84/84 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Line   src/hoge.php
 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  222    Cannot access property $interface_1 on array|Cake\Datasource\EntityInterface.
  223    Cannot access property $interface_2 on array|Cake\Datasource\EntityInterface.
  230    Cannot call method set() on array|Cake\Datasource\EntityInterface.
  238    Method src\Hoge::method_1() should return App\Model\Entity|false but returns array|Cake\Datasource\EntityInterface.
 ------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------


 [ERROR] Found 4 errors
```

PHPStan には 0 ~ 9 のレベルが存在し、0 が一番緩く 9 が一番厳格なルールになっています。
ちなみに各ルールの差異は以下の通りです（原文ママ）
> Here’s a brief overview of what’s checked on each level. Levels are cumulative - for example running level 5 also gives you all the checks from levels 0-4.
> 
> 0 . basic checks, unknown classes, unknown functions, unknown methods called on $this,  wrong number of arguments passed to those methods and functions, always undefined variables
> 1. possibly undefined variables, unknown magic methods and properties on classes with __call and __get
> 2. unknown methods checked on all expressions (not just $this), validating PHPDocs
> 3. return types, types assigned to properties
> 4. basic dead code checking - always false instanceof and other type checks, dead else branches, unreachable code after return; etc.
> 5. checking types of arguments passed to methods and functions
> 6. report missing typehints
> 7. report partially wrong union types - if you call a method that only exists on some types in a union type, level 7 starts to report that; other possibly incorrect situations
> 8. report calling methods and accessing properties on nullable types
> 9. be strict about the mixed type - the only allowed operation you can do with it is to pass it to another mixed

レベル 9 が通る頃には可読性・メンテナンス性が高いコードになっているはず。

----------------------------------------
ここまで静的解析について述べてきたが、新しく開発環境を作るたびに静的解析ツールの整備をしたり、コードの追記・修正をする度に解析ツールを手動で回すのも割と面倒...

　
そこで、Github Actions の出番である。

## Github Actions

いわゆる CI/CD 的なことをやってくれるやつです。
わざわざ CircleCI のような CI/CD サービスに登録しなくても Github 内ですべて完結するので嬉しい。

### 設定

リポジトリのルートに `.github/workflows`ディレクトリ を作成し、その中に `*.yml` の形式で記述します。
workflows 内にいくつも `.yml` を置けるので好きなだけ actions を設定できます。
CircleCI だと `.circleci/config.yml` に全ての CI/CD を記述しなければならずゴチャるので、設定が分けられるのもポイント高いですね。

試しに PHPStan の action を設定して push してみます。

```phpstan.yml
name: PHPStan
on: [commit, pull_request]

jobs:
  phpstan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          tools: composer, cs2pr, phpstan
          coverage: none
      - name: Composer install
        run: composer install
      - name: Run PHPStan
        run: vendor/bin/phpstan analyze --memory-limit=-1
```
　
　
できてる〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜。
![ 2021-12-14 20.13.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/347470/0315b29b-9f00-9111-b3e7-9079774fe228.png)


## まとめ

複数人でプロジェクトを運営する時にはコーディング規約を設けると思いますが、こういったサービスを駆使して「Github Actionsが全て通ること」なんてルールにしてしまえば楽そうですよね。

Let's enjoy PHP !