CakePHP2でもPhpStormがコード補完してくれるようにした話 / cakephp2-ide-helper

CakePHP2でもPhpStormがコード補完してくれるようにした話 / cakephp2-ide-helper

CakePHP2ではほとんどコード補完が働きません。
今回はlaravel-ide-helperのアプローチを参考に、CakePHP2でもコード補完されるようにした話をします。

https://packagist.org/packages/nazonohito51/cakephp2-ide-helper

B989cc865d53d8e26fdadac99727113c?s=128

Satoshi Kawashima

September 21, 2019
Tweet

Transcript

  1. © - BASE, Inc. CakePHP でもPhpStormが コード補完してくれるようにした話 川島 慧 /

    BASE Inc. / / PHPカンファレンス北海道
  2. © - BASE, Inc. Product Division 川島 慧 @nazonohito

  3. 課題

  4. © - BASE, Inc. CakePHP では PhpStormはほとんど コード補完してくれない

  5. © - BASE, Inc. Userモデルのオブジェクトが返ってくるが、 そのメソッドが補完されない

  6. © - BASE, Inc. CakePHP は 動的型付けバリバリだった時代の PHPフレームワーク

  7. © - BASE, Inc. 実⾏時じゃないと ⾊々確定しづらい

  8. © - BASE, Inc. =静的解析が通⽤しづらい

  9. © - BASE, Inc. =コード補完されないものが多い

  10. © - BASE, Inc. つらい

  11. © - BASE, Inc. なんとかしよ

  12. 解決のためのアイディア

  13. photos by @@@ https://hoge.dom/ laravel-ide-helper

  14. © - BASE, Inc. laravel-ide-helperとは • PhpStormが補完できないLaravelのあれやそれや を補完できるようにしてくれる君 • FacadeとかModelのプロパティやら

    • 対象のリポジトリを解析してPhpStorm向けのメタ データを書いたファイルを作ったり、PHPDocを追記 したりして実現している
  15. © - BASE, Inc. このアプローチを 流⽤すればいけるのでは?

  16. photos by @@@ https://hoge.dom/ 作った

  17. ClassRegistry::init()の解決

  18. © - BASE, Inc. ClassRegistry::init()を補完する • 引数にModelクラスの名前を⽂字列で渡すと、その Modelクラスのオブジェクトが返ってくる • プラグインのModelの場合は

    “{プラグイン 名}.{Model名}"という⽂字列を渡す Itemモデルのオブジェクトが 返ってくる
  19. © - BASE, Inc. ClassRegistry::init()の課題 • ClassRegistry::init()は引数によって戻り値の型が 変わる、いわゆるファクトリパターンな側⾯がある • Laravelにも同様の課題がapp()やApp::make()など

    にある Userモデルのオブジェクトが返ってくるはずだが、 そのメソッドが補完されない
  20. © - BASE, Inc. laravel-ide-helperにおける解決⽅法

  21. © - BASE, Inc. laravel-ide-helperにおける解決⽅法 これなに?

  22. © - BASE, Inc. .phpstorm.meta.php

  23. photos by @@@ https://hoge.dom/ https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html

  24. © - BASE, Inc. .phpstorm.meta.phpとは • PhpStormの補完機能を改善するためのメタデータ を記述できるファイル • PhpStormはどのPHPプロジェクトにも共通して当

    てはまるような解析しかしないが、プロジェクト固 有の要求に応えられるようにする機能
  25. © - BASE, Inc. .phpstorm.meta.phpで出来ること • ファクトリパターンなメソッドの引数と戻り値の型 の組み合わせを定義して補完可能にする • メソッドの引数候補を定義して補完可能にする

    • メソッドの戻り値の値候補を定義して補完可能にす る
  26. © - BASE, Inc. これを使って ClassRegistry::init()を補完するぞ

  27. © - BASE, Inc. CakePHP のディレクトリ構造 ※࣮ߦ࣌ʹطఆͷมߋ͢Δ͜ͱ΋ग़དྷ·͕͢ɺͦͷ৔߹ϥΠϒϥϦʹਖ਼͍͠Ҿ਺Λ౉͞ͳ͍ͱਖ਼ৗͳղੳ͸Ͱ͖·ͤΜ repos root /app

    /Model /Plugin SomeModel .php /SomePlugin SomeModel .php /Model SomeModel .php SomeModel .php ここに置いてあるクラスの名前を全部 glob()とかで取得する ここに置いてあるクラスの名前を全部 glob()とかで取得する Plugin配下のModelは、どのPluginに 所属しているかも解析する
  28. © - BASE, Inc. .phpstorm.meta.phpの作り⽅ • リポジトリルートに .phpstorm.meta.php という 名前のファイルを作る

    • (実はリポジトリルート以外に置いても⼤丈夫) • (もっと⾔うと.phpstorm.meta.phpというディレクトリを作ってその下に置くでも可)
  29. © - BASE, Inc. メタデータの書き⽅ • PHPSTORM_METAという名前空間配下に⽤意された PhpStormの関数を使ってメタデータを定義する 「メタデータを記述」って聞くと、 ymlやjsonみたいなstaticな構造データを書くイメージだけど、

    .phpstorm.meta.php上に関数を呼び出すPHPコードを書いて定義するというとても不思議な作り‧‧‧。
  30. © - BASE, Inc. メタデータの書き⽅ PHPSTORM_META配下としてコードを書く \PHPSTORM_META\override()に以下を渡す 第1引数:補完したいメソッド 第2引数:引数と戻り値の型のマッピング情報

  31. © - BASE, Inc. 補完されるようになった

  32. Behaviorのメソッド補完の解決

  33. © - BASE, Inc. 課題その2 • Behaviorのメソッドが補完されない ContainableBehaviorのメソッドが使 えるはずだが、補完されない

  34. © - BASE, Inc. CakePHP のBehaviorについて • 複数Modelで共通した振る舞いを再利⽤可能な形で 定義する機能 Model

    Model Model Behavior Behaviorのpublicメソッドが 使えるようになる
  35. © - BASE, Inc. Behaviorによって追加されたメソッドは補完されない • Modelクラスの__call()によって実現している • 未定義メソッドの呼び出しはBehaviorへ委譲する 形で実装されている

    • Laravelの場合、同様の問題がModelのクエリビル ダの呼び出しに起こっている
  36. © - BASE, Inc. laravel-ide-helperの解決⽅法 ModelクラスのPHPDocがlaravel-ide-helper から書き⾜されている 今回の場合は @mixin が注⽬ポイント

  37. © - BASE, Inc. laravel-ide-helperの解決⽅法 _ide_helper.phpという、名前の通り IDE向けのファイルが追加されている 本来のクラスを継承した独⾃クラスを作り、 補完してほしいメソッドをこちらに実装している

  38. © - BASE, Inc. IDE向けのクラスを仲介させ ることで補完可能にしている

  39. © - BASE, Inc. このアプローチでBehaviorを補完するぞ

  40. © - BASE, Inc. やること . 全てのBehaviorクラスを捕まえる . Behaviorを模したクラスを作る .

    上記のクラスをModelのPHPDocに@mixinとして 追記する
  41. © - BASE, Inc. 1. 全てのBehaviorを捕まえる ※࣮ߦ࣌ʹطఆͷมߋ͢Δ͜ͱ΋ग़དྷ·͕͢ɺͦͷ৔߹ϥΠϒϥϦʹਖ਼͍͠Ҿ਺Λ౉͞ͳ͍ͱਖ਼ৗͳղੳ͸Ͱ͖·ͤΜ repos root /app

    /Behavior /Plugin SomeBehavior .php /SomePlugin SomeBehavior .php /Behavior SomeBehavior .php SomeBehavior .php ここに置いてあるクラスの名前を全部 glob()とかで取得する ここに置いてあるクラスの名前を全部 glob()とかで取得する /Model /Model
  42. © - BASE, Inc. . Behaviorを模したクラスを作る • 同じメソッドを持っているだけではだめ • Behaviorのメソッドの呼び出し時は第1引数を⾶

    ばして、第2引数以降のみを渡す • 第1引数はフレームワークが代わりに⼊⼒する =第1引数を取り除いた状態のフェイクのクラスを作る
  43. © - BASE, Inc. 第1引数を除いたメソッドを持つクラスを作る • php-parserを使うと楽にできる • PHPコードをパースするライブラリ •

    PHPコードをAST(Abstract Syntax Tree)に変換 できる • ASTからPHPコードに戻すことも出来る
  44. © - BASE, Inc. php-parserで第1引数が除去されたBehaviorを作る Class_ ClassMethod Param Param ClassMethod

    Param Param Param Param
  45. © - BASE, Inc. php-parserで第1引数が除去されたBehaviorを作る Class_ ClassMethod Param Param ClassMethod

    Param Param
  46. © - BASE, Inc. php-parserで第1引数が除去されたBehaviorを作る Class_ ClassMethod Param Param ClassMethod

    Param Param ※最終的には⾊々あって少しだけ違うアプローチになりましたが、だいたいこんな感じの実装でやってます
  47. © - BASE, Inc. php-parserによる変換前後の⽐較 変換前 変換後

  48. © - BASE, Inc. php-parserから作った フェイクのBehaviorを _ide_helper.phpというファイルへ

  49. © - BASE, Inc. . ModelのPHPDocに@mixinとして追記する • barryvdh/reflection-docblockを使って既存のクラ スのPHPDocを編集する •

    https://packagist.org/packages/barryvdh/ reflection-docblock
  50. © - BASE, Inc. 補完されるようになった

  51. こんな感じでBASEでは、 フレームワーク‧ライブラリを ただのユーザとして利⽤するだけでなく、

  52. それ以外のフレームワークの知⾒や、 ⼀般化された技術など活⽤しながら 技術環境を改善しています

  53. None
  54. ご清聴 ありがとうございました