PHPのソースコードからクラス関連図を出力するphp-class-diagram を作っています。(jig inspire)
2021/11/03
2021/11/04
最近、設計について興味があるので、事例の紹介などで、java関連の開発では、jig(治具から来た名前らしい)という大変便利なツールがあるという話を聞いた。
jigのチュートリアルで紹介されている例のように、複数の関連したクラスを作るとリンク先のビジネスルール関連図が生成されるらしい。コードを書きながらおかしな依存を作っていないか確認しながら設計とプログラミングを同時に進めることができる、とても羨ましいツールのようです。
最近、仕事ではPHPばかりだから、PHPでも似たようなことができないか考えてみた。
jigはチュートリアルを見ただけでも、他にもたくさん機能がありそうだから、PHP版のjigを作ろうと思うとすぐ挫折するので、PHPソースコードからクラス関連図を生成するという部分だけが作れるか、に絞って実現方法を考え始めた。
最近のPHPでは、型を書けるようになっているので、型指定されているソースコードであることが前提
最低限作らなければならない機能についての検討
- PHPソースコードを読んで、クラス名とフィールド変数の型を調べる機能
- ファイル名とファイルの中で定義されているクラスが同じ名前であることを前提とすれば、ファイルを指定してPHPのクラス名トフィールド変数の型を調べることは可能。
- 解析対象のディレクトリ配下のディレクトリをパッケージと見做して、グルーピングをする機能
- 本当は、クラスのグルーピング情報はPHPのnamespaceで判断するのが妥当だと思うけど、CakePHP2系で作ってるクラスも解析対象にしたいので、ディレクトリをパッケージ階層と見做すようにする。namespaceが適切に定義されているクラスの場合もディレクトリ階層と同じnamespace階層に格納されているのが期待できる。
- クラスの依存関係を解析する機能
- 図を出力する機能
- 図を直接出力するとなると一気に難易度があがってしまうので、図を生成するのはPlantUMLを使うことにした。
- 解析したクラスの依存関係を、PlantUMLのクラス図として出力する。
作成した
プロトタイプ的な実装で、jigの羨ましいチュートリアルの簡単な例を再現できるようになった。
動作例
以下のようなディレクトリ配置になっている時、
└─test
└─fixtures
└─no-namespace
└─product
Product.php
Price.php
Name.php |
それぞれのファイルは、以下。
- Product.php
<?php class Product { private Name $name; private Price $price; } |
- Price.php
<?php class Price { private int $price; } |
- Name.php
<?php class Name { private string $name; } |
実行
php src/PhpClassDiagram.php test/fixtures/no-namespace |
出力結果
@startuml package "product" <<Rectangle>> { class Name class Price class Product } Product ..> Name Product ..> Price @enduml |
出力結果をPlantUMLを使って図に変換
実行時の引数のディレクトリ(test/fixtures/no-namespace)配下のディレクトリproductを、パッケージとして解釈して、その中に、Product, Price, Name というクラスがあり、依存関係を矢印で表示できた!
図に変換するところは、PlantUMLをdockerコンテナで立ち上げておけば、VSCodeでプレビューすることができるはず。
まだまだ
実用までは、まだまだ掛かりそうです。
- 見えている未対応なところ
- PHPソースコードの解析で、1対他の依存を検出するには、配列の要素の型を調べないといけなくて、PHPで表現できるのか、Documentコメントを使うのか等、考えることがありそうで、未実装
- 解析対象のクラス名が被ったら、破綻する。でもこれは、PlantUMLでの表記上でも同じなんじゃないかな?
- クラスに問題があってPHPで読めなければエラーになる?エラー表示とか考えてない。
- 今は、クラスのフィールド変数に別のクラスが定義されていることで、依存関係として扱うようにしてあるけど、そもそも依存関係ってそれだけなんだろうか?