Engineer as a Lifestyle @tenkoma

What We Find Changes Who We Become -- Peter Morville著『アンビエント・ファインダビリティ 』

PHPerKaigi 2019にて「PhpStormでコードを理解する技術」というテーマで発表しました

2019年3月29〜31日にかけて開催されたPHPerKaigi 2019の2日目に発表してきました。

KeynoteファイルもDropboxで公開します。 こちらは、そのうち削除するかもしれません。

この発表はイベントの少し前に公開した以下の4記事+補足記事で説明した話を15分のダイジェスト版という位置づけで発表しました。

PHPプロジェクト向けの定番IDEであるPhpStormに使い慣れていくと、「コードを理解する」というシンプルな目的のためでも多くの機能を使ってると思ったので、それをまとめるだけでもコンテンツになりそうだな、と思って応募しました。

発表にあたりいろいろな先人の記事を参考にし、Twitterで知らなかったテクニックを教えて頂きました。特に5名の方にあらためて感謝いたします。

PHPerKaigi 2019に参加して

参加者同士の交流を促進するための仕掛けがよく練られていてすごいと思いました。特によいと思ったのが

  • PHPerチャレンジ
  • (去年に引き続き)Interactive Round Table
  • お茶会(懇親会)でのボードゲーム

です。ボードゲーム以外参加できませんでしたが…。

2週間くらいまえからブログ記事で作っていったのですが、肝心のスライドが前日まで完成せず、1日目はずっともくもくコーナーで資料作成していたため、セッションの聴講や参加者とのコミュニケーションができず辛かったです、次は事前に準備をして望みたいです。

PhpStorm でコードを理解する (テストコードを読む・実行する・書く編)

(※諸事情によりテスト実行の準備を省略している、後で加筆したい。)

テストコードを読む

もし、前述のフィボナッチ数列を出力するコードにテストコードがあれば、コードを実行しなくても理解できた可能性が高い。テストコードは、欠陥の検出につかうツールでもあり、コードの振る舞いについて表現したドキュメントにもなるからだ。

フィボナッチ数列を出力するコード(再掲)

<?php
// nazono.php
function nazono() {
    $a = 0;
    $b = 1;
    for ($i = 1; $i <= 10; $i++) {
        $l = $a;
        $a = $b;
        $b += $l;
        print "$a\n";
    }
}
nazono();

nazono() 関数がどのように使われているか調べようとして、定義箇所で、Command+B (Mac), Ctrl+B (Win)を押したときに、テストコードが引っかかったらラッキーかもしれない。移動してみよう。

f:id:tenkoma:20190324201231p:plain
nazono() 関数がテストコードから呼び出されていたら…

ちなみにテストコード例。

<?php
namespace Nazono\Tests\Unit;

require_once __DIR__ . '/../../nazono.php';

class NazonoTest extends \PHPUnit\Framework\TestCase
{
    public function testNazono()
    {
        // 実コードを呼び出す
        nazono();
        // 想定した出力
        $expectOutput = <<<OUT
1
1
2
3
5
8
13
21
34
55

OUT;
        $this->expectOutputString($expectOutput);
    }
}

期待される出力がテストコードに書かれている。 コードを実行した結果をみるのと同じ効果が得られるのがわかると思う。

テストを実行する・書く(変更する)

  • Mac: Shift+Ctrl+R, Win/Linux: Shift+Ctrl+F10

上記のテストを実行してみる、成功したら、「生きた」テストであることがわかる。

f:id:tenkoma:20190324205815p:plain
PhpStorm から NazonoTest テストを実行した

失敗したら、実コードかテストコードのどちらかが間違っている可能性がある。*1

もしプログラムの正しい振る舞いが分かっていれば、テストコードか実コードを変更することで、実コードへの理解が深まるかもしれない。 テストを書くのが楽になる機能に「Auto-test」がある。有効にすると、ファイル変更を検知して、自動で再テストしてくれる。

f:id:tenkoma:20190327001651p:plain
Auto-test ボタン

もし複雑なコードを良く理解しないといけない場合、テストがコードを理解するためのツールになると思う。

目次

*1:それ以外の、テストのセットアップができていない可能性もある

PhpStorm でコードを理解する (コードを実行する編)

コードを実行する

コードを見ただけでは、全体的に何をやっているのかわからない、ということがよくあると思う。 例えば、以下のコードは何を出力しているコードでしょうか? (変数名などは意図的に不親切な感じにしてある)

<?php
// nazono.php
function nazono() {
    $a = 0;
    $b = 1;
    for ($i = 1; $i <= 10; $i++) {
        $l = $a;
        $a = $b;
        $b += $l;
        print "$a\n";
    }
}
nazono();

