Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

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

Satoshi Kawashima

September 21, 2019
Tweet

More Decks by Satoshi Kawashima

Other Decks in Technology

Transcript

  1. © - BASE, Inc.
    CakePHP でもPhpStormが
    コード補完してくれるようにした話
    川島 慧 / BASE Inc.
    / / PHPカンファレンス北海道

    View Slide

  2. © - BASE, Inc.
    Product Division 川島 慧
    @nazonohito

    View Slide

  3. 課題

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. © - BASE, Inc.
    つらい

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  14. © - BASE, Inc.
    laravel-ide-helperとは
    • PhpStormが補完できないLaravelのあれやそれや
    を補完できるようにしてくれる君
    • FacadeとかModelのプロパティやら
    • 対象のリポジトリを解析してPhpStorm向けのメタ
    データを書いたファイルを作ったり、PHPDocを追記
    したりして実現している

    View Slide

  15. © - BASE, Inc.
    このアプローチを
    流⽤すればいけるのでは?

    View Slide

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

    View Slide

  17. ClassRegistry::init()の解決

    View Slide

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

    View Slide

  19. © - BASE, Inc.
    ClassRegistry::init()の課題
    • ClassRegistry::init()は引数によって戻り値の型が
    変わる、いわゆるファクトリパターンな側⾯がある
    • Laravelにも同様の課題がapp()やApp::make()など
    にある
    Userモデルのオブジェクトが返ってくるはずだが、
    そのメソッドが補完されない

    View Slide

  20. © - BASE, Inc.
    laravel-ide-helperにおける解決⽅法

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. © - BASE, Inc.
    .phpstorm.meta.phpとは
    • PhpStormの補完機能を改善するためのメタデータ
    を記述できるファイル
    • PhpStormはどのPHPプロジェクトにも共通して当
    てはまるような解析しかしないが、プロジェクト固
    有の要求に応えられるようにする機能

    View Slide

  25. © - BASE, Inc.
    .phpstorm.meta.phpで出来ること
    • ファクトリパターンなメソッドの引数と戻り値の型
    の組み合わせを定義して補完可能にする
    • メソッドの引数候補を定義して補完可能にする
    • メソッドの戻り値の値候補を定義して補完可能にす

    View Slide

  26. © - BASE, Inc.
    これを使って
    ClassRegistry::init()を補完するぞ

    View Slide

  27. © - BASE, Inc.
    CakePHP のディレクトリ構造
    ※࣮ߦ࣌ʹطఆͷมߋ͢Δ͜ͱ΋ग़དྷ·͕͢ɺͦͷ৔߹ϥΠϒϥϦʹਖ਼͍͠Ҿ਺Λ౉͞ͳ͍ͱਖ਼ৗͳղੳ͸Ͱ͖·ͤΜ
    repos root
    /app
    /Model
    /Plugin
    SomeModel .php
    /SomePlugin
    SomeModel .php
    /Model
    SomeModel .php
    SomeModel .php
    ここに置いてあるクラスの名前を全部
    glob()とかで取得する
    ここに置いてあるクラスの名前を全部
    glob()とかで取得する
    Plugin配下のModelは、どのPluginに
    所属しているかも解析する

    View Slide

  28. © - BASE, Inc.
    .phpstorm.meta.phpの作り⽅
    • リポジトリルートに .phpstorm.meta.php という
    名前のファイルを作る
    • (実はリポジトリルート以外に置いても⼤丈夫)
    • (もっと⾔うと.phpstorm.meta.phpというディレクトリを作ってその下に置くでも可)

    View Slide

  29. © - BASE, Inc.
    メタデータの書き⽅
    • PHPSTORM_METAという名前空間配下に⽤意された
    PhpStormの関数を使ってメタデータを定義する
    「メタデータを記述」って聞くと、
    ymlやjsonみたいなstaticな構造データを書くイメージだけど、
    .phpstorm.meta.php上に関数を呼び出すPHPコードを書いて定義するというとても不思議な作り‧‧‧。

    View Slide

  30. © - BASE, Inc.
    メタデータの書き⽅
    PHPSTORM_META配下としてコードを書く
    \PHPSTORM_META\override()に以下を渡す
    第1引数:補完したいメソッド
    第2引数:引数と戻り値の型のマッピング情報

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. © - BASE, Inc.
    CakePHP のBehaviorについて
    • 複数Modelで共通した振る舞いを再利⽤可能な形で
    定義する機能
    Model
    Model
    Model
    Behavior
    Behaviorのpublicメソッドが
    使えるようになる

    View Slide

  35. © - BASE, Inc.
    Behaviorによって追加されたメソッドは補完されない
    • Modelクラスの__call()によって実現している
    • 未定義メソッドの呼び出しはBehaviorへ委譲する
    形で実装されている
    • Laravelの場合、同様の問題がModelのクエリビル
    ダの呼び出しに起こっている

    View Slide

  36. © - BASE, Inc.
    laravel-ide-helperの解決⽅法
    ModelクラスのPHPDocがlaravel-ide-helper
    から書き⾜されている
    今回の場合は @mixin が注⽬ポイント

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. © - BASE, Inc.
    やること
    . 全てのBehaviorクラスを捕まえる
    . Behaviorを模したクラスを作る
    . 上記のクラスをModelのPHPDocに@mixinとして
    追記する

    View Slide

  41. © - BASE, Inc.
    1. 全てのBehaviorを捕まえる
    ※࣮ߦ࣌ʹطఆͷมߋ͢Δ͜ͱ΋ग़དྷ·͕͢ɺͦͷ৔߹ϥΠϒϥϦʹਖ਼͍͠Ҿ਺Λ౉͞ͳ͍ͱਖ਼ৗͳղੳ͸Ͱ͖·ͤΜ
    repos root
    /app
    /Behavior
    /Plugin
    SomeBehavior .php
    /SomePlugin
    SomeBehavior .php
    /Behavior
    SomeBehavior .php
    SomeBehavior .php
    ここに置いてあるクラスの名前を全部
    glob()とかで取得する
    ここに置いてあるクラスの名前を全部
    glob()とかで取得する
    /Model
    /Model

    View Slide

  42. © - BASE, Inc.
    . Behaviorを模したクラスを作る
    • 同じメソッドを持っているだけではだめ
    • Behaviorのメソッドの呼び出し時は第1引数を⾶
    ばして、第2引数以降のみを渡す
    • 第1引数はフレームワークが代わりに⼊⼒する
    =第1引数を取り除いた状態のフェイクのクラスを作る

    View Slide

  43. © - BASE, Inc.
    第1引数を除いたメソッドを持つクラスを作る
    • php-parserを使うと楽にできる
    • PHPコードをパースするライブラリ
    • PHPコードをAST(Abstract Syntax Tree)に変換
    できる
    • ASTからPHPコードに戻すことも出来る

    View Slide

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

    View Slide

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

    View Slide

  46. © - BASE, Inc.
    php-parserで第1引数が除去されたBehaviorを作る
    Class_
    ClassMethod
    Param
    Param
    ClassMethod
    Param
    Param
    ※最終的には⾊々あって少しだけ違うアプローチになりましたが、だいたいこんな感じの実装でやってます

    View Slide

  47. © - BASE, Inc.
    php-parserによる変換前後の⽐較
    変換前 変換後

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  53. View Slide

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

    View Slide