はじめに
個人的に最近はPythonやJavaScript(JSはみんなそうですよね)を触ってたりしますがPHPネタです。そしてVimネタです。 いや、むしろサーバーやクラウドなどのインフラをイジイジばかりか、コードのレビューをするくらい。コード書いてない。
さて、Vimにはphpcompleteというプラグインが同梱されています。
- phpcomplete
VimでPHPを書く際に補完、定義元ジャンプをするには標準ではタグファイルを作ってその情報を元に頑張るって方法がスタンダードかと思います。
- 参考 @NanohaAsOnKaiさんの記事
タイトルにも書きましたが今回紹介するプラグインを使うとComposerの /vendor/autoload.php
を認識して賢く、高速に補完、定義元ジャンプができます。
また、その仕組をフックして Vimから見えるPHPの環境を変更することもできます。
PhpStormでいいじゃん って意見を言われてしまうので一応
はい。その通りです。PHP開発で現在最強のエディタ/IDEは PhpStorm で間違いないと思います。異論はありません。僕も持ってますよ。
また、最近は JetBrains 公式マニュアル日本語版βというものもあります。日本語マニュアル最高ですね。見やすい。
PhpStormでいいじゃん って人は PhpStorm 使いましょう。
ただ、状況によってはPhpStormがよしなにやってくれないほうが良いものもあるかもしれません。
例えばですが よしなにやってくれないから このメソッドの返り値でオブジェクトは何が返されてるのかなど
を PsySH
なんかのREPLを使って調べたりするようなこととかやるかもしれませんしね。
まー。それでも基本はPhpStormを使いましょう。
phpcd.vim の紹介
Vimに同梱されている phpcomplete.vim
をforkして作られた phpcd.vim
というプラグインを紹介します。
phpcd.vim
はPHPのReflection機構を使用してコンテキスト情報を取得するため、Vimに同梱されている phpcompete.vim
より 賢く、高速になります。タグファイルを使わないのでタグファイルを作るなどの諸々の作業も必要ありません。
GitHub
要件
PHP5.6
以上PCNTL拡張
が有効になっていること- Vimの場合は
JSON拡張
が必要でNeoVimの場合はMsgpack拡張
が必要
残念ながらドキュメントはこのリポジトリの README.md
しかありません。Vimのヘルプもありません。作者は中国の方です。ソース内に若干中国語のコメントがありました。
基本的にはデフォルトの状態で動きますが、まずは1つだけ phpcd.vim
のオプションの設定を説明します。あとでこの値をどうのこうのしますよ。
" デフォルトの設定です。letg:phpcd_autoload_path='vendor/autoload.php'
あとは Vimを起動するカレントディレクトリに .phpcd.vim
というファイルがあればそれを読み込みます。 .phpcd.vim
に 設定を変更(上書き)したい内容を記載するといった使い方をします。
" ...省略function!s:init() " {{{letg:phpcd_root= phpcd#GetRoot()let phpcd_vim =g:phpcd_root.'/.phpcd.vim'if filereadable(phpcd_vim)
exec 'source '.phpcd_vim
endif" ... 省略
感の良い人、凄腕PHPerの方々、声の大きなPHPerの方々、PHPの毛深いの方々はもうこの段階で何ができるのかなんとなく想像付くかもしれませんね。
phpcd.vimのプラグインのインストール
個人的にはvim-plugを使用していますのでその流れで記載します。
その他のプラグインマネージャーの方は お使いのプラグインマネージャーに読み替えてください。
call plug#begin('~/.vim/plugged')" ... 色々省略" vim-plugの説明を軽くしておきます" " for はファイルタイプがphpならプラグインを読み込む " do はプラグインインストール、アップデート時に実行する内容
Plug 'lvht/phpcd.vim',{'for':'php','do':'composer install'}" ... 色々省略call plug#end()
というかこれはプラグインのREADME.mdに書いてますね。プラグインマネージャーを使ってない方は泣きながら手動で入れるか、詳しい先輩に聞いてください。
https://github.com/lvht/phpcd.vim#installation--usage
monologを使って実際にみてみましょう
補完、定義元ジャンプのお話をしていますので実際に手を動かしてみましょうか。
Composerで何か入れてサンプルをと思います。
Composerといえば...そうですね。 monolog
ですね。作者が一緒。 PHPのスタンダードなロガーです。
ということで monolog
のこちらのコードっぽくやってみましょうか
https://github.com/Seldaek/monolog#basic-usage
monologサンプルの下準備
# monologacmeってディレクトリにしますかmkdir monologacme
cd monologacme
# npmだと-yなのでよく間違えます。-nって。-nって。。-y空いてるよ。
composer init -n# monologのインストール
composer require monolog/monolog
# main.phpってファイルを作りますか
vim main.php
補完
はい。補完できてますね。 C-X C-O
です。
docblock
補完候補の選択時に上部のウィンドウにdocblockも表示されてますね。便利ですね。
定義元ジャンプ
定義元ジャンプをしました。 C-]
ですね。
個人的にはウィンドウをスプリットして定義元ジャンプするのが多い派なので C-W C-]
です。
プラグインの中ではこのように 定義元ジャンプ
や 元に戻る
が設定されています。
https://github.com/lvht/phpcd.vim/blob/master/ftplugin/php_phpcd.vim#L4-L11
- 対象にジャンプ:
C-]
- 対象を横分割でジャンプして開く:
C-W C-]
- 対象を縦分割でジャンプして開く:
C-W C-\
- ジャンプ先で元の対象に戻る
C-t
個人的には更に <Leader>d
にも割り当てたりしますのでそれを元にカスタマイズ例とします。 .vimrc
にこのように書いたりします。
autocmd FileType php nnoremap <Leader>d:call<C-U>call phpcd#JumpToDefinition('split')<CR>
ユースケースとして。他言語(GoとかPythonとかJavaScriptとか諸々)のファイルでも <Leader>d
で定義元ジャンプをするって形で統一してるので、まー。そんな使い方ですね。
Slimの例で変数アノテーションでの補完や定義元ジャンプ
今度はSlimを使って手を動かしてみましょうか。
マイクロフレームワークのslimでslim-skeletonを使った例です。
https://github.com/slimphp/Slim-Skeleton
# slimacmeってディレクトリにしますか
composer create-project slim/slim-skeleton slimacme
cd slimacme
# routes.php を例にしましょう
vim src/routes.php
これは routes.php です。
この1ファイルの状況だけでは 8行目の $app
って何?って状態です。
8行目の時点での $app->
で get
メソッド を補完したり get
に定義元ジャンプしたりはできません。
しかし、16行目以降に変数アノテーションの例を書いてます。
ほら?バッチリ補完できてますね。
次は定義元ジャンプもできましたね。
LaravelでFacadeを補完したい(.phpcd.vimファイルを使います)
今度はLaravelを使って手を動かしてみましょうか。
あまりFacadeのことを書くとFacade警察がきたりと怖い思いをするかもしれませんが。。とりあえず責務を〜...以下省略。
LaravelのFacadeについては #phpgenba でおなじみの @shin1x1さんの記事を参照してください。
http://www.1x1.jp/blog/2014/03/laravel-facade-class.html
平たく説明するとLaraevlのフレームワークの中でゴニョゴニョとされないと、そんなstaticメソッド存在しないってものですね。
実行されないと存在しないので補完や定義元ジャンプをするのは不可能ですね。(通常では)
PhpStormではlaravel-ide-helperというツールを使いメタファイルを吐き出して補完や定義元ジャンプをしています。
https://github.com/barryvdh/laravel-ide-helper
同じ流れでVimからそのメタファイルを読み込んで補完や定義元ジャンプを行うといった方法をやります。
Laravelでの準備
Laravelには5.3からチュートリアルがありませんのでどういうサンプルが良いか考えるのが面倒。。
とりあえずcreate-projectで進めます。
# laravelacmeってディレクトリにしますか
composer create-project laravel/laravel laravelacme
cd laravelacme
# laravel-ide-helperのインストール
composer require --dev barryvdh/laravel-ide-helper
# laravel-ide-helperのide-helper:modelsコマンドで必要なためインストール
composer require --dev doctrine/dbal
# laravel-ide-helperのide-helper:modelsコマンドはDBが存在しないと駄目# ゆるふわにsqliteで用意touch database/database.sqlite
# .envファイルのDB_CONNECTIONをsqliteに変更sed-i-e's/^DB_CONNECTION=mysql/DB_CONNECTION=sqlite/' .env
# sqliteを使うので不要部分をコメントアウト(コメントアウトしないと怒られる)sed-i-e's/^DB_HOST=/#DB_HOST=/' .env
sed-i-e's/^DB_PORT=/#DB_PORT=/' .env
sed-i-e's/^DB_DATABASE=/#DB_DATABASE=/' .env
sed-i-e's/^DB_USERNAME=/#DB_USERNAME=/' .env
sed-i-e's/^DB_PASSWORD=/#DB_PASSWORD=/' .env
# コントローラーも作っておきましょうか。namespace下の説明で使うので。
php artisan make:controller TaskController
# migrateでテーブル作成
php artisan migrate
Fluent methodsのためにconfig/ide-helper.phpを作成する
あとで実行する ide-helper:generate
のコマンドにて Fluent methods も含める場合はこちらの対応が必要です。
laravel-ide-helperのconfig設定を上書きするといった内容になります。
<?phpreturn['include_fluent'=>true,];
laravel-ide-helperのconfig設定(デフォルト値)自体はこちらになりますね。
https://github.com/barryvdh/laravel-ide-helper/blob/master/config/ide-helper.php
というか、これくらい、ide-helperのコマンドでオプション用意してよって感じもありますが。
ide-helperのコマンドを使い各種メタファイルの作成
各種FacadeとHelperとFluentのが作成されます。
# _ide-helper.phpというファイルが作成されます
php artisan ide-helper:generate -H# モデルの各種ドックコメントとか追加
php artisan ide-helper:models -W
Vim(phpcd.vim)で上記メタファイルを読み込むための準備
phpcd_autoload.php(PHPファイル)の作成
プロジェクトルートにてこのようなファイルを作成してください。
名称は今回の説明では phpcd_autoload.php
とします。
<?php// これはComposerのオートロードですrequire'./vendor/autoload.php';// これはlaravel-ide-helperを使って作成されたファイルですrequire'./_ide_helper.php';// php artisan ide-helper:modelsでファイル吐き出しをした場合はこちらも//require './_ide_helper_models.php';
.phpcd.vim(Vim script)の作成
Vim(phpcd.vim)からみえるPHPのオートローダーパスを先程作成したファイルを指定して変更する
letg:phpcd_autoload_path='phpcd_autoload.php'
さてプロジェクトルートでVimを立ち上げてみましょう
プロジェクトルートはこの例ですと laravelacme直下
になります。
ファサード補完
Laravelのrouteファイルで試してみましょう。
:e routes/web.php
をファイルを開きます。
一番複雑なDBのファサードが補完できてますね。
ファサードでのメソッドチェーンも補完できてます
メソッドチェーンもバッチリですね(ある程度)
namespace下の場合
グローバルなネームスペース環境にファサードは読み込まれてますので バックスラッシュ(\
)を忘れずに。
ちなみにPhpStormではバックスラッシュなしでも補完できます。
ファサードについて補足
よく使われるRouteファサードについてはこのようにフレームワーク側でドックコメントを頑張って書いてくれてます。
useすればstaticメソッドも呼び出すことが可能です。
コードを抜粋します。
<?phpnamespaceIlluminate\Support\Facades;/**
* @method static \Illuminate\Support\Facades\Route get(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route post(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route put(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route delete(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route patch(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route options(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route any(string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route match(array|string $methods, string $uri, \Closure|array|string|null $action = null)
* @method static \Illuminate\Support\Facades\Route prefix(string $prefix)
* @method static \Illuminate\Routing\PendingResourceRegistration resource(string $name, string $controller, array $options = [])
* @method static \Illuminate\Routing\PendingResourceRegistration apiResource(string $name, string $controller, array $options = [])
* @method static \Illuminate\Support\Facades\Route middleware(array|string|null $middleware)
* @method static \Illuminate\Support\Facades\Route substituteBindings(\Illuminate\Support\Facades\Route $route)
* @method static void substituteImplicitBindings(\Illuminate\Support\Facades\Route $route)
* @method static \Illuminate\Support\Facades\Route as(string $value)
* @method static \Illuminate\Support\Facades\Route domain(string $value)
* @method static \Illuminate\Support\Facades\Route name(string $value)
* @method static \Illuminate\Support\Facades\Route namespace(string $value)
* @method static \Illuminate\Support\Facades\Route where(array|string $name, string $expression = null)
* @method static \Illuminate\Routing\Router group(\Closure|string|array $value)
* @method static \Illuminate\Support\Facades\Route redirect(string $uri, string $destination, int $status = 301)
* @method static \Illuminate\Support\Facades\Route view(string $uri, string $view, array $data = [])
*
* @see \Illuminate\Routing\Router
*/classRouteextendsFacade
今度は先程説明したDBファサードをみてみましょう。
<?phpnamespaceIlluminate\Support\Facades;/**
* @see \Illuminate\Database\DatabaseManager
* @see \Illuminate\Database\Connection
*/classDBextendsFacade
@see
って。 @see
って。。 @see
って。。。
Route側に書いたならせっかくなら他ファサードでも書けるものだけでも良いから書いてよっていうのが本音ですがね。
まとめ
いかがでしたでしょうか?PHPでもVimで補完や定義元ジャンプがある程度、快適になります。(ある程度)
正直、言うと諸々細かな部分はまだまだなところもあります。チェーンメソッドのチェーンメソッドのチェーンメソッドとか。それこそ小さな不具合とか。
他言語では例えば補完なら PythonならJedi, JavaScriptならtern, Goならgocode, TypeScriptならtsserverなどスタンダードな補完エンジンのツールがあったり言語自体が用意していたりします。
残念ながら今のところPHPはありません。あるなら教えてください。
現在ではLSP(language server protocol)なんてものもありますが個人的に使用した印象としてはまだまだなところも感じてます。あくまで仕様の統一をといった段階で快適か、便利かといったところのフェイズに到達するには時間がかかるでしょう。
phpcd.vim
を使って気になるところはPRでもしてください。便利になったら僕がハッピーなので。
もしくは phpcd.vim
は Vimとの諸々のやり取りはVim scriptですが処理などの部分はPHPで出来ています。nikic/PHP-Parser
を使って頑張ってる感じです。
この記事を読んだ凄腕PHPerの方々、声の大きなPHPerの方々、PHPの毛深い方々が よし、俺がPHPの補完エンジンを作ってやる!
となるのを期待してます。
改めてですが便利になったら僕がハッピーなので。
宜しくお願いします!!
次回予告
次回はLinter(phpcs)やFormatter(phpcbfとかphp-cs-fixer)とVimで連携することやPhpStormのShift×2回相当なことをやるとか。PHPのドキュメント出したりとか。ファイル内に宣言されているクラス、変数、メソッドの一覧を出す方法とか。スニペットで今回お話した補完とは別の粒度のcompletefuncのお話(入力しながら候補をしぼっていくやつ)とか。ファイラーとか。そんな粒度で他言語もとか。
諸々をお伝えするかもしれませんが お伝えしないかもしれません(あまのじゃく)