プログラムを眺めただけで分かる人もいれば、ずっと見ていてもわからない人もいるかもしれない。 今回は、実行・デバッグしてみてプログラムを理解する方法について解説する。

Run 機能を使ってスクリプトを実行する

まず、新規プロジェクトを作成し、nazono.php を保存する。素朴にphpコマンドで実行してみると以下のように出力される。 10番目までのフィボナッチ数を出力するプログラムだった。(わかりにくくするために再帰を用いていない)

$ php nazono.php
1
1
2
3
5
8
13
21
34
55

PhpStormと連携するCLI環境をDockerで用意してからProjectパネルで nazono.php を右クリックし、「Run 」とRunパネルに以下のように表示される。

f:id:tenkoma:20190323181543p:plain
Docker環境でPHPスクリプトを実行する

ここまでは、少ない設定でPHPスクリプトが実行できるようになることを示したかっただけで、Debug 機能の説明で本題に入る。

Debug 機能でスクリプト実行中の状態を確認しながら実行する(デバッグ実行)

理解しづらいコードを、デバッグ機能を使ってプログラム実行中の状態を確認しながら理解する方法について説明する。もし、手元にPHPデバッグ環境がない場合はXdebugでデバッグできる最低限のWebサーバを用意するの手順で準備してほしい。

PHPプロジェクトの多くはWebプロジェクトなので、ブラウザからアクセスしてデバッグしてみる。

f:id:tenkoma:20190324145634p:plain
PhpStorm プロジェクトの状態

[Xdebugでデバッグできる最低限のWebサーバを用意する]で準備した状態は上記のようになっていて、docker-compose up -d でコンテナを起動し、 http://localhost:8000/nazono.php にアクセスすると以下のように表示される。

f:id:tenkoma:20190324150349p:plain
ブラウザから http://localhost:8000/nazono.php を実行

ではリモートデバッグしてみる。

  • PhpStorm右上のf:id:tenkoma:20190324150534p:plain:w28をクリックしf:id:tenkoma:20190324150530p:plain:w31の状態にする(Xdebugの通信を受け付ける状態にする)
  • nazono.php 10行目右側をクリックしを表示する(ブレークポイントを作る)

この状態でもう一度ブラウザからアクセスするとブレークポイント行背景が青くなり、プログラム実行が一時停止していることがわかる。

f:id:tenkoma:20190324172152p:plain
10行目でブレークしたところ

ブレークすると、Debugパネルで変数の状態を確認できる。またエディタ上で変数が変化したときにその行の右側に内容が表示される。

ループで実行されていることを理解するために、一時的に各ループごとの $i, $a, $b の値をログに残したくなったら、ログ機能がつかえる。

10行目のブレークポイントを右クリックして「More」を選ぶとダイアログが表示されるので以下のように入力する

f:id:tenkoma:20190324174520p:plain
Breakpointsダイアログ

  • 「Evaluate and log」をチェックし、"\$i=$i, \$a=$a, \$b=$b" と入力し、「Done」で閉じる

この状態でデバッグ実行すると、DebugパネルのConsoleタブに変数の状態が出力される。

f:id:tenkoma:20190324175328p:plain
Consoleパネル

ConsoleパネルはREPLを兼ねていて、任意のPHPコードをその場で実行できるし、コード補完も効く。

コードを実行しながらコードを読んでブレークしてさらにコードを実行する…そんな試行錯誤でコードを理解する機能になっている。

目次

次はテストコードを読む・書く編

PhpStormと連携する必要最小限のDocker環境を作る

PhpStormネタをまとめるときに、Docker環境の設定の話は分離しておきたいと思ったので独立した記事にする。

シンプルなPHP CLI環境とXdebugデバッグできるWebサーバ環境について説明する。

目次

PhpStormと連携するCLI環境を用意する

f:id:tenkoma:20190324015022p:plain
php:7.3-alpine でPHPスクリプトを実行する

標準で組み込まれていない拡張が不要であれば、Dockerfile や docker-compose.yml をつくる必要はない。

設定方法

f:id:tenkoma:20190323180544p:plain
DockerでPHP実行環境を用意する

  1. Preferences を開く
  2. 左メニューでPHPを選ぶ
  3. CLI Interpreter 右側の 「...」 を選ぶ
  4. CLI Interpreterダイアログで 「+」 を選び、「From Docker, Vagrant, VM, Remote... 」を選ぶ
  5. Configure Remote PHP Interpreterダイアログで、「Docker」を選び、Image name に php:7.3-alpine と入力
  6. OK を3回選びPreferencesを閉じる(Dockerイメージのダウンロードとコンテナ起動が行われる)

