PHP開発のためのEmacs 2016 (pixiv <3 Emacs)
こんにちは、今年もピクシブ株式会社 Advent Calendar 2016です。
最近は社内にPhpStormを浸透させようと暗躍*1してますうさみ @tadsanです ヾ(〃><)ノ゙ 今年は.emacs Advent Calendar 2016も書いてます!
2014年はEmacsでpixiv-novel-modeを作ったを書きましたが、今回は私がどうやってEmacsで仕事をするのか、そしてどのようにPHP開発環境を効率化するかについて書きます。
ちなみに昨日はkanaによるpixiv開発を支えるVim (タグジャンプ編)なので、Vimをご利用の型はこちらも読んでみてください。
なんとかStormに負けたくない気持ち
今回の内容の要約は、今年3月にPHPの知見などを共有するLT会・PHP BLT #3で発表したので、長い文章を読むのがだるいひとは見といてください。
SSHとTRAMP
pixivの開発環境は現在のところローカルマシンで動かすことが容易ではないため、共用のDebian GNU/Linuxサーバーにログインして作業する必要があります。
EmacsにはTRAMP(Transparent Remote (file) Access, Multiple Protocol)機能があります。これは、リモートサーバーや別ユーザー権限でファイルを透過的に編集するための機能です*2。PC-UNIX(Linux/macOSなど)環境であれば、~/.ssh/config
の設定を読んでくれるので、SSH公開鍵でログインできる環境ならば設定は非常に簡単です。ssh hoge-serv
のようにログインできるサーバならば/ssh:hoge-serv:/
または/scp:hoge-serv:/
で開くことができます。
私はrecentfを使って履歴からファイルを開くのが好みなのですが、ローカルファイルもリモートファイルもまったく同じように絞り込んで検索できるので、とても捗って便利が強いです。
タグジャンプ
昨日のVimの記事で言及されてたタグジャンプ(クラスやメソッドの定義位置に移動する機能)ですが、私も最近まで同じ問題に悩まされてました。しかし、最近リリースされたEmacs 25.1で既存のタグジャンプの代替として搭載された新機能Xrefは定義の重複があったときには選択できるようになったので、実はあまり困ってません。
私はctags日本語対応版を自分でビルドして、こんなシェル関数でTAGS
ファイルを出力します。
phptags(){ ctags -e --php-types=c+i+d+f $(git ls-files '*.php') }
Emacs Lispパッケージ
Emacs 24.1以降、package.el
と呼ばれるパッケージ管理システムが標準機能に取り入れられました。これに更新が活溌なMELPAリポジトリを追加して利用するのが一般的です。
今回紹介するパッケージは基本的にMELPAからインストールできます。まだ利用してなければ、package.el - Emacs JPを参考にして設定してみてください*3。
インターフェイス
デフォルトの状態よりもEmacsの各機能を利用しやすくために、補完インターフェイスを設定することは有効です。詳細は君は誰とEmacsる? (補完インターフェイス紹介篇)をお読みください。
php-mode
ことあるごとに紹介してるのですが、php-modeは現在でも更新が続いてますので、「むかしダウンロードしたけど更新してない」ってひとは、最新版を使ってみてください。最近ではPHP 7.1で追加された文法のサポートなども入ってます。
web-mode
web-mode.elはHTMLテンプレートを編集するためのメジャーモードです。pixivで利用するSmartyのほか、数々のテンプレート言語に対応します。
web-modeはPHPにも対応しますが、pixivではPHPをHTMLテンプレートとしては利用することはないため、私はphp-modeを利用します。
(add-to-list 'auto-mode-alist '("\\.tpl\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
EditorConfig
EditorConfigは各種テキストエディタに対応した設定ファイルです。これを利用するとプロジェクト固有のインデントルールなどが自動で設定されるので、わざわざ設定することがなくなります。
各エディタの拡張プラグインなどがありますが、Emacsのパッケージとしてもeditorconfig-emacsがあります。
せっかくなので、pixivで使ってる.editorconfigファイルを置いておきますね。単なる設定ファイルなので著作権などはありませんから、勝手にコピペして使ってください。
Magit!
MagitはEmacs上からGitの機能を利用するためのパッケージです。Gitのほとんどの機能をEmacsと統合することができます。Gitの差分から部分ステージングできる機能(git add -p
相当)、編集中のバッファのgit blame
を表示する機能(M-x magit-blame
)、同じくファイルの履歴を表示する機能(M-x magit-log
とM-x magit-log-buufer-file
)などが革命的にべんりです。
TRAMP経由のリモートサーバでも、ローカルファイルと同様に操作できるのは圧倒的なメリットです。デメリットとしては… リモートファイルおよび巨大なリポジトリではちょっと遅いので、打鍵してから数秒待たされることを許容できないひとにはおすすめできません。
magit-find-file
最近のエディタではプロジェクト内のファイル名を検索して開く機能があります。magit-find-file.elはGit管理下のファイルを簡単に絞り込むことができます。
php-eldoc
PHPには「array_map
とarray_walk
の引数が逆!」「implode
とexplode
の引数が覚えられない」のように初心者がうんざりする仕様があります。大丈夫、そんなの私も覚えてません。
ElDocは、関数/メソッドの入力中引数を表示するEmacs標準の機能です。sabof/php-eldocはPHPの標準関数の引数に対してElDocを表示してくれます。
余談
array_map()
とarray_walk()
はそもそも役割が異なるのです。array_map()
は配列の写像をとる関数ですが、入力には複数の配列をとることもできます。つまり、array_map(function($name, $work){ return ['NAME' => $name, 'WORK' => $work]; }, $names, $works)
のような処理が書けます。一方で、array_walk
はリファレンスをとって、配列を走査・操作することができます。つまり、$ids = [[7, 5, 3], ['3', '1', '5']]; array_walk($ids, function(&$a){ sort($a); });
またはarray_walk($ids, 'sort');
でソートできることを意味します。後者の用途にarray_map()
は適しません。
phpunit.el
phpunit.elはEmacs上からPHPUnitをEmacs上から利用するためのパッケージです。私もバグ修正やTRAMP対応など開発に参加してます。
phpunit-current-project
でテスト全体(正確にはphpunit
を引数なしで起動したときに実行されるテスト)を、phpunit-current-class
で現在編集中のテストクラスにあるテストを実行できます。また、phpunit-group
で@group
が登録されたテストを選択的に実行できます。
composer.el
composer.elはPHPの依存管理性ソフトウェアであるComposerをEmacs上から透過的に利用するためのツールです。M-x composer
ではComposerの各種サブコマンドを対話的に実行できます。
EmacsからComposerを叩けて嬉しい場面もそんなに多くはありませんが、設定確認のためにM-x composer-find-json-file
で現在作業中のプロジェクトのcomposer.json
を開けたり、M-x composer-view-lock-file
でcomposer.lock
を読み込み専用モードで開けたりするのは、それなりにべんりなことがあるかもしれません。
Flycheck
Flycheckは編集中のファイルのsyntax checkを実行できるツールです。Emacs標準機能のflymakeよりも高機能・高速で、数多くの言語に対応します。容易に拡張もできるため、後述するように社内独自のコーディングルール違反を検知するlint checker機能を統合できます。
基本的な機能としてはPHPのSyntax Errorをその場で検出できるので、GitHubにpushしてからSyntax Errorに気付く、といったかっこわるいミスを避けられます。
PsySH / psysh.el
PsySHはPHPのREPL/対話シェル実装です。要は、RubyのIRBとかPryとかrails console
、Pythonのpython
コマンドを引数なしで実行したときに起動するアレです。
psysh.elはEmacs上からPsySHを起動したり、php-modeやphp-eldoc
との連携もできます。
現在のクラス名を入力
pixiv開発を支えるVim (タグジャンプ編)にもある通り、現在pixivのコーディング規約ではクラスの静的メソッドの多用とクラス名の明示が推奨されてますので、編集中のクラス名が一発で入力できますと、大変捗ります。
この機能は現在php-modeに機能追加提案してますが、まだ取り込まれてないのでphp-util.elから、自身の.emacsファイル(~/.emacs.d/init.el
)などにコピペすると良いです。
;; php-mode のキーバインドに登録する (with-eval-after-load 'php-mode (define-key php-mode-map (kbd "C-c C--") 'php-util-insert-current-class) (define-key php-mode-map (kbd "C-c C-=") 'php-util-insert-current-namespace))
pixiv-dev.el
これはpixiv開発にべんりな設定をまとめたパッケージです。コードはzonuexe/dotfiles/pixiv-dev.elにあります。
M-x pixiv-dev-copy-file-url
これは編集中のファイルのWeb上のURLをコピペするコマンドです。GitLabでもGitHubでもいいのですが、「このファイルのここ見といて」とチームメンバーにリポジトリ内のファイルを共有する際に、わざわざブラウザでファイルの位置を探してから共有するのは明らかに非効率です。
このコマンドがあればファイルパスからWeb上のURLを一発でコピペできるので、時間を大幅に節約できます。現在は完全にURLをべた書きをしてますが汎用化はできそうなので、もっと改善して、そのうちライブラリにしたいですね。
M-x pixiv-dev-shell
開発サーバーでPsySHを起動できます。カレントディレクトリがどこでも起動できるので超べんりです。
M-x pixiv-dev-find-file
リモートサーバー上のpixivのディレクトリから(わざわざカレントディレクトリを移動しなくても)ファイル選択ができます。作ったは良いのですが、あまり使ってません。
pixiv-dev-mode
マイナーモードです。このマイナーモードの機能として、psysh
をpixiv-dev-shell
のために設定したり、プロジェクト専用のFlycheckのチェッカーを定義したりします。
pixiv-lint
はファイルに対してコーディング規約のチェックを実行し、以下のようなログを標準出力します。
file:pixiv-lib/TagCounter.php line:3 col:0-5 level:warn desc:クラス定義には final を明示すること
これを解釈するチェッカーは以下のように定義できます。 (同じ記述が反復してるので、もっと簡潔に書けないのかなあ……)
(flycheck-define-checker pixiv-dev-lint "Lint for pixiv.git" :command ("pixiv-lint" source) :error-patterns ((info line-start "file:" (file-name) "\tline:" line "\tcol:" (+ (or "-" num)) "\tlevel:info" "\tdesc:" (message) line-end) (error line-start "file:" (file-name) "\tline:" line "\tcol:" (+ (or "-" num)) "\tlevel:error" "\tdesc:" (message) line-end) (warning line-start "file:" (file-name) "\tline:" line "\tcol:" (+ (or "-" num)) "\tlevel:" (+ alnum) "\tdesc:" (message) line-end)) :modes (php-mode) :next-checkers (php))
弱点なのですが、私がまだFlycheckに詳しくなくてリモートサーバーにあるスクリプトを直接利用する方法がわからず、ローカルのPATH
環境変数が通った場所にスクリプトをコピーしてくる、みたいなことをやってます。ルール更新のたびにコピーしてくるのもちょっとめんどくさいので、もっと効率化したいところです。
肝腎のlintスクリプトの件についても紹介したいところですが、Emacsの話題からは少し外れるので別の機会に紹介いたします。
まとめ
今回はEmacsで(PHPをはじめとした)コーディングのために便利なパッケージと自作の定義について説明しました。
package.el
の設定方法 package.el - Emacs JP- Emacsの各機能を利用しやすくする 君は誰とEmacsる? (補完インターフェイス紹介篇)
- PHPファイル編集モード php-mode
- HTMLテンプレート編集モード web-mode
- プロジェクトの編集設定を共有する editorconfig-emacs
- べんりなGitクライアント Magit
- リポジトリ内からファイル開くやつ magit-find-file.el
- PHPの標準関数の引数を表示するやつ sabof/php-eldoc
- PHPUnitのテスト実行するやつ phpunit.el
- Composerを操作するやつ composer.el
- 編集中のファイルを自動的にチェックするやつ Flycheck
- Emacs内から対話的にPHPを実行するやつ - psysh.el
もしわからないことがあれば、php-users-ja slackやemacs-jp slackなどのコミュニティに常駐してますので質問していただければ解決できるかもしれません ヾ(〃><)ノ゙
さて、明日のピクシブ株式会社 Advent Calendar 2016は、弊社随一のJavaScript野郎のgeta6です。
脚注
*1:もともと経費で購入できるのですが、なまじ手慣れたエディタでどうにかできちゃってる人の方が多いのが逆に悩みなのです。
*2:概念について知りたい型は、TRAMP User Manualをお読みください。(2001年頃の訳ですが、TRAMP User Manual(日本語訳)もあります)
*3:もっと詳しく知りたい型は2015年Emacsパッケージ事情をお読みください