設定後、最初の画像のように実行するのは Docker のコマンドで言えば

docker run -it -v `pwd`:/opt/project php:7.3-alpine php /opt/project/phpinfo.php

が近そう。

Xdebugでデバッグできる最低限のWebサーバを用意する

f:id:tenkoma:20190324021419p:plain
XdebugデバッグできるWebサーバを含むDocker環境

PHP (Docker Official Images) にはXdebugがインストールされていないので、 Dockerfile で、カスタマイズしたコンテナをつくる。(なるべく Unofficial なイメージは避けたい) また、ホストディレクトリをコンテナにマウントする設定を docker-compose.yml に書く。(必須ではないが、PhpStormの設定をするときにPath Mappingを自動的に読み込んでくれるので)

設定方法

まずプロジェクトフォルダに以下の Dockerfile, docker-compose.yml, php.ini を保存する

Dockerfile

FROM php:7.3-apache

RUN apt-get update \
  && pecl install xdebug \
  && docker-php-ext-enable xdebug

docker-compose.yml

version: "3.7"
services:
  web:
    build: ./
    volumes:
      - ./:/var/www/html:cached
      - ./php.ini:/usr/local/etc/php/php.ini:cached
    working_dir: /var/www/html
    ports:
      - "8000:80"

php.ini

[xdebug]
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20160303/xdebug.so
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_host = host.docker.internal

設定確認用に phpinfo.php をつくっておく。

phpinfo.php

<?php
phpinfo();

2行目の行番号右をクリックしてブレークポイントを追加しておく。(赤い丸🔴が表示される)

Terminal を開いて、プロジェクトフォルダに移動し、 docker-compose up -d と実行すると、Webサーバのコンテナが起動する。 次にPhpStorm右上の「Start Listening for PHP Debug Connections」をクリックする。

f:id:tenkoma:20190323194352p:plain
Start Listening for PHP Debug Connectionsというボタン

🚫のようなアイコンが無くなった状態で http://localhost:8000/phpinfo.php にアクセスすると以下のようなウインドウが開くので、「Accept」を選ぶ。

f:id:tenkoma:20190324022237p:plain
Incoming Connection From Xdebugウインドウ

先ほど作ったブレークポイントの行背景が青くなったら設定成功。以下のように実行中のグローバル変数が見られるようになっているはず。

f:id:tenkoma:20190324021419p:plain
XdebugデバッグできるWebサーバを含むDocker環境

Webアプリケーションだし、DBサーバも欲しいとき

Laradock を使ってみるのがよさそう。

環境構築の理解を深めるためにtenkoma/cakephp3-app-skeletonをつくってみた。説明は省略。

目次

PhpStorm でコードを理解する (経緯をたどる編)

PhpStorm でコードを理解する (コードを読む・構造を知る編)の続き。

経緯をたどる

コードを理解するために、コードが変更された経緯を理解するのが有効なこともある。そんなときに使える機能をまとめる。

Git連携

Git連携のLog機能・History機能(ファイル・ディレクトリの変更履歴)も便利だが、経緯をたどるために便利なのがAnnotate機能。

Annotate機能(git blameと同等)

  • ショートカットキー未設定・エディタの右端の行番号を右クリックして「Annotate」を選択

コード行ごとに最終変更日時・変更者が確認できる機能。「バグを埋めだ犯人捜し」に使うわけじゃないよ!

f:id:tenkoma:20190321175735p:plain
Git Annotate機能

日付・変更者名をクリックすると、同じコミットで変更したファイル一覧が表示できる

f:id:tenkoma:20190321180600p:plain
Annotate → Paths Affected in Revision

Annotateで表示される各行は右クリックするとコミット・リビジョンに関連付いたいくつかの操作ができるようになっている。

f:id:tenkoma:20190321185323p:plain
Annotate 右クリック

  • Show Diff: その行が最後に変更されたときのファイル変更(Diff)を表示
  • Annotate Revision: その行が最後の変更されたときのファイルを表示
  • Annotate Previous Revision: その行が最後の変更される直前のファイルを表示

GitHub連携

「Find in Pull Request」プラグイン

GitHub.com / GitHub Enterprise を上流リポジトリで使い、プルリクエストを使った開発をしているなら、コード変更の経緯を知るのにこのプラグインが便利。

エディタ行で右クリックして「Find Pull Request」を選択

f:id:tenkoma:20190321183119p:plain
Find Pull Request機能

getAlias() が変更されたプルリクエストのfiles 3.4 Remove more deprecated function calls by antograssiot · Pull Request #10009 · cakephp/cakephpにジャンプした。

目次

次はコードを実行する編

PhpStorm でコードを理解する (コードを読む・構造を知る編)

プログラムコードを書くとき、多くの時間を「コードを理解する」ことに費やしていると思う。

PHPコードを読み書きするのに、なぜPhpStormを使っているのか考えたとき、デバッガやバージョン管理が統合されていることも理由としてあるが、コードを理解しやすくするために多くの機能が備わっていることも理由の一つだった。

「PHPコードを理解する」という視点でPhpStorm の便利な機能をまとめてみると面白そうだなぁ、と思ったのでまとめてみる。 (PHPコードの文法をある程度理解している方向け)

分類

雑多に説明するより、とりあえず分類してみるとわかりすくなると思ったので4つに分ける。

コードを読む・構造を知る

コードを理解するのに最も基本的で必須なのがコードを読むこと。 また全体像を大雑把に把握するためにプログラムの構造を知ることも必要になってくる。 そのための機能を紹介する。

コードジャンプ

  • Mac:Command+B, Win: Ctrl+B
  • Command または Ctrl を押しながらシンボル(クラス名・メソッド名…)をクリック

大きく2つの機能がある

  • クラスやメソッドを利用してる箇所から定義元にジャンプする機能
  • 定義元から、利用する箇所を一覧・ジャンプする機能

f:id:tenkoma:20190317194745p:plain
定義元から利用箇所にジャンプする

コードが1ファイルで完結しないとき(チーム開発だと自然とそうなると思う)、読み込んでいるコードを理解したくなることはよくある。 サードパーティーライブラリのコードを理解したくなることもよくある。 そんなときコードジャンプを多用しながら構造を理解する。ジャンプ前のコードに戻るときは Mac:Command+[, Win: Ctrl+Alt+←

Quick Definition

Mac:Option+Space, Win: Shift+Ctrl+I

f:id:tenkoma:20190413213136p:plain
Quick Definition

メソッド呼び出し先のファイルに移動する前にコードをちょっとだけ確認したいとき使える

File Structure (ポップアップ) / Structure パネル

File Structure (ポップアップ): Mac:Command+F12, Win: Ctrl+F12

f:id:tenkoma:20190413204916p:plain:w300
File Structure (ポップアップ)

Structure パネル: Mac:Command+7, Win: Alt+7

f:id:tenkoma:20190317193032p:plain:w300
PhpStorm Structure パネル

クラスやインターフェイスのプロパティ・メソッドを一覧できる。 どんなメソッドを持っているのか把握するのに使う。 継承したメソッドを表示することもできる。 移動するときに一時的に表示したいなら、File Structure (ポップアップ), 常時表示させたいならStructure パネルで。

クラス・メソッドを開く(ナビゲート機能)

クラス・メソッド名は分かってるけど、ファイルツリーから辿るのは面倒くさい。

  • クラス: Mac:Command+O, Win: Ctrl+N
  • シンボル(クラス・メソッド・プロパティ): Mac:Command+Alt+O, Win: Shift+Ctrl+Alt+N

f:id:tenkoma:20190317201228p:plain
シンボルを開く(ナビゲート機能)

クラスやメソッドにアクセスしたいとき、覚えておくと全文検索(Find in Path)より楽になる。

クラス階層を表示(Show Diagram)

Mac:Command+Alt+Shift+U, Win: Shift+Ctrl+Alt+U

f:id:tenkoma:20190317202018p:plain
Show Diagram機能

あまり使ったことは無い。クラス階層が深いとき、全体像を理解するのに使えるかも。メソッド・プロパティを表示することもできる。

呼び出し階層を表示 (Call Hierarchy)

Mac:Ctrl+Opt+H, Win: Ctrl+Alt+H

f:id:tenkoma:20190413210842p:plain:w300
Hierarchy: callers (呼び出し元)

f:id:tenkoma:20190413210923p:plain:w300
Hierarchy: callees (呼び出し先)

メソッドの呼び出し元、呼び出し先をツリー表示して、たどれる機能

次は「経緯をたどる」編

目次

LEDデスクスタンド

パナソニック LEDデスクスタンド クランプタイプ ダークグレー SQ-LC516-K

パナソニック LEDデスクスタンド クランプタイプ ダークグレー SQ-LC516-K

引っ越して、机で本が読みにくい環境になってしまったので買った。 スイッチが根元にあって遠いけど、たいして気にならないので満足。